@camp.dev/bones 0.2.0 → 0.4.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.
@@ -0,0 +1,220 @@
1
+ import { MeasuredOverlay } from "./overlay.mjs";
2
+ //#region src/element/boundary.ts
3
+ const DEFAULT_DELAY = 200;
4
+ const DEFAULT_MIN_DURATION = 400;
5
+ const UPGRADE_PROPERTIES = [
6
+ "busy",
7
+ "force",
8
+ "delay",
9
+ "minDuration",
10
+ "transition",
11
+ "precision"
12
+ ];
13
+ const swallow = () => {};
14
+ function parseMs(value, fallback) {
15
+ if (value === null || value.trim() === "") return fallback;
16
+ const ms = Number(value);
17
+ return Number.isFinite(ms) && ms >= 0 ? ms : fallback;
18
+ }
19
+ const Base = typeof HTMLElement === "undefined" ? class {} : HTMLElement;
20
+ var BonesBoundary = class extends Base {
21
+ static observedAttributes = [
22
+ "busy",
23
+ "force",
24
+ "aria-busy",
25
+ "inert",
26
+ "precision"
27
+ ];
28
+ #state = "idle";
29
+ #timer;
30
+ #connected = false;
31
+ #shownAt = 0;
32
+ #hideToken = 0;
33
+ #writingOutput = false;
34
+ #overlay = new MeasuredOverlay(this);
35
+ get busy() {
36
+ return this.hasAttribute("busy");
37
+ }
38
+ set busy(value) {
39
+ this.toggleAttribute("busy", Boolean(value));
40
+ }
41
+ get force() {
42
+ return this.hasAttribute("force");
43
+ }
44
+ set force(value) {
45
+ this.toggleAttribute("force", Boolean(value));
46
+ }
47
+ get delay() {
48
+ return parseMs(this.getAttribute("delay"), 200);
49
+ }
50
+ set delay(value) {
51
+ if (value === null || value === void 0) this.removeAttribute("delay");
52
+ else this.setAttribute("delay", String(value));
53
+ }
54
+ get minDuration() {
55
+ return parseMs(this.getAttribute("min-duration"), 400);
56
+ }
57
+ set minDuration(value) {
58
+ if (value === null || value === void 0) this.removeAttribute("min-duration");
59
+ else this.setAttribute("min-duration", String(value));
60
+ }
61
+ get transition() {
62
+ return this.getAttribute("transition") === "none" ? "none" : "auto";
63
+ }
64
+ set transition(value) {
65
+ if (value === "none") this.setAttribute("transition", "none");
66
+ else this.removeAttribute("transition");
67
+ }
68
+ get precision() {
69
+ return this.getAttribute("precision") === "measured" ? "measured" : "css";
70
+ }
71
+ set precision(value) {
72
+ if (value === "measured") this.setAttribute("precision", "measured");
73
+ else this.removeAttribute("precision");
74
+ }
75
+ get showing() {
76
+ return this.#state === "showing" || this.#state === "draining" || this.#state === "hiding";
77
+ }
78
+ connectedCallback() {
79
+ for (const name of UPGRADE_PROPERTIES) this.#upgradeProperty(name);
80
+ this.#connected = true;
81
+ this.#syncPrecision();
82
+ if (this.getAttribute("aria-busy") === "true" && !this.showing) {
83
+ this.#show();
84
+ if (!this.busy && !this.force) this.#evaluate();
85
+ return;
86
+ }
87
+ this.#evaluate();
88
+ }
89
+ disconnectedCallback() {
90
+ this.#overlay.pause();
91
+ this.#connected = false;
92
+ this.#clearTimer();
93
+ if (this.#state === "pending") this.#state = "idle";
94
+ else if (this.#state === "hiding") this.#state = "showing";
95
+ }
96
+ attributeChangedCallback(name) {
97
+ if (!this.#connected) return;
98
+ if (name === "aria-busy" || name === "inert") {
99
+ this.#defendOutput();
100
+ return;
101
+ }
102
+ if (name === "precision") {
103
+ this.#syncPrecision();
104
+ return;
105
+ }
106
+ this.#evaluate();
107
+ }
108
+ #defendOutput() {
109
+ if (this.#writingOutput) return;
110
+ if (this.#state !== "showing" && this.#state !== "draining") return;
111
+ if (this.getAttribute("aria-busy") === "true" && this.hasAttribute("inert")) return;
112
+ this.#writeOutput(true);
113
+ }
114
+ #syncPrecision() {
115
+ if (this.precision === "measured") {
116
+ this.#overlay.prepare();
117
+ if (this.showing) this.#overlay.activate();
118
+ } else this.#overlay.deactivate();
119
+ }
120
+ #upgradeProperty(name) {
121
+ if (!Object.prototype.hasOwnProperty.call(this, name)) return;
122
+ const self = this;
123
+ const value = self[name];
124
+ delete self[name];
125
+ self[name] = value;
126
+ }
127
+ #clearTimer() {
128
+ if (this.#timer !== void 0) clearTimeout(this.#timer);
129
+ this.#timer = void 0;
130
+ }
131
+ #evaluate() {
132
+ if (this.force) {
133
+ if (this.#state === "draining" || this.#state === "hiding") this.#resume();
134
+ else if (this.#state !== "showing") this.#show();
135
+ return;
136
+ }
137
+ if (this.busy) {
138
+ if (this.#state === "idle") {
139
+ const delay = this.delay;
140
+ if (delay === 0) this.#show();
141
+ else {
142
+ this.#state = "pending";
143
+ this.#timer = setTimeout(() => this.#show(), delay);
144
+ }
145
+ } else if (this.#state === "draining" || this.#state === "hiding") this.#resume();
146
+ return;
147
+ }
148
+ if (this.#state === "pending") {
149
+ this.#clearTimer();
150
+ this.#state = "idle";
151
+ } else if (this.#state === "showing" || this.#state === "draining") {
152
+ this.#clearTimer();
153
+ const remaining = this.#shownAt + this.minDuration - Date.now();
154
+ if (remaining <= 0) this.#hide();
155
+ else {
156
+ this.#state = "draining";
157
+ this.#timer = setTimeout(() => this.#hide(), remaining);
158
+ }
159
+ }
160
+ }
161
+ #resume() {
162
+ this.#clearTimer();
163
+ this.#state = "showing";
164
+ this.#writeOutput(true);
165
+ }
166
+ #show() {
167
+ this.#clearTimer();
168
+ this.#state = "showing";
169
+ this.#shownAt = Date.now();
170
+ this.#writeOutput(true);
171
+ if (this.precision === "measured") this.#overlay.activate();
172
+ this.dispatchEvent(new CustomEvent("bones:show", {
173
+ bubbles: true,
174
+ composed: true
175
+ }));
176
+ }
177
+ #hide() {
178
+ this.#clearTimer();
179
+ this.#state = "hiding";
180
+ const token = ++this.#hideToken;
181
+ const update = () => {
182
+ if (this.#state !== "hiding" || token !== this.#hideToken) return;
183
+ this.#state = "idle";
184
+ this.#writeOutput(false);
185
+ this.#overlay.deactivate();
186
+ this.dispatchEvent(new CustomEvent("bones:hide", {
187
+ bubbles: true,
188
+ composed: true
189
+ }));
190
+ };
191
+ if (this.#canTransition()) {
192
+ const transition = document.startViewTransition(update);
193
+ transition?.ready?.catch(swallow);
194
+ transition?.updateCallbackDone?.catch(swallow);
195
+ transition?.finished?.catch(swallow);
196
+ } else update();
197
+ }
198
+ #writeOutput(shown) {
199
+ this.#writingOutput = true;
200
+ try {
201
+ if (shown) {
202
+ this.setAttribute("aria-busy", "true");
203
+ this.toggleAttribute("inert", true);
204
+ } else {
205
+ this.removeAttribute("aria-busy");
206
+ this.removeAttribute("inert");
207
+ }
208
+ } finally {
209
+ this.#writingOutput = false;
210
+ }
211
+ }
212
+ #canTransition() {
213
+ if (this.transition === "none") return false;
214
+ if (typeof document.startViewTransition !== "function") return false;
215
+ if (typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches) return false;
216
+ return true;
217
+ }
218
+ };
219
+ //#endregion
220
+ export { BonesBoundary, DEFAULT_DELAY, DEFAULT_MIN_DURATION };
@@ -0,0 +1,14 @@
1
+ import { BonesBoundary, DEFAULT_DELAY, DEFAULT_MIN_DURATION } from "./boundary.mjs";
2
+
3
+ //#region src/element/index.d.ts
4
+ declare global {
5
+ interface HTMLElementTagNameMap {
6
+ "bones-boundary": BonesBoundary;
7
+ }
8
+ interface HTMLElementEventMap {
9
+ "bones:show": CustomEvent;
10
+ "bones:hide": CustomEvent;
11
+ }
12
+ }
13
+ //#endregion
14
+ export { BonesBoundary, DEFAULT_DELAY, DEFAULT_MIN_DURATION };
@@ -0,0 +1,6 @@
1
+ import { BonesBoundary, DEFAULT_DELAY, DEFAULT_MIN_DURATION } from "./boundary.mjs";
2
+ //#region src/element/index.ts
3
+ if (typeof customElements !== "undefined") if (customElements.get("bones-boundary")) console.warn("bones-boundary is already defined; @camp.dev/bones/element did not register");
4
+ else customElements.define("bones-boundary", BonesBoundary);
5
+ //#endregion
6
+ export { BonesBoundary, DEFAULT_DELAY, DEFAULT_MIN_DURATION };
@@ -0,0 +1,99 @@
1
+ //#region src/element/measure.ts
2
+ const BLOCK_TAGS = new Set([
3
+ "img",
4
+ "svg",
5
+ "video",
6
+ "canvas",
7
+ "picture",
8
+ "iframe",
9
+ "embed",
10
+ "object",
11
+ "audio",
12
+ "button",
13
+ "input",
14
+ "select",
15
+ "textarea",
16
+ "progress",
17
+ "meter"
18
+ ]);
19
+ const TEXT_BAR_SCALE = .55;
20
+ function verticalOverlap(a, b) {
21
+ return Math.min(a.top + a.height, b.top + b.height) - Math.max(a.top, b.top);
22
+ }
23
+ function mergeLineRects(rects) {
24
+ const sorted = [...rects].sort((a, b) => a.top - b.top || a.left - b.left);
25
+ const merged = [];
26
+ for (const rect of sorted) {
27
+ const line = merged.findLast((candidate) => {
28
+ if (verticalOverlap(candidate, rect) < Math.min(candidate.height, rect.height) / 2) return false;
29
+ return rect.left - (candidate.left + candidate.width) <= Math.max(candidate.height, rect.height) / 2;
30
+ });
31
+ if (line === void 0) {
32
+ merged.push({ ...rect });
33
+ continue;
34
+ }
35
+ const right = Math.max(line.left + line.width, rect.left + rect.width);
36
+ const bottom = Math.max(line.top + line.height, rect.top + rect.height);
37
+ line.left = Math.min(line.left, rect.left);
38
+ line.top = Math.min(line.top, rect.top);
39
+ line.width = right - line.left;
40
+ line.height = bottom - line.top;
41
+ }
42
+ return merged;
43
+ }
44
+ function isVisible(rect) {
45
+ return rect.width > 0 && rect.height > 0;
46
+ }
47
+ function toRect(rect) {
48
+ return {
49
+ left: rect.left,
50
+ top: rect.top,
51
+ width: rect.width,
52
+ height: rect.height
53
+ };
54
+ }
55
+ function measureBones(root) {
56
+ const doc = root.ownerDocument;
57
+ const blocks = [];
58
+ const textRects = [];
59
+ const visit = (node) => {
60
+ if (node.nodeType === Node.TEXT_NODE) {
61
+ if (node.data.trim() === "") return;
62
+ const range = doc.createRange();
63
+ range.selectNodeContents(node);
64
+ if (typeof range.getClientRects !== "function") return;
65
+ for (const rect of Array.from(range.getClientRects())) {
66
+ const converted = toRect(rect);
67
+ if (isVisible(converted)) textRects.push(converted);
68
+ }
69
+ return;
70
+ }
71
+ if (node.nodeType !== Node.ELEMENT_NODE) return;
72
+ const el = node;
73
+ if (el.getAttribute("data-bones-auto") === "off") return;
74
+ if (BLOCK_TAGS.has(el.localName)) {
75
+ const rect = toRect(el.getBoundingClientRect());
76
+ if (isVisible(rect)) blocks.push({
77
+ kind: "block",
78
+ ...rect
79
+ });
80
+ return;
81
+ }
82
+ for (const child of node.childNodes) visit(child);
83
+ };
84
+ for (const child of root.childNodes) visit(child);
85
+ const bones = [...blocks];
86
+ for (const line of mergeLineRects(textRects)) {
87
+ const height = line.height * TEXT_BAR_SCALE;
88
+ bones.push({
89
+ kind: "text",
90
+ left: line.left,
91
+ top: line.top + (line.height - height) / 2,
92
+ width: line.width,
93
+ height
94
+ });
95
+ }
96
+ return bones;
97
+ }
98
+ //#endregion
99
+ export { measureBones };
@@ -0,0 +1,184 @@
1
+ import { measureBones } from "./measure.mjs";
2
+ //#region src/element/overlay.ts
3
+ const OVERLAY_CSS = `
4
+ :host([precision="measured"]) {
5
+ display: block;
6
+ position: relative;
7
+ }
8
+ /* Hidden content still lays out, so re-measurement stays valid. Deliberately
9
+ not !important: outer-tree rules beat ::slotted, which is what lets the
10
+ auto.css opt-out rule re-show exempt subtrees (and lets an author
11
+ visibility rule on a direct child win — a documented edge). */
12
+ :host([data-bones-measured]) ::slotted(*) {
13
+ visibility: hidden;
14
+ }
15
+ [part~="overlay"] {
16
+ position: absolute;
17
+ inset: 0;
18
+ pointer-events: none;
19
+ }
20
+ [part~="bone"] {
21
+ position: absolute;
22
+ background: var(--bone-base, color-mix(in srgb, rgb(from currentcolor r g b / 1) 12%, transparent));
23
+ border-radius: var(--bone-radius, 4px);
24
+ }
25
+ @keyframes bone-shimmer {
26
+ 0% { background-position: 200% 0; }
27
+ 100% { background-position: -200% 0; }
28
+ }
29
+ @keyframes bone-pulse {
30
+ 0%, 100% { opacity: 1; }
31
+ 50% { opacity: 0.5; }
32
+ }
33
+ [part~="overlay"]:not([data-bone-animate]) [part~="bone"],
34
+ [part~="overlay"][data-bone-animate="shimmer"] [part~="bone"] {
35
+ animation: bone-shimmer var(--bone-duration, 1.5s) ease-in-out infinite;
36
+ background: linear-gradient(
37
+ 90deg,
38
+ var(--bone-base, color-mix(in srgb, rgb(from currentcolor r g b / 1) 12%, transparent)) 25%,
39
+ var(--bone-highlight, color-mix(in srgb, rgb(from currentcolor r g b / 1) 6%, transparent)) 50%,
40
+ var(--bone-base, color-mix(in srgb, rgb(from currentcolor r g b / 1) 12%, transparent)) 75%
41
+ );
42
+ background-size: 200% 100%;
43
+ }
44
+ [part~="overlay"][data-bone-animate="pulse"] [part~="bone"] {
45
+ animation: bone-pulse var(--bone-duration, 1.5s) ease-in-out infinite;
46
+ }
47
+ [part~="overlay"][data-bone-animate="none"] [part~="bone"] {
48
+ animation: none;
49
+ }
50
+ /* Matches the shimmer/pulse selectors above at equal (0,3,0) specificity so
51
+ this override always wins the cascade instead of losing to source order.
52
+ data-bone-animate="none" is deliberately excluded: none still means none. */
53
+ @media (prefers-reduced-motion: reduce) {
54
+ [part~="overlay"]:not([data-bone-animate]) [part~="bone"],
55
+ [part~="overlay"][data-bone-animate="shimmer"] [part~="bone"],
56
+ [part~="overlay"][data-bone-animate="pulse"] [part~="bone"] {
57
+ animation: bone-pulse 2s ease-in-out infinite;
58
+ background: var(--bone-base, color-mix(in srgb, rgb(from currentcolor r g b / 1) 12%, transparent));
59
+ background-size: auto;
60
+ }
61
+ }
62
+ `;
63
+ let sharedSheet;
64
+ function applyStyles(root) {
65
+ if ("adoptedStyleSheets" in root && typeof CSSStyleSheet !== "undefined" && "replaceSync" in CSSStyleSheet.prototype) {
66
+ if (sharedSheet === void 0) {
67
+ sharedSheet = new CSSStyleSheet();
68
+ sharedSheet.replaceSync(OVERLAY_CSS);
69
+ }
70
+ root.adoptedStyleSheets = [...root.adoptedStyleSheets, sharedSheet];
71
+ return;
72
+ }
73
+ const style = root.ownerDocument.createElement("style");
74
+ style.textContent = OVERLAY_CSS;
75
+ root.append(style);
76
+ }
77
+ var MeasuredOverlay = class {
78
+ #host;
79
+ #container;
80
+ #observer;
81
+ #mutations;
82
+ #active = false;
83
+ #ownsAutoOff = false;
84
+ constructor(host) {
85
+ this.#host = host;
86
+ }
87
+ get active() {
88
+ return this.#active;
89
+ }
90
+ prepare() {
91
+ if (this.#host.shadowRoot) return;
92
+ const root = this.#host.attachShadow({ mode: "open" });
93
+ root.append(this.#host.ownerDocument.createElement("slot"));
94
+ applyStyles(root);
95
+ }
96
+ activate() {
97
+ this.prepare();
98
+ if (!this.#active) {
99
+ this.#active = true;
100
+ if (!this.#host.hasAttribute("data-bones-auto")) {
101
+ this.#host.setAttribute("data-bones-auto", "off");
102
+ this.#ownsAutoOff = true;
103
+ }
104
+ this.#host.setAttribute("data-bones-measured", "");
105
+ }
106
+ if (!this.#renderBars()) {
107
+ this.deactivate();
108
+ return;
109
+ }
110
+ this.#observe();
111
+ }
112
+ deactivate() {
113
+ this.#unobserve();
114
+ this.#container?.remove();
115
+ this.#container = void 0;
116
+ if (!this.#active) return;
117
+ this.#active = false;
118
+ this.#host.removeAttribute("data-bones-measured");
119
+ if (this.#ownsAutoOff) {
120
+ this.#host.removeAttribute("data-bones-auto");
121
+ this.#ownsAutoOff = false;
122
+ }
123
+ }
124
+ pause() {
125
+ this.#unobserve();
126
+ }
127
+ #observe() {
128
+ if (typeof ResizeObserver !== "undefined" && !this.#observer) {
129
+ this.#observer = new ResizeObserver(() => this.#invalidate());
130
+ this.#observer.observe(this.#host);
131
+ }
132
+ if (typeof MutationObserver !== "undefined" && !this.#mutations) {
133
+ this.#mutations = new MutationObserver(() => this.#invalidate());
134
+ this.#mutations.observe(this.#host, {
135
+ childList: true,
136
+ subtree: true,
137
+ characterData: true
138
+ });
139
+ }
140
+ }
141
+ #unobserve() {
142
+ this.#observer?.disconnect();
143
+ this.#observer = void 0;
144
+ this.#mutations?.disconnect();
145
+ this.#mutations = void 0;
146
+ }
147
+ #invalidate() {
148
+ if (this.#active && !this.#renderBars()) this.deactivate();
149
+ }
150
+ #renderBars() {
151
+ const root = this.#host.shadowRoot;
152
+ if (!root) return false;
153
+ const bones = measureBones(this.#host);
154
+ if (bones.length === 0) return false;
155
+ const doc = this.#host.ownerDocument;
156
+ if (!this.#container) {
157
+ this.#container = doc.createElement("div");
158
+ this.#container.setAttribute("part", "overlay");
159
+ this.#container.setAttribute("aria-hidden", "true");
160
+ root.append(this.#container);
161
+ }
162
+ const animate = this.#host.closest("[data-bone-animate]")?.getAttribute("data-bone-animate");
163
+ if (animate) this.#container.setAttribute("data-bone-animate", animate);
164
+ else this.#container.removeAttribute("data-bone-animate");
165
+ const origin = this.#container.getBoundingClientRect();
166
+ const style = getComputedStyle(this.#container);
167
+ const layoutWidth = Number.parseFloat(style.width);
168
+ const layoutHeight = Number.parseFloat(style.height);
169
+ const scaleX = layoutWidth > 0 ? origin.width / layoutWidth : 1;
170
+ const scaleY = layoutHeight > 0 ? origin.height / layoutHeight : 1;
171
+ this.#container.replaceChildren(...bones.map((bone) => {
172
+ const bar = doc.createElement("div");
173
+ bar.setAttribute("part", `bone bone-${bone.kind}`);
174
+ bar.style.left = `${(bone.left - origin.left) / scaleX}px`;
175
+ bar.style.top = `${(bone.top - origin.top) / scaleY}px`;
176
+ bar.style.width = `${bone.width / scaleX}px`;
177
+ bar.style.height = `${bone.height / scaleY}px`;
178
+ return bar;
179
+ }));
180
+ return true;
181
+ }
182
+ };
183
+ //#endregion
184
+ export { MeasuredOverlay };
@@ -0,0 +1,40 @@
1
+ import { BonesBoundary as BonesBoundary$1 } from "../element/boundary.mjs";
2
+ import { HTMLAttributes, ReactNode, Ref } from "react";
3
+
4
+ //#region src/react/boundary.d.ts
5
+ interface ElementAttributes extends HTMLAttributes<HTMLElement> {
6
+ busy?: boolean;
7
+ force?: boolean;
8
+ delay?: number;
9
+ "min-duration"?: number;
10
+ transition?: "auto" | "none";
11
+ precision?: "css" | "measured";
12
+ class?: string;
13
+ ref?: Ref<BonesBoundary$1>;
14
+ [dataAttribute: `data-${string}`]: string | number | boolean | undefined;
15
+ }
16
+ declare module "react" {
17
+ namespace JSX {
18
+ interface IntrinsicElements {
19
+ "bones-boundary": ElementAttributes;
20
+ }
21
+ }
22
+ }
23
+ interface BonesBoundaryProps extends Omit<ElementAttributes, "min-duration" | "inert" | "aria-busy" | "class"> {
24
+ minDuration?: number;
25
+ onShow?: (event: CustomEvent) => void;
26
+ onHide?: (event: CustomEvent) => void;
27
+ }
28
+ declare function BonesBoundary({
29
+ busy,
30
+ force,
31
+ delay,
32
+ minDuration,
33
+ transition,
34
+ onShow,
35
+ onHide,
36
+ children,
37
+ ...rest
38
+ }: BonesBoundaryProps): ReactNode;
39
+ //#endregion
40
+ export { BonesBoundary, BonesBoundaryProps };
@@ -0,0 +1,21 @@
1
+ import { createElement } from "react";
2
+ //#region src/react/boundary.ts
3
+ function BonesBoundary({ busy, force, delay, minDuration, transition, onShow, onHide, children, ...rest }) {
4
+ return createElement("bones-boundary", {
5
+ ...rest,
6
+ busy: busy ? true : void 0,
7
+ force: force ? true : void 0,
8
+ delay,
9
+ "min-duration": minDuration,
10
+ transition,
11
+ ...force ? {
12
+ "aria-busy": "true",
13
+ inert: true
14
+ } : {},
15
+ suppressHydrationWarning: true,
16
+ "onbones:show": onShow,
17
+ "onbones:hide": onHide
18
+ }, children);
19
+ }
20
+ //#endregion
21
+ export { BonesBoundary };
@@ -1,7 +1,6 @@
1
1
  import { boneAttributes } from "../core/attributes.mjs";
2
- import { cache, cloneElement, createElement, isValidElement } from "react";
2
+ import { cloneElement, createElement, isValidElement } from "react";
3
3
  //#region src/react/create-bones.ts
4
- const getBonesContext = cache(() => ({ loading: false }));
5
4
  function withKey(node, key) {
6
5
  return isValidElement(node) ? cloneElement(node, { key }) : node;
7
6
  }
@@ -40,9 +39,6 @@ function createBones(dataOrOptions, maybeOptions) {
40
39
  } else if (data != null && data === forceBones) {
41
40
  isLoading = true;
42
41
  resolved = void 0;
43
- } else if (getBonesContext().loading) {
44
- isLoading = true;
45
- resolved = void 0;
46
42
  } else if (data != null && data instanceof Promise) resolved = readPromise(data);
47
43
  else resolved = data;
48
44
  let boneCallIndex = 0;
@@ -70,4 +66,4 @@ function createBones(dataOrOptions, maybeOptions) {
70
66
  };
71
67
  }
72
68
  //#endregion
73
- export { createBones, forceBones, getBonesContext, readPromise };
69
+ export { createBones, forceBones, readPromise };
@@ -1,4 +1,4 @@
1
1
  import { BoneOptions, BoneType, MinMax, isMinMax, minMax } from "../core/attributes.mjs";
2
2
  import { CreateBonesOptions, CreateBonesReturn, createBones, forceBones, readPromise } from "./create-bones.mjs";
3
- import { Bones, BonesForce } from "./bones.mjs";
4
- export { type BoneOptions, type BoneType, Bones, BonesForce, type CreateBonesOptions, type CreateBonesReturn, type MinMax, createBones, forceBones, isMinMax, minMax, readPromise };
3
+ import { BonesBoundary, BonesBoundaryProps } from "./boundary.mjs";
4
+ export { type BoneOptions, type BoneType, BonesBoundary, type BonesBoundaryProps, type CreateBonesOptions, type CreateBonesReturn, type MinMax, createBones, forceBones, isMinMax, minMax, readPromise };
@@ -1,4 +1,4 @@
1
1
  import { isMinMax, minMax } from "../core/attributes.mjs";
2
2
  import { createBones, forceBones, readPromise } from "./create-bones.mjs";
3
- import { Bones, BonesForce } from "./bones.mjs";
4
- export { Bones, BonesForce, createBones, forceBones, isMinMax, minMax, readPromise };
3
+ import { BonesBoundary } from "./boundary.mjs";
4
+ export { BonesBoundary, createBones, forceBones, isMinMax, minMax, readPromise };
@@ -0,0 +1,4 @@
1
+ //#region src/server/bootstrap.d.ts
2
+ declare const BOOTSTRAP_SCRIPT = "<script>function __bonesSwap(e,r){var t=document.querySelector('template[data-bones-chunk=\"'+e+'\"]'),n=document.querySelector('[data-bones-slot=\"'+e+'\"]');n?(t&&(n.replaceChildren(t.content),t.remove()),r&&n.setAttribute(\"data-bones-error\",\"\"),\"boolean\"==typeof n.busy?n.busy=!1:(n.removeAttribute(\"busy\"),n.removeAttribute(\"aria-busy\"),n.removeAttribute(\"inert\"))):t&&t.remove()}</script>";
3
+ //#endregion
4
+ export { BOOTSTRAP_SCRIPT };
@@ -0,0 +1,3 @@
1
+ const BOOTSTRAP_SCRIPT = `<script>function __bonesSwap(e,r){var t=document.querySelector('template[data-bones-chunk="'+e+'"]'),n=document.querySelector('[data-bones-slot="'+e+'"]');n?(t&&(n.replaceChildren(t.content),t.remove()),r&&n.setAttribute("data-bones-error",""),"boolean"==typeof n.busy?n.busy=!1:(n.removeAttribute("busy"),n.removeAttribute("aria-busy"),n.removeAttribute("inert"))):t&&t.remove()}<\/script>`;
2
+ //#endregion
3
+ export { BOOTSTRAP_SCRIPT };
@@ -0,0 +1,17 @@
1
+ import { BOOTSTRAP_SCRIPT } from "./bootstrap.mjs";
2
+
3
+ //#region src/server/index.d.ts
4
+ declare function renderBoundary(id: string, fallbackHtml: string, attrs?: string): string;
5
+ declare function renderChunk(id: string, html: string): string;
6
+ declare function renderErrorChunk(id: string, html?: string): string;
7
+ interface StreamBonesOptions {
8
+ /**
9
+ * Renders the error HTML for a rejected slot. Returning undefined (and
10
+ * throwing) falls back to the bare error chunk, which keeps the boundary's
11
+ * fallback children.
12
+ */
13
+ onError?: (id: string, error: unknown) => string | undefined;
14
+ }
15
+ declare function streamBones(shell: string, slots: Record<string, Promise<string>>, options?: StreamBonesOptions): ReadableStream<Uint8Array>;
16
+ //#endregion
17
+ export { BOOTSTRAP_SCRIPT, StreamBonesOptions, renderBoundary, renderChunk, renderErrorChunk, streamBones };