@edgedev/create-edge-app 1.2.33 → 1.2.34
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/deploy.sh +77 -0
- package/edge/components/cms/block.vue +228 -18
- package/edge/components/cms/blockApi.vue +3 -3
- package/edge/components/cms/blockEditor.vue +374 -85
- package/edge/components/cms/blockPicker.vue +29 -3
- package/edge/components/cms/blockRender.vue +3 -3
- package/edge/components/cms/blocksManager.vue +755 -82
- package/edge/components/cms/codeEditor.vue +15 -6
- package/edge/components/cms/fontUpload.vue +318 -2
- package/edge/components/cms/htmlContent.vue +230 -89
- package/edge/components/cms/menu.vue +5 -4
- package/edge/components/cms/page.vue +750 -21
- package/edge/components/cms/site.vue +624 -84
- package/edge/components/cms/sitesManager.vue +5 -4
- package/edge/components/cms/themeEditor.vue +196 -162
- package/edge/components/editor.vue +5 -1
- package/edge/composables/global.ts +37 -5
- package/edge/composables/useCmsNewDocs.js +100 -0
- package/edge/composables/useEdgeCmsDialogPositionFix.js +19 -0
- package/edge/routes/cms/dashboard/blocks/[block].vue +5 -0
- package/edge/routes/cms/dashboard/blocks/index.vue +12 -1
- package/edge/routes/cms/dashboard/media/index.vue +5 -0
- package/edge/routes/cms/dashboard/sites/[site]/[[page]].vue +4 -0
- package/edge/routes/cms/dashboard/sites/[site].vue +4 -0
- package/edge/routes/cms/dashboard/sites/index.vue +4 -0
- package/edge/routes/cms/dashboard/templates/[page].vue +4 -0
- package/edge/routes/cms/dashboard/templates/index.vue +4 -0
- package/edge/routes/cms/dashboard/themes/[theme].vue +5 -0
- package/edge/routes/cms/dashboard/themes/index.vue +330 -1
- package/firebase.json +4 -0
- package/nuxt.config.ts +1 -1
- package/package.json +2 -2
|
@@ -35,6 +35,44 @@ const emit = defineEmits(['loaded'])
|
|
|
35
35
|
|
|
36
36
|
const scopeId = `hc-${Math.random().toString(36).slice(2)}`
|
|
37
37
|
|
|
38
|
+
const themeExtraCSS = computed(() => {
|
|
39
|
+
const value = props.theme?.extraCSS
|
|
40
|
+
return typeof value === 'string' ? value : ''
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
const toCssVarToken = (key) => {
|
|
44
|
+
return String(key || '')
|
|
45
|
+
.trim()
|
|
46
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
|
|
47
|
+
.replace(/[\s_]+/g, '-')
|
|
48
|
+
.replace(/[^a-zA-Z0-9-]/g, '-')
|
|
49
|
+
.replace(/-+/g, '-')
|
|
50
|
+
.replace(/^-|-$/g, '')
|
|
51
|
+
.toLowerCase()
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const isSafeLegacyVarKey = key => /^[A-Za-z0-9_-]+$/.test(String(key || ''))
|
|
55
|
+
|
|
56
|
+
const pushVarDecl = (decls, prefix, key, value) => {
|
|
57
|
+
const token = toCssVarToken(key)
|
|
58
|
+
if (!token)
|
|
59
|
+
return
|
|
60
|
+
|
|
61
|
+
const normalizedName = `--${prefix}-${token}`
|
|
62
|
+
decls.push(`${normalizedName}: ${value};`)
|
|
63
|
+
|
|
64
|
+
const legacyKey = String(key || '')
|
|
65
|
+
if (!isSafeLegacyVarKey(legacyKey))
|
|
66
|
+
return
|
|
67
|
+
|
|
68
|
+
const legacyName = `--${prefix}-${legacyKey}`
|
|
69
|
+
if (legacyName !== normalizedName)
|
|
70
|
+
decls.push(`${legacyName}: ${value};`)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const cssVarRef = (prefix, key) => `var(--${prefix}-${toCssVarToken(key)})`
|
|
74
|
+
const escapeClassToken = value => String(value || '').replace(/([^a-zA-Z0-9_-])/g, '\\$1')
|
|
75
|
+
|
|
38
76
|
// --- UnoCSS Runtime singleton (global, one init for the whole app) ---
|
|
39
77
|
async function ensureUnoRuntime() {
|
|
40
78
|
if (typeof window === 'undefined')
|
|
@@ -89,24 +127,24 @@ function buildGlobalThemeCSS(theme) {
|
|
|
89
127
|
const t = normalizeTheme(theme || {})
|
|
90
128
|
const { colors, fontFamily, fontSize, borderRadius, boxShadow } = t
|
|
91
129
|
const decls = []
|
|
92
|
-
Object.entries(colors).forEach(([k, v]) => decls
|
|
130
|
+
Object.entries(colors).forEach(([k, v]) => pushVarDecl(decls, 'color', k, Array.isArray(v) ? v[0] : v))
|
|
93
131
|
Object.entries(fontFamily).forEach(([k, v]) => {
|
|
94
132
|
const val = Array.isArray(v) ? v.map(x => (x.includes(' ') ? `'${x}'` : x)).join(', ') : v
|
|
95
|
-
decls
|
|
133
|
+
pushVarDecl(decls, 'font', k, val)
|
|
96
134
|
})
|
|
97
135
|
Object.entries(fontSize).forEach(([k, v]) => {
|
|
98
136
|
if (Array.isArray(v)) {
|
|
99
137
|
const [size, opts] = v
|
|
100
|
-
decls
|
|
138
|
+
pushVarDecl(decls, 'font-size', k, size)
|
|
101
139
|
if (opts && opts.lineHeight)
|
|
102
|
-
decls
|
|
140
|
+
pushVarDecl(decls, 'line-height', k, opts.lineHeight)
|
|
103
141
|
}
|
|
104
142
|
else {
|
|
105
|
-
decls
|
|
143
|
+
pushVarDecl(decls, 'font-size', k, v)
|
|
106
144
|
}
|
|
107
145
|
})
|
|
108
|
-
Object.entries(borderRadius).forEach(([k, v]) => decls
|
|
109
|
-
Object.entries(boxShadow).forEach(([k, v]) => decls
|
|
146
|
+
Object.entries(borderRadius).forEach(([k, v]) => pushVarDecl(decls, 'radius', k, v))
|
|
147
|
+
Object.entries(boxShadow).forEach(([k, v]) => pushVarDecl(decls, 'shadow', k, v))
|
|
110
148
|
return `:root{${decls.join('')}}`
|
|
111
149
|
}
|
|
112
150
|
|
|
@@ -114,25 +152,34 @@ function buildScopedThemeCSS(theme, scopeId) {
|
|
|
114
152
|
const t = normalizeTheme(theme || {})
|
|
115
153
|
const { colors, fontFamily, fontSize, borderRadius, boxShadow } = t
|
|
116
154
|
const decls = []
|
|
117
|
-
Object.entries(colors).forEach(([k, v]) => decls
|
|
155
|
+
Object.entries(colors).forEach(([k, v]) => pushVarDecl(decls, 'color', k, Array.isArray(v) ? v[0] : v))
|
|
118
156
|
Object.entries(fontFamily).forEach(([k, v]) => {
|
|
119
157
|
const val = Array.isArray(v) ? v.map(x => (x.includes(' ') ? `'${x}'` : x)).join(', ') : v
|
|
120
|
-
decls
|
|
158
|
+
pushVarDecl(decls, 'font', k, val)
|
|
121
159
|
})
|
|
122
160
|
Object.entries(fontSize).forEach(([k, v]) => {
|
|
123
161
|
if (Array.isArray(v)) {
|
|
124
162
|
const [size, opts] = v
|
|
125
|
-
decls
|
|
163
|
+
pushVarDecl(decls, 'font-size', k, size)
|
|
126
164
|
if (opts?.lineHeight)
|
|
127
|
-
decls
|
|
165
|
+
pushVarDecl(decls, 'line-height', k, opts.lineHeight)
|
|
128
166
|
}
|
|
129
167
|
else {
|
|
130
|
-
decls
|
|
168
|
+
pushVarDecl(decls, 'font-size', k, v)
|
|
131
169
|
}
|
|
132
170
|
})
|
|
133
|
-
Object.entries(borderRadius).forEach(([k, v]) => decls
|
|
134
|
-
Object.entries(boxShadow).forEach(([k, v]) => decls
|
|
135
|
-
|
|
171
|
+
Object.entries(borderRadius).forEach(([k, v]) => pushVarDecl(decls, 'radius', k, v))
|
|
172
|
+
Object.entries(boxShadow).forEach(([k, v]) => pushVarDecl(decls, 'shadow', k, v))
|
|
173
|
+
|
|
174
|
+
const rules = [`[data-theme-scope="${scopeId}"]{${decls.join('')}}`]
|
|
175
|
+
Object.keys(colors || {}).forEach((key) => {
|
|
176
|
+
const escapedKey = escapeClassToken(key)
|
|
177
|
+
const colorRef = cssVarRef('color', key)
|
|
178
|
+
rules.push(`[data-theme-scope="${scopeId}"] .bg-${escapedKey}{background-color:${colorRef} !important;}`)
|
|
179
|
+
rules.push(`[data-theme-scope="${scopeId}"] .text-${escapedKey}{color:${colorRef} !important;}`)
|
|
180
|
+
rules.push(`[data-theme-scope="${scopeId}"] .border-${escapedKey}{border-color:${colorRef} !important;}`)
|
|
181
|
+
})
|
|
182
|
+
return rules.join('')
|
|
136
183
|
}
|
|
137
184
|
|
|
138
185
|
function setGlobalThemeVars(theme) {
|
|
@@ -167,11 +214,18 @@ const safeHtml = computed(() => {
|
|
|
167
214
|
})
|
|
168
215
|
|
|
169
216
|
// Inject theme CSS variables into <head> for SSR + client
|
|
170
|
-
useHead(() =>
|
|
171
|
-
style
|
|
172
|
-
{ id:
|
|
173
|
-
]
|
|
174
|
-
|
|
217
|
+
useHead(() => {
|
|
218
|
+
const style = [
|
|
219
|
+
{ id: `htmlcontent-theme-vars-${scopeId}`, children: buildScopedThemeCSS(props.theme, scopeId) },
|
|
220
|
+
]
|
|
221
|
+
if (themeExtraCSS.value.trim()) {
|
|
222
|
+
style.push({
|
|
223
|
+
id: `htmlcontent-theme-extra-${scopeId}`,
|
|
224
|
+
children: themeExtraCSS.value,
|
|
225
|
+
})
|
|
226
|
+
}
|
|
227
|
+
return { style }
|
|
228
|
+
})
|
|
175
229
|
|
|
176
230
|
// --- Embla initializer (runs client-side only) ---
|
|
177
231
|
async function initEmblaCarousels(scope) {
|
|
@@ -339,10 +393,106 @@ async function initEmblaCarousels(scope) {
|
|
|
339
393
|
})
|
|
340
394
|
}
|
|
341
395
|
|
|
396
|
+
function initCmsNavHelpers(scope) {
|
|
397
|
+
if (!scope || !import.meta.client)
|
|
398
|
+
return
|
|
399
|
+
|
|
400
|
+
const roots = scope.querySelectorAll('.cms-nav-root, [data-cms-nav-root]')
|
|
401
|
+
roots.forEach((root) => {
|
|
402
|
+
if (root.dataset.cmsNavInit === 'true')
|
|
403
|
+
return
|
|
404
|
+
|
|
405
|
+
root.dataset.cmsNavInit = 'true'
|
|
406
|
+
const openClass = root.getAttribute('data-cms-nav-open-class') || 'is-open'
|
|
407
|
+
const panel = root.querySelector('.cms-nav-panel, [data-cms-nav-panel]')
|
|
408
|
+
const overlay = root.querySelector('.cms-nav-overlay, [data-cms-nav-overlay]')
|
|
409
|
+
const toggles = Array.from(root.querySelectorAll('.cms-nav-toggle, [data-cms-nav-toggle]'))
|
|
410
|
+
const closeButtons = Array.from(root.querySelectorAll('.cms-nav-close, [data-cms-nav-close]'))
|
|
411
|
+
const links = Array.from(root.querySelectorAll('.cms-nav-link, [data-cms-nav-link]'))
|
|
412
|
+
const closeOnLink = root.getAttribute('data-cms-nav-close-on-link') !== 'false'
|
|
413
|
+
|
|
414
|
+
const markInteractive = (el) => {
|
|
415
|
+
if (!el)
|
|
416
|
+
return
|
|
417
|
+
el.setAttribute('data-cms-interactive', 'true')
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
toggles.forEach(markInteractive)
|
|
421
|
+
closeButtons.forEach(markInteractive)
|
|
422
|
+
links.forEach(markInteractive)
|
|
423
|
+
markInteractive(panel)
|
|
424
|
+
markInteractive(overlay)
|
|
425
|
+
|
|
426
|
+
const isOpen = () => root.classList.contains(openClass)
|
|
427
|
+
|
|
428
|
+
const setOpen = (open) => {
|
|
429
|
+
root.classList.toggle(openClass, open)
|
|
430
|
+
root.setAttribute('data-cms-nav-open', open ? 'true' : 'false')
|
|
431
|
+
|
|
432
|
+
toggles.forEach((btn) => {
|
|
433
|
+
btn.setAttribute('aria-expanded', open ? 'true' : 'false')
|
|
434
|
+
})
|
|
435
|
+
|
|
436
|
+
if (panel) {
|
|
437
|
+
panel.classList.toggle('translate-x-0', open)
|
|
438
|
+
panel.classList.toggle('opacity-100', open)
|
|
439
|
+
panel.classList.toggle('pointer-events-auto', open)
|
|
440
|
+
panel.classList.toggle('translate-x-full', !open)
|
|
441
|
+
panel.classList.toggle('opacity-0', !open)
|
|
442
|
+
panel.classList.toggle('pointer-events-none', !open)
|
|
443
|
+
panel.setAttribute('aria-hidden', open ? 'false' : 'true')
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
if (overlay) {
|
|
447
|
+
overlay.classList.toggle('opacity-100', open)
|
|
448
|
+
overlay.classList.toggle('pointer-events-auto', open)
|
|
449
|
+
overlay.classList.toggle('opacity-0', !open)
|
|
450
|
+
overlay.classList.toggle('pointer-events-none', !open)
|
|
451
|
+
overlay.setAttribute('aria-hidden', open ? 'false' : 'true')
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
setOpen(root.classList.contains(openClass) || root.getAttribute('data-cms-nav-open') === 'true')
|
|
456
|
+
|
|
457
|
+
toggles.forEach((btn) => {
|
|
458
|
+
btn.addEventListener('click', (event) => {
|
|
459
|
+
event.preventDefault()
|
|
460
|
+
event.stopPropagation()
|
|
461
|
+
setOpen(!isOpen())
|
|
462
|
+
})
|
|
463
|
+
})
|
|
464
|
+
|
|
465
|
+
closeButtons.forEach((btn) => {
|
|
466
|
+
btn.addEventListener('click', (event) => {
|
|
467
|
+
event.preventDefault()
|
|
468
|
+
event.stopPropagation()
|
|
469
|
+
setOpen(false)
|
|
470
|
+
})
|
|
471
|
+
})
|
|
472
|
+
|
|
473
|
+
if (overlay) {
|
|
474
|
+
overlay.addEventListener('click', (event) => {
|
|
475
|
+
event.preventDefault()
|
|
476
|
+
event.stopPropagation()
|
|
477
|
+
setOpen(false)
|
|
478
|
+
})
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
if (closeOnLink) {
|
|
482
|
+
links.forEach((link) => {
|
|
483
|
+
link.addEventListener('click', () => {
|
|
484
|
+
setOpen(false)
|
|
485
|
+
})
|
|
486
|
+
})
|
|
487
|
+
}
|
|
488
|
+
})
|
|
489
|
+
}
|
|
490
|
+
|
|
342
491
|
function renderSafeHtml(content) {
|
|
343
492
|
if (hostEl.value) {
|
|
344
493
|
// The HTML is already in the DOM via v-html; just (re)wire behaviors
|
|
345
494
|
initEmblaCarousels(hostEl.value)
|
|
495
|
+
initCmsNavHelpers(hostEl.value)
|
|
346
496
|
}
|
|
347
497
|
}
|
|
348
498
|
|
|
@@ -378,42 +528,7 @@ function setScopedThemeVars(scopeEl, theme) {
|
|
|
378
528
|
document.head.appendChild(styleEl)
|
|
379
529
|
}
|
|
380
530
|
|
|
381
|
-
|
|
382
|
-
const { colors, fontFamily, fontSize, borderRadius, boxShadow } = theme
|
|
383
|
-
|
|
384
|
-
const decls = []
|
|
385
|
-
// colors
|
|
386
|
-
Object.entries(colors).forEach(([k, v]) => {
|
|
387
|
-
decls.push(`--color-${k}: ${Array.isArray(v) ? v[0] : v};`)
|
|
388
|
-
})
|
|
389
|
-
// fonts
|
|
390
|
-
Object.entries(fontFamily).forEach(([k, v]) => {
|
|
391
|
-
const val = Array.isArray(v) ? v.map(x => (x.includes(' ') ? `'${x}'` : x)).join(', ') : v
|
|
392
|
-
decls.push(`--font-${k}: ${val};`)
|
|
393
|
-
})
|
|
394
|
-
// font sizes
|
|
395
|
-
Object.entries(fontSize).forEach(([k, v]) => {
|
|
396
|
-
if (Array.isArray(v)) {
|
|
397
|
-
const [size, opts] = v
|
|
398
|
-
decls.push(`--font-size-${k}: ${size};`)
|
|
399
|
-
if (opts && opts.lineHeight)
|
|
400
|
-
decls.push(`--line-height-${k}: ${opts.lineHeight};`)
|
|
401
|
-
}
|
|
402
|
-
else {
|
|
403
|
-
decls.push(`--font-size-${k}: ${v};`)
|
|
404
|
-
}
|
|
405
|
-
})
|
|
406
|
-
// radii
|
|
407
|
-
Object.entries(borderRadius).forEach(([k, v]) => {
|
|
408
|
-
decls.push(`--radius-${k}: ${v};`)
|
|
409
|
-
})
|
|
410
|
-
// shadows
|
|
411
|
-
Object.entries(boxShadow).forEach(([k, v]) => {
|
|
412
|
-
decls.push(`--shadow-${k}: ${v};`)
|
|
413
|
-
})
|
|
414
|
-
|
|
415
|
-
styleEl.textContent = `
|
|
416
|
-
[data-theme-scope="${scopeId}"]{${decls.join('')}}`
|
|
531
|
+
styleEl.textContent = buildScopedThemeCSS(theme, scopeId)
|
|
417
532
|
}
|
|
418
533
|
|
|
419
534
|
// Convert utility tokens like text-brand/bg-surface/rounded-xl/shadow-card
|
|
@@ -446,7 +561,7 @@ function toVarBackedUtilities(classList, theme) {
|
|
|
446
561
|
}
|
|
447
562
|
|
|
448
563
|
if (colorKeys.has(key)) {
|
|
449
|
-
const varRef =
|
|
564
|
+
const varRef = cssVarRef('color', key)
|
|
450
565
|
|
|
451
566
|
// no /opacity → plain var()
|
|
452
567
|
if (!opacity) {
|
|
@@ -477,7 +592,7 @@ function toVarBackedUtilities(classList, theme) {
|
|
|
477
592
|
if (radiusMatch) {
|
|
478
593
|
const key = radiusMatch[1]
|
|
479
594
|
if (radiusKeys.has(key))
|
|
480
|
-
return `rounded-[
|
|
595
|
+
return `rounded-[${cssVarRef('radius', key)}]`
|
|
481
596
|
return cls
|
|
482
597
|
}
|
|
483
598
|
|
|
@@ -486,23 +601,23 @@ function toVarBackedUtilities(classList, theme) {
|
|
|
486
601
|
if (shadowMatch) {
|
|
487
602
|
const key = shadowMatch[1]
|
|
488
603
|
if (shadowKeys.has(key))
|
|
489
|
-
return `shadow-[
|
|
604
|
+
return `shadow-[${cssVarRef('shadow', key)}]`
|
|
490
605
|
return cls
|
|
491
606
|
}
|
|
492
607
|
|
|
493
608
|
// font families via root apply, including custom keys like "brand"
|
|
494
609
|
if (cls === 'font-sans')
|
|
495
|
-
return
|
|
610
|
+
return `font-[${cssVarRef('font', 'sans')}]`
|
|
496
611
|
if (cls === 'font-serif')
|
|
497
|
-
return
|
|
612
|
+
return `font-[${cssVarRef('font', 'serif')}]`
|
|
498
613
|
if (cls === 'font-mono')
|
|
499
|
-
return
|
|
614
|
+
return `font-[${cssVarRef('font', 'mono')}]`
|
|
500
615
|
|
|
501
616
|
const ffMatch = /^font-([\w-]+)$/.exec(cls)
|
|
502
617
|
if (ffMatch) {
|
|
503
618
|
const key = ffMatch[1]
|
|
504
619
|
if (Object.prototype.hasOwnProperty.call(tokens.fontFamily, key))
|
|
505
|
-
return `font-[
|
|
620
|
+
return `font-[${cssVarRef('font', key)}]`
|
|
506
621
|
}
|
|
507
622
|
|
|
508
623
|
return cls
|
|
@@ -510,6 +625,35 @@ function toVarBackedUtilities(classList, theme) {
|
|
|
510
625
|
.join(' ')
|
|
511
626
|
}
|
|
512
627
|
|
|
628
|
+
function readElementClass(el) {
|
|
629
|
+
if (!el)
|
|
630
|
+
return ''
|
|
631
|
+
if (typeof el.className === 'string')
|
|
632
|
+
return el.className
|
|
633
|
+
if (el.className && typeof el.className.baseVal === 'string')
|
|
634
|
+
return el.className.baseVal
|
|
635
|
+
return el.getAttribute('class') || ''
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
function writeElementClass(el, nextClass = '') {
|
|
639
|
+
if (!el)
|
|
640
|
+
return
|
|
641
|
+
const normalized = typeof nextClass === 'string' ? nextClass : String(nextClass || '')
|
|
642
|
+
if (typeof el.className === 'string') {
|
|
643
|
+
el.className = normalized
|
|
644
|
+
return
|
|
645
|
+
}
|
|
646
|
+
el.setAttribute('class', normalized)
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
function appendElementClasses(el, classList) {
|
|
650
|
+
const additions = typeof classList === 'string' ? classList.trim() : ''
|
|
651
|
+
if (!additions)
|
|
652
|
+
return
|
|
653
|
+
const base = readElementClass(el)
|
|
654
|
+
writeElementClass(el, `${base} ${additions}`.trim())
|
|
655
|
+
}
|
|
656
|
+
|
|
513
657
|
function applyThemeClasses(scopeEl, theme, variant = 'light', isolated = true) {
|
|
514
658
|
if (!scopeEl)
|
|
515
659
|
return
|
|
@@ -529,7 +673,7 @@ function applyThemeClasses(scopeEl, theme, variant = 'light', isolated = true) {
|
|
|
529
673
|
if (apply.root) {
|
|
530
674
|
const mapped = toVarBackedUtilities(apply.root, t)
|
|
531
675
|
if (isolated) {
|
|
532
|
-
scopeEl
|
|
676
|
+
writeElementClass(scopeEl, `block-content ${mapped}`.trim())
|
|
533
677
|
}
|
|
534
678
|
else {
|
|
535
679
|
const applied = (scopeEl.dataset.themeRootClasses || '').split(/\s+/).filter(Boolean)
|
|
@@ -547,22 +691,22 @@ function applyThemeClasses(scopeEl, theme, variant = 'light', isolated = true) {
|
|
|
547
691
|
// Optional convenience: map a few generic applies
|
|
548
692
|
if (apply.link) {
|
|
549
693
|
scopeEl.querySelectorAll('a').forEach((el) => {
|
|
550
|
-
el
|
|
694
|
+
appendElementClasses(el, toVarBackedUtilities(apply.link, t))
|
|
551
695
|
})
|
|
552
696
|
}
|
|
553
697
|
if (apply.heading) {
|
|
554
698
|
scopeEl.querySelectorAll('h1,h2,h3,h4,h5,h6').forEach((el) => {
|
|
555
|
-
el
|
|
699
|
+
appendElementClasses(el, toVarBackedUtilities(apply.heading, t))
|
|
556
700
|
})
|
|
557
701
|
}
|
|
558
702
|
if (apply.button) {
|
|
559
703
|
scopeEl.querySelectorAll('button,[data-theme="button"]').forEach((el) => {
|
|
560
|
-
el
|
|
704
|
+
appendElementClasses(el, toVarBackedUtilities(apply.button, t))
|
|
561
705
|
})
|
|
562
706
|
}
|
|
563
707
|
if (apply.badge) {
|
|
564
708
|
scopeEl.querySelectorAll('[data-theme="badge"]').forEach((el) => {
|
|
565
|
-
el
|
|
709
|
+
appendElementClasses(el, toVarBackedUtilities(apply.badge, t))
|
|
566
710
|
})
|
|
567
711
|
}
|
|
568
712
|
|
|
@@ -573,7 +717,7 @@ function applyThemeClasses(scopeEl, theme, variant = 'light', isolated = true) {
|
|
|
573
717
|
Object.entries(obj).forEach(([part, classes]) => {
|
|
574
718
|
const sel = `[data-slot="${slotBase}.${part}"]`
|
|
575
719
|
scopeEl.querySelectorAll(sel).forEach((el) => {
|
|
576
|
-
el
|
|
720
|
+
appendElementClasses(el, toVarBackedUtilities(classes, t))
|
|
577
721
|
})
|
|
578
722
|
})
|
|
579
723
|
}
|
|
@@ -628,6 +772,7 @@ function rewriteAllClasses(scopeEl, theme, isolated = true, viewportMode = 'auto
|
|
|
628
772
|
const forcedWidth = viewportModeToWidth(viewportMode)
|
|
629
773
|
|
|
630
774
|
const TEXT_SIZE_RE = /^text-(xs|sm|base|lg|xl|\d+xl)$/
|
|
775
|
+
const FONT_UTILITY_RE = /^font-([\w-]+|\[[^\]]+\])$/
|
|
631
776
|
|
|
632
777
|
const mapToken = (token) => {
|
|
633
778
|
const parts = token.split(':')
|
|
@@ -654,7 +799,8 @@ function rewriteAllClasses(scopeEl, theme, isolated = true, viewportMode = 'auto
|
|
|
654
799
|
|
|
655
800
|
const mappedCore = toVarBackedUtilities(core, theme)
|
|
656
801
|
const isTextSize = TEXT_SIZE_RE.test(nakedCore)
|
|
657
|
-
const
|
|
802
|
+
const isFontUtility = FONT_UTILITY_RE.test(nakedCore)
|
|
803
|
+
const shouldImportant = hadBreakpoint || isTextSize || isFontUtility
|
|
658
804
|
const finalCore = shouldImportant ? importantify(mappedCore) : mappedCore
|
|
659
805
|
|
|
660
806
|
return [...nextParts, finalCore].filter(Boolean).join(':')
|
|
@@ -691,7 +837,10 @@ function rewriteAllClasses(scopeEl, theme, isolated = true, viewportMode = 'auto
|
|
|
691
837
|
return ''
|
|
692
838
|
|
|
693
839
|
const mappedCore = toVarBackedUtilities(core, theme)
|
|
694
|
-
const
|
|
840
|
+
const isTextSize = TEXT_SIZE_RE.test(nakedCore)
|
|
841
|
+
const isFontUtility = FONT_UTILITY_RE.test(nakedCore)
|
|
842
|
+
const shouldImportant = hadBreakpoint || isTextSize || isFontUtility
|
|
843
|
+
const finalCore = shouldImportant ? importantify(mappedCore) : mappedCore
|
|
695
844
|
|
|
696
845
|
return [...nextParts, finalCore].filter(Boolean).join(':')
|
|
697
846
|
}
|
|
@@ -699,15 +848,7 @@ function rewriteAllClasses(scopeEl, theme, isolated = true, viewportMode = 'auto
|
|
|
699
848
|
scopeEl.querySelectorAll('[class]').forEach((el) => {
|
|
700
849
|
let base = el.dataset.viewportBaseClass
|
|
701
850
|
if (typeof base !== 'string') {
|
|
702
|
-
|
|
703
|
-
base = el.className
|
|
704
|
-
}
|
|
705
|
-
else if (el.className && typeof el.className.baseVal === 'string') {
|
|
706
|
-
base = el.className.baseVal
|
|
707
|
-
}
|
|
708
|
-
else {
|
|
709
|
-
base = el.getAttribute('class') || ''
|
|
710
|
-
}
|
|
851
|
+
base = readElementClass(el)
|
|
711
852
|
el.dataset.viewportBaseClass = base
|
|
712
853
|
}
|
|
713
854
|
const orig = typeof base === 'string' ? base : String(base || '')
|
|
@@ -719,7 +860,7 @@ function rewriteAllClasses(scopeEl, theme, isolated = true, viewportMode = 'auto
|
|
|
719
860
|
.filter(Boolean)
|
|
720
861
|
if (isolated) {
|
|
721
862
|
const mapped = mappedTokens.join(' ')
|
|
722
|
-
el
|
|
863
|
+
writeElementClass(el, mapped)
|
|
723
864
|
return
|
|
724
865
|
}
|
|
725
866
|
|
|
@@ -742,10 +883,11 @@ onMounted(async () => {
|
|
|
742
883
|
|
|
743
884
|
// Initialize carousels/behaviors for SSR-inserted HTML
|
|
744
885
|
initEmblaCarousels(hostEl.value)
|
|
886
|
+
initCmsNavHelpers(hostEl.value)
|
|
745
887
|
|
|
746
888
|
// Apply global theme once (keeps one style tag for vars; blocks can still override locally if needed)
|
|
747
889
|
// setGlobalThemeVars(props.theme)
|
|
748
|
-
setScopedThemeVars(hostEl.value,
|
|
890
|
+
setScopedThemeVars(hostEl.value, props.theme)
|
|
749
891
|
// If you later need per-block overrides, keep the next line; otherwise, it can be omitted.
|
|
750
892
|
// setScopedThemeVars(hostEl.value, normalizeTheme(props.theme))
|
|
751
893
|
applyThemeClasses(hostEl.value, props.theme, (props.theme && props.theme.variant) || 'light')
|
|
@@ -761,8 +903,9 @@ watch(
|
|
|
761
903
|
// Wait for DOM to reflect new v-html, then (re)wire behaviors and class mappings
|
|
762
904
|
await nextTick()
|
|
763
905
|
initEmblaCarousels(hostEl.value)
|
|
906
|
+
initCmsNavHelpers(hostEl.value)
|
|
764
907
|
// setGlobalThemeVars(props.theme)
|
|
765
|
-
setScopedThemeVars(hostEl.value,
|
|
908
|
+
setScopedThemeVars(hostEl.value, props.theme)
|
|
766
909
|
|
|
767
910
|
applyThemeClasses(hostEl.value, props.theme, (props.theme && props.theme.variant) || 'light')
|
|
768
911
|
rewriteAllClasses(hostEl.value, props.theme, props.isolated, props.viewportMode)
|
|
@@ -774,13 +917,11 @@ watch(
|
|
|
774
917
|
watch(
|
|
775
918
|
() => props.theme,
|
|
776
919
|
async (val) => {
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
// setGlobalThemeVars(t)
|
|
780
|
-
setScopedThemeVars(hostEl.value, t)
|
|
920
|
+
// 1) Write scoped CSS variables from the raw theme object
|
|
921
|
+
setScopedThemeVars(hostEl.value, val)
|
|
781
922
|
// 2) Apply classes based on `apply`, `slots`, and optional variants
|
|
782
|
-
applyThemeClasses(hostEl.value,
|
|
783
|
-
rewriteAllClasses(hostEl.value,
|
|
923
|
+
applyThemeClasses(hostEl.value, val, (val && val.variant) || 'light')
|
|
924
|
+
rewriteAllClasses(hostEl.value, val, props.isolated, props.viewportMode)
|
|
784
925
|
await nextTick()
|
|
785
926
|
notifyLoaded()
|
|
786
927
|
},
|
|
@@ -960,13 +960,13 @@ const theme = computed(() => {
|
|
|
960
960
|
<SidebarMenuItem v-for="({ menu, name: menuName }) in orderedMenus" :key="menuName">
|
|
961
961
|
<SidebarMenuButton class="group !px-0 hover:!bg-transparent">
|
|
962
962
|
<FolderOpen
|
|
963
|
-
class="mr-2 group-hover:text-
|
|
963
|
+
class="mr-2 group-hover:text-foreground"
|
|
964
964
|
/>
|
|
965
|
-
<span v-if="!props.isTemplateSite" class="
|
|
965
|
+
<span v-if="!props.isTemplateSite" class="!text-foreground">{{ menuName === 'Site Root' ? 'Site Menu' : menuName }}</span>
|
|
966
966
|
<SidebarGroupAction class="absolute right-2 top-0 hover:!bg-transparent">
|
|
967
967
|
<DropdownMenu>
|
|
968
968
|
<DropdownMenuTrigger as-child>
|
|
969
|
-
<SidebarMenuAction>
|
|
969
|
+
<SidebarMenuAction class="hover:bg-primary text-foreground hover:text-primary-foreground">
|
|
970
970
|
<PlusIcon />
|
|
971
971
|
</SidebarMenuAction>
|
|
972
972
|
</DropdownMenuTrigger>
|
|
@@ -1029,6 +1029,7 @@ const theme = computed(() => {
|
|
|
1029
1029
|
:class="{ 'text-gray-400': element.item === '' }"
|
|
1030
1030
|
as-child
|
|
1031
1031
|
:is-active="!isExternalLinkEntry(element) && element.item === props.page"
|
|
1032
|
+
class="text-foreground hover:bg-primary hover:text-primary-foreground"
|
|
1032
1033
|
>
|
|
1033
1034
|
<NuxtLink
|
|
1034
1035
|
v-if="!isExternalLinkEntry(element)"
|
|
@@ -1056,7 +1057,7 @@ const theme = computed(() => {
|
|
|
1056
1057
|
<div class="absolute right-0 -top-0.5">
|
|
1057
1058
|
<DropdownMenu>
|
|
1058
1059
|
<DropdownMenuTrigger as-child>
|
|
1059
|
-
<SidebarMenuAction>
|
|
1060
|
+
<SidebarMenuAction class="hover:bg-primary text-foreground hover:text-primary-foreground">
|
|
1060
1061
|
<MoreHorizontal />
|
|
1061
1062
|
</SidebarMenuAction>
|
|
1062
1063
|
</DropdownMenuTrigger>
|