Skip to content

Commit

Permalink
first version of split button
Browse files Browse the repository at this point in the history
  • Loading branch information
PhilReinking committed Nov 15, 2023
1 parent e93205d commit 7a8cf02
Show file tree
Hide file tree
Showing 4 changed files with 204 additions and 0 deletions.
63 changes: 63 additions & 0 deletions src/SplitButton/SplitButton.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { D9SplitButton } from "../index";
import { icons } from "../index";

export default {
title: "Basics/SplitButton",
component: D9SplitButton,
argTypes: {
color: {
options: ["primary", "light", "dark", "danger"],
},
size: {
options: ["small", "medium", "large"],
},
icon: { options: icons, control: { type: "select" } },
iconPosition: { options: ["left", "right"] },
onClick: { action: "clicked" },
},
};

const Template = (args: Record<string, unknown>) => ({
components: { D9SplitButton },
setup() {
return { args };
},
template: '<D9SplitButton v-bind="args" />',
});

export const Medium = Template.bind({});
Medium.args = {
label: "Split Button",
size: "medium",
};

export const Small = Template.bind({});
Small.args = {
size: "small",
label: "Split Button",
};

export const Large = Template.bind({});
Large.args = {
size: "large",
label: "Split Button",
};

export const Disabled = Template.bind({});
Disabled.args = {
label: "Split Button",
isDisabled: true,
};

export const Loading = Template.bind({});
Loading.args = {
label: "Split Button",
isLoading: true,
};

export const LoadingAndDisabled = Template.bind({});
LoadingAndDisabled.args = {
label: "Split Button",
isDisabled: true,
isLoading: true,
};
139 changes: 139 additions & 0 deletions src/SplitButton/SplitButton.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
<template>
<div
class="relative inline-flex items-center focus:outline-none focus:ring active:ring transition duration-150 ease-in-out rounded-lg overflow-hidden"
>
<button
type="button"
:aria-label="label"
:tabindex="isDisabled ? '-1' : undefined"
class="relative"
:class="buttonClasses"
@click="onClickMain"
>
<div :class="!isLoading || 'invisible'">
<span>{{ label }}</span>
</div>

<span
v-if="isLoading"
class="absolute inset-0 flex items-center justify-center"
>
<D9Icon class="ml-3 animate-spin" name="circle-notch" />
</span>
</button>
<button
class="border-l-white/50 border-l"
:class="[buttonClasses, splitButtonClasses]"
@click="onClickSplit"
>
<D9Icon :class="[iconClasses]" :name="icon" />
</button>
</div>
</template>

<script setup lang="ts">
import D9Icon from "../Icon/Icon.vue";
import { ColorScheme, Size } from "../types/types";
import { computed, withDefaults } from "vue";
interface ButtonProps {
label: string;
size?: Size;
color?: ColorScheme;
isLoading?: boolean;
isDisabled?: boolean;
icon?: string;
}
interface ButtonEmits {
(e: "onClick"): void;
(e: "onClickSplit"): void;
}
const emit = defineEmits<ButtonEmits>();
const props = withDefaults(defineProps<ButtonProps>(), {
size: "medium",
color: "primary",
isLoading: false,
isDisabled: false,
icon: "plus",
});
const buttonClasses = computed(() => {
const colors = {
"text-white bg-blue-600 border-transparent hover:bg-blue-700 active:bg-blue-700 dark:ring-blue-800 ring-offset-2 dark:ring-offset-grey-900":
props.color === "primary",
"text-blue-600 bg-grey-100 border-transparent hover:bg-blue-100 hover:text-blue-700 active:bg-grey-100 ring-blue-300 ring-offset-2 dark:ring-offset-grey-900":
props.color === "light",
"text-grey-50 bg-grey-700 border-transparent hover:bg-grey-800 active:bg-grey-700 ring-grey-500 dark:ring-grey-400 dark:ring-opacity-50 ring-offset-2 dark:ring-offset-grey-900":
props.color === "dark",
"text-red-50 bg-red-500 border-transparent hover:bg-red-600 active:bg-red-600 ring-red-400 dark:ring-red-400 dark:ring-opacity-50 ring-offset-2 dark:ring-offset-red-900":
props.color === "danger",
};
const sizes = {
"px-4 py-1 text-xs leading-5 mono-100": props.size === "small",
"px-5 py-2 text-sm leading-4 font-medium": props.size === "medium",
"px-8 pr-4 py-3 text-base leading-6 font-medium": props.size === "large",
};
const disabledClasses = {
"pointer-events-none opacity-75 cursor-not-allowed": props.isDisabled,
};
return {
...colors,
...disabledClasses,
...sizes,
};
});
const splitButtonClasses = computed(() => {
const colors = {
"border-l-white/50 border-l": props.color === "primary",
"border-l-blue-100 border-l": props.color === "light",
"border-l-grey-600 border-l": props.color === "dark",
"border-l-red-600 border-l": props.color === "danger",
};
const sizes = {
"pl-2 pr-3": props.size === "small",
"pl-3 pr-3": props.size === "medium",
"pl-4 pr-5": props.size === "large",
};
return {
...colors,
...sizes,
};
});
const iconClasses = computed(() => {
const colors = {
"text-white": props.color === "primary",
"text-blue-600": props.color === "light",
"text-grey-50": props.color === "dark",
};
return {
...colors,
};
});
function onClickMain() {
if (props.isDisabled) {
return;
}
emit("onClick");
}
function onClickSplit() {
if (props.isDisabled) {
return;
}
emit("onClickSplit");
}
</script>
1 change: 1 addition & 0 deletions src/SplitButton/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as D9SplitButton } from "./SplitButton.vue";
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ export * from "./Menu/index";
export * from "./Select/index";
export * from "./Skeleton/index";
export * from "./Spinner/index";
export * from "./SplitButton/index";
export * from "./Switch/index";
export * from "./Textarea/index";

0 comments on commit 7a8cf02

Please sign in to comment.