@uniweb/core 0.4.8 → 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.
Files changed (3) hide show
  1. package/package.json +1 -1
  2. package/src/page.js +31 -90
  3. package/src/website.js +49 -43
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniweb/core",
3
- "version": "0.4.8",
3
+ "version": "0.5.0",
4
4
  "description": "Core classes for the Uniweb platform - Uniweb, Website, Page, Block",
5
5
  "type": "module",
6
6
  "exports": {
package/src/page.js CHANGED
@@ -26,13 +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 (named layout + per-page overrides for header/footer/panels)
29
+ // Layout options (named layout + per-page overrides)
30
30
  this.layout = {
31
31
  name: pageData.layout?.name || null,
32
- header: pageData.layout?.header !== false,
33
- footer: pageData.layout?.footer !== false,
34
- leftPanel: pageData.layout?.leftPanel !== false,
35
- rightPanel: pageData.layout?.rightPanel !== false,
32
+ hide: pageData.layout?.hide || [],
33
+ params: pageData.layout?.params || {},
36
34
  }
37
35
 
38
36
  // SEO configuration
@@ -155,15 +153,12 @@ export default class Page {
155
153
 
156
154
  /**
157
155
  * Get all block groups (for Layout component)
158
- * @returns {Object} { header, body, footer, left, right }
156
+ * @returns {Object} { body, ...areas }
159
157
  */
160
158
  getBlockGroups() {
161
159
  return {
162
- header: this.getHeaderBlocks(),
163
160
  body: this.bodyBlocks,
164
- footer: this.getFooterBlocks(),
165
- left: this.getLeftBlocks(),
166
- right: this.getRightBlocks(),
161
+ ...this.getLayoutAreas(),
167
162
  }
168
163
  }
169
164
 
@@ -208,13 +203,14 @@ export default class Page {
208
203
 
209
204
  /**
210
205
  * Get all blocks (header, body, footer) as flat array
211
- * Respects page layout preferences (hasHeader, hasFooter, etc.)
206
+ * Respects page layout preferences (hide list)
212
207
  * @returns {Block[]}
213
208
  */
214
209
  getPageBlocks() {
215
210
  const blocks = []
216
- const headerBlocks = this.getHeaderBlocks()
217
- const footerBlocks = this.getFooterBlocks()
211
+ const areas = this.getLayoutAreas()
212
+ const headerBlocks = areas.header
213
+ const footerBlocks = areas.footer
218
214
 
219
215
  if (headerBlocks) blocks.push(...headerBlocks)
220
216
  blocks.push(...this.bodyBlocks)
@@ -232,69 +228,46 @@ export default class Page {
232
228
  }
233
229
 
234
230
  /**
235
- * Get header blocks (respects layout preference, delegates to website)
231
+ * Get blocks for a specific area, respecting hide list
232
+ * @param {string} areaName - Area name (e.g., 'header', 'footer', 'left')
236
233
  * @returns {Block[]|null}
237
234
  */
238
- getHeaderBlocks() {
239
- if (!this.hasHeader()) return null
240
- return this.website.getHeaderBlocks(this.getLayoutName())
235
+ getAreaBlocks(areaName) {
236
+ if (this.layout.hide.includes(areaName)) return null
237
+ return this.website.getAreaBlocks(areaName, this.getLayoutName())
241
238
  }
242
239
 
243
240
  /**
244
- * Get footer blocks (respects layout preference, delegates to website)
245
- * @returns {Block[]|null}
246
- */
247
- getFooterBlocks() {
248
- if (!this.hasFooter()) return null
249
- return this.website.getFooterBlocks(this.getLayoutName())
250
- }
251
-
252
- /**
253
- * Get left panel blocks (respects layout preference, delegates to website)
254
- * @returns {Block[]|null}
241
+ * Get all areas for this page's layout, excluding hidden areas
242
+ * @returns {Object} Map of areaName -> Block[]
255
243
  */
256
- getLeftBlocks() {
257
- if (!this.hasLeftPanel()) return null
258
- return this.website.getLeftBlocks(this.getLayoutName())
259
- }
260
-
261
- /**
262
- * Get right panel blocks (respects layout preference, delegates to website)
263
- * @returns {Block[]|null}
264
- */
265
- getRightBlocks() {
266
- if (!this.hasRightPanel()) return null
267
- return this.website.getRightBlocks(this.getLayoutName())
268
- }
269
-
270
- /**
271
- * Get header block (legacy - returns first block)
272
- * @returns {Block|null}
273
- * @deprecated Use getHeaderBlocks() instead
274
- */
275
- getHeader() {
276
- 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
277
253
  }
278
254
 
279
255
  /**
280
- * Get footer block (legacy - returns first block)
281
- * @returns {Block|null}
282
- * @deprecated Use getFooterBlocks() instead
256
+ * Get layout params for this page (merged with defaults at render time)
257
+ * @returns {Object}
283
258
  */
284
- getFooter() {
285
- return this.getFooterBlocks()?.[0] || null
259
+ getLayoutParams() {
260
+ return this.layout.params
286
261
  }
287
262
 
288
263
  /**
289
264
  * Reset block states (for scroll restoration)
290
265
  */
291
266
  resetBlockStates() {
267
+ const areas = this.getLayoutAreas()
292
268
  const allBlocks = [
293
- ...(this.getHeaderBlocks() || []),
294
269
  ...this.bodyBlocks,
295
- ...(this.getFooterBlocks() || []),
296
- ...(this.getLeftBlocks() || []),
297
- ...(this.getRightBlocks() || []),
270
+ ...Object.values(areas).flat(),
298
271
  ]
299
272
 
300
273
  for (const block of allBlocks) {
@@ -350,38 +323,6 @@ export default class Page {
350
323
  return !this.hidden && !this.hideInFooter
351
324
  }
352
325
 
353
- /**
354
- * Check if header should be rendered on this page
355
- * @returns {boolean}
356
- */
357
- hasHeader() {
358
- return this.layout.header
359
- }
360
-
361
- /**
362
- * Check if footer should be rendered on this page
363
- * @returns {boolean}
364
- */
365
- hasFooter() {
366
- return this.layout.footer
367
- }
368
-
369
- /**
370
- * Check if left panel should be rendered on this page
371
- * @returns {boolean}
372
- */
373
- hasLeftPanel() {
374
- return this.layout.leftPanel
375
- }
376
-
377
- /**
378
- * Check if right panel should be rendered on this page
379
- * @returns {boolean}
380
- */
381
- hasRightPanel() {
382
- return this.layout.rightPanel
383
- }
384
-
385
326
  /**
386
327
  * Check if page has child pages
387
328
  * @returns {boolean}
package/src/website.js CHANGED
@@ -11,35 +11,26 @@ import singularize from './singularize.js'
11
11
 
12
12
  export default class Website {
13
13
  constructor(websiteData) {
14
- const { pages = [], theme = {}, config = {}, layouts, header, footer, left, right, notFound, versionedScopes = {} } = websiteData
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
- // Named layout section sets (when site has layout subdirectories)
21
+ // General area storage: { layoutName: { areaName: Page } }
22
+ this._layoutSets = {}
22
23
  if (layouts && typeof layouts === 'object') {
23
- this._layoutSets = {}
24
- for (const [name, panelData] of Object.entries(layouts)) {
25
- this._layoutSets[name] = {
26
- headerPage: panelData.header ? new Page(panelData.header, `layout-${name}-header`, this) : null,
27
- footerPage: panelData.footer ? new Page(panelData.footer, `layout-${name}-footer`, this) : null,
28
- leftPage: panelData.left ? new Page(panelData.left, `layout-${name}-left`, this) : null,
29
- rightPage: panelData.right ? new Page(panelData.right, `layout-${name}-right`, this) : null,
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
30
  }
31
31
  }
32
- } else {
33
- this._layoutSets = null
34
32
  }
35
33
 
36
- // Layout panels as Page objects (header, footer, left, right)
37
- // Flat fields for backward compatibility (used when no named layouts)
38
- this.headerPage = header ? new Page(header, 'header', this) : null
39
- this.footerPage = footer ? new Page(footer, 'footer', this) : null
40
- this.leftPage = left ? new Page(left, 'left', this) : null
41
- this.rightPage = right ? new Page(right, 'right', this) : null
42
-
43
34
  // Store 404 page (for SPA routing)
44
35
  // Convention: pages/404/ directory
45
36
  this.notFoundPage = notFound || pages.find((p) => p.route === '/404') || null
@@ -477,13 +468,11 @@ export default class Website {
477
468
  */
478
469
  getRemoteLayout(layoutName) {
479
470
  const config = globalThis.uniweb?.foundationConfig
480
- if (!config) return null
481
- // Named layouts map
482
- if (layoutName && config.layouts?.[layoutName]) {
471
+ if (!config?.layouts) return null
472
+ if (layoutName && config.layouts[layoutName]) {
483
473
  return config.layouts[layoutName]
484
474
  }
485
- // Single Layout (backward compat)
486
- return config.Layout || null
475
+ return null
487
476
  }
488
477
 
489
478
  /**
@@ -502,35 +491,52 @@ export default class Website {
502
491
  }
503
492
 
504
493
  // ─────────────────────────────────────────────────────────────────
505
- // Layout Blocks (from layout panel pages)
494
+ // Layout Areas (general named areas)
506
495
  // ─────────────────────────────────────────────────────────────────
507
496
 
508
- getHeaderBlocks(layoutName) {
509
- if (layoutName && this._layoutSets?.[layoutName]) {
510
- return this._layoutSets[layoutName].headerPage?.bodyBlocks || null
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
511
506
  }
512
- return this.headerPage?.bodyBlocks || null
513
- }
514
-
515
- getFooterBlocks(layoutName) {
516
- if (layoutName && this._layoutSets?.[layoutName]) {
517
- return this._layoutSets[layoutName].footerPage?.bodyBlocks || null
507
+ // Fallback to 'default' layout
508
+ if (this._layoutSets.default) {
509
+ return this._layoutSets.default[areaName]?.bodyBlocks || null
518
510
  }
519
- return this.footerPage?.bodyBlocks || null
511
+ return null
520
512
  }
521
513
 
522
- getLeftBlocks(layoutName) {
523
- if (layoutName && this._layoutSets?.[layoutName]) {
524
- return this._layoutSets[layoutName].leftPage?.bodyBlocks || null
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
+ }
525
529
  }
526
- return this.leftPage?.bodyBlocks || null
530
+ return areas
527
531
  }
528
532
 
529
- getRightBlocks(layoutName) {
530
- if (layoutName && this._layoutSets?.[layoutName]) {
531
- return this._layoutSets[layoutName].rightPage?.bodyBlocks || null
532
- }
533
- return this.rightPage?.bodyBlocks || null
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
534
540
  }
535
541
 
536
542
  /**