@brickflow/ui 0.0.26 → 0.0.28
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/dist/module.d.mts +4 -1
- package/dist/module.json +2 -2
- package/dist/module.mjs +409 -11
- package/dist/runtime/components/Button/icons.demo.d.vue.ts +8 -0
- package/dist/runtime/components/Button/icons.demo.vue +25 -0
- package/dist/runtime/components/Button/icons.demo.vue.d.ts +8 -0
- package/dist/runtime/components/Button/index.d.vue.ts +45 -0
- package/dist/runtime/components/Button/index.vue +162 -0
- package/dist/runtime/components/Button/index.vue.d.ts +45 -0
- package/dist/runtime/components/Button/size.demo.d.vue.ts +7 -0
- package/dist/runtime/components/Button/size.demo.vue +78 -0
- package/dist/runtime/components/Button/size.demo.vue.d.ts +7 -0
- package/dist/runtime/components/Button/slots.demo.d.vue.ts +7 -0
- package/dist/runtime/components/Button/slots.demo.vue +23 -0
- package/dist/runtime/components/Button/slots.demo.vue.d.ts +7 -0
- package/dist/runtime/components/Button/state.demo.d.vue.ts +7 -0
- package/dist/runtime/components/Button/state.demo.vue +18 -0
- package/dist/runtime/components/Button/state.demo.vue.d.ts +7 -0
- package/dist/runtime/components/Button/variants.demo.d.vue.ts +7 -0
- package/dist/runtime/components/Button/variants.demo.vue +207 -0
- package/dist/runtime/components/Button/variants.demo.vue.d.ts +7 -0
- package/dist/runtime/components/Icon/basic.demo.d.vue.ts +8 -0
- package/dist/runtime/components/Icon/basic.demo.vue +44 -0
- package/dist/runtime/components/Icon/basic.demo.vue.d.ts +8 -0
- package/dist/runtime/components/Icon/index.d.vue.ts +7 -0
- package/dist/runtime/components/Icon/index.vue +18 -0
- package/dist/runtime/components/Icon/index.vue.d.ts +7 -0
- package/dist/runtime/components/Link/basic.demo.d.vue.ts +8 -0
- package/dist/runtime/components/Link/basic.demo.vue +27 -0
- package/dist/runtime/components/Link/basic.demo.vue.d.ts +8 -0
- package/dist/runtime/components/Link/index.d.vue.ts +51 -0
- package/dist/runtime/components/Link/index.vue +62 -0
- package/dist/runtime/components/Link/index.vue.d.ts +51 -0
- package/dist/runtime/composables/useScreen.d.ts +18 -0
- package/dist/runtime/composables/useScreen.js +68 -0
- package/dist/runtime/composables/useTheme.d.ts +17 -0
- package/dist/runtime/composables/useTheme.js +77 -0
- package/dist/runtime/composables/useTranslate.d.ts +20 -0
- package/dist/runtime/composables/useTranslate.js +17 -0
- package/dist/runtime/icon-font.d.ts +4 -0
- package/dist/runtime/pages/ui.d.vue.ts +4 -0
- package/dist/runtime/pages/ui.vue +717 -0
- package/dist/runtime/pages/ui.vue.d.ts +4 -0
- package/dist/runtime/tailwind.d.ts +4 -1
- package/dist/runtime/tailwind.js +2 -1
- package/dist/runtime/ui-catalog.d.ts +39 -0
- package/dist/runtime/ui-options.d.ts +3 -0
- package/dist/types.d.mts +2 -0
- package/package.json +10 -10
- package/dist/runtime/components/Button.d.vue.ts +0 -23
- package/dist/runtime/components/Button.vue +0 -29
- package/dist/runtime/components/Button.vue.d.ts +0 -23
- package/dist/runtime/composables/useBrickme.d.ts +0 -9
- package/dist/runtime/composables/useBrickme.js +0 -14
|
@@ -0,0 +1,717 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { useHead, useRoute, useRouter } from "nuxt/app";
|
|
3
|
+
import { computed, nextTick, ref, watch } from "vue";
|
|
4
|
+
import { uiComponents } from "#brickflow-ui-catalog";
|
|
5
|
+
import { uiThemeEnabled } from "#brickflow-ui-options";
|
|
6
|
+
import { useTheme } from "../composables/useTheme";
|
|
7
|
+
useHead({
|
|
8
|
+
meta: [
|
|
9
|
+
{
|
|
10
|
+
content: "noindex, nofollow, noarchive",
|
|
11
|
+
name: "robots"
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
title: "UI playground"
|
|
15
|
+
});
|
|
16
|
+
const route = useRoute();
|
|
17
|
+
const router = useRouter();
|
|
18
|
+
const expandedDemoCode = ref(/* @__PURE__ */ new Set());
|
|
19
|
+
const isNavigationOpen = ref(false);
|
|
20
|
+
const mobileNavigation = ref();
|
|
21
|
+
const stylesExpanded = ref(false);
|
|
22
|
+
const theme = uiThemeEnabled ? useTheme() : void 0;
|
|
23
|
+
const isDark = computed(() => theme?.isDark.value ?? false);
|
|
24
|
+
const toggleTheme = () => theme?.toggleTheme();
|
|
25
|
+
const activeComponent = computed(() => {
|
|
26
|
+
const component = route.query.component;
|
|
27
|
+
const id = typeof component === "string" ? component : void 0;
|
|
28
|
+
return uiComponents.find((item) => item.id === id) ?? uiComponents[0];
|
|
29
|
+
});
|
|
30
|
+
const visibleStyles = computed(() => {
|
|
31
|
+
const styles = activeComponent.value?.styles ?? [];
|
|
32
|
+
return stylesExpanded.value ? styles : styles.slice(0, 5);
|
|
33
|
+
});
|
|
34
|
+
const getDemoCodeKey = (demoId) => `${activeComponent.value?.id}:${demoId}`;
|
|
35
|
+
const escapeHtml = (value) => value.replace(
|
|
36
|
+
/[&<>"']/g,
|
|
37
|
+
(character) => ({
|
|
38
|
+
'"': """,
|
|
39
|
+
"&": "&",
|
|
40
|
+
"'": "'",
|
|
41
|
+
"<": "<",
|
|
42
|
+
">": ">"
|
|
43
|
+
})[character] ?? character
|
|
44
|
+
);
|
|
45
|
+
const token = (className, value) => `<span class="${className}">${value}</span>`;
|
|
46
|
+
const highlightTypeScript = (source) => {
|
|
47
|
+
const pattern = /\/\*[\s\S]*?\*\/|\/\/[^\n]*|`(?:\\[\s\S]|[^`])*`|'(?:\\[\s\S]|[^'])*'|"(?:\\[\s\S]|[^"])*"|\b(?:as|async|await|const|export|from|function|import|interface|let|return|type)\b|\b\d+(?:\.\d+)?\b/g;
|
|
48
|
+
let html = "";
|
|
49
|
+
let lastIndex = 0;
|
|
50
|
+
for (const match of source.matchAll(pattern)) {
|
|
51
|
+
const index = match.index ?? 0;
|
|
52
|
+
const value = match[0];
|
|
53
|
+
html += escapeHtml(source.slice(lastIndex, index));
|
|
54
|
+
if (value.startsWith("//") || value.startsWith("/*")) {
|
|
55
|
+
html += token("ui-code-comment", escapeHtml(value));
|
|
56
|
+
} else if (value.startsWith("'") || value.startsWith('"') || value.startsWith("`")) {
|
|
57
|
+
html += token("ui-code-string", escapeHtml(value));
|
|
58
|
+
} else if (/^\d/.test(value)) {
|
|
59
|
+
html += token("ui-code-number", value);
|
|
60
|
+
} else {
|
|
61
|
+
html += token("ui-code-keyword", value);
|
|
62
|
+
}
|
|
63
|
+
lastIndex = index + value.length;
|
|
64
|
+
}
|
|
65
|
+
return html + escapeHtml(source.slice(lastIndex));
|
|
66
|
+
};
|
|
67
|
+
const highlightPropType = (source) => {
|
|
68
|
+
const pattern = /`(?:\\[\s\S]|[^`])*`|'(?:\\[\s\S]|[^'])*'|"(?:\\[\s\S]|[^"])*"|\b(?:any|bigint|boolean|never|null|number|object|string|symbol|undefined|unknown|void)\b|\b[A-Z]\w*\b|\|/g;
|
|
69
|
+
let html = "";
|
|
70
|
+
let lastIndex = 0;
|
|
71
|
+
for (const match of source.matchAll(pattern)) {
|
|
72
|
+
const index = match.index ?? 0;
|
|
73
|
+
const value = match[0];
|
|
74
|
+
html += escapeHtml(source.slice(lastIndex, index));
|
|
75
|
+
if (value.startsWith("'") || value.startsWith('"') || value.startsWith("`")) {
|
|
76
|
+
html += token("ui-code-string", escapeHtml(value));
|
|
77
|
+
} else if (value === "|") {
|
|
78
|
+
html += token("ui-code-punctuation", value);
|
|
79
|
+
} else if (/^[A-Z]/.test(value)) {
|
|
80
|
+
html += token("ui-code-tag", escapeHtml(value));
|
|
81
|
+
} else {
|
|
82
|
+
html += token("ui-code-keyword", value);
|
|
83
|
+
}
|
|
84
|
+
lastIndex = index + value.length;
|
|
85
|
+
}
|
|
86
|
+
return html + escapeHtml(source.slice(lastIndex));
|
|
87
|
+
};
|
|
88
|
+
const highlightTag = (source) => {
|
|
89
|
+
const opening = source.match(/^<(\/)?([\w.-]+)/);
|
|
90
|
+
const closing = source.match(/\/?>\s*$/);
|
|
91
|
+
if (!opening || !closing) {
|
|
92
|
+
return escapeHtml(source);
|
|
93
|
+
}
|
|
94
|
+
const attributes = source.slice(opening[0].length, source.length - closing[0].length);
|
|
95
|
+
let html = token("ui-code-punctuation", "<");
|
|
96
|
+
let attributeIndex = 0;
|
|
97
|
+
if (opening[1]) {
|
|
98
|
+
html += token("ui-code-punctuation", "/");
|
|
99
|
+
}
|
|
100
|
+
html += token("ui-code-tag", escapeHtml(opening[2] ?? ""));
|
|
101
|
+
while (attributeIndex < attributes.length) {
|
|
102
|
+
const character = attributes[attributeIndex] ?? "";
|
|
103
|
+
if (/\s/.test(character)) {
|
|
104
|
+
html += escapeHtml(character);
|
|
105
|
+
attributeIndex += 1;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
const nameStart = attributeIndex;
|
|
109
|
+
while (attributeIndex < attributes.length && !/[\s=]/.test(attributes[attributeIndex] ?? "")) {
|
|
110
|
+
attributeIndex += 1;
|
|
111
|
+
}
|
|
112
|
+
const name = attributes.slice(nameStart, attributeIndex);
|
|
113
|
+
html += token("ui-code-attribute", escapeHtml(name));
|
|
114
|
+
const whitespaceStart = attributeIndex;
|
|
115
|
+
while (/\s/.test(attributes[attributeIndex] ?? "")) {
|
|
116
|
+
attributeIndex += 1;
|
|
117
|
+
}
|
|
118
|
+
html += escapeHtml(attributes.slice(whitespaceStart, attributeIndex));
|
|
119
|
+
if (attributes[attributeIndex] !== "=") {
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
html += token("ui-code-punctuation", "=");
|
|
123
|
+
attributeIndex += 1;
|
|
124
|
+
const valueWhitespaceStart = attributeIndex;
|
|
125
|
+
while (/\s/.test(attributes[attributeIndex] ?? "")) {
|
|
126
|
+
attributeIndex += 1;
|
|
127
|
+
}
|
|
128
|
+
html += escapeHtml(attributes.slice(valueWhitespaceStart, attributeIndex));
|
|
129
|
+
const quote = attributes[attributeIndex];
|
|
130
|
+
const valueStart = attributeIndex;
|
|
131
|
+
if (quote === '"' || quote === "'") {
|
|
132
|
+
attributeIndex += 1;
|
|
133
|
+
while (attributeIndex < attributes.length && attributes[attributeIndex] !== quote) {
|
|
134
|
+
attributeIndex += attributes[attributeIndex] === "\\" ? 2 : 1;
|
|
135
|
+
}
|
|
136
|
+
attributeIndex += 1;
|
|
137
|
+
} else {
|
|
138
|
+
while (attributeIndex < attributes.length && !/\s/.test(attributes[attributeIndex] ?? "")) {
|
|
139
|
+
attributeIndex += 1;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
html += token("ui-code-string", escapeHtml(attributes.slice(valueStart, attributeIndex)));
|
|
143
|
+
}
|
|
144
|
+
html += token("ui-code-punctuation", escapeHtml(closing[0]));
|
|
145
|
+
return html;
|
|
146
|
+
};
|
|
147
|
+
const highlightTemplate = (source) => {
|
|
148
|
+
let html = "";
|
|
149
|
+
let index = 0;
|
|
150
|
+
while (index < source.length) {
|
|
151
|
+
const commentStart = source.indexOf("<!--", index);
|
|
152
|
+
const interpolationStart = source.indexOf("{{", index);
|
|
153
|
+
const tagStart = source.indexOf("<", index);
|
|
154
|
+
const starts = [commentStart, interpolationStart, tagStart].filter((start2) => start2 !== -1);
|
|
155
|
+
const start = Math.min(...starts);
|
|
156
|
+
if (!Number.isFinite(start)) {
|
|
157
|
+
return html + escapeHtml(source.slice(index));
|
|
158
|
+
}
|
|
159
|
+
html += escapeHtml(source.slice(index, start));
|
|
160
|
+
if (start === commentStart) {
|
|
161
|
+
const end = source.indexOf("-->", start + 4);
|
|
162
|
+
if (end === -1) {
|
|
163
|
+
return html + token("ui-code-comment", escapeHtml(source.slice(start)));
|
|
164
|
+
}
|
|
165
|
+
html += token("ui-code-comment", escapeHtml(source.slice(start, end + 3)));
|
|
166
|
+
index = end + 3;
|
|
167
|
+
} else if (start === interpolationStart) {
|
|
168
|
+
const end = source.indexOf("}}", start + 2);
|
|
169
|
+
if (end === -1) {
|
|
170
|
+
return html + escapeHtml(source.slice(start));
|
|
171
|
+
}
|
|
172
|
+
html += token("ui-code-punctuation", "{{");
|
|
173
|
+
html += highlightTypeScript(source.slice(start + 2, end));
|
|
174
|
+
html += token("ui-code-punctuation", "}}");
|
|
175
|
+
index = end + 2;
|
|
176
|
+
} else {
|
|
177
|
+
let tagEnd = start + 1;
|
|
178
|
+
let quote = "";
|
|
179
|
+
while (tagEnd < source.length) {
|
|
180
|
+
const character = source[tagEnd] ?? "";
|
|
181
|
+
if (quote) {
|
|
182
|
+
if (character === "\\") {
|
|
183
|
+
tagEnd += 2;
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
if (character === quote) {
|
|
187
|
+
quote = "";
|
|
188
|
+
}
|
|
189
|
+
} else if (character === '"' || character === "'") {
|
|
190
|
+
quote = character;
|
|
191
|
+
} else if (character === ">") {
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
tagEnd += 1;
|
|
195
|
+
}
|
|
196
|
+
const tag = source.slice(start, tagEnd + 1);
|
|
197
|
+
if (/^<\/?[\w.-]/.test(tag)) {
|
|
198
|
+
html += highlightTag(tag);
|
|
199
|
+
} else {
|
|
200
|
+
html += escapeHtml(tag);
|
|
201
|
+
}
|
|
202
|
+
index = tagEnd + 1;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return html;
|
|
206
|
+
};
|
|
207
|
+
const highlightDemoCode = (source) => {
|
|
208
|
+
const scriptPattern = /(<script\b[^>]*>)([\s\S]*?)(<\/script>)/g;
|
|
209
|
+
let html = "";
|
|
210
|
+
let lastIndex = 0;
|
|
211
|
+
for (const match of source.matchAll(scriptPattern)) {
|
|
212
|
+
const index = match.index ?? 0;
|
|
213
|
+
html += highlightTemplate(source.slice(lastIndex, index));
|
|
214
|
+
html += highlightTag(match[1] ?? "");
|
|
215
|
+
html += highlightTypeScript(match[2] ?? "");
|
|
216
|
+
html += highlightTag(match[3] ?? "");
|
|
217
|
+
lastIndex = index + match[0].length;
|
|
218
|
+
}
|
|
219
|
+
return html + highlightTemplate(source.slice(lastIndex));
|
|
220
|
+
};
|
|
221
|
+
const highlightTailwindValue = (value) => {
|
|
222
|
+
const pattern = /\d+(?:\.\d+)?/g;
|
|
223
|
+
let html = "";
|
|
224
|
+
let lastIndex = 0;
|
|
225
|
+
for (const match of value.matchAll(pattern)) {
|
|
226
|
+
const index = match.index ?? 0;
|
|
227
|
+
html += token("ui-code-string", escapeHtml(value.slice(lastIndex, index)));
|
|
228
|
+
html += token("ui-code-number", match[0]);
|
|
229
|
+
lastIndex = index + match[0].length;
|
|
230
|
+
}
|
|
231
|
+
return html + token("ui-code-string", escapeHtml(value.slice(lastIndex)));
|
|
232
|
+
};
|
|
233
|
+
const getTailwindColorSwatch = (utility) => {
|
|
234
|
+
const themeColor = utility.match(
|
|
235
|
+
/(?:^|-)(alt|danger|info|main|plain|warn|win)-(50|100|200|300|400|500|600|700|800|900|950)(?:\/(\d{1,3}))?(?:$|-)/
|
|
236
|
+
);
|
|
237
|
+
const neutralColor = utility.match(/(?:^|-)(black|white)(?:\/(\d{1,3}))?$/);
|
|
238
|
+
const match = themeColor ?? neutralColor;
|
|
239
|
+
if (!match) {
|
|
240
|
+
return "";
|
|
241
|
+
}
|
|
242
|
+
const [, name, shade, opacity] = match;
|
|
243
|
+
const color = shade ? `var(--color-${name}-${shade})` : `var(--color-${name})`;
|
|
244
|
+
const background = opacity ? `color-mix(in srgb, ${color} ${opacity}%, transparent)` : color;
|
|
245
|
+
return `<span aria-hidden="true" class="ui-color-swatch" style="--ui-color-swatch: ${background}"></span>`;
|
|
246
|
+
};
|
|
247
|
+
const highlightTailwindUtility = (utility) => {
|
|
248
|
+
let source = utility;
|
|
249
|
+
let html = "";
|
|
250
|
+
for (const prefix of ["!", "-"]) {
|
|
251
|
+
if (source.startsWith(prefix)) {
|
|
252
|
+
html += token("ui-code-punctuation", prefix);
|
|
253
|
+
source = source.slice(prefix.length);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
html += getTailwindColorSwatch(source);
|
|
257
|
+
const separatorIndex = source.indexOf("-");
|
|
258
|
+
if (separatorIndex === -1) {
|
|
259
|
+
return html + token("ui-code-attribute", escapeHtml(source));
|
|
260
|
+
}
|
|
261
|
+
html += token("ui-code-attribute", escapeHtml(source.slice(0, separatorIndex)));
|
|
262
|
+
html += token("ui-code-punctuation", "-");
|
|
263
|
+
return html + highlightTailwindValue(source.slice(separatorIndex + 1));
|
|
264
|
+
};
|
|
265
|
+
const highlightTailwindClass = (className) => {
|
|
266
|
+
const segments = [];
|
|
267
|
+
let start = 0;
|
|
268
|
+
let bracketDepth = 0;
|
|
269
|
+
for (let index = 0; index < className.length; index += 1) {
|
|
270
|
+
const character = className[index];
|
|
271
|
+
if (character === "[") {
|
|
272
|
+
bracketDepth += 1;
|
|
273
|
+
} else if (character === "]") {
|
|
274
|
+
bracketDepth = Math.max(0, bracketDepth - 1);
|
|
275
|
+
} else if (character === ":" && bracketDepth === 0) {
|
|
276
|
+
segments.push(className.slice(start, index));
|
|
277
|
+
start = index + 1;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
const utility = className.slice(start);
|
|
281
|
+
const variants = segments.map(
|
|
282
|
+
(segment) => `${token("ui-code-keyword", escapeHtml(segment))}${token("ui-code-punctuation", ":")}`
|
|
283
|
+
);
|
|
284
|
+
return variants.join("") + highlightTailwindUtility(utility);
|
|
285
|
+
};
|
|
286
|
+
const highlightStyleValue = (value) => {
|
|
287
|
+
if (value === void 0) {
|
|
288
|
+
return token("ui-code-comment", "\u2014");
|
|
289
|
+
}
|
|
290
|
+
if (value === "") {
|
|
291
|
+
return token("ui-code-string", """");
|
|
292
|
+
}
|
|
293
|
+
return value.split(/(\s+)/).map((part) => /\s/.test(part) ? escapeHtml(part) : highlightTailwindClass(part)).join("");
|
|
294
|
+
};
|
|
295
|
+
const isDemoCodeExpanded = (demoId) => expandedDemoCode.value.has(getDemoCodeKey(demoId));
|
|
296
|
+
const toggleDemoCode = (demoId) => {
|
|
297
|
+
const key = getDemoCodeKey(demoId);
|
|
298
|
+
const next = new Set(expandedDemoCode.value);
|
|
299
|
+
next.has(key) ? next.delete(key) : next.add(key);
|
|
300
|
+
expandedDemoCode.value = next;
|
|
301
|
+
};
|
|
302
|
+
const closeNavigation = () => {
|
|
303
|
+
isNavigationOpen.value = false;
|
|
304
|
+
};
|
|
305
|
+
watch(isNavigationOpen, async (isOpen) => {
|
|
306
|
+
if (isOpen) {
|
|
307
|
+
await nextTick();
|
|
308
|
+
mobileNavigation.value?.focus();
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
watch(
|
|
312
|
+
() => route.query.component,
|
|
313
|
+
(component) => {
|
|
314
|
+
closeNavigation();
|
|
315
|
+
window.scrollTo({ behavior: "smooth", top: 0 });
|
|
316
|
+
if (typeof component === "string" && uiComponents.some((item) => item.id === component)) {
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
const firstComponent = uiComponents[0];
|
|
320
|
+
if (!firstComponent) {
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
router.replace({
|
|
324
|
+
path: "/ui",
|
|
325
|
+
query: {
|
|
326
|
+
...route.query,
|
|
327
|
+
component: firstComponent.id
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
},
|
|
331
|
+
{ immediate: true }
|
|
332
|
+
);
|
|
333
|
+
watch(
|
|
334
|
+
() => activeComponent.value?.id,
|
|
335
|
+
() => {
|
|
336
|
+
stylesExpanded.value = false;
|
|
337
|
+
}
|
|
338
|
+
);
|
|
339
|
+
</script>
|
|
340
|
+
|
|
341
|
+
<template>
|
|
342
|
+
<main class="min-h-screen">
|
|
343
|
+
<header
|
|
344
|
+
class="sticky top-0 z-30 flex items-center justify-between gap-4 border-b border-plain-800 bg-plain-950/95 px-4 py-3 backdrop-blur md:hidden"
|
|
345
|
+
>
|
|
346
|
+
<div class="min-w-0">
|
|
347
|
+
<p class="text-xs font-medium tracking-widest text-plain-500 uppercase">Components</p>
|
|
348
|
+
<p class="truncate text-sm font-medium text-plain-100">{{ activeComponent?.name }}</p>
|
|
349
|
+
</div>
|
|
350
|
+
<button
|
|
351
|
+
type="button"
|
|
352
|
+
class="flex size-10 shrink-0 cursor-pointer items-center justify-center rounded-lg border border-plain-700 text-plain-200 transition hover:border-main-700 hover:bg-main-900 hover:text-main-100 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-main-400"
|
|
353
|
+
aria-controls="ui-mobile-navigation"
|
|
354
|
+
:aria-expanded="isNavigationOpen"
|
|
355
|
+
aria-label="Open components menu"
|
|
356
|
+
@click="isNavigationOpen = true"
|
|
357
|
+
>
|
|
358
|
+
<svg
|
|
359
|
+
class="size-5"
|
|
360
|
+
aria-hidden="true"
|
|
361
|
+
fill="none"
|
|
362
|
+
stroke="currentColor"
|
|
363
|
+
stroke-linecap="round"
|
|
364
|
+
stroke-width="2"
|
|
365
|
+
viewBox="0 0 24 24"
|
|
366
|
+
>
|
|
367
|
+
<path d="M4 7h16M4 12h16M4 17h16" />
|
|
368
|
+
</svg>
|
|
369
|
+
</button>
|
|
370
|
+
</header>
|
|
371
|
+
|
|
372
|
+
<div
|
|
373
|
+
v-if="isNavigationOpen"
|
|
374
|
+
class="fixed inset-0 z-40 md:hidden"
|
|
375
|
+
@keydown.esc="closeNavigation"
|
|
376
|
+
>
|
|
377
|
+
<button
|
|
378
|
+
class="absolute inset-0 cursor-default bg-black/60"
|
|
379
|
+
aria-label="Close components menu"
|
|
380
|
+
type="button"
|
|
381
|
+
@click="closeNavigation"
|
|
382
|
+
/>
|
|
383
|
+
<aside
|
|
384
|
+
id="ui-mobile-navigation"
|
|
385
|
+
ref="mobileNavigation"
|
|
386
|
+
class="relative flex h-full flex-col border-r border-plain-800 bg-plain-950 p-4 shadow-2xl"
|
|
387
|
+
:class="$style.mobileNavigation"
|
|
388
|
+
aria-label="UI components"
|
|
389
|
+
tabindex="-1"
|
|
390
|
+
>
|
|
391
|
+
<div class="flex items-center justify-between gap-4 px-2 py-1">
|
|
392
|
+
<p class="text-xs font-medium tracking-widest text-plain-500 uppercase">Components</p>
|
|
393
|
+
<button
|
|
394
|
+
type="button"
|
|
395
|
+
class="flex size-8 cursor-pointer items-center justify-center rounded-lg text-plain-400 transition hover:bg-plain-900 hover:text-plain-100 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-main-400"
|
|
396
|
+
aria-label="Close components menu"
|
|
397
|
+
@click="closeNavigation"
|
|
398
|
+
>
|
|
399
|
+
<svg
|
|
400
|
+
class="size-5"
|
|
401
|
+
aria-hidden="true"
|
|
402
|
+
fill="none"
|
|
403
|
+
stroke="currentColor"
|
|
404
|
+
stroke-linecap="round"
|
|
405
|
+
stroke-width="2"
|
|
406
|
+
viewBox="0 0 24 24"
|
|
407
|
+
>
|
|
408
|
+
<path d="m6 6 12 12M18 6 6 18" />
|
|
409
|
+
</svg>
|
|
410
|
+
</button>
|
|
411
|
+
</div>
|
|
412
|
+
<nav
|
|
413
|
+
class="mt-3 space-y-1 overflow-y-auto"
|
|
414
|
+
aria-label="UI components"
|
|
415
|
+
>
|
|
416
|
+
<NuxtLink
|
|
417
|
+
v-for="component in uiComponents"
|
|
418
|
+
:key="component.id"
|
|
419
|
+
:aria-current="activeComponent?.id === component.id ? 'page' : void 0"
|
|
420
|
+
:class="[
|
|
421
|
+
'block rounded-lg px-3 py-2.5 text-sm transition',
|
|
422
|
+
activeComponent?.id === component.id ? 'bg-main-900 text-main-100' : 'text-plain-400 hover:bg-plain-900 hover:text-plain-100'
|
|
423
|
+
]"
|
|
424
|
+
:to="{
|
|
425
|
+
path: '/ui',
|
|
426
|
+
query: {
|
|
427
|
+
...route.query,
|
|
428
|
+
component: component.id
|
|
429
|
+
}
|
|
430
|
+
}"
|
|
431
|
+
@click="closeNavigation"
|
|
432
|
+
>
|
|
433
|
+
{{ component.name }}
|
|
434
|
+
</NuxtLink>
|
|
435
|
+
</nav>
|
|
436
|
+
</aside>
|
|
437
|
+
</div>
|
|
438
|
+
|
|
439
|
+
<div class="mx-auto flex min-h-screen max-w-7xl">
|
|
440
|
+
<aside class="hidden w-64 shrink-0 border-r border-plain-800 px-4 py-6 md:block">
|
|
441
|
+
<p class="px-3 text-xs font-medium tracking-widest text-plain-500 uppercase">Components</p>
|
|
442
|
+
|
|
443
|
+
<nav
|
|
444
|
+
class="sticky top-2 mt-4 space-y-1"
|
|
445
|
+
aria-label="UI components"
|
|
446
|
+
>
|
|
447
|
+
<NuxtLink
|
|
448
|
+
v-for="component in uiComponents"
|
|
449
|
+
:key="component.id"
|
|
450
|
+
:aria-current="activeComponent?.id === component.id ? 'page' : void 0"
|
|
451
|
+
:class="[
|
|
452
|
+
'block rounded-lg px-3 py-2 text-sm transition',
|
|
453
|
+
activeComponent?.id === component.id ? 'bg-main-900 text-main-100' : 'text-plain-400 hover:bg-plain-900 hover:text-plain-100'
|
|
454
|
+
]"
|
|
455
|
+
:to="{
|
|
456
|
+
path: '/ui',
|
|
457
|
+
query: {
|
|
458
|
+
...route.query,
|
|
459
|
+
component: component.id
|
|
460
|
+
}
|
|
461
|
+
}"
|
|
462
|
+
>
|
|
463
|
+
{{ component.name }}
|
|
464
|
+
</NuxtLink>
|
|
465
|
+
</nav>
|
|
466
|
+
</aside>
|
|
467
|
+
|
|
468
|
+
<section class="min-w-0 flex-1 px-4 py-6 sm:px-6 md:px-10">
|
|
469
|
+
<template v-if="activeComponent">
|
|
470
|
+
<div class="flex items-center gap-3">
|
|
471
|
+
<h1 class="text-2xl leading-1 font-semibold sm:text-3xl">{{ activeComponent.name }}</h1>
|
|
472
|
+
<button
|
|
473
|
+
v-if="uiThemeEnabled"
|
|
474
|
+
type="button"
|
|
475
|
+
class="flex size-7 shrink-0 cursor-pointer items-center justify-center rounded-lg border border-plain-700 text-plain-400 transition hover:border-main-700 hover:bg-main-900 hover:text-main-200 focus-visible:outline-2 focus-visible:outline-offset-2"
|
|
476
|
+
:aria-label="isDark ? 'Switch to light theme' : 'Switch to dark theme'"
|
|
477
|
+
:title="isDark ? 'Switch to light theme' : 'Switch to dark theme'"
|
|
478
|
+
@click="toggleTheme"
|
|
479
|
+
>
|
|
480
|
+
<svg
|
|
481
|
+
v-if="isDark"
|
|
482
|
+
class="size-3"
|
|
483
|
+
aria-hidden="true"
|
|
484
|
+
fill="none"
|
|
485
|
+
stroke="currentColor"
|
|
486
|
+
stroke-linecap="round"
|
|
487
|
+
stroke-linejoin="round"
|
|
488
|
+
stroke-width="2"
|
|
489
|
+
viewBox="0 0 24 24"
|
|
490
|
+
>
|
|
491
|
+
<circle
|
|
492
|
+
cx="12"
|
|
493
|
+
cy="12"
|
|
494
|
+
r="4"
|
|
495
|
+
/>
|
|
496
|
+
<path
|
|
497
|
+
d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41"
|
|
498
|
+
/>
|
|
499
|
+
</svg>
|
|
500
|
+
<svg
|
|
501
|
+
v-else
|
|
502
|
+
class="size-3"
|
|
503
|
+
aria-hidden="true"
|
|
504
|
+
fill="none"
|
|
505
|
+
stroke="currentColor"
|
|
506
|
+
stroke-linecap="round"
|
|
507
|
+
stroke-linejoin="round"
|
|
508
|
+
stroke-width="2"
|
|
509
|
+
viewBox="0 0 24 24"
|
|
510
|
+
>
|
|
511
|
+
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79Z" />
|
|
512
|
+
</svg>
|
|
513
|
+
</button>
|
|
514
|
+
</div>
|
|
515
|
+
<section
|
|
516
|
+
v-for="demo in activeComponent.demos"
|
|
517
|
+
:key="demo.title"
|
|
518
|
+
class="mt-6 overflow-hidden rounded-2xl border border-plain-800 bg-plain-900/50 first:mt-8"
|
|
519
|
+
>
|
|
520
|
+
<header
|
|
521
|
+
class="flex items-start justify-between gap-3 border-b border-plain-800 bg-plain-900 px-4 py-3 sm:gap-4 sm:px-8 sm:py-4"
|
|
522
|
+
>
|
|
523
|
+
<div>
|
|
524
|
+
<h2 class="text-base font-medium text-plain-100">{{ demo.title }}</h2>
|
|
525
|
+
<p
|
|
526
|
+
v-if="demo.description"
|
|
527
|
+
class="mt-1 text-sm text-plain-400"
|
|
528
|
+
>
|
|
529
|
+
{{ demo.description }}
|
|
530
|
+
</p>
|
|
531
|
+
</div>
|
|
532
|
+
<button
|
|
533
|
+
type="button"
|
|
534
|
+
:aria-label="isDemoCodeExpanded(demo.id) ? 'Hide code' : 'View code'"
|
|
535
|
+
:class="[
|
|
536
|
+
'flex size-6 shrink-0 cursor-pointer items-center justify-center rounded-md border transition focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-main-400',
|
|
537
|
+
isDemoCodeExpanded(demo.id) ? 'border-main-700 bg-main-900 text-main-200' : 'border-plain-700 text-plain-400 hover:border-main-700 hover:text-main-200'
|
|
538
|
+
]"
|
|
539
|
+
:title="isDemoCodeExpanded(demo.id) ? 'Hide code' : 'View code'"
|
|
540
|
+
@click="toggleDemoCode(demo.id)"
|
|
541
|
+
>
|
|
542
|
+
<svg
|
|
543
|
+
class="size-4"
|
|
544
|
+
aria-hidden="true"
|
|
545
|
+
fill="none"
|
|
546
|
+
stroke="currentColor"
|
|
547
|
+
stroke-linecap="round"
|
|
548
|
+
stroke-linejoin="round"
|
|
549
|
+
stroke-width="2"
|
|
550
|
+
viewBox="0 0 24 24"
|
|
551
|
+
>
|
|
552
|
+
<path d="m8 9-3 3 3 3" />
|
|
553
|
+
<path d="m16 9 3 3-3 3" />
|
|
554
|
+
<path d="m14 5-4 14" />
|
|
555
|
+
</svg>
|
|
556
|
+
</button>
|
|
557
|
+
</header>
|
|
558
|
+
<div class="p-4 sm:p-8">
|
|
559
|
+
<component :is="demo.component" />
|
|
560
|
+
</div>
|
|
561
|
+
<div
|
|
562
|
+
v-if="isDemoCodeExpanded(demo.id)"
|
|
563
|
+
class="border-t border-plain-800 bg-plain-950/80 p-4 sm:p-8"
|
|
564
|
+
>
|
|
565
|
+
<pre
|
|
566
|
+
class="overflow-x-auto text-sm leading-6 text-plain-300"
|
|
567
|
+
><code v-html="highlightDemoCode(demo.code.trim())" /></pre>
|
|
568
|
+
</div>
|
|
569
|
+
</section>
|
|
570
|
+
<p
|
|
571
|
+
v-if="activeComponent.demos.length === 0"
|
|
572
|
+
class="mt-8 text-plain-400"
|
|
573
|
+
>
|
|
574
|
+
Add a
|
|
575
|
+
<code class="text-plain-200">*.demo.vue</code>
|
|
576
|
+
file to this component folder.
|
|
577
|
+
</p>
|
|
578
|
+
<section class="mt-8 overflow-hidden rounded-2xl border border-plain-800 bg-plain-900/50">
|
|
579
|
+
<header class="border-b border-plain-800 bg-plain-900 px-4 py-3 sm:px-8 sm:py-4">
|
|
580
|
+
<h2 class="text-base font-medium text-plain-100">Component API</h2>
|
|
581
|
+
</header>
|
|
582
|
+
|
|
583
|
+
<div class="grid divide-y divide-plain-800 lg:grid-cols-2 lg:divide-x lg:divide-y-0">
|
|
584
|
+
<section class="px-4 py-5 sm:px-6">
|
|
585
|
+
<h3 class="text-sm font-medium text-plain-200">Props</h3>
|
|
586
|
+
<div class="mt-4 overflow-x-auto">
|
|
587
|
+
<table class="w-full min-w-80 text-left text-sm sm:min-w-96">
|
|
588
|
+
<!-- <thead class="text-xs tracking-wider text-plain-500 uppercase">
|
|
589
|
+
<tr>
|
|
590
|
+
<th class="pb-3 font-medium">Name</th>
|
|
591
|
+
<th class="pb-3 font-medium">Type</th>
|
|
592
|
+
<th class="pb-3 text-right font-medium">Required</th>
|
|
593
|
+
</tr>
|
|
594
|
+
</thead> -->
|
|
595
|
+
<tbody class="divide-y divide-plain-800 text-plain-300">
|
|
596
|
+
<tr
|
|
597
|
+
v-for="prop in activeComponent.props"
|
|
598
|
+
:key="prop.name"
|
|
599
|
+
>
|
|
600
|
+
<td class="py-3 pr-4 font-mono text-plain-400">{{ prop.name }}</td>
|
|
601
|
+
<td
|
|
602
|
+
class="py-3 pr-4 font-mono text-xs break-all"
|
|
603
|
+
v-html="highlightPropType(prop.type)"
|
|
604
|
+
/>
|
|
605
|
+
<td
|
|
606
|
+
class="py-3 text-right text-xs"
|
|
607
|
+
:class="prop.required ? 'text-danger-600' : 'text-plain-500'"
|
|
608
|
+
>
|
|
609
|
+
{{ prop.required ? "*" : "-" }}
|
|
610
|
+
</td>
|
|
611
|
+
</tr>
|
|
612
|
+
<tr v-if="activeComponent.props.length === 0">
|
|
613
|
+
<td
|
|
614
|
+
colspan="3"
|
|
615
|
+
class="py-3 text-plain-500"
|
|
616
|
+
>
|
|
617
|
+
No props declared.
|
|
618
|
+
</td>
|
|
619
|
+
</tr>
|
|
620
|
+
</tbody>
|
|
621
|
+
</table>
|
|
622
|
+
</div>
|
|
623
|
+
</section>
|
|
624
|
+
|
|
625
|
+
<section class="px-4 py-5 sm:px-6">
|
|
626
|
+
<h3 class="text-sm font-medium text-plain-200">Slots</h3>
|
|
627
|
+
<div
|
|
628
|
+
v-if="activeComponent.slots.length"
|
|
629
|
+
class="mt-4 flex flex-wrap gap-2"
|
|
630
|
+
>
|
|
631
|
+
<code
|
|
632
|
+
v-for="slot in activeComponent.slots"
|
|
633
|
+
:key="slot"
|
|
634
|
+
class="rounded-md border border-plain-800 bg-plain-950 px-2.5 py-1.5 text-xs"
|
|
635
|
+
:class="slot === 'default' ? 'text-plain-500' : 'text-plain-400'"
|
|
636
|
+
>
|
|
637
|
+
{{ slot }}
|
|
638
|
+
</code>
|
|
639
|
+
</div>
|
|
640
|
+
<p
|
|
641
|
+
v-else
|
|
642
|
+
class="mt-4 text-sm text-plain-500"
|
|
643
|
+
>
|
|
644
|
+
No slots detected.
|
|
645
|
+
</p>
|
|
646
|
+
</section>
|
|
647
|
+
</div>
|
|
648
|
+
</section>
|
|
649
|
+
|
|
650
|
+
<section
|
|
651
|
+
v-if="activeComponent.styles.length"
|
|
652
|
+
class="mt-6 overflow-hidden rounded-2xl border border-plain-800 bg-plain-900/50"
|
|
653
|
+
>
|
|
654
|
+
<header
|
|
655
|
+
class="flex items-center justify-between gap-3 border-b border-plain-800 bg-plain-900 px-4 py-3 sm:gap-4 sm:px-8 sm:py-4"
|
|
656
|
+
>
|
|
657
|
+
<div class="flex items-center gap-3">
|
|
658
|
+
<div>
|
|
659
|
+
<h2 class="text-base font-medium text-plain-100">UI styles</h2>
|
|
660
|
+
</div>
|
|
661
|
+
</div>
|
|
662
|
+
<span class="rounded-full border border-plain-700 px-2.5 py-1 text-xs text-plain-400">
|
|
663
|
+
{{ activeComponent.styles.length }} fields
|
|
664
|
+
</span>
|
|
665
|
+
</header>
|
|
666
|
+
|
|
667
|
+
<div class="px-4 py-2 sm:px-8">
|
|
668
|
+
<div class="divide-y divide-plain-800">
|
|
669
|
+
<div
|
|
670
|
+
v-for="style in visibleStyles"
|
|
671
|
+
:key="style.path"
|
|
672
|
+
class="grid gap-2 py-4 sm:gap-6"
|
|
673
|
+
:class="$style.styleGrid"
|
|
674
|
+
>
|
|
675
|
+
<code class="font-mono text-xs text-main-300">{{ style.path }}</code>
|
|
676
|
+
<code
|
|
677
|
+
class="font-mono text-xs wrap-break-word text-plain-300"
|
|
678
|
+
:class="$style.styleValue"
|
|
679
|
+
v-html="highlightStyleValue(style.value)"
|
|
680
|
+
/>
|
|
681
|
+
</div>
|
|
682
|
+
</div>
|
|
683
|
+
</div>
|
|
684
|
+
|
|
685
|
+
<footer
|
|
686
|
+
v-if="activeComponent.styles.length > 5"
|
|
687
|
+
class="sticky bottom-0 z-10 border-t border-plain-800 bg-plain-900/95 px-4 py-3 backdrop-blur sm:px-8"
|
|
688
|
+
>
|
|
689
|
+
<button
|
|
690
|
+
type="button"
|
|
691
|
+
class="flex w-full cursor-pointer items-center justify-center text-sm font-medium text-info-500 transition hover:text-info-400 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-info-400"
|
|
692
|
+
@click="stylesExpanded = !stylesExpanded"
|
|
693
|
+
>
|
|
694
|
+
{{ stylesExpanded ? "Collapse styles" : `Show all ${activeComponent.styles.length} styles` }}
|
|
695
|
+
</button>
|
|
696
|
+
</footer>
|
|
697
|
+
</section>
|
|
698
|
+
</template>
|
|
699
|
+
|
|
700
|
+
<p
|
|
701
|
+
v-else
|
|
702
|
+
class="text-plain-400"
|
|
703
|
+
>
|
|
704
|
+
Add
|
|
705
|
+
<code class="text-plain-200">Component/index.vue</code>
|
|
706
|
+
and a
|
|
707
|
+
<code class="text-plain-200">*.demo.vue</code>
|
|
708
|
+
file to show it here.
|
|
709
|
+
</p>
|
|
710
|
+
</section>
|
|
711
|
+
</div>
|
|
712
|
+
</main>
|
|
713
|
+
</template>
|
|
714
|
+
|
|
715
|
+
<style module>
|
|
716
|
+
:global(.ui-code-comment){color:var(--color-plain-500)}:global(.ui-code-string){color:var(--color-win-400)}:global(.ui-code-keyword){color:var(--color-main-400)}:global(.ui-code-number){color:var(--color-warn-300)}:global(.ui-code-tag){color:var(--color-info-500)}:global(.ui-code-attribute){color:var(--color-alt-300)}:global(.ui-code-punctuation){color:var(--color-plain-400)}:global(.ui-color-swatch){background:var(--ui-color-swatch);border:1px solid hsla(0,0%,100%,.1);border-radius:.2rem;display:inline-block;height:.65rem;margin-right:.25rem;opacity:.85;vertical-align:-.1rem;width:.65rem}.mobileNavigation{width:min(18rem,calc(100vw - 3rem))}@media (width >= 40rem){.styleGrid{grid-template-columns:minmax(12rem,.8fr) minmax(0,1.8fr)}}.styleValue :global(.ui-code-keyword){color:var(--color-plain-500)}.styleValue :global(.ui-code-attribute){color:var(--color-plain-200)}.styleValue :global(.ui-code-string){color:var(--color-plain-300)}.styleValue :global(.ui-code-number){color:var(--color-plain-400)}.styleValue :global(.ui-code-punctuation){color:var(--color-plain-500)}
|
|
717
|
+
</style>
|