aberdeen 1.18.2 → 1.19.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/README.md +9 -0
- package/dist/src/aberdeen.d.ts +27 -0
- package/{dist-docs/assets/aberdeen/aberdeen.js → dist/src/aberdeen.dev.js} +776 -15
- package/dist/src/aberdeen.dev.js.map +12 -0
- package/dist/src/aberdeen.js +59 -13
- package/dist/src/aberdeen.js.map +3 -3
- package/dist/src/devtools.d.ts +26 -0
- package/html-to-aberdeen +9 -5
- package/package.json +17 -10
- package/skill/A.md +127 -0
- package/skill/Dispatcher.md +51 -0
- package/skill/OPAQUE.md +15 -0
- package/skill/PromiseProxy.md +25 -0
- package/skill/Route.md +50 -0
- package/skill/SKILL.md +272 -156
- package/skill/applyCanon.md +20 -0
- package/skill/applyPrediction.md +15 -0
- package/skill/back.md +11 -0
- package/skill/clean.md +43 -0
- package/skill/clone.md +15 -0
- package/skill/copy.md +40 -0
- package/skill/count.md +32 -0
- package/skill/cssVars.md +15 -0
- package/skill/darkMode.md +26 -0
- package/skill/derive.md +56 -0
- package/skill/developer-tools.md +23 -0
- package/skill/disableCreateDestroy.md +11 -0
- package/skill/dump.md +37 -0
- package/skill/freeze.md +20 -0
- package/skill/full-example-multi-page-app.md +146 -0
- package/skill/go.md +25 -0
- package/skill/grow.md +13 -0
- package/skill/html-to-aberdeen.md +9 -0
- package/skill/insertCss.md +79 -0
- package/skill/insertGlobalCss.md +68 -0
- package/skill/interceptLinks.md +19 -0
- package/skill/invertString.md +32 -0
- package/skill/isEmpty.md +38 -0
- package/skill/map.md +16 -0
- package/skill/matchCurrent.md +27 -0
- package/skill/merge.md +27 -0
- package/skill/mount.md +48 -0
- package/skill/multiMap.md +15 -0
- package/skill/onEach.md +14 -0
- package/skill/partition.md +15 -0
- package/skill/peek.md +36 -0
- package/skill/persistScroll.md +13 -0
- package/skill/proxy.md +11 -0
- package/skill/push.md +13 -0
- package/skill/ref.md +39 -0
- package/skill/runQueue.md +34 -0
- package/skill/setErrorHandler.md +48 -0
- package/skill/setLog.md +9 -0
- package/skill/setSpacingCssVars.md +31 -0
- package/skill/shrink.md +13 -0
- package/skill/unmountAll.md +9 -0
- package/skill/unproxy.md +43 -0
- package/skill/up.md +13 -0
- package/src/aberdeen.ts +194 -73
- package/src/devtools.ts +685 -0
- package/dist-docs/.nojekyll +0 -1
- package/dist-docs/Tutorial/index.html +0 -305
- package/dist-docs/aberdeen/A/index.html +0 -116
- package/dist-docs/aberdeen/CUSTOM_DUMP/index.html +0 -7
- package/dist-docs/aberdeen/NO_COPY/index.html +0 -2
- package/dist-docs/aberdeen/OPAQUE/index.html +0 -12
- package/dist-docs/aberdeen/PromiseProxy/index.html +0 -8
- package/dist-docs/aberdeen/clean/index.html +0 -11
- package/dist-docs/aberdeen/clone/index.html +0 -5
- package/dist-docs/aberdeen/copy/index.html +0 -22
- package/dist-docs/aberdeen/count/index.html +0 -7
- package/dist-docs/aberdeen/cssVars/index.html +0 -11
- package/dist-docs/aberdeen/darkMode/index.html +0 -9
- package/dist-docs/aberdeen/default/index.html +0 -342
- package/dist-docs/aberdeen/derive/index.html +0 -18
- package/dist-docs/aberdeen/disableCreateDestroy/index.html +0 -6
- package/dist-docs/aberdeen/dump/index.html +0 -11
- package/dist-docs/aberdeen/index.html +0 -54
- package/dist-docs/aberdeen/insertCss/index.html +0 -30
- package/dist-docs/aberdeen/insertGlobalCss/index.html +0 -19
- package/dist-docs/aberdeen/invertString/index.html +0 -13
- package/dist-docs/aberdeen/isEmpty/index.html +0 -12
- package/dist-docs/aberdeen/map/index.html +0 -26
- package/dist-docs/aberdeen/merge/index.html +0 -11
- package/dist-docs/aberdeen/mount/index.html +0 -17
- package/dist-docs/aberdeen/multiMap/index.html +0 -28
- package/dist-docs/aberdeen/onEach/index.html +0 -19
- package/dist-docs/aberdeen/partition/index.html +0 -36
- package/dist-docs/aberdeen/peek/index.html +0 -40
- package/dist-docs/aberdeen/proxy/index.html +0 -35
- package/dist-docs/aberdeen/ref/index.html +0 -13
- package/dist-docs/aberdeen/runQueue/index.html +0 -13
- package/dist-docs/aberdeen/setErrorHandler/index.html +0 -16
- package/dist-docs/aberdeen/setSpacingCssVars/index.html +0 -8
- package/dist-docs/aberdeen/unmountAll/index.html +0 -5
- package/dist-docs/aberdeen/unproxy/index.html +0 -11
- package/dist-docs/assets/aberdeen/aberdeen.d.ts +0 -963
- package/dist-docs/assets/aberdeen/aberdeen.js.map +0 -11
- package/dist-docs/assets/aberdeen/dispatcher.d.ts +0 -57
- package/dist-docs/assets/aberdeen/dispatcher.js +0 -66
- package/dist-docs/assets/aberdeen/dispatcher.js.map +0 -10
- package/dist-docs/assets/aberdeen/helpers/reverseSortedSet.d.ts +0 -96
- package/dist-docs/assets/aberdeen/prediction.d.ts +0 -33
- package/dist-docs/assets/aberdeen/prediction.js +0 -112
- package/dist-docs/assets/aberdeen/prediction.js.map +0 -10
- package/dist-docs/assets/aberdeen/route.d.ts +0 -137
- package/dist-docs/assets/aberdeen/route.js +0 -287
- package/dist-docs/assets/aberdeen/route.js.map +0 -10
- package/dist-docs/assets/aberdeen/transitions.d.ts +0 -18
- package/dist-docs/assets/aberdeen/transitions.js +0 -47
- package/dist-docs/assets/aberdeen/transitions.js.map +0 -10
- package/dist-docs/assets/custom.js +0 -573
- package/dist-docs/assets/favicon.png +0 -0
- package/dist-docs/assets/hierarchy.js +0 -1
- package/dist-docs/assets/highlight.css +0 -134
- package/dist-docs/assets/icons.js +0 -18
- package/dist-docs/assets/icons.svg +0 -1
- package/dist-docs/assets/main.js +0 -60
- package/dist-docs/assets/navigation.js +0 -1
- package/dist-docs/assets/search.js +0 -1
- package/dist-docs/assets/style.css +0 -1640
- package/dist-docs/dispatcher/Dispatcher/index.html +0 -21
- package/dist-docs/dispatcher/MATCH_FAILED/index.html +0 -2
- package/dist-docs/dispatcher/MATCH_REST/index.html +0 -2
- package/dist-docs/dispatcher/index.html +0 -4
- package/dist-docs/hierarchy.html +0 -1
- package/dist-docs/index.html +0 -61
- package/dist-docs/media/CHANGELOG.md +0 -266
- package/dist-docs/modules.html +0 -1
- package/dist-docs/prediction/applyCanon/index.html +0 -13
- package/dist-docs/prediction/applyPrediction/index.html +0 -8
- package/dist-docs/prediction/index.html +0 -9
- package/dist-docs/route/Route/index.html +0 -23
- package/dist-docs/route/back/index.html +0 -4
- package/dist-docs/route/current/index.html +0 -2
- package/dist-docs/route/go/index.html +0 -14
- package/dist-docs/route/index.html +0 -15
- package/dist-docs/route/interceptLinks/index.html +0 -8
- package/dist-docs/route/matchCurrent/index.html +0 -10
- package/dist-docs/route/persistScroll/index.html +0 -6
- package/dist-docs/route/push/index.html +0 -6
- package/dist-docs/route/setLog/index.html +0 -3
- package/dist-docs/route/up/index.html +0 -5
- package/dist-docs/sitemap.xml +0 -239
- package/dist-docs/transitions/grow/index.html +0 -6
- package/dist-docs/transitions/index.html +0 -5
- package/dist-docs/transitions/shrink/index.html +0 -6
- package/skill/aberdeen.md +0 -4586
- package/skill/dispatcher.md +0 -129
- package/skill/prediction.md +0 -73
- package/skill/route.md +0 -322
- package/skill/transitions.md +0 -59
package/src/aberdeen.ts
CHANGED
|
@@ -16,6 +16,7 @@ interface QueueRunner {
|
|
|
16
16
|
|
|
17
17
|
let sortedQueue: ReverseSortedSet<QueueRunner, "prio"> | undefined; // When set, a runQueue is scheduled or currently running.
|
|
18
18
|
let runQueueDepth = 0; // Incremented when a queue event causes another queue event to be added. Reset when queue is empty. Throw when >= 42 to break (infinite) recursion.
|
|
19
|
+
let freezeCount = 0; // While > 0 (see `freeze()`), `runQueue` is paused and updates accumulate.
|
|
19
20
|
let topRedrawScope: Scope | undefined; // The scope that triggered the current redraw. Elements drawn at this scope level may trigger 'create' animations.
|
|
20
21
|
// During a teardown (a scope being cleaned), this holds the element that *survives* that teardown:
|
|
21
22
|
// the element whose child content is being removed, but which itself stays in the DOM. Value-restoring
|
|
@@ -108,6 +109,7 @@ function queue(runner: QueueRunner) {
|
|
|
108
109
|
* ```
|
|
109
110
|
*/
|
|
110
111
|
export function runQueue(): void {
|
|
112
|
+
if (freezeCount) return; // Paused by `freeze()`; updates stay queued until thawed.
|
|
111
113
|
let time = Date.now();
|
|
112
114
|
while (sortedQueue) {
|
|
113
115
|
const runner = sortedQueue.fetchLast();
|
|
@@ -121,6 +123,33 @@ export function runQueue(): void {
|
|
|
121
123
|
if (time > 9) console.debug(`Aberdeen queue took ${time}ms`);
|
|
122
124
|
}
|
|
123
125
|
|
|
126
|
+
/**
|
|
127
|
+
* Pause processing of reactive updates until the returned *thaw* function is called.
|
|
128
|
+
*
|
|
129
|
+
* While frozen, changes to observed data still accumulate, but no re-renders run. Freezes
|
|
130
|
+
* stack: if there are multiple outstanding freezes, redraws resume only once the last one is
|
|
131
|
+
* thawed. This is useful to batch an async burst of changes into a single update pass, or to
|
|
132
|
+
* hold the UI steady (e.g. the dev tools use it for "freeze redraws").
|
|
133
|
+
*
|
|
134
|
+
* @returns A function that releases this freeze. Calling it more than once has no effect.
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```typescript
|
|
138
|
+
* const thaw = A.freeze();
|
|
139
|
+
* // ...make many changes without intermediate redraws...
|
|
140
|
+
* thaw(); // redraws run now (if no other freezes remain)
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
export function freeze(): () => void {
|
|
144
|
+
freezeCount++;
|
|
145
|
+
let released = false;
|
|
146
|
+
return () => {
|
|
147
|
+
if (released) return;
|
|
148
|
+
released = true;
|
|
149
|
+
if (!--freezeCount) runQueue();
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
124
153
|
/**
|
|
125
154
|
* A sort key, as used by {@link onEach}, is a value that determines the order of items. It can
|
|
126
155
|
* be a number, string, or an array of numbers/strings. The sort key is used to sort items
|
|
@@ -315,6 +344,7 @@ abstract class ContentScope extends Scope {
|
|
|
315
344
|
if (!this.changes) {
|
|
316
345
|
this.changes = new Map();
|
|
317
346
|
queue(this);
|
|
347
|
+
dev?.schedule(this, index, oldData, newData, new Error());
|
|
318
348
|
}
|
|
319
349
|
|
|
320
350
|
let targetDelta = this.changes.get(target);
|
|
@@ -388,6 +418,9 @@ class ChainedScope extends ContentScope {
|
|
|
388
418
|
// We're always adding ourselve as a cleaner, in order to run our own cleaners
|
|
389
419
|
// and to remove ourselve from the queue (if we happen to be in there).
|
|
390
420
|
if (!useParentCleaners) currentScope.cleaners.push(this);
|
|
421
|
+
|
|
422
|
+
// Covers RegularScope, ResultScope and SetArgScope; the enclosing render scope is the parent.
|
|
423
|
+
dev?.create(this, currentScope, new Error());
|
|
391
424
|
}
|
|
392
425
|
|
|
393
426
|
getPrecedingNode(): Node | undefined {
|
|
@@ -425,6 +458,7 @@ class RegularScope extends ChainedScope {
|
|
|
425
458
|
redraw() {
|
|
426
459
|
const savedScope = currentScope;
|
|
427
460
|
currentScope = this;
|
|
461
|
+
dev?.render(this);
|
|
428
462
|
try {
|
|
429
463
|
this.renderer();
|
|
430
464
|
} catch (e) {
|
|
@@ -453,7 +487,10 @@ class MountScope extends ContentScope {
|
|
|
453
487
|
) {
|
|
454
488
|
super();
|
|
455
489
|
this.svg = el.namespaceURI === 'http://www.w3.org/2000/svg';
|
|
456
|
-
|
|
490
|
+
|
|
491
|
+
// Register before the first redraw so its render is attributed to this scope.
|
|
492
|
+
dev?.create(this, currentScope, new Error());
|
|
493
|
+
|
|
457
494
|
const oldTopRedrawScope = topRedrawScope;
|
|
458
495
|
topRedrawScope = this;
|
|
459
496
|
this.redraw();
|
|
@@ -604,6 +641,9 @@ class OnEachScope extends Scope {
|
|
|
604
641
|
|
|
605
642
|
currentScope.cleaners.push(this);
|
|
606
643
|
|
|
644
|
+
// Register before creating item scopes, so they can attach under this scope.
|
|
645
|
+
dev?.create(this, currentScope, new Error());
|
|
646
|
+
|
|
607
647
|
// Do _addChild() calls for initial items
|
|
608
648
|
if (target instanceof Array) {
|
|
609
649
|
for (let i = 0; i < target.length; i++) {
|
|
@@ -642,7 +682,12 @@ class OnEachScope extends Scope {
|
|
|
642
682
|
this.changedIndexes = new Map();
|
|
643
683
|
for (const index of indexes.keys()) {
|
|
644
684
|
const oldScope = this.byIndex.get(index);
|
|
645
|
-
if (oldScope)
|
|
685
|
+
if (oldScope) {
|
|
686
|
+
oldScope.remove();
|
|
687
|
+
// The old item scope is discarded here (recreated below if it survives), so drop it
|
|
688
|
+
// from the dev tree; a fresh OnEachItemScope re-attaches itself.
|
|
689
|
+
dev?.delete(oldScope);
|
|
690
|
+
}
|
|
646
691
|
|
|
647
692
|
if (this.target instanceof Set || this.target instanceof Map ? this.target.has(index) : index in this.target) {
|
|
648
693
|
// Item still exists
|
|
@@ -707,6 +752,9 @@ class OnEachItemScope extends ContentScope {
|
|
|
707
752
|
|
|
708
753
|
// Don't register to be cleaned by parent scope, as the OnEachScope will manage this for us (for efficiency)
|
|
709
754
|
|
|
755
|
+
// An onEach item is its own scope; its parent in the tree is the OnEachScope.
|
|
756
|
+
dev?.create(this, this.parent, new Error());
|
|
757
|
+
|
|
710
758
|
if (topRedraw) topRedrawScope = this;
|
|
711
759
|
this.redraw();
|
|
712
760
|
}
|
|
@@ -792,6 +840,7 @@ class OnEachItemScope extends ContentScope {
|
|
|
792
840
|
// Since makeSortKey may get() the Store, we'll need to set currentScope first.
|
|
793
841
|
const savedScope = currentScope;
|
|
794
842
|
currentScope = this;
|
|
843
|
+
dev?.render(this);
|
|
795
844
|
|
|
796
845
|
let sortKey: any;
|
|
797
846
|
try {
|
|
@@ -850,6 +899,7 @@ class OnEachItemScope extends ContentScope {
|
|
|
850
899
|
function addNode(el: Element, node: Node) {
|
|
851
900
|
if (el !== currentScope.el) {
|
|
852
901
|
el.appendChild(node);
|
|
902
|
+
dev?.node(currentScope, node, false);
|
|
853
903
|
return;
|
|
854
904
|
}
|
|
855
905
|
const parentEl = currentScope.el;
|
|
@@ -859,6 +909,7 @@ function addNode(el: Element, node: Node) {
|
|
|
859
909
|
prevNode ? prevNode.nextSibling : parentEl.firstChild,
|
|
860
910
|
);
|
|
861
911
|
currentScope.lastChild = node;
|
|
912
|
+
dev?.node(currentScope, node, true);
|
|
862
913
|
}
|
|
863
914
|
|
|
864
915
|
/**
|
|
@@ -868,6 +919,62 @@ function addNode(el: Element, node: Node) {
|
|
|
868
919
|
const ROOT_SCOPE = new RootScope();
|
|
869
920
|
let currentScope: ContentScope = ROOT_SCOPE;
|
|
870
921
|
|
|
922
|
+
// === Developer tools instrumentation =======================================
|
|
923
|
+
// The core emits events through `dev(type, ...args)` only when the dev tools are connected.
|
|
924
|
+
// The tools — the scope tree, node→scope / element→scope maps, stack traces, the UI, and the
|
|
925
|
+
// `?abdev=1` / Ctrl-Alt-A bootstrap — all live in `./devtools`, which *wraps* this module:
|
|
926
|
+
// in a dev build the `aberdeen` entry point is that wrapper, and it installs the sink via
|
|
927
|
+
// `A._setDev` synchronously at load (before the app's first `mount()`, so no buffering).
|
|
928
|
+
//
|
|
929
|
+
// There is deliberately no build-time flag: the hooks are always present, costing only a
|
|
930
|
+
// nullish check (`dev?.x(…)` doesn't even evaluate its arguments — no `new Error()`) when no
|
|
931
|
+
// tools are attached. Keeping them unconditional avoids the footgun of a stripped core being
|
|
932
|
+
// paired with the wrapper, or vice-versa.
|
|
933
|
+
|
|
934
|
+
/** @internal The instrumentation hooks the dev tools install via {@link _setDev}. Scopes are
|
|
935
|
+
* passed opaquely (`any`); the tools read `.constructor.name`, `.el`, etc. off them. */
|
|
936
|
+
export interface DevHooks {
|
|
937
|
+
/** A scope was created under `parent` (its onEach scope, for items); `err` holds the creation
|
|
938
|
+
* stack, or is `undefined` for scopes replayed on connect (they predate the tools). */
|
|
939
|
+
create(scope: any, parent: any, err: Error | undefined): void;
|
|
940
|
+
/** A scope is about to (re)render, replacing the DOM nodes and child scopes it last produced. */
|
|
941
|
+
render(scope: any): void;
|
|
942
|
+
/** A proxy change (`target[index]`, `oldData`→`newData`) scheduled `scope` to re-render; `err` holds the triggering stack. */
|
|
943
|
+
schedule(scope: any, index: any, oldData: any, newData: any, err: Error): void;
|
|
944
|
+
/** `scope` inserted DOM `node`; `top` marks a direct sibling at the scope's own level (vs. a descendant). */
|
|
945
|
+
node(scope: any, node: Node, top: boolean): void;
|
|
946
|
+
/** `scope` subscribed to `target[index]`. */
|
|
947
|
+
read(scope: any, target: any, index: any): void;
|
|
948
|
+
/** `scope` was permanently removed (an onEach item that no longer exists). */
|
|
949
|
+
delete(scope: any): void;
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
/** @internal Event sink, installed by the dev-tools wrapper via {@link _setDev}. */
|
|
953
|
+
let dev: DevHooks | undefined;
|
|
954
|
+
|
|
955
|
+
/** @internal Install (or, with `undefined`, remove) the dev-tools hooks. Used by the
|
|
956
|
+
* `./devtools` wrapper. A named `@internal` export (rather than an `A` member) so it is
|
|
957
|
+
* stripped from the public type declarations.
|
|
958
|
+
*
|
|
959
|
+
* On connect the *already existing* scope tree is replayed as `create` events (with no
|
|
960
|
+
* stack — those scopes predate the tools), so the tool shows the current tree immediately,
|
|
961
|
+
* not only what changes afterwards. Their child scopes live in `cleaners` (and onEach items
|
|
962
|
+
* in `byIndex`); nodes/reads/stacks for them fill in naturally as they re-render. */
|
|
963
|
+
export function _setDev(hooks: DevHooks | undefined) {
|
|
964
|
+
dev = hooks;
|
|
965
|
+
if (!hooks) return;
|
|
966
|
+
const childScopes = (scope: any): Scope[] =>
|
|
967
|
+
scope instanceof OnEachScope
|
|
968
|
+
? [...scope.byIndex.values()]
|
|
969
|
+
: (scope.cleaners as any[]).filter((c): c is Scope => c instanceof Scope);
|
|
970
|
+
const replay = (scope: Scope, parent: Scope) => {
|
|
971
|
+
hooks.create(scope, parent, undefined);
|
|
972
|
+
for (const child of childScopes(scope)) replay(child, scope);
|
|
973
|
+
};
|
|
974
|
+
for (const child of childScopes(ROOT_SCOPE)) replay(child, ROOT_SCOPE);
|
|
975
|
+
}
|
|
976
|
+
// === End developer tools instrumentation ===================================
|
|
977
|
+
|
|
871
978
|
/**
|
|
872
979
|
* Execute a function in a never-cleaned root scope. Even {@link unmountAll} will not
|
|
873
980
|
* clean up observers/nodes created by the function.
|
|
@@ -925,6 +1032,8 @@ function subscribe(
|
|
|
925
1032
|
) {
|
|
926
1033
|
if (observer === ROOT_SCOPE || peeking) return;
|
|
927
1034
|
|
|
1035
|
+
if (observer === currentScope) dev?.read(currentScope, target, index);
|
|
1036
|
+
|
|
928
1037
|
let byTarget = subscribers.get(target);
|
|
929
1038
|
if (!byTarget) subscribers.set(target, (byTarget = new Map()));
|
|
930
1039
|
|
|
@@ -2565,6 +2674,10 @@ let cssCount = 0;
|
|
|
2565
2674
|
* Both forms can be mixed: `'m:$3 box-shadow: 0 2px 4px rgba(0,0,0,0.2); bg:$cardBg'`
|
|
2566
2675
|
*
|
|
2567
2676
|
* Supports the same CSS shortcuts as {@link A | A} and CSS variable references with `$` (e.g., `$primary`, `$3`).
|
|
2677
|
+
*
|
|
2678
|
+
* CSS is inserted into the <head> in an order relative to other insert(Global)Css items that is consistent
|
|
2679
|
+
* based on when the containing Aberdeen scope was first defined. This allows changing styles without changing
|
|
2680
|
+
* order-based precedence.
|
|
2568
2681
|
*
|
|
2569
2682
|
* @param style - A concise style string or a style object.
|
|
2570
2683
|
* @returns The unique class name prefix used for scoping (e.g., `.AbdStl1`).
|
|
@@ -2617,13 +2730,87 @@ export function insertCss(style: string | object): string {
|
|
|
2617
2730
|
const prefix = `.AbdStl${++cssCount}`;
|
|
2618
2731
|
const css = typeof style === 'string' ? styleStringToCss(style, prefix) : objectToCss(style, prefix);
|
|
2619
2732
|
if (css) {
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2733
|
+
const cnt = cssSnippetCount++;
|
|
2734
|
+
const key = arrayToStr([currentScope.prio, cnt]);
|
|
2735
|
+
cssSnippets[key] = css;
|
|
2736
|
+
clean(() => delete cssSnippets[key]);
|
|
2623
2737
|
}
|
|
2624
2738
|
return prefix;
|
|
2625
2739
|
}
|
|
2626
|
-
|
|
2740
|
+
|
|
2741
|
+
/**
|
|
2742
|
+
* Inserts CSS rules globally (unscoped).
|
|
2743
|
+
*
|
|
2744
|
+
* Works exactly like {@link insertCss}, but without prefixing selectors with a unique class name.
|
|
2745
|
+
* This is useful for global resets, base styles, or styles that need to apply to the entire document.
|
|
2746
|
+
*
|
|
2747
|
+
* Accepts the same concise style string syntax and CSS shortcuts as {@link insertCss}.
|
|
2748
|
+
* See {@link insertCss} for detailed documentation on syntax and shortcuts.
|
|
2749
|
+
*
|
|
2750
|
+
* @param style - Object with selectors as keys and concise CSS strings as values.
|
|
2751
|
+
*
|
|
2752
|
+
* CSS is inserted into the <head> in an order relative to other insert(Global)Css items that is consistent
|
|
2753
|
+
* based on when the containing Aberdeen scope was first defined. This allows changing styles without changing
|
|
2754
|
+
* order-based precedence.
|
|
2755
|
+
*
|
|
2756
|
+
* @example Global Reset and Base Styles
|
|
2757
|
+
* ```typescript
|
|
2758
|
+
* // Set up global styles using CSS shortcuts
|
|
2759
|
+
* A.insertGlobalCss({
|
|
2760
|
+
* "*": "m:0 p:0 box-sizing:border-box",
|
|
2761
|
+
* "body": "font-family: system-ui, sans-serif; m:0 p:$3 bg:#434 fg:#d0dafa",
|
|
2762
|
+
* "a": "text-decoration:none fg:#57f",
|
|
2763
|
+
* "a:hover": "text-decoration:underline",
|
|
2764
|
+
* "code": "font-family:monospace bg:#222 fg:#afc p:4px r:3px"
|
|
2765
|
+
* });
|
|
2766
|
+
*
|
|
2767
|
+
* A('h2#Title without margins');
|
|
2768
|
+
* A('a#This is a link');
|
|
2769
|
+
* A('code#const x = 42;');
|
|
2770
|
+
* ```
|
|
2771
|
+
*
|
|
2772
|
+
* @example Responsive Global Styles
|
|
2773
|
+
* ```typescript
|
|
2774
|
+
* A.insertGlobalCss({
|
|
2775
|
+
* "html": "font-size:16px",
|
|
2776
|
+
* "body": "line-height:1.6",
|
|
2777
|
+
* "h1, h2, h3": "font-weight:600 mt:$4 mb:$2",
|
|
2778
|
+
* "@media (max-width: 768px)": {
|
|
2779
|
+
* "html": "font-size:14px",
|
|
2780
|
+
* "body": "p:$2"
|
|
2781
|
+
* },
|
|
2782
|
+
* "@media (prefers-color-scheme: dark)": {
|
|
2783
|
+
* "body": "bg:#1a1a1a fg:#e5e5e5",
|
|
2784
|
+
* "code": "bg:#2a2a2a"
|
|
2785
|
+
* }
|
|
2786
|
+
* });
|
|
2787
|
+
* ```
|
|
2788
|
+
* At-rules such as `@media` and `@keyframes` should use nested objects. For keyframes,
|
|
2789
|
+
* the step selectors (`0%`, `50%`, `100%`, etc.) become the nested keys and each value
|
|
2790
|
+
* should be a concise CSS declaration string.
|
|
2791
|
+
*
|
|
2792
|
+
* @example Animation Keyframes
|
|
2793
|
+
* ```typescript
|
|
2794
|
+
* A.insertGlobalCss({
|
|
2795
|
+
* "@keyframes connection-pulse": {
|
|
2796
|
+
* "0%": "box-shadow: inset 0 0 0 0 rgba(255, 70, 70, 0.4);",
|
|
2797
|
+
* "50%": "box-shadow: inset 0 0 0 6px rgba(255, 70, 70, 0.08);",
|
|
2798
|
+
* "100%": "box-shadow: inset 0 0 0 0 rgba(255, 70, 70, 0.4);"
|
|
2799
|
+
* }
|
|
2800
|
+
* });
|
|
2801
|
+
* ```
|
|
2802
|
+
*/
|
|
2803
|
+
export function insertGlobalCss(style: object) {
|
|
2804
|
+
const css = objectToCss(style, "");
|
|
2805
|
+
if (css) {
|
|
2806
|
+
const cnt = cssSnippetCount++;
|
|
2807
|
+
const key = arrayToStr([currentScope.prio, cnt]);
|
|
2808
|
+
cssSnippets[key] = css;
|
|
2809
|
+
clean(() => delete cssSnippets[key]);
|
|
2810
|
+
}
|
|
2811
|
+
}
|
|
2812
|
+
|
|
2813
|
+
let cssSnippets = proxy({} as Record<string, string>);
|
|
2627
2814
|
let cssSnippetCount = 0;
|
|
2628
2815
|
|
|
2629
2816
|
function combinePrefixSelector(prefix: string, key: string): string {
|
|
@@ -2707,73 +2894,6 @@ function styleStringToCss(styleStr: string, selector: string): string {
|
|
|
2707
2894
|
return props ? `${selector}{${props}}\n` : "";
|
|
2708
2895
|
}
|
|
2709
2896
|
|
|
2710
|
-
/**
|
|
2711
|
-
* Inserts CSS rules globally (unscoped).
|
|
2712
|
-
*
|
|
2713
|
-
* Works exactly like {@link insertCss}, but without prefixing selectors with a unique class name.
|
|
2714
|
-
* This is useful for global resets, base styles, or styles that need to apply to the entire document.
|
|
2715
|
-
*
|
|
2716
|
-
* Accepts the same concise style string syntax and CSS shortcuts as {@link insertCss}.
|
|
2717
|
-
* See {@link insertCss} for detailed documentation on syntax and shortcuts.
|
|
2718
|
-
*
|
|
2719
|
-
* @param style - Object with selectors as keys and concise CSS strings as values.
|
|
2720
|
-
*
|
|
2721
|
-
* @example Global Reset and Base Styles
|
|
2722
|
-
* ```typescript
|
|
2723
|
-
* // Set up global styles using CSS shortcuts
|
|
2724
|
-
* A.insertGlobalCss({
|
|
2725
|
-
* "*": "m:0 p:0 box-sizing:border-box",
|
|
2726
|
-
* "body": "font-family: system-ui, sans-serif; m:0 p:$3 bg:#434 fg:#d0dafa",
|
|
2727
|
-
* "a": "text-decoration:none fg:#57f",
|
|
2728
|
-
* "a:hover": "text-decoration:underline",
|
|
2729
|
-
* "code": "font-family:monospace bg:#222 fg:#afc p:4px r:3px"
|
|
2730
|
-
* });
|
|
2731
|
-
*
|
|
2732
|
-
* A('h2#Title without margins');
|
|
2733
|
-
* A('a#This is a link');
|
|
2734
|
-
* A('code#const x = 42;');
|
|
2735
|
-
* ```
|
|
2736
|
-
*
|
|
2737
|
-
* @example Responsive Global Styles
|
|
2738
|
-
* ```typescript
|
|
2739
|
-
* A.insertGlobalCss({
|
|
2740
|
-
* "html": "font-size:16px",
|
|
2741
|
-
* "body": "line-height:1.6",
|
|
2742
|
-
* "h1, h2, h3": "font-weight:600 mt:$4 mb:$2",
|
|
2743
|
-
* "@media (max-width: 768px)": {
|
|
2744
|
-
* "html": "font-size:14px",
|
|
2745
|
-
* "body": "p:$2"
|
|
2746
|
-
* },
|
|
2747
|
-
* "@media (prefers-color-scheme: dark)": {
|
|
2748
|
-
* "body": "bg:#1a1a1a fg:#e5e5e5",
|
|
2749
|
-
* "code": "bg:#2a2a2a"
|
|
2750
|
-
* }
|
|
2751
|
-
* });
|
|
2752
|
-
* ```
|
|
2753
|
-
* At-rules such as `@media` and `@keyframes` should use nested objects. For keyframes,
|
|
2754
|
-
* the step selectors (`0%`, `50%`, `100%`, etc.) become the nested keys and each value
|
|
2755
|
-
* should be a concise CSS declaration string.
|
|
2756
|
-
*
|
|
2757
|
-
* @example Animation Keyframes
|
|
2758
|
-
* ```typescript
|
|
2759
|
-
* A.insertGlobalCss({
|
|
2760
|
-
* "@keyframes connection-pulse": {
|
|
2761
|
-
* "0%": "box-shadow: inset 0 0 0 0 rgba(255, 70, 70, 0.4);",
|
|
2762
|
-
* "50%": "box-shadow: inset 0 0 0 6px rgba(255, 70, 70, 0.08);",
|
|
2763
|
-
* "100%": "box-shadow: inset 0 0 0 0 rgba(255, 70, 70, 0.4);"
|
|
2764
|
-
* }
|
|
2765
|
-
* });
|
|
2766
|
-
* ```
|
|
2767
|
-
*/
|
|
2768
|
-
export function insertGlobalCss(style: object) {
|
|
2769
|
-
const css = objectToCss(style, "");
|
|
2770
|
-
if (css) {
|
|
2771
|
-
let cnt = cssSnippetCount++;
|
|
2772
|
-
cssSnippets[cnt] = css;
|
|
2773
|
-
clean(() => delete cssSnippets[cnt]);
|
|
2774
|
-
}
|
|
2775
|
-
}
|
|
2776
|
-
|
|
2777
2897
|
const CSS_SHORT: Record<string, string | string[]> = {
|
|
2778
2898
|
m: "margin",
|
|
2779
2899
|
mt: "margin-top",
|
|
@@ -3597,6 +3717,7 @@ export default Object.assign(A, {
|
|
|
3597
3717
|
/** {@inheritDoc derive} */ derive,
|
|
3598
3718
|
/** {@inheritDoc disableCreateDestroy} */ disableCreateDestroy,
|
|
3599
3719
|
/** {@inheritDoc dump} */ dump,
|
|
3720
|
+
/** {@inheritDoc freeze} */ freeze,
|
|
3600
3721
|
/** {@inheritDoc insertCss} */ insertCss,
|
|
3601
3722
|
/** {@inheritDoc insertGlobalCss} */ insertGlobalCss,
|
|
3602
3723
|
/** {@inheritDoc invertString} */ invertString,
|