@pie-players/pie-players-shared 0.3.68 → 0.3.70
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/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/loaders/iife-adapter.js +7 -3
- package/dist/pie/element-observer.d.ts +59 -0
- package/dist/pie/element-observer.js +131 -0
- package/dist/pie/index.d.ts +4 -2
- package/dist/pie/index.js +7 -2
- package/dist/pie/initialization.d.ts +13 -13
- package/dist/pie/initialization.js +97 -147
- package/dist/pie/initialize-element.d.ts +23 -0
- package/dist/pie/initialize-element.js +78 -0
- package/dist/pie/instrumentation-event-map.d.ts +1 -0
- package/dist/pie/instrumentation-event-map.js +18 -0
- package/dist/pie/math-rendering.js +6 -2
- package/dist/pie/types.d.ts +10 -0
- package/dist/pie/utils.d.ts +27 -0
- package/dist/pie/utils.js +56 -1
- package/dist/security/index.d.ts +3 -2
- package/dist/security/index.js +3 -2
- package/dist/security/sanitize-forbidden-lists.js +9 -0
- package/dist/security/sanitize-item-markup.js +4 -0
- package/dist/security/sanitize-style-attribute.d.ts +48 -0
- package/dist/security/sanitize-style-attribute.js +129 -0
- package/dist/security/sanitize-svg-icon.js +2 -0
- package/dist/security/validate-style-url.d.ts +13 -0
- package/dist/security/validate-style-url.js +36 -2
- package/dist/security/wrap-overwide-images.d.ts +7 -0
- package/dist/security/wrap-overwide-images.js +10 -1
- package/dist/security/wrap-overwide-tables.d.ts +7 -0
- package/dist/security/wrap-overwide-tables.js +10 -1
- package/dist/security/wrap-overwide.d.ts +16 -0
- package/dist/security/wrap-overwide.js +52 -0
- package/dist/ui/overlay-containment.d.ts +48 -0
- package/dist/ui/overlay-containment.js +65 -0
- package/package.json +7 -7
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
* The wrapping itself lives in `./wrap-overwide.js`, shared with the image
|
|
27
27
|
* wrapper: only the four values below and the accessible name differ.
|
|
28
28
|
*/
|
|
29
|
-
import { wrapOverwideInElement, wrapOverwideMarkup, } from "./wrap-overwide.js";
|
|
29
|
+
import { isOverwideWrapMutation, wrapOverwideInElement, wrapOverwideMarkup, } from "./wrap-overwide.js";
|
|
30
30
|
function buildAriaLabel(table) {
|
|
31
31
|
// Authors commonly label tables via <caption>, aria-label, or aria-labelledby.
|
|
32
32
|
// Prefer the most explicit signal and fall back to the generic label so
|
|
@@ -75,3 +75,12 @@ export function wrapOverwideTablesInElement(root, options = {}) {
|
|
|
75
75
|
export function wrapOverwideTables(markup) {
|
|
76
76
|
return wrapOverwideMarkup(markup, TABLE_SPEC);
|
|
77
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* True when `record` mentions nothing but the output of
|
|
80
|
+
* {@link wrapOverwideTablesInElement} — a `pie-table-scroll` wrapper, or a
|
|
81
|
+
* `<table>` moving inside one. An observer-driven caller ignores such a record
|
|
82
|
+
* so its own wrap does not schedule a second pass.
|
|
83
|
+
*/
|
|
84
|
+
export function isOverwideTableWrapMutation(record) {
|
|
85
|
+
return isOverwideWrapMutation(record, TABLE_SPEC);
|
|
86
|
+
}
|
|
@@ -49,3 +49,19 @@ export declare function wrapOverwideInElement(root: Element, spec: OverwideWrapS
|
|
|
49
49
|
* is returned unchanged and the browser re-run on hydrate performs the wrap.
|
|
50
50
|
*/
|
|
51
51
|
export declare function wrapOverwideMarkup(markup: string, spec: OverwideWrapSpec): string;
|
|
52
|
+
/**
|
|
53
|
+
* True when `record` mentions nothing but this module's own live-DOM output for
|
|
54
|
+
* `spec`: a wrapper landing beside the node it wraps, or that node moving inside
|
|
55
|
+
* it.
|
|
56
|
+
*
|
|
57
|
+
* An observer-driven caller re-runs {@link wrapOverwideInElement} on every
|
|
58
|
+
* mutation tick, and the wrap itself inserts elements — so without this test the
|
|
59
|
+
* pass retriggers the observer that scheduled it, converging only because the
|
|
60
|
+
* wrap is idempotent. Ignoring the pass's own records is what removes the
|
|
61
|
+
* retrigger; a PIE element that re-renders over its own subtree and drops the
|
|
62
|
+
* wrapper is the case where absorbing it instead becomes a sustained loop.
|
|
63
|
+
*
|
|
64
|
+
* Conservative in the safe direction: anything it cannot account for reads as
|
|
65
|
+
* foreign, which costs one extra pass and never a missed wrap.
|
|
66
|
+
*/
|
|
67
|
+
export declare function isOverwideWrapMutation(record: MutationRecord, spec: OverwideWrapSpec): boolean;
|
|
@@ -89,3 +89,55 @@ export function wrapOverwideMarkup(markup, spec) {
|
|
|
89
89
|
});
|
|
90
90
|
return wrapped > 0 ? body.innerHTML : markup;
|
|
91
91
|
}
|
|
92
|
+
function isElement(node) {
|
|
93
|
+
return node.nodeType === 1;
|
|
94
|
+
}
|
|
95
|
+
/** A `spec` wrapper holding nothing but the nodes `spec` wraps. */
|
|
96
|
+
function isWrapperFor(node, spec) {
|
|
97
|
+
if (!isElement(node))
|
|
98
|
+
return false;
|
|
99
|
+
if (!node.classList?.contains(spec.wrapperClass))
|
|
100
|
+
return false;
|
|
101
|
+
for (const child of Array.from(node.children)) {
|
|
102
|
+
if (!child.matches(spec.selector))
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
/** A node `spec` wraps, currently held by a `spec` wrapper. */
|
|
108
|
+
function isWrappedBy(node, spec) {
|
|
109
|
+
if (!isElement(node))
|
|
110
|
+
return false;
|
|
111
|
+
const parent = node.parentElement;
|
|
112
|
+
if (!parent?.classList?.contains(spec.wrapperClass))
|
|
113
|
+
return false;
|
|
114
|
+
return node.matches(spec.selector);
|
|
115
|
+
}
|
|
116
|
+
function everyNodeIsWrapOutput(nodes, spec) {
|
|
117
|
+
for (const node of Array.from(nodes)) {
|
|
118
|
+
if (!isWrapperFor(node, spec) && !isWrappedBy(node, spec))
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* True when `record` mentions nothing but this module's own live-DOM output for
|
|
125
|
+
* `spec`: a wrapper landing beside the node it wraps, or that node moving inside
|
|
126
|
+
* it.
|
|
127
|
+
*
|
|
128
|
+
* An observer-driven caller re-runs {@link wrapOverwideInElement} on every
|
|
129
|
+
* mutation tick, and the wrap itself inserts elements — so without this test the
|
|
130
|
+
* pass retriggers the observer that scheduled it, converging only because the
|
|
131
|
+
* wrap is idempotent. Ignoring the pass's own records is what removes the
|
|
132
|
+
* retrigger; a PIE element that re-renders over its own subtree and drops the
|
|
133
|
+
* wrapper is the case where absorbing it instead becomes a sustained loop.
|
|
134
|
+
*
|
|
135
|
+
* Conservative in the safe direction: anything it cannot account for reads as
|
|
136
|
+
* foreign, which costs one extra pass and never a missed wrap.
|
|
137
|
+
*/
|
|
138
|
+
export function isOverwideWrapMutation(record, spec) {
|
|
139
|
+
if (record.type !== "childList")
|
|
140
|
+
return false;
|
|
141
|
+
return (everyNodeIsWrapOutput(record.addedNodes, spec) &&
|
|
142
|
+
everyNodeIsWrapOutput(record.removedNodes, spec));
|
|
143
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Containment for tool overlays that position themselves.
|
|
3
|
+
*
|
|
4
|
+
* An overlay that draws its own surface computes its own coordinates, and those
|
|
5
|
+
* coordinates are only meaningful against the box the browser resolved as its
|
|
6
|
+
* containing block. Reading that box from the element rather than from
|
|
7
|
+
* `window` is what keeps a tool correct wherever a host mounts it: the same
|
|
8
|
+
* numbers that centre a tool in the viewport put it outside a pane.
|
|
9
|
+
*/
|
|
10
|
+
/** Distance kept clear of the block's edges so an overlay's controls stay reachable. */
|
|
11
|
+
export declare const DEFAULT_CONTAINMENT_GUTTER = 4;
|
|
12
|
+
export interface Size {
|
|
13
|
+
width: number;
|
|
14
|
+
height: number;
|
|
15
|
+
}
|
|
16
|
+
export interface Point {
|
|
17
|
+
x: number;
|
|
18
|
+
y: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The box `element` resolves its `left`/`top` against: its containing block, or
|
|
22
|
+
* the viewport when that is the initial containing block.
|
|
23
|
+
*
|
|
24
|
+
* `offsetParent` is the containing block as the browser resolved it, including
|
|
25
|
+
* across a shadow boundary, which is what makes this correct for a tool mounted
|
|
26
|
+
* by a host it knows nothing about. Returns `undefined` outside a browser.
|
|
27
|
+
*/
|
|
28
|
+
export declare function resolveContainingBlockRect(element: HTMLElement | undefined | null): DOMRect | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* Clamps a translate offset applied to a box that is already centred in its
|
|
31
|
+
* containing block, so the box stays inside it.
|
|
32
|
+
*
|
|
33
|
+
* The travel available on each axis is symmetric about the centre: half the
|
|
34
|
+
* block, less half the box and the gutter. A box wider or taller than its block
|
|
35
|
+
* cannot satisfy that on the offending axis, so it stays centred there rather
|
|
36
|
+
* than clamping to an inverted range.
|
|
37
|
+
*/
|
|
38
|
+
export declare function clampOffsetWithinBlock(offset: Point, box: Size, block: Size, gutter?: number): Point;
|
|
39
|
+
/**
|
|
40
|
+
* Clamps an absolute centre point, in containing-block coordinates, so the box
|
|
41
|
+
* it positions stays inside that block.
|
|
42
|
+
*
|
|
43
|
+
* The same invariant as `clampOffsetWithinBlock` in the coordinate system an
|
|
44
|
+
* overlay uses when it writes `left`/`top` instead of translating: the two are
|
|
45
|
+
* conjugate by a translation of half the block, including the degenerate case
|
|
46
|
+
* where a box exceeds its block and centres on the offending axis.
|
|
47
|
+
*/
|
|
48
|
+
export declare function clampPointWithinBlock(point: Point, box: Size, block: Size, gutter?: number): Point;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Containment for tool overlays that position themselves.
|
|
3
|
+
*
|
|
4
|
+
* An overlay that draws its own surface computes its own coordinates, and those
|
|
5
|
+
* coordinates are only meaningful against the box the browser resolved as its
|
|
6
|
+
* containing block. Reading that box from the element rather than from
|
|
7
|
+
* `window` is what keeps a tool correct wherever a host mounts it: the same
|
|
8
|
+
* numbers that centre a tool in the viewport put it outside a pane.
|
|
9
|
+
*/
|
|
10
|
+
/** Distance kept clear of the block's edges so an overlay's controls stay reachable. */
|
|
11
|
+
export const DEFAULT_CONTAINMENT_GUTTER = 4;
|
|
12
|
+
/**
|
|
13
|
+
* The box `element` resolves its `left`/`top` against: its containing block, or
|
|
14
|
+
* the viewport when that is the initial containing block.
|
|
15
|
+
*
|
|
16
|
+
* `offsetParent` is the containing block as the browser resolved it, including
|
|
17
|
+
* across a shadow boundary, which is what makes this correct for a tool mounted
|
|
18
|
+
* by a host it knows nothing about. Returns `undefined` outside a browser.
|
|
19
|
+
*/
|
|
20
|
+
export function resolveContainingBlockRect(element) {
|
|
21
|
+
if (typeof window === "undefined" || !element)
|
|
22
|
+
return undefined;
|
|
23
|
+
const parent = element.offsetParent;
|
|
24
|
+
if (parent instanceof HTMLElement) {
|
|
25
|
+
const rect = parent.getBoundingClientRect();
|
|
26
|
+
if (rect.width > 0 && rect.height > 0)
|
|
27
|
+
return rect;
|
|
28
|
+
}
|
|
29
|
+
return new DOMRect(0, 0, window.innerWidth, window.innerHeight);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Clamps a translate offset applied to a box that is already centred in its
|
|
33
|
+
* containing block, so the box stays inside it.
|
|
34
|
+
*
|
|
35
|
+
* The travel available on each axis is symmetric about the centre: half the
|
|
36
|
+
* block, less half the box and the gutter. A box wider or taller than its block
|
|
37
|
+
* cannot satisfy that on the offending axis, so it stays centred there rather
|
|
38
|
+
* than clamping to an inverted range.
|
|
39
|
+
*/
|
|
40
|
+
export function clampOffsetWithinBlock(offset, box, block, gutter = DEFAULT_CONTAINMENT_GUTTER) {
|
|
41
|
+
const clampAxis = (value, boxExtent, blockExtent) => {
|
|
42
|
+
const travel = blockExtent / 2 - boxExtent / 2 - gutter;
|
|
43
|
+
if (travel <= 0)
|
|
44
|
+
return 0;
|
|
45
|
+
return Math.max(-travel, Math.min(travel, value));
|
|
46
|
+
};
|
|
47
|
+
return {
|
|
48
|
+
x: clampAxis(offset.x, box.width, block.width),
|
|
49
|
+
y: clampAxis(offset.y, box.height, block.height),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Clamps an absolute centre point, in containing-block coordinates, so the box
|
|
54
|
+
* it positions stays inside that block.
|
|
55
|
+
*
|
|
56
|
+
* The same invariant as `clampOffsetWithinBlock` in the coordinate system an
|
|
57
|
+
* overlay uses when it writes `left`/`top` instead of translating: the two are
|
|
58
|
+
* conjugate by a translation of half the block, including the degenerate case
|
|
59
|
+
* where a box exceeds its block and centres on the offending axis.
|
|
60
|
+
*/
|
|
61
|
+
export function clampPointWithinBlock(point, box, block, gutter = DEFAULT_CONTAINMENT_GUTTER) {
|
|
62
|
+
const centre = { x: block.width / 2, y: block.height / 2 };
|
|
63
|
+
const offset = clampOffsetWithinBlock({ x: point.x - centre.x, y: point.y - centre.y }, box, block, gutter);
|
|
64
|
+
return { x: centre.x + offset.x, y: centre.y + offset.y };
|
|
65
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pie-players/pie-players-shared",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.70",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Shared runtime + UI utilities for PIE players",
|
|
6
6
|
"license": "MIT",
|
|
@@ -106,8 +106,8 @@
|
|
|
106
106
|
"dist"
|
|
107
107
|
],
|
|
108
108
|
"dependencies": {
|
|
109
|
-
"@pie-lib/math-rendering-module": "
|
|
110
|
-
"dompurify": "^3.4.
|
|
109
|
+
"@pie-lib/math-rendering-module": "6.0.0",
|
|
110
|
+
"dompurify": "^3.4.14",
|
|
111
111
|
"semver": "^7.8.5"
|
|
112
112
|
},
|
|
113
113
|
"scripts": {
|
|
@@ -116,18 +116,18 @@
|
|
|
116
116
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
117
117
|
"lint": "biome check .",
|
|
118
118
|
"check": "tsc -p tsconfig.json --noEmit",
|
|
119
|
-
"test": "bun test
|
|
119
|
+
"test": "bun test",
|
|
120
120
|
"check-i18n": "bun run src/i18n/scripts/check-coverage.ts",
|
|
121
121
|
"scan-hardcoded": "bun run src/i18n/scripts/scan-hardcoded.ts"
|
|
122
122
|
},
|
|
123
123
|
"devDependencies": {
|
|
124
|
-
"@biomejs/biome": "^2.5.
|
|
125
|
-
"@happy-dom/global-registrator": "^20.11.
|
|
124
|
+
"@biomejs/biome": "^2.5.11",
|
|
125
|
+
"@happy-dom/global-registrator": "^20.11.15",
|
|
126
126
|
"@playwright/test": "^1.62.1",
|
|
127
127
|
"@types/semver": "^7.8.0",
|
|
128
128
|
"esbuild": "^0.28.2",
|
|
129
129
|
"glob": "^13.0.0",
|
|
130
|
-
"svelte": "^5.
|
|
130
|
+
"svelte": "^5.57.0",
|
|
131
131
|
"typescript": "^5.9.3"
|
|
132
132
|
},
|
|
133
133
|
"homepage": "https://github.com/pie-framework/pie-players/tree/master/packages/players-shared#readme",
|