@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,47 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/vue3'
|
|
2
|
+
import TagsSelect from './tags-select.vue'
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof TagsSelect> = {
|
|
5
|
+
title: 'Core/TagsSelect',
|
|
6
|
+
component: TagsSelect,
|
|
7
|
+
parameters: {
|
|
8
|
+
layout: 'centered',
|
|
9
|
+
docs: {
|
|
10
|
+
description: {
|
|
11
|
+
component: 'A button variant of tags that displays multiple overlapping colored dots (up to 3) with optional icons. Used for actions like adding tags or showing tag groups. The dots are displayed with a mask effect to create an overlapping appearance. Each dot can have a custom color, icon, and icon color. The component opens a popover with a full tag management interface that allows users to create new tags, edit existing tags, and select/deselect tags. Supports 16 different color options and maintains a list of all created tags.',
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
argTypes: {
|
|
16
|
+
class: {
|
|
17
|
+
control: 'text',
|
|
18
|
+
description: 'Custom CSS classes for the button container.',
|
|
19
|
+
},
|
|
20
|
+
labelClass: {
|
|
21
|
+
control: 'text',
|
|
22
|
+
description: 'Custom CSS classes for the label text.',
|
|
23
|
+
},
|
|
24
|
+
modelValue: {
|
|
25
|
+
control: 'object',
|
|
26
|
+
description: 'Array of selected tags. Each tag must have a `color` (one of 16 predefined colors) and a `name` (string). The `isSelected` property is computed internally and should not be included when passing tags via v-model. This prop supports v-model binding.',
|
|
27
|
+
},
|
|
28
|
+
options: {
|
|
29
|
+
control: 'object',
|
|
30
|
+
description: 'Array of available tag options to display in the popover. These tags will be merged with the selected tags to form the complete list of available tags. Each tag must have a `color` and `name` property. The `isSelected` property is computed internally based on `modelValue` and should not be included when passing tags.',
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export default meta
|
|
36
|
+
|
|
37
|
+
type Story = StoryObj<typeof TagsSelect>
|
|
38
|
+
|
|
39
|
+
export const Default: Story = {
|
|
40
|
+
render: () => ({
|
|
41
|
+
components: { TagsSelect },
|
|
42
|
+
setup() {
|
|
43
|
+
return {}
|
|
44
|
+
},
|
|
45
|
+
template: `<TagsSelect>Add tag</TagsSelect>`,
|
|
46
|
+
}),
|
|
47
|
+
}
|
|
@@ -0,0 +1,637 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from 'vue'
|
|
3
|
+
import { PopoverRoot, PopoverTrigger, PopoverPortal, PopoverContent } from 'reka-ui'
|
|
4
|
+
|
|
5
|
+
const props = withDefaults(defineProps<{
|
|
6
|
+
class?: HTMLAttributes['class']
|
|
7
|
+
labelClass?: HTMLAttributes['class']
|
|
8
|
+
modelValue?: Tag[]
|
|
9
|
+
options: Tag[]
|
|
10
|
+
createTagLabel?: string
|
|
11
|
+
selectedLabel?: string
|
|
12
|
+
newTagLabel?: string
|
|
13
|
+
editTagLabel?: string
|
|
14
|
+
defineNamePlaceholder?: string
|
|
15
|
+
addNewNamePlaceholder?: string
|
|
16
|
+
createButtonLabel?: string
|
|
17
|
+
saveChangesButtonLabel?: string
|
|
18
|
+
}>(), {
|
|
19
|
+
modelValue: () => [],
|
|
20
|
+
createTagLabel: 'Create tag',
|
|
21
|
+
selectedLabel: 'selected',
|
|
22
|
+
newTagLabel: 'New tag',
|
|
23
|
+
editTagLabel: 'Edit tag',
|
|
24
|
+
defineNamePlaceholder: 'Define a name',
|
|
25
|
+
addNewNamePlaceholder: 'Add new name',
|
|
26
|
+
createButtonLabel: 'Create',
|
|
27
|
+
saveChangesButtonLabel: 'Save changes',
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
const emit = defineEmits<{
|
|
31
|
+
'update:modelValue': [tags: Tag[]]
|
|
32
|
+
}>()
|
|
33
|
+
|
|
34
|
+
const ALL_COLORS = ['orange', 'amber', 'yellow', 'lime', 'green', 'emerald', 'teal', 'cyan', 'sky', 'blue', 'indigo', 'purple', 'pink', 'rose', 'red', 'neutral'] as const
|
|
35
|
+
|
|
36
|
+
type Color = typeof ALL_COLORS[number]
|
|
37
|
+
type ContentType = 'new-tag' | 'existing-tag' | 'edit-tag' | null
|
|
38
|
+
|
|
39
|
+
interface Tag {
|
|
40
|
+
id: string;
|
|
41
|
+
color: Color
|
|
42
|
+
name: string
|
|
43
|
+
isSelected?: boolean
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface Dot {
|
|
47
|
+
color: Color
|
|
48
|
+
lightColor?: boolean
|
|
49
|
+
class?: HTMLAttributes['class']
|
|
50
|
+
icon?: string
|
|
51
|
+
iconColor?: string
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const EMPTY_DOTS: Dot[] = [
|
|
55
|
+
{ color: 'neutral', lightColor: true },
|
|
56
|
+
{ color: 'neutral', lightColor: true },
|
|
57
|
+
{ color: 'neutral', icon: 'i-ph-plus-bold' },
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
function getTagKey(tag: Tag): string {
|
|
61
|
+
return tag.id
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function getTagKeys(tags: Tag[]): Set<string> {
|
|
65
|
+
return new Set(tags.map(getTagKey))
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function mergeTagsUnique(...tagArrays: (Tag[] | undefined)[]): Tag[] {
|
|
69
|
+
const seen = new Set<string>()
|
|
70
|
+
const result: Tag[] = []
|
|
71
|
+
|
|
72
|
+
for (const tags of tagArrays) {
|
|
73
|
+
if (!tags?.length) {
|
|
74
|
+
continue
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
for (const tag of tags) {
|
|
78
|
+
const key = getTagKey(tag)
|
|
79
|
+
|
|
80
|
+
if (!seen.has(key)) {
|
|
81
|
+
seen.add(key)
|
|
82
|
+
result.push(tag)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return result
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function resolveColor(color: Color, lightColor = false): string {
|
|
91
|
+
const colorMap: Record<Color, string> = {
|
|
92
|
+
orange: lightColor ? 'bg-orange-300' : 'bg-orange-500',
|
|
93
|
+
amber: lightColor ? 'bg-amber-300' : 'bg-amber-500',
|
|
94
|
+
yellow: lightColor ? 'bg-yellow-300' : 'bg-yellow-500',
|
|
95
|
+
lime: lightColor ? 'bg-lime-300' : 'bg-lime-500',
|
|
96
|
+
green: lightColor ? 'bg-green-300' : 'bg-green-500',
|
|
97
|
+
emerald: lightColor ? 'bg-emerald-300' : 'bg-emerald-500',
|
|
98
|
+
teal: lightColor ? 'bg-teal-300' : 'bg-teal-500',
|
|
99
|
+
cyan: lightColor ? 'bg-cyan-300' : 'bg-cyan-500',
|
|
100
|
+
sky: lightColor ? 'bg-sky-300' : 'bg-sky-500',
|
|
101
|
+
blue: lightColor ? 'bg-blue-300' : 'bg-blue-500',
|
|
102
|
+
indigo: lightColor ? 'bg-indigo-300' : 'bg-indigo-500',
|
|
103
|
+
purple: lightColor ? 'bg-purple-300' : 'bg-purple-500',
|
|
104
|
+
pink: lightColor ? 'bg-pink-300' : 'bg-pink-500',
|
|
105
|
+
rose: lightColor ? 'bg-rose-300' : 'bg-rose-500',
|
|
106
|
+
red: lightColor ? 'bg-red-300' : 'bg-red-500',
|
|
107
|
+
neutral: lightColor ? 'bg-neutral-300' : 'bg-neutral-500',
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (lightColor && !colorMap[color]) {
|
|
111
|
+
const colorWith300 = color.replace(/\d+$/, '300')
|
|
112
|
+
return `bg-${colorWith300}`
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return colorMap[color] || `bg-${color}`
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const allCreatedTags = ref<Tag[]>([])
|
|
119
|
+
const localSelectedTags = ref<Tag[]>(props.modelValue || [])
|
|
120
|
+
const popoverOpen = ref(false)
|
|
121
|
+
const selectedContent = ref<ContentType>(allCreatedTags.value.length > 0 ? 'existing-tag' : 'new-tag')
|
|
122
|
+
const newTagName = ref<string>('')
|
|
123
|
+
const selectedColorsForNewTag = ref<Color | null>(null)
|
|
124
|
+
const selectedTagsForApply = ref<Set<string>>(new Set())
|
|
125
|
+
const editingTag = ref<Tag | null>(null)
|
|
126
|
+
const editingTagName = ref<string>('')
|
|
127
|
+
const editingTagColor = ref<Color | null>(null)
|
|
128
|
+
const isCreatingTag = ref(false)
|
|
129
|
+
|
|
130
|
+
function updateAllCreatedTags() {
|
|
131
|
+
const mergedTags = mergeTagsUnique(props.options, props.modelValue)
|
|
132
|
+
const selectedKeys = getTagKeys(props.modelValue || [])
|
|
133
|
+
|
|
134
|
+
allCreatedTags.value = mergedTags.map(tag => ({
|
|
135
|
+
...tag,
|
|
136
|
+
isSelected: selectedKeys.has(getTagKey(tag)),
|
|
137
|
+
}))
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function resetEditingState() {
|
|
141
|
+
editingTag.value = null
|
|
142
|
+
editingTagName.value = ''
|
|
143
|
+
editingTagColor.value = null
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function resetNewTagForm() {
|
|
147
|
+
newTagName.value = ''
|
|
148
|
+
selectedColorsForNewTag.value = null
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const contentMeasure = useTemplateRef<HTMLElement>('contentMeasure')
|
|
152
|
+
const { height: heightContent } = useElementSize(contentMeasure)
|
|
153
|
+
|
|
154
|
+
const tags = computed({
|
|
155
|
+
get: () => localSelectedTags.value,
|
|
156
|
+
set: (value) => {
|
|
157
|
+
localSelectedTags.value = value
|
|
158
|
+
emit('update:modelValue', value)
|
|
159
|
+
},
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
const hasTags = computed(() => allCreatedTags.value.length > 0)
|
|
163
|
+
|
|
164
|
+
const triggerDots = computed<Dot[]>(() => {
|
|
165
|
+
if (tags.value.length > 0) {
|
|
166
|
+
return tags.value.slice(0, 3).map(tag => ({ color: tag.color }))
|
|
167
|
+
}
|
|
168
|
+
if (hasTags.value) {
|
|
169
|
+
return allCreatedTags.value.slice(0, 3).map(tag => ({ color: tag.color }))
|
|
170
|
+
}
|
|
171
|
+
return EMPTY_DOTS
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
const computedHeightContent = computed(() => {
|
|
175
|
+
if (heightContent.value > 0) {
|
|
176
|
+
return `${heightContent.value}px`
|
|
177
|
+
}
|
|
178
|
+
return undefined
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
const labelText = computed(() => {
|
|
182
|
+
if (hasTags.value && tags.value.length === 0) {
|
|
183
|
+
return `${allCreatedTags.value.length} tag${allCreatedTags.value.length > 1 ? 's' : ''}`
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (tags.value.length > 0) {
|
|
187
|
+
return `${tags.value.length} ${props.selectedLabel}`
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return props.createTagLabel
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
const contentConfigs = computed(() => {
|
|
194
|
+
const configs = [
|
|
195
|
+
{ type: 'new-tag' as const, slideIndex: -1 },
|
|
196
|
+
{ type: 'existing-tag' as const, slideIndex: 1 },
|
|
197
|
+
{ type: 'edit-tag' as const, slideIndex: -1 },
|
|
198
|
+
]
|
|
199
|
+
|
|
200
|
+
return configs.filter(config => selectedContent.value === config.type)
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
watch(() => props.options, updateAllCreatedTags, { immediate: true })
|
|
204
|
+
|
|
205
|
+
watch(() => props.modelValue, (newTags) => {
|
|
206
|
+
if (!newTags?.length) {
|
|
207
|
+
localSelectedTags.value = []
|
|
208
|
+
updateAllCreatedTags()
|
|
209
|
+
return
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const currentKeys = getTagKeys(localSelectedTags.value)
|
|
213
|
+
const newKeys = getTagKeys(newTags)
|
|
214
|
+
const isDifferent = currentKeys.size !== newKeys.size || !Array.from(newKeys).every(key => currentKeys.has(key))
|
|
215
|
+
|
|
216
|
+
if (isDifferent) {
|
|
217
|
+
localSelectedTags.value = [...newTags]
|
|
218
|
+
updateAllCreatedTags()
|
|
219
|
+
}
|
|
220
|
+
}, { immediate: true })
|
|
221
|
+
|
|
222
|
+
watch(popoverOpen, (isOpen) => {
|
|
223
|
+
if (isOpen) {
|
|
224
|
+
selectedContent.value = hasTags.value ? 'existing-tag' : 'new-tag'
|
|
225
|
+
selectedTagsForApply.value = getTagKeys(localSelectedTags.value)
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
resetEditingState()
|
|
229
|
+
}
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
watch(() => localSelectedTags.value, (newTags) => {
|
|
233
|
+
if (isCreatingTag.value) {
|
|
234
|
+
return
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (newTags?.length) {
|
|
238
|
+
selectedTagsForApply.value = getTagKeys(newTags)
|
|
239
|
+
|
|
240
|
+
const selectedKeys = getTagKeys(newTags)
|
|
241
|
+
|
|
242
|
+
allCreatedTags.value = allCreatedTags.value.map(tag => ({
|
|
243
|
+
...tag,
|
|
244
|
+
isSelected: selectedKeys.has(getTagKey(tag)),
|
|
245
|
+
}))
|
|
246
|
+
|
|
247
|
+
if (selectedContent.value === null) {
|
|
248
|
+
selectedContent.value = 'existing-tag'
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
selectedTagsForApply.value = new Set()
|
|
253
|
+
|
|
254
|
+
allCreatedTags.value = allCreatedTags.value.map(tag => ({
|
|
255
|
+
...tag,
|
|
256
|
+
isSelected: false,
|
|
257
|
+
}))
|
|
258
|
+
}
|
|
259
|
+
}, { immediate: true })
|
|
260
|
+
|
|
261
|
+
function handleSelectColor(color: Color) {
|
|
262
|
+
if (!newTagName.value?.trim()) {
|
|
263
|
+
return
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
selectedColorsForNewTag.value = selectedColorsForNewTag.value === color ? null : color
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function handleSelectColorForEdit(color: Color) {
|
|
270
|
+
if (!editingTagName.value?.trim()) {
|
|
271
|
+
return
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
editingTagColor.value = editingTagColor.value === color ? null : color
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function createNewTag() {
|
|
278
|
+
const trimmedName = newTagName.value?.trim()
|
|
279
|
+
|
|
280
|
+
if (!trimmedName || !selectedColorsForNewTag.value) {
|
|
281
|
+
return
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
isCreatingTag.value = true
|
|
285
|
+
|
|
286
|
+
const newTag: Tag = {
|
|
287
|
+
id: crypto.randomUUID(),
|
|
288
|
+
color: selectedColorsForNewTag.value,
|
|
289
|
+
name: trimmedName,
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const tagKey = getTagKey(newTag)
|
|
293
|
+
const allCreatedKeys = getTagKeys(allCreatedTags.value)
|
|
294
|
+
|
|
295
|
+
if (!allCreatedKeys.has(tagKey)) {
|
|
296
|
+
const tagWithSelection: Tag = { ...newTag, isSelected: true }
|
|
297
|
+
|
|
298
|
+
allCreatedTags.value = [...allCreatedTags.value, tagWithSelection]
|
|
299
|
+
tags.value = [...tags.value, newTag]
|
|
300
|
+
|
|
301
|
+
selectedTagsForApply.value.add(tagKey)
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
resetNewTagForm()
|
|
305
|
+
|
|
306
|
+
selectedContent.value = 'existing-tag'
|
|
307
|
+
|
|
308
|
+
nextTick(() => {
|
|
309
|
+
isCreatingTag.value = false
|
|
310
|
+
})
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function toggleTagSelection(tag: Tag, checked: boolean) {
|
|
314
|
+
const tagKey = getTagKey(tag)
|
|
315
|
+
|
|
316
|
+
if (checked) {
|
|
317
|
+
selectedTagsForApply.value.add(tagKey)
|
|
318
|
+
}
|
|
319
|
+
else {
|
|
320
|
+
selectedTagsForApply.value.delete(tagKey)
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Apply changes immediately
|
|
324
|
+
const selectedTags = allCreatedTags.value.filter(isTagSelected)
|
|
325
|
+
tags.value = selectedTags.map((tag) => {
|
|
326
|
+
const { isSelected, ...tagWithoutSelection } = tag
|
|
327
|
+
return tagWithoutSelection
|
|
328
|
+
})
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function isTagSelected(tag: Tag): boolean {
|
|
332
|
+
return selectedTagsForApply.value.has(getTagKey(tag))
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function handleCreateMore() {
|
|
336
|
+
resetNewTagForm()
|
|
337
|
+
selectedContent.value = 'new-tag'
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function handleBackToTags() {
|
|
341
|
+
selectedContent.value = 'existing-tag'
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function handleEditTag(tag: Tag) {
|
|
345
|
+
editingTag.value = tag
|
|
346
|
+
editingTagName.value = tag.name
|
|
347
|
+
editingTagColor.value = tag.color
|
|
348
|
+
|
|
349
|
+
selectedContent.value = 'edit-tag'
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function saveEditedTag() {
|
|
353
|
+
if (!editingTag.value || !editingTagName.value?.trim() || !editingTagColor.value) {
|
|
354
|
+
return
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const oldTagKey = getTagKey(editingTag.value)
|
|
358
|
+
const newTag: Tag = {
|
|
359
|
+
id: editingTag.value.id,
|
|
360
|
+
color: editingTagColor.value,
|
|
361
|
+
name: editingTagName.value.trim(),
|
|
362
|
+
}
|
|
363
|
+
const newTagKey = getTagKey(newTag)
|
|
364
|
+
|
|
365
|
+
const tagIndex = allCreatedTags.value.findIndex(t => getTagKey(t) === oldTagKey)
|
|
366
|
+
|
|
367
|
+
if (tagIndex !== -1 && allCreatedTags.value[tagIndex]) {
|
|
368
|
+
const wasSelected = allCreatedTags.value[tagIndex].isSelected ?? false
|
|
369
|
+
|
|
370
|
+
allCreatedTags.value[tagIndex] = { ...newTag, isSelected: wasSelected }
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const wasSelected = selectedTagsForApply.value.has(oldTagKey)
|
|
374
|
+
|
|
375
|
+
if (wasSelected) {
|
|
376
|
+
selectedTagsForApply.value.delete(oldTagKey)
|
|
377
|
+
selectedTagsForApply.value.add(newTagKey)
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const selectedIndex = localSelectedTags.value.findIndex(t => getTagKey(t) === oldTagKey)
|
|
381
|
+
|
|
382
|
+
if (selectedIndex !== -1) {
|
|
383
|
+
localSelectedTags.value[selectedIndex] = { ...newTag }
|
|
384
|
+
tags.value = localSelectedTags.value
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
resetEditingState()
|
|
388
|
+
|
|
389
|
+
selectedContent.value = 'existing-tag'
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function cancelEditTag() {
|
|
393
|
+
resetEditingState()
|
|
394
|
+
selectedContent.value = 'existing-tag'
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function getSlideMotionVariants(index: number) {
|
|
398
|
+
return {
|
|
399
|
+
initial: {
|
|
400
|
+
x: `${110 * index}%`,
|
|
401
|
+
opacity: 0,
|
|
402
|
+
},
|
|
403
|
+
animate: {
|
|
404
|
+
x: 0,
|
|
405
|
+
opacity: 1,
|
|
406
|
+
},
|
|
407
|
+
exit: {
|
|
408
|
+
x: `${-110 * index}%`,
|
|
409
|
+
opacity: 0,
|
|
410
|
+
},
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
const SPRING_CONFIG = {
|
|
415
|
+
type: 'spring',
|
|
416
|
+
duration: 0.5,
|
|
417
|
+
bounce: 0,
|
|
418
|
+
} as const
|
|
419
|
+
|
|
420
|
+
updateAllCreatedTags()
|
|
421
|
+
</script>
|
|
422
|
+
|
|
423
|
+
<template>
|
|
424
|
+
<MotionConfig :transition="SPRING_CONFIG">
|
|
425
|
+
<PopoverRoot v-model:open="popoverOpen">
|
|
426
|
+
<PopoverTrigger
|
|
427
|
+
as="button"
|
|
428
|
+
:class="cn(
|
|
429
|
+
'group w-fit flex items-center gap-[6px] px-[12px] py-[7px] rounded-[10px] bg-background border-[0.5px] border-border-strong',
|
|
430
|
+
'transition ease-in-out duration-80 hover:bg-background-subtle data-[state=open]:bg-background-subtle',
|
|
431
|
+
props.class,
|
|
432
|
+
)"
|
|
433
|
+
>
|
|
434
|
+
<div class="flex items-center">
|
|
435
|
+
<div
|
|
436
|
+
v-for="(dot, index) in triggerDots"
|
|
437
|
+
:key="index"
|
|
438
|
+
:class="cn(
|
|
439
|
+
'flex items-center justify-center w-[12px] h-[12px] rounded-full dot-mask [&:not(:first-child)]:-ml-[4px]',
|
|
440
|
+
resolveColor(dot.color, dot.lightColor),
|
|
441
|
+
)"
|
|
442
|
+
>
|
|
443
|
+
<TelaIcon v-if="dot.icon" :name="dot.icon" size="7px" :color="dot.iconColor || 'white-1000'" />
|
|
444
|
+
</div>
|
|
445
|
+
</div>
|
|
446
|
+
<span :class="cn('body-14-semibold text-text-primary', props.labelClass)">
|
|
447
|
+
{{ labelText }}
|
|
448
|
+
</span>
|
|
449
|
+
</PopoverTrigger>
|
|
450
|
+
<PopoverPortal>
|
|
451
|
+
<PopoverContent class="TagsSelectContent" :side-offset="8" w-220px pt-12px px-16px bg-background border-0.5px border-border shadow-lg rounded-12px overflow-hidden>
|
|
452
|
+
<div
|
|
453
|
+
class="[transition:height_0.3s_ease-out]"
|
|
454
|
+
:style="computedHeightContent ? { height: computedHeightContent } : {}"
|
|
455
|
+
>
|
|
456
|
+
<div ref="contentMeasure">
|
|
457
|
+
<AnimatePresence mode="popLayout" :initial="false">
|
|
458
|
+
<Motion
|
|
459
|
+
v-for="config in contentConfigs"
|
|
460
|
+
:key="config.type"
|
|
461
|
+
v-bind="getSlideMotionVariants(config.slideIndex)"
|
|
462
|
+
flex="~ col"
|
|
463
|
+
tabindex="-1"
|
|
464
|
+
>
|
|
465
|
+
<div v-if="config.type === 'new-tag'" flex="~ col" gap-12px pb-16px>
|
|
466
|
+
<div flex="~ col" gap-4px>
|
|
467
|
+
<button
|
|
468
|
+
v-if="allCreatedTags.length > 0"
|
|
469
|
+
flex items-center justify-center w-24px h-24px rounded-6px hover:bg-background-muted ml--6px mt--4px
|
|
470
|
+
@click="handleBackToTags"
|
|
471
|
+
>
|
|
472
|
+
<TelaIcon name="i-ph-arrow-left" size="16px" color="icon" />
|
|
473
|
+
</button>
|
|
474
|
+
<h5 heading-h5-semibold>
|
|
475
|
+
{{ props.newTagLabel }}
|
|
476
|
+
</h5>
|
|
477
|
+
</div>
|
|
478
|
+
<TelaInput
|
|
479
|
+
v-model="newTagName"
|
|
480
|
+
:placeholder="props.defineNamePlaceholder"
|
|
481
|
+
input-class="px-[6px]! pt-[2px]! pb-[3px]! rounded-[6px]!"
|
|
482
|
+
input-font-class="body-12-regular!"
|
|
483
|
+
autofocus
|
|
484
|
+
@keydown.enter="createNewTag"
|
|
485
|
+
/>
|
|
486
|
+
<div class="grid grid-cols-8 gap-4px">
|
|
487
|
+
<button
|
|
488
|
+
v-for="color in ALL_COLORS"
|
|
489
|
+
:key="color"
|
|
490
|
+
w-20px h-20px rounded-4px flex items-center justify-center hover:bg-background-lowered
|
|
491
|
+
@click="handleSelectColor(color)"
|
|
492
|
+
>
|
|
493
|
+
<div
|
|
494
|
+
:class="cn('flex items-center justify-center w-12px h-12px rounded-full', resolveColor(color))"
|
|
495
|
+
>
|
|
496
|
+
<TelaIcon
|
|
497
|
+
v-if="selectedColorsForNewTag === color"
|
|
498
|
+
name="i-ph-check-bold"
|
|
499
|
+
size="8px"
|
|
500
|
+
color="white-1000"
|
|
501
|
+
/>
|
|
502
|
+
</div>
|
|
503
|
+
</button>
|
|
504
|
+
</div>
|
|
505
|
+
<TelaButton size="sm" :disabled="!newTagName || newTagName.trim() === '' || !selectedColorsForNewTag" @click="createNewTag">
|
|
506
|
+
{{ props.createButtonLabel }}
|
|
507
|
+
</TelaButton>
|
|
508
|
+
</div>
|
|
509
|
+
<div v-if="config.type === 'existing-tag'" flex="~ col" pb-10px>
|
|
510
|
+
<div flex items-center justify-between>
|
|
511
|
+
<h5 heading-h5-semibold>
|
|
512
|
+
{{ allCreatedTags.length }} tag{{ allCreatedTags.length > 1 ? 's' : '' }}
|
|
513
|
+
</h5>
|
|
514
|
+
<button flex items-center justify-center w-24px h-24px rounded-6px hover:bg-background-muted mr--4px @click="handleCreateMore">
|
|
515
|
+
<TelaIcon name="i-ph-plus" size="16px" color="icon" />
|
|
516
|
+
</button>
|
|
517
|
+
</div>
|
|
518
|
+
<div flex="~ col" mt-4px>
|
|
519
|
+
<button v-for="tag in allCreatedTags" :key="`${tag.color}-${tag.name}`" class="group/tag" flex items-center justify-between hover:bg-background-muted px-8px mx--8px py-4px rounded-8px @click="toggleTagSelection(tag, !isTagSelected(tag))">
|
|
520
|
+
<div flex items-center gap-6px min-w-0>
|
|
521
|
+
<div :class="cn('w-[8px] h-[8px] rounded-full flex-shrink-0', resolveColor(tag.color))" />
|
|
522
|
+
<span body-14-medium text-text-primary capitalize truncate text-start>
|
|
523
|
+
{{ tag.name }}
|
|
524
|
+
</span>
|
|
525
|
+
</div>
|
|
526
|
+
<div flex items-center gap-4px>
|
|
527
|
+
<button
|
|
528
|
+
class="group opacity-0 group-hover/tag:opacity-100" flex items-center justify-center w-17px h-17px rounded-5px hover:bg-background-lowered
|
|
529
|
+
@click.stop="handleEditTag(tag)"
|
|
530
|
+
>
|
|
531
|
+
<TelaIcon name="i-ph-pencil-simple" size="12px" color="icon-subtle group-hover:icon" />
|
|
532
|
+
</button>
|
|
533
|
+
<TelaCheckbox :model-value="isTagSelected(tag)" @update:model-value="(checked) => toggleTagSelection(tag, checked === true)" @click.stop />
|
|
534
|
+
</div>
|
|
535
|
+
</button>
|
|
536
|
+
</div>
|
|
537
|
+
</div>
|
|
538
|
+
<div v-if="config.type === 'edit-tag'" flex="~ col" gap-12px pb-16px>
|
|
539
|
+
<div flex="~ col" gap-4px>
|
|
540
|
+
<button
|
|
541
|
+
flex items-center justify-center w-24px h-24px rounded-6px hover:bg-background-muted ml--6px mt--4px
|
|
542
|
+
@click="cancelEditTag"
|
|
543
|
+
>
|
|
544
|
+
<TelaIcon name="i-ph-arrow-left" size="16px" color="icon" />
|
|
545
|
+
</button>
|
|
546
|
+
<h5 heading-h5-semibold>
|
|
547
|
+
{{ props.editTagLabel }}
|
|
548
|
+
</h5>
|
|
549
|
+
</div>
|
|
550
|
+
<TelaInput
|
|
551
|
+
v-model="editingTagName"
|
|
552
|
+
:placeholder="props.addNewNamePlaceholder"
|
|
553
|
+
input-class="px-[6px]! pt-[2px]! pb-[3px]! rounded-[6px]!"
|
|
554
|
+
input-font-class="body-12-regular!"
|
|
555
|
+
@keydown.enter="saveEditedTag"
|
|
556
|
+
/>
|
|
557
|
+
<div class="grid grid-cols-8 gap-4px">
|
|
558
|
+
<button
|
|
559
|
+
v-for="color in ALL_COLORS"
|
|
560
|
+
:key="color"
|
|
561
|
+
w-20px h-20px rounded-4px flex items-center justify-center hover:bg-background-lowered
|
|
562
|
+
@click="handleSelectColorForEdit(color)"
|
|
563
|
+
>
|
|
564
|
+
<div
|
|
565
|
+
:class="cn('flex items-center justify-center w-12px h-12px rounded-full', resolveColor(color))"
|
|
566
|
+
>
|
|
567
|
+
<TelaIcon
|
|
568
|
+
v-if="editingTagColor === color"
|
|
569
|
+
name="i-ph-check-bold"
|
|
570
|
+
size="8px"
|
|
571
|
+
color="white-1000"
|
|
572
|
+
/>
|
|
573
|
+
</div>
|
|
574
|
+
</button>
|
|
575
|
+
</div>
|
|
576
|
+
<TelaButton size="sm" :disabled="!editingTagName || editingTagName.trim() === '' || !editingTagColor" @click="saveEditedTag">
|
|
577
|
+
{{ props.saveChangesButtonLabel }}
|
|
578
|
+
</TelaButton>
|
|
579
|
+
</div>
|
|
580
|
+
</Motion>
|
|
581
|
+
</AnimatePresence>
|
|
582
|
+
</div>
|
|
583
|
+
</div>
|
|
584
|
+
</PopoverContent>
|
|
585
|
+
</PopoverPortal>
|
|
586
|
+
</PopoverRoot>
|
|
587
|
+
</MotionConfig>
|
|
588
|
+
</template>
|
|
589
|
+
|
|
590
|
+
<style>
|
|
591
|
+
.dot-mask:not(:last-child) {
|
|
592
|
+
mask-image: radial-gradient(circle 6px at right center, transparent 6px, #fff 6px);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
.TagsSelectContent {
|
|
596
|
+
transform-origin: var(--reka-popover-content-transform-origin);
|
|
597
|
+
|
|
598
|
+
&[data-state="open"] {
|
|
599
|
+
animation: slideDownAndFade 0.12s ease-out forwards;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
&[data-state="closed"] {
|
|
603
|
+
animation: slideUpAndFade 0.12s ease-out forwards;
|
|
604
|
+
animation-delay: 0s;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
@keyframes slideDownAndFade {
|
|
609
|
+
from {
|
|
610
|
+
opacity: 0;
|
|
611
|
+
transform: translateY(2px) scale(0.97);
|
|
612
|
+
}
|
|
613
|
+
to {
|
|
614
|
+
opacity: 1;
|
|
615
|
+
transform: scale(1);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
@keyframes slideUpAndFade {
|
|
620
|
+
from {
|
|
621
|
+
opacity: 1;
|
|
622
|
+
transform: translateY(0) scale(1);
|
|
623
|
+
}
|
|
624
|
+
to {
|
|
625
|
+
opacity: 0;
|
|
626
|
+
transform: translateY(-2px) scale(0.97);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
.slide-fade-enter-active {
|
|
631
|
+
transition: all 0.3s ease;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
.slide-fade-leave-active {
|
|
635
|
+
transition: all 0.3s cubic-bezier(1.0, 0.5, 0.8, 1.0);
|
|
636
|
+
}
|
|
637
|
+
</style>
|