srcdev-nuxt-components 9.1.29 → 9.1.30
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/skills/components/banner-video.md +37 -45
- package/.claude/skills/components/grid-stack.md +133 -0
- package/.claude/skills/index.md +3 -1
- package/.claude/skills/new-app-scaffold.md +301 -0
- package/README.md +15 -0
- package/app/components/01.atoms/banner-video/BannerVideo.vue +25 -19
- package/app/components/01.atoms/banner-video/stories/BannerVideo.stories.ts +25 -36
- package/app/components/01.atoms/banner-video/tests/BannerVideo.spec.ts +15 -39
- package/app/components/01.atoms/banner-video/tests/__snapshots__/BannerVideo.spec.ts.snap +2 -2
- package/app/components/01.atoms/grid-stack/GridStack.vue +34 -0
- package/app/components/01.atoms/grid-stack/stories/GridStack.stories.ts +162 -0
- package/app/components/01.atoms/grid-stack/tests/GridStack.spec.ts +66 -0
- package/app/pages/banner-video.vue +13 -71
- package/app/pages/grid-stack.vue +252 -0
- package/app/pages/index.vue +5 -0
- package/package.json +1 -1
|
@@ -6,9 +6,7 @@
|
|
|
6
6
|
src="/images/banners/video/lake-banner.mp4"
|
|
7
7
|
poster="/images/banners/video/lake-banner.jpg"
|
|
8
8
|
alt="A serene lake landscape"
|
|
9
|
-
:
|
|
10
|
-
:max-height-tablet="maxHeightTablet || undefined"
|
|
11
|
-
:max-height-mobile="maxHeightMobile || undefined"
|
|
9
|
+
:depth="depth"
|
|
12
10
|
:aspect-ratio="aspectRatio"
|
|
13
11
|
:tag="tag"
|
|
14
12
|
:object-fit="objectFit"
|
|
@@ -23,41 +21,11 @@
|
|
|
23
21
|
|
|
24
22
|
<div class="demo-controls__row">
|
|
25
23
|
<MultipleRadiobuttons
|
|
26
|
-
v-model="
|
|
27
|
-
v-model:field-data="
|
|
28
|
-
name="
|
|
29
|
-
label="
|
|
30
|
-
legend="
|
|
31
|
-
error-message=""
|
|
32
|
-
:is-button="true"
|
|
33
|
-
:is-pill="true"
|
|
34
|
-
:field-has-error="false"
|
|
35
|
-
options-layout="inline"
|
|
36
|
-
/>
|
|
37
|
-
</div>
|
|
38
|
-
|
|
39
|
-
<div class="demo-controls__row">
|
|
40
|
-
<MultipleRadiobuttons
|
|
41
|
-
v-model="maxHeightTablet"
|
|
42
|
-
v-model:field-data="maxHeightTabletData"
|
|
43
|
-
name="maxHeightTablet"
|
|
44
|
-
label="maxHeightTablet"
|
|
45
|
-
legend="maxHeightTablet (≥768px, falls back to maxHeight)"
|
|
46
|
-
error-message=""
|
|
47
|
-
:is-button="true"
|
|
48
|
-
:is-pill="true"
|
|
49
|
-
:field-has-error="false"
|
|
50
|
-
options-layout="inline"
|
|
51
|
-
/>
|
|
52
|
-
</div>
|
|
53
|
-
|
|
54
|
-
<div class="demo-controls__row">
|
|
55
|
-
<MultipleRadiobuttons
|
|
56
|
-
v-model="maxHeightMobile"
|
|
57
|
-
v-model:field-data="maxHeightMobileData"
|
|
58
|
-
name="maxHeightMobile"
|
|
59
|
-
label="maxHeightMobile"
|
|
60
|
-
legend="maxHeightMobile (<768px, falls back through tablet → desktop)"
|
|
24
|
+
v-model="depth"
|
|
25
|
+
v-model:field-data="depthData"
|
|
26
|
+
name="depth"
|
|
27
|
+
label="depth"
|
|
28
|
+
legend="depth"
|
|
61
29
|
error-message=""
|
|
62
30
|
:is-button="true"
|
|
63
31
|
:is-pill="true"
|
|
@@ -160,52 +128,26 @@ useHead({
|
|
|
160
128
|
bodyAttrs: { class: "banner-video-page" },
|
|
161
129
|
});
|
|
162
130
|
|
|
163
|
-
const
|
|
164
|
-
const maxHeightTablet = ref("");
|
|
165
|
-
const maxHeightMobile = ref("");
|
|
131
|
+
const depth = ref<"xs" | "sm" | "md" | "lg" | "xl">("md");
|
|
166
132
|
const aspectRatio = ref("21/9");
|
|
167
133
|
const tag = ref<"section" | "div" | "header" | "main" | "article">("section");
|
|
168
134
|
const objectFit = ref<"cover" | "contain" | "fill" | "none" | "scale-down">("cover");
|
|
169
135
|
const verticalPosition = ref<"start" | "center" | "end">("center");
|
|
170
136
|
const horizontalPosition = ref<"start" | "center" | "end">("center");
|
|
171
137
|
|
|
172
|
-
const
|
|
138
|
+
const depthData = ref<IFormMultipleOptions>({
|
|
173
139
|
data: [
|
|
174
|
-
{ id: "
|
|
175
|
-
{ id: "
|
|
176
|
-
{ id: "
|
|
177
|
-
{ id: "
|
|
178
|
-
{ id: "
|
|
140
|
+
{ id: "depth-xs", name: "depth", value: "xs", label: "xs" },
|
|
141
|
+
{ id: "depth-sm", name: "depth", value: "sm", label: "sm" },
|
|
142
|
+
{ id: "depth-md", name: "depth", value: "md", label: "md" },
|
|
143
|
+
{ id: "depth-lg", name: "depth", value: "lg", label: "lg" },
|
|
144
|
+
{ id: "depth-xl", name: "depth", value: "xl", label: "xl" },
|
|
179
145
|
],
|
|
180
146
|
total: 5,
|
|
181
147
|
skip: 0,
|
|
182
148
|
limit: 5,
|
|
183
149
|
});
|
|
184
150
|
|
|
185
|
-
const maxHeightTabletData = ref<IFormMultipleOptions>({
|
|
186
|
-
data: [
|
|
187
|
-
{ id: "mht-unset", name: "maxHeightTablet", value: "", label: "unset" },
|
|
188
|
-
{ id: "mht-32rem", name: "maxHeightTablet", value: "32rem", label: "32rem" },
|
|
189
|
-
{ id: "mht-40rem", name: "maxHeightTablet", value: "40rem", label: "40rem" },
|
|
190
|
-
{ id: "mht-56rem", name: "maxHeightTablet", value: "56rem", label: "56rem" },
|
|
191
|
-
],
|
|
192
|
-
total: 4,
|
|
193
|
-
skip: 0,
|
|
194
|
-
limit: 4,
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
const maxHeightMobileData = ref<IFormMultipleOptions>({
|
|
198
|
-
data: [
|
|
199
|
-
{ id: "mhm-unset", name: "maxHeightMobile", value: "", label: "unset" },
|
|
200
|
-
{ id: "mhm-24rem", name: "maxHeightMobile", value: "24rem", label: "24rem" },
|
|
201
|
-
{ id: "mhm-32rem", name: "maxHeightMobile", value: "32rem", label: "32rem" },
|
|
202
|
-
{ id: "mhm-40rem", name: "maxHeightMobile", value: "40rem", label: "40rem" },
|
|
203
|
-
],
|
|
204
|
-
total: 4,
|
|
205
|
-
skip: 0,
|
|
206
|
-
limit: 4,
|
|
207
|
-
});
|
|
208
|
-
|
|
209
151
|
const aspectRatioData = ref<IFormMultipleOptions>({
|
|
210
152
|
data: [
|
|
211
153
|
{ id: "ar-16-9", name: "aspectRatio", value: "16/9", label: "16/9" },
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<NuxtLayout name="default">
|
|
4
|
+
<template #layout-content>
|
|
5
|
+
<!-- Demo 3: BannerVideo + overlay -->
|
|
6
|
+
<LayoutRow tag="div" variant="full-width" :style-class-passthrough="['mbe-48']">
|
|
7
|
+
<GridStack :tag="tag" class="grid-stack-video-demo">
|
|
8
|
+
<template #layer-1>
|
|
9
|
+
<BannerVideo
|
|
10
|
+
src="/images/banners/video/lake-banner.mp4"
|
|
11
|
+
poster="/images/banners/video/lake-banner.jpg"
|
|
12
|
+
alt="A serene lake landscape"
|
|
13
|
+
aspect-ratio="4/3"
|
|
14
|
+
object-fit="cover"
|
|
15
|
+
vertical-position="center"
|
|
16
|
+
horizontal-position="center"
|
|
17
|
+
/>
|
|
18
|
+
</template>
|
|
19
|
+
<template #layer-2>
|
|
20
|
+
<div class="video-overlay">
|
|
21
|
+
<div class="video-overlay__content">
|
|
22
|
+
<p class="video-overlay__eyebrow">GridStack layer-2</p>
|
|
23
|
+
<h2 class="video-overlay__heading">Content over video</h2>
|
|
24
|
+
<p class="video-overlay__body">
|
|
25
|
+
This layer sits on top of the BannerVideo without
|
|
26
|
+
<code>position: absolute</code>
|
|
27
|
+
.
|
|
28
|
+
</p>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
</template>
|
|
32
|
+
</GridStack>
|
|
33
|
+
</LayoutRow>
|
|
34
|
+
|
|
35
|
+
<LayoutRow tag="div" variant="full-width" :style-class-passthrough="['mbe-32']">
|
|
36
|
+
<h1 class="page-heading-1">GridStack</h1>
|
|
37
|
+
<p class="page-body-normal">
|
|
38
|
+
Stacks slot content in the z-axis using
|
|
39
|
+
<code>grid-template-areas</code>
|
|
40
|
+
— no
|
|
41
|
+
<code>position: absolute</code>
|
|
42
|
+
. Layers share a single grid area; DOM order determines z-order (last slot is on top).
|
|
43
|
+
</p>
|
|
44
|
+
</LayoutRow>
|
|
45
|
+
|
|
46
|
+
<!-- Demo 1: Two layers -->
|
|
47
|
+
<LayoutRow tag="div" variant="full-width" :style-class-passthrough="['mbe-48']">
|
|
48
|
+
<h2 class="page-heading-2 mbe-16">Two layers</h2>
|
|
49
|
+
<GridStack class="grid-stack-demo">
|
|
50
|
+
<template #layer-1>
|
|
51
|
+
<div class="demo-layer demo-layer--base">layer-1 (base)</div>
|
|
52
|
+
</template>
|
|
53
|
+
<template #layer-2>
|
|
54
|
+
<div class="demo-layer demo-layer--overlay">layer-2 (overlay)</div>
|
|
55
|
+
</template>
|
|
56
|
+
</GridStack>
|
|
57
|
+
</LayoutRow>
|
|
58
|
+
|
|
59
|
+
<!-- Demo 2: Three layers -->
|
|
60
|
+
<LayoutRow tag="div" variant="full-width" :style-class-passthrough="['mbe-48']">
|
|
61
|
+
<h2 class="page-heading-2 mbe-16">Three layers</h2>
|
|
62
|
+
<GridStack class="grid-stack-demo">
|
|
63
|
+
<template #layer-1>
|
|
64
|
+
<div class="demo-layer demo-layer--base">layer-1 (base)</div>
|
|
65
|
+
</template>
|
|
66
|
+
<template #layer-2>
|
|
67
|
+
<div class="demo-layer demo-layer--mid">layer-2 (mid)</div>
|
|
68
|
+
</template>
|
|
69
|
+
<template #layer-3>
|
|
70
|
+
<div class="demo-layer demo-layer--overlay">layer-3 (overlay)</div>
|
|
71
|
+
</template>
|
|
72
|
+
</GridStack>
|
|
73
|
+
</LayoutRow>
|
|
74
|
+
|
|
75
|
+
<!-- Controls -->
|
|
76
|
+
<LayoutRow tag="div" variant="full-width">
|
|
77
|
+
<div class="grid-stack-demo-controls">
|
|
78
|
+
<p class="demo-controls__heading">Props (video demo)</p>
|
|
79
|
+
<div class="demo-controls__row">
|
|
80
|
+
<MultipleRadiobuttons
|
|
81
|
+
v-model="tag"
|
|
82
|
+
v-model:field-data="tagData"
|
|
83
|
+
name="tag"
|
|
84
|
+
label="tag"
|
|
85
|
+
legend="tag"
|
|
86
|
+
error-message=""
|
|
87
|
+
:is-button="true"
|
|
88
|
+
:is-pill="true"
|
|
89
|
+
:field-has-error="false"
|
|
90
|
+
options-layout="inline"
|
|
91
|
+
/>
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
</LayoutRow>
|
|
95
|
+
</template>
|
|
96
|
+
</NuxtLayout>
|
|
97
|
+
</div>
|
|
98
|
+
</template>
|
|
99
|
+
|
|
100
|
+
<script setup lang="ts">
|
|
101
|
+
import type { IFormMultipleOptions } from "~/types/forms/types.forms";
|
|
102
|
+
|
|
103
|
+
definePageMeta({
|
|
104
|
+
layout: false,
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
useHead({
|
|
108
|
+
title: "GridStack",
|
|
109
|
+
meta: [{ name: "description", content: "GridStack component demo" }],
|
|
110
|
+
bodyAttrs: { class: "grid-stack-page" },
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const tag = ref<"div" | "section" | "article" | "main">("div");
|
|
114
|
+
|
|
115
|
+
const tagData = ref<IFormMultipleOptions>({
|
|
116
|
+
data: [
|
|
117
|
+
{ id: "tag-div", name: "tag", value: "div", label: "div" },
|
|
118
|
+
{ id: "tag-section", name: "tag", value: "section", label: "section" },
|
|
119
|
+
{ id: "tag-article", name: "tag", value: "article", label: "article" },
|
|
120
|
+
{ id: "tag-main", name: "tag", value: "main", label: "main" },
|
|
121
|
+
],
|
|
122
|
+
total: 4,
|
|
123
|
+
skip: 0,
|
|
124
|
+
limit: 4,
|
|
125
|
+
});
|
|
126
|
+
</script>
|
|
127
|
+
|
|
128
|
+
<style lang="css">
|
|
129
|
+
.grid-stack-page {
|
|
130
|
+
code {
|
|
131
|
+
font-family: monospace;
|
|
132
|
+
font-size: 0.9em;
|
|
133
|
+
background: var(--slate-02);
|
|
134
|
+
padding: 0.1em 0.4em;
|
|
135
|
+
border-radius: 0.3rem;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.mbe-16 {
|
|
139
|
+
margin-block-end: 1.6rem;
|
|
140
|
+
}
|
|
141
|
+
.mbe-48 {
|
|
142
|
+
margin-block-end: 4.8rem;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/* Colored layer demos */
|
|
146
|
+
.grid-stack-demo {
|
|
147
|
+
border-radius: 0.8rem;
|
|
148
|
+
overflow: hidden;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.demo-layer {
|
|
152
|
+
display: flex;
|
|
153
|
+
align-items: center;
|
|
154
|
+
justify-content: center;
|
|
155
|
+
min-block-size: 20rem;
|
|
156
|
+
font-size: 1.4rem;
|
|
157
|
+
font-weight: 600;
|
|
158
|
+
border-radius: 0.8rem;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.demo-layer--base {
|
|
162
|
+
background: var(--slate-03);
|
|
163
|
+
color: var(--slate-09);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.demo-layer--mid {
|
|
167
|
+
background: color-mix(in srgb, var(--orange-06) 40%, transparent);
|
|
168
|
+
color: var(--orange-10);
|
|
169
|
+
margin: 2rem;
|
|
170
|
+
border-radius: 0.6rem;
|
|
171
|
+
border: 2px dashed var(--orange-07);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.demo-layer--overlay {
|
|
175
|
+
display: flex;
|
|
176
|
+
align-items: flex-end;
|
|
177
|
+
justify-content: flex-start;
|
|
178
|
+
padding: 1.6rem;
|
|
179
|
+
background: linear-gradient(to top, color-mix(in srgb, var(--slate-10) 60%, transparent) 0%, transparent 60%);
|
|
180
|
+
color: white;
|
|
181
|
+
border-radius: 0.8rem;
|
|
182
|
+
pointer-events: none;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/* Video overlay demo */
|
|
186
|
+
.video-overlay {
|
|
187
|
+
display: grid;
|
|
188
|
+
place-items: center;
|
|
189
|
+
pointer-events: none;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.video-overlay__content {
|
|
193
|
+
text-align: center;
|
|
194
|
+
padding: 2.4rem;
|
|
195
|
+
pointer-events: auto;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.video-overlay__eyebrow {
|
|
199
|
+
font-size: 1.1rem;
|
|
200
|
+
font-weight: 700;
|
|
201
|
+
text-transform: uppercase;
|
|
202
|
+
letter-spacing: 0.12em;
|
|
203
|
+
color: white;
|
|
204
|
+
margin-block-end: 0.8rem;
|
|
205
|
+
opacity: 0.8;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.video-overlay__heading {
|
|
209
|
+
font-size: 4rem;
|
|
210
|
+
font-weight: 700;
|
|
211
|
+
color: white;
|
|
212
|
+
margin-block-end: 1.2rem;
|
|
213
|
+
text-shadow: 0 2px 12px rgba(0 0 0 / 0.5);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.video-overlay__body {
|
|
217
|
+
font-size: 1.6rem;
|
|
218
|
+
color: white;
|
|
219
|
+
opacity: 0.9;
|
|
220
|
+
text-shadow: 0 1px 6px rgba(0 0 0 / 0.4);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.video-overlay__body code {
|
|
224
|
+
background: rgba(0 0 0 / 0.3);
|
|
225
|
+
color: white;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/* Controls */
|
|
229
|
+
.grid-stack-demo-controls {
|
|
230
|
+
padding: 1.6rem;
|
|
231
|
+
border: 1px solid var(--slate-06);
|
|
232
|
+
border-radius: 0.8rem;
|
|
233
|
+
|
|
234
|
+
.demo-controls__heading {
|
|
235
|
+
font-size: 1.1rem;
|
|
236
|
+
font-weight: 600;
|
|
237
|
+
text-transform: uppercase;
|
|
238
|
+
letter-spacing: 0.08em;
|
|
239
|
+
color: var(--slate-08);
|
|
240
|
+
margin-block-end: 1.2rem;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.demo-controls__row {
|
|
244
|
+
margin-block-end: 1.2rem;
|
|
245
|
+
|
|
246
|
+
&:last-child {
|
|
247
|
+
margin-block-end: 0;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
</style>
|
package/app/pages/index.vue
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
<div>
|
|
3
3
|
<NuxtLayout name="default">
|
|
4
4
|
<template #layout-content>
|
|
5
|
+
<LayoutRow tag="div" variant="full-width" :style-class-passthrough="['mbe-20']">
|
|
6
|
+
<h2 class="page-heading-2">GridStack</h2>
|
|
7
|
+
<NuxtLink class="page-body-normal" to="/grid-stack">GridStack</NuxtLink>
|
|
8
|
+
</LayoutRow>
|
|
9
|
+
|
|
5
10
|
<LayoutRow tag="div" variant="full-width" :style-class-passthrough="['mbe-20']">
|
|
6
11
|
<h1 class="page-heading-1">Nuxt3 Components Layer</h1>
|
|
7
12
|
<p>
|