@panphora/clayjs 1.13.1 → 1.15.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/THIRD-PARTY-NOTICES.md +19 -0
- package/dist/clay.standalone.js +69385 -40992
- package/dist/server/checkpoint.cjs +1 -0
- package/dist/server/checkpoint.mjs +8 -0
- package/dist/server/core.cjs +72210 -0
- package/dist/server/rooms.cjs +1 -0
- package/dist/server/rooms.mjs +66 -0
- package/docs/guide.md +127 -99
- package/entries/clay.js +22 -0
- package/entries/sap.js +5 -5
- package/package.json +16 -2
- package/packed-contract.json +18 -3
- package/src/core/boot-phase.js +6 -0
- package/src/core/conflict-download.js +12 -3
- package/src/core/host-meta.js +75 -0
- package/src/core/persist.js +11 -0
- package/src/core/save-core.js +266 -31
- package/src/core/save.js +154 -36
- package/src/core/snapshot.js +51 -13
- package/src/core/style-comparisons.js +37 -0
- package/src/dom/insert-style-tag.js +11 -12
- package/src/lib/derived.js +116 -0
- package/src/lib/effect.js +130 -0
- package/src/lib/mutation.js +3 -1
- package/src/lib/style-adoption.js +9 -0
- package/src/lib/style.js +47 -0
- package/src/live/fast-lane.js +347 -60
- package/src/live/local-lane.js +32 -6
- package/src/live/ops.js +77 -0
- package/src/live/provenance.js +129 -0
- package/src/live/save-history.js +188 -0
- package/src/live/server-lane.js +9 -2
- package/src/live/store.js +194 -16
- package/src/loader-logic.js +16 -4
- package/src/loader.js +37 -1
- package/src/page/address.js +20 -0
- package/src/page/adopt-boot.js +189 -0
- package/src/page/adopt.js +240 -0
- package/src/page/ai-tool.js +188 -0
- package/src/page/announce.js +21 -0
- package/src/page/blocks.js +1170 -0
- package/src/page/blocksel.js +269 -0
- package/src/page/board-drag.js +226 -0
- package/src/page/board-panel.js +353 -0
- package/src/page/board-style.js +146 -0
- package/src/page/board-view.js +447 -0
- package/src/page/board.js +41 -0
- package/src/page/chart.js +220 -0
- package/src/page/checklist.js +143 -0
- package/src/page/chrome.js +587 -0
- package/src/page/csv-import.js +169 -0
- package/src/page/drag.js +202 -0
- package/src/page/drop.js +112 -0
- package/src/page/editor.js +386 -0
- package/src/page/highlight.js +268 -0
- package/src/page/inline.js +60 -1
- package/src/page/items.js +159 -0
- package/src/page/link.js +247 -0
- package/src/page/markdown.js +71 -0
- package/src/page/media.js +570 -0
- package/src/page/merge.js +130 -0
- package/src/page/model.js +71 -9
- package/src/page/placeholder.js +23 -0
- package/src/page/projection.js +647 -0
- package/src/page/recover.js +193 -0
- package/src/page/rich/cell.js +16 -0
- package/src/page/rich/chart.js +104 -0
- package/src/page/rich/csv.js +118 -0
- package/src/page/rich/page-tool.js +86 -0
- package/src/page/rich/tool-runtime.js +195 -0
- package/src/page/save-projection.js +53 -0
- package/src/page/save-states.js +85 -0
- package/src/page/schema.js +12 -5
- package/src/page/table-kinds.js +20 -0
- package/src/page/table-paste.js +57 -0
- package/src/page/table-select.js +244 -0
- package/src/page/table-style.js +25 -0
- package/src/page/table.js +650 -0
- package/src/page/themes/content.css +46 -0
- package/src/page/themes/light-1.css +269 -0
- package/src/page/themes/light-2.css +211 -0
- package/src/page/themes/light-3.css +130 -0
- package/src/page/themes/light-4.css +299 -0
- package/src/page/tool-data.js +25 -0
- package/src/page/tool-view.js +68 -0
- package/src/page/tools.js +133 -0
- package/src/page/view.js +136 -0
- package/src/page/ybind/author.js +25 -0
- package/src/page/ybind/binding.js +92 -0
- package/src/page/ybind/board-commands.js +170 -0
- package/src/page/ybind/board.js +89 -0
- package/src/page/ybind/bridge.js +382 -0
- package/src/page/ybind/conversion.js +26 -0
- package/src/page/ybind/core.js +7 -0
- package/src/page/ybind/decode.js +208 -0
- package/src/page/ybind/doc.js +58 -0
- package/src/page/ybind/external.js +190 -0
- package/src/page/ybind/group.js +124 -0
- package/src/page/ybind/index.js +12 -0
- package/src/page/ybind/layout.js +27 -0
- package/src/page/ybind/marks.js +18 -0
- package/src/page/ybind/migrate.js +152 -0
- package/src/page/ybind/moves.js +288 -0
- package/src/page/ybind/origins.js +257 -0
- package/src/page/ybind/parts.js +44 -0
- package/src/page/ybind/projection-map.js +92 -0
- package/src/page/ybind/provider.js +285 -0
- package/src/page/ybind/recovery.js +411 -0
- package/src/page/ybind/seed.js +27 -0
- package/src/page/ybind/table.js +166 -0
- package/src/page/ybind/tokens.js +93 -0
- package/src/page/ybind/walk.js +333 -0
- package/src/page/ybind/writer.js +307 -0
- package/src/plugins/derive.js +12 -0
- package/src/plugins/indicator.js +16 -6
- package/src/plugins/live.js +742 -65
- package/src/plugins/page.js +547 -0
- package/src/plugins/undo.js +62 -4
- package/src/server/checkpoint/index.js +218 -0
- package/src/server/rooms/index.js +59 -0
- package/src/server/rooms/live-data-ops.js +369 -0
- package/src/server/rooms/live-data-rooms.js +965 -0
- package/src/server/rooms/live-data-tags.js +49 -0
- package/src/server/rooms/live-page-history.js +41 -0
- package/src/server/rooms/live-provenance.js +142 -0
- package/src/server/rooms/page-admission.js +79 -0
- package/src/server/rooms/page-conversion.js +62 -0
- package/src/server/rooms/page-rebase.js +108 -0
- package/src/server/rooms/page-state.js +91 -0
- package/src/server/rooms/page-write-admission.js +63 -0
- package/src/server/rooms/page-write-guard.js +23 -0
- package/src/server/rooms/recent.js +18 -0
- package/src/server/rooms/spec-wire.js +17 -0
- package/src/standalone.js +22 -0
- package/src/sync/conflict-revert.js +2 -0
- package/src/sync/conflicts.js +2 -2
- package/src/sync/live-sync.js +36 -8
- package/src/sync/stream.js +62 -16
- package/src/ui/index.js +4 -0
- package/src/ui/toast.js +21 -5
- package/src/vendor/pierre/pierre.vendor.js +3 -0
- package/src/vendor/sap-core.vendor.js +9 -0
- package/src/vendor/sap-ownership.vendor.js +111 -0
- package/src/vendor/yjs.vendor.js +7 -0
- package/src/page/binding.js +0 -647
- package/src/page/replay.js +0 -578
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { ownsAttr, paintsOf, stripPaints, eachPainted } from "../vendor/sap-ownership.vendor.js";
|
|
2
|
+
import { bootPhase } from "../core/boot-phase.js";
|
|
3
|
+
|
|
4
|
+
// Values a derive engine (sapjs) paints from the page's records: attr:, class:,
|
|
5
|
+
// css:, text and show output inside a [sap] root. They are saved like any other
|
|
6
|
+
// content, but they are never an edit: comparisons, the dirty gate, undo and
|
|
7
|
+
// merges leave them out, and the engine repaints them after every merge, before
|
|
8
|
+
// anything records a baseline. Which parts of an element are painted is read
|
|
9
|
+
// from its own attributes, so the same answer holds for the live page, a
|
|
10
|
+
// snapshot clone and a parsed frame. No expression is evaluated here.
|
|
11
|
+
|
|
12
|
+
export const isDerivedAttr = (el, name) => ownsAttr(el, name);
|
|
13
|
+
|
|
14
|
+
export const ownsDerivedText = (el) => !!el && el.nodeType === 1 && !!paintsOf(el)?.text;
|
|
15
|
+
|
|
16
|
+
/** Remove every painted value from a detached tree, in place. Never the live page. */
|
|
17
|
+
export const stripDerived = (root) => stripPaints(root);
|
|
18
|
+
|
|
19
|
+
/** Every painted value under root, in document order, as one string. */
|
|
20
|
+
export function paintSignature(root) {
|
|
21
|
+
const parts = [];
|
|
22
|
+
eachPainted(root, (el, p) => {
|
|
23
|
+
parts.push(el.localName);
|
|
24
|
+
for (const n of p.attrs) if (n !== "sap-error") parts.push(n + "=" + (el.getAttribute(n) ?? "\u0000"));
|
|
25
|
+
for (const t of p.classes) parts.push("." + t + "=" + el.classList.contains(t));
|
|
26
|
+
for (const v of p.vars) parts.push(v + "=" + el.style.getPropertyValue(v));
|
|
27
|
+
if (p.text) parts.push("#" + el.textContent);
|
|
28
|
+
});
|
|
29
|
+
return parts.join("\u0001");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** paintSignature of a document's bytes, such as the file on disk. */
|
|
33
|
+
export function paintSignatureOfHtml(html) {
|
|
34
|
+
return paintSignature(new DOMParser().parseFromString(html, "text/html"));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function withoutTokens(value, owned, split, join, nameOf) {
|
|
38
|
+
return split(value || "").map((t) => t.trim()).filter((t) => t && !owned.includes(nameOf(t))).sort().join(join);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// A class or style change that only touched painted tokens.
|
|
42
|
+
function onlyPaintedTokens(el, name, before, after) {
|
|
43
|
+
const p = paintsOf(el);
|
|
44
|
+
if (!p) return false;
|
|
45
|
+
if (name === "class") {
|
|
46
|
+
if (!p.classes.length) return false;
|
|
47
|
+
const rest = (v) => withoutTokens(v, p.classes, (s) => s.split(/\s+/), " ", (t) => t);
|
|
48
|
+
return rest(before) === rest(after);
|
|
49
|
+
}
|
|
50
|
+
if (!p.vars.length) return false;
|
|
51
|
+
const rest = (v) => withoutTokens(v, p.vars, (s) => s.split(";"), ";", (t) => t.split(":")[0].trim());
|
|
52
|
+
return rest(before) === rest(after);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** True when a mutation-hub change only rewrote painted output. */
|
|
56
|
+
export function isDerivedChange(change) {
|
|
57
|
+
if (change.__derived !== undefined) return change.__derived;
|
|
58
|
+
const el = change.element;
|
|
59
|
+
let derived = false;
|
|
60
|
+
if (el && el.nodeType === 1) {
|
|
61
|
+
if (change.type === "attribute") {
|
|
62
|
+
const name = change.attribute;
|
|
63
|
+
derived = isDerivedAttr(el, name) ||
|
|
64
|
+
((name === "class" || name === "style") && onlyPaintedTokens(el, name, change.oldValue, change.newValue));
|
|
65
|
+
} else if (change.type === "characterData") {
|
|
66
|
+
derived = ownsDerivedText(el);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
change.__derived = derived;
|
|
70
|
+
return derived;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const engines = new Set();
|
|
74
|
+
let mountedOnce = false;
|
|
75
|
+
|
|
76
|
+
/** True while a derive engine is registered, or the loader holds boot for one. */
|
|
77
|
+
export function derivedActive() {
|
|
78
|
+
return engines.size > 0 || bootPhase.holdSettle;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const mountWaiters = [];
|
|
82
|
+
|
|
83
|
+
/** Run every registered engine's repaint, synchronously. Live sync calls it right after a merge applies. */
|
|
84
|
+
export function repaintDerived() {
|
|
85
|
+
for (const repaint of [...engines]) {
|
|
86
|
+
try {
|
|
87
|
+
repaint();
|
|
88
|
+
} catch (err) {
|
|
89
|
+
console.error("clayjs: a derived repaint failed:", err);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Calls fn once an engine has finished its first pass, or after timeoutMs without one. */
|
|
95
|
+
export function afterDerivedMount(fn, timeoutMs) {
|
|
96
|
+
if (mountedOnce || typeof document === "undefined" || !document.querySelector("[sap]")) return fn();
|
|
97
|
+
let done = false;
|
|
98
|
+
const once = () => {
|
|
99
|
+
if (done) return;
|
|
100
|
+
done = true;
|
|
101
|
+
fn();
|
|
102
|
+
};
|
|
103
|
+
mountWaiters.push(once);
|
|
104
|
+
setTimeout(once, timeoutMs);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export const derived = {
|
|
108
|
+
register(repaint) {
|
|
109
|
+
engines.add(repaint);
|
|
110
|
+
return () => engines.delete(repaint);
|
|
111
|
+
},
|
|
112
|
+
mounted() {
|
|
113
|
+
mountedOnce = true;
|
|
114
|
+
for (const fn of mountWaiters.splice(0)) fn();
|
|
115
|
+
},
|
|
116
|
+
};
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// clay.effect(fn): run fn soon and again after anything that can change what it
|
|
2
|
+
// reads. Local edits arrive through the mutation hub; a live-sync merge runs with
|
|
3
|
+
// the hub paused, so clay:sync-applied covers it; undo and redo write through the
|
|
4
|
+
// hub as well, and their events are a backstop. fn runs with the hub paused, so
|
|
5
|
+
// its own writes (derived counts, totals) never re-trigger it, never start an
|
|
6
|
+
// autosave and never enter undo history. The first run is on a microtask too:
|
|
7
|
+
// pausing the hub mid-way through an undo.commit would split the command. fn must
|
|
8
|
+
// be synchronous: writes after an await land outside the pause and would loop.
|
|
9
|
+
//
|
|
10
|
+
// In edit mode each effect warns once about the misuses it can see: a write to
|
|
11
|
+
// saved, dirty-tracked content (saved with the file, but never marks it unsaved),
|
|
12
|
+
// an effect created inside an effect, undo.commit inside an effect (checked by the
|
|
13
|
+
// undo plugin through effect.running), and an effect that keeps re-running.
|
|
14
|
+
import { isTabLocalAttr } from "./root-attrs.js";
|
|
15
|
+
|
|
16
|
+
const LOOP_RUNS = 50;
|
|
17
|
+
const LOOP_WINDOW_MS = 1000;
|
|
18
|
+
const NO_SNAPSHOT = '[clay~="no-snapshot"], [no-snapshot], [snapshot-remove]';
|
|
19
|
+
|
|
20
|
+
let running = 0;
|
|
21
|
+
|
|
22
|
+
const describe = (node) => {
|
|
23
|
+
const el = node.nodeType === 1 ? node : node.parentElement;
|
|
24
|
+
if (!el) return "a detached node";
|
|
25
|
+
let s = el.localName;
|
|
26
|
+
if (el.id) s += "#" + el.id;
|
|
27
|
+
else if (el.classList.length) s += "." + [...el.classList].slice(0, 2).join(".");
|
|
28
|
+
return "<" + s + ">";
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
function savedWrite(records) {
|
|
32
|
+
const policy = window.clay?.region?.resolveRegionPolicy;
|
|
33
|
+
if (!policy) return null;
|
|
34
|
+
const tracked = (el) => !!el && !el.closest(NO_SNAPSHOT) && policy(el).dirtyTracked;
|
|
35
|
+
for (const r of records) {
|
|
36
|
+
if (r.type === "attributes" && isTabLocalAttr(r.attributeName, r.target)) continue;
|
|
37
|
+
const el = r.target.nodeType === 1 ? r.target : r.target.parentElement;
|
|
38
|
+
if (!tracked(el)) continue;
|
|
39
|
+
// A child added or removed counts only when it is saved itself: chrome
|
|
40
|
+
// inserted into a saved parent (clay="editor-ui") is stripped on save.
|
|
41
|
+
if (r.type === "childList" && ![...r.addedNodes, ...r.removedNodes].some((n) => n.nodeType !== 1 || tracked(n))) continue;
|
|
42
|
+
return r.target;
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function effect(fn) {
|
|
48
|
+
const clay = window.clay;
|
|
49
|
+
const Mutation = clay.Mutation;
|
|
50
|
+
const warned = new Set();
|
|
51
|
+
const warn = (key, message) => {
|
|
52
|
+
if (warned.has(key)) return;
|
|
53
|
+
warned.add(key);
|
|
54
|
+
console.warn("clay.effect: " + message);
|
|
55
|
+
};
|
|
56
|
+
let queued = false;
|
|
57
|
+
let stopped = false;
|
|
58
|
+
let windowStart = 0;
|
|
59
|
+
let runs = 0;
|
|
60
|
+
|
|
61
|
+
if (running && clay.isEditMode) {
|
|
62
|
+
console.warn("clay.effect: an effect was created inside another effect, so a new one starts on every run. Create effects once, in clay.start.");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const watchLoop = () => {
|
|
66
|
+
const now = Date.now();
|
|
67
|
+
if (now - windowStart > LOOP_WINDOW_MS) { windowStart = now; runs = 0; }
|
|
68
|
+
if (++runs > LOOP_RUNS) {
|
|
69
|
+
warn("loop", `this effect ran more than ${LOOP_RUNS} times in a second. Something keeps changing the document, often a timer or async callback the effect started.`);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const run = () => {
|
|
74
|
+
queued = false;
|
|
75
|
+
if (stopped) return;
|
|
76
|
+
let observer = null;
|
|
77
|
+
Mutation.pause();
|
|
78
|
+
running++;
|
|
79
|
+
try {
|
|
80
|
+
if (clay.isEditMode === true) {
|
|
81
|
+
watchLoop();
|
|
82
|
+
if (!warned.has("saved")) {
|
|
83
|
+
observer = new MutationObserver(() => {});
|
|
84
|
+
observer.observe(document.documentElement, { subtree: true, childList: true, attributes: true, characterData: true });
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const result = fn();
|
|
88
|
+
if (result && typeof result.then === "function") {
|
|
89
|
+
stop();
|
|
90
|
+
console.error("clay.effect: fn returned a promise. It must be synchronous, so this effect was stopped.");
|
|
91
|
+
}
|
|
92
|
+
} catch (err) {
|
|
93
|
+
console.error("clay.effect:", err);
|
|
94
|
+
} finally {
|
|
95
|
+
running--;
|
|
96
|
+
Mutation.resume();
|
|
97
|
+
const records = observer ? observer.takeRecords() : [];
|
|
98
|
+
observer?.disconnect();
|
|
99
|
+
if (records.length) {
|
|
100
|
+
const target = savedWrite(records);
|
|
101
|
+
if (target) {
|
|
102
|
+
warn("saved", `this effect changed ${describe(target)}, which is saved with the file. Effect writes never mark the page unsaved, so a merge can leave it dirty with no save scheduled. Draw derived values inside clay="no-dirty" or clay="editor-ui".`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
const schedule = () => {
|
|
108
|
+
if (queued || stopped) return;
|
|
109
|
+
queued = true;
|
|
110
|
+
queueMicrotask(run);
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const offMutation = Mutation.onAnyChange({ debounce: 0, require: "observed" }, schedule);
|
|
114
|
+
document.addEventListener("clay:sync-applied", schedule);
|
|
115
|
+
clay.undo?.on?.("undo", schedule);
|
|
116
|
+
clay.undo?.on?.("redo", schedule);
|
|
117
|
+
|
|
118
|
+
const stop = () => {
|
|
119
|
+
stopped = true;
|
|
120
|
+
offMutation();
|
|
121
|
+
document.removeEventListener("clay:sync-applied", schedule);
|
|
122
|
+
clay.undo?.off?.("undo", schedule);
|
|
123
|
+
clay.undo?.off?.("redo", schedule);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
schedule();
|
|
127
|
+
return stop;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
Object.defineProperty(effect, "running", { get: () => running > 0 });
|
package/src/lib/mutation.js
CHANGED
|
@@ -71,6 +71,7 @@ import { resolveRegionPolicy, isInert, strictestPolicy, skipForPolicy } from './
|
|
|
71
71
|
import { isPolicyAttribute } from './region-capabilities.js';
|
|
72
72
|
import { ROOT_LIBRARY_ATTRS, ROUTE_ROOT_PREFIX, ROUTE_ACTIVE_ATTR } from './root-attrs.js';
|
|
73
73
|
import { isEphemeralAttr } from './ephemeral.js';
|
|
74
|
+
import { isDerivedChange } from './derived.js';
|
|
74
75
|
|
|
75
76
|
import { isUserDrivenNow, markUserDriven } from './user-gesture.js';
|
|
76
77
|
|
|
@@ -215,6 +216,7 @@ const localMutation = {
|
|
|
215
216
|
// and their legacy equivalents), resolved against the callback's `require`.
|
|
216
217
|
let filteredChanges = changes.filter(
|
|
217
218
|
change => {
|
|
219
|
+
if (require !== 'observed' && isDerivedChange(change)) return false;
|
|
218
220
|
const currentSkipped = skipForPolicy(this._policyForChange(change), require, skip);
|
|
219
221
|
if (!change.policyTransition) return !currentSkipped;
|
|
220
222
|
const previousSkipped = skipForPolicy(change.previousPolicy, require, skip);
|
|
@@ -647,7 +649,7 @@ const localMutation = {
|
|
|
647
649
|
// Runs synchronously in the MO callback; skipped during paused-morph drains.
|
|
648
650
|
if (!onlyNonPausable && isUserDrivenNow()) {
|
|
649
651
|
for (const change of changes) {
|
|
650
|
-
if (!skipForPolicy(this._policyForChange(change), 'dirty')) {
|
|
652
|
+
if (!isDerivedChange(change) && !skipForPolicy(this._policyForChange(change), 'dirty')) {
|
|
651
653
|
markUserDriven();
|
|
652
654
|
break;
|
|
653
655
|
}
|
package/src/lib/style.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { insertStyles, getStyleBaseUrl } from "../dom/insert-style-tag.js";
|
|
2
|
+
import Mutation from "./mutation.js";
|
|
3
|
+
import { adoptStyleComparisons } from "./style-adoption.js";
|
|
4
|
+
|
|
5
|
+
// A style a script owns. The tag saves with the page, so the next load paints
|
|
6
|
+
// styled before any script runs. Each call writes the current CSS into the saved
|
|
7
|
+
// tag in place (or points the saved <link> at the current href), so a page saved
|
|
8
|
+
// by an older script picks up newer styles. The tag is no-dirty: an update is
|
|
9
|
+
// carried by the next real save and never counts as an edit, starts an autosave
|
|
10
|
+
// or raises the close warning. A caller can add region tokens (editor-ui keeps a
|
|
11
|
+
// style a plugin re-injects on every boot out of the saved file).
|
|
12
|
+
const marker = (extra = []) => (el) => {
|
|
13
|
+
const tokens = (el.getAttribute("clay") ?? "").split(/\s+/).filter(Boolean);
|
|
14
|
+
const missing = ["no-dirty", ...extra].filter((token) => !tokens.includes(token));
|
|
15
|
+
if (missing.length) el.setAttribute("clay", [...tokens, ...missing].join(" "));
|
|
16
|
+
};
|
|
17
|
+
const markNoDirty = marker();
|
|
18
|
+
|
|
19
|
+
export function style(nameOrHref, css) {
|
|
20
|
+
return writeStyle(nameOrHref, css);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function styleWithLegacyId(name, css, legacyId, tokens = []) {
|
|
24
|
+
return writeStyle(name, css, legacyId, tokens);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function writeStyle(nameOrHref, css, legacyId, tokens = []) {
|
|
28
|
+
const inline = typeof css === "string";
|
|
29
|
+
const identity = inline ? { name: nameOrHref, legacyId } : { href: getStyleBaseUrl(nameOrHref) };
|
|
30
|
+
const legacy = legacyId === undefined ? [] : document.querySelectorAll(`style[id="${legacyId}"]:not([data-name])`);
|
|
31
|
+
Mutation.pause();
|
|
32
|
+
try {
|
|
33
|
+
adoptStyleComparisons(identity);
|
|
34
|
+
for (const el of legacy) el.dataset.name = nameOrHref;
|
|
35
|
+
if (inline) {
|
|
36
|
+
const el = insertStyles(nameOrHref, css, marker(tokens));
|
|
37
|
+
if (legacyId !== undefined) el.id = legacyId;
|
|
38
|
+
return el;
|
|
39
|
+
}
|
|
40
|
+
return insertStyles(nameOrHref, (link) => {
|
|
41
|
+
markNoDirty(link);
|
|
42
|
+
if (link.getAttribute("href") !== nameOrHref) link.setAttribute("href", nameOrHref);
|
|
43
|
+
});
|
|
44
|
+
} finally {
|
|
45
|
+
Mutation.resume();
|
|
46
|
+
}
|
|
47
|
+
}
|