@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,138 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/vue3'
|
|
2
|
+
import ChatMessage from './index.vue'
|
|
3
|
+
|
|
4
|
+
const meta = {
|
|
5
|
+
title: 'Components/Chat/Message',
|
|
6
|
+
component: ChatMessage,
|
|
7
|
+
tags: ['autodocs'],
|
|
8
|
+
parameters: {
|
|
9
|
+
layout: 'centered',
|
|
10
|
+
docs: {
|
|
11
|
+
description: {
|
|
12
|
+
component: 'A chat message component that displays individual messages in a chat interface. Supports avatars, timestamps, replies, loading states, delete actions, and message threading. Used within the Chat component to render message content.',
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
argTypes: {
|
|
17
|
+
id: {
|
|
18
|
+
control: 'text',
|
|
19
|
+
description: 'Unique identifier for the message.',
|
|
20
|
+
},
|
|
21
|
+
content: {
|
|
22
|
+
control: 'text',
|
|
23
|
+
description: 'The message text content.',
|
|
24
|
+
},
|
|
25
|
+
timestamp: {
|
|
26
|
+
control: 'object',
|
|
27
|
+
description: 'Date object representing when the message was sent.',
|
|
28
|
+
},
|
|
29
|
+
author: {
|
|
30
|
+
control: 'text',
|
|
31
|
+
description: 'Name of the message author.',
|
|
32
|
+
},
|
|
33
|
+
authorEmail: {
|
|
34
|
+
control: 'text',
|
|
35
|
+
description: 'Email address of the message author.',
|
|
36
|
+
},
|
|
37
|
+
avatarUrl: {
|
|
38
|
+
control: 'text',
|
|
39
|
+
description: 'URL of the author\'s avatar image.',
|
|
40
|
+
},
|
|
41
|
+
version: {
|
|
42
|
+
control: 'text',
|
|
43
|
+
description: 'Version identifier for the message.',
|
|
44
|
+
},
|
|
45
|
+
isReply: {
|
|
46
|
+
control: 'boolean',
|
|
47
|
+
description: 'Whether this message is displayed as a reply to another message.',
|
|
48
|
+
},
|
|
49
|
+
replies: {
|
|
50
|
+
control: 'object',
|
|
51
|
+
description: 'Array of reply message objects nested under this message.',
|
|
52
|
+
},
|
|
53
|
+
loading: {
|
|
54
|
+
control: 'boolean',
|
|
55
|
+
description: 'Show loading state for the message (e.g., while sending).',
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
} satisfies Meta<typeof ChatMessage>
|
|
59
|
+
|
|
60
|
+
export default meta
|
|
61
|
+
type Story = StoryObj<typeof meta>
|
|
62
|
+
|
|
63
|
+
export const Default: Story = {
|
|
64
|
+
args: {
|
|
65
|
+
id: '1',
|
|
66
|
+
content: 'This is a sample message content',
|
|
67
|
+
timestamp: new Date('2024-09-18'),
|
|
68
|
+
author: 'John Doe',
|
|
69
|
+
authorEmail: 'john@doe.com',
|
|
70
|
+
version: 'VER-1',
|
|
71
|
+
},
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export const Loading: Story = {
|
|
75
|
+
args: {
|
|
76
|
+
...Default.args,
|
|
77
|
+
loading: true,
|
|
78
|
+
},
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export const WithAvatar: Story = {
|
|
82
|
+
args: {
|
|
83
|
+
...Default.args,
|
|
84
|
+
avatarUrl: 'https://github.com/shadcn.png',
|
|
85
|
+
},
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export const WithReplyButton: Story = {
|
|
89
|
+
args: {
|
|
90
|
+
...Default.args,
|
|
91
|
+
},
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export const WithReplies: Story = {
|
|
95
|
+
args: {
|
|
96
|
+
...Default.args,
|
|
97
|
+
replies: [
|
|
98
|
+
{
|
|
99
|
+
id: '1',
|
|
100
|
+
content: 'This is a reply',
|
|
101
|
+
author: 'Jane Doe',
|
|
102
|
+
authorEmail: 'jane@doe.com',
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
},
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export const AsReply: Story = {
|
|
109
|
+
args: {
|
|
110
|
+
...Default.args,
|
|
111
|
+
isReply: true,
|
|
112
|
+
},
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export const WithDeleteAction: Story = {
|
|
116
|
+
args: {
|
|
117
|
+
...Default.args,
|
|
118
|
+
},
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export const CompleteExample: Story = {
|
|
122
|
+
args: {
|
|
123
|
+
id: '1',
|
|
124
|
+
content: 'Yep, this is still happening. @thierry can we review the parser?',
|
|
125
|
+
timestamp: new Date('2024-09-18'),
|
|
126
|
+
author: 'Guilherme Oliveira',
|
|
127
|
+
avatarUrl: 'https://github.com/shadcn.png',
|
|
128
|
+
authorEmail: 'guilherme@tela.com',
|
|
129
|
+
replies: [
|
|
130
|
+
{
|
|
131
|
+
id: '1',
|
|
132
|
+
content: 'This is a reply',
|
|
133
|
+
authorEmail: 'jane@doe.com',
|
|
134
|
+
author: 'Jane Doe',
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
},
|
|
138
|
+
}
|
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { TreeItem } from 'radix-vue'
|
|
3
|
+
import { isToday } from 'date-fns'
|
|
4
|
+
import { computed } from 'vue'
|
|
5
|
+
import type { User } from '../types'
|
|
6
|
+
|
|
7
|
+
interface ChatMessageProps {
|
|
8
|
+
id: string
|
|
9
|
+
content: string
|
|
10
|
+
timestamp?: Date | string
|
|
11
|
+
author: string
|
|
12
|
+
authorEmail: string
|
|
13
|
+
version?: string
|
|
14
|
+
avatarUrl?: string
|
|
15
|
+
isReply?: boolean
|
|
16
|
+
loading?: boolean
|
|
17
|
+
depth?: number
|
|
18
|
+
replies?: ChatMessageProps[]
|
|
19
|
+
allowReply?: boolean
|
|
20
|
+
parentElement?: HTMLElement
|
|
21
|
+
isChat?: boolean
|
|
22
|
+
currentUser?: User
|
|
23
|
+
otherUsers?: User[]
|
|
24
|
+
isExpanded?: boolean
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const props = defineProps<ChatMessageProps>()
|
|
28
|
+
const emit = defineEmits<{
|
|
29
|
+
reply: []
|
|
30
|
+
delete: [id: string]
|
|
31
|
+
toggleExpand: []
|
|
32
|
+
}>()
|
|
33
|
+
|
|
34
|
+
const treeItemRef = ref<HTMLElement>()
|
|
35
|
+
const heightValue = ref(0)
|
|
36
|
+
const isExpandedManually = ref(props.isExpanded ?? false)
|
|
37
|
+
const isMounted = ref(false)
|
|
38
|
+
const isUnmounting = ref(false)
|
|
39
|
+
|
|
40
|
+
// Wrap TreeItem content in a component
|
|
41
|
+
const MessageContent = defineComponent({
|
|
42
|
+
inheritAttrs: false,
|
|
43
|
+
props: {
|
|
44
|
+
isExpanded: Boolean,
|
|
45
|
+
handleToggle: Function,
|
|
46
|
+
},
|
|
47
|
+
setup(props, { slots }) {
|
|
48
|
+
return () => slots.default?.({
|
|
49
|
+
isExpanded: props.isExpanded,
|
|
50
|
+
handleToggle: () => props.handleToggle?.(),
|
|
51
|
+
})
|
|
52
|
+
},
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
onMounted(() => {
|
|
56
|
+
isMounted.value = true
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
onBeforeUnmount(() => {
|
|
60
|
+
isUnmounting.value = true
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
async function calculateTotalHeight(message: ChatMessageProps): Promise<number> {
|
|
64
|
+
try {
|
|
65
|
+
await nextTick()
|
|
66
|
+
|
|
67
|
+
// Guard against unmounting during the async operation
|
|
68
|
+
if (isUnmounting.value) {
|
|
69
|
+
return 0
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const contentElement = document.getElementById(`message-content-${message.id}`)
|
|
73
|
+
const baseHeight = contentElement?.clientHeight ?? 1
|
|
74
|
+
|
|
75
|
+
if (message.replies?.length) {
|
|
76
|
+
const repliesHeight = await Promise.all(
|
|
77
|
+
message.replies.map(reply => calculateTotalHeight(reply)),
|
|
78
|
+
).then(heights =>
|
|
79
|
+
heights.reduce((acc, height) => acc + height, 0),
|
|
80
|
+
)
|
|
81
|
+
return baseHeight + repliesHeight
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return baseHeight
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
console.error('Error calculating total height', error)
|
|
88
|
+
return 0
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const hasReplies = computed(() => props.replies && props.replies.length > 0)
|
|
93
|
+
const { t, locale } = useI18n()
|
|
94
|
+
|
|
95
|
+
const timestampString = computed(() => {
|
|
96
|
+
if (!props.timestamp)
|
|
97
|
+
return ''
|
|
98
|
+
|
|
99
|
+
const date = new Date(props.timestamp)
|
|
100
|
+
if (isToday(date))
|
|
101
|
+
return t('chat.todayAt', { time: date.toLocaleTimeString(locale.value ?? 'en-US', { hour: '2-digit', minute: '2-digit', hour12: false }) })
|
|
102
|
+
|
|
103
|
+
return t('chat.onDate', { date: date.toLocaleDateString(locale.value ?? 'en-US', { day: '2-digit', month: 'short', year: 'numeric' }) })
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
const beforeWidth = computed(() => {
|
|
107
|
+
if (props.parentElement) {
|
|
108
|
+
if (hasReplies.value) {
|
|
109
|
+
return `${(props.depth ?? 1) * 24}px`
|
|
110
|
+
}
|
|
111
|
+
if (props.depth && props.depth <= 2) {
|
|
112
|
+
return `80px`
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return '0px'
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
const resolvedContent = computed(() => {
|
|
119
|
+
if (props.currentUser && props.otherUsers) {
|
|
120
|
+
return props.content.replace(/@([\w.-]+@[\w.-]+\.\w+)/g, (match, email) => {
|
|
121
|
+
const user = props.otherUsers?.find(user => user.email === email)
|
|
122
|
+
return user ? `<span class="mention" data-type="mention" data-label="${user.name}">@${user.name}</span>` : match
|
|
123
|
+
})
|
|
124
|
+
}
|
|
125
|
+
return props.content
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
const totalHeight = computed(() => heightValue.value ? `${heightValue.value}px` : '0px')
|
|
129
|
+
|
|
130
|
+
watch([() => props.replies, isMounted], async () => {
|
|
131
|
+
if (!isMounted.value || isUnmounting.value)
|
|
132
|
+
return
|
|
133
|
+
|
|
134
|
+
const height = await calculateTotalHeight(props)
|
|
135
|
+
if (!isUnmounting.value) {
|
|
136
|
+
heightValue.value = height + 10
|
|
137
|
+
}
|
|
138
|
+
}, { immediate: true })
|
|
139
|
+
|
|
140
|
+
watch(isExpandedManually, async () => {
|
|
141
|
+
const height = await calculateTotalHeight(props)
|
|
142
|
+
heightValue.value = height
|
|
143
|
+
}, { immediate: true })
|
|
144
|
+
</script>
|
|
145
|
+
|
|
146
|
+
<template>
|
|
147
|
+
<component
|
|
148
|
+
:is="isChat ? TreeItem : MessageContent" ref="treeItemRef"
|
|
149
|
+
:key="props.id"
|
|
150
|
+
v-slot="slotProps"
|
|
151
|
+
:level="props.depth ?? 1"
|
|
152
|
+
v-bind="{ value: { id: props.id, children: props.replies } }"
|
|
153
|
+
class="mb-16px"
|
|
154
|
+
:cursor="hasReplies ? 'pointer' : 'default'"
|
|
155
|
+
@click.stop="(e: Event) => {
|
|
156
|
+
isExpandedManually = !isExpandedManually
|
|
157
|
+
}"
|
|
158
|
+
>
|
|
159
|
+
<div
|
|
160
|
+
class="message-container"
|
|
161
|
+
:class="[
|
|
162
|
+
hasReplies ? `
|
|
163
|
+
after:absolute
|
|
164
|
+
after:left-6
|
|
165
|
+
after:top-8
|
|
166
|
+
after:bottom-0
|
|
167
|
+
after:w-[1px]
|
|
168
|
+
after:bg-gray-300
|
|
169
|
+
after:z-1
|
|
170
|
+
after:h-${totalHeight}`
|
|
171
|
+
: '',
|
|
172
|
+
isReply ? `
|
|
173
|
+
before:absolute
|
|
174
|
+
before:left-[-40px]
|
|
175
|
+
before:top-3.5
|
|
176
|
+
before:h-[12px]
|
|
177
|
+
before:w-[20px]
|
|
178
|
+
before:border-l
|
|
179
|
+
before:border-b
|
|
180
|
+
before:border-gray-300
|
|
181
|
+
before:rounded-bl-[12px]
|
|
182
|
+
before:z-1
|
|
183
|
+
ml-10
|
|
184
|
+
mt-2
|
|
185
|
+
` : '',
|
|
186
|
+
{ 'before:w-[80px]': props.isReply },
|
|
187
|
+
]"
|
|
188
|
+
:style="{
|
|
189
|
+
'--before-width': beforeWidth,
|
|
190
|
+
'--after-height': totalHeight,
|
|
191
|
+
}"
|
|
192
|
+
>
|
|
193
|
+
<div :id="`message-content-${props.id}`" class="flex-1 p-8px">
|
|
194
|
+
<div class="flex items-start gap-3">
|
|
195
|
+
<TelaTooltip v-if="!props.loading" :content="author">
|
|
196
|
+
<TelaAvatar
|
|
197
|
+
:image="avatarUrl"
|
|
198
|
+
:alt="author"
|
|
199
|
+
size="sm"
|
|
200
|
+
rounded="full"
|
|
201
|
+
class="h-8 w-8 shrink-0 relative z-10 bg-white"
|
|
202
|
+
/>
|
|
203
|
+
</TelaTooltip>
|
|
204
|
+
<div v-else>
|
|
205
|
+
<TelaSkeleton class="w-40px h-40px rounded-full" />
|
|
206
|
+
</div>
|
|
207
|
+
|
|
208
|
+
<div class="flex-1">
|
|
209
|
+
<div class="flex items-center gap-2">
|
|
210
|
+
<div class="flex flex-col mb-2">
|
|
211
|
+
<span v-if="!props.loading" class="body-14-semibold text-gray-800">{{ author }}</span>
|
|
212
|
+
<TelaSkeleton v-else class="w-100px h-14px mb-2" />
|
|
213
|
+
<div v-if="!props.loading" flex="~ gap-1 items-center">
|
|
214
|
+
<span class="body-12-regular text-gray-600">{{ timestampString }}</span>
|
|
215
|
+
<TelaIcon v-if="version" name="i-ph-dot" color="gray-300" />
|
|
216
|
+
<div v-if="version" py-2px px-4px rounded-5px bg-gray-200 h-16px>
|
|
217
|
+
<p body-caption-9-bold text-gray-600>
|
|
218
|
+
{{ version }}
|
|
219
|
+
</p>
|
|
220
|
+
</div>
|
|
221
|
+
</div>
|
|
222
|
+
<TelaSkeleton v-else class="w-80px h-8px" />
|
|
223
|
+
</div>
|
|
224
|
+
<div flex-auto />
|
|
225
|
+
<TelaDropdownMenu
|
|
226
|
+
v-if="!props.loading"
|
|
227
|
+
:disabled="props.id === 'new'"
|
|
228
|
+
:items="[
|
|
229
|
+
{
|
|
230
|
+
label: 'Delete',
|
|
231
|
+
color: 'negative',
|
|
232
|
+
disabled: !props.authorEmail
|
|
233
|
+
|| !props.currentUser?.email
|
|
234
|
+
|| props.authorEmail !== props.currentUser?.email,
|
|
235
|
+
icon: 'i-ph-trash-simple-bold',
|
|
236
|
+
click: () => emit('delete', props.id),
|
|
237
|
+
},
|
|
238
|
+
]"
|
|
239
|
+
>
|
|
240
|
+
<TelaIconButton
|
|
241
|
+
size="3xs"
|
|
242
|
+
color="secondary"
|
|
243
|
+
icon="i-ph-dots-three-vertical-bold"
|
|
244
|
+
/>
|
|
245
|
+
</TelaDropdownMenu>
|
|
246
|
+
</div>
|
|
247
|
+
|
|
248
|
+
<div v-if="!props.loading" class="mt-1 body-16-regular text-gray-800">
|
|
249
|
+
<span v-html="resolvedContent" />
|
|
250
|
+
</div>
|
|
251
|
+
<div v-else flex="~ col gap-8px">
|
|
252
|
+
<TelaSkeleton
|
|
253
|
+
v-for="width in [50, 30]"
|
|
254
|
+
:key="width"
|
|
255
|
+
:style="{ width: `${width}%` }"
|
|
256
|
+
h-12px
|
|
257
|
+
/>
|
|
258
|
+
</div>
|
|
259
|
+
|
|
260
|
+
<div v-if="!props.loading" class="flex items-start gap-2 mt-8px">
|
|
261
|
+
<TelaButton
|
|
262
|
+
v-if="allowReply"
|
|
263
|
+
variant="ghost"
|
|
264
|
+
size="sm"
|
|
265
|
+
b="0.5px solid gray-300"
|
|
266
|
+
bg-white
|
|
267
|
+
:disabled="props.id === 'new'"
|
|
268
|
+
class="text-gray-900 body-12-semibold"
|
|
269
|
+
@click.stop="emit('reply')"
|
|
270
|
+
>
|
|
271
|
+
<TelaIcon name="i-ph-arrow-bend-down-right" size="sm" />
|
|
272
|
+
Reply
|
|
273
|
+
</TelaButton>
|
|
274
|
+
</div>
|
|
275
|
+
</div>
|
|
276
|
+
</div>
|
|
277
|
+
<TelaIconButton
|
|
278
|
+
v-if="hasReplies"
|
|
279
|
+
z-50
|
|
280
|
+
size="2xs"
|
|
281
|
+
color="secondary"
|
|
282
|
+
icon="i-ph-caret-down"
|
|
283
|
+
b="0.5px solid gray-300"
|
|
284
|
+
h-24px
|
|
285
|
+
w-24px
|
|
286
|
+
:class="{ 'rotate-0': slotProps.isExpanded, '-rotate-90': !slotProps.isExpanded }"
|
|
287
|
+
class="absolute top-70px left-12px z-10 bg-white"
|
|
288
|
+
@click.stop="(e: Event) => {
|
|
289
|
+
slotProps.handleToggle(e)
|
|
290
|
+
isExpandedManually = !isExpandedManually
|
|
291
|
+
}"
|
|
292
|
+
/>
|
|
293
|
+
</div>
|
|
294
|
+
</div>
|
|
295
|
+
|
|
296
|
+
<!-- Recursively render replies as nested TreeItems -->
|
|
297
|
+
<template v-if="hasReplies && slotProps.isExpanded">
|
|
298
|
+
<component
|
|
299
|
+
:is="isChat ? TreeItem : 'div'"
|
|
300
|
+
v-for="reply in replies"
|
|
301
|
+
:key="reply.id"
|
|
302
|
+
:value="{ id: reply.id }"
|
|
303
|
+
:level="(props.depth ?? 1) + 1"
|
|
304
|
+
class="mt-10px ml-24px"
|
|
305
|
+
:style="{
|
|
306
|
+
width: `calc(100% - 62px)`,
|
|
307
|
+
}"
|
|
308
|
+
>
|
|
309
|
+
<TelaChatTextMessage
|
|
310
|
+
:id="reply.id"
|
|
311
|
+
:content="reply.content"
|
|
312
|
+
:timestamp="reply.timestamp"
|
|
313
|
+
:author="reply.author"
|
|
314
|
+
:author-email="reply.authorEmail"
|
|
315
|
+
:avatar-url="reply.avatarUrl"
|
|
316
|
+
:is-reply="true"
|
|
317
|
+
:depth="(props.depth ?? 1) + 1"
|
|
318
|
+
:replies="reply.replies"
|
|
319
|
+
:parent-element="treeItemRef ?? undefined"
|
|
320
|
+
:allow-reply="false"
|
|
321
|
+
:current-user="props.currentUser"
|
|
322
|
+
:other-users="props.otherUsers"
|
|
323
|
+
:is-expanded="isExpandedManually"
|
|
324
|
+
@delete="() => emit('delete', reply.id)"
|
|
325
|
+
/>
|
|
326
|
+
</component>
|
|
327
|
+
</template>
|
|
328
|
+
</component>
|
|
329
|
+
</template>
|
|
330
|
+
|
|
331
|
+
<style scoped>
|
|
332
|
+
.message-container {
|
|
333
|
+
display: flex;
|
|
334
|
+
flex-direction: row;
|
|
335
|
+
width: 100%;
|
|
336
|
+
gap: 3px;
|
|
337
|
+
position: relative;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
.message-container::before {
|
|
341
|
+
width: var(--before-width);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.message-container::after {
|
|
345
|
+
height: var(--after-height);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
.rotate-0 {
|
|
349
|
+
transform: rotate(0deg);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
.-rotate-90 {
|
|
353
|
+
transform: rotate(-90deg);
|
|
354
|
+
}
|
|
355
|
+
</style>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface Message {
|
|
2
|
+
id: string
|
|
3
|
+
content: string
|
|
4
|
+
timestamp?: Date
|
|
5
|
+
author: string
|
|
6
|
+
authorEmail: string
|
|
7
|
+
avatarUrl?: string
|
|
8
|
+
version?: string
|
|
9
|
+
isReply?: boolean
|
|
10
|
+
replies?: Message[]
|
|
11
|
+
parentId?: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type User = {
|
|
15
|
+
id: string
|
|
16
|
+
name: string
|
|
17
|
+
email: string
|
|
18
|
+
avatarUrl: string
|
|
19
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { Label, useForwardPropsEmits } from 'reka-ui'
|
|
3
|
+
import type { CheckboxRootProps, CheckboxRootEmits } from 'reka-ui'
|
|
4
|
+
import type { HTMLAttributes } from 'vue'
|
|
5
|
+
|
|
6
|
+
const props = defineProps<CheckboxRootProps & { label: string, description: string, class?: HTMLAttributes['class'] }>()
|
|
7
|
+
const emits = defineEmits<CheckboxRootEmits>()
|
|
8
|
+
|
|
9
|
+
const forwarded = useForwardPropsEmits(props, emits)
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<template>
|
|
13
|
+
<Label
|
|
14
|
+
:class="cn(
|
|
15
|
+
'w-[380px] p-[12px] flex items-center gap-[12px] rounded-[8px] bg-white-1000 cursor-pointer border-[0.5px] border-gray-200 transition-colors disabled:cursor-not-allowed disabled:bg-gray-100 disabled:border-gray-200',
|
|
16
|
+
'has-[[aria-checked=false]]:hover:bg-gray-50 has-[[aria-checked=false]]:hover:border-gray-300 has-[[aria-checked=true]]:bg-blue-100/60 has-[[aria-checked=true]]:border-blue-700',
|
|
17
|
+
props.class,
|
|
18
|
+
)"
|
|
19
|
+
>
|
|
20
|
+
<TelaCheckbox v-bind="forwarded" />
|
|
21
|
+
<div>
|
|
22
|
+
<h4 class="body-12-medium text-gray-900">
|
|
23
|
+
{{ label }}
|
|
24
|
+
</h4>
|
|
25
|
+
<p class="body-12-regular text-gray-500">
|
|
26
|
+
{{ description }}
|
|
27
|
+
</p>
|
|
28
|
+
</div>
|
|
29
|
+
</Label>
|
|
30
|
+
</template>
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
|
|
2
|
+
import * as CheckboxStories from './checkbox.stories.ts';
|
|
3
|
+
|
|
4
|
+
<Meta of={CheckboxStories} />
|
|
5
|
+
|
|
6
|
+
# TelaCheckbox
|
|
7
|
+
|
|
8
|
+
A checkbox component built on reka-ui. Supports v-model binding, disabled states, and integrates with forms. Features smooth check animations and includes a checkbox card variant for more complex use cases with labels and descriptions.
|
|
9
|
+
|
|
10
|
+
## Examples
|
|
11
|
+
|
|
12
|
+
### With Label
|
|
13
|
+
|
|
14
|
+
<Canvas of={CheckboxStories.WithLabel} />
|
|
15
|
+
|
|
16
|
+
### Disabled
|
|
17
|
+
|
|
18
|
+
<Canvas of={CheckboxStories.Disabled} />
|
|
19
|
+
|
|
20
|
+
### With Model Value
|
|
21
|
+
|
|
22
|
+
<Canvas of={CheckboxStories.WithModelValue} />
|
|
23
|
+
|
|
24
|
+
### Checkbox Card
|
|
25
|
+
|
|
26
|
+
<Canvas of={CheckboxStories.Card} />
|
|
27
|
+
|
|
28
|
+
### Basic Usage
|
|
29
|
+
|
|
30
|
+
```vue
|
|
31
|
+
<script setup>
|
|
32
|
+
import { ref } from 'vue'
|
|
33
|
+
|
|
34
|
+
const checked = ref(false)
|
|
35
|
+
</script>
|
|
36
|
+
|
|
37
|
+
<template>
|
|
38
|
+
<TelaCheckbox v-model="checked" />
|
|
39
|
+
</template>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### With Label Code
|
|
43
|
+
|
|
44
|
+
```vue
|
|
45
|
+
<script setup>
|
|
46
|
+
import { ref } from 'vue'
|
|
47
|
+
|
|
48
|
+
const accepted = ref(false)
|
|
49
|
+
</script>
|
|
50
|
+
|
|
51
|
+
<template>
|
|
52
|
+
<div class="flex items-center gap-2">
|
|
53
|
+
<TelaCheckbox id="terms" v-model="accepted" />
|
|
54
|
+
<label for="terms" class="cursor-pointer">
|
|
55
|
+
I accept the terms and conditions
|
|
56
|
+
</label>
|
|
57
|
+
</div>
|
|
58
|
+
</template>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Disabled State
|
|
62
|
+
|
|
63
|
+
```vue
|
|
64
|
+
<TelaCheckbox disabled :model-value="true" />
|
|
65
|
+
<TelaCheckbox disabled :model-value="false" />
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### In Forms
|
|
69
|
+
|
|
70
|
+
```vue
|
|
71
|
+
<script setup>
|
|
72
|
+
import { ref } from 'vue'
|
|
73
|
+
|
|
74
|
+
const preferences = ref([])
|
|
75
|
+
</script>
|
|
76
|
+
|
|
77
|
+
<template>
|
|
78
|
+
<form>
|
|
79
|
+
<div class="flex items-center gap-2">
|
|
80
|
+
<TelaCheckbox
|
|
81
|
+
id="notifications"
|
|
82
|
+
v-model="preferences"
|
|
83
|
+
value="notifications"
|
|
84
|
+
name="preferences"
|
|
85
|
+
/>
|
|
86
|
+
<label for="notifications">Enable notifications</label>
|
|
87
|
+
</div>
|
|
88
|
+
|
|
89
|
+
<div class="flex items-center gap-2">
|
|
90
|
+
<TelaCheckbox
|
|
91
|
+
id="newsletter"
|
|
92
|
+
v-model="preferences"
|
|
93
|
+
value="newsletter"
|
|
94
|
+
name="preferences"
|
|
95
|
+
/>
|
|
96
|
+
<label for="newsletter">Subscribe to newsletter</label>
|
|
97
|
+
</div>
|
|
98
|
+
</form>
|
|
99
|
+
</template>
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Checkbox Card Code
|
|
103
|
+
|
|
104
|
+
```vue
|
|
105
|
+
<TelaCheckboxCard
|
|
106
|
+
label="Make available to all workspaces"
|
|
107
|
+
description="All workspaces will have access to this template."
|
|
108
|
+
/>
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Props
|
|
112
|
+
|
|
113
|
+
<ArgTypes />
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
type CheckboxProps = {
|
|
117
|
+
modelValue?: boolean
|
|
118
|
+
disabled?: boolean
|
|
119
|
+
required?: boolean
|
|
120
|
+
name?: string
|
|
121
|
+
value?: string
|
|
122
|
+
class?: string
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Features
|
|
127
|
+
|
|
128
|
+
- **Two-way Binding**: Full v-model support for reactive state
|
|
129
|
+
- **Smooth Animation**: Check mark animates in with path drawing effect
|
|
130
|
+
- **Disabled State**: Proper disabled styling with grayed out appearance
|
|
131
|
+
- **Form Integration**: Works with native form submission
|
|
132
|
+
- **Hover States**: Interactive hover feedback
|
|
133
|
+
- **Focus Ring**: Keyboard navigation with visible focus indicator
|
|
134
|
+
- **Accessible**: Built on reka-ui with proper ARIA attributes
|
|
135
|
+
- **Card Variant**: TelaCheckboxCard for enhanced layouts with labels and descriptions
|
|
136
|
+
|
|
137
|
+
## Components
|
|
138
|
+
|
|
139
|
+
- `TelaCheckbox` - Standard checkbox component
|
|
140
|
+
- `TelaCheckboxCard` - Enhanced checkbox with label and description layout
|
|
141
|
+
|
|
142
|
+
## States
|
|
143
|
+
|
|
144
|
+
- **Unchecked**: White background with gray border
|
|
145
|
+
- **Checked**: Dark background with white checkmark
|
|
146
|
+
- **Hover Unchecked**: Light gray background
|
|
147
|
+
- **Hover Checked**: Slightly lighter dark background
|
|
148
|
+
- **Disabled**: Reduced opacity, cursor not-allowed
|
|
149
|
+
|
|
150
|
+
## Animation
|
|
151
|
+
|
|
152
|
+
The checkbox uses Motion components for smooth check mark animation:
|
|
153
|
+
- Scale animation from 0.85 to 1
|
|
154
|
+
- Path length animation from 0 to 1 (drawing effect)
|
|
155
|
+
- 200ms duration with custom easing
|
|
156
|
+
|
|
157
|
+
## Accessibility
|
|
158
|
+
|
|
159
|
+
- Built on reka-ui's CheckboxRoot and CheckboxIndicator
|
|
160
|
+
- Proper ARIA attributes for screen readers
|
|
161
|
+
- Keyboard support (Space to toggle)
|
|
162
|
+
- Focus visible ring for keyboard navigation
|
|
163
|
+
- Works with form labels using `id` and `for` attributes
|
|
164
|
+
- Disabled state properly conveyed
|