Skip to content

Commit

Permalink
feat(dropdown): reworked dropdown component
Browse files Browse the repository at this point in the history
  • Loading branch information
gravitano committed May 11, 2022
1 parent 20d8702 commit 6c2b934
Show file tree
Hide file tree
Showing 13 changed files with 242 additions and 281 deletions.
3 changes: 2 additions & 1 deletion packages/dropdown/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
"feather-icons": "^4.28.0",
"tailwindcss": "^3.0.23",
"vue": "^3.2.31",
"vue-feather": "^2.0.0"
"vue-feather": "^2.0.0",
"vue-remix-icons": "^1.1.1"
},
"devDependencies": {
"@gits-id/tailwind-config": "^0.2.4",
Expand Down
91 changes: 91 additions & 0 deletions packages/dropdown/src/Dropdown.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import type {Story} from '@storybook/vue3';
import Dropdown from './Dropdown.vue';
import DropdownItem from './DropdownItem.vue';
import type {DropdownItemProps} from './types';
import {RiCalendarFill} from 'vue-remix-icons';

const icons = ['calendar', 'attachment', 'download', 'clock', 'document'];

const genItems = (length = 5): DropdownItemProps[] =>
Array.from({length}, (_, v) => ({
text: `Item ${v + 1}`,
// icon: icons[Math.floor(Math.random() * icons.length)],
}));

const items = [...genItems(2), {divider: true}, ...genItems(3)];

export default {
title: 'Components/Dropdown',
component: Dropdown,
args: {
modelValue: false,
right: false,
btnProps: {
variant: 'secondary',
},
label: 'Options',
items,
},
};

const Template: Story = (args, {argTypes}) => ({
components: {Dropdown},
setup() {
return {args, argTypes};
},
template: `
<div class="flex justify-center">
<Dropdown v-bind="args"/>
</div>
`,
});

export const Default = Template.bind({});
Default.args = {};

export const Right = Template.bind({});
Right.args = {
right: true,
};

export const RouterLink = Template.bind({});
RouterLink.args = {
items: [
{
text: 'Link 1',
to: '/home',
},
],
};

export const Href = Template.bind({});
Href.args = {
items: [
{
text: 'Link 1',
href: '/home',
},
{
text: 'Link 2',
href: '/setting',
newTab: true,
},
],
};

export const Slots: Story = (args, {argTypes}) => ({
components: {Dropdown, DropdownItem},
setup() {
return {args, argTypes};
},
template: `
<div class="flex justify-center">
<Dropdown v-bind="args">
<DropdownItem text="Calendar" icon="calendar"/>
<DropdownItem text="Files" icon="document" />
<DropdownItem divider/>
<DropdownItem text="Timer" icon="clock"/>
</Dropdown>
</div>
`,
});
69 changes: 69 additions & 0 deletions packages/dropdown/src/Dropdown.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<script setup lang="ts">
import {Menu, MenuButton, MenuItems} from '@headlessui/vue';
import type {DropdownItemProps} from './types';
import DropdownItem from './DropdownItem.vue';
// import IconArrowDown from '~icons/ri/arrow-down-s-line';
import {RiArrowDownSLine as IconArrowDown} from 'vue-remix-icons';
const props = withDefaults(
defineProps<{
modelValue?: boolean;
btnProps?: Record<string, any>;
label?: string;
right?: boolean;
items?: DropdownItemProps[];
}>(),
{
modelValue: false,
btnProps: () => ({
variant: 'secondary',
}),
label: 'Options',
right: false,
items: () => [],
},
);
</script>

<template>
<Menu as="div" class="relative inline-block text-left">
<div>
<slot name="activator" :btn-props="btnProps" :label="label">
<MenuButton as="button" v-bind="btnProps">
{{ label }}
<IconArrowDown class="w-5 h-5 inline" aria-hidden="true" />
</MenuButton>
</slot>
</div>

<transition
enter-active-class="transition duration-100 ease-out"
enter-from-class="transform scale-95 opacity-0"
enter-to-class="transform scale-100 opacity-100"
leave-active-class="transition duration-75 ease-in"
leave-from-class="transform scale-100 opacity-100"
leave-to-class="transform scale-95 opacity-0"
>
<MenuItems
class="
absolute
z-10
p-1
w-56
mt-2
origin-top-right
bg-white
rounded-md
shadow-lg
ring-1 ring-black ring-opacity-5
focus:outline-none
"
:class="right ? 'right-0' : 'left-0'"
>
<slot>
<DropdownItem v-for="item in items" :key="item.text" v-bind="item" />
</slot>
</MenuItems>
</transition>
</Menu>
</template>
11 changes: 11 additions & 0 deletions packages/dropdown/src/DropdownButton.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<script setup lang="ts">
import { MenuButton } from '@headlessui/vue';
</script>

<template>
<MenuButton v-slot="props">
<slot v-bind="props" />
</MenuButton>
</template>

<style scoped></style>
56 changes: 56 additions & 0 deletions packages/dropdown/src/DropdownItem.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<script setup lang="ts">
import { MenuItem } from '@headlessui/vue';
import { RouterLink } from 'vue-router';
import { computed } from 'vue';
const props = withDefaults(
defineProps<{
text?: string;
to?: string;
href?: string;
icon?: any;
iconClass?: string;
newTab?: boolean;
divider?: boolean;
}>(),
{
iconClass: 'w-5 h-5',
}
);
const computedComponent = computed(() => {
if (props.href) return 'a';
return props.to ? RouterLink : 'button';
});
const toProps = computed(() => {
return props.to ? { to: props.to } : {};
});
const hrefProps = computed(() => {
return props.href ? { href: props.href } : {};
});
</script>

<template>
<MenuItem v-slot="{ active }">
<div v-if="divider" class="border-b my-1 -mx-1"></div>
<component
v-else
:is="computedComponent"
:target="href && newTab ? '_blank' : undefined"
:rel="href && newTab ? 'noopener' : null"
:class="[
active ? 'bg-gray-200' : 'text-gray-900',
'group flex gap-2 rounded-md items-center w-full px-2 py-2',
]"
v-bind="{ ...hrefProps, ...toProps, ...$attrs }"
>
<slot name="icon">
<component :is="icon" :class="iconClass" />
</slot>
<slot>{{ text }}</slot>
</component>
</MenuItem>
</template>
47 changes: 0 additions & 47 deletions packages/dropdown/src/VDropdown.stories.ts

This file was deleted.

Loading

0 comments on commit 6c2b934

Please sign in to comment.