@meistrari/tela-build 1.48.0 → 1.49.1
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/components/tela/avatar/label/avatar-label.mdx +93 -0
- package/components/tela/avatar/label/avatar-label.stories.ts +75 -0
- package/components/tela/avatar/label/avatar-label.vue +28 -0
- package/components/tela/card/card.vue +2 -2
- package/components/tela/collapsible-group/collapsible-group-item.vue +47 -0
- package/components/tela/collapsible-group/collapsible-group.mdx +174 -0
- package/components/tela/collapsible-group/collapsible-group.stories.ts +134 -0
- package/components/tela/collapsible-group/collapsible-group.vue +5 -0
- package/components/tela/details/details-content-columns.vue +5 -0
- package/components/tela/details/details-content.vue +10 -0
- package/components/tela/details/details-footer.vue +5 -0
- package/components/tela/details/details-review-confirm.vue +42 -0
- package/components/tela/details/details-summary-label.vue +16 -0
- package/components/tela/details/details-summary.vue +5 -0
- package/components/tela/details/details-title.vue +17 -0
- package/components/tela/details/details.mdx +475 -0
- package/components/tela/details/details.vue +5 -0
- package/components/tela/details/hero/hero-aside.vue +5 -0
- package/components/tela/details/hero/hero-identity.vue +8 -0
- package/components/tela/details/hero/hero-main.vue +5 -0
- package/components/tela/details/hero/hero-metric-group.vue +13 -0
- package/components/tela/details/hero/hero-metric.vue +16 -0
- package/components/tela/details/hero/hero-metrics-card.vue +7 -0
- package/components/tela/details/hero/hero-stat.vue +16 -0
- package/components/tela/details/hero/hero-stats.vue +5 -0
- package/components/tela/details/hero/hero.vue +9 -0
- package/components/tela/details/left/left-header.vue +5 -0
- package/components/tela/details/left/left.vue +20 -0
- package/components/tela/details/right/right.vue +13 -0
- package/components/tela/details/use-details-scroll.ts +91 -0
- package/components/tela/dot-separator.vue +3 -0
- package/components/tela/header/header.vue +1 -1
- package/components/tela/header/leading/summary/summary-status.vue +1 -1
- package/components/tela/home/home.mdx +6 -5
- package/components/tela/home/metrics/metrics-card.vue +5 -3
- package/components/tela/home/metrics/metrics.vue +27 -2
- package/components/tela/preview/preview-content.vue +2 -2
- package/components/tela/preview/preview-floating-bar.vue +1 -1
- package/components/tela/segment-bar/segment-bar.mdx +57 -0
- package/components/tela/segment-bar/segment-bar.stories.ts +115 -0
- package/components/tela/segment-bar/segment-bar.vue +88 -0
- package/components/tela/status/status.mdx +23 -1
- package/components/tela/status/status.stories.ts +40 -0
- package/components/tela/status/status.vue +22 -3
- package/components/tela/tabs/tabs-indicator.vue +1 -1
- package/components/tela/tabs/tabs-trigger.vue +1 -1
- package/package.json +1 -1
- 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>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { HTMLAttributes } from 'vue'
|
|
3
3
|
|
|
4
|
-
const props = withDefaults(defineProps
|
|
5
|
-
size?: 'xs' | 'sm'
|
|
4
|
+
const props = withDefaults(defineProps<{
|
|
5
|
+
size?: 'xs' | 'sm' | 'md'
|
|
6
6
|
class?: HTMLAttributes['class']
|
|
7
7
|
/** @deprecated Use `class` instead */
|
|
8
8
|
contentPadding?: HTMLAttributes['class']
|
|
@@ -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.
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/vue3'
|
|
2
|
+
|
|
3
|
+
import CollapsibleGroup from './collapsible-group.vue'
|
|
4
|
+
import CollapsibleGroupItem from './collapsible-group-item.vue'
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof CollapsibleGroupItem> = {
|
|
7
|
+
title: 'Core/CollapsibleGroup',
|
|
8
|
+
component: CollapsibleGroupItem,
|
|
9
|
+
parameters: {
|
|
10
|
+
layout: 'centered',
|
|
11
|
+
docs: {
|
|
12
|
+
description: {
|
|
13
|
+
component: 'A grouped collapsible component for stacking multiple expandable items in a single bordered container. Each item displays a title, an optional item count with customizable singular/plural labels, and a slot for arbitrary content. Useful for accordion-style sections, document outlines, and grouped detail panels.',
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
argTypes: {
|
|
18
|
+
title: {
|
|
19
|
+
control: 'text',
|
|
20
|
+
description: 'Title text displayed in the collapsible item header.',
|
|
21
|
+
},
|
|
22
|
+
itemsLength: {
|
|
23
|
+
control: 'number',
|
|
24
|
+
description: 'Optional number shown next to the title (e.g. "4 itens"). Hidden when falsy.',
|
|
25
|
+
},
|
|
26
|
+
singularLabel: {
|
|
27
|
+
control: 'text',
|
|
28
|
+
description: 'Label used when `itemsLength === 1`. Defaults to `item`.',
|
|
29
|
+
},
|
|
30
|
+
pluralLabel: {
|
|
31
|
+
control: 'text',
|
|
32
|
+
description: 'Label used when `itemsLength !== 1`. Defaults to `itens`.',
|
|
33
|
+
},
|
|
34
|
+
open: {
|
|
35
|
+
control: 'boolean',
|
|
36
|
+
description: 'Controls the open state of the item (v-model:open).',
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export default meta
|
|
42
|
+
|
|
43
|
+
type Story = StoryObj<typeof meta>
|
|
44
|
+
|
|
45
|
+
export const Default: Story = {
|
|
46
|
+
render: () => ({
|
|
47
|
+
components: { CollapsibleGroup, CollapsibleGroupItem },
|
|
48
|
+
template: `
|
|
49
|
+
<div style="width: 600px;">
|
|
50
|
+
<CollapsibleGroup>
|
|
51
|
+
<CollapsibleGroupItem title="Item 1" :items-length="4">
|
|
52
|
+
<p class="text-body-16-regular">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
|
|
53
|
+
</CollapsibleGroupItem>
|
|
54
|
+
<CollapsibleGroupItem title="Item 2" :items-length="2">
|
|
55
|
+
<p class="text-body-16-regular">Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
|
|
56
|
+
</CollapsibleGroupItem>
|
|
57
|
+
<CollapsibleGroupItem title="Item 3" :items-length="1">
|
|
58
|
+
<p class="text-body-16-regular">Ut enim ad minim veniam, quis nostrud exercitation ullamco.</p>
|
|
59
|
+
</CollapsibleGroupItem>
|
|
60
|
+
</CollapsibleGroup>
|
|
61
|
+
</div>
|
|
62
|
+
`,
|
|
63
|
+
}),
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export const WithoutCount: Story = {
|
|
67
|
+
render: () => ({
|
|
68
|
+
components: { CollapsibleGroup, CollapsibleGroupItem },
|
|
69
|
+
template: `
|
|
70
|
+
<div style="width: 600px;">
|
|
71
|
+
<CollapsibleGroup>
|
|
72
|
+
<CollapsibleGroupItem title="Overview">
|
|
73
|
+
<p class="text-body-16-regular">No item count is shown when itemsLength is omitted.</p>
|
|
74
|
+
</CollapsibleGroupItem>
|
|
75
|
+
<CollapsibleGroupItem title="Details">
|
|
76
|
+
<p class="text-body-16-regular">Just title and content.</p>
|
|
77
|
+
</CollapsibleGroupItem>
|
|
78
|
+
</CollapsibleGroup>
|
|
79
|
+
</div>
|
|
80
|
+
`,
|
|
81
|
+
}),
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export const CustomLabels: Story = {
|
|
85
|
+
render: () => ({
|
|
86
|
+
components: { CollapsibleGroup, CollapsibleGroupItem },
|
|
87
|
+
template: `
|
|
88
|
+
<div style="width: 600px;">
|
|
89
|
+
<CollapsibleGroup>
|
|
90
|
+
<CollapsibleGroupItem
|
|
91
|
+
title="Documents"
|
|
92
|
+
:items-length="1"
|
|
93
|
+
singular-label="file"
|
|
94
|
+
plural-label="files"
|
|
95
|
+
>
|
|
96
|
+
<p class="text-body-16-regular">A single file.</p>
|
|
97
|
+
</CollapsibleGroupItem>
|
|
98
|
+
<CollapsibleGroupItem
|
|
99
|
+
title="Reports"
|
|
100
|
+
:items-length="12"
|
|
101
|
+
singular-label="file"
|
|
102
|
+
plural-label="files"
|
|
103
|
+
>
|
|
104
|
+
<p class="text-body-16-regular">Twelve files inside.</p>
|
|
105
|
+
</CollapsibleGroupItem>
|
|
106
|
+
</CollapsibleGroup>
|
|
107
|
+
</div>
|
|
108
|
+
`,
|
|
109
|
+
}),
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export const SingleItem: Story = {
|
|
113
|
+
render: args => ({
|
|
114
|
+
components: { CollapsibleGroup, CollapsibleGroupItem },
|
|
115
|
+
setup() {
|
|
116
|
+
return { args }
|
|
117
|
+
},
|
|
118
|
+
template: `
|
|
119
|
+
<div style="width: 600px;">
|
|
120
|
+
<CollapsibleGroup>
|
|
121
|
+
<CollapsibleGroupItem v-bind="args">
|
|
122
|
+
<p class="text-body-16-regular">Standalone item content.</p>
|
|
123
|
+
</CollapsibleGroupItem>
|
|
124
|
+
</CollapsibleGroup>
|
|
125
|
+
</div>
|
|
126
|
+
`,
|
|
127
|
+
}),
|
|
128
|
+
args: {
|
|
129
|
+
title: 'Standalone Item',
|
|
130
|
+
itemsLength: 3,
|
|
131
|
+
singularLabel: 'item',
|
|
132
|
+
pluralLabel: 'itens',
|
|
133
|
+
},
|
|
134
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
interface Props {
|
|
3
|
+
reviewLabel: string
|
|
4
|
+
actionLabel: string
|
|
5
|
+
actionDisabled?: boolean
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const props = defineProps<Props>()
|
|
9
|
+
|
|
10
|
+
const emit = defineEmits<{
|
|
11
|
+
confirm: []
|
|
12
|
+
}>()
|
|
13
|
+
|
|
14
|
+
const checked = defineModel<boolean>('checked', { default: false })
|
|
15
|
+
|
|
16
|
+
const isActionDisabled = computed(() => !checked.value || Boolean(props.actionDisabled))
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<template>
|
|
20
|
+
<div
|
|
21
|
+
flex="~ col"
|
|
22
|
+
gap-20px
|
|
23
|
+
max-w-640px
|
|
24
|
+
mx-auto
|
|
25
|
+
w-full
|
|
26
|
+
>
|
|
27
|
+
<label flex items-center gap-12px>
|
|
28
|
+
<TelaCheckbox id="review-check" v-model="checked" />
|
|
29
|
+
<span heading-h5-semibold text-primary cursor-pointer select-none>{{ reviewLabel }}</span>
|
|
30
|
+
</label>
|
|
31
|
+
|
|
32
|
+
<TelaButton
|
|
33
|
+
variant="primary"
|
|
34
|
+
size="md"
|
|
35
|
+
w-full
|
|
36
|
+
:disabled="isActionDisabled"
|
|
37
|
+
@click="emit('confirm')"
|
|
38
|
+
>
|
|
39
|
+
{{ actionLabel }}
|
|
40
|
+
</TelaButton>
|
|
41
|
+
</div>
|
|
42
|
+
</template>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
const props = withDefaults(defineProps<{ variant?: 'default' | 'contrast' }>(), {
|
|
3
|
+
variant: 'default',
|
|
4
|
+
})
|
|
5
|
+
|
|
6
|
+
const variantClass = computed(() => ({
|
|
7
|
+
default: 'body-12-regular text-secondary',
|
|
8
|
+
contrast: 'body-12-semibold text-primary',
|
|
9
|
+
}[props.variant]))
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<template>
|
|
13
|
+
<p :class="variantClass">
|
|
14
|
+
<slot />
|
|
15
|
+
</p>
|
|
16
|
+
</template>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
const props = withDefaults(defineProps<{ size?: 'sm' | 'md' | 'lg' }>(), {
|
|
3
|
+
size: 'lg',
|
|
4
|
+
})
|
|
5
|
+
|
|
6
|
+
const sizeClass = computed(() => ({
|
|
7
|
+
lg: 'heading-h2-semibold',
|
|
8
|
+
md: 'heading-h3-semibold',
|
|
9
|
+
sm: 'heading-h5-semibold',
|
|
10
|
+
}[props.size]))
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<template>
|
|
14
|
+
<h2 :class="sizeClass" text-primary>
|
|
15
|
+
<slot />
|
|
16
|
+
</h2>
|
|
17
|
+
</template>
|