@meistrari/tela-build 1.0.0
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/README.md +75 -0
- package/app.config.ts +73 -0
- package/components/tela/animated/animated-calculating-number.vue +16 -0
- package/components/tela/animated/animated-number.mdx +248 -0
- package/components/tela/animated/animated-number.stories.ts +52 -0
- package/components/tela/animated/animated-number.vue +23 -0
- package/components/tela/animated/animated-text.vue +124 -0
- package/components/tela/animated/animated-value.vue +68 -0
- package/components/tela/avatar/avatar.mdx +117 -0
- package/components/tela/avatar/avatar.stories.ts +62 -0
- package/components/tela/avatar/avatar.vue +71 -0
- package/components/tela/avatar/group/avatar-group.stories.ts +78 -0
- package/components/tela/avatar/group/avatar-group.vue +46 -0
- package/components/tela/badge/badge.mdx +154 -0
- package/components/tela/badge/badge.stories.ts +82 -0
- package/components/tela/badge/badge.vue +41 -0
- package/components/tela/button/button.mdx +155 -0
- package/components/tela/button/button.stories.ts +202 -0
- package/components/tela/button/button.vue +107 -0
- package/components/tela/card.vue +30 -0
- package/components/tela/chart/chart-bar.vue +58 -0
- package/components/tela/chat/chat.mdx +268 -0
- package/components/tela/chat/chat.stories.ts +253 -0
- package/components/tela/chat/command/index.vue +41 -0
- package/components/tela/chat/command/mention/index.vue +138 -0
- package/components/tela/chat/index.vue +112 -0
- package/components/tela/chat/pure-text-input/chat-text-input.vue +190 -0
- package/components/tela/chat/text-input/chat-text-input.stories.ts +128 -0
- package/components/tela/chat/text-input/index.vue +217 -0
- package/components/tela/chat/text-message/chat-text-message.stories.ts +138 -0
- package/components/tela/chat/text-message/index.vue +355 -0
- package/components/tela/chat/types.ts +19 -0
- package/components/tela/checkbox/checkbox-card.vue +30 -0
- package/components/tela/checkbox/checkbox.mdx +164 -0
- package/components/tela/checkbox/checkbox.stories.ts +104 -0
- package/components/tela/checkbox/checkbox.vue +43 -0
- package/components/tela/collapsible/Collapsible.vue +15 -0
- package/components/tela/collapsible/CollapsibleContent.vue +59 -0
- package/components/tela/collapsible/CollapsibleTrigger.vue +12 -0
- package/components/tela/collapsible/collapsible.mdx +157 -0
- package/components/tela/collapsible-section/collapsible-section.mdx +180 -0
- package/components/tela/collapsible-section/collapsible-section.stories.ts +53 -0
- package/components/tela/collapsible-section/collapsible-section.vue +51 -0
- package/components/tela/collapsible-section-with-actions.vue +98 -0
- package/components/tela/combobox/combobox-anchor.vue +24 -0
- package/components/tela/combobox/combobox-empty.vue +19 -0
- package/components/tela/combobox/combobox-group.vue +24 -0
- package/components/tela/combobox/combobox-indicator.vue +22 -0
- package/components/tela/combobox/combobox-input.vue +31 -0
- package/components/tela/combobox/combobox-item.vue +28 -0
- package/components/tela/combobox/combobox-label.vue +24 -0
- package/components/tela/combobox/combobox-list.vue +90 -0
- package/components/tela/combobox/combobox-module-selector.vue +366 -0
- package/components/tela/combobox/combobox-root.vue +15 -0
- package/components/tela/combobox/combobox-trigger.vue +12 -0
- package/components/tela/combobox/combobox.mdx +285 -0
- package/components/tela/combobox/combobox.stories.ts +232 -0
- package/components/tela/combobox/combobox.vue +497 -0
- package/components/tela/command/command-dialog.vue +22 -0
- package/components/tela/command/command-empty.vue +25 -0
- package/components/tela/command/command-group.vue +46 -0
- package/components/tela/command/command-input.vue +38 -0
- package/components/tela/command/command-item.vue +78 -0
- package/components/tela/command/command-list.vue +78 -0
- package/components/tela/command/command-separator.vue +23 -0
- package/components/tela/command/command-shortcut.vue +13 -0
- package/components/tela/command/command.vue +88 -0
- package/components/tela/command/dialog-base.vue +15 -0
- package/components/tela/command/dialog-content.vue +50 -0
- package/components/tela/command/utils.ts +15 -0
- package/components/tela/complex-table/complex-table-cell.stories.ts +145 -0
- package/components/tela/complex-table/complex-table-cell.vue +45 -0
- package/components/tela/complex-table/complex-table-header-cell.stories.ts +103 -0
- package/components/tela/complex-table/complex-table-header-cell.vue +48 -0
- package/components/tela/complex-table/complex-table-header.stories.ts +89 -0
- package/components/tela/complex-table/complex-table-header.vue +70 -0
- package/components/tela/complex-table/complex-table-row.vue +199 -0
- package/components/tela/complex-table/complex-table-virtualized.vue +326 -0
- package/components/tela/complex-table/complex-table.stories.ts +358 -0
- package/components/tela/complex-table/complex-table.vue +237 -0
- package/components/tela/complex-table/composables/table-common.ts +93 -0
- package/components/tela/complex-table/composables/table-selection.ts +87 -0
- package/components/tela/complex-table/composables/virtual-scroll.ts +252 -0
- package/components/tela/complex-table/styles/table-shared.css +170 -0
- package/components/tela/complex-table/types.ts +63 -0
- package/components/tela/complex-table/utils.ts +35 -0
- package/components/tela/confirm-button/confirm-button.vue +137 -0
- package/components/tela/confirmation-modal/confirmation-modal.vue +72 -0
- package/components/tela/copy-button.vue +86 -0
- package/components/tela/date-range-picker.vue +221 -0
- package/components/tela/dialog/dialog.mdx +170 -0
- package/components/tela/dialog/dialog.vue +182 -0
- package/components/tela/disabled-area.vue +16 -0
- package/components/tela/disclaimer/disclaimer.mdx +238 -0
- package/components/tela/disclaimer/disclaimer.stories.ts +196 -0
- package/components/tela/disclaimer/disclaimer.vue +125 -0
- package/components/tela/dropdown-menu/DropdownMenu.vue +121 -0
- package/components/tela/dropdown-menu/DropdownMenuCheckboxItem.vue +40 -0
- package/components/tela/dropdown-menu/DropdownMenuContent.vue +75 -0
- package/components/tela/dropdown-menu/DropdownMenuGroup.vue +12 -0
- package/components/tela/dropdown-menu/DropdownMenuItem.vue +137 -0
- package/components/tela/dropdown-menu/DropdownMenuLabel.vue +26 -0
- package/components/tela/dropdown-menu/DropdownMenuRadioGroup.vue +18 -0
- package/components/tela/dropdown-menu/DropdownMenuRadioItem.vue +40 -0
- package/components/tela/dropdown-menu/DropdownMenuRoot.vue +15 -0
- package/components/tela/dropdown-menu/DropdownMenuSeparator.vue +21 -0
- package/components/tela/dropdown-menu/DropdownMenuShortcut.vue +14 -0
- package/components/tela/dropdown-menu/DropdownMenuSub.vue +18 -0
- package/components/tela/dropdown-menu/DropdownMenuSubContent.vue +30 -0
- package/components/tela/dropdown-menu/DropdownMenuSubTrigger.vue +35 -0
- package/components/tela/dropdown-menu/DropdownMenuTrigger.vue +14 -0
- package/components/tela/dropdown-menu/dropdown-menu.mdx +265 -0
- package/components/tela/dropdown-menu/dropdown-menu.stories.ts +156 -0
- package/components/tela/expandable-input.vue +96 -0
- package/components/tela/file-drop.vue +37 -0
- package/components/tela/file-upload/file-upload.mdx +189 -0
- package/components/tela/file-upload/file-upload.stories.ts +48 -0
- package/components/tela/file-upload/file-upload.vue +205 -0
- package/components/tela/filters/checkbox-filter.stories.ts +218 -0
- package/components/tela/filters/checkbox-filter.vue +165 -0
- package/components/tela/filters/date-filter.stories.ts +258 -0
- package/components/tela/filters/date-filter.vue +200 -0
- package/components/tela/filters/user-filter.stories.ts +344 -0
- package/components/tela/filters/user-filter.vue +271 -0
- package/components/tela/hover-card/hover-card.mdx +221 -0
- package/components/tela/hover-card/hover-card.stories.ts +87 -0
- package/components/tela/hover-card/hover-card.vue +61 -0
- package/components/tela/icon/custom.vue +319 -0
- package/components/tela/icon/spinner.vue +12 -0
- package/components/tela/icon-button/icon-button.vue +114 -0
- package/components/tela/icon.vue +37 -0
- package/components/tela/initials.vue +28 -0
- package/components/tela/inline-input.vue +77 -0
- package/components/tela/input/input.mdx +182 -0
- package/components/tela/input/input.stories.ts +153 -0
- package/components/tela/input/tela-input.vue +240 -0
- package/components/tela/kbd/kbd-return.vue +6 -0
- package/components/tela/kbd/kbd.mdx +238 -0
- package/components/tela/kbd/kbd.vue +18 -0
- package/components/tela/label/label.mdx +121 -0
- package/components/tela/label/label.stories.ts +37 -0
- package/components/tela/label/label.vue +25 -0
- package/components/tela/link-decoration/link-decoration.vue +19 -0
- package/components/tela/live-label.vue +32 -0
- package/components/tela/long-press-button.vue +98 -0
- package/components/tela/menubar/menubar-content.vue +77 -0
- package/components/tela/menubar/menubar-item.vue +32 -0
- package/components/tela/menubar/menubar-label.vue +14 -0
- package/components/tela/menubar/menubar-menu.vue +12 -0
- package/components/tela/menubar/menubar-root.vue +30 -0
- package/components/tela/menubar/menubar-separator.vue +17 -0
- package/components/tela/menubar/menubar-shortcut.vue +14 -0
- package/components/tela/menubar/menubar-sub-content.vue +36 -0
- package/components/tela/menubar/menubar-sub-trigger.vue +28 -0
- package/components/tela/menubar/menubar-sub.vue +20 -0
- package/components/tela/menubar/menubar-trigger.vue +27 -0
- package/components/tela/menubar/menubar.vue +298 -0
- package/components/tela/modal/modal.mdx +145 -0
- package/components/tela/modal/modal.vue +242 -0
- package/components/tela/multiple-select/multiple-select.mdx +274 -0
- package/components/tela/multiple-select/multiple-select.stories.ts +325 -0
- package/components/tela/multiple-select/multiple-select.vue +666 -0
- package/components/tela/pane.vue +110 -0
- package/components/tela/popover/popover-content.vue +48 -0
- package/components/tela/popover/popover-trigger.vue +12 -0
- package/components/tela/popover/popover.mdx +239 -0
- package/components/tela/popover/popover.stories.ts +150 -0
- package/components/tela/popover/popover.vue +15 -0
- package/components/tela/popover-list/popover-list-nested.vue +104 -0
- package/components/tela/popover-list/popover-list.stories.ts +330 -0
- package/components/tela/popover-list/popover-list.vue +191 -0
- package/components/tela/radio-button.vue +66 -0
- package/components/tela/radio-group/radio-group-item.vue +40 -0
- package/components/tela/radio-group/radio-group-root.vue +26 -0
- package/components/tela/radio-group/radio-group.mdx +78 -0
- package/components/tela/radio-group/radio-group.stories.ts +106 -0
- package/components/tela/radio-group/radio-group.vue +23 -0
- package/components/tela/range-calendar.stories.ts +110 -0
- package/components/tela/range-calendar.vue +109 -0
- package/components/tela/scroll-area/scroll-area.mdx +183 -0
- package/components/tela/scroll-area/scroll-area.vue +30 -0
- package/components/tela/scroll-area/scroll-bar.vue +31 -0
- package/components/tela/segment-toggle.stories.ts +114 -0
- package/components/tela/segment-toggle.vue +66 -0
- package/components/tela/select-menu/select-menu-content.vue +106 -0
- package/components/tela/select-menu/select-menu-down-button.vue +20 -0
- package/components/tela/select-menu/select-menu-group.vue +16 -0
- package/components/tela/select-menu/select-menu-item.vue +40 -0
- package/components/tela/select-menu/select-menu-root.vue +15 -0
- package/components/tela/select-menu/select-menu-trigger.vue +34 -0
- package/components/tela/select-menu/select-menu-up-button.vue +20 -0
- package/components/tela/select-menu/select-menu-value.vue +12 -0
- package/components/tela/select-menu/select-menu.mdx +221 -0
- package/components/tela/select-menu/select-menu.stories.ts +91 -0
- package/components/tela/select-menu/select-menu.vue +165 -0
- package/components/tela/selector/selector.vue +47 -0
- package/components/tela/sheet/sheet-close.vue +12 -0
- package/components/tela/sheet/sheet-content.vue +57 -0
- package/components/tela/sheet/sheet-description.vue +23 -0
- package/components/tela/sheet/sheet-footer.vue +18 -0
- package/components/tela/sheet/sheet-header.vue +15 -0
- package/components/tela/sheet/sheet-root.vue +18 -0
- package/components/tela/sheet/sheet-title.vue +23 -0
- package/components/tela/sheet/sheet-trigger.vue +12 -0
- package/components/tela/sheet/sheet.client.vue +150 -0
- package/components/tela/sheet/sheet.mdx +176 -0
- package/components/tela/sheet/sheet.stories.ts +201 -0
- package/components/tela/sheet/variants.ts +22 -0
- package/components/tela/side-sheet/side-sheet.mdx +131 -0
- package/components/tela/side-sheet/side-sheet.stories.ts +134 -0
- package/components/tela/side-sheet/side-sheet.vue +106 -0
- package/components/tela/skeleton/skeleton.mdx +165 -0
- package/components/tela/skeleton/skeleton.stories.ts +35 -0
- package/components/tela/skeleton/skeleton.vue +45 -0
- package/components/tela/skeleton-icon.vue +24 -0
- package/components/tela/span.vue +24 -0
- package/components/tela/star-button.vue +70 -0
- package/components/tela/status/status-lean.vue +30 -0
- package/components/tela/status/status.mdx +187 -0
- package/components/tela/status/status.stories.ts +160 -0
- package/components/tela/status/status.vue +420 -0
- package/components/tela/status-bar/status-bar.mdx +178 -0
- package/components/tela/status-bar/status-bar.stories.ts +64 -0
- package/components/tela/status-bar/status-bar.vue +56 -0
- package/components/tela/status-bar/types.ts +5 -0
- package/components/tela/switch/switch.mdx +118 -0
- package/components/tela/switch/switch.stories.ts +80 -0
- package/components/tela/switch/switch.vue +56 -0
- package/components/tela/table/table-body.vue +13 -0
- package/components/tela/table/table-caption.vue +13 -0
- package/components/tela/table/table-cell.vue +20 -0
- package/components/tela/table/table-empty.vue +37 -0
- package/components/tela/table/table-footer.vue +13 -0
- package/components/tela/table/table-head.vue +13 -0
- package/components/tela/table/table-header.vue +13 -0
- package/components/tela/table/table-row.vue +13 -0
- package/components/tela/table/table.mdx +230 -0
- package/components/tela/table/table.stories.ts +384 -0
- package/components/tela/table/table.vue +15 -0
- package/components/tela/tabs/tabs-content.vue +20 -0
- package/components/tela/tabs/tabs-indicator.vue +22 -0
- package/components/tela/tabs/tabs-list.vue +23 -0
- package/components/tela/tabs/tabs-root.vue +15 -0
- package/components/tela/tabs/tabs-trigger.vue +27 -0
- package/components/tela/tabs/tabs.mdx +138 -0
- package/components/tela/tabs/tabs.stories.ts +72 -0
- package/components/tela/tabs/tabs.vue +61 -0
- package/components/tela/tags/tags-select.mdx +318 -0
- package/components/tela/tags/tags-select.stories.ts +47 -0
- package/components/tela/tags/tags-select.vue +637 -0
- package/components/tela/tags/tags.mdx +151 -0
- package/components/tela/tags/tags.stories.ts +118 -0
- package/components/tela/tags/tags.vue +112 -0
- package/components/tela/textarea/textarea.mdx +102 -0
- package/components/tela/textarea/textarea.stories.ts +50 -0
- package/components/tela/textarea/textarea.vue +34 -0
- package/components/tela/toggle-group.vue +91 -0
- package/components/tela/tooltip/tooltip-content.vue +45 -0
- package/components/tela/tooltip/tooltip-provider.vue +12 -0
- package/components/tela/tooltip/tooltip-root.vue +15 -0
- package/components/tela/tooltip/tooltip-trigger.vue +12 -0
- package/components/tela/tooltip/tooltip.mdx +196 -0
- package/components/tela/tooltip/tooltip.stories.ts +200 -0
- package/components/tela/tooltip/tooltip.vue +91 -0
- package/components/tela/tooltip-group/tooltip-group-trigger.vue +92 -0
- package/components/tela/tooltip-group/tooltip-group.mdx +236 -0
- package/components/tela/tooltip-group/tooltip-group.stories.ts +465 -0
- package/components/tela/tooltip-group/tooltip-group.vue +35 -0
- package/components/tela/transparent-input.vue +151 -0
- package/components/tela/variable-icon.vue +28 -0
- package/components/tela/variable-input.vue +77 -0
- package/components/tela/wide-button/wide-button.vue +40 -0
- package/components.json +18 -0
- package/composables/status-toast.ts +67 -0
- package/css/reset.css +386 -0
- package/css/text.css +22 -0
- package/lib/doc-generator.ts +903 -0
- package/lib/extractors/volar-extract.ts +186 -0
- package/lib/type-resolver.ts +402 -0
- package/lib/utils.ts +6 -0
- package/modules/tela-build-docs/index.ts +139 -0
- package/nuxt.config.ts +80 -0
- package/package.json +84 -0
- package/plugins/test-id.ts +7 -0
- package/tsconfig.json +7 -0
- package/types/custom-icon.ts +1 -0
- package/types/index.ts +2 -0
- package/types/status.ts +1 -0
- package/unocss.config.ts +89 -0
- package/utils/component-utils.ts +30 -0
- package/utils/design-tokens.ts +431 -0
- package/utils/fold.ts +8 -0
- package/utils/select-menu.ts +10 -0
- package/utils/status.ts +1 -0
- package/utils/without-keys.ts +34 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
|
|
2
|
+
import * as FileUploadStories from './file-upload.stories.ts';
|
|
3
|
+
|
|
4
|
+
<Meta of={FileUploadStories} />
|
|
5
|
+
|
|
6
|
+
# TelaFileUpload
|
|
7
|
+
|
|
8
|
+
A file upload component that provides drag-and-drop functionality and traditional file selection. Supports multiple file selection, file type restrictions, and custom styling. Emits events for file selection and validation.
|
|
9
|
+
|
|
10
|
+
## Examples
|
|
11
|
+
|
|
12
|
+
### Basic Usage
|
|
13
|
+
|
|
14
|
+
```vue
|
|
15
|
+
<script setup>
|
|
16
|
+
const handleFiles = (files) => {
|
|
17
|
+
console.log('Selected files:', files)
|
|
18
|
+
}
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<template>
|
|
22
|
+
<TelaFileUpload @change="handleFiles" />
|
|
23
|
+
</template>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Accept Specific File Types
|
|
27
|
+
|
|
28
|
+
```vue
|
|
29
|
+
<!-- Images only -->
|
|
30
|
+
<TelaFileUpload accept="image/*" />
|
|
31
|
+
|
|
32
|
+
<!-- PDFs only -->
|
|
33
|
+
<TelaFileUpload accept=".pdf" />
|
|
34
|
+
|
|
35
|
+
<!-- Images and PDFs -->
|
|
36
|
+
<TelaFileUpload accept="image/*,.pdf" />
|
|
37
|
+
|
|
38
|
+
<!-- Multiple types -->
|
|
39
|
+
<TelaFileUpload accept=".jpg,.jpeg,.png,.pdf,.doc,.docx" />
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Multiple Files
|
|
43
|
+
|
|
44
|
+
```vue
|
|
45
|
+
<TelaFileUpload multiple @change="handleFiles" />
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### With Max Size
|
|
49
|
+
|
|
50
|
+
```vue
|
|
51
|
+
<TelaFileUpload
|
|
52
|
+
:max-size="5242880"
|
|
53
|
+
@change="handleFiles"
|
|
54
|
+
@error="handleError"
|
|
55
|
+
/>
|
|
56
|
+
<!-- 5MB = 5 * 1024 * 1024 bytes -->
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Disabled State
|
|
60
|
+
|
|
61
|
+
```vue
|
|
62
|
+
<TelaFileUpload disabled />
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Complete Example
|
|
66
|
+
|
|
67
|
+
```vue
|
|
68
|
+
<script setup>
|
|
69
|
+
import { ref } from 'vue'
|
|
70
|
+
|
|
71
|
+
const files = ref([])
|
|
72
|
+
const error = ref(null)
|
|
73
|
+
|
|
74
|
+
const handleChange = (selectedFiles) => {
|
|
75
|
+
files.value = Array.from(selectedFiles)
|
|
76
|
+
error.value = null
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const handleError = (errorMessage) => {
|
|
80
|
+
error.value = errorMessage
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const removeFile = (index) => {
|
|
84
|
+
files.value.splice(index, 1)
|
|
85
|
+
}
|
|
86
|
+
</script>
|
|
87
|
+
|
|
88
|
+
<template>
|
|
89
|
+
<div>
|
|
90
|
+
<TelaFileUpload
|
|
91
|
+
accept="image/*,.pdf"
|
|
92
|
+
multiple
|
|
93
|
+
:max-size="10485760"
|
|
94
|
+
@change="handleChange"
|
|
95
|
+
@error="handleError"
|
|
96
|
+
/>
|
|
97
|
+
|
|
98
|
+
<div v-if="error" class="text-red-500 mt-2">
|
|
99
|
+
{{ error }}
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
<div v-if="files.length > 0" class="mt-4">
|
|
103
|
+
<h4 class="font-semibold mb-2">Selected Files:</h4>
|
|
104
|
+
<ul class="space-y-2">
|
|
105
|
+
<li
|
|
106
|
+
v-for="(file, index) in files"
|
|
107
|
+
:key="index"
|
|
108
|
+
class="flex items-center justify-between p-2 bg-gray-100 rounded"
|
|
109
|
+
>
|
|
110
|
+
<span>{{ file.name }} ({{ formatFileSize(file.size) }})</span>
|
|
111
|
+
<TelaButton
|
|
112
|
+
size="sm"
|
|
113
|
+
variant="ghost"
|
|
114
|
+
@click="removeFile(index)"
|
|
115
|
+
>
|
|
116
|
+
Remove
|
|
117
|
+
</TelaButton>
|
|
118
|
+
</li>
|
|
119
|
+
</ul>
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
</template>
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Props
|
|
126
|
+
|
|
127
|
+
<ArgTypes />
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
type FileUploadProps = {
|
|
131
|
+
accept?: string
|
|
132
|
+
multiple?: boolean
|
|
133
|
+
disabled?: boolean
|
|
134
|
+
maxSize?: number
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Events
|
|
139
|
+
|
|
140
|
+
- `change` - Emitted when files are selected with FileList
|
|
141
|
+
- `error` - Emitted when validation fails (e.g., file too large)
|
|
142
|
+
|
|
143
|
+
## Features
|
|
144
|
+
|
|
145
|
+
- **Drag and Drop**: Drag files onto the component
|
|
146
|
+
- **Click to Browse**: Traditional file picker
|
|
147
|
+
- **File Type Filtering**: Accept specific file types
|
|
148
|
+
- **Multiple Files**: Select multiple files at once
|
|
149
|
+
- **Size Validation**: Enforce maximum file size
|
|
150
|
+
- **Disabled State**: Prevent file selection
|
|
151
|
+
- **Visual Feedback**: Hover and drag states
|
|
152
|
+
- **Accessible**: Keyboard navigation support
|
|
153
|
+
|
|
154
|
+
## File Size Limits
|
|
155
|
+
|
|
156
|
+
Common file size limits:
|
|
157
|
+
- 1MB = 1048576 bytes
|
|
158
|
+
- 5MB = 5242880 bytes
|
|
159
|
+
- 10MB = 10485760 bytes
|
|
160
|
+
- 50MB = 52428800 bytes
|
|
161
|
+
- 100MB = 104857600 bytes
|
|
162
|
+
|
|
163
|
+
## Accepted File Types
|
|
164
|
+
|
|
165
|
+
Common accept patterns:
|
|
166
|
+
- Images: `image/*`
|
|
167
|
+
- PDFs: `.pdf` or `application/pdf`
|
|
168
|
+
- Documents: `.doc,.docx,.txt`
|
|
169
|
+
- Spreadsheets: `.xls,.xlsx,.csv`
|
|
170
|
+
- Audio: `audio/*`
|
|
171
|
+
- Video: `video/*`
|
|
172
|
+
- Any: `*/*` (not recommended)
|
|
173
|
+
|
|
174
|
+
## Best Practices
|
|
175
|
+
|
|
176
|
+
1. **Set Accept Types**: Always specify accepted file types
|
|
177
|
+
2. **Validate Size**: Set reasonable max size limits
|
|
178
|
+
3. **Provide Feedback**: Show selected files and errors
|
|
179
|
+
4. **Handle Errors**: Gracefully handle validation failures
|
|
180
|
+
5. **Show Progress**: Display upload progress for large files
|
|
181
|
+
6. **Clear Instructions**: Tell users what files are accepted
|
|
182
|
+
|
|
183
|
+
## Accessibility
|
|
184
|
+
|
|
185
|
+
- Keyboard accessible file input
|
|
186
|
+
- Proper ARIA labels
|
|
187
|
+
- Focus states
|
|
188
|
+
- Disabled state properly conveyed
|
|
189
|
+
- Screen reader friendly
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook-vue/vue3'
|
|
2
|
+
|
|
3
|
+
import FileUpload from './file-upload.vue'
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof FileUpload> = {
|
|
6
|
+
title: 'Core/FileUpload',
|
|
7
|
+
component: FileUpload,
|
|
8
|
+
parameters: {
|
|
9
|
+
layout: 'centered',
|
|
10
|
+
docs: {
|
|
11
|
+
description: {
|
|
12
|
+
component: 'A file upload component that provides drag-and-drop functionality and traditional file selection. Supports multiple file selection, file type restrictions, and custom styling. Emits events for file selection and validation.',
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
argTypes: {
|
|
17
|
+
accept: {
|
|
18
|
+
control: 'text',
|
|
19
|
+
description: 'Comma-separated list of accepted file types (e.g., "image/*,.pdf").',
|
|
20
|
+
},
|
|
21
|
+
multiple: {
|
|
22
|
+
control: 'boolean',
|
|
23
|
+
description: 'Allow selection of multiple files.',
|
|
24
|
+
},
|
|
25
|
+
disabled: {
|
|
26
|
+
control: 'boolean',
|
|
27
|
+
description: 'Disable the file upload component.',
|
|
28
|
+
},
|
|
29
|
+
maxSize: {
|
|
30
|
+
control: 'number',
|
|
31
|
+
description: 'Maximum file size in bytes allowed for upload.',
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export default meta
|
|
37
|
+
|
|
38
|
+
type Story = StoryObj<typeof meta>
|
|
39
|
+
|
|
40
|
+
export const Default: Story = {
|
|
41
|
+
render: () => ({
|
|
42
|
+
components: { FileUpload },
|
|
43
|
+
template: `
|
|
44
|
+
<div style="width: 100%; height: 300px; display: flex; align-items: center; justify-content: center;">
|
|
45
|
+
<FileUpload />
|
|
46
|
+
</div>`,
|
|
47
|
+
}),
|
|
48
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { ref } from 'vue'
|
|
3
|
+
import { useI18n } from 'vue-i18n'
|
|
4
|
+
|
|
5
|
+
const props = withDefaults(defineProps<{
|
|
6
|
+
class?: string
|
|
7
|
+
multiple?: boolean
|
|
8
|
+
maxFiles?: number
|
|
9
|
+
maxFileSize?: number // in MB
|
|
10
|
+
acceptedFormats?: string[] // e.g., ['image/png', 'image/jpeg', 'application/pdf']
|
|
11
|
+
acceptedExtensions?: string[] // e.g., ['.png', '.jpg', '.pdf'] - for display
|
|
12
|
+
}>(), {
|
|
13
|
+
multiple: true,
|
|
14
|
+
maxFiles: 5,
|
|
15
|
+
maxFileSize: 10, // 10MB default
|
|
16
|
+
acceptedFormats: () => [],
|
|
17
|
+
acceptedExtensions: () => [],
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
const emit = defineEmits<{
|
|
21
|
+
error: [message: string]
|
|
22
|
+
}>()
|
|
23
|
+
|
|
24
|
+
const { t } = useI18n()
|
|
25
|
+
const files = defineModel<File[]>({ required: true })
|
|
26
|
+
const isDragging = ref(false)
|
|
27
|
+
|
|
28
|
+
function validateFiles(newFiles: FileList | File[]): File[] {
|
|
29
|
+
const fileArray = Array.from(newFiles)
|
|
30
|
+
const maxSizeBytes = props.maxFileSize * 1024 * 1024
|
|
31
|
+
|
|
32
|
+
// Check file formats if specified
|
|
33
|
+
if (props.acceptedFormats.length > 0) {
|
|
34
|
+
const invalidFiles = fileArray.filter((file) => {
|
|
35
|
+
// Check MIME type
|
|
36
|
+
const mimeTypeMatch = props.acceptedFormats.includes(file.type)
|
|
37
|
+
|
|
38
|
+
// Check file extension as fallback
|
|
39
|
+
const extension = `.${file.name.split('.').pop()?.toLowerCase()}`
|
|
40
|
+
const extensionMatch = props.acceptedExtensions.some(ext =>
|
|
41
|
+
extension === ext.toLowerCase(),
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
return !mimeTypeMatch && !extensionMatch
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
if (invalidFiles.length > 0) {
|
|
48
|
+
const formatsDisplay = props.acceptedExtensions.length > 0
|
|
49
|
+
? props.acceptedExtensions.join(', ').toUpperCase()
|
|
50
|
+
: props.acceptedFormats.join(', ')
|
|
51
|
+
emit('error', t('common.fileUpload.errors.invalidFormat', { formats: formatsDisplay }))
|
|
52
|
+
return []
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Check file sizes
|
|
57
|
+
const oversizedFiles = fileArray.filter(file => file.size > maxSizeBytes)
|
|
58
|
+
if (oversizedFiles.length > 0) {
|
|
59
|
+
emit('error', t('common.fileUpload.errors.fileTooLarge', { maxSize: props.maxFileSize }))
|
|
60
|
+
return []
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Check total file count
|
|
64
|
+
const totalFiles = files.value.length + fileArray.length
|
|
65
|
+
if (totalFiles > props.maxFiles) {
|
|
66
|
+
emit('error', t('common.fileUpload.errors.tooManyFiles', { maxFiles: props.maxFiles }))
|
|
67
|
+
return []
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return fileArray
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function handleFileChange(event: Event) {
|
|
74
|
+
const input = event.target as HTMLInputElement
|
|
75
|
+
|
|
76
|
+
if (input.files && input.files.length > 0) {
|
|
77
|
+
const validFiles = validateFiles(input.files)
|
|
78
|
+
|
|
79
|
+
if (validFiles.length > 0) {
|
|
80
|
+
if (props.multiple) {
|
|
81
|
+
// Append new files to existing ones
|
|
82
|
+
files.value = [...files.value, ...validFiles]
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
files.value = [validFiles[0]]
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Reset input value to allow selecting the same file again
|
|
91
|
+
input.value = ''
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function handleDragEnter(event: DragEvent) {
|
|
95
|
+
event.preventDefault()
|
|
96
|
+
isDragging.value = true
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function handleDragLeave(event: DragEvent) {
|
|
100
|
+
event.preventDefault()
|
|
101
|
+
isDragging.value = false
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function handleDragOver(event: DragEvent) {
|
|
105
|
+
event.preventDefault()
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function handleDrop(event: DragEvent) {
|
|
109
|
+
event.preventDefault()
|
|
110
|
+
isDragging.value = false
|
|
111
|
+
|
|
112
|
+
const droppedFiles = event.dataTransfer?.files
|
|
113
|
+
|
|
114
|
+
if (droppedFiles && droppedFiles.length > 0) {
|
|
115
|
+
const validFiles = validateFiles(droppedFiles)
|
|
116
|
+
|
|
117
|
+
if (validFiles.length > 0) {
|
|
118
|
+
if (props.multiple) {
|
|
119
|
+
// Append new files to existing ones
|
|
120
|
+
files.value = [...files.value, ...validFiles]
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
files.value = [validFiles[0]]
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function removeFile(index: number) {
|
|
130
|
+
files.value = files.value.filter((_, i) => i !== index)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function formatFileSize(bytes: number): string {
|
|
134
|
+
if (bytes === 0)
|
|
135
|
+
return '0 Bytes'
|
|
136
|
+
const k = 1024
|
|
137
|
+
const sizes = ['Bytes', 'KB', 'MB', 'GB']
|
|
138
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
|
139
|
+
return `${Math.round(bytes / k ** i * 100) / 100} ${sizes[i]}`
|
|
140
|
+
}
|
|
141
|
+
</script>
|
|
142
|
+
|
|
143
|
+
<template>
|
|
144
|
+
<div class="flex flex-col gap-2 w-full">
|
|
145
|
+
<label
|
|
146
|
+
for="file-upload"
|
|
147
|
+
:class="cn(
|
|
148
|
+
'w-full py-4.5 flex flex-col items-center justify-center gap-2.5 cursor-pointer bg-gray-50 border-[1px] border-gray-300 border-dashed rounded-xl transition-colors',
|
|
149
|
+
isDragging && 'border-gray-300 bg-gray-100',
|
|
150
|
+
props.class,
|
|
151
|
+
)"
|
|
152
|
+
@dragenter="handleDragEnter"
|
|
153
|
+
@dragleave="handleDragLeave"
|
|
154
|
+
@dragover="handleDragOver"
|
|
155
|
+
@drop="handleDrop"
|
|
156
|
+
>
|
|
157
|
+
<TelaIcon name="i-ph-upload-simple-bold" size="16px" class="text-gray-700" />
|
|
158
|
+
<div class="flex flex-col items-center justify-center gap-1">
|
|
159
|
+
<h3 class="text-gray-700 text-body-14-semibold font-580">
|
|
160
|
+
{{ $t('common.fileUpload.dragAndDrop') }} <TelaLinkDecoration class="text-gray-700">{{ $t('common.fileUpload.orBrowse') }}</TelaLinkDecoration>
|
|
161
|
+
</h3>
|
|
162
|
+
<p class="text-10px leading-12px tracking-normal text-gray-400">
|
|
163
|
+
<template v-if="acceptedExtensions.length > 0">
|
|
164
|
+
{{ acceptedExtensions.join(', ').toUpperCase() }}
|
|
165
|
+
</template>
|
|
166
|
+
<template v-else>
|
|
167
|
+
{{ $t('common.fileUpload.supportedFormats') }}
|
|
168
|
+
</template>
|
|
169
|
+
</p>
|
|
170
|
+
<span class="text-10px leading-12px tracking-normal text-gray-400">
|
|
171
|
+
{{ t('common.fileUpload.maxSize', { maxSize: maxFileSize }) }} • {{ t('common.fileUpload.filesCount', { current: files.length, max: maxFiles }) }}
|
|
172
|
+
</span>
|
|
173
|
+
</div>
|
|
174
|
+
</label>
|
|
175
|
+
|
|
176
|
+
<div v-if="files.length > 0" class="flex flex-col gap-2px ">
|
|
177
|
+
<div
|
|
178
|
+
v-for="(file, index) in files"
|
|
179
|
+
:key="`${file.name}-${index}`"
|
|
180
|
+
class="flex items-center justify-between px-3 py-2 bg-gray-50 rounded-lg border-1px border-gray-300"
|
|
181
|
+
>
|
|
182
|
+
<div class="flex items-center gap-2 flex-1 min-w-0">
|
|
183
|
+
<TelaIcon name="i-ph-file-bold" size="14px" class="text-gray-500 flex-shrink-0" />
|
|
184
|
+
<span class="text-body-14-regular text-gray-700 truncate">{{ file.name }}</span>
|
|
185
|
+
<span class="text-10px text-gray-400 flex-shrink-0">({{ formatFileSize(file.size) }})</span>
|
|
186
|
+
</div>
|
|
187
|
+
<button
|
|
188
|
+
type="button"
|
|
189
|
+
class="flex-shrink-0 p-1 hover:bg-gray-100 rounded transition-colors"
|
|
190
|
+
@click="removeFile(index)"
|
|
191
|
+
>
|
|
192
|
+
<TelaIcon name="i-ph-x-bold" size="12px" class="text-gray-500" />
|
|
193
|
+
</button>
|
|
194
|
+
</div>
|
|
195
|
+
</div>
|
|
196
|
+
</div>
|
|
197
|
+
<input
|
|
198
|
+
id="file-upload"
|
|
199
|
+
type="file"
|
|
200
|
+
class="hidden"
|
|
201
|
+
:multiple="props.multiple"
|
|
202
|
+
:accept="acceptedFormats.length > 0 ? acceptedFormats.join(',') : undefined"
|
|
203
|
+
@change="handleFileChange"
|
|
204
|
+
>
|
|
205
|
+
</template>
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/vue3'
|
|
2
|
+
import { ref } from 'vue'
|
|
3
|
+
import CheckboxFilter from './checkbox-filter.vue'
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof CheckboxFilter> = {
|
|
6
|
+
title: 'Filters/CheckboxFilter',
|
|
7
|
+
component: CheckboxFilter,
|
|
8
|
+
tags: ['autodocs'],
|
|
9
|
+
parameters: {
|
|
10
|
+
layout: 'centered',
|
|
11
|
+
docs: {
|
|
12
|
+
description: {
|
|
13
|
+
component: 'A dropdown filter component with searchable checkboxes for multi-select filtering. Features include a search bar, scrollable options list, and a sticky apply button. Ideal for filtering data by multiple criteria.',
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
argTypes: {
|
|
18
|
+
buttonLabel: {
|
|
19
|
+
control: 'text',
|
|
20
|
+
description: 'The label displayed on the filter button',
|
|
21
|
+
},
|
|
22
|
+
identifier: {
|
|
23
|
+
control: 'text',
|
|
24
|
+
description: 'Unique identifier for the filter',
|
|
25
|
+
},
|
|
26
|
+
options: {
|
|
27
|
+
control: 'object',
|
|
28
|
+
description: 'Object containing filter options. Each key should have a value and label property',
|
|
29
|
+
},
|
|
30
|
+
isActive: {
|
|
31
|
+
control: 'boolean',
|
|
32
|
+
description: 'Whether the filter is currently active (has selections applied)',
|
|
33
|
+
},
|
|
34
|
+
disabled: {
|
|
35
|
+
control: 'boolean',
|
|
36
|
+
description: 'Whether the filter button is disabled',
|
|
37
|
+
},
|
|
38
|
+
loading: {
|
|
39
|
+
control: 'boolean',
|
|
40
|
+
description: 'Whether the filter is in a loading state',
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export default meta
|
|
46
|
+
|
|
47
|
+
type Story = StoryObj<typeof meta>
|
|
48
|
+
|
|
49
|
+
export const Default: Story = {
|
|
50
|
+
render: args => ({
|
|
51
|
+
components: { CheckboxFilter },
|
|
52
|
+
setup() {
|
|
53
|
+
const selected = ref({ identifier: 'type', label: 'Type', value: undefined })
|
|
54
|
+
const isActive = ref(false)
|
|
55
|
+
|
|
56
|
+
function handleApply(value: any) {
|
|
57
|
+
selected.value = {
|
|
58
|
+
identifier: 'type',
|
|
59
|
+
label: value.type.map((t: string) => t.charAt(0).toUpperCase() + t.slice(1)).join(', '),
|
|
60
|
+
value: value.type,
|
|
61
|
+
}
|
|
62
|
+
isActive.value = true
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function handleReset() {
|
|
66
|
+
selected.value = { identifier: 'type', label: 'Type', value: undefined }
|
|
67
|
+
isActive.value = false
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return { selected, isActive, handleApply, handleReset, args }
|
|
71
|
+
},
|
|
72
|
+
template: `
|
|
73
|
+
<div class="flex flex-col gap-4 p-4">
|
|
74
|
+
<CheckboxFilter
|
|
75
|
+
v-bind="args"
|
|
76
|
+
:selected="selected"
|
|
77
|
+
:is-active="isActive"
|
|
78
|
+
@apply="handleApply"
|
|
79
|
+
@reset="handleReset"
|
|
80
|
+
/>
|
|
81
|
+
</div>
|
|
82
|
+
`,
|
|
83
|
+
}),
|
|
84
|
+
args: {
|
|
85
|
+
buttonLabel: 'Type',
|
|
86
|
+
identifier: 'type',
|
|
87
|
+
options: {
|
|
88
|
+
completion: { value: 'completion', label: 'Completion' },
|
|
89
|
+
parser: { value: 'parser', label: 'Parser' },
|
|
90
|
+
workflow: { value: 'workflow', label: 'Workflow' },
|
|
91
|
+
},
|
|
92
|
+
isActive: false,
|
|
93
|
+
disabled: false,
|
|
94
|
+
loading: false,
|
|
95
|
+
},
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export const WithManyOptions: Story = {
|
|
99
|
+
render: args => ({
|
|
100
|
+
components: { CheckboxFilter },
|
|
101
|
+
setup() {
|
|
102
|
+
const selected = ref({ identifier: 'model', label: 'Model', value: undefined })
|
|
103
|
+
const isActive = ref(false)
|
|
104
|
+
|
|
105
|
+
function handleApply(value: any) {
|
|
106
|
+
const models = value.model
|
|
107
|
+
const label = models.length > 2
|
|
108
|
+
? `${models[0]} & ${models.length - 1} others`
|
|
109
|
+
: models.join(' & ')
|
|
110
|
+
selected.value = {
|
|
111
|
+
identifier: 'model',
|
|
112
|
+
label,
|
|
113
|
+
value: models,
|
|
114
|
+
}
|
|
115
|
+
isActive.value = true
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function handleReset() {
|
|
119
|
+
selected.value = { identifier: 'model', label: 'Model', value: undefined }
|
|
120
|
+
isActive.value = false
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return { selected, isActive, handleApply, handleReset, args }
|
|
124
|
+
},
|
|
125
|
+
template: `
|
|
126
|
+
<div class="flex flex-col gap-4 p-4">
|
|
127
|
+
<CheckboxFilter
|
|
128
|
+
v-bind="args"
|
|
129
|
+
:selected="selected"
|
|
130
|
+
:is-active="isActive"
|
|
131
|
+
@apply="handleApply"
|
|
132
|
+
@reset="handleReset"
|
|
133
|
+
/>
|
|
134
|
+
</div>
|
|
135
|
+
`,
|
|
136
|
+
}),
|
|
137
|
+
args: {
|
|
138
|
+
buttonLabel: 'Model',
|
|
139
|
+
identifier: 'model',
|
|
140
|
+
options: {
|
|
141
|
+
'gpt-4': { value: 'gpt-4', label: 'GPT-4' },
|
|
142
|
+
'gpt-4-turbo': { value: 'gpt-4-turbo', label: 'GPT-4 Turbo' },
|
|
143
|
+
'gpt-3.5-turbo': { value: 'gpt-3.5-turbo', label: 'GPT-3.5 Turbo' },
|
|
144
|
+
'claude-3-opus': { value: 'claude-3-opus', label: 'Claude 3 Opus' },
|
|
145
|
+
'claude-3-sonnet': { value: 'claude-3-sonnet', label: 'Claude 3 Sonnet' },
|
|
146
|
+
'claude-3-haiku': { value: 'claude-3-haiku', label: 'Claude 3 Haiku' },
|
|
147
|
+
'gemini-pro': { value: 'gemini-pro', label: 'Gemini Pro' },
|
|
148
|
+
'llama-2-70b': { value: 'llama-2-70b', label: 'Llama 2 70B' },
|
|
149
|
+
},
|
|
150
|
+
isActive: false,
|
|
151
|
+
disabled: false,
|
|
152
|
+
loading: false,
|
|
153
|
+
},
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export const Active: Story = {
|
|
157
|
+
render: args => ({
|
|
158
|
+
components: { CheckboxFilter },
|
|
159
|
+
setup() {
|
|
160
|
+
const selected = ref({ identifier: 'status', label: 'Succeeded, Failed', value: ['success', 'failed'] })
|
|
161
|
+
const isActive = ref(true)
|
|
162
|
+
|
|
163
|
+
function handleApply(value: any) {
|
|
164
|
+
selected.value = {
|
|
165
|
+
identifier: 'status',
|
|
166
|
+
label: value.status.map((s: string) => s.charAt(0).toUpperCase() + s.slice(1)).join(', '),
|
|
167
|
+
value: value.status,
|
|
168
|
+
}
|
|
169
|
+
isActive.value = true
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function handleReset() {
|
|
173
|
+
selected.value = { identifier: 'status', label: 'Status', value: [] }
|
|
174
|
+
isActive.value = false
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return { selected, isActive, handleApply, handleReset, args }
|
|
178
|
+
},
|
|
179
|
+
template: `
|
|
180
|
+
<div class="flex flex-col gap-4 p-4">
|
|
181
|
+
<CheckboxFilter
|
|
182
|
+
v-bind="args"
|
|
183
|
+
:selected="selected"
|
|
184
|
+
:is-active="isActive"
|
|
185
|
+
@apply="handleApply"
|
|
186
|
+
@reset="handleReset"
|
|
187
|
+
/>
|
|
188
|
+
</div>
|
|
189
|
+
`,
|
|
190
|
+
}),
|
|
191
|
+
args: {
|
|
192
|
+
buttonLabel: 'Status',
|
|
193
|
+
identifier: 'status',
|
|
194
|
+
options: {
|
|
195
|
+
success: { value: 'success', label: 'Succeeded' },
|
|
196
|
+
failed: { value: 'failed', label: 'Failed' },
|
|
197
|
+
},
|
|
198
|
+
isActive: true,
|
|
199
|
+
disabled: false,
|
|
200
|
+
loading: false,
|
|
201
|
+
},
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export const Disabled: Story = {
|
|
205
|
+
...Default,
|
|
206
|
+
args: {
|
|
207
|
+
...Default.args,
|
|
208
|
+
disabled: true,
|
|
209
|
+
},
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export const Loading: Story = {
|
|
213
|
+
...Default,
|
|
214
|
+
args: {
|
|
215
|
+
...Default.args,
|
|
216
|
+
loading: true,
|
|
217
|
+
},
|
|
218
|
+
}
|