@uniweb/runtime 1.5.1 → 1.5.3
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/ssr.js +16 -8
- package/dist/ssr.js.map +1 -1
- package/package.json +4 -4
- package/src/components/Layout.jsx +7 -3
- package/src/ssr-renderer.js +7 -3
- package/src/sticky-top.js +2 -0
- package/src/under-header.js +45 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uniweb/runtime",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.3",
|
|
4
4
|
"description": "Minimal runtime for loading Uniweb foundations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -36,15 +36,15 @@
|
|
|
36
36
|
"node": ">=22.12"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@uniweb/core": "~1.6.
|
|
40
|
-
"@uniweb/theming": "~1.3.
|
|
39
|
+
"@uniweb/core": "~1.6.8",
|
|
40
|
+
"@uniweb/theming": "~1.3.2"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@vitejs/plugin-react": "^4.5.2",
|
|
44
44
|
"esbuild": "^0.21.0 || ^0.23.0 || ^0.24.0 || ^0.25.0 || ^0.27.0 || ^0.28.0",
|
|
45
45
|
"vite": "^7.3.1",
|
|
46
46
|
"vitest": "^4.1.7",
|
|
47
|
-
"@uniweb/build": "1.18.
|
|
47
|
+
"@uniweb/build": "1.18.1"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
50
|
"react": "^19.0.0",
|
|
@@ -35,7 +35,7 @@ import { useEffect } from 'react'
|
|
|
35
35
|
import Blocks from './Blocks.jsx'
|
|
36
36
|
import { resolveLayoutTransitions, resolveLayoutLayers, areaWrapperProps } from '../area-wrappers.js'
|
|
37
37
|
import { layoutStickiness, zoneProps } from '../sticky.js'
|
|
38
|
-
import { headerOverlay, overlayBoxProps } from '../under-header.js'
|
|
38
|
+
import { headerOverlay, overlayBoxProps, headerAreaProps } from '../under-header.js'
|
|
39
39
|
import { warnUnmovableSticky } from '../sticky-check.js'
|
|
40
40
|
import { watchStickyTop, bodySectionOf } from '../sticky-top.js'
|
|
41
41
|
// The built-in layout — one copy, which the prerender renders too (`../default-layout.js`).
|
|
@@ -102,8 +102,12 @@ export default function Layout({ page, website }) {
|
|
|
102
102
|
const overlay = headerOverlay(page)
|
|
103
103
|
|
|
104
104
|
const wrap = (region, children) => {
|
|
105
|
-
|
|
106
|
-
if (
|
|
105
|
+
const props = areaWrapperProps(region, transitions, layers, RemoteLayout ? sticky[region] : null)
|
|
106
|
+
if (RemoteLayout && region === 'header') {
|
|
107
|
+
// The same boxes on every page, the name on one that keeps the header's height.
|
|
108
|
+
const { outer, inner } = headerAreaProps(props, overlay)
|
|
109
|
+
return <div {...outer}>{inner ? <div {...inner}>{children}</div> : children}</div>
|
|
110
|
+
}
|
|
107
111
|
return props ? <div {...props}>{children}</div> : children
|
|
108
112
|
}
|
|
109
113
|
|
package/src/ssr-renderer.js
CHANGED
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
} from './wire-foundation.js'
|
|
35
35
|
import { resolveLayoutTransitions, resolveLayoutLayers, areaWrapperProps } from './area-wrappers.js'
|
|
36
36
|
import { layoutStickiness, sectionStickyProps, sectionScrollMargin, zoneProps } from './sticky.js'
|
|
37
|
-
import { headerOverlay, overlayBoxProps, openingUnderHeader, underHeaderProps } from './under-header.js'
|
|
37
|
+
import { headerOverlay, overlayBoxProps, openingUnderHeader, underHeaderProps, headerAreaProps } from './under-header.js'
|
|
38
38
|
import { DefaultLayout } from './default-layout.js'
|
|
39
39
|
import { renderAppearanceBootScript } from './appearance.js'
|
|
40
40
|
import { documentTitle } from './document-title.js'
|
|
@@ -274,8 +274,12 @@ export function renderLayout(page, website) {
|
|
|
274
274
|
// Mirror Layout.jsx: the header lies over a page's opening section under it (`under-header.js`).
|
|
275
275
|
const overlay = headerOverlay(page)
|
|
276
276
|
const wrapArea = (name, element) => {
|
|
277
|
-
|
|
278
|
-
if (
|
|
277
|
+
const props = areaWrapperProps(name, transitions, layers, RemoteLayout ? sticky[name] : null)
|
|
278
|
+
if (RemoteLayout && name === 'header') {
|
|
279
|
+
// Mirror Layout.jsx: the same boxes on every page, the name on one that keeps the header's height.
|
|
280
|
+
const { outer, inner } = headerAreaProps(props, overlay)
|
|
281
|
+
return React.createElement('div', outer, inner ? React.createElement('div', inner, element) : element)
|
|
282
|
+
}
|
|
279
283
|
return props ? React.createElement('div', props, element) : element
|
|
280
284
|
}
|
|
281
285
|
|
package/src/sticky-top.js
CHANGED
|
@@ -72,6 +72,8 @@ function drawnHeight(box) {
|
|
|
72
72
|
*/
|
|
73
73
|
export function measureHeaderOverlay(doc) {
|
|
74
74
|
const box = doc.querySelector('[data-uw-header="over"]')
|
|
75
|
+
// A page under the header whose layout drew no header has no box to measure, and the page's
|
|
76
|
+
// stylesheet pads its section by nothing (`underHeaderProps`).
|
|
75
77
|
return box ? Math.round(drawnHeight(box) * 100) / 100 : null
|
|
76
78
|
}
|
|
77
79
|
|
package/src/under-header.js
CHANGED
|
@@ -28,6 +28,9 @@ export const HEADER_HEIGHT = '--uw-header-height'
|
|
|
28
28
|
/** What the browser measured, on the root, which the opening section's value defers to. */
|
|
29
29
|
export const MEASURED_HEADER_HEIGHT = '--uw-header-measured'
|
|
30
30
|
|
|
31
|
+
/** The header types' declared height, on the opening section's wrapper: the value before measuring. */
|
|
32
|
+
export const DECLARED_HEADER_HEIGHT = '--uw-header-declared'
|
|
33
|
+
|
|
31
34
|
/**
|
|
32
35
|
* Whether the page's header lies over its opening section, and how tall the header is before the
|
|
33
36
|
* browser measures it: the `height` its sections' types declare, else `0px` until measured.
|
|
@@ -37,9 +40,8 @@ export const MEASURED_HEADER_HEIGHT = '--uw-header-measured'
|
|
|
37
40
|
*/
|
|
38
41
|
export function headerOverlay(page) {
|
|
39
42
|
const first = page?.getBodyBlocks?.()?.[0]
|
|
40
|
-
if (!first?.
|
|
41
|
-
const header = page.getLayoutAreas
|
|
42
|
-
if (!Array.isArray(header) || header.length === 0) return null
|
|
43
|
+
if (!first?.liesUnderHeader?.()) return null
|
|
44
|
+
const header = page.getLayoutAreas().header
|
|
43
45
|
const heights = header.map((block) => block?.height).filter(Boolean)
|
|
44
46
|
const height = heights.length === 0 ? '0px' : heights.length === 1 ? heights[0] : `calc(${heights.join(' + ')})`
|
|
45
47
|
return { height }
|
|
@@ -58,15 +60,52 @@ export function overlayBoxProps(props) {
|
|
|
58
60
|
}
|
|
59
61
|
|
|
60
62
|
/**
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
+
* The boxes a foundation layout's header area is drawn in, the same in both renderers.
|
|
64
|
+
*
|
|
65
|
+
* ⭐ THE SAME ELEMENTS ON EVERY PAGE. Between a page whose opening section lies under the header and
|
|
66
|
+
* one with none, only styles change, so React keeps the header mounted, with its state: a search
|
|
67
|
+
* box's words, a menu, an animation that ran once. So the area always has its wrapper — its layer
|
|
68
|
+
* and stickiness, collapsed on a page under the header, and `display: contents`, no box at all,
|
|
69
|
+
* where it has nothing to carry (a layout that ranks its own stacking and a header that neither
|
|
70
|
+
* sticks nor is named). ⛔ For part of 2026-11-06 a wrapper appeared only on a page under the
|
|
71
|
+
* header there, and React rebuilt the header on each move between the two kinds of page.
|
|
72
|
+
*
|
|
73
|
+
* ⭐ THE NAME A LAYOUT ASKS FOR GOES ON AN ELEMENT INSIDE THE WRAPPER, which keeps the header's
|
|
74
|
+
* height on every page. A named box is captured at its own size, and one with no height is captured
|
|
75
|
+
* as nothing, so on the collapsed wrapper the header was gone for the length of a page switch; the
|
|
76
|
+
* element inside is the header's extent everywhere, and two pages' headers pair as the layout means.
|
|
77
|
+
* ⛔ For part of 2026-11-06 a collapsed wrapper dropped the name instead, which quietly ignored what
|
|
78
|
+
* the layout asked for on exactly those pages.
|
|
79
|
+
*
|
|
80
|
+
* @param {Object|null} wrapper - the area wrapper's props (`areaWrapperProps`), its name included
|
|
81
|
+
* @param {{ height: string }|null} overlay - `headerOverlay(page)`
|
|
82
|
+
* @returns {{ outer: Object, inner: Object|null }} the wrapper's props, and the named element's
|
|
83
|
+
*/
|
|
84
|
+
export function headerAreaProps(wrapper, overlay) {
|
|
85
|
+
const { viewTransitionName, ...style } = wrapper?.style || {}
|
|
86
|
+
const inner = viewTransitionName ? { style: { viewTransitionName } } : null
|
|
87
|
+
const base = { ...(wrapper || {}), style }
|
|
88
|
+
if (overlay) return { outer: overlayBoxProps(base), inner }
|
|
89
|
+
return { outer: Object.keys(style).length ? base : { ...base, style: { display: 'contents' } }, inner }
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* What the opening section's wrapper adds when the header lies over it: the header type's declared
|
|
94
|
+
* height, and the mark the page's stylesheet keys on.
|
|
95
|
+
*
|
|
96
|
+
* ⭐ THE STYLESHEET GIVES THE SECTION ITS `--uw-header-height` (`@uniweb/theming`): what the browser
|
|
97
|
+
* measured, else the declared height, on a page where a header box lies over it
|
|
98
|
+
* (`[data-uw-header="over"]`), and `0px` on one where none does — a layout that draws no header on
|
|
99
|
+
* the page. A rule rather than this style, since only the rendered page holds that answer, and so
|
|
100
|
+
* the first paint, the prerender's included, pads by what is there. ⛔ Until 2026-11-06 the section
|
|
101
|
+
* was padded by the declared height whatever the layout drew, until the browser measured.
|
|
63
102
|
*
|
|
64
103
|
* @param {{ height: string }} overlay - `headerOverlay(page)`
|
|
65
104
|
* @returns {{ style: Object, 'data-uw-under-header': string }}
|
|
66
105
|
*/
|
|
67
106
|
export function underHeaderProps(overlay) {
|
|
68
107
|
return {
|
|
69
|
-
style: { [
|
|
108
|
+
style: { [DECLARED_HEADER_HEIGHT]: overlay.height },
|
|
70
109
|
'data-uw-under-header': '',
|
|
71
110
|
}
|
|
72
111
|
}
|