@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,23 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from 'vue'
|
|
3
|
+
import { reactiveOmit } from '@vueuse/core'
|
|
4
|
+
import { TabsList } from 'reka-ui'
|
|
5
|
+
import type { TabsListProps } from 'reka-ui'
|
|
6
|
+
import { cn } from '@/lib/utils'
|
|
7
|
+
|
|
8
|
+
const props = defineProps<TabsListProps & { class?: HTMLAttributes['class'] }>()
|
|
9
|
+
|
|
10
|
+
const delegatedProps = reactiveOmit(props, 'class')
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<template>
|
|
14
|
+
<TabsList
|
|
15
|
+
v-bind="delegatedProps"
|
|
16
|
+
:class="cn(
|
|
17
|
+
'relative inline-flex border-b-[0.5px] border-gray-200 items-center bg-white-1000 px-2 py-0.5',
|
|
18
|
+
props.class,
|
|
19
|
+
)"
|
|
20
|
+
>
|
|
21
|
+
<slot />
|
|
22
|
+
</TabsList>
|
|
23
|
+
</template>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { TabsRootEmits, TabsRootProps } from 'reka-ui'
|
|
3
|
+
import { TabsRoot, useForwardPropsEmits } from 'reka-ui'
|
|
4
|
+
|
|
5
|
+
const props = defineProps<TabsRootProps>()
|
|
6
|
+
const emits = defineEmits<TabsRootEmits>()
|
|
7
|
+
|
|
8
|
+
const forwarded = useForwardPropsEmits(props, emits)
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<template>
|
|
12
|
+
<TabsRoot v-bind="forwarded">
|
|
13
|
+
<slot />
|
|
14
|
+
</TabsRoot>
|
|
15
|
+
</template>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from 'vue'
|
|
3
|
+
import { reactiveOmit } from '@vueuse/core'
|
|
4
|
+
import { TabsTrigger, useForwardProps } from 'reka-ui'
|
|
5
|
+
import type { TabsTriggerProps } from 'reka-ui'
|
|
6
|
+
import { cn } from '@/lib/utils'
|
|
7
|
+
|
|
8
|
+
const props = defineProps<TabsTriggerProps & { class?: HTMLAttributes['class'] }>()
|
|
9
|
+
|
|
10
|
+
const delegatedProps = reactiveOmit(props, 'class')
|
|
11
|
+
|
|
12
|
+
const forwardedProps = useForwardProps(delegatedProps)
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<template>
|
|
16
|
+
<TabsTrigger
|
|
17
|
+
v-bind="forwardedProps"
|
|
18
|
+
:class="cn(
|
|
19
|
+
'inline-flex cursor-pointer items-center justify-center whitespace-nowrap px-[6px] py-1 text-sm font-medium transition-all focus-visible:outline-none disabled:pointer-events-none text-gray-400 hover:text-black disabled:opacity-50 data-[state=active]:text-black data-[state=active]:shadow-sm',
|
|
20
|
+
props.class,
|
|
21
|
+
)"
|
|
22
|
+
>
|
|
23
|
+
<span class="truncate">
|
|
24
|
+
<slot />
|
|
25
|
+
</span>
|
|
26
|
+
</TabsTrigger>
|
|
27
|
+
</template>
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
|
|
2
|
+
import * as TabsStories from './tabs.stories.ts';
|
|
3
|
+
|
|
4
|
+
<Meta of={TabsStories} />
|
|
5
|
+
|
|
6
|
+
# TelaTabs
|
|
7
|
+
|
|
8
|
+
A tabs component that organizes content into separate panels accessible via tab buttons. Supports v-model binding for the selected tab and provides a content slot for displaying tab-specific content. Useful for organizing related content into distinct sections.
|
|
9
|
+
|
|
10
|
+
## Examples
|
|
11
|
+
|
|
12
|
+
### Basic Usage
|
|
13
|
+
|
|
14
|
+
```vue
|
|
15
|
+
<script setup>
|
|
16
|
+
import { ref } from 'vue'
|
|
17
|
+
|
|
18
|
+
const selectedTab = ref('overview')
|
|
19
|
+
|
|
20
|
+
const tabs = [
|
|
21
|
+
{ tab: 'overview', label: 'Overview' },
|
|
22
|
+
{ tab: 'details', label: 'Details' },
|
|
23
|
+
{ tab: 'settings', label: 'Settings' }
|
|
24
|
+
]
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
<template>
|
|
28
|
+
<TelaTabs v-model="selectedTab" :options="tabs">
|
|
29
|
+
<template #content>
|
|
30
|
+
<div v-if="selectedTab === 'overview'">
|
|
31
|
+
<h3>Overview Content</h3>
|
|
32
|
+
<p>Overview information goes here</p>
|
|
33
|
+
</div>
|
|
34
|
+
<div v-else-if="selectedTab === 'details'">
|
|
35
|
+
<h3>Details Content</h3>
|
|
36
|
+
<p>Detailed information goes here</p>
|
|
37
|
+
</div>
|
|
38
|
+
<div v-else-if="selectedTab === 'settings'">
|
|
39
|
+
<h3>Settings Content</h3>
|
|
40
|
+
<p>Settings options go here</p>
|
|
41
|
+
</div>
|
|
42
|
+
</template>
|
|
43
|
+
</TelaTabs>
|
|
44
|
+
</template>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### With Default Value
|
|
48
|
+
|
|
49
|
+
```vue
|
|
50
|
+
<TelaTabs
|
|
51
|
+
v-model="selectedTab"
|
|
52
|
+
:options="tabs"
|
|
53
|
+
default-value="details"
|
|
54
|
+
>
|
|
55
|
+
<template #content>
|
|
56
|
+
<!-- Tab content -->
|
|
57
|
+
</template>
|
|
58
|
+
</TelaTabs>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Dynamic Tab Content
|
|
62
|
+
|
|
63
|
+
```vue
|
|
64
|
+
<script setup>
|
|
65
|
+
import { ref, computed } from 'vue'
|
|
66
|
+
|
|
67
|
+
const selectedTab = ref('tab1')
|
|
68
|
+
const tabs = [
|
|
69
|
+
{ tab: 'tab1', label: 'First Tab' },
|
|
70
|
+
{ tab: 'tab2', label: 'Second Tab' },
|
|
71
|
+
{ tab: 'tab3', label: 'Third Tab' }
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
const content = computed(() => {
|
|
75
|
+
const contentMap = {
|
|
76
|
+
tab1: 'Content for first tab',
|
|
77
|
+
tab2: 'Content for second tab',
|
|
78
|
+
tab3: 'Content for third tab'
|
|
79
|
+
}
|
|
80
|
+
return contentMap[selectedTab.value]
|
|
81
|
+
})
|
|
82
|
+
</script>
|
|
83
|
+
|
|
84
|
+
<template>
|
|
85
|
+
<TelaTabs v-model="selectedTab" :options="tabs">
|
|
86
|
+
<template #content>
|
|
87
|
+
<div>{{ content }}</div>
|
|
88
|
+
</template>
|
|
89
|
+
</TelaTabs>
|
|
90
|
+
</template>
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Props
|
|
94
|
+
|
|
95
|
+
<ArgTypes />
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
type TabOption = {
|
|
99
|
+
tab: string // value
|
|
100
|
+
label: string // display text
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
type TabsProps = {
|
|
104
|
+
modelValue?: string
|
|
105
|
+
options: TabOption[]
|
|
106
|
+
defaultValue?: string
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Slots
|
|
111
|
+
|
|
112
|
+
- `content` - Tab panel content area
|
|
113
|
+
|
|
114
|
+
## Components
|
|
115
|
+
|
|
116
|
+
The Tabs system consists of these reka-ui components:
|
|
117
|
+
- `TelaTabsRoot` - Root tabs container
|
|
118
|
+
- `TelaTabsList` - Container for tab triggers
|
|
119
|
+
- `TelaTabsTrigger` - Individual tab button
|
|
120
|
+
- `TelaTabsContent` - Tab panel content
|
|
121
|
+
- `TelaTabsIndicator` - Visual indicator for active tab
|
|
122
|
+
|
|
123
|
+
## Features
|
|
124
|
+
|
|
125
|
+
- **Two-way Binding**: Full v-model support for selected tab
|
|
126
|
+
- **Default Value**: Set initial tab on mount
|
|
127
|
+
- **Keyboard Navigation**: Arrow keys to switch tabs, Enter/Space to activate
|
|
128
|
+
- **Accessible**: Built on reka-ui with proper ARIA attributes
|
|
129
|
+
- **Flexible Content**: Use slots for custom tab panel content
|
|
130
|
+
- **Visual Indicator**: Animated indicator shows active tab
|
|
131
|
+
|
|
132
|
+
## Accessibility
|
|
133
|
+
|
|
134
|
+
- Built on reka-ui primitives
|
|
135
|
+
- Proper ARIA attributes (role="tablist", role="tab", role="tabpanel")
|
|
136
|
+
- Keyboard navigation (Arrow keys, Home, End, Space, Enter)
|
|
137
|
+
- Focus management between tabs
|
|
138
|
+
- Selected state properly conveyed to screen readers
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { ref } from 'vue'
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/vue3'
|
|
3
|
+
import Tabs from './tabs.vue'
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof Tabs> = {
|
|
6
|
+
title: 'Core/Tabs',
|
|
7
|
+
component: Tabs,
|
|
8
|
+
parameters: {
|
|
9
|
+
layout: 'centered',
|
|
10
|
+
docs: {
|
|
11
|
+
description: {
|
|
12
|
+
component: 'A tabs component that organizes content into separate panels accessible via tab buttons. Supports v-model binding for the selected tab and provides a content slot for displaying tab-specific content. Useful for organizing related content into distinct sections.',
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
argTypes: {
|
|
17
|
+
modelValue: {
|
|
18
|
+
control: 'text',
|
|
19
|
+
description: 'The currently selected tab value (v-model).',
|
|
20
|
+
},
|
|
21
|
+
options: {
|
|
22
|
+
control: 'object',
|
|
23
|
+
description: 'Array of tab options. Each option should have a `tab` (value) and `label` (display text) property.',
|
|
24
|
+
},
|
|
25
|
+
defaultValue: {
|
|
26
|
+
control: 'text',
|
|
27
|
+
description: 'Default selected tab value when component first mounts.',
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export default meta
|
|
33
|
+
|
|
34
|
+
type Story = StoryObj<typeof meta>
|
|
35
|
+
|
|
36
|
+
export const Default: Story = {
|
|
37
|
+
render: args => ({
|
|
38
|
+
components: { Tabs },
|
|
39
|
+
setup() {
|
|
40
|
+
const selectedTab = ref('tab1')
|
|
41
|
+
return { selectedTab, args }
|
|
42
|
+
},
|
|
43
|
+
template: `
|
|
44
|
+
<Tabs v-model="selectedTab" :options="args.options" :default-value="args.defaultValue">
|
|
45
|
+
<template #content>
|
|
46
|
+
<div min-w-250px>
|
|
47
|
+
<div v-if="selectedTab === 'tab1'" class="flex flex-col gap-2">
|
|
48
|
+
<h3 body-16-semibold>Tab 1 Content</h3>
|
|
49
|
+
<p body-14-regular>This is the content for Tab 1.</p>
|
|
50
|
+
</div>
|
|
51
|
+
<div v-else-if="selectedTab === 'tab2'" class="flex flex-col gap-2">
|
|
52
|
+
<h3 body-16-semibold>Tab 2 Content</h3>
|
|
53
|
+
<p body-14-regular>This is the content for Tab 2.</p>
|
|
54
|
+
</div>
|
|
55
|
+
<div v-else-if="selectedTab === 'tab3'" class="flex flex-col gap-2">
|
|
56
|
+
<h3 body-16-semibold>Tab 3 Content</h3>
|
|
57
|
+
<p body-14-regular>This is the content for Tab 3.</p>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
</template>
|
|
61
|
+
</Tabs>
|
|
62
|
+
`,
|
|
63
|
+
}),
|
|
64
|
+
args: {
|
|
65
|
+
options: [
|
|
66
|
+
{ tab: 'tab1', label: 'Tab 1' },
|
|
67
|
+
{ tab: 'tab2', label: 'Tab 2' },
|
|
68
|
+
{ tab: 'tab3', label: 'Tab 3' },
|
|
69
|
+
],
|
|
70
|
+
defaultValue: 'tab1',
|
|
71
|
+
},
|
|
72
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { TabsRootProps } from 'reka-ui'
|
|
3
|
+
import type { HTMLAttributes } from 'vue'
|
|
4
|
+
|
|
5
|
+
import TelaTabsRoot from './tabs-root.vue'
|
|
6
|
+
import TelaTabsList from './tabs-list.vue'
|
|
7
|
+
import TelaTabsIndicator from './tabs-indicator.vue'
|
|
8
|
+
import TelaTabsTrigger from './tabs-trigger.vue'
|
|
9
|
+
import TelaTabsContent from './tabs-content.vue'
|
|
10
|
+
|
|
11
|
+
type Option = { tab: string, label: string }
|
|
12
|
+
|
|
13
|
+
interface Props extends TabsRootProps {
|
|
14
|
+
options: Option[]
|
|
15
|
+
defaultValue?: string
|
|
16
|
+
tabsListClass?: HTMLAttributes['class']
|
|
17
|
+
tabsTriggerClass?: HTMLAttributes['class']
|
|
18
|
+
tabsContentClass?: HTMLAttributes['class']
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const props = defineProps<Props>()
|
|
22
|
+
|
|
23
|
+
const tabs = computed(() => {
|
|
24
|
+
if (!props.options || props.options.length === 0) {
|
|
25
|
+
return []
|
|
26
|
+
}
|
|
27
|
+
return props.options.map(option => option.tab)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
const selectedTab = defineModel<string>({ required: true })
|
|
31
|
+
const defaultTab = computed(() => {
|
|
32
|
+
if (props.defaultValue) {
|
|
33
|
+
return props.defaultValue
|
|
34
|
+
}
|
|
35
|
+
if (props.options && props.options.length > 0) {
|
|
36
|
+
return props.options[0].tab
|
|
37
|
+
}
|
|
38
|
+
return ''
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
function getLabel(tab: string) {
|
|
42
|
+
if (!props.options) {
|
|
43
|
+
return ''
|
|
44
|
+
}
|
|
45
|
+
return props.options.find(option => option.tab === tab)?.label || ''
|
|
46
|
+
}
|
|
47
|
+
</script>
|
|
48
|
+
|
|
49
|
+
<template>
|
|
50
|
+
<TelaTabsRoot v-model="selectedTab" :default-value="defaultTab">
|
|
51
|
+
<TelaTabsList :class="cn('px-0 gap-3', tabsListClass)">
|
|
52
|
+
<TelaTabsIndicator />
|
|
53
|
+
<TelaTabsTrigger v-for="tab in tabs" :key="tab" :value="tab" :class="cn('px-0', tabsTriggerClass)">
|
|
54
|
+
{{ getLabel(tab) }}
|
|
55
|
+
</TelaTabsTrigger>
|
|
56
|
+
</TelaTabsList>
|
|
57
|
+
<TelaTabsContent :value="selectedTab" :class="cn('pt-4', tabsContentClass)">
|
|
58
|
+
<slot name="content" />
|
|
59
|
+
</TelaTabsContent>
|
|
60
|
+
</TelaTabsRoot>
|
|
61
|
+
</template>
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
|
|
2
|
+
import * as TagsSelectStories from './tags-select.stories.ts';
|
|
3
|
+
|
|
4
|
+
<Meta of={TagsSelectStories} />
|
|
5
|
+
|
|
6
|
+
# TelaTagsSelect
|
|
7
|
+
|
|
8
|
+
A comprehensive tag management component that combines selection, creation, and editing capabilities in a popover interface. Displays selected tags as overlapping colored dots (up to 3) with a mask effect, and provides a full-featured interface for managing tags with 16 color options.
|
|
9
|
+
|
|
10
|
+
## Examples
|
|
11
|
+
|
|
12
|
+
### Default
|
|
13
|
+
|
|
14
|
+
<Canvas of={TagsSelectStories.Default} />
|
|
15
|
+
|
|
16
|
+
### Basic Usage
|
|
17
|
+
|
|
18
|
+
```vue
|
|
19
|
+
<script setup>
|
|
20
|
+
const selectedTags = ref([
|
|
21
|
+
{ id: '1', name: 'Design', color: 'purple' },
|
|
22
|
+
{ id: '2', name: 'Development', color: 'blue' }
|
|
23
|
+
])
|
|
24
|
+
|
|
25
|
+
const availableTags = ref([
|
|
26
|
+
{ id: '1', name: 'Design', color: 'purple' },
|
|
27
|
+
{ id: '2', name: 'Development', color: 'blue' },
|
|
28
|
+
{ id: '3', name: 'Marketing', color: 'green' }
|
|
29
|
+
])
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<template>
|
|
33
|
+
<TelaTagsSelect
|
|
34
|
+
v-model="selectedTags"
|
|
35
|
+
:options="availableTags"
|
|
36
|
+
/>
|
|
37
|
+
</template>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### With Custom Labels
|
|
41
|
+
|
|
42
|
+
```vue
|
|
43
|
+
<TelaTagsSelect
|
|
44
|
+
v-model="selectedTags"
|
|
45
|
+
:options="availableTags"
|
|
46
|
+
create-tag-label="Nova tag"
|
|
47
|
+
selected-label="selecionadas"
|
|
48
|
+
new-tag-label="Nova tag"
|
|
49
|
+
edit-tag-label="Editar tag"
|
|
50
|
+
define-name-placeholder="Defina um nome"
|
|
51
|
+
add-new-name-placeholder="Adicionar novo nome"
|
|
52
|
+
create-button-label="Criar"
|
|
53
|
+
save-changes-button-label="Salvar alterações"
|
|
54
|
+
/>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Programmatic Tag Management
|
|
58
|
+
|
|
59
|
+
```vue
|
|
60
|
+
<script setup>
|
|
61
|
+
const selectedTags = ref([])
|
|
62
|
+
const availableTags = ref([
|
|
63
|
+
{ id: '1', name: 'Work', color: 'blue' },
|
|
64
|
+
{ id: '2', name: 'Personal', color: 'green' },
|
|
65
|
+
{ id: '3', name: 'Urgent', color: 'red' }
|
|
66
|
+
])
|
|
67
|
+
|
|
68
|
+
function addTag(tag) {
|
|
69
|
+
selectedTags.value = [...selectedTags.value, tag]
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function removeTag(tagId) {
|
|
73
|
+
selectedTags.value = selectedTags.value.filter(t => t.id !== tagId)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function clearAll() {
|
|
77
|
+
selectedTags.value = []
|
|
78
|
+
}
|
|
79
|
+
</script>
|
|
80
|
+
|
|
81
|
+
<template>
|
|
82
|
+
<div class="flex flex-col gap-4">
|
|
83
|
+
<TelaTagsSelect
|
|
84
|
+
v-model="selectedTags"
|
|
85
|
+
:options="availableTags"
|
|
86
|
+
/>
|
|
87
|
+
|
|
88
|
+
<div class="flex gap-2">
|
|
89
|
+
<TelaButton size="sm" @click="clearAll">
|
|
90
|
+
Clear All
|
|
91
|
+
</TelaButton>
|
|
92
|
+
<TelaButton size="sm" variant="secondary" @click="addTag(availableTags[0])">
|
|
93
|
+
Select First
|
|
94
|
+
</TelaButton>
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
</template>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Read-Only Display
|
|
101
|
+
|
|
102
|
+
```vue
|
|
103
|
+
<script setup>
|
|
104
|
+
const tags = ref([
|
|
105
|
+
{ id: '1', name: 'Completed', color: 'green' },
|
|
106
|
+
{ id: '2', name: 'Reviewed', color: 'blue' },
|
|
107
|
+
{ id: '3', name: 'Published', color: 'purple' }
|
|
108
|
+
])
|
|
109
|
+
</script>
|
|
110
|
+
|
|
111
|
+
<template>
|
|
112
|
+
<TelaTagsSelect
|
|
113
|
+
:model-value="tags"
|
|
114
|
+
:options="tags"
|
|
115
|
+
/>
|
|
116
|
+
</template>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Props
|
|
120
|
+
|
|
121
|
+
<ArgTypes />
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
type Color = 'orange' | 'amber' | 'yellow' | 'lime' | 'green' | 'emerald' | 'teal' | 'cyan' | 'sky' | 'blue' | 'indigo' | 'purple' | 'pink' | 'rose' | 'red' | 'neutral'
|
|
125
|
+
|
|
126
|
+
interface Tag {
|
|
127
|
+
id: string
|
|
128
|
+
color: Color
|
|
129
|
+
name: string
|
|
130
|
+
isSelected?: boolean // Computed internally, don't include when passing tags
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
type TagsSelectProps = {
|
|
134
|
+
class?: string
|
|
135
|
+
labelClass?: string
|
|
136
|
+
modelValue?: Tag[]
|
|
137
|
+
options: Tag[]
|
|
138
|
+
createTagLabel?: string
|
|
139
|
+
selectedLabel?: string
|
|
140
|
+
newTagLabel?: string
|
|
141
|
+
editTagLabel?: string
|
|
142
|
+
defineNamePlaceholder?: string
|
|
143
|
+
addNewNamePlaceholder?: string
|
|
144
|
+
createButtonLabel?: string
|
|
145
|
+
saveChangesButtonLabel?: string
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Features
|
|
150
|
+
|
|
151
|
+
### Tag Management Interface
|
|
152
|
+
- **Three Content Views**:
|
|
153
|
+
1. **New Tag**: Create new tags with name and color selection
|
|
154
|
+
2. **Existing Tags**: Browse, select, and manage existing tags
|
|
155
|
+
3. **Edit Tag**: Modify tag name and color
|
|
156
|
+
|
|
157
|
+
### Visual Feedback
|
|
158
|
+
- **Overlapping Dots**: Shows up to 3 tag colors with a mask effect
|
|
159
|
+
- **Dynamic Label**: Updates based on selection state
|
|
160
|
+
- "Create tag" when no tags exist
|
|
161
|
+
- "X tags" when tags exist but none selected
|
|
162
|
+
- "X selected" when tags are selected
|
|
163
|
+
|
|
164
|
+
### Tag Creation
|
|
165
|
+
- Name input with enter key support
|
|
166
|
+
- 16 color options in an 8-column grid
|
|
167
|
+
- Real-time color preview
|
|
168
|
+
- Automatic selection upon creation
|
|
169
|
+
- "Create More" option to add multiple tags
|
|
170
|
+
|
|
171
|
+
### Tag Selection
|
|
172
|
+
- Visual checkbox for each tag
|
|
173
|
+
- Colored dot indicator showing tag color
|
|
174
|
+
- Hover states with edit button
|
|
175
|
+
- Multi-select capability
|
|
176
|
+
- Apply button to confirm selections
|
|
177
|
+
|
|
178
|
+
### Tag Editing
|
|
179
|
+
- Edit existing tag names
|
|
180
|
+
- Change tag colors
|
|
181
|
+
- Updates all instances of the tag
|
|
182
|
+
- Preserves selection state during edits
|
|
183
|
+
- Cancel option to discard changes
|
|
184
|
+
|
|
185
|
+
### Animations & Transitions
|
|
186
|
+
- **Popover**: Smooth slide-down/fade-in animation (120ms)
|
|
187
|
+
- **Content Switching**: Slide transitions between views with spring physics
|
|
188
|
+
- **Height Animation**: Smooth height transitions when switching content
|
|
189
|
+
- **Spring Configuration**: Natural bounce-free animations (duration: 500ms)
|
|
190
|
+
|
|
191
|
+
## Color Options
|
|
192
|
+
|
|
193
|
+
The component supports 16 different colors:
|
|
194
|
+
|
|
195
|
+
**Warm Colors:**
|
|
196
|
+
- orange, amber, yellow
|
|
197
|
+
|
|
198
|
+
**Fresh Colors:**
|
|
199
|
+
- lime, green, emerald
|
|
200
|
+
|
|
201
|
+
**Cool Colors:**
|
|
202
|
+
- teal, cyan, sky, blue
|
|
203
|
+
|
|
204
|
+
**Deep Colors:**
|
|
205
|
+
- indigo, purple
|
|
206
|
+
|
|
207
|
+
**Vibrant Colors:**
|
|
208
|
+
- pink, rose, red
|
|
209
|
+
|
|
210
|
+
**Neutral:**
|
|
211
|
+
- neutral (gray)
|
|
212
|
+
|
|
213
|
+
Each color is available in both standard (500 shade) and light (300 shade) variants.
|
|
214
|
+
|
|
215
|
+
## Behavior
|
|
216
|
+
|
|
217
|
+
### Tag Merging
|
|
218
|
+
The component intelligently merges `modelValue` and `options` to create a complete list of available tags:
|
|
219
|
+
- Uses tag `id` as the unique identifier
|
|
220
|
+
- Prevents duplicates automatically
|
|
221
|
+
- Maintains selection state across merges
|
|
222
|
+
|
|
223
|
+
### Selection State
|
|
224
|
+
- Selection is tracked internally using tag IDs
|
|
225
|
+
- Changes are emitted via `update:modelValue` event
|
|
226
|
+
- Supports v-model for two-way binding
|
|
227
|
+
- Selection state persists during editing operations
|
|
228
|
+
|
|
229
|
+
### Keyboard Support
|
|
230
|
+
- **Enter**: Create new tag or save edited tag
|
|
231
|
+
- Tab navigation through color options
|
|
232
|
+
- Escape to close popover (via Reka UI)
|
|
233
|
+
|
|
234
|
+
### Dot Display Logic
|
|
235
|
+
1. **With Selected Tags**: Shows first 3 selected tags' colors
|
|
236
|
+
2. **With Available Tags**: Shows first 3 available tags' colors
|
|
237
|
+
3. **No Tags**: Shows placeholder dots (2 neutral + 1 with plus icon)
|
|
238
|
+
|
|
239
|
+
## Popover States
|
|
240
|
+
|
|
241
|
+
### Trigger Button
|
|
242
|
+
- Displays overlapping colored dots
|
|
243
|
+
- Shows dynamic label text
|
|
244
|
+
- Hover and open states with background changes
|
|
245
|
+
- Smooth transitions (80ms ease-in-out)
|
|
246
|
+
|
|
247
|
+
### Content Panel
|
|
248
|
+
- 220px width
|
|
249
|
+
- Shadow and border styling
|
|
250
|
+
- Smooth entry/exit animations
|
|
251
|
+
- Automatic positioning via Reka UI
|
|
252
|
+
- 8px offset from trigger
|
|
253
|
+
|
|
254
|
+
## Styling
|
|
255
|
+
|
|
256
|
+
### Dot Mask Effect
|
|
257
|
+
The overlapping dot effect is achieved using CSS mask:
|
|
258
|
+
```css
|
|
259
|
+
.dot-mask:not(:last-child) {
|
|
260
|
+
mask-image: radial-gradient(circle 6px at right center, transparent 6px, #fff 6px);
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### Animation Keyframes
|
|
265
|
+
- **slideDownAndFade**: Entry animation with scale and translate
|
|
266
|
+
- **slideUpAndFade**: Exit animation with scale and translate
|
|
267
|
+
- Transform origin set to popover content origin
|
|
268
|
+
|
|
269
|
+
## Integration with Form Systems
|
|
270
|
+
|
|
271
|
+
The component works seamlessly with form libraries:
|
|
272
|
+
|
|
273
|
+
```vue
|
|
274
|
+
<script setup>
|
|
275
|
+
import { useForm } from 'vee-validate'
|
|
276
|
+
|
|
277
|
+
const { values } = useForm({
|
|
278
|
+
initialValues: {
|
|
279
|
+
tags: []
|
|
280
|
+
}
|
|
281
|
+
})
|
|
282
|
+
</script>
|
|
283
|
+
|
|
284
|
+
<template>
|
|
285
|
+
<Field v-slot="{ value, handleChange }" name="tags">
|
|
286
|
+
<TelaTagsSelect
|
|
287
|
+
:model-value="value"
|
|
288
|
+
:options="availableTags"
|
|
289
|
+
@update:model-value="handleChange"
|
|
290
|
+
/>
|
|
291
|
+
</Field>
|
|
292
|
+
</template>
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
## Accessibility
|
|
296
|
+
|
|
297
|
+
- Proper popover semantics via Reka UI
|
|
298
|
+
- Keyboard navigation support
|
|
299
|
+
- Focus management on open/close
|
|
300
|
+
- Clear visual feedback for interactive elements
|
|
301
|
+
- Semantic button and form elements
|
|
302
|
+
- Proper ARIA attributes from Reka UI
|
|
303
|
+
|
|
304
|
+
## Performance Considerations
|
|
305
|
+
|
|
306
|
+
- Efficient tag merging with Set-based deduplication
|
|
307
|
+
- Computed properties for derived state
|
|
308
|
+
- Reactive refs for optimal reactivity
|
|
309
|
+
- Animation optimizations with GPU-accelerated transforms
|
|
310
|
+
- Lazy content rendering based on selected view
|
|
311
|
+
|
|
312
|
+
## Best Practices
|
|
313
|
+
|
|
314
|
+
1. **Always provide unique IDs**: Each tag must have a unique `id` property
|
|
315
|
+
2. **Don't include `isSelected`**: This property is computed internally
|
|
316
|
+
3. **Use v-model**: Prefer v-model over manual event handling
|
|
317
|
+
4. **Provide options**: Always pass available tags via the `options` prop
|
|
318
|
+
5. **Localize labels**: Use the label props for internationalization support
|