vanilla-vue-ui 0.0.7 → 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,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 SlideOver from './WSlideOver.vue';
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof SlideOver> = {
|
|
7
|
+
component: SlideOver,
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export default meta;
|
|
11
|
+
type Story = StoryObj<typeof SlideOver>;
|
|
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: { SlideOver },
|
|
21
|
+
template: '<SlideOver isOpen="true"></SlideOver>',
|
|
22
|
+
}),
|
|
23
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<TransitionRoot as="template" :show="isOpen">
|
|
3
|
+
<Dialog as="div" class="relative z-50" @close="close">
|
|
4
|
+
<div class="fixed inset-0" />
|
|
5
|
+
|
|
6
|
+
<div class="fixed inset-0 overflow-hidden">
|
|
7
|
+
<div class="absolute inset-0 overflow-hidden">
|
|
8
|
+
<div class="pointer-events-none fixed inset-y-0 right-0 flex max-w-full pl-10">
|
|
9
|
+
<TransitionChild as="template" enter="transform transition ease-in-out duration-500 sm:duration-700" enter-from="translate-x-full" enter-to="translate-x-0" leave="transform transition ease-in-out duration-500 sm:duration-700" leave-from="translate-x-0" leave-to="translate-x-full">
|
|
10
|
+
<DialogPanel class="pointer-events-auto w-screen max-w-md">
|
|
11
|
+
<div class="flex h-full flex-col overflow-y-scroll bg-white py-6 shadow-xl">
|
|
12
|
+
<div class="px-4 sm:px-6">
|
|
13
|
+
<div class="flex items-start justify-end">
|
|
14
|
+
<div class="ml-3 flex h-7 items-center">
|
|
15
|
+
<button type="button" class="relative rounded-md bg-white text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2" @click="close">
|
|
16
|
+
<span class="absolute -inset-2.5" />
|
|
17
|
+
<span class="sr-only">Close panel</span>
|
|
18
|
+
<XMarkIcon class="h-6 w-6" aria-hidden="true" />
|
|
19
|
+
</button>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
<slot />
|
|
23
|
+
</div>
|
|
24
|
+
<div class="relative mt-6 flex-1 px-4 sm:px-6"/>
|
|
25
|
+
</div>
|
|
26
|
+
</DialogPanel>
|
|
27
|
+
</TransitionChild>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
</Dialog>
|
|
32
|
+
</TransitionRoot>
|
|
33
|
+
</template>
|
|
34
|
+
|
|
35
|
+
<script setup lang="ts">
|
|
36
|
+
import { Dialog, DialogPanel, TransitionChild, TransitionRoot } from '@headlessui/vue'
|
|
37
|
+
import { XMarkIcon } from '@heroicons/vue/24/outline'
|
|
38
|
+
import { defineProps, defineEmits } from 'vue'
|
|
39
|
+
|
|
40
|
+
defineProps({
|
|
41
|
+
isOpen: Boolean
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
// const open = ref(true)
|
|
45
|
+
|
|
46
|
+
// イベントの定義
|
|
47
|
+
const emit = defineEmits(['update:isOpen'])
|
|
48
|
+
|
|
49
|
+
const close = () => {
|
|
50
|
+
// Emit an event to notify parent component to change the state
|
|
51
|
+
emit('update:isOpen', false)
|
|
52
|
+
}
|
|
53
|
+
</script>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Replace vue3 with vue if you are using Storybook for Vue 2
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/vue3';
|
|
3
|
+
import Step from './Step.vue';
|
|
4
|
+
import type { StepContent } from './StepContent'
|
|
5
|
+
|
|
6
|
+
type StepProps = InstanceType<typeof Step>['$props']
|
|
7
|
+
|
|
8
|
+
const meta: Meta<typeof Step> = {
|
|
9
|
+
component: Step,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export default meta;
|
|
13
|
+
type Story = StoryObj<typeof Step>;
|
|
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
|
+
|
|
21
|
+
const steps: StepContent[] = [
|
|
22
|
+
{ number: 1, status: 'Complete' },
|
|
23
|
+
{ number: 2, status: 'Complete' },
|
|
24
|
+
{ number: 3, status: 'Complete' },
|
|
25
|
+
{ number: 4, status: 'Current' },
|
|
26
|
+
{ number: 5, status: 'Upcoming' },
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
export const Primary: Story = {
|
|
30
|
+
render: (args: StepProps) => ({
|
|
31
|
+
setup() {
|
|
32
|
+
return {
|
|
33
|
+
...args,
|
|
34
|
+
};
|
|
35
|
+
},
|
|
36
|
+
components: { Step },
|
|
37
|
+
template: '<Step :steps="steps"></Step>',
|
|
38
|
+
}),
|
|
39
|
+
args: {
|
|
40
|
+
steps: steps
|
|
41
|
+
},
|
|
42
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<!-- eslint-disable vue/multi-word-component-names -->
|
|
2
|
+
<template>
|
|
3
|
+
<div v-if="steps" class="flex justify-center">
|
|
4
|
+
<nav aria-label="Progress">
|
|
5
|
+
<ol role="list" class="flex items-center">
|
|
6
|
+
<li v-for="(step, stepIdx) in steps" :key="step.number" :class="[stepIdx !== steps.length - 1 ? 'pr-8 sm:pr-20' : '', 'relative']">
|
|
7
|
+
<template v-if="step.status === 'Complete'">
|
|
8
|
+
<div class="absolute inset-0 flex items-center" aria-hidden="true">
|
|
9
|
+
<div class="h-0.5 w-full bg-indigo-600" />
|
|
10
|
+
</div>
|
|
11
|
+
<div class="relative flex h-8 w-8 items-center justify-center rounded-full bg-indigo-600 hover:bg-indigo-900">
|
|
12
|
+
<CheckIcon class="h-5 w-5 text-white" aria-hidden="true" />
|
|
13
|
+
</div>
|
|
14
|
+
</template>
|
|
15
|
+
<template v-else-if="step.status === 'Current'">
|
|
16
|
+
<div class="absolute inset-0 flex items-center" aria-hidden="true">
|
|
17
|
+
<div class="h-0.5 w-full bg-gray-200" />
|
|
18
|
+
</div>
|
|
19
|
+
<div class="relative flex h-8 w-8 items-center justify-center rounded-full border-2 border-indigo-600 bg-white" aria-current="step">
|
|
20
|
+
<span class="h-2.5 w-2.5 rounded-full bg-indigo-600" aria-hidden="true" />
|
|
21
|
+
</div>
|
|
22
|
+
</template>
|
|
23
|
+
<template v-else>
|
|
24
|
+
<div class="absolute inset-0 flex items-center" aria-hidden="true">
|
|
25
|
+
<div class="h-0.5 w-full bg-gray-200" />
|
|
26
|
+
</div>
|
|
27
|
+
<div class="group relative flex h-8 w-8 items-center justify-center rounded-full border-2 border-gray-300 bg-white hover:border-gray-400">
|
|
28
|
+
<span class="h-2.5 w-2.5 rounded-full bg-transparent group-hover:bg-gray-300" aria-hidden="true" />
|
|
29
|
+
</div>
|
|
30
|
+
</template>
|
|
31
|
+
</li>
|
|
32
|
+
</ol>
|
|
33
|
+
</nav>
|
|
34
|
+
</div>
|
|
35
|
+
</template>
|
|
36
|
+
|
|
37
|
+
<script setup lang="ts">
|
|
38
|
+
import { defineProps, type PropType } from 'vue'
|
|
39
|
+
import { CheckIcon } from '@heroicons/vue/20/solid'
|
|
40
|
+
import type { StepContent } from './StepContent';
|
|
41
|
+
|
|
42
|
+
defineProps({
|
|
43
|
+
steps: {
|
|
44
|
+
type: Array as PropType<StepContent[]>,
|
|
45
|
+
require: true
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
</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 Table from './Table.vue';
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof Table> = {
|
|
7
|
+
component: Table,
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export default meta;
|
|
11
|
+
type Story = StoryObj<typeof Table>;
|
|
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: { Table },
|
|
21
|
+
template: '<Table></Table>',
|
|
22
|
+
}),
|
|
23
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<!-- eslint-disable vue/multi-word-component-names -->
|
|
2
|
+
<template>
|
|
3
|
+
<div>
|
|
4
|
+
<div class="mt-6 border-t border-gray-100">
|
|
5
|
+
<dl class="divide-y divide-gray-100">
|
|
6
|
+
<div v-for="(record, i) in data" :key="i" class="px-4 py-6 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
|
|
7
|
+
<dt class="text-sm font-medium leading-6 text-gray-900">{{ record.title }}</dt>
|
|
8
|
+
<dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">{{ record.content }}</dd>
|
|
9
|
+
</div>
|
|
10
|
+
</dl>
|
|
11
|
+
</div>
|
|
12
|
+
</div>
|
|
13
|
+
</template>
|
|
14
|
+
|
|
15
|
+
<script setup lang="ts">
|
|
16
|
+
|
|
17
|
+
interface Record {
|
|
18
|
+
title: string;
|
|
19
|
+
content: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const data: Record[] = [{
|
|
23
|
+
title: 'data1',
|
|
24
|
+
content: 'content1'
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
title: 'data2',
|
|
28
|
+
content: 'content2'
|
|
29
|
+
}]
|
|
30
|
+
</script>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { mount } from '@vue/test-utils';
|
|
3
|
+
import TextArea from './WTextArea.vue'; // コンポーネントのパスを適切に設定してください
|
|
4
|
+
|
|
5
|
+
describe('TextArea', () => {
|
|
6
|
+
it('renders correctly', () => {
|
|
7
|
+
const wrapper = mount(TextArea);
|
|
8
|
+
|
|
9
|
+
// コンポーネントが正しくレンダリングされるか確認
|
|
10
|
+
expect(wrapper.find('div.overflow-hidden').exists()).toBe(true);
|
|
11
|
+
|
|
12
|
+
// テキストエリアが存在し、正しい属性を持つことを確認
|
|
13
|
+
const textarea = wrapper.find('textarea[name="comment"]');
|
|
14
|
+
expect(textarea.exists()).toBe(true);
|
|
15
|
+
expect(textarea.attributes('rows')).toBe('3');
|
|
16
|
+
expect(textarea.attributes('placeholder')).toBe('Add your comment...');
|
|
17
|
+
});
|
|
18
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Replace vue3 with vue if you are using Storybook for Vue 2
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/vue3';
|
|
3
|
+
import TextArea from './WTextArea.vue';
|
|
4
|
+
|
|
5
|
+
type TextAreaProps = InstanceType<typeof TextArea>['$props']
|
|
6
|
+
|
|
7
|
+
const meta: Meta<typeof TextArea> = {
|
|
8
|
+
component: TextArea,
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export default meta;
|
|
12
|
+
type Story = StoryObj<typeof TextArea>;
|
|
13
|
+
|
|
14
|
+
/*
|
|
15
|
+
*👇 Render functions are a fSamework 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: (args: TextAreaProps) => ({
|
|
21
|
+
setup() {
|
|
22
|
+
return {
|
|
23
|
+
...args
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
components: { TextArea },
|
|
27
|
+
template: `
|
|
28
|
+
<div class="p-4">
|
|
29
|
+
<div class="mb-2"><TextArea size="xs"></TextArea></div>
|
|
30
|
+
<div class="mb-2"><TextArea size="sm"></TextArea></div>
|
|
31
|
+
<div class="mb-2"><TextArea size="base"></TextArea></div>
|
|
32
|
+
<div class="mb-2"><TextArea size="lg"></TextArea></div>
|
|
33
|
+
<div class="mb-2"><TextArea size="2xl"></TextArea></div>
|
|
34
|
+
<div class="mb-2"><TextArea size="3xl"></TextArea></div>
|
|
35
|
+
<div class="mb-2"><TextArea size="6xl"></TextArea></div>
|
|
36
|
+
</div>
|
|
37
|
+
`,
|
|
38
|
+
}),
|
|
39
|
+
args: {
|
|
40
|
+
}
|
|
41
|
+
};
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="overflow-hidden px-4 py-4 rounded-lg ring-1 ring-inset ring-gray-300 focus-within:ring-2 focus-within:ring-primary dark:focus-within:ring-primary-dark bg-surface dark:bg-surface-dark">
|
|
3
|
+
<label :for="name" class="sr-only">{{ label }}</label>
|
|
4
|
+
<textarea
|
|
5
|
+
:id="id"
|
|
6
|
+
:name="name"
|
|
7
|
+
:rows=String(rows)
|
|
8
|
+
:value="value"
|
|
9
|
+
:class="[
|
|
10
|
+
textSize,
|
|
11
|
+
mergedClasses.content?.input?.base,
|
|
12
|
+
mergedClasses.content?.input?.backgroundColor,
|
|
13
|
+
mergedClasses.content?.input?.color,
|
|
14
|
+
]"
|
|
15
|
+
:placeholder="placeholder"
|
|
16
|
+
:required="required"
|
|
17
|
+
@change="changeValue($event)"
|
|
18
|
+
@input="inputValue($event)"
|
|
19
|
+
/>
|
|
20
|
+
</div>
|
|
21
|
+
<p v-if="errorMassage.length > 0" class="mt-1 ml-1 text-sm text-red-600">{{ errorMassage }}</p>
|
|
22
|
+
</template>
|
|
23
|
+
|
|
24
|
+
<script setup lang="ts">
|
|
25
|
+
import { ref, defineProps, type PropType } from 'vue'
|
|
26
|
+
import type { TextAreaSize } from './TextAreaSize'
|
|
27
|
+
import type { ClassObject } from '../../types/ClassObject';
|
|
28
|
+
import { deepMergeClassObject } from '../../util';
|
|
29
|
+
|
|
30
|
+
const props = defineProps({
|
|
31
|
+
value: {
|
|
32
|
+
type: String as PropType<string>
|
|
33
|
+
},
|
|
34
|
+
label: {
|
|
35
|
+
type: String as PropType<string>
|
|
36
|
+
},
|
|
37
|
+
placeholder: {
|
|
38
|
+
type: String as PropType<string>
|
|
39
|
+
},
|
|
40
|
+
type: {
|
|
41
|
+
type: String as PropType<string>
|
|
42
|
+
},
|
|
43
|
+
autocomplete: {
|
|
44
|
+
type: String as PropType<string>
|
|
45
|
+
},
|
|
46
|
+
required: {
|
|
47
|
+
type: Boolean as PropType<boolean>
|
|
48
|
+
},
|
|
49
|
+
rules: {
|
|
50
|
+
type: Array as PropType<ValidationRule[]>
|
|
51
|
+
},
|
|
52
|
+
rows: {
|
|
53
|
+
type: Number as PropType<number>,
|
|
54
|
+
default: 3
|
|
55
|
+
},
|
|
56
|
+
size: {
|
|
57
|
+
type: String as PropType<TextAreaSize>,
|
|
58
|
+
default: 'base',
|
|
59
|
+
},
|
|
60
|
+
name: {
|
|
61
|
+
type: String as PropType<string>
|
|
62
|
+
},
|
|
63
|
+
id: {
|
|
64
|
+
type: String as PropType<string>
|
|
65
|
+
},
|
|
66
|
+
classes: {
|
|
67
|
+
type: Object as PropType<ClassObject>,
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const defaultClasses: ClassObject = {
|
|
72
|
+
content: {
|
|
73
|
+
input: {
|
|
74
|
+
base: 'block w-full resize-none border-0 placeholder:text-gray-400 focus:outline-none focus:ring-0',
|
|
75
|
+
backgroundColor: 'bg-surface dark:bg-surface-dark',
|
|
76
|
+
color: 'text-onSurface dark:text-onSurface-dark'
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// props.classesが渡されていない場合、defaultClassesを使用する
|
|
82
|
+
const mergedClasses = props.classes ? deepMergeClassObject(defaultClasses, props.classes) : defaultClasses;
|
|
83
|
+
|
|
84
|
+
const textSize = ref('text-' + props.size)
|
|
85
|
+
|
|
86
|
+
// emit を定義
|
|
87
|
+
const emit = defineEmits<{
|
|
88
|
+
change: [value: Event],
|
|
89
|
+
input: [value: Event]
|
|
90
|
+
}>()
|
|
91
|
+
|
|
92
|
+
const errorMassage = ref("")
|
|
93
|
+
|
|
94
|
+
type ValidationRule = (value: string) => boolean | string
|
|
95
|
+
|
|
96
|
+
//
|
|
97
|
+
// Sample
|
|
98
|
+
//
|
|
99
|
+
// let rules: ValidationRule[] = [
|
|
100
|
+
// value => !!value || 'Required.', // required
|
|
101
|
+
// value => value.length <= 20 || 'Max 20 characters', // max
|
|
102
|
+
// value => {
|
|
103
|
+
// const pattern = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
|
|
104
|
+
// return pattern.test(value) || 'Invalid e-mail.'
|
|
105
|
+
// }, // email
|
|
106
|
+
// ]
|
|
107
|
+
|
|
108
|
+
// 初回バリデーション
|
|
109
|
+
if (props.value) {
|
|
110
|
+
const setupErrorMessage = validate(props.value)
|
|
111
|
+
if (setupErrorMessage as boolean !== true) {
|
|
112
|
+
errorMassage.value = setupErrorMessage as string
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// input の変更を受け取る
|
|
117
|
+
function changeValue(event: Event) {
|
|
118
|
+
const target = event.target as HTMLInputElement | null
|
|
119
|
+
if (target) {
|
|
120
|
+
const validationResult = validate(target.value)
|
|
121
|
+
if (validationResult === true) {
|
|
122
|
+
// バリデーションに通ったら
|
|
123
|
+
emit('change', event)
|
|
124
|
+
errorMassage.value = ""; // エラーメッセージをクリア
|
|
125
|
+
} else {
|
|
126
|
+
errorMassage.value = validationResult as string; // エラーメッセージを設定
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function inputValue(event: Event) {
|
|
132
|
+
const target = event.target as HTMLInputElement | null
|
|
133
|
+
if (target) {
|
|
134
|
+
const validationResult = validate(target.value)
|
|
135
|
+
if (validationResult === true) {
|
|
136
|
+
// バリデーションに通ったら
|
|
137
|
+
emit('input', event)
|
|
138
|
+
errorMassage.value = ""; // エラーメッセージをクリア
|
|
139
|
+
} else {
|
|
140
|
+
errorMassage.value = validationResult as string; // エラーメッセージを設定
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// バリデーション
|
|
146
|
+
function validate(value: string): boolean | string {
|
|
147
|
+
if (props.rules) {
|
|
148
|
+
for (const rule of props.rules) {
|
|
149
|
+
const result = rule(value);
|
|
150
|
+
if (result !== true) {
|
|
151
|
+
// ルールが文字列(エラーメッセージ)を返した場合、バリデーション失敗
|
|
152
|
+
return result;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return true; // すべてのルールが成功
|
|
157
|
+
}
|
|
158
|
+
</script>
|
package/package.json
CHANGED