@meistrari/tela-build 1.47.3 → 1.49.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.
Files changed (75) hide show
  1. package/components/tela/avatar/label/avatar-label.mdx +93 -0
  2. package/components/tela/avatar/label/avatar-label.stories.ts +75 -0
  3. package/components/tela/avatar/label/avatar-label.vue +28 -0
  4. package/components/tela/button/button.vue +6 -6
  5. package/components/tela/card/card.mdx +10 -8
  6. package/components/tela/card/card.stories.ts +19 -19
  7. package/components/tela/card/card.vue +6 -4
  8. package/components/tela/chart/chart-bar.vue +2 -2
  9. package/components/tela/collapsible-group/collapsible-group-item.vue +47 -0
  10. package/components/tela/collapsible-group/collapsible-group.mdx +174 -0
  11. package/components/tela/collapsible-group/collapsible-group.stories.ts +134 -0
  12. package/components/tela/collapsible-group/collapsible-group.vue +5 -0
  13. package/components/tela/create-card/create-card.mdx +84 -0
  14. package/components/tela/create-card/create-card.stories.ts +133 -0
  15. package/components/tela/create-card/create-card.vue +108 -0
  16. package/components/tela/details/details-content-columns.vue +5 -0
  17. package/components/tela/details/details-content.vue +10 -0
  18. package/components/tela/details/details-footer.vue +5 -0
  19. package/components/tela/details/details-review-confirm.vue +42 -0
  20. package/components/tela/details/details-summary-label.vue +16 -0
  21. package/components/tela/details/details-summary.vue +5 -0
  22. package/components/tela/details/details-title.vue +17 -0
  23. package/components/tela/details/details.mdx +475 -0
  24. package/components/tela/details/details.vue +5 -0
  25. package/components/tela/details/hero/hero-aside.vue +5 -0
  26. package/components/tela/details/hero/hero-identity.vue +8 -0
  27. package/components/tela/details/hero/hero-main.vue +5 -0
  28. package/components/tela/details/hero/hero-metric-group.vue +13 -0
  29. package/components/tela/details/hero/hero-metric.vue +16 -0
  30. package/components/tela/details/hero/hero-metrics-card.vue +7 -0
  31. package/components/tela/details/hero/hero-stat.vue +16 -0
  32. package/components/tela/details/hero/hero-stats.vue +5 -0
  33. package/components/tela/details/hero/hero.vue +9 -0
  34. package/components/tela/details/left/left-header.vue +5 -0
  35. package/components/tela/details/left/left.vue +20 -0
  36. package/components/tela/details/right/right.vue +13 -0
  37. package/components/tela/details/use-details-scroll.ts +91 -0
  38. package/components/tela/dot-separator.vue +3 -0
  39. package/components/tela/filter/filter-trigger.vue +1 -1
  40. package/components/tela/header/header.vue +1 -1
  41. package/components/tela/header/leading/summary/summary-status.vue +1 -1
  42. package/components/tela/home/home-body.vue +5 -0
  43. package/components/tela/home/home-content.vue +5 -0
  44. package/components/tela/home/home-section.vue +5 -0
  45. package/components/tela/home/home-title.vue +5 -0
  46. package/components/tela/home/home-toolbar.vue +10 -0
  47. package/components/tela/home/home.mdx +285 -0
  48. package/components/tela/home/home.vue +5 -0
  49. package/components/tela/home/metrics/metrics-card.vue +23 -0
  50. package/components/tela/home/metrics/metrics-group.vue +5 -0
  51. package/components/tela/home/metrics/metrics-stat.vue +23 -0
  52. package/components/tela/home/metrics/metrics.vue +18 -0
  53. package/components/tela/icon/custom.vue +9 -6
  54. package/components/tela/input/input.vue +1 -1
  55. package/components/tela/preview/preview-content.vue +2 -2
  56. package/components/tela/preview/preview-floating-bar.vue +1 -1
  57. package/components/tela/progress-bar/progress-bar.mdx +117 -0
  58. package/components/tela/progress-bar/progress-bar.stories.ts +148 -0
  59. package/components/tela/progress-bar/progress-bar.vue +88 -0
  60. package/components/tela/segment-bar/segment-bar.mdx +57 -0
  61. package/components/tela/segment-bar/segment-bar.stories.ts +115 -0
  62. package/components/tela/segment-bar/segment-bar.vue +88 -0
  63. package/components/tela/sidebar/sidebar-footer.vue +1 -1
  64. package/components/tela/sidebar/sidebar-item.vue +2 -1
  65. package/components/tela/sidebar/sidebar.mdx +2 -2
  66. package/components/tela/status/status.mdx +23 -1
  67. package/components/tela/status/status.stories.ts +40 -0
  68. package/components/tela/status/status.vue +22 -3
  69. package/components/tela/table/table-head.vue +1 -1
  70. package/components/tela/table/table-header.vue +1 -1
  71. package/components/tela/table/table-row.vue +1 -1
  72. package/components/tela/tabs/tabs-indicator.vue +1 -1
  73. package/components/tela/tabs/tabs-trigger.vue +1 -1
  74. package/package.json +1 -1
  75. package/utils/design-tokens.ts +1 -1
@@ -0,0 +1,93 @@
1
+ import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
2
+ import * as AvatarLabelStories from './avatar-label.stories.ts';
3
+
4
+ <Meta of={AvatarLabelStories} />
5
+
6
+ # TelaAvatarLabel
7
+
8
+ An inline avatar + text chip. Pairs a `TelaAvatar` with a text label so any "entity reference" (created-by, assigned-to, owner, reviewer, member, etc.) renders consistently across summary rows, list cells, and metadata strips. The component is named after its **shape**, not a role — pick the role at the call site via the `label`.
9
+
10
+ ## Examples
11
+
12
+ ### Basic Usage
13
+
14
+ ```vue
15
+ <TelaAvatarLabel label="Gustavo Rodrigues" />
16
+ ```
17
+
18
+ ### With Avatar Image
19
+
20
+ ```vue
21
+ <TelaAvatarLabel
22
+ label="Jane Doe"
23
+ avatar-src="https://i.pravatar.cc/150?img=5"
24
+ />
25
+ ```
26
+
27
+ ### Larger Avatar
28
+
29
+ ```vue
30
+ <TelaAvatarLabel
31
+ label="Project Phoenix"
32
+ avatar-size="sm"
33
+ />
34
+ ```
35
+
36
+ ### Inside a Summary Row
37
+
38
+ A common usage — combining text labels, a `TelaDotSeparator`, and an avatar label:
39
+
40
+ ```vue
41
+ <TelaDetailsSummary>
42
+ <TelaDetailsSummaryLabel>
43
+ Processed 23 minutes ago
44
+ </TelaDetailsSummaryLabel>
45
+ <TelaDotSeparator />
46
+ <TelaAvatarLabel label="Gustavo Rodrigues" />
47
+ </TelaDetailsSummary>
48
+ ```
49
+
50
+ ### Custom Alt Text
51
+
52
+ By default `avatarAlt` falls back to `label`. Override when the alt text needs to differ (e.g. for screen reader context):
53
+
54
+ ```vue
55
+ <TelaAvatarLabel
56
+ label="Gustavo Rodrigues"
57
+ avatar-alt="Avatar of Gustavo Rodrigues, case reviewer"
58
+ />
59
+ ```
60
+
61
+ ## Props
62
+
63
+ <ArgTypes />
64
+
65
+ ```typescript
66
+ type AvatarLabelProps = {
67
+ label: string
68
+ avatarSize?: '2xs' | 'xs' | 'sm' | 'md' | 'lg' // default: '2xs'
69
+ avatarSrc?: string
70
+ avatarAlt?: string // falls back to `label`
71
+ }
72
+ ```
73
+
74
+ ## Slots
75
+
76
+ This component does not expose slots — `label` is the single source of truth for the text. If you need rich content next to the avatar, compose `TelaAvatar` directly instead.
77
+
78
+ ## Layout
79
+
80
+ Renders as `flex items-center gap-4px` — a small inline chip designed to sit next to other inline elements (text, separators, badges) inside a summary row.
81
+
82
+ ## Use Cases
83
+
84
+ - **Summary rows** in details headers (created-by, assigned-to, last edited by).
85
+ - **List cells** showing the owner / responsible person for a row.
86
+ - **Activity feeds** to attribute an action to someone.
87
+ - **Comment headers** above a comment body.
88
+ - **Notification rows** indicating who triggered an event.
89
+
90
+ ## Related
91
+
92
+ - `TelaAvatar` — underlying avatar primitive.
93
+ - `TelaDotSeparator` — pairs naturally inside a summary row between labels.
@@ -0,0 +1,75 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3'
2
+ import TelaAvatarLabel from './avatar-label.vue'
3
+
4
+ const meta = {
5
+ title: 'Core/AvatarLabel',
6
+ component: TelaAvatarLabel,
7
+ tags: ['autodocs'],
8
+ parameters: {
9
+ layout: 'centered',
10
+ docs: {
11
+ description: {
12
+ component: 'An inline avatar + text chip. Pairs a `TelaAvatar` with a label, useful for representing any entity reference (created-by, assigned-to, owner, reviewer, etc.) in summary rows, lists, or metadata.',
13
+ },
14
+ },
15
+ },
16
+ argTypes: {
17
+ label: {
18
+ control: 'text',
19
+ description: 'Text rendered next to the avatar.',
20
+ },
21
+ avatarSize: {
22
+ control: 'select',
23
+ options: ['2xs', 'xs', 'sm', 'md', 'lg'],
24
+ description: 'Size variant for the avatar. Defaults to `2xs` to fit inline in summary rows.',
25
+ },
26
+ avatarSrc: {
27
+ control: 'text',
28
+ description: 'Image URL for the avatar. When omitted, the avatar falls back to its initials/placeholder behavior.',
29
+ },
30
+ avatarAlt: {
31
+ control: 'text',
32
+ description: 'Alt text for the avatar image. Falls back to `label` when not provided.',
33
+ },
34
+ },
35
+ args: {
36
+ label: 'Gustavo Rodrigues',
37
+ avatarSize: '2xs',
38
+ },
39
+ } satisfies Meta<typeof TelaAvatarLabel>
40
+
41
+ export default meta
42
+
43
+ type Story = StoryObj<typeof meta>
44
+
45
+ export const Default: Story = {}
46
+
47
+ export const WithImage: Story = {
48
+ args: {
49
+ label: 'Jane Doe',
50
+ avatarSrc: 'https://i.pravatar.cc/150?img=5',
51
+ },
52
+ }
53
+
54
+ export const Larger: Story = {
55
+ args: {
56
+ label: 'Project Phoenix',
57
+ avatarSize: 'sm',
58
+ },
59
+ }
60
+
61
+ export const InSummaryRow: Story = {
62
+ render: args => ({
63
+ components: { TelaAvatarLabel },
64
+ setup() {
65
+ return { args }
66
+ },
67
+ template: `
68
+ <div class="flex items-center gap-8px">
69
+ <p class="text-body-12-regular text-secondary">Created 23 minutes ago</p>
70
+ <div aria-hidden="true" class="size-3px rounded-full bg-icon-subtle" />
71
+ <TelaAvatarLabel v-bind="args" />
72
+ </div>
73
+ `,
74
+ }),
75
+ }
@@ -0,0 +1,28 @@
1
+ <script setup lang="ts">
2
+ type AvatarSize = '2xs' | 'xs' | 'sm' | 'md' | 'lg'
3
+
4
+ interface Props {
5
+ label: string
6
+ avatarSize?: AvatarSize
7
+ avatarSrc?: string
8
+ avatarAlt?: string
9
+ }
10
+
11
+ withDefaults(defineProps<Props>(), {
12
+ avatarSize: '2xs',
13
+ })
14
+ </script>
15
+
16
+ <template>
17
+ <div flex items-center gap-4px>
18
+ <TelaAvatar
19
+ :size="avatarSize"
20
+ :src="avatarSrc"
21
+ :alt="avatarAlt ?? label"
22
+ rounded-full
23
+ />
24
+ <p body-12-regular text-secondary>
25
+ {{ label }}
26
+ </p>
27
+ </div>
28
+ </template>
@@ -33,16 +33,16 @@ const sizeStyle = computed(() => (({
33
33
 
34
34
  const variantStyle = computed(() => (({
35
35
  'primary': fold`
36
- bg-gray-900 text-white
37
- hover:bg-gray-800
38
- active:bg-gray-700
36
+ bg-neutral-900 text-white
37
+ hover:bg-neutral-800
38
+ active:bg-neutral-700
39
39
  focus-visible:ring-0.5px focus-visible:ring-cyan-600
40
40
  `,
41
41
  'secondary': fold`
42
- bg-white text-gray-900 border border-0.5px
42
+ bg-white text-neutral-900 border border-0.5px
43
43
  [box-shadow:0_1px_6px_0_rgba(103,127,148,0.05)]
44
- hover:bg-subtle hover:border-gray-300
45
- active:bg-muted active:border-gray-400/60
44
+ hover:bg-subtle hover:border-strong
45
+ active:bg-muted active:border-neutral-400/60
46
46
  focus-visible:ring-0.5px focus-visible:ring-cyan-600
47
47
  `,
48
48
  'ghost': fold`
@@ -14,16 +14,18 @@ A surface container component used to group related content with consistent visu
14
14
 
15
15
  Always use the `size` prop to control card padding — it maps directly to the standardized values. Never apply padding manually to `<TelaCard>` or its inner elements.
16
16
 
17
+ Only `xs` and `sm` are available. Both share `rounded-12px`.
18
+
17
19
  | `size` | Applied classes | Use for |
18
20
  |--------|------------------|---------|
19
- | `md` *(default)* | `p-32px sm:p-48px rounded-24px` | Standard and large cards |
20
- | `sm` | `p-24px rounded-12px` | Small cards, inner containers |
21
+ | `xs` | `p-20px` | Compact cards, dense inner containers |
22
+ | `sm` *(default)* | `p-24px` | Standard cards |
21
23
 
22
24
  ```vue
23
- <!-- Correct — use size prop -->
24
- <TelaCard size="md">...</TelaCard>
25
+ <!-- Correct — use xs or sm -->
26
+ <TelaCard size="xs">...</TelaCard>
25
27
  <TelaCard size="sm">...</TelaCard>
26
- <TelaCard>...</TelaCard> <!-- md is the default -->
28
+ <TelaCard>...</TelaCard> <!-- sm is the default -->
27
29
 
28
30
  <!-- Incorrect — never apply padding manually -->
29
31
  <TelaCard class="p-20px">...</TelaCard>
@@ -43,10 +45,10 @@ Always use the `size` prop to control card padding — it maps directly to the s
43
45
  </TelaCard>
44
46
  ```
45
47
 
46
- ### Minor / Inner Card
48
+ ### Compact / Inner Card
47
49
 
48
50
  ```vue
49
- <TelaCard size="sm">
51
+ <TelaCard size="xs">
50
52
  <span class="body-12-medium text-secondary">Compact content</span>
51
53
  </TelaCard>
52
54
  ```
@@ -85,7 +87,7 @@ Cards in grids must use `h-full` for consistent heights.
85
87
 
86
88
  | Prop | Type | Default | Description |
87
89
  |------|------|---------|-------------|
88
- | `size` | `'sm' \| 'md'` | `'md'` | `md` = `p-32px sm:p-48px rounded-24px`, `sm` = `p-24px rounded-12px` |
90
+ | `size` | `'xs' \| 'sm'` | `'sm'` | `xs` = `p-20px`, `sm` = `p-24px`. Both use `rounded-12px` |
89
91
 
90
92
  ## Slots
91
93
 
@@ -8,15 +8,15 @@ const meta: Meta<typeof Card> = {
8
8
  layout: 'centered',
9
9
  docs: {
10
10
  description: {
11
- component: 'A surface container that groups related content with consistent visual boundaries. Use the `size` prop to control padding — `md` for standard cards, `sm` for minor or inner containers.',
11
+ component: 'A surface container that groups related content with consistent visual boundaries. Use the `size` prop to control padding — `sm` for standard cards, `xs` for compact or inner containers.',
12
12
  },
13
13
  },
14
14
  },
15
15
  argTypes: {
16
16
  size: {
17
17
  control: 'select',
18
- options: ['md', 'sm'],
19
- description: '`md` applies standard card padding. `sm` applies minor/inner container padding.',
18
+ options: ['xs', 'sm'],
19
+ description: '`sm` applies standard card padding. `xs` applies compact/inner container padding.',
20
20
  },
21
21
  },
22
22
  }
@@ -41,7 +41,7 @@ export const Default: Story = {
41
41
  `,
42
42
  }),
43
43
  args: {
44
- size: 'md',
44
+ size: 'sm',
45
45
  },
46
46
  }
47
47
 
@@ -49,10 +49,10 @@ export const Standard: Story = {
49
49
  render: () => ({
50
50
  components: { Card },
51
51
  template: `
52
- <Card size="md">
52
+ <Card size="sm">
53
53
  <div style="display: flex; flex-direction: column; gap: 8px; min-width: 280px;">
54
54
  <p class="heading-h4-semibold text-contrast">Standard Card</p>
55
- <p class="body-14-regular text-secondary">Used for large, standard, and medium cards.</p>
55
+ <p class="body-14-regular text-secondary">Used for standard content surfaces.</p>
56
56
  </div>
57
57
  </Card>
58
58
  `,
@@ -60,7 +60,7 @@ export const Standard: Story = {
60
60
  parameters: {
61
61
  docs: {
62
62
  description: {
63
- story: 'Default size (`md`). Use for primary content surfaces.',
63
+ story: 'Default size (`sm`). Use for primary content surfaces.',
64
64
  },
65
65
  },
66
66
  },
@@ -70,10 +70,10 @@ export const Minor: Story = {
70
70
  render: () => ({
71
71
  components: { Card },
72
72
  template: `
73
- <Card size="sm">
73
+ <Card size="xs">
74
74
  <div style="display: flex; flex-direction: column; gap: 8px; min-width: 240px;">
75
- <p class="heading-h5-semibold text-contrast">Minor Card</p>
76
- <p class="body-12-regular text-secondary">Used for small cards and inner containers.</p>
75
+ <p class="heading-h5-semibold text-contrast">Compact Card</p>
76
+ <p class="body-12-regular text-secondary">Used for compact cards and inner containers.</p>
77
77
  </div>
78
78
  </Card>
79
79
  `,
@@ -81,7 +81,7 @@ export const Minor: Story = {
81
81
  parameters: {
82
82
  docs: {
83
83
  description: {
84
- story: 'Small size (`sm`). Use for compact cards or nested inner containers.',
84
+ story: 'Compact size (`xs`). Use for compact cards or nested inner containers.',
85
85
  },
86
86
  },
87
87
  },
@@ -92,18 +92,18 @@ export const SizeComparison: Story = {
92
92
  components: { Card },
93
93
  template: `
94
94
  <div style="display: flex; gap: 16px; align-items: flex-start;">
95
- <Card size="md">
95
+ <Card size="sm">
96
96
  <div style="display: flex; flex-direction: column; gap: 6px; min-width: 200px;">
97
- <p class="body-12-medium text-secondary">size="md"</p>
97
+ <p class="body-12-medium text-secondary">size="sm"</p>
98
98
  <p class="heading-h4-semibold text-contrast">Standard</p>
99
- <p class="body-14-regular text-tertiary">p-32px sm:p-48px padding</p>
99
+ <p class="body-14-regular text-tertiary">p-24px padding</p>
100
100
  </div>
101
101
  </Card>
102
- <Card size="sm">
102
+ <Card size="xs">
103
103
  <div style="display: flex; flex-direction: column; gap: 6px; min-width: 200px;">
104
- <p class="body-12-medium text-secondary">size="sm"</p>
105
- <p class="heading-h4-semibold text-contrast">Minor</p>
106
- <p class="body-14-regular text-tertiary">p-24px padding</p>
104
+ <p class="body-12-medium text-secondary">size="xs"</p>
105
+ <p class="heading-h4-semibold text-contrast">Compact</p>
106
+ <p class="body-14-regular text-tertiary">p-20px padding</p>
107
107
  </div>
108
108
  </Card>
109
109
  </div>
@@ -136,7 +136,7 @@ export const Grid: Story = {
136
136
  layout: 'padded',
137
137
  docs: {
138
138
  description: {
139
- story: 'Cards used in a grid layout. No padding override needed — `size="md"` is the default.',
139
+ story: 'Cards used in a grid layout. No padding override needed — `size="sm"` is the default.',
140
140
  },
141
141
  },
142
142
  },
@@ -2,20 +2,22 @@
2
2
  import type { HTMLAttributes } from 'vue'
3
3
 
4
4
  const props = withDefaults(defineProps<{
5
- size?: 'sm' | 'md'
5
+ size?: 'xs' | 'sm' | 'md'
6
6
  class?: HTMLAttributes['class']
7
7
  /** @deprecated Use `class` instead */
8
8
  contentPadding?: HTMLAttributes['class']
9
9
  /** @deprecated Use `class` instead */
10
10
  borderRadius?: HTMLAttributes['class']
11
11
  }>(), {
12
- size: 'md',
12
+ size: 'sm',
13
13
  })
14
14
 
15
15
  const sizeStyles = computed(() => (({
16
+ xs: { padding: 'p-20px' },
16
17
  sm: { padding: 'p-24px' },
18
+ /** @deprecated: Use 'xs' or 'sm' instead */
17
19
  md: { padding: 'p-32px' },
18
- }) as Record<string, { padding: string }>)[props.size ?? 'md'] ?? { padding: '' })
20
+ }) as Record<string, { padding: string }>)[props.size ?? 'sm'] ?? { padding: '' })
19
21
 
20
22
  const paddingClass = computed(() => props.contentPadding ?? sizeStyles.value.padding)
21
23
  const rootEl = ref<HTMLElement | null>(null)
@@ -26,7 +28,7 @@ defineExpose({
26
28
  </script>
27
29
 
28
30
  <template>
29
- <div ref="rootEl" :class="cn('rounded-16px bg border-0.5px border', paddingClass, props.class)">
31
+ <div ref="rootEl" :class="cn('rounded-12px bg border-0.5px border', paddingClass, props.class)">
30
32
  <slot />
31
33
  </div>
32
34
  </template>
@@ -8,7 +8,7 @@ const props = withDefaults(defineProps<{
8
8
  }>(), {
9
9
  height: '12px',
10
10
  gap: '2px',
11
- duration: 1000,
11
+ duration: 400,
12
12
  })
13
13
 
14
14
  const animatedFilled = ref(0)
@@ -52,7 +52,7 @@ watch(() => props.filled, (newValue) => {
52
52
  :style="{ height }"
53
53
  w-1px
54
54
  rounded-1px
55
- :bg="bar.isFilled ? '#5DC16B' : '#DFE3E7'"
55
+ :bg="bar.isFilled ? 'green-500' : 'neutral-200'"
56
56
  />
57
57
  </div>
58
58
  </template>
@@ -0,0 +1,47 @@
1
+ <script setup lang="ts">
2
+ const props = withDefaults(defineProps<{
3
+ title: string
4
+ itemsLength?: number
5
+ singularLabel?: string
6
+ pluralLabel?: string
7
+ }>(), {
8
+ singularLabel: 'item',
9
+ pluralLabel: 'items',
10
+ })
11
+
12
+ const open = defineModel<boolean>('open', { default: false })
13
+ </script>
14
+
15
+ <template>
16
+ <TelaCollapsible v-model:open="open" class="first:rounded-t-inherit last:rounded-b-inherit">
17
+ <TelaCollapsibleTrigger
18
+ class="group data-[state=closed]:rounded-b-inherit focus-visible:ring-1 focus-visible:ring-neutral-400"
19
+ outline-none w-full first:rounded-t-inherit
20
+ >
21
+ <div flex items-center justify-between py-20px pl-16px pr-20px>
22
+ <div flex items-center gap-16px>
23
+ <TelaIcon
24
+ name="i-ph-caret-down"
25
+ size="24px"
26
+ color="icon-tertiary"
27
+ class="transition-all ease-out group-data-[state=open]:rotate-180"
28
+ />
29
+ <h5 heading-h4-semibold text-primary>
30
+ {{ title }}
31
+ </h5>
32
+ </div>
33
+ <div flex items-center gap-14px>
34
+ <slot name="trigger-trailing" />
35
+ <p v-if="props.itemsLength" body-14-regular text-secondary>
36
+ {{ props.itemsLength }} {{ props.itemsLength === 1 ? props.singularLabel : props.pluralLabel }}
37
+ </p>
38
+ </div>
39
+ </div>
40
+ </TelaCollapsibleTrigger>
41
+ <TelaCollapsibleContent>
42
+ <div pl-56px pr-36px pb-28px>
43
+ <slot />
44
+ </div>
45
+ </TelaCollapsibleContent>
46
+ </TelaCollapsible>
47
+ </template>
@@ -0,0 +1,174 @@
1
+ import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
2
+ import * as CollapsibleGroupStories from './collapsible-group.stories.ts';
3
+
4
+ <Meta of={CollapsibleGroupStories} />
5
+
6
+ # TelaCollapsibleGroup
7
+
8
+ A grouped collapsible component for stacking multiple expandable items in a single bordered container with dividers between rows. Each `TelaCollapsibleGroupItem` supports a title, an optional item count with customizable singular/plural labels, an open/close model, and a slot for arbitrary content. Useful for accordion-style sections, document outlines, and grouped detail panels.
9
+
10
+ ## Examples
11
+
12
+ ### Basic Usage
13
+
14
+ ```vue
15
+ <TelaCollapsibleGroup>
16
+ <TelaCollapsibleGroupItem title="Item 1" :items-length="4">
17
+ <p>Lorem ipsum dolor sit amet.</p>
18
+ </TelaCollapsibleGroupItem>
19
+ <TelaCollapsibleGroupItem title="Item 2" :items-length="2">
20
+ <p>Sed do eiusmod tempor incididunt.</p>
21
+ </TelaCollapsibleGroupItem>
22
+ <TelaCollapsibleGroupItem title="Item 3" :items-length="1">
23
+ <p>Ut enim ad minim veniam.</p>
24
+ </TelaCollapsibleGroupItem>
25
+ </TelaCollapsibleGroup>
26
+ ```
27
+
28
+ ### Without Item Count
29
+
30
+ Omit `itemsLength` to hide the count badge entirely.
31
+
32
+ ```vue
33
+ <TelaCollapsibleGroup>
34
+ <TelaCollapsibleGroupItem title="Overview">
35
+ <p>Just a title and content.</p>
36
+ </TelaCollapsibleGroupItem>
37
+ <TelaCollapsibleGroupItem title="Details">
38
+ <p>No count is shown.</p>
39
+ </TelaCollapsibleGroupItem>
40
+ </TelaCollapsibleGroup>
41
+ ```
42
+
43
+ ### Custom Singular / Plural Labels
44
+
45
+ The count text defaults to `item` / `items`. Pass `singularLabel` and `pluralLabel` to localize or customize.
46
+
47
+ ```vue
48
+ <TelaCollapsibleGroup>
49
+ <TelaCollapsibleGroupItem
50
+ title="Documents"
51
+ :items-length="1"
52
+ singular-label="file"
53
+ plural-label="files"
54
+ >
55
+ <p>A single file.</p>
56
+ </TelaCollapsibleGroupItem>
57
+ <TelaCollapsibleGroupItem
58
+ title="Reports"
59
+ :items-length="12"
60
+ singular-label="file"
61
+ plural-label="files"
62
+ >
63
+ <p>Twelve files inside.</p>
64
+ </TelaCollapsibleGroupItem>
65
+ </TelaCollapsibleGroup>
66
+ ```
67
+
68
+ ### Controlled Open State
69
+
70
+ Use `v-model:open` to control whether an item is expanded.
71
+
72
+ ```vue
73
+ <script setup>
74
+ const isOpen = ref(true)
75
+ </script>
76
+
77
+ <template>
78
+ <TelaCollapsibleGroup>
79
+ <TelaCollapsibleGroupItem
80
+ v-model:open="isOpen"
81
+ title="Controlled Item"
82
+ :items-length="3"
83
+ >
84
+ <p>Open state is bound to a parent ref.</p>
85
+ </TelaCollapsibleGroupItem>
86
+ </TelaCollapsibleGroup>
87
+ </template>
88
+ ```
89
+
90
+ ### Custom Trailing Slot
91
+
92
+ The `trigger-trailing` slot lets you place arbitrary content (badges, actions) next to the item count.
93
+
94
+ ```vue
95
+ <TelaCollapsibleGroup>
96
+ <TelaCollapsibleGroupItem title="With Action" :items-length="2">
97
+ <template #trigger-trailing>
98
+ <TelaBadge variant="warning">Pending</TelaBadge>
99
+ </template>
100
+ <p>Item content goes here.</p>
101
+ </TelaCollapsibleGroupItem>
102
+ </TelaCollapsibleGroup>
103
+ ```
104
+
105
+ ## Props
106
+
107
+ ### TelaCollapsibleGroup
108
+
109
+ Wrapper component. Accepts no props — renders a bordered, divided container around its default slot.
110
+
111
+ ### TelaCollapsibleGroupItem
112
+
113
+ <ArgTypes />
114
+
115
+ ```typescript
116
+ type CollapsibleGroupItemProps = {
117
+ title: string
118
+ itemsLength?: number
119
+ singularLabel?: string // default: 'item'
120
+ pluralLabel?: string // default: 'items'
121
+ }
122
+
123
+ type CollapsibleGroupItemModels = {
124
+ open?: boolean // v-model:open
125
+ }
126
+ ```
127
+
128
+ ## Slots
129
+
130
+ ### TelaCollapsibleGroup
131
+
132
+ - `default` — One or more `TelaCollapsibleGroupItem` components.
133
+
134
+ ### TelaCollapsibleGroupItem
135
+
136
+ - `default` — Content revealed when the item is expanded.
137
+ - `trigger-trailing` — Custom content rendered to the left of the item count in the header row.
138
+
139
+ ## Components
140
+
141
+ The collapsible group system consists of two components:
142
+
143
+ - `TelaCollapsibleGroup` — Bordered container with dividers between items.
144
+ - `TelaCollapsibleGroupItem` — Individual expandable row with title, optional count, and content.
145
+
146
+ ## Features
147
+
148
+ - **Stacked Layout**: Multiple items share a single bordered container with dividers.
149
+ - **Item Count**: Optional numeric badge with customizable singular/plural labels.
150
+ - **Controlled or Uncontrolled**: Use `v-model:open` or let each item manage its own state.
151
+ - **Custom Trailing Content**: Slot for badges or actions next to the count.
152
+ - **Smooth Animation**: Caret icon rotates and content animates on toggle.
153
+ - **Accessible**: Built on `TelaCollapsible` (reka-ui Collapsible primitives).
154
+
155
+ ## Use Cases
156
+
157
+ - **Document Sections**: Group chapters, attachments, or structured details.
158
+ - **Accordions**: Stack related expandable items with consistent styling.
159
+ - **File / Folder Lists**: Show grouped items with counts.
160
+ - **Detail Panels**: Organize record metadata into expandable groups.
161
+
162
+ ## Best Practices
163
+
164
+ 1. **Consistent Counts**: When using `itemsLength`, apply it to all items in the group for visual rhythm.
165
+ 2. **Localize Labels**: Always pass `singularLabel` / `pluralLabel` when copy needs to match the surrounding locale.
166
+ 3. **Avoid Deep Nesting**: Don't nest `TelaCollapsibleGroup` inside another item — flatten when possible.
167
+ 4. **Concise Titles**: Keep titles short so the row stays scannable.
168
+
169
+ ## Accessibility
170
+
171
+ - Built on `TelaCollapsible` / reka-ui Collapsible primitives.
172
+ - Proper ARIA attributes (`aria-expanded`, `aria-controls`).
173
+ - Keyboard navigation (Space/Enter to toggle).
174
+ - Focus management and screen reader support.