srcdev-nuxt-components 9.4.7 → 9.4.8
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/.claude/component-ledger/audit.json +1 -1
- package/.claude/component-ledger/output.html +1 -1
- package/.claude/skills/component-dynamic-slots.md +1 -1
- package/.claude/skills/components/header-block.md +92 -0
- package/.claude/skills/components/rotating-carousel-image.md +101 -0
- package/.claude/skills/components/skip-links.md +82 -0
- package/.claude/skills/components/tabs-core.md +187 -0
- package/.claude/skills/index.md +4 -0
- package/.vscode/srcdev-component-header-block.code-snippets +41 -0
- package/.vscode/srcdev-component-rotating-carousel-image.code-snippets +74 -0
- package/.vscode/srcdev-component-skip-links.code-snippets +55 -0
- package/.vscode/srcdev-component-tabs-core.code-snippets +78 -0
- package/app/assets/styles/setup/05.typography/02.utility-classes/_font-classes-page-heading.css +2 -1
- package/app/components/01.atoms/animations/rotating-carousel-image/CONSUMER-STYLING.md +54 -0
- package/app/components/01.atoms/animations/rotating-carousel-image/RotatingCarouselImage.vue +315 -0
- package/app/components/01.atoms/animations/rotating-carousel-image/stories/RotatingCarouselImage.stories.ts +156 -0
- package/app/components/01.atoms/animations/rotating-carousel-image/tests/RotatingCarouselImage.spec.ts +230 -0
- package/app/components/01.atoms/animations/rotating-carousel-image/tests/__snapshots__/RotatingCarouselImage.spec.ts.snap +19 -0
- package/app/components/01.atoms/navigation/skip-links/CONSUMER-STYLING.md +31 -0
- package/app/components/01.atoms/navigation/skip-links/SkipLinks.vue +92 -0
- package/app/components/01.atoms/navigation/skip-links/stories/SkipLinks.stories.ts +113 -0
- package/app/components/01.atoms/navigation/skip-links/tests/SkipLinks.spec.ts +71 -0
- package/app/components/01.atoms/navigation/skip-links/tests/__snapshots__/SkipLinks.spec.ts.snap +15 -0
- package/app/components/01.atoms/navigation/tabs/CONSUMER-STYLING.md +99 -0
- package/app/components/01.atoms/navigation/tabs/TabsCore.vue +272 -0
- package/app/components/01.atoms/navigation/tabs/stories/TabsCore.stories.ts +199 -0
- package/app/components/01.atoms/navigation/tabs/tests/TabsCore.spec.ts +274 -0
- package/app/components/01.atoms/text-blocks/header-block/CONSUMER-STYLING.md +43 -0
- package/app/components/01.atoms/text-blocks/header-block/HeaderBlock.vue +50 -0
- package/app/components/01.atoms/text-blocks/header-block/stories/HeaderBlock.stories.ts +111 -0
- package/app/components/01.atoms/text-blocks/header-block/tests/HeaderBlock.spec.ts +119 -0
- package/app/components/01.atoms/text-blocks/header-block/tests/__snapshots__/HeaderBlock.spec.ts.snap +5 -0
- package/app/components/03.organisms/site-header/SiteHeader.vue +1 -1
- package/app/components/03.organisms/site-header/tests/SiteHeader.spec.ts +1 -1
- package/app/components/03.organisms/site-header/tests/__snapshots__/SiteHeader.spec.ts.snap +3 -3
- package/app/composables/useTabs.ts +225 -207
- package/app/types/components/index.ts +2 -0
- package/app/types/components/rotating-carousel-image.d.ts +4 -0
- package/app/types/components/skip-links.d.ts +4 -0
- package/package.json +1 -1
- package/app/components/01.atoms/grids/data-grid/tests/__snapshots__/DataGrid.spec.ts.snap +0 -11
- package/app/components/rotating-carousel/RotatingCarouselImage.vue +0 -216
- package/app/components/skip-links/SkipLinks.vue +0 -60
- package/app/components/tabs/TabsCore.vue +0 -306
- package/app/components/typography/HeaderBlock.vue +0 -35
- /package/app/components/01.atoms/grids/{data-grid → auto-grid}/AutoGrid.vue +0 -0
- /package/app/components/01.atoms/grids/{data-grid → auto-grid}/stories/AutoGrid.stories.ts +0 -0
- /package/app/components/01.atoms/grids/{data-grid → auto-grid}/tests/AutoGrid.spec.ts +0 -0
- /package/app/components/01.atoms/grids/{data-grid → auto-grid}/tests/__snapshots__/AutoGrid.spec.ts.snap +0 -0
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="tabs-core" :class="[`axis-${axis}`]">
|
|
3
|
+
<ul
|
|
4
|
+
ref="tabsNavRef"
|
|
5
|
+
role="tablist"
|
|
6
|
+
:aria-label="ariaLabel"
|
|
7
|
+
class="tabs-list"
|
|
8
|
+
:class="[elementClasses]"
|
|
9
|
+
@mouseleave="resetHoverToActivePosition()"
|
|
10
|
+
>
|
|
11
|
+
<li v-for="(index, key) in itemCount" :key="key">
|
|
12
|
+
<button
|
|
13
|
+
:id="`tab-${key}-trigger`"
|
|
14
|
+
:data-tab-index="key"
|
|
15
|
+
data-nav-item
|
|
16
|
+
type="button"
|
|
17
|
+
role="tab"
|
|
18
|
+
aria-selected="false"
|
|
19
|
+
class="tabs-list-item"
|
|
20
|
+
@click.prevent="navItemClicked($event)"
|
|
21
|
+
@mouseenter="navItemHovered($event)"
|
|
22
|
+
@keydown="navItemKeydown($event)"
|
|
23
|
+
>
|
|
24
|
+
<slot :name="`tab-${key}-trigger`"></slot>
|
|
25
|
+
</button>
|
|
26
|
+
</li>
|
|
27
|
+
</ul>
|
|
28
|
+
<div class="tab-content-wrapper">
|
|
29
|
+
<div
|
|
30
|
+
v-for="(item, key) in itemCount"
|
|
31
|
+
:id="`tab-${key}-content`"
|
|
32
|
+
:key="key"
|
|
33
|
+
ref="tabsContentRefs"
|
|
34
|
+
class="tab-content"
|
|
35
|
+
:aria-labelledby="`tab-${key}-trigger`"
|
|
36
|
+
role="tabpanel"
|
|
37
|
+
tabindex="0"
|
|
38
|
+
aria-hidden="true"
|
|
39
|
+
>
|
|
40
|
+
<slot :name="`tab-${key}-content`"></slot>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
</template>
|
|
45
|
+
|
|
46
|
+
<script setup lang="ts">
|
|
47
|
+
interface Props {
|
|
48
|
+
axis?: "x" | "y";
|
|
49
|
+
transitionDuration?: number;
|
|
50
|
+
/** Number of tabs to render — drives both the trigger and content indexed slots. */
|
|
51
|
+
itemCount: number;
|
|
52
|
+
/** Shows a moving highlight behind the hovered tab. */
|
|
53
|
+
trackHover?: boolean;
|
|
54
|
+
/** Shows a moving highlight behind the active tab. */
|
|
55
|
+
trackActive?: boolean;
|
|
56
|
+
/** Shows a moving underline/sideline indicator beneath the active tab. */
|
|
57
|
+
trackIndicator?: boolean;
|
|
58
|
+
/** aria-label on the tablist — override for localisation. */
|
|
59
|
+
ariaLabel?: string;
|
|
60
|
+
styleClassPassthrough?: string | string[];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
64
|
+
axis: "x",
|
|
65
|
+
transitionDuration: 200,
|
|
66
|
+
trackHover: true,
|
|
67
|
+
trackActive: true,
|
|
68
|
+
trackIndicator: true,
|
|
69
|
+
ariaLabel: "Tabs",
|
|
70
|
+
styleClassPassthrough: () => [],
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const { elementClasses } = useStyleClassPassthrough(props.styleClassPassthrough);
|
|
74
|
+
|
|
75
|
+
const tabsNavRef = ref<HTMLElement | null>(null);
|
|
76
|
+
const tabsContentRefs = ref<HTMLElement[] | null>(null);
|
|
77
|
+
|
|
78
|
+
const { initNavDecorators, navItemClicked, navItemHovered, navItemKeydown, resetHoverToActivePosition } = useTabs(
|
|
79
|
+
props.axis,
|
|
80
|
+
tabsNavRef,
|
|
81
|
+
tabsContentRefs,
|
|
82
|
+
props.transitionDuration,
|
|
83
|
+
{ trackHover: props.trackHover, trackActive: props.trackActive, trackIndicator: props.trackIndicator }
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
onMounted(() => {
|
|
87
|
+
initNavDecorators();
|
|
88
|
+
});
|
|
89
|
+
</script>
|
|
90
|
+
|
|
91
|
+
<style lang="css">
|
|
92
|
+
@layer components {
|
|
93
|
+
.tabs-core {
|
|
94
|
+
.tabs-list {
|
|
95
|
+
position: relative;
|
|
96
|
+
display: flex;
|
|
97
|
+
width: fit-content;
|
|
98
|
+
z-index: 1;
|
|
99
|
+
|
|
100
|
+
list-style-type: none;
|
|
101
|
+
margin: 0;
|
|
102
|
+
padding: 0;
|
|
103
|
+
|
|
104
|
+
border-bottom: var(--tabs-nav-border, 0.1rem solid var(--slate-06));
|
|
105
|
+
|
|
106
|
+
.nav__hovered {
|
|
107
|
+
content: "";
|
|
108
|
+
position: absolute;
|
|
109
|
+
left: 0;
|
|
110
|
+
right: 0;
|
|
111
|
+
bottom: 0;
|
|
112
|
+
top: 0;
|
|
113
|
+
scale: var(--_width-hovered, 0.125) 1;
|
|
114
|
+
translate: var(--_x-hovered, 0) 0;
|
|
115
|
+
transform-origin: left;
|
|
116
|
+
transition:
|
|
117
|
+
scale var(--_transition-duration),
|
|
118
|
+
translate var(--_transition-duration);
|
|
119
|
+
z-index: 1;
|
|
120
|
+
background: var(--tabs-hover-indicator-colour, var(--slate-07));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.nav__active {
|
|
124
|
+
content: "";
|
|
125
|
+
position: absolute;
|
|
126
|
+
left: 0;
|
|
127
|
+
right: 0;
|
|
128
|
+
bottom: 0;
|
|
129
|
+
top: 0;
|
|
130
|
+
scale: var(--_width-active, 0.125) 1;
|
|
131
|
+
translate: var(--_x-active, 0) 0;
|
|
132
|
+
transform-origin: left;
|
|
133
|
+
transition:
|
|
134
|
+
scale var(--_transition-duration),
|
|
135
|
+
translate var(--_transition-duration);
|
|
136
|
+
z-index: 2;
|
|
137
|
+
background: var(--tabs-active-indicator-colour, var(--slate-09));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.nav__active-indicator {
|
|
141
|
+
content: "";
|
|
142
|
+
position: absolute;
|
|
143
|
+
left: 0;
|
|
144
|
+
right: 0;
|
|
145
|
+
bottom: 0;
|
|
146
|
+
scale: var(--_width-active, 0.125) 1;
|
|
147
|
+
translate: var(--_x-active, 0) 0;
|
|
148
|
+
transform-origin: left;
|
|
149
|
+
transition:
|
|
150
|
+
scale var(--_transition-duration),
|
|
151
|
+
translate var(--_transition-duration);
|
|
152
|
+
z-index: 3;
|
|
153
|
+
background: var(--tabs-underline-indicator-colour, var(--slate-10));
|
|
154
|
+
height: var(--tabs-underline-indicator-height, 0.4rem);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.tabs-list-item {
|
|
158
|
+
opacity: var(--tabs-list-item-opacity, 0.7);
|
|
159
|
+
position: relative;
|
|
160
|
+
transition: color var(--tabs-list-item-colour-transition-duration, 100ms);
|
|
161
|
+
z-index: 4;
|
|
162
|
+
background: transparent;
|
|
163
|
+
border: 0;
|
|
164
|
+
color: var(--tabs-list-item-colour, var(--slate-10));
|
|
165
|
+
cursor: pointer;
|
|
166
|
+
font: inherit;
|
|
167
|
+
text-transform: var(--tabs-list-item-text-transform, uppercase);
|
|
168
|
+
font-weight: var(--tabs-list-item-font-weight, 500);
|
|
169
|
+
margin: 0;
|
|
170
|
+
padding: var(--tabs-list-item-padding-block, 1em) var(--tabs-list-item-padding-inline, 2em);
|
|
171
|
+
|
|
172
|
+
/* Text colour tracks the indicator actually behind it — hover and active can differ. */
|
|
173
|
+
&:hover {
|
|
174
|
+
opacity: 1;
|
|
175
|
+
color: var(--tabs-hover-indicator-text-colour, var(--slate-00));
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
&[aria-selected="true"] {
|
|
179
|
+
opacity: 1;
|
|
180
|
+
color: var(
|
|
181
|
+
--tabs-list-item-colour-selected,
|
|
182
|
+
var(--tabs-active-indicator-text-colour, var(--slate-00))
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
&:focus-visible {
|
|
187
|
+
outline: var(--tabs-list-item-focus-outline-width, 2px) solid var(--theme-ring);
|
|
188
|
+
outline-offset: var(--tabs-list-item-focus-outline-offset, -2px);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.tab-content-wrapper {
|
|
194
|
+
display: grid;
|
|
195
|
+
grid-template-areas: "element-stack";
|
|
196
|
+
|
|
197
|
+
background-color: var(--tabs-content-background-colour, var(--slate-09));
|
|
198
|
+
border: var(--tabs-content-border-width, 0.1rem) solid var(--tabs-content-border-colour, var(--slate-06));
|
|
199
|
+
border-radius: var(--tabs-content-border-radius, 0);
|
|
200
|
+
outline: var(--tabs-content-border-width, 0.1rem) solid var(--tabs-content-border-colour, var(--slate-06));
|
|
201
|
+
|
|
202
|
+
.tab-content {
|
|
203
|
+
grid-area: element-stack;
|
|
204
|
+
display: none;
|
|
205
|
+
|
|
206
|
+
&:focus-visible {
|
|
207
|
+
outline: var(--tabs-list-item-focus-outline-width, 2px) solid var(--theme-ring);
|
|
208
|
+
outline-offset: calc(-1 * var(--tabs-list-item-focus-outline-width, 2px));
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/*
|
|
214
|
+
* Deal with axis-y
|
|
215
|
+
**/
|
|
216
|
+
|
|
217
|
+
&.axis-y {
|
|
218
|
+
display: flex;
|
|
219
|
+
flex-direction: row;
|
|
220
|
+
gap: var(--tabs-axis-y-gap, 2em);
|
|
221
|
+
|
|
222
|
+
.tabs-list {
|
|
223
|
+
flex-direction: column;
|
|
224
|
+
|
|
225
|
+
border-bottom: initial;
|
|
226
|
+
border-left: var(--tabs-nav-border, 0.1rem solid var(--slate-06));
|
|
227
|
+
position: relative;
|
|
228
|
+
|
|
229
|
+
.tabs-list-item {
|
|
230
|
+
text-align: left;
|
|
231
|
+
width: 100%;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.nav__hovered,
|
|
235
|
+
.nav__active {
|
|
236
|
+
left: 0;
|
|
237
|
+
right: initial;
|
|
238
|
+
bottom: initial;
|
|
239
|
+
top: 0;
|
|
240
|
+
height: var(--_y-height);
|
|
241
|
+
transform-origin: top;
|
|
242
|
+
width: var(--_y-width);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.nav__hovered {
|
|
246
|
+
translate: 0 var(--_y-hovered, 0);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
.nav__active {
|
|
250
|
+
translate: 0 var(--_y-active, 0);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.nav__active-indicator {
|
|
254
|
+
left: 0;
|
|
255
|
+
right: initial;
|
|
256
|
+
bottom: initial;
|
|
257
|
+
top: 0;
|
|
258
|
+
height: var(--_y-height);
|
|
259
|
+
scale: var(--_width-active, 0.125) 1;
|
|
260
|
+
translate: 0 var(--_y-active, 0);
|
|
261
|
+
transform-origin: top;
|
|
262
|
+
width: var(--tabs-underline-indicator-height, 0.4em);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.tab-content-wrapper {
|
|
267
|
+
flex-grow: 1;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
</style>
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import TabsCore from "../TabsCore.vue";
|
|
2
|
+
import type { Meta, StoryObj } from "@nuxtjs/storybook";
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof TabsCore> = {
|
|
5
|
+
title: "Atoms/Navigation/TabsCore",
|
|
6
|
+
component: TabsCore,
|
|
7
|
+
argTypes: {
|
|
8
|
+
axis: {
|
|
9
|
+
control: "radio",
|
|
10
|
+
options: ["x", "y"],
|
|
11
|
+
description: "Layout axis — horizontal tab row or vertical tab column",
|
|
12
|
+
},
|
|
13
|
+
itemCount: {
|
|
14
|
+
control: { type: "number", min: 1, max: 6, step: 1 },
|
|
15
|
+
description: "Number of tabs — drives the tab-{n}-trigger / tab-{n}-content indexed slots",
|
|
16
|
+
},
|
|
17
|
+
transitionDuration: {
|
|
18
|
+
control: { type: "range", min: 0, max: 600, step: 10 },
|
|
19
|
+
description: "Duration (ms) of the moving indicator's transition",
|
|
20
|
+
},
|
|
21
|
+
trackHover: {
|
|
22
|
+
control: "boolean",
|
|
23
|
+
description: "Shows a moving highlight behind the hovered tab",
|
|
24
|
+
},
|
|
25
|
+
trackActive: {
|
|
26
|
+
control: "boolean",
|
|
27
|
+
description: "Shows a moving highlight behind the active tab",
|
|
28
|
+
},
|
|
29
|
+
trackIndicator: {
|
|
30
|
+
control: "boolean",
|
|
31
|
+
description: "Shows a moving underline/sideline indicator beneath the active tab",
|
|
32
|
+
},
|
|
33
|
+
ariaLabel: {
|
|
34
|
+
control: "text",
|
|
35
|
+
description: "aria-label on the tablist — override for localisation",
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
args: {
|
|
39
|
+
axis: "x",
|
|
40
|
+
itemCount: 3,
|
|
41
|
+
transitionDuration: 200,
|
|
42
|
+
trackHover: true,
|
|
43
|
+
trackActive: true,
|
|
44
|
+
trackIndicator: true,
|
|
45
|
+
ariaLabel: "Tabs",
|
|
46
|
+
},
|
|
47
|
+
parameters: {
|
|
48
|
+
docs: {
|
|
49
|
+
description: {
|
|
50
|
+
component:
|
|
51
|
+
"A tablist built from indexed slots (tab-{n}-trigger / tab-{n}-content, driven by itemCount) with moving hover/active/underline indicators. Supports arrow-key navigation (Left/Right or Up/Down depending on axis, plus Home/End) per the WAI-ARIA Tabs pattern.",
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export default meta;
|
|
58
|
+
type Story = StoryObj<typeof TabsCore>;
|
|
59
|
+
|
|
60
|
+
// ─── Fixtures ─────────────────────────────────────────────────────────────────
|
|
61
|
+
|
|
62
|
+
const panels = [
|
|
63
|
+
{ label: "Overview", content: "A short summary of the product goes here." },
|
|
64
|
+
{ label: "Specs", content: "Technical specifications and dimensions." },
|
|
65
|
+
{ label: "Reviews", content: "Customer reviews and ratings." },
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
const tabSlots = panels
|
|
69
|
+
.map(
|
|
70
|
+
(panel, index) => `
|
|
71
|
+
<template #tab-${index}-trigger>${panel.label}</template>
|
|
72
|
+
<template #tab-${index}-content>
|
|
73
|
+
<div style="padding: 1.6rem;">${panel.content}</div>
|
|
74
|
+
</template>`
|
|
75
|
+
)
|
|
76
|
+
.join("\n");
|
|
77
|
+
|
|
78
|
+
const fivePanels = [
|
|
79
|
+
{ label: "Overview", content: "A short summary of the product goes here." },
|
|
80
|
+
{ label: "Specs", content: "Technical specifications and dimensions." },
|
|
81
|
+
{ label: "Reviews", content: "Customer reviews and ratings." },
|
|
82
|
+
{ label: "Shipping", content: "Delivery estimates and carrier options." },
|
|
83
|
+
{ label: "Returns", content: "Return window and refund policy." },
|
|
84
|
+
];
|
|
85
|
+
|
|
86
|
+
const fiveTabSlots = fivePanels
|
|
87
|
+
.map(
|
|
88
|
+
(panel, index) => `
|
|
89
|
+
<template #tab-${index}-trigger>${panel.label}</template>
|
|
90
|
+
<template #tab-${index}-content>
|
|
91
|
+
<div style="padding: 1.6rem;">${panel.content}</div>
|
|
92
|
+
</template>`
|
|
93
|
+
)
|
|
94
|
+
.join("\n");
|
|
95
|
+
|
|
96
|
+
// ─── Stories ──────────────────────────────────────────────────────────────────
|
|
97
|
+
|
|
98
|
+
export const Horizontal: Story = {
|
|
99
|
+
args: { itemCount: panels.length },
|
|
100
|
+
render: (args) => ({
|
|
101
|
+
components: { TabsCore },
|
|
102
|
+
setup() {
|
|
103
|
+
return { args };
|
|
104
|
+
},
|
|
105
|
+
template: `
|
|
106
|
+
<TabsCore v-bind="args">
|
|
107
|
+
${tabSlots}
|
|
108
|
+
</TabsCore>
|
|
109
|
+
`,
|
|
110
|
+
}),
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export const Vertical: Story = {
|
|
114
|
+
args: { itemCount: panels.length, axis: "y" },
|
|
115
|
+
render: (args) => ({
|
|
116
|
+
components: { TabsCore },
|
|
117
|
+
setup() {
|
|
118
|
+
return { args };
|
|
119
|
+
},
|
|
120
|
+
template: `
|
|
121
|
+
<TabsCore v-bind="args">
|
|
122
|
+
${tabSlots}
|
|
123
|
+
</TabsCore>
|
|
124
|
+
`,
|
|
125
|
+
}),
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
export const IndicatorsOff: Story = {
|
|
129
|
+
name: "Indicators Off",
|
|
130
|
+
args: { itemCount: panels.length, trackHover: false, trackActive: false, trackIndicator: false },
|
|
131
|
+
render: (args) => ({
|
|
132
|
+
components: { TabsCore },
|
|
133
|
+
setup() {
|
|
134
|
+
return { args };
|
|
135
|
+
},
|
|
136
|
+
template: `
|
|
137
|
+
<TabsCore v-bind="args">
|
|
138
|
+
${tabSlots}
|
|
139
|
+
</TabsCore>
|
|
140
|
+
`,
|
|
141
|
+
}),
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
export const FiveTabs: Story = {
|
|
145
|
+
name: "Five Tabs (regression check)",
|
|
146
|
+
args: { itemCount: fivePanels.length },
|
|
147
|
+
parameters: {
|
|
148
|
+
docs: {
|
|
149
|
+
description: {
|
|
150
|
+
story:
|
|
151
|
+
"itemCount raised to 5 as a regression check — the original hand-rolled version of this component had an indicator-positioning issue that only showed up with more than 3 tabs. Try hovering quickly across several tabs in a row, then clicking a tab several positions away from the active one (e.g. tab 1 → tab 5), to confirm the hover/active/underline indicators land in the right place without jumping or lagging behind.",
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
render: (args) => ({
|
|
156
|
+
components: { TabsCore },
|
|
157
|
+
setup() {
|
|
158
|
+
return { args };
|
|
159
|
+
},
|
|
160
|
+
template: `
|
|
161
|
+
<TabsCore v-bind="args">
|
|
162
|
+
${fiveTabSlots}
|
|
163
|
+
</TabsCore>
|
|
164
|
+
`,
|
|
165
|
+
}),
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
export const IndicatorColours: Story = {
|
|
169
|
+
name: "Indicator Colours (demo)",
|
|
170
|
+
args: { itemCount: panels.length },
|
|
171
|
+
parameters: {
|
|
172
|
+
docs: {
|
|
173
|
+
description: {
|
|
174
|
+
story:
|
|
175
|
+
"Each moving indicator given a distinct colour via its public CSS token, purely to make the three separate states (hover / active / underline) easy to tell apart. Hover a tab to see the green highlight move independently of the blue active highlight — the label text stays white against both backgrounds via the matching --tabs-hover-indicator-text-colour / --tabs-active-indicator-text-colour tokens (see CONSUMER-STYLING.md), which previously had no effect since they were wired to the wrong element.",
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
render: (args) => ({
|
|
180
|
+
components: { TabsCore },
|
|
181
|
+
setup() {
|
|
182
|
+
const indicatorColourVars = {
|
|
183
|
+
"--tabs-hover-indicator-colour": "seagreen",
|
|
184
|
+
"--tabs-hover-indicator-text-colour": "white",
|
|
185
|
+
"--tabs-active-indicator-colour": "steelblue",
|
|
186
|
+
"--tabs-active-indicator-text-colour": "white",
|
|
187
|
+
"--tabs-underline-indicator-colour": "darkorange",
|
|
188
|
+
};
|
|
189
|
+
return { args, indicatorColourVars };
|
|
190
|
+
},
|
|
191
|
+
template: `
|
|
192
|
+
<div :style="indicatorColourVars">
|
|
193
|
+
<TabsCore v-bind="args">
|
|
194
|
+
${tabSlots}
|
|
195
|
+
</TabsCore>
|
|
196
|
+
</div>
|
|
197
|
+
`,
|
|
198
|
+
}),
|
|
199
|
+
};
|