@uniweb/core 0.5.10 → 0.5.11
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/package.json +3 -2
- package/src/block.js +58 -19
- package/src/website.js +13 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uniweb/core",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.11",
|
|
4
4
|
"description": "Core classes for the Uniweb platform - Uniweb, Website, Page, Block",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -30,7 +30,8 @@
|
|
|
30
30
|
"jest": "^29.7.0"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@uniweb/semantic-parser": "1.1.6"
|
|
33
|
+
"@uniweb/semantic-parser": "1.1.6",
|
|
34
|
+
"@uniweb/theming": "0.1.1"
|
|
34
35
|
},
|
|
35
36
|
"scripts": {
|
|
36
37
|
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js"
|
package/src/block.js
CHANGED
|
@@ -6,21 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { parseContent as parseSemanticContent } from '@uniweb/semantic-parser'
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Resolve bare palette references to var() in theme overrides.
|
|
12
|
-
* Allows content authors to write `primary: neutral-900` in frontmatter
|
|
13
|
-
* instead of `primary: var(--neutral-900)`.
|
|
14
|
-
*/
|
|
15
|
-
const SHADE_LEVELS = new Set([50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950])
|
|
16
|
-
|
|
17
|
-
function resolveOverrideValue(value) {
|
|
18
|
-
if (typeof value !== 'string' || value.includes('(') || value.startsWith('#')) return value
|
|
19
|
-
const bare = value.replace(/^-{0,2}/, '')
|
|
20
|
-
const match = bare.match(/^([a-z][a-z0-9]*)-(\d+)$/)
|
|
21
|
-
if (match && SHADE_LEVELS.has(parseInt(match[2], 10))) return `var(--${bare})`
|
|
22
|
-
return value
|
|
23
|
-
}
|
|
9
|
+
import { normalizeTokenValue } from '@uniweb/theming'
|
|
24
10
|
|
|
25
11
|
export default class Block {
|
|
26
12
|
constructor(blockData, id, page) {
|
|
@@ -58,20 +44,26 @@ export default class Block {
|
|
|
58
44
|
|
|
59
45
|
// Normalize theme: supports string ("light") or object ({ mode, ...tokenOverrides })
|
|
60
46
|
// Resolve bare palette refs (e.g. "primary: neutral-900" → var(--neutral-900))
|
|
47
|
+
//
|
|
48
|
+
// themeName values:
|
|
49
|
+
// '' (empty) = Auto — section inherits from site's appearance/scheme
|
|
50
|
+
// 'light' = Pinned to light context
|
|
51
|
+
// 'medium' = Pinned to dim context
|
|
52
|
+
// 'dark' = Pinned to dark context
|
|
61
53
|
const rawTheme = blockConfig.theme
|
|
62
54
|
if (rawTheme && typeof rawTheme === 'object') {
|
|
63
55
|
const { mode, ...overrides } = rawTheme
|
|
64
|
-
this.themeName = mode || '
|
|
56
|
+
this.themeName = mode || ''
|
|
65
57
|
if (Object.keys(overrides).length > 0) {
|
|
66
58
|
for (const key of Object.keys(overrides)) {
|
|
67
|
-
overrides[key] =
|
|
59
|
+
overrides[key] = normalizeTokenValue(overrides[key])
|
|
68
60
|
}
|
|
69
61
|
this.contextOverrides = overrides
|
|
70
62
|
} else {
|
|
71
63
|
this.contextOverrides = null
|
|
72
64
|
}
|
|
73
65
|
} else {
|
|
74
|
-
this.themeName = rawTheme
|
|
66
|
+
this.themeName = rawTheme ?? ''
|
|
75
67
|
this.contextOverrides = null
|
|
76
68
|
}
|
|
77
69
|
|
|
@@ -149,6 +141,10 @@ export default class Block {
|
|
|
149
141
|
// Context (static, defined per component type)
|
|
150
142
|
this.context = null
|
|
151
143
|
|
|
144
|
+
// Component-level CSS variables (merged meta.js defaults + frontmatter overrides)
|
|
145
|
+
// Populated by initComponent() — context-independent, emitted on #section-{id}
|
|
146
|
+
this.componentVars = null
|
|
147
|
+
|
|
152
148
|
Object.seal(this)
|
|
153
149
|
}
|
|
154
150
|
|
|
@@ -176,6 +172,11 @@ export default class Block {
|
|
|
176
172
|
return this.extractFromProseMirror(content)
|
|
177
173
|
}
|
|
178
174
|
|
|
175
|
+
// Wrapped ProseMirror document (Content API format: { doc: { type: "doc", ... } })
|
|
176
|
+
if (content?.doc?.type === 'doc') {
|
|
177
|
+
return this.extractFromProseMirror(content.doc)
|
|
178
|
+
}
|
|
179
|
+
|
|
179
180
|
// Plain object content — pass through directly.
|
|
180
181
|
// guaranteeContentStructure() in prepare-props will fill in missing fields.
|
|
181
182
|
if (content && typeof content === 'object' && !Array.isArray(content)) {
|
|
@@ -284,6 +285,12 @@ export default class Block {
|
|
|
284
285
|
// Source: meta.js context field
|
|
285
286
|
this.context = meta.context ? { ...meta.context } : null
|
|
286
287
|
|
|
288
|
+
// Merge component-level CSS vars: meta.js defaults + frontmatter overrides
|
|
289
|
+
// Source: meta.js vars field (defaults), section frontmatter vars: key (overrides)
|
|
290
|
+
if (meta.vars) {
|
|
291
|
+
this.componentVars = Block.mergeComponentVars(meta.vars, this.properties.vars)
|
|
292
|
+
}
|
|
293
|
+
|
|
287
294
|
return this.Component
|
|
288
295
|
}
|
|
289
296
|
|
|
@@ -434,6 +441,38 @@ export default class Block {
|
|
|
434
441
|
return this.dynamicContext
|
|
435
442
|
}
|
|
436
443
|
|
|
444
|
+
/**
|
|
445
|
+
* Merge component-level CSS variable defaults with frontmatter overrides.
|
|
446
|
+
*
|
|
447
|
+
* Schema vars (from meta.js) can be:
|
|
448
|
+
* - String shorthand: 'card-gap': '1.5rem' → { default: '1.5rem' }
|
|
449
|
+
* - Object: 'card-gap': { default: '1.5rem', label: 'Card Gap' }
|
|
450
|
+
*
|
|
451
|
+
* @param {Object} schemaVars - Var definitions from meta.js
|
|
452
|
+
* @param {Object} [frontmatterVars] - Overrides from section frontmatter
|
|
453
|
+
* @returns {Object} Flat { name: value } object for CSS emission
|
|
454
|
+
*/
|
|
455
|
+
static mergeComponentVars(schemaVars, frontmatterVars = {}) {
|
|
456
|
+
const merged = {}
|
|
457
|
+
|
|
458
|
+
for (const [name, config] of Object.entries(schemaVars)) {
|
|
459
|
+
const defaultVal = typeof config === 'string' ? config : config?.default
|
|
460
|
+
if (defaultVal != null) {
|
|
461
|
+
merged[name] = defaultVal
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
if (frontmatterVars && typeof frontmatterVars === 'object') {
|
|
466
|
+
for (const [name, value] of Object.entries(frontmatterVars)) {
|
|
467
|
+
if (value != null && name in schemaVars) {
|
|
468
|
+
merged[name] = String(value)
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
return Object.keys(merged).length > 0 ? merged : null
|
|
474
|
+
}
|
|
475
|
+
|
|
437
476
|
/**
|
|
438
477
|
* Normalize a background value from section frontmatter
|
|
439
478
|
*
|
|
@@ -464,7 +503,7 @@ export default class Block {
|
|
|
464
503
|
|
|
465
504
|
// Anything else → CSS color (hex, rgb, hsl, oklch, named color, var())
|
|
466
505
|
// Resolve bare palette refs (e.g. "primary-900" → "var(--primary-900)")
|
|
467
|
-
return { mode: 'color', color:
|
|
506
|
+
return { mode: 'color', color: normalizeTokenValue(raw) }
|
|
468
507
|
}
|
|
469
508
|
|
|
470
509
|
// Object with explicit mode — pass through
|
package/src/website.js
CHANGED
|
@@ -577,12 +577,19 @@ export default class Website {
|
|
|
577
577
|
* makeHref('/about') // → '/about' (passthrough)
|
|
578
578
|
*/
|
|
579
579
|
makeHref(href) {
|
|
580
|
-
if (!href
|
|
580
|
+
if (!href) return href
|
|
581
|
+
|
|
582
|
+
// Support both page: (current) and topic: (legacy) prefixes
|
|
583
|
+
let withoutPrefix
|
|
584
|
+
if (href.startsWith('page:')) {
|
|
585
|
+
withoutPrefix = href.slice(5)
|
|
586
|
+
} else if (href.startsWith('topic:')) {
|
|
587
|
+
withoutPrefix = href.slice(6)
|
|
588
|
+
} else {
|
|
581
589
|
return href
|
|
582
590
|
}
|
|
583
591
|
|
|
584
592
|
// Parse page reference: page:pageId#sectionId
|
|
585
|
-
const withoutPrefix = href.slice(5) // Remove 'page:'
|
|
586
593
|
const [pageId, sectionId] = withoutPrefix.split('#')
|
|
587
594
|
|
|
588
595
|
// Look up page by ID (explicit or route-based)
|
|
@@ -878,6 +885,10 @@ export default class Website {
|
|
|
878
885
|
if (navType === 'footer' && page.hideInFooter) return false
|
|
879
886
|
}
|
|
880
887
|
|
|
888
|
+
// Skip empty folders (no content) that have no visible children.
|
|
889
|
+
// Folders with children still appear as dropdown parents.
|
|
890
|
+
if (!page.hasContent() && !page.children?.some(isPageVisible)) return false
|
|
891
|
+
|
|
881
892
|
// Apply custom filter if provided
|
|
882
893
|
if (customFilter && !customFilter(page)) return false
|
|
883
894
|
|