@ng-native/fabric 0.3.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.
- package/dist/aria-props.d.ts +16 -0
- package/dist/aria-props.js +78 -0
- package/dist/aria-props.js.map +1 -0
- package/dist/css.d.ts +73 -1
- package/dist/css.js +345 -31
- package/dist/css.js.map +1 -1
- package/dist/element-styles.d.ts +15 -0
- package/dist/element-styles.js +103 -0
- package/dist/element-styles.js.map +1 -0
- package/dist/engine.d.ts +147 -6
- package/dist/engine.js +602 -68
- package/dist/engine.js.map +1 -1
- package/dist/font-faces.d.ts +5 -2
- package/dist/font-faces.js +13 -2
- package/dist/font-faces.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/inline-derived-token.js +11 -10
- package/dist/inline-derived-token.js.map +1 -1
- package/dist/inline-token.d.ts +7 -0
- package/dist/inline-token.js +50 -4
- package/dist/inline-token.js.map +1 -1
- package/dist/inline-transform.d.ts +5 -1
- package/dist/inline-transform.js +34 -6
- package/dist/inline-transform.js.map +1 -1
- package/package.json +1 -1
package/dist/css.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* framework-agnostic and knows nothing about Angular.
|
|
7
7
|
*/
|
|
8
8
|
import { mixChannels, mixColours, } from "./color-mix.js";
|
|
9
|
+
import { ELEMENT_STYLES } from "./element-styles.js";
|
|
9
10
|
import { relativeColour } from "./relative-colour.js";
|
|
10
11
|
/** A run of CSS whitespace: spaces, tabs, newlines, carriage returns and form feeds, and no other. */
|
|
11
12
|
export const CSS_SPACE = /[ \t\n\r\f]+/;
|
|
@@ -57,7 +58,15 @@ const INHERITED = new Set([
|
|
|
57
58
|
'textShadowRadius',
|
|
58
59
|
'textShadowColor',
|
|
59
60
|
'selectable',
|
|
61
|
+
// CSS inherits it, and its `none` is the element alone: a descendant's `auto` takes touches
|
|
62
|
+
// again. The engine commits a computed `none` as native's `box-none`, which means that.
|
|
63
|
+
'pointerEvents',
|
|
60
64
|
]);
|
|
65
|
+
/**
|
|
66
|
+
* The `pointer-events` values CSS has. Native's own `box-none` and `box-only` say what their
|
|
67
|
+
* children do already, so neither is handed down.
|
|
68
|
+
*/
|
|
69
|
+
const INHERITED_POINTER_EVENTS = new Set(['none', 'auto']);
|
|
61
70
|
/**
|
|
62
71
|
* `border-style: none`, which the compiler writes as a style native does not have. On the web it
|
|
63
72
|
* makes every border width 0, whichever rule set the width, so `border-hidden border-x` draws no
|
|
@@ -353,6 +362,44 @@ function carryDeferred(deferred, rule) {
|
|
|
353
362
|
(kept ??= []).push(...rule.deferred);
|
|
354
363
|
return kept;
|
|
355
364
|
}
|
|
365
|
+
const NO_DECLARATIONS = Object.freeze({});
|
|
366
|
+
const pick = (from, names) => Object.fromEntries(names.map((name) => [name, from[name]]));
|
|
367
|
+
const isImportant = (rule) => rule.important !== undefined ||
|
|
368
|
+
rule.importantTokens !== undefined ||
|
|
369
|
+
rule.deferred?.some((one) => one.important) === true;
|
|
370
|
+
/**
|
|
371
|
+
* The matched rules in the order their declarations apply, weakest first.
|
|
372
|
+
*
|
|
373
|
+
* That is the order they matched in, unless a layered rule is important. Layers turn round for
|
|
374
|
+
* `!important`: an important declaration in a layer beats one in a later layer, and both beat one
|
|
375
|
+
* in no layer. So each such rule is split, its plain declarations where it matched, and its
|
|
376
|
+
* important ones after every plain one, in the layers' order reversed.
|
|
377
|
+
*/
|
|
378
|
+
function byImportance(matched, places) {
|
|
379
|
+
if (!matched.some((rule) => rule.layer !== undefined && isImportant(rule)))
|
|
380
|
+
return matched;
|
|
381
|
+
const plain = matched.map((rule) => isImportant(rule)
|
|
382
|
+
? {
|
|
383
|
+
...rule,
|
|
384
|
+
important: undefined,
|
|
385
|
+
importantTokens: undefined,
|
|
386
|
+
deferred: rule.deferred?.filter((one) => !one.important),
|
|
387
|
+
}
|
|
388
|
+
: rule);
|
|
389
|
+
const important = matched
|
|
390
|
+
.filter(isImportant)
|
|
391
|
+
// Stable, so rules of one layer keep the order their specificity gave them. Sorted before
|
|
392
|
+
// they are copied, since a rule's place is kept by the rule itself.
|
|
393
|
+
.sort((a, b) => layerOrder(places.get(b), places.get(a)))
|
|
394
|
+
.map((rule) => ({
|
|
395
|
+
...rule,
|
|
396
|
+
declarations: NO_DECLARATIONS,
|
|
397
|
+
// Its important custom properties alone: the plain ones were applied where it matched.
|
|
398
|
+
tokens: rule.importantTokens && pick(rule.tokens, rule.importantTokens),
|
|
399
|
+
deferred: rule.deferred?.filter((one) => one.important),
|
|
400
|
+
}));
|
|
401
|
+
return [...plain, ...important];
|
|
402
|
+
}
|
|
356
403
|
function countPosition(node, siblings, fromEnd) {
|
|
357
404
|
let position = 0;
|
|
358
405
|
let seen = 0;
|
|
@@ -388,6 +435,35 @@ function lastElement(siblings) {
|
|
|
388
435
|
* precedence people already have in their fingers rather than inventing a rule for native.
|
|
389
436
|
*/
|
|
390
437
|
const COMPONENT_SPECIFICITY_BUMP = 1_000;
|
|
438
|
+
/**
|
|
439
|
+
* Two rules' layers, weakest first: a layer before each one named after it, a layer's nested
|
|
440
|
+
* layers before its own rules, and every layered rule before a rule in no layer.
|
|
441
|
+
*/
|
|
442
|
+
function layerOrder(a, b) {
|
|
443
|
+
if (a === b)
|
|
444
|
+
return 0;
|
|
445
|
+
if (a === undefined)
|
|
446
|
+
return 1;
|
|
447
|
+
if (b === undefined)
|
|
448
|
+
return -1;
|
|
449
|
+
const shared = Math.min(a.length, b.length);
|
|
450
|
+
for (let depth = 0; depth < shared; depth++) {
|
|
451
|
+
if (a[depth] !== b[depth])
|
|
452
|
+
return a[depth] - b[depth];
|
|
453
|
+
}
|
|
454
|
+
// One is nested in the other, whose own rules are the stronger.
|
|
455
|
+
return b.length - a.length;
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* The engine's own styles for HTML's text elements, as rules to match before any a stylesheet
|
|
459
|
+
* holds, so every rule an author writes beats them whatever its specificity, as a browser's own
|
|
460
|
+
* sheet is beaten. Offered only to a node marked `styled`, which few are.
|
|
461
|
+
*/
|
|
462
|
+
const ELEMENT_ENTRIES = ELEMENT_STYLES.rules.map((rule) => ({
|
|
463
|
+
rule,
|
|
464
|
+
sheet: ELEMENT_STYLES,
|
|
465
|
+
weight: rule.specificity,
|
|
466
|
+
}));
|
|
391
467
|
/**
|
|
392
468
|
* The bucket a rule is filed under: the most selective simple selector in its rightmost compound,
|
|
393
469
|
* which is what a browser calls the key selector.
|
|
@@ -499,12 +575,82 @@ function inheritFrom(parentInherited, own) {
|
|
|
499
575
|
for (const key of Object.keys(own)) {
|
|
500
576
|
if (!INHERITED.has(key))
|
|
501
577
|
continue;
|
|
578
|
+
// `pointer-events: inherit`, which only an inline style on an element no rule matches still
|
|
579
|
+
// holds here: what the parent hands down stands.
|
|
580
|
+
if (key === 'pointerEvents' && own[key] === 'inherit')
|
|
581
|
+
continue;
|
|
502
582
|
if (inherited === parentInherited)
|
|
503
583
|
inherited = { ...parentInherited };
|
|
504
|
-
|
|
584
|
+
if (key === 'pointerEvents' && !INHERITED_POINTER_EVENTS.has(own[key])) {
|
|
585
|
+
delete inherited[key];
|
|
586
|
+
}
|
|
587
|
+
else {
|
|
588
|
+
inherited[key] = own[key];
|
|
589
|
+
}
|
|
505
590
|
}
|
|
506
591
|
return inherited;
|
|
507
592
|
}
|
|
593
|
+
/**
|
|
594
|
+
* A text decoration's colour, which travels with its line rather than inheriting on its own. A
|
|
595
|
+
* decoration is drawn under the text inside the element that declares it, in that element's
|
|
596
|
+
* decoration colour or its text colour, and a text's own decoration colour only colours a line it
|
|
597
|
+
* declares itself. `style` is the node's, and gets the colour its line is drawn in; what its
|
|
598
|
+
* children inherit is returned.
|
|
599
|
+
*/
|
|
600
|
+
function decorate(style, inherited, own) {
|
|
601
|
+
const declared = own['textDecorationLine'] !== undefined;
|
|
602
|
+
// With no line declared here or above, nothing is drawn and the colour is left as computed.
|
|
603
|
+
if (!declared && !drawsLine(inherited['textDecorationLine']))
|
|
604
|
+
return inherited;
|
|
605
|
+
const colour = declared
|
|
606
|
+
? (own['textDecorationColor'] ?? style['color'])
|
|
607
|
+
: inherited['textDecorationColor'];
|
|
608
|
+
if (colour === undefined)
|
|
609
|
+
delete style['textDecorationColor'];
|
|
610
|
+
else
|
|
611
|
+
style['textDecorationColor'] = colour;
|
|
612
|
+
if (!declared || inherited['textDecorationColor'] === colour)
|
|
613
|
+
return inherited;
|
|
614
|
+
const { textDecorationColor: _parent, ...rest } = inherited;
|
|
615
|
+
return colour === undefined ? rest : { ...rest, textDecorationColor: colour };
|
|
616
|
+
}
|
|
617
|
+
function drawsLine(line) {
|
|
618
|
+
return line !== undefined && line !== 'none';
|
|
619
|
+
}
|
|
620
|
+
/** Whether an inline style sets a property children inherit. Allocates nothing: it runs per set. */
|
|
621
|
+
export function setsInherited(style) {
|
|
622
|
+
if (!style || typeof style !== 'object')
|
|
623
|
+
return false;
|
|
624
|
+
if (Array.isArray(style))
|
|
625
|
+
return style.some(setsInherited);
|
|
626
|
+
for (const key in style) {
|
|
627
|
+
if (INHERITED.has(key) && style[key] != null)
|
|
628
|
+
return true;
|
|
629
|
+
}
|
|
630
|
+
return false;
|
|
631
|
+
}
|
|
632
|
+
/**
|
|
633
|
+
* The inherited properties a node's inline style sets, or null for none. Inline style is the
|
|
634
|
+
* strongest normal declaration on its element, so what it sets is inherited, and read by
|
|
635
|
+
* `color: inherit` and `currentColor`, as a rule's would be.
|
|
636
|
+
*/
|
|
637
|
+
export function inlineInherited(style) {
|
|
638
|
+
let out = null;
|
|
639
|
+
// Flattened first, as native applies it, so a later entry's null clears an earlier value.
|
|
640
|
+
for (const [key, value] of Object.entries(flattenInline(style, {}))) {
|
|
641
|
+
if (INHERITED.has(key) && value !== undefined && value !== null)
|
|
642
|
+
(out ??= {})[key] = value;
|
|
643
|
+
}
|
|
644
|
+
return out;
|
|
645
|
+
}
|
|
646
|
+
function flattenInline(style, into) {
|
|
647
|
+
if (Array.isArray(style))
|
|
648
|
+
for (const entry of style)
|
|
649
|
+
flattenInline(entry, into);
|
|
650
|
+
else if (style && typeof style === 'object')
|
|
651
|
+
Object.assign(into, style);
|
|
652
|
+
return into;
|
|
653
|
+
}
|
|
508
654
|
/**
|
|
509
655
|
* The runtime half of CSS for one engine: the global sheet, the conditions media queries are
|
|
510
656
|
* evaluated against, and the memo that makes resolution cost nodes x rules rather than
|
|
@@ -542,6 +688,21 @@ export class StyleResolver {
|
|
|
542
688
|
* single index.
|
|
543
689
|
*/
|
|
544
690
|
merged = new WeakMap();
|
|
691
|
+
/**
|
|
692
|
+
* Whether any sheet merged so far has a rule in a cascade layer. Until one does, rules are
|
|
693
|
+
* ordered by weight alone and the cascade does not look for layers, which is nearly every app.
|
|
694
|
+
* It stays set once a layered sheet has gone, which costs a look at each cascade and no more.
|
|
695
|
+
*/
|
|
696
|
+
layered = false;
|
|
697
|
+
/**
|
|
698
|
+
* Every layer the sheets seen so far have named, in the order each name was first seen, which
|
|
699
|
+
* is the one order a document has for them. A name keeps its place when its sheet is replaced.
|
|
700
|
+
*/
|
|
701
|
+
layerNames = { place: 0, nested: new Map() };
|
|
702
|
+
/** Each layered rule's place among them, set when its sheet is first merged. */
|
|
703
|
+
layerPlaces = new WeakMap();
|
|
704
|
+
layeredSheets = new WeakSet();
|
|
705
|
+
unnamedLayers = 0;
|
|
545
706
|
/**
|
|
546
707
|
* The application-level sheet, if any: the one set of rules allowed to match a node whatever
|
|
547
708
|
* component created it, which is what resets, utility classes and `:root` tokens all need.
|
|
@@ -641,11 +802,13 @@ export class StyleResolver {
|
|
|
641
802
|
return cached;
|
|
642
803
|
}
|
|
643
804
|
const parentInherited = parent ? parent.inherited : EMPTY;
|
|
644
|
-
|
|
805
|
+
// A box the engine made around loose text is in no template, so no rule matches it.
|
|
806
|
+
if (node.anonymous || this.hasNoRules(node)) {
|
|
645
807
|
return this.unstyled(node, epoch, parent, parentContext, parentInherited);
|
|
646
808
|
}
|
|
647
809
|
styleStats.nodesResolved++;
|
|
648
|
-
const
|
|
810
|
+
const candidates = this.rulesFor(node);
|
|
811
|
+
const matched = this.matched(node, node.styled ? ELEMENT_ENTRIES.concat(candidates) : candidates);
|
|
649
812
|
const parentTokens = parent ? parent.tokens : this.tokensOnRoot;
|
|
650
813
|
const styled = this.styled(node, matched, parentTokens, parentInherited);
|
|
651
814
|
const cache = {
|
|
@@ -685,7 +848,9 @@ export class StyleResolver {
|
|
|
685
848
|
node.hostSheet === null &&
|
|
686
849
|
this.globalSheet === null &&
|
|
687
850
|
this.addedSheets.length === 0 &&
|
|
688
|
-
!node.customProperties
|
|
851
|
+
!node.customProperties &&
|
|
852
|
+
!node.boundStyle &&
|
|
853
|
+
!node.styled);
|
|
689
854
|
}
|
|
690
855
|
/**
|
|
691
856
|
* A node with no rules anywhere: it renders with what it inherits and passes the same map on.
|
|
@@ -698,7 +863,10 @@ export class StyleResolver {
|
|
|
698
863
|
*/
|
|
699
864
|
unstyled(node, epoch, parent, parentContext, parentInherited) {
|
|
700
865
|
const tokens = parent ? parent.tokens : this.tokensOnRoot;
|
|
701
|
-
|
|
866
|
+
// What inline style sets is inherited too, with no rule to read it against.
|
|
867
|
+
const inline = node.inlineInherits ? inlineInherited(node.props['style']) : null;
|
|
868
|
+
if (!inline &&
|
|
869
|
+
parentInherited === EMPTY &&
|
|
702
870
|
parentContext === ROOT_CONTEXT &&
|
|
703
871
|
tokens === NO_TOKENS &&
|
|
704
872
|
!node.styleDirty) {
|
|
@@ -714,7 +882,7 @@ export class StyleResolver {
|
|
|
714
882
|
context: {},
|
|
715
883
|
parentContext,
|
|
716
884
|
style: parentInherited,
|
|
717
|
-
inherited: parentInherited,
|
|
885
|
+
inherited: inline ? inheritFrom(parentInherited, inline) : parentInherited,
|
|
718
886
|
tokens,
|
|
719
887
|
};
|
|
720
888
|
node.styleCache = passthrough;
|
|
@@ -756,6 +924,7 @@ export class StyleResolver {
|
|
|
756
924
|
const add = (from, bump) => {
|
|
757
925
|
if (!from)
|
|
758
926
|
return;
|
|
927
|
+
this.placeLayers(from);
|
|
759
928
|
for (const rule of from.rules) {
|
|
760
929
|
entries.push({ rule, sheet: from, weight: rule.specificity + bump });
|
|
761
930
|
}
|
|
@@ -765,7 +934,41 @@ export class StyleResolver {
|
|
|
765
934
|
add(sheet, COMPONENT_SPECIFICITY_BUMP);
|
|
766
935
|
for (const added of this.addedSheets)
|
|
767
936
|
add(added, 0);
|
|
768
|
-
|
|
937
|
+
if (!entries.some((entry) => entry.rule.layer !== undefined)) {
|
|
938
|
+
return entries.sort((a, b) => a.weight - b.weight);
|
|
939
|
+
}
|
|
940
|
+
this.layered = true;
|
|
941
|
+
const places = this.layerPlaces;
|
|
942
|
+
return entries.sort((a, b) => layerOrder(places.get(a.rule), places.get(b.rule)) || a.weight - b.weight);
|
|
943
|
+
}
|
|
944
|
+
/**
|
|
945
|
+
* Gives each of a sheet's layers its place among every sheet's, the first time the sheet is
|
|
946
|
+
* merged, and each of its layered rules that place. Sheets are merged in the order a document
|
|
947
|
+
* holds them: the app's global sheet, then each component's as it first renders.
|
|
948
|
+
*/
|
|
949
|
+
placeLayers(sheet) {
|
|
950
|
+
if (!sheet.layers || this.layeredSheets.has(sheet))
|
|
951
|
+
return;
|
|
952
|
+
this.layeredSheets.add(sheet);
|
|
953
|
+
// A block with no name is a layer of this sheet's alone.
|
|
954
|
+
const unnamed = `\0${this.unnamedLayers++}`;
|
|
955
|
+
const places = sheet.layers.map((path) => {
|
|
956
|
+
const place = [];
|
|
957
|
+
let names = this.layerNames;
|
|
958
|
+
for (const part of path) {
|
|
959
|
+
const name = part.startsWith('\0') ? unnamed + part : part;
|
|
960
|
+
let nested = names.nested.get(name);
|
|
961
|
+
if (!nested)
|
|
962
|
+
names.nested.set(name, (nested = { place: names.nested.size, nested: new Map() }));
|
|
963
|
+
place.push(nested.place);
|
|
964
|
+
names = nested;
|
|
965
|
+
}
|
|
966
|
+
return place;
|
|
967
|
+
});
|
|
968
|
+
for (const rule of sheet.rules) {
|
|
969
|
+
if (rule.layer !== undefined)
|
|
970
|
+
this.layerPlaces.set(rule, places[rule.layer]);
|
|
971
|
+
}
|
|
769
972
|
}
|
|
770
973
|
/**
|
|
771
974
|
* A node's style from the rules it matched, shared with every other node that matched the same
|
|
@@ -777,8 +980,11 @@ export class StyleResolver {
|
|
|
777
980
|
* written to after, so sharing it is safe; each node still has a cache and a context of its own.
|
|
778
981
|
*/
|
|
779
982
|
styled(node, matched, parentTokens, parentInherited) {
|
|
780
|
-
|
|
781
|
-
|
|
983
|
+
// Shared between nodes that match alike, unless the node sets something of its own.
|
|
984
|
+
const inline = node.inlineInherits ? inlineInherited(node.props['style']) : null;
|
|
985
|
+
if (node.customProperties || inline || node.boundStyle) {
|
|
986
|
+
return this.style(node, matched, parentTokens, parentInherited, inline);
|
|
987
|
+
}
|
|
782
988
|
if (this.sharedGeneration !== this.generation) {
|
|
783
989
|
this.shared.clear();
|
|
784
990
|
this.sharedGeneration = this.generation;
|
|
@@ -808,8 +1014,15 @@ export class StyleResolver {
|
|
|
808
1014
|
return id;
|
|
809
1015
|
}
|
|
810
1016
|
/** A node's style from the rules it matched, worked out. */
|
|
811
|
-
style(node, matched, parentTokens, parentInherited) {
|
|
812
|
-
|
|
1017
|
+
style(node, matched, parentTokens, parentInherited, inline = null) {
|
|
1018
|
+
// Inline style is the last normal declaration, under every important one.
|
|
1019
|
+
const bound = node.boundStyle;
|
|
1020
|
+
const result = this.cascade(inline || bound
|
|
1021
|
+
? [
|
|
1022
|
+
...matched,
|
|
1023
|
+
{ declarations: inline ?? EMPTY, ...(bound && { deferred: bound }) },
|
|
1024
|
+
]
|
|
1025
|
+
: matched);
|
|
813
1026
|
// Tokens are in scope for this node's own declarations as well as its descendants', so they
|
|
814
1027
|
// are merged before any `var()` here is resolved.
|
|
815
1028
|
const tokens = tokensInScope(parentTokens, result.tokens, node.customProperties);
|
|
@@ -819,11 +1032,11 @@ export class StyleResolver {
|
|
|
819
1032
|
}
|
|
820
1033
|
if (own['borderStyle'] === 'none')
|
|
821
1034
|
drawNoBorder(own);
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
};
|
|
1035
|
+
// `pointer-events: inherit` won the cascade: what the parent hands down stands.
|
|
1036
|
+
if (own['pointerEvents'] === 'inherit')
|
|
1037
|
+
delete own['pointerEvents'];
|
|
1038
|
+
const style = { ...parentInherited, ...own };
|
|
1039
|
+
return { style, inherited: decorate(style, inheritFrom(parentInherited, own), own), tokens };
|
|
827
1040
|
}
|
|
828
1041
|
/** The rules that apply to a node, weakest first. */
|
|
829
1042
|
matched(node, entries) {
|
|
@@ -840,8 +1053,9 @@ export class StyleResolver {
|
|
|
840
1053
|
const important = {};
|
|
841
1054
|
let hasImportant = false;
|
|
842
1055
|
let tokens = null;
|
|
1056
|
+
let importantTokens = null;
|
|
843
1057
|
let deferred = null;
|
|
844
|
-
for (const rule of matched) {
|
|
1058
|
+
for (const rule of this.layered ? byImportance(matched, this.layerPlaces) : matched) {
|
|
845
1059
|
Object.assign(normal, rule.declarations);
|
|
846
1060
|
if (rule.important) {
|
|
847
1061
|
Object.assign(important, rule.important);
|
|
@@ -849,8 +1063,14 @@ export class StyleResolver {
|
|
|
849
1063
|
}
|
|
850
1064
|
if (rule.tokens)
|
|
851
1065
|
Object.assign((tokens ??= {}), rule.tokens);
|
|
1066
|
+
// An important custom property beats every plain one, whatever the rules' order.
|
|
1067
|
+
if (rule.importantTokens) {
|
|
1068
|
+
Object.assign((importantTokens ??= {}), pick(rule.tokens, rule.importantTokens));
|
|
1069
|
+
}
|
|
852
1070
|
deferred = carryDeferred(deferred, rule);
|
|
853
1071
|
}
|
|
1072
|
+
if (importantTokens)
|
|
1073
|
+
Object.assign(tokens, importantTokens);
|
|
854
1074
|
return {
|
|
855
1075
|
declarations: hasImportant ? { ...normal, ...important } : normal,
|
|
856
1076
|
important: hasImportant ? important : null,
|
|
@@ -969,6 +1189,14 @@ export class StyleResolver {
|
|
|
969
1189
|
const values = lineValues(line, tokens);
|
|
970
1190
|
if (!values)
|
|
971
1191
|
return undefined;
|
|
1192
|
+
if (line.colour !== undefined) {
|
|
1193
|
+
// A colour that cannot be worked out makes the whole line invalid, as an unset token does.
|
|
1194
|
+
const colour = this.settledWithin(line.colour, declaration, own, parentInherited, tokens);
|
|
1195
|
+
if (colour === undefined)
|
|
1196
|
+
return undefined;
|
|
1197
|
+
for (const prop of line.colors)
|
|
1198
|
+
values[prop] = colour;
|
|
1199
|
+
}
|
|
972
1200
|
// `currentcolor` is the colour in scope, which only the node knows.
|
|
973
1201
|
if (values[line.colors[0]] === 'currentcolor') {
|
|
974
1202
|
const colour = own['color'] ?? parentInherited['color'] ?? 'black';
|
|
@@ -993,7 +1221,7 @@ export class StyleResolver {
|
|
|
993
1221
|
return;
|
|
994
1222
|
if (declaration.alternatives?.some((alternative) => alternative in tokens))
|
|
995
1223
|
return;
|
|
996
|
-
this.onUndefinedToken(name, declaration.props);
|
|
1224
|
+
this.onUndefinedToken(name, declaration.props, declaration.on);
|
|
997
1225
|
}
|
|
998
1226
|
/** A display token set to a value native has no display for. */
|
|
999
1227
|
reportDisplay(declaration, tokens) {
|
|
@@ -1022,13 +1250,15 @@ export class StyleResolver {
|
|
|
1022
1250
|
const fill = (part) => this.filledIn(part, declaration, own, parentInherited, tokens);
|
|
1023
1251
|
if (Array.isArray(value))
|
|
1024
1252
|
return this.filledList(value, fill, tokens);
|
|
1025
|
-
const marked = settledMarker(value, tokens);
|
|
1026
1253
|
// `currentcolor` is the colour in scope, which only the node knows: Tailwind's ring default.
|
|
1027
1254
|
// On `color` itself that is the inherited colour, as CSS reads it.
|
|
1028
|
-
|
|
1255
|
+
const current = () => {
|
|
1029
1256
|
const ownColour = declaration.props.includes('color') ? undefined : own['color'];
|
|
1030
|
-
return ownColour ?? parentInherited['color'] ?? 'black';
|
|
1031
|
-
}
|
|
1257
|
+
return (ownColour ?? parentInherited['color'] ?? 'black');
|
|
1258
|
+
};
|
|
1259
|
+
const marked = settledMarker(value, tokens, current);
|
|
1260
|
+
if (marked === 'currentcolor')
|
|
1261
|
+
return current();
|
|
1032
1262
|
if (marked !== NOT_A_MARKER)
|
|
1033
1263
|
return marked ?? UNSETTLED;
|
|
1034
1264
|
const pending = value.__defer;
|
|
@@ -1531,6 +1761,75 @@ function tokenForm(token, kind) {
|
|
|
1531
1761
|
const value = formOf(token, kind);
|
|
1532
1762
|
return value === undefined && kind === 'color' && isCurrentColour(token) ? CURRENT_COLOUR : value;
|
|
1533
1763
|
}
|
|
1764
|
+
const BOUND_NUMBERS = new Set([
|
|
1765
|
+
'opacity',
|
|
1766
|
+
'flex',
|
|
1767
|
+
'flexGrow',
|
|
1768
|
+
'flexShrink',
|
|
1769
|
+
'zIndex',
|
|
1770
|
+
'aspectRatio',
|
|
1771
|
+
'elevation',
|
|
1772
|
+
]);
|
|
1773
|
+
const BOUND_LENGTH = /^(?:(?:min|max)?(?:width|height)|top|right|bottom|left|start|end|gap|fontSize|letterSpacing)$|(?:margin|padding|inset|Width|Radius|Gap|Basis)/i;
|
|
1774
|
+
/** The properties whose token is read in a form of its own. */
|
|
1775
|
+
const BOUND_KINDS = {
|
|
1776
|
+
display: 'display',
|
|
1777
|
+
transform: 'transform',
|
|
1778
|
+
boxShadow: 'shadow',
|
|
1779
|
+
filter: 'filter',
|
|
1780
|
+
};
|
|
1781
|
+
/** What a token is read as for a bound declaration of `prop`, as the compiler says for a rule. */
|
|
1782
|
+
function boundKind(prop) {
|
|
1783
|
+
// `fill` and `stroke` are a shape's paint, which is a colour.
|
|
1784
|
+
if (/color$/i.test(prop) || prop === 'fill' || prop === 'stroke')
|
|
1785
|
+
return 'color';
|
|
1786
|
+
if (prop === 'fontWeight')
|
|
1787
|
+
return 'weight';
|
|
1788
|
+
if (prop === 'fontFamily')
|
|
1789
|
+
return 'family';
|
|
1790
|
+
if (prop === 'lineHeight')
|
|
1791
|
+
return 'lineHeight';
|
|
1792
|
+
if (prop in BOUND_KINDS)
|
|
1793
|
+
return BOUND_KINDS[prop];
|
|
1794
|
+
if (BOUND_NUMBERS.has(prop))
|
|
1795
|
+
return 'number';
|
|
1796
|
+
return BOUND_LENGTH.test(prop) ? 'length' : 'keyword';
|
|
1797
|
+
}
|
|
1798
|
+
/**
|
|
1799
|
+
* A bound declaration whose value is one `var()`, `var(--brand, red)`, as the deferred declaration
|
|
1800
|
+
* the compiler writes for the same value in a rule. `token` is the value as a token, which is an
|
|
1801
|
+
* alias when it is a `var()`; null for any other.
|
|
1802
|
+
*
|
|
1803
|
+
* ponytail: the whole value has to be the `var()`. One inside a longer value, `calc()` included,
|
|
1804
|
+
* is what the compiler parses at build time and nothing here does; write the sum in a custom
|
|
1805
|
+
* property, `[style.--gap]`, and read that in the stylesheet.
|
|
1806
|
+
*/
|
|
1807
|
+
export function boundDeclaration(prop, token, on) {
|
|
1808
|
+
if (!token?.alias)
|
|
1809
|
+
return null;
|
|
1810
|
+
const kind = boundKind(prop);
|
|
1811
|
+
const [reference, ...alternatives] = aliasesOf(token);
|
|
1812
|
+
for (; token?.alias; token = token.fallback)
|
|
1813
|
+
;
|
|
1814
|
+
// `tokenForm`, so a fallback of `currentColor` is the colour of the element, as in a rule.
|
|
1815
|
+
const fallback = token && !isDerived(token) ? tokenForm(token, kind) : undefined;
|
|
1816
|
+
return {
|
|
1817
|
+
props: [prop],
|
|
1818
|
+
...(on === undefined ? {} : { on }),
|
|
1819
|
+
kind,
|
|
1820
|
+
reference: reference,
|
|
1821
|
+
...(alternatives.length ? { alternatives } : {}),
|
|
1822
|
+
...(fallback !== undefined ? { fallback } : {}),
|
|
1823
|
+
...(token && isDerived(token) ? { fallbackToken: token } : {}),
|
|
1824
|
+
};
|
|
1825
|
+
}
|
|
1826
|
+
/** The names a `var()` tries in turn: its own, then each `var()` nested as its fallback. */
|
|
1827
|
+
function aliasesOf(token) {
|
|
1828
|
+
const names = [];
|
|
1829
|
+
for (; token?.alias; token = token.fallback)
|
|
1830
|
+
names.push(token.alias);
|
|
1831
|
+
return names;
|
|
1832
|
+
}
|
|
1534
1833
|
/** The fallback written, or one made of other tokens, worked out from the tokens where it is used. */
|
|
1535
1834
|
function fallbackOf(declaration, tokens) {
|
|
1536
1835
|
const token = declaration.fallbackToken;
|
|
@@ -1744,8 +2043,11 @@ function placed(position, tokens) {
|
|
|
1744
2043
|
/**
|
|
1745
2044
|
* A colour expression with its tokens looked up and any mix in it worked out. Undefined when a
|
|
1746
2045
|
* token it needs is not defined anywhere and has nothing to fall back to.
|
|
2046
|
+
*
|
|
2047
|
+
* `current` is the node's colour, for a `currentColor` inside a mix or a relative colour. One that
|
|
2048
|
+
* is the whole expression is answered as `currentcolor`, for the caller to fill in.
|
|
1747
2049
|
*/
|
|
1748
|
-
function resolveColour(expression, tokens) {
|
|
2050
|
+
function resolveColour(expression, tokens, current) {
|
|
1749
2051
|
if ('color' in expression)
|
|
1750
2052
|
return expression.color;
|
|
1751
2053
|
if ('channels' in expression)
|
|
@@ -1756,15 +2058,27 @@ function resolveColour(expression, tokens) {
|
|
|
1756
2058
|
return tokenColour(expression, tokens);
|
|
1757
2059
|
if ('relative' in expression) {
|
|
1758
2060
|
const { space, from, channels, alpha } = expression.relative;
|
|
1759
|
-
const origin =
|
|
2061
|
+
const origin = innerColour(from, tokens, current);
|
|
1760
2062
|
return origin === undefined ? undefined : relativeColour(space, origin, channels, alpha);
|
|
1761
2063
|
}
|
|
1762
|
-
const sides = mixSides(expression.mix, tokens);
|
|
2064
|
+
const sides = mixSides(expression.mix, tokens, current);
|
|
1763
2065
|
return sides && mixColours(expression.mix.space, ...sides, expression.mix.hue);
|
|
1764
2066
|
}
|
|
2067
|
+
/** A colour inside another: `currentColor` there, written or in a token, is the node's colour. */
|
|
2068
|
+
function innerColour(expression, tokens, current) {
|
|
2069
|
+
if (!current)
|
|
2070
|
+
return resolveColour(expression, tokens);
|
|
2071
|
+
if ('reference' in expression) {
|
|
2072
|
+
const names = [expression.reference, ...(expression.alternatives ?? [])];
|
|
2073
|
+
if (isCurrentColour(firstSet(names, tokens)))
|
|
2074
|
+
return current();
|
|
2075
|
+
}
|
|
2076
|
+
const colour = resolveColour(expression, tokens, current);
|
|
2077
|
+
return colour?.toLowerCase() === 'currentcolor' ? current() : colour;
|
|
2078
|
+
}
|
|
1765
2079
|
/** A mix's two sides, a mix inside it unrounded, as CSS rounds only the colour at the end. */
|
|
1766
|
-
function mixSides(mix, tokens) {
|
|
1767
|
-
const colour = (side) => 'mix' in side ? unroundedMix(side.mix, tokens) :
|
|
2080
|
+
function mixSides(mix, tokens, current) {
|
|
2081
|
+
const colour = (side) => 'mix' in side ? unroundedMix(side.mix, tokens, current) : innerColour(side, tokens, current);
|
|
1768
2082
|
const first = colour(mix.a);
|
|
1769
2083
|
const second = colour(mix.b);
|
|
1770
2084
|
if (first === undefined || second === undefined)
|
|
@@ -1774,8 +2088,8 @@ function mixSides(mix, tokens) {
|
|
|
1774
2088
|
{ colour: second, percentage: mix.bPercentage },
|
|
1775
2089
|
];
|
|
1776
2090
|
}
|
|
1777
|
-
function unroundedMix(mix, tokens) {
|
|
1778
|
-
const sides = mixSides(mix, tokens);
|
|
2091
|
+
function unroundedMix(mix, tokens, current) {
|
|
2092
|
+
const sides = mixSides(mix, tokens, current);
|
|
1779
2093
|
return sides && mixChannels(mix.space, ...sides, mix.hue);
|
|
1780
2094
|
}
|
|
1781
2095
|
/** A calc marker worked out, as its slot takes it; undefined when a token in it is not set. */
|
|
@@ -1951,10 +2265,10 @@ const NEEDS_FILLING = new WeakMap();
|
|
|
1951
2265
|
/** What `settledMarker` says of a part that is not a token's marker at all. */
|
|
1952
2266
|
const NOT_A_MARKER = Symbol('not a marker');
|
|
1953
2267
|
/** A colour, length or calc marker settled from the tokens, or undefined when one cannot be. */
|
|
1954
|
-
function settledMarker(value, tokens) {
|
|
2268
|
+
function settledMarker(value, tokens, current) {
|
|
1955
2269
|
const colour = colourMarker(value);
|
|
1956
2270
|
if (colour)
|
|
1957
|
-
return resolveColour(colour, tokens);
|
|
2271
|
+
return resolveColour(colour, tokens, current);
|
|
1958
2272
|
// Whether a shadow is inset, from a token that is the word or nothing: `ring-inset`.
|
|
1959
2273
|
const inset = value.__inset;
|
|
1960
2274
|
if (inset)
|