pimelon-ui 0.1.0-alpha.9 → 0.1.5

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.
Files changed (49) hide show
  1. package/package.json +6 -13
  2. package/src/components/Autocomplete.story.vue +25 -0
  3. package/src/components/Autocomplete.vue +38 -22
  4. package/src/components/Avatar.story.vue +28 -0
  5. package/src/components/Badge.story.vue +26 -0
  6. package/src/components/Button.story.vue +31 -0
  7. package/src/components/Checkbox.story.vue +22 -0
  8. package/src/components/Checkbox.vue +4 -1
  9. package/src/components/CommandPalette/CommandPalette.vue +110 -0
  10. package/src/components/CommandPalette/CommandPaletteItem.vue +28 -0
  11. package/src/components/Dialog.story.vue +39 -0
  12. package/src/components/Dropdown.story.vue +98 -0
  13. package/src/components/ErrorMessage.story.vue +28 -0
  14. package/src/components/FileUploader.story.vue +34 -0
  15. package/src/components/FormControl.story.vue +101 -0
  16. package/src/components/ListView/ListRow.vue +29 -0
  17. package/src/components/ListView/ListRowItem.vue +10 -0
  18. package/src/components/ListView/ListView.vue +116 -0
  19. package/src/components/Popover.story.vue +29 -0
  20. package/src/components/Progress.story.vue +34 -0
  21. package/src/components/Select.story.vue +21 -0
  22. package/src/components/Select.vue +1 -0
  23. package/src/components/Spinner.story.vue +14 -0
  24. package/src/components/Switch.story.vue +28 -0
  25. package/src/components/Switch.vue +11 -15
  26. package/src/components/TabButtons.vue +54 -0
  27. package/src/components/TextInput.story.vue +81 -0
  28. package/src/components/TextInput.vue +8 -2
  29. package/src/components/Textarea.vue +1 -0
  30. package/src/components/Tooltip.story.vue +11 -0
  31. package/src/env.d.ts +2 -0
  32. package/src/index.js +6 -0
  33. package/src/resources/documentResource.js +35 -2
  34. package/src/resources/listResource.js +5 -1
  35. package/src/style.css +6 -0
  36. package/src/utils/fileUploadHandler.ts +62 -57
  37. package/src/utils/{melonRequest.js → frappeRequest.js} +2 -9
  38. package/src/components/Avatar.stories.ts +0 -110
  39. package/src/components/Badge.stories.js +0 -149
  40. package/src/components/Button.stories.js +0 -173
  41. package/src/components/Divider.stories.ts +0 -110
  42. package/src/components/Dropdown.stories.ts +0 -73
  43. package/src/components/Progress.stories.js +0 -80
  44. package/src/components/Spinner.stories.ts +0 -13
  45. package/src/components/Switch.stories.js +0 -52
  46. package/src/components/TextInput.stories.ts +0 -143
  47. package/src/tokens/Color.vue +0 -194
  48. package/src/utils/tailwind.config.stories.js +0 -8
  49. package/src/vite-env.d.ts +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pimelon-ui",
3
- "version": "0.1.0-alpha.9",
3
+ "version": "0.1.5",
4
4
  "description": "A set of components and utilities for rapid UI development",
5
5
  "main": "./src/index.js",
6
6
  "scripts": {
@@ -14,8 +14,9 @@
14
14
  "dev": "vite",
15
15
  "build": "vite build",
16
16
  "preview": "vite preview",
17
- "storybook": "storybook dev -p 6006",
18
- "build-storybook": "storybook build"
17
+ "story:dev": "histoire dev",
18
+ "story:build": "histoire build",
19
+ "story:preview": "histoire preview"
19
20
  },
20
21
  "files": [
21
22
  "src",
@@ -60,24 +61,16 @@
60
61
  "vue-router": "^4.1.6"
61
62
  },
62
63
  "devDependencies": {
63
- "@storybook/addon-essentials": "^7.0.0-beta.61",
64
- "@storybook/addon-interactions": "^7.0.0-beta.61",
65
- "@storybook/addon-links": "^7.0.0-beta.61",
66
- "@storybook/blocks": "^7.0.0-alpha.8",
67
- "@storybook/testing-library": "^0.0.14-next.1",
68
- "@storybook/vue3": "^7.0.0-beta.61",
69
- "@storybook/vue3-vite": "^7.0.0-beta.61",
64
+ "@histoire/plugin-vue": "^0.16.1",
70
65
  "@vitejs/plugin-vue": "^4.0.0",
71
66
  "autoprefixer": "^10.4.13",
72
67
  "cross-fetch": "^3.1.5",
68
+ "histoire": "^0.16.2",
73
69
  "husky": "^8.0.3",
74
70
  "lint-staged": ">=10",
75
71
  "postcss": "^8.4.21",
76
72
  "prettier": "2.7.1",
77
73
  "prettier-plugin-tailwindcss": "^0.1.13",
78
- "react": "^18.2.0",
79
- "react-dom": "^18.2.0",
80
- "storybook": "^7.0.0-beta.61",
81
74
  "tailwindcss": "^3.2.7",
82
75
  "typescript": "^5.0.2",
83
76
  "vite": "^4.1.0",
@@ -0,0 +1,25 @@
1
+ <script setup lang="ts">
2
+ import { ref } from 'vue'
3
+ import Autocomplete from './Autocomplete.vue'
4
+
5
+ const value = ref('')
6
+ const options = [
7
+ { label: 'John Doe', value: 'john-doe' },
8
+ { label: 'Jane Doe', value: 'jane-doe' },
9
+ { label: 'John Smith', value: 'john-smith' },
10
+ { label: 'Jane Smith', value: 'jane-smith' },
11
+ { label: 'John Wayne', value: 'john-wayne' },
12
+ { label: 'Jane Wayne', value: 'jane-wayne' },
13
+ ]
14
+ </script>
15
+ <template>
16
+ <Story :layout="{ width: 500, type: 'grid' }" autoPropsDisabled>
17
+ <div class="p-2">
18
+ <Autocomplete
19
+ :options="options"
20
+ v-model="value"
21
+ placeholder="Select person"
22
+ />
23
+ </div>
24
+ </Story>
25
+ </template>
@@ -2,28 +2,33 @@
2
2
  <Combobox v-model="selectedValue" nullable v-slot="{ open: isComboboxOpen }">
3
3
  <Popover class="w-full" v-model:show="showOptions">
4
4
  <template #target="{ open: openPopover, togglePopover }">
5
- <div class="w-full">
6
- <button
7
- class="flex h-7 w-full items-center justify-between rounded bg-gray-100 py-1 pl-3 pr-2 transition-colors hover:bg-gray-200 focus:ring-2 focus:ring-gray-400"
8
- :class="{ 'bg-gray-200': isComboboxOpen }"
9
- @click="() => togglePopover()"
10
- >
11
- <span
12
- class="overflow-hidden text-ellipsis whitespace-nowrap text-base leading-5"
13
- v-if="selectedValue"
5
+ <slot name="target" v-bind="{ open: openPopover, togglePopover }">
6
+ <div class="w-full">
7
+ <button
8
+ class="flex h-7 w-full items-center justify-between gap-2 rounded bg-gray-100 px-2 py-1 transition-colors hover:bg-gray-200 focus:ring-2 focus:ring-gray-400"
9
+ :class="{ 'bg-gray-200': isComboboxOpen }"
10
+ @click="() => togglePopover()"
14
11
  >
15
- {{ displayValue(selectedValue) }}
16
- </span>
17
- <span class="text-base leading-5 text-gray-500" v-else>
18
- {{ placeholder || '' }}
19
- </span>
20
- <FeatherIcon
21
- name="chevron-down"
22
- class="h-4 w-4 text-gray-500"
23
- aria-hidden="true"
24
- />
25
- </button>
26
- </div>
12
+ <div class="flex items-center">
13
+ <slot name="prefix" />
14
+ <span
15
+ class="overflow-hidden text-ellipsis whitespace-nowrap text-base leading-5"
16
+ v-if="selectedValue"
17
+ >
18
+ {{ displayValue(selectedValue) }}
19
+ </span>
20
+ <span class="text-base leading-5 text-gray-500" v-else>
21
+ {{ placeholder || '' }}
22
+ </span>
23
+ </div>
24
+ <FeatherIcon
25
+ name="chevron-down"
26
+ class="h-4 w-4 text-gray-600"
27
+ aria-hidden="true"
28
+ />
29
+ </button>
30
+ </div>
31
+ </slot>
27
32
  </template>
28
33
  <template #body="{ isOpen }">
29
34
  <div v-show="isOpen">
@@ -36,6 +41,7 @@
36
41
  >
37
42
  <div class="relative w-full">
38
43
  <ComboboxInput
44
+ ref="search"
39
45
  class="form-input w-full"
40
46
  type="text"
41
47
  @change="
@@ -80,7 +86,10 @@
80
86
  { 'bg-gray-100': active },
81
87
  ]"
82
88
  >
83
- <slot name="prefix" v-bind="{ active, selected, option }" />
89
+ <slot
90
+ name="item-prefix"
91
+ v-bind="{ active, selected, option }"
92
+ />
84
93
  {{ option.label }}
85
94
  </li>
86
95
  </ComboboxOption>
@@ -168,6 +177,13 @@ export default {
168
177
  query(q) {
169
178
  this.$emit('update:query', q)
170
179
  },
180
+ showOptions(val) {
181
+ if (val) {
182
+ this.$nextTick(() => {
183
+ this.$refs.search.el.focus()
184
+ })
185
+ }
186
+ },
171
187
  },
172
188
  methods: {
173
189
  filterOptions(options) {
@@ -0,0 +1,28 @@
1
+ <script setup lang="ts">
2
+ import { reactive } from 'vue'
3
+ import Avatar from './Avatar.vue'
4
+ const state = reactive({
5
+ image: 'https://avatars.githubusercontent.com/u/499550?s=60&v=4',
6
+ label: 'EY',
7
+ size: 'md',
8
+ })
9
+
10
+ const shapes = ['circle', 'square']
11
+ const sizes = ['xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl']
12
+ </script>
13
+
14
+ <template>
15
+ <Story :layout="{ type: 'grid', width: 300 }">
16
+ <Variant v-for="shape in shapes" :key="shape" :title="shape">
17
+ <Avatar :shape="shape" v-bind="state" />
18
+ </Variant>
19
+ <Variant v-for="shape in shapes" :key="shape" :title="shape">
20
+ <Avatar :shape="shape" v-bind="state" :image="null" />
21
+ </Variant>
22
+
23
+ <template #controls>
24
+ <HstText v-model="state.label" title="Label" />
25
+ <HstSelect v-model="state.size" :options="sizes" title="Size" />
26
+ </template>
27
+ </Story>
28
+ </template>
@@ -0,0 +1,26 @@
1
+ <script setup lang="ts">
2
+ import { reactive } from 'vue'
3
+ import Badge from './Badge.vue'
4
+ const state = reactive({
5
+ theme: 'gray',
6
+ size: 'sm',
7
+ label: 'Badge',
8
+ })
9
+ const variants = ['solid', 'subtle', 'outline', 'ghost']
10
+ const themes = ['gray', 'blue', 'green', 'orange', 'red']
11
+ const sizes = ['sm', 'md', 'lg']
12
+ </script>
13
+
14
+ <template>
15
+ <Story :layout="{ type: 'grid', width: 300 }">
16
+ <Variant v-for="variant in variants" :key="variant" :title="variant">
17
+ <Badge :variant="variant" v-bind="state">{{ state.label }}</Badge>
18
+ </Variant>
19
+
20
+ <template #controls>
21
+ <HstText v-model="state.label" title="Content" />
22
+ <HstSelect v-model="state.theme" :options="themes" title="Theme" />
23
+ <HstSelect v-model="state.size" :options="sizes" title="Size" />
24
+ </template>
25
+ </Story>
26
+ </template>
@@ -0,0 +1,31 @@
1
+ <script setup lang="ts">
2
+ import { reactive } from 'vue'
3
+ import Button from './Button.vue'
4
+ const state = reactive({
5
+ theme: 'gray',
6
+ size: 'sm',
7
+ label: 'Button',
8
+ loading: false,
9
+ loadingText: null,
10
+ disabled: false,
11
+ link: null,
12
+ })
13
+ const variants = ['solid', 'subtle', 'outline', 'ghost']
14
+ const themes = ['gray', 'blue', 'green', 'red']
15
+ const sizes = ['sm', 'md', 'lg', 'xl', '2xl']
16
+ </script>
17
+
18
+ <template>
19
+ <Story :layout="{ type: 'grid', width: 300 }">
20
+ <Variant v-for="variant in variants" :key="variant" :title="variant">
21
+ <Button :variant="variant" v-bind="state">{{ state.label }}</Button>
22
+ </Variant>
23
+
24
+ <template #controls>
25
+ <HstText v-model="state.label" title="Content" />
26
+ <HstCheckbox v-model="state.disabled" title="Disabled" />
27
+ <HstSelect v-model="state.theme" :options="themes" title="Theme" />
28
+ <HstSelect v-model="state.size" :options="sizes" title="Size" />
29
+ </template>
30
+ </Story>
31
+ </template>
@@ -0,0 +1,22 @@
1
+ <script setup lang="ts">
2
+ import { reactive } from 'vue'
3
+ import Checkbox from './Checkbox.vue'
4
+
5
+ const state = reactive({
6
+ size: 'sm',
7
+ value: false,
8
+ })
9
+
10
+ const sizes = ['sm', 'md']
11
+ </script>
12
+ <template>
13
+ <Story :layout="{ width: 500, type: 'grid' }">
14
+ <div class="p-2">
15
+ <Checkbox v-bind="state" v-model="state.value" label="Enable feature" />
16
+ </div>
17
+
18
+ <template #controls>
19
+ <HstSelect v-model="state.size" :options="sizes" title="Size" />
20
+ </template>
21
+ </Story>
22
+ </template>
@@ -16,6 +16,7 @@
16
16
  :id="htmlId"
17
17
  :checked="Boolean(modelValue)"
18
18
  @change="(e) => $emit('update:modelValue', (e.target as HTMLInputElement).checked)"
19
+ v-bind="attrs"
19
20
  />
20
21
  <label class="block" :class="labelClasses" v-if="label" :for="htmlId">
21
22
  {{ label }}
@@ -23,7 +24,7 @@
23
24
  </div>
24
25
  </template>
25
26
  <script lang="ts" setup>
26
- import { computed } from 'vue'
27
+ import { computed, useAttrs } from 'vue'
27
28
  import { useId } from '../utils/useId'
28
29
 
29
30
  interface CheckboxProps {
@@ -40,6 +41,8 @@ const props = withDefaults(defineProps<CheckboxProps>(), {
40
41
  padding: false,
41
42
  })
42
43
 
44
+ const attrs = useAttrs()
45
+
43
46
  const htmlId = props.id ?? useId()
44
47
 
45
48
  const labelClasses = computed(() => {
@@ -0,0 +1,110 @@
1
+ <template>
2
+ <template>
3
+ <Dialog
4
+ v-model="show"
5
+ :options="{ size: 'xl', position: 'top' }"
6
+ @after-leave="searchQuery = ''"
7
+ >
8
+ <template #body>
9
+ <div>
10
+ <Combobox nullable @update:model-value="select">
11
+ <div class="relative">
12
+ <div class="absolute inset-y-0 left-0 flex items-center pl-4.5">
13
+ <FeatherIcon name="search" class="h-4 w-4" />
14
+ </div>
15
+ <ComboboxInput
16
+ placeholder="Search"
17
+ class="w-full border-none bg-transparent py-3 pl-11.5 pr-4.5 text-base text-gray-800 placeholder-gray-500 focus:ring-0"
18
+ v-model="searchQuery"
19
+ autocomplete="off"
20
+ />
21
+ </div>
22
+ <ComboboxOptions
23
+ class="max-h-96 overflow-auto border-t border-gray-100"
24
+ static
25
+ :hold="true"
26
+ >
27
+ <div
28
+ class="mb-2 mt-4.5 first:mt-3"
29
+ v-for="(group, index) in groups"
30
+ :key="group.title"
31
+ >
32
+ <div
33
+ class="mb-2.5 px-4.5 text-base text-gray-600"
34
+ v-if="!group.hideTitle"
35
+ >
36
+ {{ group.title }}
37
+ </div>
38
+ <ComboboxOption
39
+ v-for="item in group.items"
40
+ :key="item.name"
41
+ v-slot="{ active }"
42
+ :value="item"
43
+ class="px-2.5"
44
+ :disabled="item.disabled"
45
+ >
46
+ <component
47
+ :is="group.component"
48
+ :item="item"
49
+ :active="active"
50
+ />
51
+ </ComboboxOption>
52
+ </div>
53
+ </ComboboxOptions>
54
+ </Combobox>
55
+ </div>
56
+ </template>
57
+ </Dialog>
58
+ </template>
59
+ </template>
60
+
61
+ <script setup>
62
+ import {
63
+ Combobox,
64
+ ComboboxInput,
65
+ ComboboxOption,
66
+ ComboboxOptions,
67
+ } from '@headlessui/vue'
68
+ import { computed, onBeforeUnmount, onMounted } from 'vue'
69
+
70
+ const emit = defineEmits(['update:show', 'update:searchQuery', 'select'])
71
+ const props = defineProps({
72
+ show: { type: Boolean, default: false },
73
+ searchQuery: { type: String, default: '' },
74
+ groups: { type: Array, default: () => [] },
75
+ })
76
+
77
+ const show = computed({
78
+ get: () => props.show,
79
+ set: (value) => emit('update:show', value),
80
+ })
81
+
82
+ const searchQuery = computed({
83
+ get: () => props.searchQuery,
84
+ set: (value) => emit('update:searchQuery', value),
85
+ })
86
+
87
+ function select(item) {
88
+ emit('select', item)
89
+ show.value = false
90
+ }
91
+
92
+ function keydownWatcher(e) {
93
+ if (e.key === 'Escape' && show.value) {
94
+ show.value = false
95
+ e.preventDefault()
96
+ }
97
+
98
+ if (
99
+ e.key === 'k' &&
100
+ (e.ctrlKey || e.metaKey) &&
101
+ !e.target.classList.contains('ProseMirror')
102
+ ) {
103
+ show.value = true
104
+ e.preventDefault()
105
+ }
106
+ }
107
+
108
+ onMounted(() => window.addEventListener('keydown', keydownWatcher))
109
+ onBeforeUnmount(() => window.removeEventListener('keydown', keydownWatcher))
110
+ </script>
@@ -0,0 +1,28 @@
1
+ <script setup>
2
+ defineProps({
3
+ item: { type: Object, required: true },
4
+ active: { type: Boolean, default: false },
5
+ })
6
+ </script>
7
+
8
+ <template>
9
+ <div
10
+ class="flex w-full min-w-0 items-center rounded px-2 py-2 text-base font-medium text-gray-800"
11
+ :class="{ 'bg-gray-200': active }"
12
+ >
13
+ <component
14
+ :is="item.icon"
15
+ v-if="item.icon"
16
+ class="mr-3 h-4 w-4 text-gray-700"
17
+ />
18
+ <span class="overflow-hidden text-ellipsis whitespace-nowrap">
19
+ {{ item.title }}
20
+ </span>
21
+ <span
22
+ v-if="item.description"
23
+ class="ml-auto whitespace-nowrap pl-2 text-gray-600"
24
+ >
25
+ {{ item.description }}
26
+ </span>
27
+ </div>
28
+ </template>
@@ -0,0 +1,39 @@
1
+ <script setup lang="ts">
2
+ import { ref } from 'vue'
3
+ import Dialog from './Dialog.vue'
4
+ import Button from './Button.vue'
5
+
6
+ const dialog1 = ref(false)
7
+ const dialog2 = ref(false)
8
+ </script>
9
+ <template>
10
+ <Story :layout="{ width: 500, type: 'grid' }">
11
+ <Variant title="With options" autoPropsDisabled>
12
+ <Button @click="dialog1 = true">Show Dialog</Button>
13
+ <Dialog
14
+ :options="{
15
+ title: 'Confirm',
16
+ message: 'Are you sure you want to confirm this action?',
17
+ size: 'xl',
18
+ actions: [{ label: 'Confirm', variant: 'solid', onClick: () => {} }],
19
+ }"
20
+ v-model="dialog1"
21
+ />
22
+ </Variant>
23
+ <Variant title="With slots" autoPropsDisabled>
24
+ <Button @click="dialog2 = true">Show Dialog</Button>
25
+ <Dialog v-model="dialog2">
26
+ <template #body-title>
27
+ <h3>Custom Title</h3>
28
+ </template>
29
+ <template #body-content>
30
+ <p>Custom Body</p>
31
+ </template>
32
+ <template #actions>
33
+ <Button variant="solid">Confirm</Button>
34
+ <Button class="ml-2" @click="dialog2 = false">Close</Button>
35
+ </template>
36
+ </Dialog>
37
+ </Variant>
38
+ </Story>
39
+ </template>
@@ -0,0 +1,98 @@
1
+ <script setup lang="ts">
2
+ import { h } from 'vue'
3
+ import Dropdown from './Dropdown.vue'
4
+ import FeatherIcon from './FeatherIcon.vue'
5
+ import Button from './Button.vue'
6
+ </script>
7
+
8
+ <template>
9
+ <Story :layout="{ type: 'grid', width: 300 }">
10
+ <Variant title="Basic">
11
+ <Dropdown
12
+ :options="[
13
+ {
14
+ label: 'Edit Title',
15
+ onClick: () => {},
16
+ icon: () => h(FeatherIcon, { name: 'edit-2' }),
17
+ },
18
+ {
19
+ label: 'Manage Members',
20
+ onClick: () => {},
21
+ icon: () => h(FeatherIcon, { name: 'users' }),
22
+ },
23
+ {
24
+ label: 'Delete this project',
25
+ onClick: () => {},
26
+ icon: () => h(FeatherIcon, { name: 'trash' }),
27
+ },
28
+ ]"
29
+ />
30
+ </Variant>
31
+
32
+ <Variant title="Button prop">
33
+ <Dropdown
34
+ :options="[
35
+ {
36
+ label: 'Edit Title',
37
+ onClick: () => {},
38
+ },
39
+ {
40
+ label: 'Manage Members',
41
+ onClick: () => {},
42
+ },
43
+ {
44
+ label: 'Delete this project',
45
+ onClick: () => {},
46
+ },
47
+ ]"
48
+ :button="{
49
+ label: 'Actions',
50
+ }"
51
+ />
52
+ </Variant>
53
+
54
+ <Variant title="Custom Button and Groups">
55
+ <Dropdown
56
+ :options="[
57
+ {
58
+ group: 'Manage',
59
+ items: [
60
+ {
61
+ label: 'Edit Title',
62
+ icon: () => h(FeatherIcon, { name: 'edit' }),
63
+ },
64
+ {
65
+ label: 'Manage Members',
66
+ icon: () => h(FeatherIcon, { name: 'users' }),
67
+ },
68
+ ],
69
+ },
70
+ {
71
+ group: 'Delete',
72
+ items: [
73
+ {
74
+ label: 'Delete users',
75
+ icon: () => h(FeatherIcon, { name: 'edit' }),
76
+ },
77
+ {
78
+ label: 'Delete this project',
79
+ icon: () => h(FeatherIcon, { name: 'trash' }),
80
+ },
81
+ ],
82
+ },
83
+ ]"
84
+ >
85
+ <Button>
86
+ <template #icon>
87
+ <FeatherIcon name="more-horizontal" class="h-4 w-4" />
88
+ </template>
89
+ </Button>
90
+ </Dropdown>
91
+ </Variant>
92
+
93
+ <template #controls>
94
+ <!-- <HstText v-model="state.label" title="Label" />
95
+ <HstSelect v-model="state.size" :options="sizes" title="Size" /> -->
96
+ </template>
97
+ </Story>
98
+ </template>
@@ -0,0 +1,28 @@
1
+ <script setup lang="ts">
2
+ import { ref } from 'vue'
3
+ import ErrorMessage from './ErrorMessage.vue'
4
+
5
+ const message = ref('Invalid value')
6
+ const error = new Error('There was an error')
7
+ </script>
8
+ <template>
9
+ <Story :layout="{ width: 500, type: 'grid' }" autoPropsDisabled>
10
+ <Variant title="String message">
11
+ <ErrorMessage :message="message" />
12
+ </Variant>
13
+
14
+ <Variant title="Error object">
15
+ <ErrorMessage :message="error" />
16
+
17
+ <template #source>
18
+ <textarea v-pre>
19
+ <ErrorMessage :message="Error('There was an error')" />
20
+ </textarea>
21
+ </template>
22
+ </Variant>
23
+
24
+ <Variant title="Falsy value">
25
+ <ErrorMessage message="" />
26
+ </Variant>
27
+ </Story>
28
+ </template>
@@ -0,0 +1,34 @@
1
+ <script setup lang="ts">
2
+ import FileUploader from './FileUploader.vue'
3
+ import Button from './Button.vue'
4
+
5
+ const validateFileFunction = (fileObject) => {}
6
+ const onSuccess = (file) => {}
7
+ </script>
8
+ <template>
9
+ <Story :layout="{ width: 500, type: 'grid' }" autoPropsDisabled>
10
+ <FileUploader
11
+ :fileTypes="['image/*']"
12
+ :validateFile="validateFileFunction"
13
+ @success="onSuccess"
14
+ >
15
+ <template
16
+ v-slot="{
17
+ file,
18
+ uploading,
19
+ progress,
20
+ uploaded,
21
+ message,
22
+ error,
23
+ total,
24
+ success,
25
+ openFileSelector,
26
+ }"
27
+ >
28
+ <Button @click="openFileSelector" :loading="uploading">
29
+ {{ uploading ? `Uploading ${progress}%` : 'Upload Image' }}
30
+ </Button>
31
+ </template>
32
+ </FileUploader>
33
+ </Story>
34
+ </template>