vanilla-vue-ui 0.0.8 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/basic/accordion/AccordionContent.ts +4 -0
- package/basic/accordion/WAccordion.stories.ts +49 -0
- package/basic/accordion/WAccordion.vue +86 -0
- package/basic/card/WCard.stories.ts +36 -0
- package/basic/card/WCard.vue +22 -0
- package/basic/carousel/WCarousel.stories.ts +29 -0
- package/basic/carousel/WCarousel.vue +90 -0
- package/basic/checkbox/WCheckbox.spec.ts +58 -0
- package/basic/checkbox/WCheckbox.stories.ts +51 -0
- package/basic/checkbox/WCheckbox.vue +42 -0
- package/basic/date-picker/WDatePicker.stories.ts +79 -0
- package/basic/date-picker/WDatePicker.vue +271 -0
- package/basic/dialog/DialogStore.ts +57 -0
- package/basic/dialog/WDialog.stories.ts +38 -0
- package/basic/dialog/WDialog.vue +66 -0
- package/basic/divider/WDivider.spec.ts +42 -0
- package/basic/divider/WDivider.stories.ts +40 -0
- package/basic/divider/WDivider.vue +56 -0
- package/basic/feed/TimeLine.ts +9 -0
- package/basic/feed/WFeed.spec.ts +64 -0
- package/basic/feed/WFeed.stories.ts +62 -0
- package/basic/feed/WFeed.vue +41 -0
- package/basic/floating-button/WFloatingButton.stories.ts +24 -0
- package/basic/floating-button/WFloatingButton.vue +25 -0
- package/basic/gradient-text/WGradientText.stories.ts +23 -0
- package/basic/gradient-text/WGradientText.vue +5 -0
- package/basic/horizontal-scroll/WHorizontalScroll.stories.ts +29 -0
- package/basic/horizontal-scroll/WHorizontalScroll.vue +5 -0
- package/basic/loading/LoadingStore.ts +57 -0
- package/basic/loading/WLoading.stories.ts +27 -0
- package/basic/loading/WLoading.vue +54 -0
- package/basic/menu/Menu.stories.ts +55 -0
- package/basic/menu/WMenu.vue +96 -0
- package/basic/menu/WMenuOption.ts +5 -0
- package/basic/navigation-drawer/NavigationDrawerContent.ts +11 -0
- package/basic/navigation-drawer/WNavigationDrawer.stories.ts +59 -0
- package/basic/navigation-drawer/WNavigationDrawer.vue +123 -0
- package/basic/notification/NotificationStore.ts +48 -0
- package/basic/notification/WNotification.stories.ts +27 -0
- package/basic/notification/WNotification.vue +44 -0
- package/basic/pagination/WPagination.stories.ts +30 -0
- package/basic/pagination/WPagination.vue +58 -0
- package/basic/popup/PopupStore.ts +40 -0
- package/basic/popup/WPopup.stories.ts +27 -0
- package/basic/popup/WPopup.vue +67 -0
- package/basic/select/SelectOption.ts +4 -0
- package/basic/select/WSelect.stories.ts +56 -0
- package/basic/select/WSelect.vue +58 -0
- package/basic/skeleton-loader/WSkeletonBar.vue +24 -0
- package/basic/skeleton-loader/WSkeletonLoader.stories.ts +23 -0
- package/basic/skeleton-loader/WSkeletonLoader.vue +17 -0
- package/basic/slide-over/WSlideOver.stories.ts +23 -0
- package/basic/slide-over/WSlideOver.vue +53 -0
- package/basic/step/StepContent.ts +8 -0
- package/basic/step/StepStatus.ts +3 -0
- package/basic/step/WStep.stories.ts +42 -0
- package/basic/step/WStep.vue +48 -0
- package/basic/table/WTable.stories.ts +23 -0
- package/basic/table/WTable.vue +30 -0
- package/basic/text-area/TextAreaSize.ts +2 -0
- package/basic/text-area/WTextArea.spec.ts +18 -0
- package/basic/text-area/WTextArea.stories.ts +41 -0
- package/basic/text-area/WTextArea.vue +158 -0
- package/package.json +1 -1
- package/page-template/app-template/WAppTemplate.vue +2 -2
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// Replace vue3 with vue if you are using Storybook for Vue 2
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/vue3';
|
|
3
|
+
import { CheckIcon, HandThumbUpIcon, UserIcon } from '@heroicons/vue/20/solid'
|
|
4
|
+
import Feed from './Feed.vue';
|
|
5
|
+
|
|
6
|
+
type FeedProps = InstanceType<typeof Feed>['$props']
|
|
7
|
+
|
|
8
|
+
const meta: Meta<typeof Feed> = {
|
|
9
|
+
component: Feed,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export default meta;
|
|
13
|
+
type Story = StoryObj<typeof Feed>;
|
|
14
|
+
|
|
15
|
+
/*
|
|
16
|
+
*👇 Render functions are a framework specific feature to allow you control on how the component renders.
|
|
17
|
+
* See https://storybook.js.org/docs/api/csf
|
|
18
|
+
* to learn how to use render functions.
|
|
19
|
+
*/
|
|
20
|
+
export const Primary: Story = {
|
|
21
|
+
render: (args: FeedProps) => ({
|
|
22
|
+
setup() {
|
|
23
|
+
return {
|
|
24
|
+
...args,
|
|
25
|
+
};
|
|
26
|
+
},
|
|
27
|
+
components: { Feed, CheckIcon, HandThumbUpIcon, UserIcon },
|
|
28
|
+
template: '<Feed :timeline="timeline"></Feed>',
|
|
29
|
+
}),
|
|
30
|
+
args: {
|
|
31
|
+
timeline: [
|
|
32
|
+
{
|
|
33
|
+
id: 1,
|
|
34
|
+
content: 'Applied to',
|
|
35
|
+
date: '2020-09-20',
|
|
36
|
+
icon: UserIcon,
|
|
37
|
+
iconBackground: 'bg-gray-400',
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
id: 2,
|
|
41
|
+
content: 'Advanced to phone screening by',
|
|
42
|
+
date: '2020-09-22',
|
|
43
|
+
icon: HandThumbUpIcon,
|
|
44
|
+
iconBackground: 'bg-blue-500',
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
id: 3,
|
|
48
|
+
content: 'Completed phone screening with',
|
|
49
|
+
date: '2020-09-28',
|
|
50
|
+
icon: CheckIcon,
|
|
51
|
+
iconBackground: 'bg-green-500',
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
id: 4,
|
|
55
|
+
content: 'Advanced to interview by',
|
|
56
|
+
date: '2020-09-30',
|
|
57
|
+
icon: HandThumbUpIcon,
|
|
58
|
+
iconBackground: 'bg-blue-500',
|
|
59
|
+
},
|
|
60
|
+
]
|
|
61
|
+
},
|
|
62
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<!-- eslint-disable vue/multi-word-component-names -->
|
|
2
|
+
<template>
|
|
3
|
+
<div class="flow-root">
|
|
4
|
+
<ul role="list" class="-mb-8">
|
|
5
|
+
<li v-for="(event, eventIdx) in timeline" :key="event.id">
|
|
6
|
+
<div class="relative pb-8">
|
|
7
|
+
<span v-if="eventIdx !== timeline.length - 1" class="absolute left-4 top-4 -ml-px h-full w-0.5 bg-gray-200" aria-hidden="true" />
|
|
8
|
+
<div class="relative flex space-x-3">
|
|
9
|
+
<div>
|
|
10
|
+
<span :class="[event.iconBackground, 'h-8 w-8 rounded-full flex items-center justify-center ring-8 ring-white']">
|
|
11
|
+
<component :is="event.icon" class="h-5 w-5 text-white" aria-hidden="true" />
|
|
12
|
+
</span>
|
|
13
|
+
</div>
|
|
14
|
+
<div class="flex min-w-0 flex-1 justify-between space-x-4 pt-1.5">
|
|
15
|
+
<div>
|
|
16
|
+
<p class="text-sm text-gray-500">
|
|
17
|
+
{{ event.content }}
|
|
18
|
+
</p>
|
|
19
|
+
</div>
|
|
20
|
+
<div class="whitespace-nowrap text-right text-sm text-gray-500">
|
|
21
|
+
<time :datetime="event.date">{{ event.date }}</time>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
</li>
|
|
27
|
+
</ul>
|
|
28
|
+
</div>
|
|
29
|
+
</template>
|
|
30
|
+
|
|
31
|
+
<script setup lang="ts">
|
|
32
|
+
import { defineProps, type PropType } from 'vue';
|
|
33
|
+
import type { TimeLine } from './TimeLine'
|
|
34
|
+
|
|
35
|
+
defineProps({
|
|
36
|
+
timeline: {
|
|
37
|
+
type: Array as PropType<TimeLine[]>,
|
|
38
|
+
required: true
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
</script>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Replace vue3 with vue if you are using Storybook for Vue 2
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/vue3';
|
|
3
|
+
|
|
4
|
+
import FloatingButton from './FloatingButton.vue';
|
|
5
|
+
import { QuestionMarkCircleIcon } from '@heroicons/vue/24/outline'
|
|
6
|
+
|
|
7
|
+
const meta: Meta<typeof FloatingButton> = {
|
|
8
|
+
component: FloatingButton,
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export default meta;
|
|
12
|
+
type Story = StoryObj<typeof FloatingButton>;
|
|
13
|
+
|
|
14
|
+
/*
|
|
15
|
+
*👇 Render functions are a framework specific feature to allow you control on how the component renders.
|
|
16
|
+
* See https://storybook.js.org/docs/api/csf
|
|
17
|
+
* to learn how to use render functions.
|
|
18
|
+
*/
|
|
19
|
+
export const Primary: Story = {
|
|
20
|
+
render: () => ({
|
|
21
|
+
components: { FloatingButton, QuestionMarkCircleIcon },
|
|
22
|
+
template: '<FloatingButton><QuestionMarkCircleIcon class="h-12 w-12" aria-hidden="true" /></FloatingButton>',
|
|
23
|
+
}),
|
|
24
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="static">
|
|
3
|
+
<div class="fixed bottom-4 right-6">
|
|
4
|
+
<button type="button" :class="classes.base">
|
|
5
|
+
<slot />
|
|
6
|
+
</button>
|
|
7
|
+
</div>
|
|
8
|
+
</div>
|
|
9
|
+
</template>
|
|
10
|
+
|
|
11
|
+
<script setup lang="ts">
|
|
12
|
+
import type { PropType } from 'vue';
|
|
13
|
+
import type { ClassObject } from '../../types/ClassObject';
|
|
14
|
+
|
|
15
|
+
defineProps({
|
|
16
|
+
classes: {
|
|
17
|
+
type: Object as PropType<ClassObject>,
|
|
18
|
+
default() {
|
|
19
|
+
return {
|
|
20
|
+
base: 'rounded-full bg-indigo-600 p-1 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600'
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
</script>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Replace vue3 with vue if you are using Storybook for Vue 2
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/vue3';
|
|
3
|
+
|
|
4
|
+
import GradientText from './GradientText.vue';
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof GradientText> = {
|
|
7
|
+
component: GradientText,
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export default meta;
|
|
11
|
+
type Story = StoryObj<typeof GradientText>;
|
|
12
|
+
|
|
13
|
+
/*
|
|
14
|
+
*👇 Render functions are a framework specific feature to allow you control on how the component renders.
|
|
15
|
+
* See https://storybook.js.org/docs/api/csf
|
|
16
|
+
* to learn how to use render functions.
|
|
17
|
+
*/
|
|
18
|
+
export const Primary: Story = {
|
|
19
|
+
render: () => ({
|
|
20
|
+
components: { GradientText },
|
|
21
|
+
template: '<GradientText>GradientText</GradientText>',
|
|
22
|
+
}),
|
|
23
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Replace vue3 with vue if you are using Storybook for Vue 2
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/vue3';
|
|
3
|
+
|
|
4
|
+
import HorizontalScroll from './HorizontalScroll.vue';
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof HorizontalScroll> = {
|
|
7
|
+
component: HorizontalScroll,
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export default meta;
|
|
11
|
+
type Story = StoryObj<typeof HorizontalScroll>;
|
|
12
|
+
|
|
13
|
+
/*
|
|
14
|
+
*👇 Render functions are a framework specific feature to allow you control on how the component renders.
|
|
15
|
+
* See https://storybook.js.org/docs/api/csf
|
|
16
|
+
* to learn how to use render functions.
|
|
17
|
+
*/
|
|
18
|
+
export const Primary: Story = {
|
|
19
|
+
render: () => ({
|
|
20
|
+
components: { HorizontalScroll },
|
|
21
|
+
template: `
|
|
22
|
+
<HorizontalScroll>
|
|
23
|
+
<img src="/carousel-0.webp" alt="Slide 1">
|
|
24
|
+
<img src="/carousel-1.webp" alt="Slide 2">
|
|
25
|
+
<img src="/carousel-2.webp" alt="Slide 3">
|
|
26
|
+
</HorizontalScroll>
|
|
27
|
+
`,
|
|
28
|
+
}),
|
|
29
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { ref, computed } from 'vue'
|
|
2
|
+
import { defineStore } from 'pinia'
|
|
3
|
+
|
|
4
|
+
export const LoadingStore = defineStore('loading', () => {
|
|
5
|
+
|
|
6
|
+
// リアクティブにするために ref を使用する
|
|
7
|
+
const _isOpen = ref(false)
|
|
8
|
+
const _title = ref('')
|
|
9
|
+
const _contentText = ref('')
|
|
10
|
+
const _okButtonText = ref('')
|
|
11
|
+
const _cancelButtonText = ref('')
|
|
12
|
+
let _completion: ((isConfirmed: boolean | null) => void) | null = null
|
|
13
|
+
// true にするとダイアログ以外の場所をタップしても消えない
|
|
14
|
+
// ボタンでしか閉じれなくなる
|
|
15
|
+
const _persistent = ref(false)
|
|
16
|
+
|
|
17
|
+
// computed にすることで直接変更できなくする
|
|
18
|
+
const isOpen = computed(() => _isOpen)
|
|
19
|
+
const getTitle = computed(() => _title)
|
|
20
|
+
const getContentText = computed(() => _contentText)
|
|
21
|
+
const getOkButtonText = computed(() => _okButtonText)
|
|
22
|
+
const getCancelButtonText = computed(() => _cancelButtonText)
|
|
23
|
+
const getPersistent = computed(() => _persistent)
|
|
24
|
+
|
|
25
|
+
function open({ title, contentText, okButtonText = 'OK', cancelButtonText = '', persistent = false, completion = null }: { title: string; contentText: string; okButtonText?: string; cancelButtonText?: string; persistent?: boolean; completion?: ((isConfirmed: boolean | null) => void) | null }) {
|
|
26
|
+
|
|
27
|
+
_title.value = title
|
|
28
|
+
_contentText.value = contentText
|
|
29
|
+
_okButtonText.value = okButtonText
|
|
30
|
+
_cancelButtonText.value = cancelButtonText
|
|
31
|
+
_persistent.value = persistent
|
|
32
|
+
_isOpen.value = true
|
|
33
|
+
_completion = completion
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function close({ isConfirmed = null }: { isConfirmed?: boolean | null }) {
|
|
37
|
+
|
|
38
|
+
_isOpen.value = false
|
|
39
|
+
_title.value = ''
|
|
40
|
+
_contentText.value = ''
|
|
41
|
+
_okButtonText.value = ''
|
|
42
|
+
_cancelButtonText.value = ''
|
|
43
|
+
_persistent.value = false
|
|
44
|
+
|
|
45
|
+
// コールバック関数が含まれていれば実行
|
|
46
|
+
if (_completion) {
|
|
47
|
+
|
|
48
|
+
// 実行
|
|
49
|
+
_completion(isConfirmed)
|
|
50
|
+
|
|
51
|
+
// リセット
|
|
52
|
+
_completion = null
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return { isOpen, getPersistent, getTitle, getContentText, getOkButtonText, getCancelButtonText, open, close }
|
|
57
|
+
})
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Replace vue3 with vue if you are using Storybook for Vue 2
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/vue3';
|
|
3
|
+
import { LoadingStore } from './LoadingStore';
|
|
4
|
+
import Loading from './Loading.vue';
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof Loading> = {
|
|
7
|
+
component: Loading,
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export default meta;
|
|
11
|
+
type Story = StoryObj<typeof Loading>;
|
|
12
|
+
|
|
13
|
+
/*
|
|
14
|
+
*👇 Render functions are a framework specific feature to allow you control on how the component renders.
|
|
15
|
+
* See https://storybook.js.org/docs/api/csf
|
|
16
|
+
* to learn how to use render functions.
|
|
17
|
+
*/
|
|
18
|
+
export const Primary: Story = {
|
|
19
|
+
render: () => ({
|
|
20
|
+
setup() {
|
|
21
|
+
const dialogStore = LoadingStore()
|
|
22
|
+
dialogStore.open({ title: 'Loading', contentText: 'しばらくお待ちください', persistent: true })
|
|
23
|
+
},
|
|
24
|
+
components: { Loading },
|
|
25
|
+
template: '<Loading></Loading>',
|
|
26
|
+
}),
|
|
27
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<!-- eslint-disable vue/multi-word-component-names -->
|
|
2
|
+
<template>
|
|
3
|
+
<TransitionRoot as="template" :show="isOpen">
|
|
4
|
+
<Dialog as="div" class="relative z-100" @close="close({ isConfirmed: null })">
|
|
5
|
+
<TransitionChild as="template" enter="ease-out duration-300" enter-from="opacity-0" enter-to="opacity-100" leave="ease-in duration-200" leave-from="opacity-100" leave-to="opacity-0">
|
|
6
|
+
<div class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
|
|
7
|
+
</TransitionChild>
|
|
8
|
+
|
|
9
|
+
<div class="fixed inset-0 z-100 w-screen overflow-y-auto">
|
|
10
|
+
<div class="flex min-h-full items-center justify-center p-4 text-center sm:p-0">
|
|
11
|
+
<TransitionChild as="template" enter="ease-out duration-300" enter-from="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" enter-to="opacity-100 translate-y-0 sm:scale-100" leave="ease-in duration-200" leave-from="opacity-100 translate-y-0 sm:scale-100" leave-to="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95">
|
|
12
|
+
<DialogPanel class="relative transform overflow-hidden rounded-lg bg-white px-4 pb-4 pt-5 text-left shadow-xl transition-all sm:my-8 w-full sm:w-full sm:max-w-sm sm:p-6">
|
|
13
|
+
<div>
|
|
14
|
+
<div class="mx-auto flex h-12 w-12 items-center justify-center rounded-full">
|
|
15
|
+
<div class="animate-spin h-10 w-10 border-4 primary rounded-full border-t-transparent"/>
|
|
16
|
+
</div>
|
|
17
|
+
<div class="mt-3 text-center sm:mt-5">
|
|
18
|
+
<DialogTitle as="h3" class="text-base font-semibold leading-6 text-gray-900">{{ title }}</DialogTitle>
|
|
19
|
+
<div class="mt-2">
|
|
20
|
+
<p class="text-sm text-gray-500">{{ contentText }}</p>
|
|
21
|
+
<slot />
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
</DialogPanel>
|
|
26
|
+
</TransitionChild>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
</Dialog>
|
|
30
|
+
</TransitionRoot>
|
|
31
|
+
</template>
|
|
32
|
+
|
|
33
|
+
<script setup lang="ts">
|
|
34
|
+
import { Dialog, DialogPanel, DialogTitle, TransitionChild, TransitionRoot } from '@headlessui/vue'
|
|
35
|
+
import { LoadingStore } from './LoadingStore'
|
|
36
|
+
|
|
37
|
+
// ダイアログ用 state を購読
|
|
38
|
+
const dialogStore = LoadingStore()
|
|
39
|
+
// ストアの状態とアクションをコンポーネントにマッピング
|
|
40
|
+
const isOpen = dialogStore.isOpen;
|
|
41
|
+
const title = dialogStore.getTitle;
|
|
42
|
+
const contentText = dialogStore.getContentText;
|
|
43
|
+
const persistent = dialogStore.getPersistent
|
|
44
|
+
|
|
45
|
+
// ダイアログを閉じる
|
|
46
|
+
function close({ isConfirmed }: { isConfirmed: boolean | null }) {
|
|
47
|
+
|
|
48
|
+
// 回答が強制されていて、ボタンを押してない場合はダイアログを閉じさせない
|
|
49
|
+
if (persistent.value == false || isConfirmed != null) {
|
|
50
|
+
dialogStore.close({ isConfirmed: isConfirmed })
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
</script>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// Replace vue3 with vue if you are using Storybook for Vue 2
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/vue3';
|
|
3
|
+
import Menu from './Menu.vue';
|
|
4
|
+
import { ChevronDownIcon } from '@heroicons/vue/20/solid'
|
|
5
|
+
|
|
6
|
+
type MenuProps = InstanceType<typeof Menu>['$props']
|
|
7
|
+
|
|
8
|
+
const meta: Meta<typeof Menu> = {
|
|
9
|
+
component: Menu,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export default meta;
|
|
13
|
+
type Story = StoryObj<typeof Menu>;
|
|
14
|
+
|
|
15
|
+
const menuItems = [
|
|
16
|
+
{ name: 'Analytics', onClick: () => console.log('click.') },
|
|
17
|
+
{ name: 'Engagement', href: '#' },
|
|
18
|
+
{ name: 'Security', href: '#' },
|
|
19
|
+
{ name: 'Integrations', href: '#' },
|
|
20
|
+
{ name: 'Automations', href: '#' },
|
|
21
|
+
{ name: 'Reports', href: '#' },
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
/*
|
|
25
|
+
*👇 Render functions are a framework specific feature to allow you control on how the component renders.
|
|
26
|
+
* See https://storybook.js.org/docs/api/csf
|
|
27
|
+
* to learn how to use render functions.
|
|
28
|
+
*/
|
|
29
|
+
export const Primary: Story = {
|
|
30
|
+
render: (args: MenuProps) => ({
|
|
31
|
+
setup() {
|
|
32
|
+
return {
|
|
33
|
+
...args
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
components: { Menu, ChevronDownIcon },
|
|
37
|
+
template: `
|
|
38
|
+
<div class="text-center w-full">
|
|
39
|
+
<Menu :items="items" menuClass="right-0" :classes="classes">
|
|
40
|
+
<span>Solutions</span>
|
|
41
|
+
<ChevronDownIcon class="h-5 w-5" aria-hidden="true" />
|
|
42
|
+
</Menu>
|
|
43
|
+
</div>`,
|
|
44
|
+
}),
|
|
45
|
+
args: {
|
|
46
|
+
items: menuItems,
|
|
47
|
+
classes: {
|
|
48
|
+
title: {
|
|
49
|
+
text: {
|
|
50
|
+
color: 'text-onSurface dark:text-onSurface-dark'
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
<!-- eslint-disable vue/multi-word-component-names -->
|
|
2
|
+
<template>
|
|
3
|
+
<Popover class="relative">
|
|
4
|
+
<PopoverButton
|
|
5
|
+
:class="[
|
|
6
|
+
mergedClasses.title?.layout,
|
|
7
|
+
mergedClasses.title?.text?.color,
|
|
8
|
+
mergedClasses.title?.text?.size,
|
|
9
|
+
mergedClasses.title?.text?.layout,
|
|
10
|
+
mergedClasses.title?.text?.base
|
|
11
|
+
]">
|
|
12
|
+
<slot />
|
|
13
|
+
</PopoverButton>
|
|
14
|
+
|
|
15
|
+
<transition enter-active-class="transition ease-out duration-200" enter-from-class="opacity-0 translate-y-1" enter-to-class="opacity-100 translate-y-0" leave-active-class="transition ease-in duration-150" leave-from-class="opacity-100 translate-y-0" leave-to-class="opacity-0 translate-y-1">
|
|
16
|
+
<PopoverPanel
|
|
17
|
+
v-slot="{ close }"
|
|
18
|
+
:class="[
|
|
19
|
+
menuClass,
|
|
20
|
+
mergedClasses.content?.base,
|
|
21
|
+
mergedClasses.content?.layout,
|
|
22
|
+
mergedClasses.content?.rounded,
|
|
23
|
+
mergedClasses.content?.backgroundColor,
|
|
24
|
+
mergedClasses.content?.text?.color,
|
|
25
|
+
mergedClasses.content?.text?.size,
|
|
26
|
+
mergedClasses.content?.text?.layout,
|
|
27
|
+
mergedClasses.content?.text?.base
|
|
28
|
+
]"
|
|
29
|
+
>
|
|
30
|
+
<div v-for="item in items" :key="item.name">
|
|
31
|
+
<p v-if="item.onClick" class="block p-2 hover:text-indigo-600" @click="clickItem(close, item.onClick)">{{ item.name }}</p>
|
|
32
|
+
<a v-else :href="item.href" class="block p-2 hover:text-indigo-600">{{ item.name }}</a>
|
|
33
|
+
</div>
|
|
34
|
+
</PopoverPanel>
|
|
35
|
+
</transition>
|
|
36
|
+
</Popover>
|
|
37
|
+
</template>
|
|
38
|
+
|
|
39
|
+
<script setup lang="ts">
|
|
40
|
+
import { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue'
|
|
41
|
+
import type { PropType } from 'vue';
|
|
42
|
+
import type { MenuOption } from './MenuOption';
|
|
43
|
+
import type { ClassObject } from '../../types/ClassObject';
|
|
44
|
+
import { deepMergeClassObject } from '../../util';
|
|
45
|
+
|
|
46
|
+
const props = defineProps({
|
|
47
|
+
items: {
|
|
48
|
+
type: Array as PropType<MenuOption[]>,
|
|
49
|
+
required: true
|
|
50
|
+
},
|
|
51
|
+
menuClass: {
|
|
52
|
+
type: String as PropType<string>,
|
|
53
|
+
default: ''
|
|
54
|
+
},
|
|
55
|
+
classes: {
|
|
56
|
+
type: Object as PropType<ClassObject>,
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const defaultClasses: ClassObject = {
|
|
61
|
+
title: {
|
|
62
|
+
text: {
|
|
63
|
+
base: 'font-semibold',
|
|
64
|
+
color: 'text-gray-900',
|
|
65
|
+
size: 'text-sm',
|
|
66
|
+
layout: 'leading-6'
|
|
67
|
+
},
|
|
68
|
+
layout: 'inline-flex items-center gap-x-1'
|
|
69
|
+
},
|
|
70
|
+
content: {
|
|
71
|
+
text: {
|
|
72
|
+
base: 'font-semibold',
|
|
73
|
+
color: 'text-gray-900',
|
|
74
|
+
size: 'text-sm',
|
|
75
|
+
layout: 'leading-6'
|
|
76
|
+
},
|
|
77
|
+
layout: 'absolute',
|
|
78
|
+
rounded: 'rounded-xl',
|
|
79
|
+
backgroundColor: 'bg-white',
|
|
80
|
+
base: 'z-10 mt-1 w-56 shrink p-4 shadow-lg ring-1 ring-gray-900/5 top-full'
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// props.classesが渡されていない場合、defaultClassesを使用する
|
|
85
|
+
const mergedClasses = props.classes ? deepMergeClassObject(defaultClasses, props.classes) : defaultClasses;
|
|
86
|
+
|
|
87
|
+
function clickItem(close: any, func?: () => void) {
|
|
88
|
+
|
|
89
|
+
if (func !== undefined) {
|
|
90
|
+
func()
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
close()
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
</script>
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// Replace vue3 with vue if you are using Storybook for Vue 2
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/vue3';
|
|
3
|
+
import NavigationDrawer from './NavigationDrawer.vue';
|
|
4
|
+
import {
|
|
5
|
+
HomeIcon,
|
|
6
|
+
BellAlertIcon,
|
|
7
|
+
ChatBubbleBottomCenterTextIcon,
|
|
8
|
+
Cog6ToothIcon,
|
|
9
|
+
CurrencyYenIcon,
|
|
10
|
+
CubeIcon,
|
|
11
|
+
BookOpenIcon,
|
|
12
|
+
MinusIcon
|
|
13
|
+
} from '@heroicons/vue/24/outline'
|
|
14
|
+
|
|
15
|
+
type NavigationDrawerProps = InstanceType<typeof NavigationDrawer>['$props']
|
|
16
|
+
|
|
17
|
+
const meta: Meta<typeof NavigationDrawer> = {
|
|
18
|
+
component: NavigationDrawer,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default meta;
|
|
22
|
+
type Story = StoryObj<typeof NavigationDrawer>;
|
|
23
|
+
|
|
24
|
+
/*
|
|
25
|
+
*👇 Render functions are a framework specific feature to allow you control on how the component renders.
|
|
26
|
+
* See https://storybook.js.org/docs/api/csf
|
|
27
|
+
* to learn how to use render functions.
|
|
28
|
+
*/
|
|
29
|
+
export const Primary: Story = {
|
|
30
|
+
render: (args: NavigationDrawerProps) => ({
|
|
31
|
+
setup() {
|
|
32
|
+
return {
|
|
33
|
+
...args
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
components: { NavigationDrawer },
|
|
37
|
+
template: '<NavigationDrawer :title="title" :navigationTop="navigationTop" :navigationBottom="navigationBottom"></NavigationDrawer>',
|
|
38
|
+
}),
|
|
39
|
+
args: {
|
|
40
|
+
title: 'ダッシュボード',
|
|
41
|
+
navigationTop: [
|
|
42
|
+
{ name: 'メニュー', href: '/menu', onClick: undefined, icon: HomeIcon, current: false },
|
|
43
|
+
{ name: 'リクエスト', href: '/request', icon: CubeIcon, current: false },
|
|
44
|
+
{ name: '履歴', href: '/history', icon: BookOpenIcon, current: false },
|
|
45
|
+
{ name: '支払い', href: '/payment', icon: CurrencyYenIcon, current: false, isOpen: true,
|
|
46
|
+
subItems: [
|
|
47
|
+
{ name: '現金', href: '/payment', icon: MinusIcon, current: false },
|
|
48
|
+
{ name: 'クレジット', href: '/payment', icon: MinusIcon, current: false }
|
|
49
|
+
]
|
|
50
|
+
},
|
|
51
|
+
{ name: 'メッセージ', href: '/message', icon: BellAlertIcon, current: false },
|
|
52
|
+
{ name: '問い合わせ', href: '/contact', icon: ChatBubbleBottomCenterTextIcon, current: false },
|
|
53
|
+
{ name: '設定', href: '/setting', icon: Cog6ToothIcon, current: false },
|
|
54
|
+
],
|
|
55
|
+
navigationBottom: [
|
|
56
|
+
{ name: 'ログアウト', href: undefined, onClick: () => console.log('click.'), icon: undefined, current: undefined },
|
|
57
|
+
]
|
|
58
|
+
}
|
|
59
|
+
};
|