srcdev-nuxt-components 9.1.43 → 9.1.45

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 (22) hide show
  1. package/.claude/skills/components/display-avatar.md +187 -0
  2. package/.claude/skills/components/display-chip.md +213 -0
  3. package/.claude/skills/components/display-pill.md +162 -0
  4. package/.claude/skills/index.md +3 -0
  5. package/app/components/01.atoms/display-avatar/DisplayAvatar.vue +130 -0
  6. package/app/components/{display-avatar → 01.atoms/display-avatar}/stories/DisplayAvatar.stories.ts +13 -1
  7. package/app/components/01.atoms/display-avatar/tests/DisplayAvatar.spec.ts +208 -0
  8. package/app/components/01.atoms/display-avatar/tests/__snapshots__/DisplayAvatar.spec.ts.snap +11 -0
  9. package/app/components/01.atoms/display-pill/DisplayPill.vue +134 -0
  10. package/app/components/01.atoms/display-pill/stories/DisplayPill.stories.ts +88 -0
  11. package/app/components/01.atoms/display-pill/tests/DisplayPill.spec.ts +159 -0
  12. package/app/components/01.atoms/display-pill/tests/__snapshots__/DisplayPill.spec.ts.snap +7 -0
  13. package/app/components/02.molecules/display-chip/DisplayChip.vue +189 -0
  14. package/app/components/{display-chip → 02.molecules/display-chip}/stories/DisplayChip.stories.ts +38 -54
  15. package/app/components/02.molecules/display-chip/tests/DisplayChip.spec.ts +191 -0
  16. package/app/components/02.molecules/display-chip/tests/__snapshots__/DisplayChip.spec.ts.snap +12 -0
  17. package/app/layouts/default.vue +1 -0
  18. package/app/pages/ui/display-chip.vue +201 -66
  19. package/app/pages/ui/display-pill.vue +402 -0
  20. package/package.json +1 -1
  21. package/app/components/display-avatar/DisplayAvatar.vue +0 -148
  22. package/app/components/display-chip/DisplayChip.vue +0 -187
@@ -0,0 +1,402 @@
1
+ <template>
2
+ <div>
3
+ <NuxtLayout name="default">
4
+ <template #layout-content>
5
+ <LayoutRow tag="div" variant="popout">
6
+ <h1 class="page-heading-2">Display Pill</h1>
7
+
8
+ <!-- ── QA Panel (dev only) ───────────────────────────────── -->
9
+ <div v-if="isDev" class="qa-panel">
10
+ <details class="qa-panel__details" open>
11
+ <summary class="qa-panel__summary">
12
+ <span class="qa-panel__title">QA — DisplayPill</span>
13
+ <code class="qa-panel__status">
14
+ variant:{{ qaVariant }} · size:{{ qaSize }} · reversed:{{ qaReversed }}
15
+ </code>
16
+ </summary>
17
+ <div class="qa-panel__body">
18
+
19
+ <div class="qa-panel__group">
20
+ <span class="qa-panel__label">Variant</span>
21
+ <div class="qa-panel__chips">
22
+ <button
23
+ v-for="v in variants"
24
+ :key="v"
25
+ class="qa-panel__chip"
26
+ :class="{ 'is-active': qaVariant === v }"
27
+ @click="qaVariant = v"
28
+ >{{ v }}</button>
29
+ </div>
30
+ </div>
31
+
32
+ <div class="qa-panel__group">
33
+ <span class="qa-panel__label">Size</span>
34
+ <div class="qa-panel__chips">
35
+ <button
36
+ v-for="s in sizes"
37
+ :key="s"
38
+ class="qa-panel__chip"
39
+ :class="{ 'is-active': qaSize === s }"
40
+ @click="qaSize = s"
41
+ >{{ s }}</button>
42
+ </div>
43
+ </div>
44
+
45
+ <div class="qa-panel__group">
46
+ <span class="qa-panel__label">Reversed</span>
47
+ <div class="qa-panel__chips">
48
+ <button
49
+ class="qa-panel__chip"
50
+ :class="{ 'is-active': !qaReversed }"
51
+ @click="qaReversed = false"
52
+ >icon → text</button>
53
+ <button
54
+ class="qa-panel__chip"
55
+ :class="{ 'is-active': qaReversed }"
56
+ @click="qaReversed = true"
57
+ >text → icon</button>
58
+ </div>
59
+ </div>
60
+
61
+ <div class="qa-panel__group">
62
+ <span class="qa-panel__label">Tag</span>
63
+ <div class="qa-panel__chips">
64
+ <button
65
+ v-for="t in tags"
66
+ :key="t"
67
+ class="qa-panel__chip"
68
+ :class="{ 'is-active': qaTag === t }"
69
+ @click="qaTag = t"
70
+ >&lt;{{ t }}&gt;</button>
71
+ </div>
72
+ </div>
73
+
74
+ </div>
75
+ </details>
76
+ </div>
77
+
78
+ <!-- ── Demos ─────────────────────────────────────────────── -->
79
+ <section>
80
+ <h2 class="page-heading-3">Interactive preview</h2>
81
+ <div class="demo-row">
82
+ <DisplayPill
83
+ :tag="qaTag"
84
+ label="Label text"
85
+ :variant="qaVariant"
86
+ :size="qaSize"
87
+ :reversed="qaReversed"
88
+ >
89
+ <template #icon>
90
+ <Icon name="material-symbols:star-outline-rounded" class="demo-icon" aria-hidden="true" />
91
+ </template>
92
+ </DisplayPill>
93
+ </div>
94
+
95
+ <h2 class="page-heading-3">All variants</h2>
96
+ <div class="dl">
97
+ <template v-for="v in variants" :key="v">
98
+ <div class="dt">{{ v }}</div>
99
+ <div class="dd">
100
+ <DisplayPill :variant="v" label="Status" :size="qaSize" :reversed="qaReversed">
101
+ <template #icon>
102
+ <Icon name="material-symbols:circle" class="demo-icon" aria-hidden="true" />
103
+ </template>
104
+ </DisplayPill>
105
+ </div>
106
+ </template>
107
+ </div>
108
+
109
+ <h2 class="page-heading-3">All sizes</h2>
110
+ <div class="dl">
111
+ <template v-for="s in sizes" :key="s">
112
+ <div class="dt">{{ s }}</div>
113
+ <div class="dd">
114
+ <DisplayPill :size="s" :variant="qaVariant" label="Label" :reversed="qaReversed">
115
+ <template #icon>
116
+ <Icon name="material-symbols:bolt-rounded" class="demo-icon" aria-hidden="true" />
117
+ </template>
118
+ </DisplayPill>
119
+ </div>
120
+ </template>
121
+ </div>
122
+
123
+ <h2 class="page-heading-3">Border &amp; outline</h2>
124
+ <div class="dl">
125
+ <div class="dt">Solid border</div>
126
+ <div class="dd">
127
+ <DisplayPill
128
+ label="Bordered"
129
+ :variant="qaVariant"
130
+ :size="qaSize"
131
+ style-class-passthrough="demo-bordered"
132
+ >
133
+ <template #icon>
134
+ <Icon name="material-symbols:circle-outline" class="demo-icon" aria-hidden="true" />
135
+ </template>
136
+ </DisplayPill>
137
+ </div>
138
+
139
+ <div class="dt">Dashed border</div>
140
+ <div class="dd">
141
+ <DisplayPill
142
+ label="Dashed"
143
+ :variant="qaVariant"
144
+ :size="qaSize"
145
+ style-class-passthrough="demo-dashed"
146
+ >
147
+ <template #icon>
148
+ <Icon name="material-symbols:circle-outline" class="demo-icon" aria-hidden="true" />
149
+ </template>
150
+ </DisplayPill>
151
+ </div>
152
+
153
+ <div class="dt">Outline (focus-ring style)</div>
154
+ <div class="dd">
155
+ <DisplayPill
156
+ label="Outlined"
157
+ :variant="qaVariant"
158
+ :size="qaSize"
159
+ style-class-passthrough="demo-outlined"
160
+ >
161
+ <template #icon>
162
+ <Icon name="material-symbols:circle-outline" class="demo-icon" aria-hidden="true" />
163
+ </template>
164
+ </DisplayPill>
165
+ </div>
166
+
167
+ <div class="dt">Border + outline</div>
168
+ <div class="dd">
169
+ <DisplayPill
170
+ label="Both"
171
+ :variant="qaVariant"
172
+ :size="qaSize"
173
+ style-class-passthrough="demo-border-and-outline"
174
+ >
175
+ <template #icon>
176
+ <Icon name="material-symbols:circle-outline" class="demo-icon" aria-hidden="true" />
177
+ </template>
178
+ </DisplayPill>
179
+ </div>
180
+ </div>
181
+
182
+ <h2 class="page-heading-3">Icon only / label only</h2>
183
+ <div class="dl">
184
+ <div class="dt">Icon + label (default)</div>
185
+ <div class="dd">
186
+ <DisplayPill :variant="qaVariant" label="With icon" :size="qaSize">
187
+ <template #icon>
188
+ <Icon name="material-symbols:check-circle-outline-rounded" class="demo-icon" aria-hidden="true" />
189
+ </template>
190
+ </DisplayPill>
191
+ </div>
192
+
193
+ <div class="dt">Label only (no icon)</div>
194
+ <div class="dd">
195
+ <DisplayPill :variant="qaVariant" label="No icon" :size="qaSize" />
196
+ </div>
197
+
198
+ <div class="dt">Icon only (no label)</div>
199
+ <div class="dd">
200
+ <DisplayPill :variant="qaVariant" :size="qaSize" aria-label="Status icon">
201
+ <template #icon>
202
+ <Icon name="material-symbols:check-circle-outline-rounded" class="demo-icon" aria-hidden="true" />
203
+ </template>
204
+ </DisplayPill>
205
+ </div>
206
+
207
+ <div class="dt">Default slot (custom content)</div>
208
+ <div class="dd">
209
+ <DisplayPill :variant="qaVariant" :size="qaSize">
210
+ <template #icon>
211
+ <Icon name="material-symbols:info-outline-rounded" class="demo-icon" aria-hidden="true" />
212
+ </template>
213
+ <strong>Custom</strong>&nbsp;slot
214
+ </DisplayPill>
215
+ </div>
216
+ </div>
217
+ </section>
218
+ </LayoutRow>
219
+ </template>
220
+ </NuxtLayout>
221
+ </div>
222
+ </template>
223
+
224
+ <script setup lang="ts">
225
+ definePageMeta({
226
+ layout: false,
227
+ });
228
+
229
+ useHead({
230
+ title: "UI Display Pill",
231
+ meta: [{ name: "description", content: "Examples of UI Display Pill" }],
232
+ bodyAttrs: {
233
+ class: "ui-display-pill-page",
234
+ },
235
+ });
236
+
237
+ const isDev = import.meta.dev;
238
+
239
+ const variants = ["default", "primary", "success", "warning", "danger", "neutral"] as const;
240
+ const sizes = ["sm", "md", "lg"] as const;
241
+ const tags = ["span", "div", "button", "a"] as const;
242
+
243
+ type Variant = (typeof variants)[number];
244
+ type Size = (typeof sizes)[number];
245
+ type Tag = (typeof tags)[number];
246
+
247
+ const qaVariant = ref<Variant>("default");
248
+ const qaSize = ref<Size>("md");
249
+ const qaReversed = ref(false);
250
+ const qaTag = ref<Tag>("span");
251
+ </script>
252
+
253
+ <style lang="css">
254
+ .ui-display-pill-page {
255
+ .qa-panel {
256
+ background: oklch(15% 0 0);
257
+ color: white;
258
+ font-size: 1.3rem;
259
+ margin-block: 2rem;
260
+ }
261
+
262
+ .qa-panel__details {
263
+ padding: 1rem 2rem;
264
+ }
265
+
266
+ .qa-panel__summary {
267
+ cursor: pointer;
268
+ display: flex;
269
+ align-items: center;
270
+ gap: 1.6rem;
271
+ list-style: none;
272
+ user-select: none;
273
+
274
+ &::-webkit-details-marker { display: none; }
275
+ }
276
+
277
+ .qa-panel__title {
278
+ font-weight: 600;
279
+ font-size: 1.1rem;
280
+ text-transform: uppercase;
281
+ letter-spacing: 0.08em;
282
+ }
283
+
284
+ .qa-panel__status {
285
+ font-family: monospace;
286
+ font-size: 1.2rem;
287
+ background: oklch(0% 0 0 / 0.3);
288
+ padding: 0.2rem 0.8rem;
289
+ border-radius: 0.4rem;
290
+ user-select: text;
291
+ cursor: text;
292
+ }
293
+
294
+ .qa-panel__body {
295
+ display: flex;
296
+ flex-wrap: wrap;
297
+ gap: 2.4rem;
298
+ padding-block: 1.2rem 0.4rem;
299
+ }
300
+
301
+ .qa-panel__group {
302
+ display: flex;
303
+ flex-direction: column;
304
+ gap: 0.6rem;
305
+ }
306
+
307
+ .qa-panel__label {
308
+ font-size: 1.1rem;
309
+ text-transform: uppercase;
310
+ letter-spacing: 0.08em;
311
+ opacity: 0.55;
312
+ }
313
+
314
+ .qa-panel__chips {
315
+ display: flex;
316
+ flex-wrap: wrap;
317
+ gap: 0.4rem;
318
+ }
319
+
320
+ .qa-panel__chip {
321
+ font-family: monospace;
322
+ font-size: 1.2rem;
323
+ color: white;
324
+ background: oklch(0% 0 0 / 0.25);
325
+ border: 1px solid oklch(100% 0 0 / 0.18);
326
+ padding: 0.3rem 1rem;
327
+ border-radius: 0.4rem;
328
+ cursor: pointer;
329
+ transition: background 0.15s;
330
+
331
+ &:hover { background: oklch(0% 0 0 / 0.4); }
332
+
333
+ &.is-active {
334
+ background: oklch(55% 0.18 240);
335
+ border-color: oklch(55% 0.18 240);
336
+ }
337
+ }
338
+
339
+ /* ── Demo sections ─────────────────────────────────────────────── */
340
+
341
+ section {
342
+ margin-top: 2rem;
343
+
344
+ .demo-row {
345
+ display: flex;
346
+ align-items: center;
347
+ gap: 1.6rem;
348
+ flex-wrap: wrap;
349
+ padding-block: 2rem;
350
+ }
351
+
352
+ .dl {
353
+ display: grid;
354
+ grid-template-columns: auto auto;
355
+ gap: 1.6rem;
356
+ align-items: center;
357
+ justify-content: start;
358
+ margin-block: 1.6rem 2.4rem;
359
+
360
+ .dt {
361
+ font-weight: bold;
362
+ font-size: 1.3rem;
363
+ }
364
+
365
+ .dd {
366
+ margin: 0;
367
+ }
368
+ }
369
+
370
+ .demo-icon {
371
+ width: 1em;
372
+ height: 1em;
373
+ font-size: var(--theme-pill-icon-size, 1.4rem);
374
+ }
375
+
376
+ /* ── Border / outline demo overrides ──────────────────────────── */
377
+
378
+ .demo-bordered.display-pill {
379
+ --theme-pill-border-color: currentColor;
380
+ --theme-pill-border-width: 1.5px;
381
+ }
382
+
383
+ .demo-dashed.display-pill {
384
+ --theme-pill-border-color: currentColor;
385
+ --theme-pill-border-width: 1.5px;
386
+ --theme-pill-border-style: dashed;
387
+ }
388
+
389
+ .demo-outlined.display-pill {
390
+ --theme-pill-outline: 2px solid currentColor;
391
+ --theme-pill-outline-offset: 3px;
392
+ }
393
+
394
+ .demo-border-and-outline.display-pill {
395
+ --theme-pill-border-color: currentColor;
396
+ --theme-pill-border-width: 1.5px;
397
+ --theme-pill-outline: 2px solid currentColor;
398
+ --theme-pill-outline-offset: 3px;
399
+ }
400
+ }
401
+ }
402
+ </style>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "srcdev-nuxt-components",
3
3
  "type": "module",
4
- "version": "9.1.43",
4
+ "version": "9.1.45",
5
5
  "main": "nuxt.config.ts",
6
6
  "types": "types.d.ts",
7
7
  "license": "MIT",
@@ -1,148 +0,0 @@
1
- <template>
2
- <component
3
- :is="props.chip ? DisplayChip : as"
4
- v-bind="props.chip ? (typeof props.chip === 'object' ? { config: props.chip } : { config: chipDefaultConfig }) : {}"
5
- class="display-avatar"
6
- :class="[size, elementClasses]"
7
- :style-class-passthrough="elementClasses"
8
- >
9
- <slot name="default">
10
- <NuxtImg v-if="src" :src :alt="alt || 'Avatar'" width="100%" height="100%" class="avatar-image" />
11
- <span v-else>{{ fallback }}</span>
12
- </slot>
13
- <slot name="icon"></slot>
14
- </component>
15
- </template>
16
-
17
- <script setup lang="ts">
18
- import DisplayChip from "../display-chip/DisplayChip.vue";
19
- import type { DisplayChipProps } from "../../types/components";
20
-
21
- export interface AvatarSlots {
22
- default(props?: {}): any;
23
- icon(props?: {}): any;
24
- }
25
-
26
- const props = defineProps({
27
- as: {
28
- type: [String, Object] as PropType<any>,
29
- default: "span",
30
- },
31
- src: {
32
- type: String,
33
- default: undefined,
34
- },
35
- alt: {
36
- type: String,
37
- default: undefined,
38
- },
39
- text: {
40
- type: String,
41
- default: undefined,
42
- },
43
- size: {
44
- type: String as PropType<"xs" | "s" | "md" | "lg" | "xl" | string>,
45
- default: "md",
46
- },
47
- chip: {
48
- type: [Boolean, Object] as PropType<boolean | DisplayChipProps>,
49
- default: undefined,
50
- },
51
- class: {
52
- type: [String, Array, Object] as PropType<any>,
53
- default: undefined,
54
- },
55
- style: {
56
- type: [String, Array, Object] as PropType<any>,
57
- default: undefined,
58
- },
59
- styleClassPassthrough: {
60
- type: [String, Array] as PropType<string | string[]>,
61
- default: () => [],
62
- },
63
- });
64
-
65
- defineSlots<AvatarSlots>();
66
-
67
- const { elementClasses, resetElementClasses, updateElementClasses } = useStyleClassPassthrough(
68
- props.styleClassPassthrough
69
- );
70
-
71
- if (props.chip && typeof props.chip === "object" && !("styleClassPassthrough" in props.chip)) {
72
- updateElementClasses(["display-avatar", props.size]);
73
- }
74
-
75
- const fallback = computed(
76
- () =>
77
- props.text ||
78
- (props.alt || "")
79
- .split(" ")
80
- .map((word) => word.charAt(0))
81
- .join("")
82
- .substring(0, 2)
83
- );
84
-
85
- const chipDefaultConfig = {
86
- size: "12px",
87
- maskWidth: "4px",
88
- offset: "0px",
89
- angle: "90deg",
90
- };
91
-
92
- watch(
93
- () => props.styleClassPassthrough,
94
- () => {
95
- resetElementClasses(props.styleClassPassthrough);
96
- }
97
- );
98
- </script>
99
-
100
- <style lang="css">
101
- @layer components {
102
- .display-avatar {
103
- display: flex;
104
- align-items: center;
105
- justify-content: center;
106
- border-radius: 50%;
107
- color: var(--slate-03);
108
-
109
- isolation: isolate;
110
-
111
- &.xs {
112
- width: 24px;
113
- height: 24px;
114
- font-size: 0.75rem;
115
- }
116
- &.s {
117
- width: 32px;
118
- height: 32px;
119
- font-size: 0.875rem;
120
- }
121
- &.md {
122
- width: 40px;
123
- height: 40px;
124
- font-size: 1rem;
125
- }
126
- &.lg {
127
- width: 48px;
128
- height: 48px;
129
- font-size: 1.125rem;
130
- }
131
- &.xl {
132
- width: 56px;
133
- height: 56px;
134
- font-size: 1.25rem;
135
- }
136
-
137
- .avatar-image {
138
- width: 100%;
139
- border-radius: 50%;
140
- object-fit: cover;
141
- }
142
-
143
- .avatar-icon {
144
- font-size: 24px;
145
- }
146
- }
147
- }
148
- </style>