@uniweb/runtime 1.1.12 → 1.1.14
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 +72 -18
- package/dist/ssr.js.map +1 -1
- package/package.json +4 -4
- package/src/area-wrappers.js +53 -7
- package/src/components/BlockRenderer.jsx +14 -1
- package/src/components/Blocks.jsx +8 -2
- package/src/components/Layout.jsx +28 -6
- package/src/components/PageRenderer.jsx +1 -1
- package/src/default-layout.js +13 -4
- package/src/ssr-renderer.js +36 -10
- package/src/sticky-check.js +110 -0
- package/src/sticky-top.js +100 -0
- package/src/sticky.js +124 -0
package/src/sticky.js
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a section's `sticky` setting lands — pure, so the browser's renderer
|
|
3
|
+
* (`components/Layout.jsx`, `BlockRenderer.jsx`) and the prerender's (`ssr-renderer.js`) make the
|
|
4
|
+
* same boxes sticky, and a prerendered page does not change when the browser renders over it.
|
|
5
|
+
*
|
|
6
|
+
* ⭐ NO BOX A COMPONENT DRAWS CAN STICK. `position: sticky` moves an element only within its
|
|
7
|
+
* parent's box, and a component's markup sits in boxes as tall as itself: the section's wrapper,
|
|
8
|
+
* the layout area's wrapper (`area-wrappers.js`), the built-in layout's zone (`default-layout.js`).
|
|
9
|
+
* So a section asks — `sticky:`, one of a section's own settings (`@uniweb/schemas/section`),
|
|
10
|
+
* defaulted by its type's `meta.js` — and the runtime makes sticky the box that can:
|
|
11
|
+
*
|
|
12
|
+
* - in the body, the section's own wrapper, whose parent, the body, spans every section;
|
|
13
|
+
* - a child section rendered as a section (`<ChildBlocks wrapAs>`, kit's `ChildGrid`), its own
|
|
14
|
+
* wrapper too, which then stays in view within the box its parent places it in — in
|
|
15
|
+
* `ChildGrid`, its cell, as tall as the row — and no further. Top-aligned, so a flex or grid
|
|
16
|
+
* item has room. At one column (a phone) a child is its own row and does not stick, which is
|
|
17
|
+
* what a side panel stacked under the content should do;
|
|
18
|
+
* - in any other area, the area's box when any of its sections is sticky: the zone, in the
|
|
19
|
+
* built-in layout; the area wrapper, in a foundation's layout. That box takes the area's
|
|
20
|
+
* layer, and a sticky area's layer is above every area that scrolls under it
|
|
21
|
+
* (`resolveLayoutLayers`).
|
|
22
|
+
*
|
|
23
|
+
* ⭐ A section stays in view BELOW what is stuck at the top. A sticky area sticks at `0px`; a
|
|
24
|
+
* sticky section with no `top` of its own sticks at `--uw-sticky-top`, how far down the page's
|
|
25
|
+
* stuck chrome reaches, which the browser measures (`sticky-top.js`) — so a side panel or a
|
|
26
|
+
* sub-navigation clears a stuck header whatever its height. Unmeasured — a prerendered page
|
|
27
|
+
* before the browser runs, a page with nothing stuck — it is `0px`.
|
|
28
|
+
*
|
|
29
|
+
* Every box this module makes sticky says so, `data-uw-sticky` = `area`, `section` or `child`,
|
|
30
|
+
* for what reads them in the browser: the measurement, and `uniweb dev`'s check
|
|
31
|
+
* (`sticky-check.js`).
|
|
32
|
+
*
|
|
33
|
+
* ⛔ A layout that wraps an area in an element of its own confines the area wrapper, as the zone
|
|
34
|
+
* does in the built-in layout. That element is the layout's, so the layout makes it sticky, when
|
|
35
|
+
* `page.isSticky(area)`.
|
|
36
|
+
*
|
|
37
|
+
* ⛔ Until 2026-10-19 there was no setting, and a header's own `sticky top-0` — the starter
|
|
38
|
+
* templates' — scrolled away with the page. Until 2026-10-20 a child section's setting did
|
|
39
|
+
* nothing, and a sticky section stuck at `0px`, under a stuck header.
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
/** The very top of the viewport — a sticky area with no `top` of its own. */
|
|
43
|
+
export const STICKY_TOP = '0px'
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Where a sticky section with no `top` of its own stays: below what is stuck at the top
|
|
47
|
+
* (`sticky-top.js` measures it), else the very top.
|
|
48
|
+
*/
|
|
49
|
+
export const STICKY_SECTION_TOP = 'var(--uw-sticky-top, 0px)'
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The style that keeps a box in view.
|
|
53
|
+
*
|
|
54
|
+
* @param {string|null} [top] - how far below the viewport's top, a CSS length
|
|
55
|
+
* @param {number|null} [zIndex] - its layer, when it has one
|
|
56
|
+
* @returns {{ position: 'sticky', top: string, zIndex?: number }}
|
|
57
|
+
*/
|
|
58
|
+
export function stickyStyle(top, zIndex) {
|
|
59
|
+
const style = { position: 'sticky', top: top || STICKY_TOP }
|
|
60
|
+
if (zIndex != null) style.zIndex = zIndex
|
|
61
|
+
return style
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Whether an area stays in view — when any of its sections is sticky — and how far below the
|
|
66
|
+
* top: the first sticky section's.
|
|
67
|
+
*
|
|
68
|
+
* @param {Array<Object>|null|undefined} blocks - the area's blocks, their components initialized
|
|
69
|
+
* @returns {{ top: string }|null}
|
|
70
|
+
*/
|
|
71
|
+
export function areaStickiness(blocks) {
|
|
72
|
+
const first = (blocks || []).find((block) => block?.sticky)
|
|
73
|
+
return first ? { top: first.stickyTop || STICKY_TOP } : null
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Each layout area's stickiness, read from its sections — what both renderers compute before the
|
|
78
|
+
* areas' layers, since a sticky area is lifted above the rest (`resolveLayoutLayers`).
|
|
79
|
+
*
|
|
80
|
+
* @param {Object<string, Array<Object>>} areas - area name → its blocks, their components initialized
|
|
81
|
+
* @returns {Object<string, { top: string }|null>}
|
|
82
|
+
*/
|
|
83
|
+
export function layoutStickiness(areas) {
|
|
84
|
+
const sticky = {}
|
|
85
|
+
for (const [name, blocks] of Object.entries(areas || {})) sticky[name] = areaStickiness(blocks)
|
|
86
|
+
return sticky
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* The props a built-in layout zone takes when its area sticks — or null.
|
|
91
|
+
*
|
|
92
|
+
* @param {{ top: string }|null|undefined} stickiness - the area's (`areaStickiness`)
|
|
93
|
+
* @param {number|null} [layer] - the area's layer (`resolveLayoutLayers`)
|
|
94
|
+
* @returns {{ style: Object, 'data-uw-sticky': 'area' }|null}
|
|
95
|
+
*/
|
|
96
|
+
export function zoneProps(stickiness, layer) {
|
|
97
|
+
return stickiness ? { style: stickyStyle(stickiness.top, layer), 'data-uw-sticky': 'area' } : null
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* The style a sticky section's own wrapper takes — a body section's, or a child section's — or
|
|
102
|
+
* null. Below what is stuck at the top unless its type says how far down; layer 1, so it paints
|
|
103
|
+
* over the sections after it, which are positioned only when they have a background; and
|
|
104
|
+
* top-aligned, so that as a flex or grid item it is not stretched to the row, which would leave
|
|
105
|
+
* it no room to move (in normal flow `align-self` does nothing).
|
|
106
|
+
*
|
|
107
|
+
* @param {Object} block - its component initialized
|
|
108
|
+
* @returns {Object|null}
|
|
109
|
+
*/
|
|
110
|
+
export function sectionStickyStyle(block) {
|
|
111
|
+
return block?.sticky ? { ...stickyStyle(block.stickyTop || STICKY_SECTION_TOP, 1), alignSelf: 'start' } : null
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* What a section's wrapper adds when it sticks: its style, and which kind of box it is.
|
|
116
|
+
*
|
|
117
|
+
* @param {Object} block - its component initialized
|
|
118
|
+
* @param {'section'|'child'|boolean} kind - a body section (`true` reads as one), or a child section
|
|
119
|
+
* @returns {{ style: Object, 'data-uw-sticky': string }|null}
|
|
120
|
+
*/
|
|
121
|
+
export function sectionStickyProps(block, kind) {
|
|
122
|
+
const style = kind ? sectionStickyStyle(block) : null
|
|
123
|
+
return style ? { style, 'data-uw-sticky': kind === 'child' ? 'child' : 'section' } : null
|
|
124
|
+
}
|