@kywi-software/core 0.10.0 → 0.11.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/AGENT-PATTERNS.md +15 -13
- package/README.md +67 -0
- package/dist/admin/layout-editor/editor-canvas.d.ts +20 -7
- package/dist/admin/layout-editor/editor-canvas.d.ts.map +1 -1
- package/dist/admin/layout-editor/editor-canvas.js +31 -26
- package/dist/admin/layout-editor/editor-canvas.js.map +1 -1
- package/dist/admin/layout-editor/editor.css +87 -2
- package/dist/admin/layout-editor/inline-text.d.ts +56 -4
- package/dist/admin/layout-editor/inline-text.d.ts.map +1 -1
- package/dist/admin/layout-editor/inline-text.js +134 -11
- package/dist/admin/layout-editor/inline-text.js.map +1 -1
- package/dist/admin/layout-editor/module-overlay.d.ts +26 -13
- package/dist/admin/layout-editor/module-overlay.d.ts.map +1 -1
- package/dist/admin/layout-editor/module-overlay.js +200 -21
- package/dist/admin/layout-editor/module-overlay.js.map +1 -1
- package/dist/admin/layout-editor/overlay-shell.d.ts.map +1 -1
- package/dist/admin/layout-editor/overlay-shell.js +38 -11
- package/dist/admin/layout-editor/overlay-shell.js.map +1 -1
- package/dist/admin/layout-editor/props-panel.js +1 -1
- package/dist/admin/layout-editor/props-panel.js.map +1 -1
- package/dist/admin/layout-editor/section-overlay.d.ts +8 -1
- package/dist/admin/layout-editor/section-overlay.d.ts.map +1 -1
- package/dist/admin/layout-editor/section-overlay.js +8 -8
- package/dist/admin/layout-editor/section-overlay.js.map +1 -1
- package/dist/admin/layout-editor/section-variant-container-config.js +5 -5
- package/dist/admin/layout-editor/section-variant-container-config.js.map +1 -1
- package/dist/admin/layout-editor/use-chrome-flip.d.ts +111 -11
- package/dist/admin/layout-editor/use-chrome-flip.d.ts.map +1 -1
- package/dist/admin/layout-editor/use-chrome-flip.js +233 -33
- package/dist/admin/layout-editor/use-chrome-flip.js.map +1 -1
- package/dist/admin/styles/admin.css +14 -1
- package/dist/layout/comments-module.d.ts.map +1 -1
- package/dist/layout/comments-module.js +6 -2
- package/dist/layout/comments-module.js.map +1 -1
- package/dist/layout/module-render-context.d.ts +32 -0
- package/dist/layout/module-render-context.d.ts.map +1 -1
- package/dist/layout/module-render-context.js +32 -1
- package/dist/layout/module-render-context.js.map +1 -1
- package/dist/layout/modules-extended.d.ts.map +1 -1
- package/dist/layout/modules-extended.js +21 -11
- package/dist/layout/modules-extended.js.map +1 -1
- package/dist/layout/modules.d.ts.map +1 -1
- package/dist/layout/modules.js +15 -10
- package/dist/layout/modules.js.map +1 -1
- package/dist/scope/inline-editing.d.ts.map +1 -1
- package/dist/scope/inline-editing.js +10 -0
- package/dist/scope/inline-editing.js.map +1 -1
- package/dist/scope/sticky-offset.d.ts +185 -0
- package/dist/scope/sticky-offset.d.ts.map +1 -0
- package/dist/scope/sticky-offset.js +432 -0
- package/dist/scope/sticky-offset.js.map +1 -0
- package/dist/site/styles.css +41 -3
- package/package.json +1 -1
|
@@ -2,8 +2,62 @@ import React from 'react';
|
|
|
2
2
|
/**
|
|
3
3
|
* An owner whose top edge sits above this line has no room for a chrome row
|
|
4
4
|
* between the fixed bar and itself, so its chrome has to move.
|
|
5
|
+
*
|
|
6
|
+
* The floor, not the whole story: a page whose own header is `position: sticky;
|
|
7
|
+
* top: 0` has a SECOND band under the editor's bar, because the sticky-offset
|
|
8
|
+
* detector re-pins that header just below it (kywi-cms#146). Clearing only the
|
|
9
|
+
* editor's bar then lands the chrome inside the site's nav — measured on the
|
|
10
|
+
* reference app at a clamped chrome of y 60–92 against a nav band of 46–111.
|
|
11
|
+
* So the live strip is this plus whatever the detector published; see
|
|
12
|
+
* {@link topStripPx}.
|
|
5
13
|
*/
|
|
6
14
|
export declare const BAR_STRIP_PX: number;
|
|
15
|
+
/**
|
|
16
|
+
* Width of the open module-palette rail, used when `--kywi-overlay-rail-left`
|
|
17
|
+
* cannot be read (server render, a host that lazy-loads editor.css after the
|
|
18
|
+
* first measurement, jsdom). Mirrors `--kywi-editor-panel-width-left` in
|
|
19
|
+
* editor.css, which the rail token resolves to.
|
|
20
|
+
*
|
|
21
|
+
* The rail is `box-sizing: border-box` (it carries `.kywi-overlay-chrome`, see
|
|
22
|
+
* the box-sizing rule at the top of editor.css), so this number is the rail's
|
|
23
|
+
* FULL on-screen footprint — its 1px right border included. That is what makes
|
|
24
|
+
* "read the token" and "measure the rail" the same answer, and it is why the
|
|
25
|
+
* token is the source here: it is stable while the rail's own width transition
|
|
26
|
+
* is still running, which a rect read during a palette toggle is not.
|
|
27
|
+
*/
|
|
28
|
+
export declare const RAIL_WIDTH_FALLBACK_PX = 232;
|
|
29
|
+
/**
|
|
30
|
+
* The narrowest strip of owner worth pushing the bar into. A chrome bar cannot
|
|
31
|
+
* be pushed so far right that its LEFT edge leaves its owner: it is absolutely
|
|
32
|
+
* positioned INSIDE the owner, and a bar that starts past a narrow band's right
|
|
33
|
+
* edge points at nothing. So on an owner too narrow to both clear the rail and
|
|
34
|
+
* still leave this much room, the offset stops here and the bar stays (partly)
|
|
35
|
+
* behind the rail — which the owner fixes with one click on the palette toggle.
|
|
36
|
+
*
|
|
37
|
+
* What this actually guarantees, precisely: the bar's left edge stays inside the
|
|
38
|
+
* owner with at least 200px of the owner to its right — NOT that the whole bar
|
|
39
|
+
* fits. The in-place bar renders its own controls plus Personalize and A/B, so a
|
|
40
|
+
* fully-actioned one is nearer 300px wide and can still overhang the owner's
|
|
41
|
+
* right edge; the chrome carries no max-width to stop it. That overhang is
|
|
42
|
+
* cosmetic (the buttons remain over the page, clickable, and visibly attached to
|
|
43
|
+
* their band) and deliberate: the alternative is refusing to move the bar out
|
|
44
|
+
* from under the rail at all, which is the bug this cap exists inside of.
|
|
45
|
+
*/
|
|
46
|
+
export declare const CHROME_MIN_WIDTH_PX = 200;
|
|
47
|
+
/** What the two measurements resolve to for one owner, in one layout frame. */
|
|
48
|
+
export interface ChromeOffsets {
|
|
49
|
+
/**
|
|
50
|
+
* Distance to pin the chrome DOWN from its owner's top edge so it clears the
|
|
51
|
+
* editor's fixed bar, or null for the ordinary above-position (kywi-cms#122).
|
|
52
|
+
*/
|
|
53
|
+
clampPx: number | null;
|
|
54
|
+
/**
|
|
55
|
+
* Distance to push the chrome RIGHT from its owner's left edge so it clears
|
|
56
|
+
* the open module-palette rail, or null when there is nothing to clear
|
|
57
|
+
* (kywi-cms#132).
|
|
58
|
+
*/
|
|
59
|
+
railAvoidPx: number | null;
|
|
60
|
+
}
|
|
7
61
|
/**
|
|
8
62
|
* In-place chrome floats ABOVE its owner (`transform: translateY(-100%)`, see
|
|
9
63
|
* `.kywi-ip-section-chrome` / `.kywi-ip-vc-chrome` in editor.css). For an owner
|
|
@@ -13,10 +67,39 @@ export declare const BAR_STRIP_PX: number;
|
|
|
13
67
|
* an edge-adjacent element's chrome can never cover the rails (kywi-cms#118).
|
|
14
68
|
* The fix is therefore positional (kywi-cms#122).
|
|
15
69
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
70
|
+
* The same argument runs sideways for the open module palette (kywi-cms#132).
|
|
71
|
+
* That rail is fixed to the left viewport edge, 232px wide, and out-ranks
|
|
72
|
+
* chrome by the same deliberate tier — so a FULL-WIDTH section, whose chrome
|
|
73
|
+
* renders at the section's own `left: 0`, has its grip, +Col, Personalize, A/B
|
|
74
|
+
* and × buttons sitting under the palette, unclickable. z-index cannot rescue
|
|
75
|
+
* that either (the rail must stay on top), and unlike an arbitrary site header
|
|
76
|
+
* the rail's footprint is a value the editor owns, so the fix is again
|
|
77
|
+
* positional: push the chrome right until it clears the rail.
|
|
78
|
+
*
|
|
79
|
+
* The vertical strip is not always just the bar, though. On a site whose header
|
|
80
|
+
* is `position: sticky; top: 0` — the common theme pattern — the editor re-pins
|
|
81
|
+
* that header directly under its own bar for the session (sticky-offset.ts,
|
|
82
|
+
* kywi-cms#146), so there is a SECOND band to clear. Clearing only the bar puts
|
|
83
|
+
* the chrome inside the site's nav: measured on the reference app at a clamped
|
|
84
|
+
* chrome of y 60–92 against a nav band of 46–111. The detector publishes that
|
|
85
|
+
* band's height and the clamp adds it, which is a no-op (0) on a page with no
|
|
86
|
+
* top-anchored chrome — i.e. every page the #122 geometry was written against.
|
|
87
|
+
*
|
|
88
|
+
* Returns both offsets from ONE rect read, in px:
|
|
89
|
+
* - `clampPx` — distance to pin the chrome DOWN from its owner's top edge so
|
|
90
|
+
* it lands just under the fixed bar (and the site's offset nav, if any), or
|
|
91
|
+
* null when the owner has room above it and the ordinary above-position
|
|
92
|
+
* applies. The caller turns that into `--kywi-ip-chrome-clamp` +
|
|
93
|
+
* `.kywi-ip-chrome--clamped`.
|
|
94
|
+
* - `railAvoidPx` — distance to push the chrome RIGHT so it clears the open
|
|
95
|
+
* palette rail, or null when the palette is closed or the owner already
|
|
96
|
+
* starts to the right of it. The caller turns that into
|
|
97
|
+
* `--kywi-ip-chrome-left` + `.kywi-ip-chrome--rail-avoid`.
|
|
98
|
+
*
|
|
99
|
+
* One hook rather than two: both offsets are read off the same
|
|
100
|
+
* `getBoundingClientRect()`, which is also the rule the drag work landed on
|
|
101
|
+
* (kywi-cms#136) — chrome geometry comes from rects taken in one frame, never
|
|
102
|
+
* from separately-sampled measurements that can disagree by a scroll.
|
|
20
103
|
*
|
|
21
104
|
* Pinning inside the owner rather than hanging the bar off the owner's BOTTOM
|
|
22
105
|
* edge (the first shape this fix took): a hero taller than the viewport put its
|
|
@@ -25,18 +108,35 @@ export declare const BAR_STRIP_PX: number;
|
|
|
25
108
|
* app. Clamping keeps the bar one row under the editor's own bar for as long as
|
|
26
109
|
* any of its owner is on screen, which is the whole point of the fix.
|
|
27
110
|
*
|
|
28
|
-
* Re-measured on mount
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
111
|
+
* Re-measured on mount, on every scroll/resize, and whenever the owner's own box
|
|
112
|
+
* changes — a `ResizeObserver`, for the layout moves no window event reports
|
|
113
|
+
* (see the effect). The owner's top edge moves relative to the bar as the reader
|
|
114
|
+
* scrolls, and while clamped the offset has to track it pixel for pixel. That is
|
|
115
|
+
* a style write per scroll for the one or two chromes near the top strip; every
|
|
116
|
+
* other chrome returns a stable value (null, or the bottom clamp below) and
|
|
117
|
+
* React bails out without touching the DOM at all.
|
|
34
118
|
*
|
|
35
119
|
* @param ownerRef the element the chrome is positioned against (the section or
|
|
36
120
|
* variant-container root). A null ref never clamps.
|
|
37
121
|
* @param enabled pass false for surfaces with no floating chrome — the admin
|
|
38
122
|
* artboard has its own header and in-flow toolbars, so it must neither clamp
|
|
39
123
|
* nor pay for the listeners.
|
|
124
|
+
* @param paletteOpen whether the left module-palette rail is currently expanded.
|
|
125
|
+
* In-place only; the admin Layout tab's palette is an in-flow column that
|
|
126
|
+
* displaces the canvas rather than floating over it, so nothing there has a
|
|
127
|
+
* rail to avoid. Part of the effect's deps, so toggling the palette
|
|
128
|
+
* re-measures immediately rather than waiting for the next scroll.
|
|
129
|
+
*/
|
|
130
|
+
export declare function useChromeFlip(ownerRef: React.RefObject<HTMLElement | null>, enabled?: boolean, paletteOpen?: boolean): ChromeOffsets;
|
|
131
|
+
/**
|
|
132
|
+
* The modifier classes the measured offsets ask for, in the order the CSS
|
|
133
|
+
* expects them. Shared by both consumers (`SectionOverlay` and
|
|
134
|
+
* `VariantContainerBlock`) so a third one cannot half-implement the contract.
|
|
135
|
+
*/
|
|
136
|
+
export declare function chromeOffsetClasses(offsets: ChromeOffsets): string[];
|
|
137
|
+
/**
|
|
138
|
+
* The custom properties those classes read, or undefined when neither offset
|
|
139
|
+
* applies (so the element keeps no `style` attribute at all in the common case).
|
|
40
140
|
*/
|
|
41
|
-
export declare function
|
|
141
|
+
export declare function chromeOffsetStyle(offsets: ChromeOffsets): React.CSSProperties | undefined;
|
|
42
142
|
//# sourceMappingURL=use-chrome-flip.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-chrome-flip.d.ts","sourceRoot":"","sources":["../../../src/admin/layout-editor/use-chrome-flip.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"use-chrome-flip.d.ts","sourceRoot":"","sources":["../../../src/admin/layout-editor/use-chrome-flip.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAA;AAiBzB;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,YAAY,QAAgC,CAAA;AAEzD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,sBAAsB,MAAM,CAAA;AAEzC;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,mBAAmB,MAAM,CAAA;AAEtC,+EAA+E;AAC/E,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB;;;;OAIG;IACH,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;CAC3B;AAuBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoEG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,EAC7C,OAAO,UAAO,EACd,WAAW,UAAQ,GAClB,aAAa,CAkIf;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,EAAE,CAKpE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,aAAa,GAAG,KAAK,CAAC,aAAa,GAAG,SAAS,CAMzF"}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import React from 'react';
|
|
3
|
+
import { readStickyOffsetPx } from '../../scope/sticky-offset.js';
|
|
3
4
|
/**
|
|
4
5
|
* Height of the editor's fixed top bar, mirroring `--kywi-overlay-bar-height`
|
|
5
6
|
* in editor.css (the page itself is offset by that token via
|
|
6
|
-
* `.kywi-frontend-edit--editing { padding-top: … }
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* `.kywi-frontend-edit--editing { padding-top: … }`, and the site's own
|
|
8
|
+
* top-anchored elements by `sticky-offset.ts`). It has to be a number here —
|
|
9
|
+
* this is arithmetic against a `getBoundingClientRect()`, not a layout value
|
|
10
|
+
* CSS could resolve — so all of those must be changed together.
|
|
9
11
|
*/
|
|
10
12
|
const BAR_HEIGHT_PX = 46;
|
|
11
13
|
/** One chrome row: the height a floating section/container bar occupies. */
|
|
@@ -13,8 +15,68 @@ const CHROME_ROW_PX = 40;
|
|
|
13
15
|
/**
|
|
14
16
|
* An owner whose top edge sits above this line has no room for a chrome row
|
|
15
17
|
* between the fixed bar and itself, so its chrome has to move.
|
|
18
|
+
*
|
|
19
|
+
* The floor, not the whole story: a page whose own header is `position: sticky;
|
|
20
|
+
* top: 0` has a SECOND band under the editor's bar, because the sticky-offset
|
|
21
|
+
* detector re-pins that header just below it (kywi-cms#146). Clearing only the
|
|
22
|
+
* editor's bar then lands the chrome inside the site's nav — measured on the
|
|
23
|
+
* reference app at a clamped chrome of y 60–92 against a nav band of 46–111.
|
|
24
|
+
* So the live strip is this plus whatever the detector published; see
|
|
25
|
+
* {@link topStripPx}.
|
|
16
26
|
*/
|
|
17
27
|
export const BAR_STRIP_PX = BAR_HEIGHT_PX + CHROME_ROW_PX;
|
|
28
|
+
/**
|
|
29
|
+
* Width of the open module-palette rail, used when `--kywi-overlay-rail-left`
|
|
30
|
+
* cannot be read (server render, a host that lazy-loads editor.css after the
|
|
31
|
+
* first measurement, jsdom). Mirrors `--kywi-editor-panel-width-left` in
|
|
32
|
+
* editor.css, which the rail token resolves to.
|
|
33
|
+
*
|
|
34
|
+
* The rail is `box-sizing: border-box` (it carries `.kywi-overlay-chrome`, see
|
|
35
|
+
* the box-sizing rule at the top of editor.css), so this number is the rail's
|
|
36
|
+
* FULL on-screen footprint — its 1px right border included. That is what makes
|
|
37
|
+
* "read the token" and "measure the rail" the same answer, and it is why the
|
|
38
|
+
* token is the source here: it is stable while the rail's own width transition
|
|
39
|
+
* is still running, which a rect read during a palette toggle is not.
|
|
40
|
+
*/
|
|
41
|
+
export const RAIL_WIDTH_FALLBACK_PX = 232;
|
|
42
|
+
/**
|
|
43
|
+
* The narrowest strip of owner worth pushing the bar into. A chrome bar cannot
|
|
44
|
+
* be pushed so far right that its LEFT edge leaves its owner: it is absolutely
|
|
45
|
+
* positioned INSIDE the owner, and a bar that starts past a narrow band's right
|
|
46
|
+
* edge points at nothing. So on an owner too narrow to both clear the rail and
|
|
47
|
+
* still leave this much room, the offset stops here and the bar stays (partly)
|
|
48
|
+
* behind the rail — which the owner fixes with one click on the palette toggle.
|
|
49
|
+
*
|
|
50
|
+
* What this actually guarantees, precisely: the bar's left edge stays inside the
|
|
51
|
+
* owner with at least 200px of the owner to its right — NOT that the whole bar
|
|
52
|
+
* fits. The in-place bar renders its own controls plus Personalize and A/B, so a
|
|
53
|
+
* fully-actioned one is nearer 300px wide and can still overhang the owner's
|
|
54
|
+
* right edge; the chrome carries no max-width to stop it. That overhang is
|
|
55
|
+
* cosmetic (the buttons remain over the page, clickable, and visibly attached to
|
|
56
|
+
* their band) and deliberate: the alternative is refusing to move the bar out
|
|
57
|
+
* from under the rail at all, which is the bug this cap exists inside of.
|
|
58
|
+
*/
|
|
59
|
+
export const CHROME_MIN_WIDTH_PX = 200;
|
|
60
|
+
/**
|
|
61
|
+
* Current width of the left rail, from the token the rail itself is sized by,
|
|
62
|
+
* so a host that re-themes `--kywi-editor-panel-width-left` moves the chrome
|
|
63
|
+
* with the rail instead of leaving it behind a wider one.
|
|
64
|
+
*
|
|
65
|
+
* `getPropertyValue` returns the custom property's COMPUTED value, i.e. with
|
|
66
|
+
* its own `var()` reference already substituted — so this reads "232px", not
|
|
67
|
+
* "var(--kywi-editor-panel-width-left)". Either way a value this cannot parse
|
|
68
|
+
* falls back rather than producing NaN arithmetic.
|
|
69
|
+
*/
|
|
70
|
+
function readRailWidthPx() {
|
|
71
|
+
if (typeof window === 'undefined' || typeof document === 'undefined') {
|
|
72
|
+
return RAIL_WIDTH_FALLBACK_PX;
|
|
73
|
+
}
|
|
74
|
+
const raw = window
|
|
75
|
+
.getComputedStyle(document.documentElement)
|
|
76
|
+
.getPropertyValue('--kywi-overlay-rail-left');
|
|
77
|
+
const parsed = Number.parseFloat(raw);
|
|
78
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : RAIL_WIDTH_FALLBACK_PX;
|
|
79
|
+
}
|
|
18
80
|
/**
|
|
19
81
|
* In-place chrome floats ABOVE its owner (`transform: translateY(-100%)`, see
|
|
20
82
|
* `.kywi-ip-section-chrome` / `.kywi-ip-vc-chrome` in editor.css). For an owner
|
|
@@ -24,10 +86,39 @@ export const BAR_STRIP_PX = BAR_HEIGHT_PX + CHROME_ROW_PX;
|
|
|
24
86
|
* an edge-adjacent element's chrome can never cover the rails (kywi-cms#118).
|
|
25
87
|
* The fix is therefore positional (kywi-cms#122).
|
|
26
88
|
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
89
|
+
* The same argument runs sideways for the open module palette (kywi-cms#132).
|
|
90
|
+
* That rail is fixed to the left viewport edge, 232px wide, and out-ranks
|
|
91
|
+
* chrome by the same deliberate tier — so a FULL-WIDTH section, whose chrome
|
|
92
|
+
* renders at the section's own `left: 0`, has its grip, +Col, Personalize, A/B
|
|
93
|
+
* and × buttons sitting under the palette, unclickable. z-index cannot rescue
|
|
94
|
+
* that either (the rail must stay on top), and unlike an arbitrary site header
|
|
95
|
+
* the rail's footprint is a value the editor owns, so the fix is again
|
|
96
|
+
* positional: push the chrome right until it clears the rail.
|
|
97
|
+
*
|
|
98
|
+
* The vertical strip is not always just the bar, though. On a site whose header
|
|
99
|
+
* is `position: sticky; top: 0` — the common theme pattern — the editor re-pins
|
|
100
|
+
* that header directly under its own bar for the session (sticky-offset.ts,
|
|
101
|
+
* kywi-cms#146), so there is a SECOND band to clear. Clearing only the bar puts
|
|
102
|
+
* the chrome inside the site's nav: measured on the reference app at a clamped
|
|
103
|
+
* chrome of y 60–92 against a nav band of 46–111. The detector publishes that
|
|
104
|
+
* band's height and the clamp adds it, which is a no-op (0) on a page with no
|
|
105
|
+
* top-anchored chrome — i.e. every page the #122 geometry was written against.
|
|
106
|
+
*
|
|
107
|
+
* Returns both offsets from ONE rect read, in px:
|
|
108
|
+
* - `clampPx` — distance to pin the chrome DOWN from its owner's top edge so
|
|
109
|
+
* it lands just under the fixed bar (and the site's offset nav, if any), or
|
|
110
|
+
* null when the owner has room above it and the ordinary above-position
|
|
111
|
+
* applies. The caller turns that into `--kywi-ip-chrome-clamp` +
|
|
112
|
+
* `.kywi-ip-chrome--clamped`.
|
|
113
|
+
* - `railAvoidPx` — distance to push the chrome RIGHT so it clears the open
|
|
114
|
+
* palette rail, or null when the palette is closed or the owner already
|
|
115
|
+
* starts to the right of it. The caller turns that into
|
|
116
|
+
* `--kywi-ip-chrome-left` + `.kywi-ip-chrome--rail-avoid`.
|
|
117
|
+
*
|
|
118
|
+
* One hook rather than two: both offsets are read off the same
|
|
119
|
+
* `getBoundingClientRect()`, which is also the rule the drag work landed on
|
|
120
|
+
* (kywi-cms#136) — chrome geometry comes from rects taken in one frame, never
|
|
121
|
+
* from separately-sampled measurements that can disagree by a scroll.
|
|
31
122
|
*
|
|
32
123
|
* Pinning inside the owner rather than hanging the bar off the owner's BOTTOM
|
|
33
124
|
* edge (the first shape this fix took): a hero taller than the viewport put its
|
|
@@ -36,49 +127,106 @@ export const BAR_STRIP_PX = BAR_HEIGHT_PX + CHROME_ROW_PX;
|
|
|
36
127
|
* app. Clamping keeps the bar one row under the editor's own bar for as long as
|
|
37
128
|
* any of its owner is on screen, which is the whole point of the fix.
|
|
38
129
|
*
|
|
39
|
-
* Re-measured on mount
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
130
|
+
* Re-measured on mount, on every scroll/resize, and whenever the owner's own box
|
|
131
|
+
* changes — a `ResizeObserver`, for the layout moves no window event reports
|
|
132
|
+
* (see the effect). The owner's top edge moves relative to the bar as the reader
|
|
133
|
+
* scrolls, and while clamped the offset has to track it pixel for pixel. That is
|
|
134
|
+
* a style write per scroll for the one or two chromes near the top strip; every
|
|
135
|
+
* other chrome returns a stable value (null, or the bottom clamp below) and
|
|
136
|
+
* React bails out without touching the DOM at all.
|
|
45
137
|
*
|
|
46
138
|
* @param ownerRef the element the chrome is positioned against (the section or
|
|
47
139
|
* variant-container root). A null ref never clamps.
|
|
48
140
|
* @param enabled pass false for surfaces with no floating chrome — the admin
|
|
49
141
|
* artboard has its own header and in-flow toolbars, so it must neither clamp
|
|
50
142
|
* nor pay for the listeners.
|
|
143
|
+
* @param paletteOpen whether the left module-palette rail is currently expanded.
|
|
144
|
+
* In-place only; the admin Layout tab's palette is an in-flow column that
|
|
145
|
+
* displaces the canvas rather than floating over it, so nothing there has a
|
|
146
|
+
* rail to avoid. Part of the effect's deps, so toggling the palette
|
|
147
|
+
* re-measures immediately rather than waiting for the next scroll.
|
|
51
148
|
*/
|
|
52
|
-
export function useChromeFlip(ownerRef, enabled = true) {
|
|
149
|
+
export function useChromeFlip(ownerRef, enabled = true, paletteOpen = false) {
|
|
53
150
|
const [clampOffsetPx, setClampOffsetPx] = React.useState(null);
|
|
151
|
+
const [railAvoidOffsetPx, setRailAvoidOffsetPx] = React.useState(null);
|
|
54
152
|
React.useEffect(() => {
|
|
55
153
|
if (!enabled || typeof window === 'undefined') {
|
|
56
154
|
setClampOffsetPx(null);
|
|
155
|
+
setRailAvoidOffsetPx(null);
|
|
57
156
|
return;
|
|
58
157
|
}
|
|
158
|
+
// Read once per effect run, not per scroll: this is a theme token, so it
|
|
159
|
+
// can only change with a stylesheet, and `getComputedStyle` in a scroll
|
|
160
|
+
// handler is exactly the kind of per-frame style read the clamp's own
|
|
161
|
+
// comment is careful to avoid. A palette toggle re-runs the effect (it is
|
|
162
|
+
// in the deps), which is also when a re-read could matter.
|
|
163
|
+
const railWidthPx = readRailWidthPx();
|
|
59
164
|
const measure = () => {
|
|
60
165
|
const owner = ownerRef.current;
|
|
61
166
|
if (!owner)
|
|
62
167
|
return;
|
|
63
|
-
|
|
64
|
-
|
|
168
|
+
// ONE rect for both axes — see the kywi-cms#136 note above.
|
|
169
|
+
const { top, height, left, width } = owner.getBoundingClientRect();
|
|
170
|
+
// ── Vertical: clear the editor's fixed top bar (kywi-cms#122) and the
|
|
171
|
+
// site's offset sticky band on top of it (kywi-cms#146) ─────────────
|
|
172
|
+
//
|
|
173
|
+
// Read per measurement, unlike the rail width above: that is a theme
|
|
174
|
+
// token which can only change with a stylesheet, whereas this tracks a
|
|
175
|
+
// live element whose height changes when its nav wraps. Cheap enough for
|
|
176
|
+
// a scroll handler because `readStickyOffsetPx` reads back the INLINE
|
|
177
|
+
// custom property the detector wrote — a CSSOM read, not a
|
|
178
|
+
// `getComputedStyle` that could force a style recalculation. 0 (the whole
|
|
179
|
+
// pre-#146 behaviour) whenever the page has no offset top band.
|
|
180
|
+
const stickyPx = readStickyOffsetPx();
|
|
181
|
+
// Where a clamped bar has to land: under the editor's bar AND under the
|
|
182
|
+
// site's nav, which the detector re-pinned directly beneath it.
|
|
183
|
+
const stripBottomPx = BAR_HEIGHT_PX + stickyPx;
|
|
184
|
+
if (top >= BAR_STRIP_PX + stickyPx) {
|
|
65
185
|
setClampOffsetPx(null);
|
|
66
|
-
return;
|
|
67
186
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
187
|
+
else {
|
|
188
|
+
// How far down from the owner's top edge the bar has to sit to clear the
|
|
189
|
+
// fixed bar. Never negative: an owner already below the bar keeps its
|
|
190
|
+
// chrome flush with its own top edge rather than being pulled up into it.
|
|
191
|
+
const wanted = Math.max(0, stripBottomPx - top);
|
|
192
|
+
// …but never past the owner's own bottom edge either. Once the owner has
|
|
193
|
+
// scrolled far enough that the bar would otherwise hang below it, the bar
|
|
194
|
+
// rides out with its owner instead of sticking to the top of the screen
|
|
195
|
+
// over whatever the reader is actually looking at. This also caps the
|
|
196
|
+
// per-scroll churn: for anything well above the fold the offset stops
|
|
197
|
+
// changing, and the style writes stop with it.
|
|
198
|
+
const maxInsideOwner = Math.max(0, height - CHROME_ROW_PX);
|
|
199
|
+
// Rounded so sub-pixel rects (browser zoom, fractional layouts) don't
|
|
200
|
+
// re-render on every frame of a scroll that hasn't moved a whole pixel.
|
|
201
|
+
setClampOffsetPx(Math.round(Math.min(wanted, maxInsideOwner)));
|
|
202
|
+
}
|
|
203
|
+
// ── Horizontal: clear the open palette rail (kywi-cms#132) ────────────
|
|
204
|
+
if (!paletteOpen || left >= railWidthPx) {
|
|
205
|
+
// A closed palette, or an owner (a centred container, an inset band)
|
|
206
|
+
// that already starts to the right of the rail — the ordinary
|
|
207
|
+
// left-edge position is reachable, so leave it alone.
|
|
208
|
+
setRailAvoidOffsetPx(null);
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
// Full-width owners are the case in the issue: left 0, so the chrome
|
|
212
|
+
// needs the rail's whole width. A partly-covered owner needs only the
|
|
213
|
+
// overlap.
|
|
214
|
+
const wanted = railWidthPx - left;
|
|
215
|
+
// Capped so the bar's left edge stays inside the owner it belongs to.
|
|
216
|
+
// A band narrower than the rail plus a usable strip keeps whatever room
|
|
217
|
+
// it has; pushing further would start the bar past the band's own right
|
|
218
|
+
// edge, over the section beside it.
|
|
219
|
+
const maxInsideOwner = Math.max(0, width - CHROME_MIN_WIDTH_PX);
|
|
220
|
+
// Rounded AWAY from each obstacle, not to the nearest pixel. A
|
|
221
|
+
// fractional owner left (zoom, a centred layout on an odd viewport)
|
|
222
|
+
// rounded to nearest can land the bar half a pixel back INSIDE the rail
|
|
223
|
+
// — owner left 124.6 + round(107.4) = 231.6, against a rail that ends at
|
|
224
|
+
// 232 — which is both visibly clipped and a real hit-test failure at the
|
|
225
|
+
// bar's left edge. `ceil` on the push clears the rail; `floor` on the
|
|
226
|
+
// cap keeps the guarantee above (≥200px of owner to the bar's right)
|
|
227
|
+
// rather than overshooting it by the same sub-pixel.
|
|
228
|
+
setRailAvoidOffsetPx(Math.min(Math.ceil(wanted), Math.floor(maxInsideOwner)));
|
|
229
|
+
}
|
|
82
230
|
};
|
|
83
231
|
measure();
|
|
84
232
|
// `capture: true` so a scroll inside any nested scroll container on the
|
|
@@ -87,11 +235,63 @@ export function useChromeFlip(ownerRef, enabled = true) {
|
|
|
87
235
|
const listenerOptions = { passive: true, capture: true };
|
|
88
236
|
window.addEventListener('scroll', measure, listenerOptions);
|
|
89
237
|
window.addEventListener('resize', measure, listenerOptions);
|
|
238
|
+
// Scroll and resize are window events, and the owner can move under BOTH
|
|
239
|
+
// measurements without either firing. The live case is the responsive
|
|
240
|
+
// preview: switching Desktop → Mobile re-caps the page's max-width
|
|
241
|
+
// (`.kywi-ip-page--constrained`, overlay-shell), so every band's left edge
|
|
242
|
+
// and width jump inwards while the window is exactly the size it was and
|
|
243
|
+
// nothing scrolled. Nor is there a prop change to fall back on: the offsets
|
|
244
|
+
// are state inside this hook, so the canvas's React.memo has nothing to
|
|
245
|
+
// re-render on. Measured on the reference app at 1280 with the palette out:
|
|
246
|
+
// a band at x=124 takes a 108px push, and switching to Mobile moves it to
|
|
247
|
+
// x=452.5 — the bar stayed at 560.5, a third of the band's width adrift,
|
|
248
|
+
// until something scrolled. Observing the owner heals both axes; the same
|
|
249
|
+
// layout change is what moves its top edge relative to the fixed bar.
|
|
250
|
+
//
|
|
251
|
+
// Feature-detected rather than assumed: happy-dom ships an inert stub and
|
|
252
|
+
// older hosts have none, and a chrome that only re-measures on scroll is
|
|
253
|
+
// exactly the behaviour this shipped with, so a missing observer degrades
|
|
254
|
+
// instead of throwing.
|
|
255
|
+
const ResizeObserverCtor = window
|
|
256
|
+
.ResizeObserver;
|
|
257
|
+
let observer = null;
|
|
258
|
+
if (typeof ResizeObserverCtor === 'function') {
|
|
259
|
+
observer = new ResizeObserverCtor(measure);
|
|
260
|
+
const owner = ownerRef.current;
|
|
261
|
+
if (owner)
|
|
262
|
+
observer.observe(owner);
|
|
263
|
+
}
|
|
90
264
|
return () => {
|
|
91
265
|
window.removeEventListener('scroll', measure, listenerOptions);
|
|
92
266
|
window.removeEventListener('resize', measure, listenerOptions);
|
|
267
|
+
observer?.disconnect();
|
|
93
268
|
};
|
|
94
|
-
}, [ownerRef, enabled]);
|
|
95
|
-
return clampOffsetPx;
|
|
269
|
+
}, [ownerRef, enabled, paletteOpen]);
|
|
270
|
+
return React.useMemo(() => ({ clampPx: clampOffsetPx, railAvoidPx: railAvoidOffsetPx }), [clampOffsetPx, railAvoidOffsetPx]);
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* The modifier classes the measured offsets ask for, in the order the CSS
|
|
274
|
+
* expects them. Shared by both consumers (`SectionOverlay` and
|
|
275
|
+
* `VariantContainerBlock`) so a third one cannot half-implement the contract.
|
|
276
|
+
*/
|
|
277
|
+
export function chromeOffsetClasses(offsets) {
|
|
278
|
+
const classes = [];
|
|
279
|
+
if (offsets.clampPx !== null)
|
|
280
|
+
classes.push('kywi-ip-chrome--clamped');
|
|
281
|
+
if (offsets.railAvoidPx !== null)
|
|
282
|
+
classes.push('kywi-ip-chrome--rail-avoid');
|
|
283
|
+
return classes;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* The custom properties those classes read, or undefined when neither offset
|
|
287
|
+
* applies (so the element keeps no `style` attribute at all in the common case).
|
|
288
|
+
*/
|
|
289
|
+
export function chromeOffsetStyle(offsets) {
|
|
290
|
+
if (offsets.clampPx === null && offsets.railAvoidPx === null)
|
|
291
|
+
return undefined;
|
|
292
|
+
return {
|
|
293
|
+
...(offsets.clampPx !== null ? { '--kywi-ip-chrome-clamp': `${offsets.clampPx}px` } : {}),
|
|
294
|
+
...(offsets.railAvoidPx !== null ? { '--kywi-ip-chrome-left': `${offsets.railAvoidPx}px` } : {}),
|
|
295
|
+
};
|
|
96
296
|
}
|
|
97
297
|
//# sourceMappingURL=use-chrome-flip.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-chrome-flip.js","sourceRoot":"","sources":["../../../src/admin/layout-editor/use-chrome-flip.ts"],"names":[],"mappings":"AAAA,YAAY,CAAA;AACZ,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB
|
|
1
|
+
{"version":3,"file":"use-chrome-flip.js","sourceRoot":"","sources":["../../../src/admin/layout-editor/use-chrome-flip.ts"],"names":[],"mappings":"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"}
|
|
@@ -183,8 +183,21 @@
|
|
|
183
183
|
2. EDITOR BRIDGE — retheme existing styled areas via shared tokens.
|
|
184
184
|
Only accent/status vars are remapped; the editors' dark panels and layout
|
|
185
185
|
mechanics are untouched.
|
|
186
|
+
|
|
187
|
+
Scoped to `.kywi-admin-shell` rather than `:root` (kywi-cms#144): CSS custom
|
|
188
|
+
properties resolve from the NEAREST declaring ancestor, not declaration
|
|
189
|
+
order between stylesheets. In generated apps, layout-editor/editor.css
|
|
190
|
+
loads lazily (only when the Layout tab mounts) and gets injected AFTER
|
|
191
|
+
this stylesheet — a `:root` remap here would lose to editor.css's later
|
|
192
|
+
`:root` declaration of the same tokens purely because it loaded second,
|
|
193
|
+
silently reverting the admin Layout tab to editor.css's indigo defaults.
|
|
194
|
+
An element-scoped declaration on `.kywi-admin-shell` wins regardless of
|
|
195
|
+
injection order, because it is closer to the elements that consume these
|
|
196
|
+
tokens than any `:root` rule can be. The front-of-site overlay editor has
|
|
197
|
+
no `.kywi-admin-shell` ancestor and intentionally keeps editor.css's own
|
|
198
|
+
indigo identity there (#94) — this scoping change does not touch it.
|
|
186
199
|
========================================================================== */
|
|
187
|
-
|
|
200
|
+
.kywi-admin-shell {
|
|
188
201
|
--kywi-editor-accent: var(--kywi-green-500);
|
|
189
202
|
--kywi-editor-accent-light: rgba(18, 128, 95, 0.14);
|
|
190
203
|
--kywi-editor-success: var(--kywi-green-400);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"comments-module.d.ts","sourceRoot":"","sources":["../../src/layout/comments-module.tsx"],"names":[],"mappings":"AAKA;;;;;;;;;;GAUG;AAEH,UAAU,WAAW;IACnB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC/B;
|
|
1
|
+
{"version":3,"file":"comments-module.d.ts","sourceRoot":"","sources":["../../src/layout/comments-module.tsx"],"names":[],"mappings":"AAKA;;;;;;;;;;GAUG;AAEH,UAAU,WAAW;IACnB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC/B;AA+OD,wBAAgB,cAAc,CAAC,EAAE,KAAK,EAAE,EAAE,WAAW,2CAmBpD"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useContext, useEffect, useRef, useState } from 'react';
|
|
4
|
-
import { ModuleEditorContext } from './module-render-context.js';
|
|
4
|
+
import { ModuleEditorContext, useInlinePropAnchor } from './module-render-context.js';
|
|
5
5
|
const COMMENTS_ENDPOINT = '/api/v1/comments';
|
|
6
6
|
const HONEYPOT_FIELD = '_kywi_hp';
|
|
7
7
|
/**
|
|
@@ -40,7 +40,11 @@ function authorLabel(c) {
|
|
|
40
40
|
}
|
|
41
41
|
// ─── Editor preview (non-interactive) ────────────────────────────────────────
|
|
42
42
|
function CommentsModulePreview({ title, submitLabel }) {
|
|
43
|
-
|
|
43
|
+
// Only the heading is anchored for inline editing (kywi-cms#145). `submitLabel`
|
|
44
|
+
// renders on a DISABLED button, which cannot take focus — anchoring it would
|
|
45
|
+
// start an edit the caret can never enter or leave. It stays a rail edit.
|
|
46
|
+
const anchor = useInlinePropAnchor();
|
|
47
|
+
return (_jsxs("div", { className: "kywi-module kywi-module-comments kywi-module-comments-preview", "aria-hidden": "true", children: [_jsx("h2", { className: "kywi-comments-title", ...anchor('title'), children: title }), _jsx("p", { className: "kywi-comments-preview-note", children: "Approved comments for this content appear here; visitors can post new ones (held for moderation)." }), _jsxs("div", { className: "kywi-comments-form kywi-comments-form-preview", children: [_jsx("input", { className: "kywi-form-input", disabled: true, placeholder: "Name (optional)" }), _jsx("textarea", { className: "kywi-form-textarea", disabled: true, placeholder: "Add a comment\u2026" }), _jsx("button", { type: "button", className: "kywi-form-submit", disabled: true, children: submitLabel })] })] }));
|
|
44
48
|
}
|
|
45
49
|
// ─── Public view (interactive) ───────────────────────────────────────────────
|
|
46
50
|
function CommentsModuleView({ title, submitLabel, allowAnonymous, explicitId, }) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"comments-module.js","sourceRoot":"","sources":["../../src/layout/comments-module.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAc,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AACtE,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;
|
|
1
|
+
{"version":3,"file":"comments-module.js","sourceRoot":"","sources":["../../src/layout/comments-module.tsx"],"names":[],"mappings":"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"}
|