@uniweb/core 0.4.7 → 0.5.0
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 +1 -1
- package/src/page.js +41 -90
- package/src/website.js +69 -20
package/package.json
CHANGED
package/src/page.js
CHANGED
|
@@ -26,12 +26,11 @@ export default class Page {
|
|
|
26
26
|
this.hideInHeader = pageData.hideInHeader || false
|
|
27
27
|
this.hideInFooter = pageData.hideInFooter || false
|
|
28
28
|
|
|
29
|
-
// Layout options (per-page overrides
|
|
29
|
+
// Layout options (named layout + per-page overrides)
|
|
30
30
|
this.layout = {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
rightPanel: pageData.layout?.rightPanel !== false,
|
|
31
|
+
name: pageData.layout?.name || null,
|
|
32
|
+
hide: pageData.layout?.hide || [],
|
|
33
|
+
params: pageData.layout?.params || {},
|
|
35
34
|
}
|
|
36
35
|
|
|
37
36
|
// SEO configuration
|
|
@@ -143,17 +142,23 @@ export default class Page {
|
|
|
143
142
|
return this.dynamicContext?.paramValue || null
|
|
144
143
|
}
|
|
145
144
|
|
|
145
|
+
/**
|
|
146
|
+
* Get the resolved layout name for this page.
|
|
147
|
+
* Cascade: page.layout.name > foundation defaultLayout > null
|
|
148
|
+
* @returns {string|null}
|
|
149
|
+
*/
|
|
150
|
+
getLayoutName() {
|
|
151
|
+
return this.layout.name || this.website?.getDefaultLayoutName() || null
|
|
152
|
+
}
|
|
153
|
+
|
|
146
154
|
/**
|
|
147
155
|
* Get all block groups (for Layout component)
|
|
148
|
-
* @returns {Object} {
|
|
156
|
+
* @returns {Object} { body, ...areas }
|
|
149
157
|
*/
|
|
150
158
|
getBlockGroups() {
|
|
151
159
|
return {
|
|
152
|
-
header: this.getHeaderBlocks(),
|
|
153
160
|
body: this.bodyBlocks,
|
|
154
|
-
|
|
155
|
-
left: this.getLeftBlocks(),
|
|
156
|
-
right: this.getRightBlocks(),
|
|
161
|
+
...this.getLayoutAreas(),
|
|
157
162
|
}
|
|
158
163
|
}
|
|
159
164
|
|
|
@@ -198,13 +203,14 @@ export default class Page {
|
|
|
198
203
|
|
|
199
204
|
/**
|
|
200
205
|
* Get all blocks (header, body, footer) as flat array
|
|
201
|
-
* Respects page layout preferences (
|
|
206
|
+
* Respects page layout preferences (hide list)
|
|
202
207
|
* @returns {Block[]}
|
|
203
208
|
*/
|
|
204
209
|
getPageBlocks() {
|
|
205
210
|
const blocks = []
|
|
206
|
-
const
|
|
207
|
-
const
|
|
211
|
+
const areas = this.getLayoutAreas()
|
|
212
|
+
const headerBlocks = areas.header
|
|
213
|
+
const footerBlocks = areas.footer
|
|
208
214
|
|
|
209
215
|
if (headerBlocks) blocks.push(...headerBlocks)
|
|
210
216
|
blocks.push(...this.bodyBlocks)
|
|
@@ -222,69 +228,46 @@ export default class Page {
|
|
|
222
228
|
}
|
|
223
229
|
|
|
224
230
|
/**
|
|
225
|
-
* Get
|
|
231
|
+
* Get blocks for a specific area, respecting hide list
|
|
232
|
+
* @param {string} areaName - Area name (e.g., 'header', 'footer', 'left')
|
|
226
233
|
* @returns {Block[]|null}
|
|
227
234
|
*/
|
|
228
|
-
|
|
229
|
-
if (
|
|
230
|
-
return this.website.
|
|
235
|
+
getAreaBlocks(areaName) {
|
|
236
|
+
if (this.layout.hide.includes(areaName)) return null
|
|
237
|
+
return this.website.getAreaBlocks(areaName, this.getLayoutName())
|
|
231
238
|
}
|
|
232
239
|
|
|
233
240
|
/**
|
|
234
|
-
* Get
|
|
235
|
-
* @returns {Block[]
|
|
241
|
+
* Get all areas for this page's layout, excluding hidden areas
|
|
242
|
+
* @returns {Object} Map of areaName -> Block[]
|
|
236
243
|
*/
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
getLeftBlocks() {
|
|
247
|
-
if (!this.hasLeftPanel()) return null
|
|
248
|
-
return this.website.getLeftBlocks()
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* Get right panel blocks (respects layout preference, delegates to website)
|
|
253
|
-
* @returns {Block[]|null}
|
|
254
|
-
*/
|
|
255
|
-
getRightBlocks() {
|
|
256
|
-
if (!this.hasRightPanel()) return null
|
|
257
|
-
return this.website.getRightBlocks()
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* Get header block (legacy - returns first block)
|
|
262
|
-
* @returns {Block|null}
|
|
263
|
-
* @deprecated Use getHeaderBlocks() instead
|
|
264
|
-
*/
|
|
265
|
-
getHeader() {
|
|
266
|
-
return this.getHeaderBlocks()?.[0] || null
|
|
244
|
+
getLayoutAreas() {
|
|
245
|
+
const allAreas = this.website.getLayoutAreas(this.getLayoutName())
|
|
246
|
+
const result = {}
|
|
247
|
+
for (const [name, blocks] of Object.entries(allAreas)) {
|
|
248
|
+
if (!this.layout.hide.includes(name)) {
|
|
249
|
+
result[name] = blocks
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return result
|
|
267
253
|
}
|
|
268
254
|
|
|
269
255
|
/**
|
|
270
|
-
* Get
|
|
271
|
-
* @returns {
|
|
272
|
-
* @deprecated Use getFooterBlocks() instead
|
|
256
|
+
* Get layout params for this page (merged with defaults at render time)
|
|
257
|
+
* @returns {Object}
|
|
273
258
|
*/
|
|
274
|
-
|
|
275
|
-
return this.
|
|
259
|
+
getLayoutParams() {
|
|
260
|
+
return this.layout.params
|
|
276
261
|
}
|
|
277
262
|
|
|
278
263
|
/**
|
|
279
264
|
* Reset block states (for scroll restoration)
|
|
280
265
|
*/
|
|
281
266
|
resetBlockStates() {
|
|
267
|
+
const areas = this.getLayoutAreas()
|
|
282
268
|
const allBlocks = [
|
|
283
|
-
...(this.getHeaderBlocks() || []),
|
|
284
269
|
...this.bodyBlocks,
|
|
285
|
-
...(
|
|
286
|
-
...(this.getLeftBlocks() || []),
|
|
287
|
-
...(this.getRightBlocks() || []),
|
|
270
|
+
...Object.values(areas).flat(),
|
|
288
271
|
]
|
|
289
272
|
|
|
290
273
|
for (const block of allBlocks) {
|
|
@@ -340,38 +323,6 @@ export default class Page {
|
|
|
340
323
|
return !this.hidden && !this.hideInFooter
|
|
341
324
|
}
|
|
342
325
|
|
|
343
|
-
/**
|
|
344
|
-
* Check if header should be rendered on this page
|
|
345
|
-
* @returns {boolean}
|
|
346
|
-
*/
|
|
347
|
-
hasHeader() {
|
|
348
|
-
return this.layout.header
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
/**
|
|
352
|
-
* Check if footer should be rendered on this page
|
|
353
|
-
* @returns {boolean}
|
|
354
|
-
*/
|
|
355
|
-
hasFooter() {
|
|
356
|
-
return this.layout.footer
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
/**
|
|
360
|
-
* Check if left panel should be rendered on this page
|
|
361
|
-
* @returns {boolean}
|
|
362
|
-
*/
|
|
363
|
-
hasLeftPanel() {
|
|
364
|
-
return this.layout.leftPanel
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
/**
|
|
368
|
-
* Check if right panel should be rendered on this page
|
|
369
|
-
* @returns {boolean}
|
|
370
|
-
*/
|
|
371
|
-
hasRightPanel() {
|
|
372
|
-
return this.layout.rightPanel
|
|
373
|
-
}
|
|
374
|
-
|
|
375
326
|
/**
|
|
376
327
|
* Check if page has child pages
|
|
377
328
|
* @returns {boolean}
|
package/src/website.js
CHANGED
|
@@ -11,19 +11,25 @@ import singularize from './singularize.js'
|
|
|
11
11
|
|
|
12
12
|
export default class Website {
|
|
13
13
|
constructor(websiteData) {
|
|
14
|
-
const { pages = [], theme = {}, config = {},
|
|
14
|
+
const { pages = [], theme = {}, config = {}, layouts, notFound, versionedScopes = {} } = websiteData
|
|
15
15
|
|
|
16
16
|
// Site metadata
|
|
17
17
|
this.name = config.name || ''
|
|
18
18
|
this.description = config.description || ''
|
|
19
19
|
this.url = config.url || ''
|
|
20
20
|
|
|
21
|
-
//
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
// General area storage: { layoutName: { areaName: Page } }
|
|
22
|
+
this._layoutSets = {}
|
|
23
|
+
if (layouts && typeof layouts === 'object') {
|
|
24
|
+
for (const [name, areaData] of Object.entries(layouts)) {
|
|
25
|
+
this._layoutSets[name] = {}
|
|
26
|
+
for (const [areaName, pageData] of Object.entries(areaData)) {
|
|
27
|
+
if (pageData) {
|
|
28
|
+
this._layoutSets[name][areaName] = new Page(pageData, `layout-${name}-${areaName}`, this)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
27
33
|
|
|
28
34
|
// Store 404 page (for SPA routing)
|
|
29
35
|
// Convention: pages/404/ directory
|
|
@@ -458,9 +464,23 @@ export default class Website {
|
|
|
458
464
|
|
|
459
465
|
/**
|
|
460
466
|
* Get remote layout component from foundation config
|
|
467
|
+
* @param {string|null} layoutName - Named layout to look up (null = default)
|
|
461
468
|
*/
|
|
462
|
-
getRemoteLayout() {
|
|
463
|
-
|
|
469
|
+
getRemoteLayout(layoutName) {
|
|
470
|
+
const config = globalThis.uniweb?.foundationConfig
|
|
471
|
+
if (!config?.layouts) return null
|
|
472
|
+
if (layoutName && config.layouts[layoutName]) {
|
|
473
|
+
return config.layouts[layoutName]
|
|
474
|
+
}
|
|
475
|
+
return null
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Get default layout name from foundation config
|
|
480
|
+
* @returns {string|null}
|
|
481
|
+
*/
|
|
482
|
+
getDefaultLayoutName() {
|
|
483
|
+
return globalThis.uniweb?.foundationConfig?.defaultLayout || null
|
|
464
484
|
}
|
|
465
485
|
|
|
466
486
|
/**
|
|
@@ -471,23 +491,52 @@ export default class Website {
|
|
|
471
491
|
}
|
|
472
492
|
|
|
473
493
|
// ─────────────────────────────────────────────────────────────────
|
|
474
|
-
// Layout
|
|
494
|
+
// Layout Areas (general named areas)
|
|
475
495
|
// ─────────────────────────────────────────────────────────────────
|
|
476
496
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
497
|
+
/**
|
|
498
|
+
* Get blocks for a specific area, with layout name resolution
|
|
499
|
+
* @param {string} areaName - Area name (e.g., 'header', 'footer', 'left', 'sidebar')
|
|
500
|
+
* @param {string} [layoutName] - Named layout to look in (falls back to 'default')
|
|
501
|
+
* @returns {Block[]|null}
|
|
502
|
+
*/
|
|
503
|
+
getAreaBlocks(areaName, layoutName) {
|
|
504
|
+
if (layoutName && this._layoutSets[layoutName]) {
|
|
505
|
+
return this._layoutSets[layoutName][areaName]?.bodyBlocks || null
|
|
506
|
+
}
|
|
507
|
+
// Fallback to 'default' layout
|
|
508
|
+
if (this._layoutSets.default) {
|
|
509
|
+
return this._layoutSets.default[areaName]?.bodyBlocks || null
|
|
510
|
+
}
|
|
511
|
+
return null
|
|
483
512
|
}
|
|
484
513
|
|
|
485
|
-
|
|
486
|
-
|
|
514
|
+
/**
|
|
515
|
+
* Get all areas for a layout as { areaName: Block[] }
|
|
516
|
+
* @param {string} [layoutName] - Named layout (falls back to 'default')
|
|
517
|
+
* @returns {Object} Map of areaName -> Block[]
|
|
518
|
+
*/
|
|
519
|
+
getLayoutAreas(layoutName) {
|
|
520
|
+
const setName = layoutName || 'default'
|
|
521
|
+
const layoutSet = this._layoutSets[setName] || this._layoutSets.default
|
|
522
|
+
if (!layoutSet) return {}
|
|
523
|
+
|
|
524
|
+
const areas = {}
|
|
525
|
+
for (const [areaName, page] of Object.entries(layoutSet)) {
|
|
526
|
+
if (page?.bodyBlocks) {
|
|
527
|
+
areas[areaName] = page.bodyBlocks
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
return areas
|
|
487
531
|
}
|
|
488
532
|
|
|
489
|
-
|
|
490
|
-
|
|
533
|
+
/**
|
|
534
|
+
* Get layout metadata from foundation config
|
|
535
|
+
* @param {string} layoutName - Layout name
|
|
536
|
+
* @returns {Object|null} Layout meta { areas, transitions, defaults }
|
|
537
|
+
*/
|
|
538
|
+
getLayoutMeta(layoutName) {
|
|
539
|
+
return globalThis.uniweb?.foundationConfig?.layoutMeta?.[layoutName] || null
|
|
491
540
|
}
|
|
492
541
|
|
|
493
542
|
/**
|