@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,155 @@
|
|
|
1
|
+
import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
|
|
2
|
+
import * as ButtonStories from './button.stories.ts';
|
|
3
|
+
|
|
4
|
+
<Meta of={ButtonStories} />
|
|
5
|
+
|
|
6
|
+
# TelaButton
|
|
7
|
+
|
|
8
|
+
A versatile button component with multiple variants, sizes, and states. Supports icons, loading states, and can function as a link when provided with a `to` prop. Built for consistent UI interactions across the application.
|
|
9
|
+
|
|
10
|
+
## Examples
|
|
11
|
+
|
|
12
|
+
### All Sizes
|
|
13
|
+
|
|
14
|
+
<Canvas of={ButtonStories.Sizes} />
|
|
15
|
+
|
|
16
|
+
### With Icon
|
|
17
|
+
|
|
18
|
+
<Canvas of={ButtonStories.WithIcon} />
|
|
19
|
+
|
|
20
|
+
### Loading State
|
|
21
|
+
|
|
22
|
+
<Canvas of={ButtonStories.Loading} />
|
|
23
|
+
|
|
24
|
+
### As a Link
|
|
25
|
+
|
|
26
|
+
<Canvas of={ButtonStories.LinkButton} />
|
|
27
|
+
|
|
28
|
+
### All Variants
|
|
29
|
+
|
|
30
|
+
<Canvas of={ButtonStories.Variants} />
|
|
31
|
+
|
|
32
|
+
### Disabled State
|
|
33
|
+
|
|
34
|
+
<Canvas of={ButtonStories.Disabled} />
|
|
35
|
+
|
|
36
|
+
### All Combinations
|
|
37
|
+
|
|
38
|
+
<Canvas of={ButtonStories.AllCombinations} />
|
|
39
|
+
|
|
40
|
+
### Basic Usage
|
|
41
|
+
|
|
42
|
+
```vue
|
|
43
|
+
<TelaButton variant="primary" size="md">
|
|
44
|
+
Click me
|
|
45
|
+
</TelaButton>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### With Icon Code
|
|
49
|
+
|
|
50
|
+
```vue
|
|
51
|
+
<TelaButton icon="i-ph-plus" leading variant="primary">
|
|
52
|
+
Add Item
|
|
53
|
+
</TelaButton>
|
|
54
|
+
|
|
55
|
+
<TelaButton icon="i-ph-trash" variant="danger">
|
|
56
|
+
Delete
|
|
57
|
+
</TelaButton>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Loading State Code
|
|
61
|
+
|
|
62
|
+
```vue
|
|
63
|
+
<TelaButton loading variant="primary">
|
|
64
|
+
Saving...
|
|
65
|
+
</TelaButton>
|
|
66
|
+
|
|
67
|
+
<TelaButton loading leading variant="secondary">
|
|
68
|
+
Processing
|
|
69
|
+
</TelaButton>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### As a Link Code
|
|
73
|
+
|
|
74
|
+
```vue
|
|
75
|
+
<TelaButton to="/home" variant="secondary">
|
|
76
|
+
Go to Home
|
|
77
|
+
</TelaButton>
|
|
78
|
+
|
|
79
|
+
<TelaButton to="https://example.com" target="_blank" variant="ghost">
|
|
80
|
+
External Link
|
|
81
|
+
</TelaButton>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Using Slots
|
|
85
|
+
|
|
86
|
+
```vue
|
|
87
|
+
<TelaButton variant="primary">
|
|
88
|
+
<template #leading>
|
|
89
|
+
<TelaIcon name="i-ph-check" />
|
|
90
|
+
</template>
|
|
91
|
+
Confirm
|
|
92
|
+
</TelaButton>
|
|
93
|
+
|
|
94
|
+
<TelaButton variant="secondary">
|
|
95
|
+
Save
|
|
96
|
+
<template #trailing>
|
|
97
|
+
<TelaIcon name="i-ph-arrow-right" />
|
|
98
|
+
</template>
|
|
99
|
+
</TelaButton>
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Props
|
|
103
|
+
|
|
104
|
+
<ArgTypes />
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
type ButtonSize = 'sm' | 'md' | 'lg'
|
|
108
|
+
type ButtonVariant = 'primary' | 'secondary' | 'ghost' | 'success' | 'danger'
|
|
109
|
+
|
|
110
|
+
type ButtonProps = {
|
|
111
|
+
size?: ButtonSize
|
|
112
|
+
variant?: ButtonVariant
|
|
113
|
+
icon?: string
|
|
114
|
+
loading?: boolean
|
|
115
|
+
leading?: boolean
|
|
116
|
+
disabled?: boolean
|
|
117
|
+
to?: string
|
|
118
|
+
target?: '_blank' | null
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Slots
|
|
123
|
+
|
|
124
|
+
The Button component supports three slots:
|
|
125
|
+
|
|
126
|
+
- `default` - Main button content/text
|
|
127
|
+
- `leading` - Content before the main text (e.g., icons)
|
|
128
|
+
- `trailing` - Content after the main text (e.g., icons, badges)
|
|
129
|
+
|
|
130
|
+
## Features
|
|
131
|
+
|
|
132
|
+
- **Multiple Variants**: Primary, secondary, ghost, success, and danger styles
|
|
133
|
+
- **Flexible Sizing**: Small, medium, and large sizes
|
|
134
|
+
- **Icon Support**: Leading or trailing icons with automatic sizing
|
|
135
|
+
- **Loading State**: Built-in loading spinner with automatic disable
|
|
136
|
+
- **Link Functionality**: Renders as NuxtLink when `to` prop is provided
|
|
137
|
+
- **Disabled State**: Proper disabled styling and interaction prevention
|
|
138
|
+
- **Customizable**: Accepts custom classes for extended styling
|
|
139
|
+
|
|
140
|
+
## Styling
|
|
141
|
+
|
|
142
|
+
The button uses dynamic computed styles based on size and variant:
|
|
143
|
+
|
|
144
|
+
- **Primary**: Dark background with white text, hover and focus states
|
|
145
|
+
- **Secondary**: White background with border, subtle hover effects
|
|
146
|
+
- **Ghost**: Transparent background, minimal styling
|
|
147
|
+
- **Danger**: Red-themed for destructive actions
|
|
148
|
+
- **Disabled**: Grayed out, cursor not-allowed
|
|
149
|
+
|
|
150
|
+
## Accessibility
|
|
151
|
+
|
|
152
|
+
- Proper button semantics with `<button>` element
|
|
153
|
+
- Disabled state prevents interaction
|
|
154
|
+
- Focus visible styles for keyboard navigation
|
|
155
|
+
- Semantic link when using `to` prop
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/vue3'
|
|
2
|
+
import Button from './button.vue'
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof Button> = {
|
|
5
|
+
title: 'Core/Button',
|
|
6
|
+
component: Button,
|
|
7
|
+
parameters: {
|
|
8
|
+
layout: 'centered',
|
|
9
|
+
docs: {
|
|
10
|
+
description: {
|
|
11
|
+
component: 'A versatile button component with multiple variants, sizes, and states. Supports icons, loading states, and can function as a link when provided with a `to` prop. Built for consistent UI interactions across the application.',
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
argTypes: {
|
|
16
|
+
size: {
|
|
17
|
+
control: 'select',
|
|
18
|
+
options: ['xs', 'sm', 'md', 'lg'],
|
|
19
|
+
description: 'Size of the button. Controls padding, font size, and icon size.',
|
|
20
|
+
},
|
|
21
|
+
variant: {
|
|
22
|
+
control: 'select',
|
|
23
|
+
options: ['primary', 'secondary', 'ghost', 'success', 'danger'],
|
|
24
|
+
description: 'Visual style variant of the button. Primary is the default emphasized style, secondary is outlined, ghost is minimal, success is for positive actions, and danger is for destructive actions.',
|
|
25
|
+
},
|
|
26
|
+
icon: {
|
|
27
|
+
control: 'text',
|
|
28
|
+
description: 'Icon class or name to display in the button. Use icon names like "i-ph-plus" or similar iconify classes.',
|
|
29
|
+
},
|
|
30
|
+
loading: {
|
|
31
|
+
control: 'boolean',
|
|
32
|
+
description: 'Show loading spinner and disable button interaction while loading.',
|
|
33
|
+
},
|
|
34
|
+
leading: {
|
|
35
|
+
control: 'boolean',
|
|
36
|
+
description: 'Place icon before the text content. If false, icon appears after text.',
|
|
37
|
+
},
|
|
38
|
+
disabled: {
|
|
39
|
+
control: 'boolean',
|
|
40
|
+
description: 'Disable the button, preventing user interaction and applying disabled styling.',
|
|
41
|
+
},
|
|
42
|
+
to: {
|
|
43
|
+
control: 'text',
|
|
44
|
+
description: 'URL or route path to navigate to when the button is clicked. When provided, the button renders as a NuxtLink instead of a button element.',
|
|
45
|
+
},
|
|
46
|
+
target: {
|
|
47
|
+
control: 'text',
|
|
48
|
+
description: 'Target attribute for links. Use "_blank" to open in a new tab. Only applicable when `to` prop is provided.',
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export default meta
|
|
54
|
+
|
|
55
|
+
type Story = StoryObj<typeof Button>
|
|
56
|
+
|
|
57
|
+
export const Default: Story = {
|
|
58
|
+
render: args => ({
|
|
59
|
+
components: { Button },
|
|
60
|
+
setup() {
|
|
61
|
+
return { args }
|
|
62
|
+
},
|
|
63
|
+
template: `<Button v-bind="args">Button</Button>`,
|
|
64
|
+
}),
|
|
65
|
+
args: {
|
|
66
|
+
size: 'md',
|
|
67
|
+
variant: 'primary',
|
|
68
|
+
},
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export const Loading: Story = {
|
|
72
|
+
render: args => ({
|
|
73
|
+
components: { Button },
|
|
74
|
+
setup() {
|
|
75
|
+
return { args }
|
|
76
|
+
},
|
|
77
|
+
template: `<Button v-bind="args">Loading</Button>`,
|
|
78
|
+
}),
|
|
79
|
+
args: {
|
|
80
|
+
loading: true,
|
|
81
|
+
size: 'md',
|
|
82
|
+
variant: 'primary',
|
|
83
|
+
},
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export const Disabled: Story = {
|
|
87
|
+
render: args => ({
|
|
88
|
+
components: { Button },
|
|
89
|
+
setup() {
|
|
90
|
+
return { args }
|
|
91
|
+
},
|
|
92
|
+
template: `<Button v-bind="args">Disabled</Button>`,
|
|
93
|
+
}),
|
|
94
|
+
args: {
|
|
95
|
+
disabled: true,
|
|
96
|
+
size: 'md',
|
|
97
|
+
variant: 'primary',
|
|
98
|
+
},
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export const WithIcon: Story = {
|
|
102
|
+
render: args => ({
|
|
103
|
+
components: { Button },
|
|
104
|
+
setup() {
|
|
105
|
+
return { args }
|
|
106
|
+
},
|
|
107
|
+
template: `<Button v-bind="args">With Icon</Button>`,
|
|
108
|
+
}),
|
|
109
|
+
args: {
|
|
110
|
+
icon: 'i-ph-plus',
|
|
111
|
+
leading: true,
|
|
112
|
+
size: 'md',
|
|
113
|
+
variant: 'primary',
|
|
114
|
+
},
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export const LinkButton: Story = {
|
|
118
|
+
render: args => ({
|
|
119
|
+
components: { Button },
|
|
120
|
+
setup() {
|
|
121
|
+
return { args }
|
|
122
|
+
},
|
|
123
|
+
template: `<Button v-bind="args">Go to Home</Button>`,
|
|
124
|
+
}),
|
|
125
|
+
args: {
|
|
126
|
+
to: '/home',
|
|
127
|
+
size: 'md',
|
|
128
|
+
variant: 'secondary',
|
|
129
|
+
},
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export const Sizes: Story = {
|
|
133
|
+
render: () => ({
|
|
134
|
+
components: { Button },
|
|
135
|
+
template: `
|
|
136
|
+
<div style="display: flex; gap: 10px; align-items: center;">
|
|
137
|
+
<Button size="sm" variant="primary">SM</Button>
|
|
138
|
+
<Button size="md" variant="primary">MD</Button>
|
|
139
|
+
<Button size="lg" variant="primary">LG</Button>
|
|
140
|
+
</div>
|
|
141
|
+
`,
|
|
142
|
+
}),
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export const Variants: Story = {
|
|
146
|
+
render: () => ({
|
|
147
|
+
components: { Button },
|
|
148
|
+
template: `
|
|
149
|
+
<div style="display: flex; gap: 10px; flex-wrap: wrap; align-items: center;">
|
|
150
|
+
<Button variant="primary">Primary</Button>
|
|
151
|
+
<Button variant="secondary">Secondary</Button>
|
|
152
|
+
<Button variant="ghost">Ghost</Button>
|
|
153
|
+
<Button variant="danger">Danger</Button>
|
|
154
|
+
</div>
|
|
155
|
+
`,
|
|
156
|
+
}),
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export const AllCombinations: Story = {
|
|
160
|
+
render: () => ({
|
|
161
|
+
components: { Button },
|
|
162
|
+
template: `
|
|
163
|
+
<div style="display: flex; flex-direction: column; gap: 20px;">
|
|
164
|
+
<div>
|
|
165
|
+
<h3>Sizes</h3>
|
|
166
|
+
<div style="display: flex; gap: 10px; align-items: center;">
|
|
167
|
+
<Button size="sm" variant="primary">SM</Button>
|
|
168
|
+
<Button size="md" variant="primary">MD</Button>
|
|
169
|
+
<Button size="lg" variant="primary">LG</Button>
|
|
170
|
+
</div>
|
|
171
|
+
</div>
|
|
172
|
+
<div>
|
|
173
|
+
<h3>Variants</h3>
|
|
174
|
+
<div style="display: flex; gap: 10px; flex-wrap: wrap; align-items: center;">
|
|
175
|
+
<Button variant="primary">Primary</Button>
|
|
176
|
+
<Button variant="secondary">Secondary</Button>
|
|
177
|
+
<Button variant="ghost">Ghost</Button>
|
|
178
|
+
<Button variant="danger">Danger</Button>
|
|
179
|
+
</div>
|
|
180
|
+
</div>
|
|
181
|
+
<div>
|
|
182
|
+
<h3>States</h3>
|
|
183
|
+
<div style="display: flex; gap: 10px; align-items: center;">
|
|
184
|
+
<Button disabled variant="primary">Disabled</Button>
|
|
185
|
+
<Button loading variant="primary">Loading</Button>
|
|
186
|
+
</div>
|
|
187
|
+
</div>
|
|
188
|
+
<div>
|
|
189
|
+
<h3>With Icons</h3>
|
|
190
|
+
<div style="display: flex; gap: 10px; align-items: center;">
|
|
191
|
+
<Button icon="i-ph-plus-bold" leading variant="primary">Add</Button>
|
|
192
|
+
<Button icon="i-ph-trash-bold" variant="danger">Delete</Button>
|
|
193
|
+
</div>
|
|
194
|
+
</div>
|
|
195
|
+
<div>
|
|
196
|
+
<h3>Link Button</h3>
|
|
197
|
+
<Button to="/home" variant="secondary">Go to Home</Button>
|
|
198
|
+
</div>
|
|
199
|
+
</div>
|
|
200
|
+
`,
|
|
201
|
+
}),
|
|
202
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { NuxtLink } from '#components'
|
|
3
|
+
|
|
4
|
+
export type ButtonSize = 'sm' | 'md' | 'lg'
|
|
5
|
+
export type ButtonVariant = 'primary' | 'secondary' | 'ghost' | 'success' | 'danger'
|
|
6
|
+
|
|
7
|
+
export type ButtonProps = {
|
|
8
|
+
size?: ButtonSize
|
|
9
|
+
variant?: ButtonVariant
|
|
10
|
+
icon?: string
|
|
11
|
+
loading?: boolean
|
|
12
|
+
leading?: boolean
|
|
13
|
+
disabled?: boolean
|
|
14
|
+
to?: string
|
|
15
|
+
target?: '_blank' | null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const props = defineProps<ButtonProps>()
|
|
19
|
+
|
|
20
|
+
const resolvedSize = computed(() => props.size || 'md')
|
|
21
|
+
const resolvedVariant = computed(() =>
|
|
22
|
+
(props.disabled && 'disabled')
|
|
23
|
+
|| (props.loading && 'loading')
|
|
24
|
+
|| props.variant
|
|
25
|
+
|| 'primary',
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
const sizeStyle = computed(() => (({
|
|
29
|
+
sm: 'text-12px leading-16px font-580 tracking-normal px-10px py-4px rounded-9px',
|
|
30
|
+
md: 'text-14px leading-18px font-580 -tracking-0.01em px-12px py-7px rounded-11px [&>svg]:text-10px',
|
|
31
|
+
lg: 'text-16px leading-20px font-580 -tracking-0.01em px-16px py-10px rounded-13px [&>svg]:text-12px',
|
|
32
|
+
}) as Record<ButtonSize, string>)[resolvedSize.value] ?? '')
|
|
33
|
+
|
|
34
|
+
const variantStyle = computed(() => (({
|
|
35
|
+
primary: fold`
|
|
36
|
+
bg-gray-900 text-white
|
|
37
|
+
hover:bg-gray-800
|
|
38
|
+
active:bg-gray-700
|
|
39
|
+
focus-visible:ring-0.5px focus-visible:ring-cyan-600
|
|
40
|
+
`,
|
|
41
|
+
secondary: fold`
|
|
42
|
+
bg-white text-gray-900 border-gray-300 border-0.5px
|
|
43
|
+
hover:bg-gray-50 hover:border-gray-300 hover:shadow-subtle
|
|
44
|
+
active:bg-gray-100 active:border-gray-400/60
|
|
45
|
+
focus-visible:ring-0.5px focus-visible:ring-cyan-600
|
|
46
|
+
`,
|
|
47
|
+
ghost: fold`
|
|
48
|
+
bg-transparent text-gray-900
|
|
49
|
+
hover:bg-gray-100
|
|
50
|
+
focus-visible:ring-0.5px focus-visible:ring-cyan-600
|
|
51
|
+
`,
|
|
52
|
+
disabled: fold`
|
|
53
|
+
bg-gray-100 text-gray-400 !cursor-not-allowed
|
|
54
|
+
`,
|
|
55
|
+
loading: fold`
|
|
56
|
+
bg-gray-200 text-gray-600
|
|
57
|
+
`,
|
|
58
|
+
danger: fold`
|
|
59
|
+
bg-red-100 text-red-500
|
|
60
|
+
hover:bg-red-100/80
|
|
61
|
+
focus:outline-red-600
|
|
62
|
+
`,
|
|
63
|
+
}) as Record<ButtonVariant | 'disabled' | 'loading', string>)[resolvedVariant.value] ?? '')
|
|
64
|
+
|
|
65
|
+
const resolvedStyle = computed(() => [
|
|
66
|
+
sizeStyle.value,
|
|
67
|
+
variantStyle.value,
|
|
68
|
+
].join(' '))
|
|
69
|
+
|
|
70
|
+
const tag = computed(() => props.to ? NuxtLink : 'button')
|
|
71
|
+
|
|
72
|
+
const iconSize = computed(() => {
|
|
73
|
+
switch (sizeStyle.value) {
|
|
74
|
+
case 'xs':
|
|
75
|
+
return '11px'
|
|
76
|
+
case 'sm':
|
|
77
|
+
return '11px'
|
|
78
|
+
case 'md':
|
|
79
|
+
return '13px'
|
|
80
|
+
case 'lg':
|
|
81
|
+
return '13px'
|
|
82
|
+
default:
|
|
83
|
+
return '13px'
|
|
84
|
+
}
|
|
85
|
+
})
|
|
86
|
+
</script>
|
|
87
|
+
|
|
88
|
+
<template>
|
|
89
|
+
<component
|
|
90
|
+
:is="tag"
|
|
91
|
+
:to="to"
|
|
92
|
+
:class="resolvedStyle" transition ease-in-out duration-80 flex items-center justify-center gap-6px
|
|
93
|
+
:disabled="disabled"
|
|
94
|
+
:cursor="disabled ? 'not-allowed' : 'pointer'"
|
|
95
|
+
:target="target"
|
|
96
|
+
border-box
|
|
97
|
+
select-none
|
|
98
|
+
>
|
|
99
|
+
<TelaIconSpinner v-if="loading && leading" animate-spin />
|
|
100
|
+
<slot v-else-if="$slots.leading" name="leading" />
|
|
101
|
+
<TelaIcon v-else-if="icon && leading" :name="icon" :size="iconSize" min-h-14px min-w-14px />
|
|
102
|
+
<slot />
|
|
103
|
+
<TelaIconSpinner v-if="loading && !leading" animate-spin ml-0.5 />
|
|
104
|
+
<slot v-else-if="$slots.trailing" name="trailing" />
|
|
105
|
+
<TelaIcon v-else-if="icon && !leading" :name="icon" :size="iconSize" />
|
|
106
|
+
</component>
|
|
107
|
+
</template>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { defineProps, ref } from 'vue'
|
|
3
|
+
|
|
4
|
+
defineProps<{
|
|
5
|
+
contentPadding?: string;
|
|
6
|
+
borderRadius?: string;
|
|
7
|
+
}>()
|
|
8
|
+
|
|
9
|
+
const rootEl = ref<HTMLElement | null>(null)
|
|
10
|
+
|
|
11
|
+
defineExpose({
|
|
12
|
+
el: rootEl,
|
|
13
|
+
})
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<template>
|
|
17
|
+
<div
|
|
18
|
+
ref="rootEl"
|
|
19
|
+
bg-white
|
|
20
|
+
:class="borderRadius ? `rounded-${borderRadius}` : 'rounded-16px'"
|
|
21
|
+
b=".5 gray-200"
|
|
22
|
+
>
|
|
23
|
+
<div :class="contentPadding ?? 'p-32px'">
|
|
24
|
+
<slot />
|
|
25
|
+
</div>
|
|
26
|
+
<div v-if="$slots.footer" p-19px b="t-1 #EBEBEB">
|
|
27
|
+
<slot name="footer" />
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
</template>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
const props = withDefaults(defineProps<{
|
|
3
|
+
total: number
|
|
4
|
+
filled: number
|
|
5
|
+
height?: string
|
|
6
|
+
gap?: string
|
|
7
|
+
duration?: number
|
|
8
|
+
}>(), {
|
|
9
|
+
height: '12px',
|
|
10
|
+
gap: '2px',
|
|
11
|
+
duration: 1000,
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
const animatedFilled = ref(0)
|
|
15
|
+
|
|
16
|
+
const bars = computed(() => {
|
|
17
|
+
return Array.from({ length: props.total }, (_, index) => ({
|
|
18
|
+
index,
|
|
19
|
+
isFilled: index < animatedFilled.value,
|
|
20
|
+
}))
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
watch(() => props.filled, (newValue) => {
|
|
24
|
+
const startValue = animatedFilled.value
|
|
25
|
+
const startTime = Date.now()
|
|
26
|
+
const animate = () => {
|
|
27
|
+
const elapsed = Date.now() - startTime
|
|
28
|
+
const progress = Math.min(elapsed / props.duration, 1)
|
|
29
|
+
|
|
30
|
+
const easeOutQuad = (t: number) => t * (2 - t)
|
|
31
|
+
const easedProgress = easeOutQuad(progress)
|
|
32
|
+
|
|
33
|
+
animatedFilled.value = Math.floor(startValue + (newValue - startValue) * easedProgress)
|
|
34
|
+
|
|
35
|
+
if (progress < 1) {
|
|
36
|
+
requestAnimationFrame(animate)
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
animatedFilled.value = newValue
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
animate()
|
|
44
|
+
}, { immediate: true })
|
|
45
|
+
</script>
|
|
46
|
+
|
|
47
|
+
<template>
|
|
48
|
+
<div flex="~" :style="{ gap }" items-center>
|
|
49
|
+
<div
|
|
50
|
+
v-for="bar in bars"
|
|
51
|
+
:key="bar.index"
|
|
52
|
+
:style="{ height }"
|
|
53
|
+
w-1px
|
|
54
|
+
rounded-1px
|
|
55
|
+
:bg="bar.isFilled ? '#5DC16B' : '#DFE3E7'"
|
|
56
|
+
/>
|
|
57
|
+
</div>
|
|
58
|
+
</template>
|