@fluixi-ui/fx 0.1.0-alpha.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/LICENSE +21 -0
- package/README.md +164 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/lib/color.d.ts +18 -0
- package/dist/lib/color.d.ts.map +1 -0
- package/dist/lib/color.js +53 -0
- package/dist/lib/hash.d.ts +6 -0
- package/dist/lib/hash.d.ts.map +1 -0
- package/dist/lib/hash.js +12 -0
- package/dist/lib/merge.d.ts +8 -0
- package/dist/lib/merge.d.ts.map +1 -0
- package/dist/lib/merge.js +46 -0
- package/dist/lib/props.d.ts +20 -0
- package/dist/lib/props.d.ts.map +1 -0
- package/dist/lib/props.js +50 -0
- package/dist/lib/resolve.d.ts +8 -0
- package/dist/lib/resolve.d.ts.map +1 -0
- package/dist/lib/resolve.js +142 -0
- package/dist/lib/scales.d.ts +59 -0
- package/dist/lib/scales.d.ts.map +1 -0
- package/dist/lib/scales.js +222 -0
- package/dist/lib/selectors.d.ts +128 -0
- package/dist/lib/selectors.d.ts.map +1 -0
- package/dist/lib/selectors.js +82 -0
- package/dist/lib/sheet.d.ts +17 -0
- package/dist/lib/sheet.d.ts.map +1 -0
- package/dist/lib/sheet.js +74 -0
- package/dist/lib/types.d.ts +100 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/types.js +1 -0
- package/dist/tsconfig.lib.tsbuildinfo +1 -0
- package/package.json +48 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { vars } from '@fluixi-ui/tokens/vars';
|
|
2
|
+
/**
|
|
3
|
+
* A token scale. Membership decides two things for every CSS property: which
|
|
4
|
+
* token map a string key is looked up in, and what a bare number means.
|
|
5
|
+
*/
|
|
6
|
+
export type Scale = 'space' | 'size' | 'color' | 'radius' | 'shadow' | 'fontSize' | 'fontWeight' | 'lineHeight' | 'letterSpacing' | 'fontFamily' | 'borderWidth' | 'duration' | 'easing' | 'opacity' | 'zIndex' | 'unitless';
|
|
7
|
+
export type SpaceStep = keyof typeof vars.space;
|
|
8
|
+
export type ColorToken = keyof typeof vars.color;
|
|
9
|
+
export type RadiusToken = keyof typeof vars.radius;
|
|
10
|
+
export type ShadowToken = keyof typeof vars.shadow;
|
|
11
|
+
export type FontSizeToken = keyof typeof vars.fontSize;
|
|
12
|
+
export type FontWeightToken = keyof typeof vars.fontWeight;
|
|
13
|
+
export type LineHeightToken = keyof typeof vars.lineHeight;
|
|
14
|
+
export type LetterSpacingToken = keyof typeof vars.letterSpacing;
|
|
15
|
+
export type FontFamilyToken = keyof typeof vars.fontFamily;
|
|
16
|
+
export type BorderWidthToken = keyof typeof vars.borderWidth;
|
|
17
|
+
export type DurationToken = keyof typeof vars.duration;
|
|
18
|
+
export type EasingToken = keyof typeof vars.easing;
|
|
19
|
+
export type OpacityToken = keyof typeof vars.opacity;
|
|
20
|
+
export type ZIndexToken = keyof typeof vars.zIndex;
|
|
21
|
+
/**
|
|
22
|
+
* Negative space steps, for pulling an element out of its flow (`m: -2`). Written
|
|
23
|
+
* out because TypeScript can't negate a numeric literal union generically.
|
|
24
|
+
*/
|
|
25
|
+
export type NegativeSpaceStep = -1 | -2 | -3 | -4 | -5 | -6 | -8 | -10 | -12 | -16 | -20 | -24;
|
|
26
|
+
declare const SPACE_PROPS: readonly ["padding", "paddingTop", "paddingRight", "paddingBottom", "paddingLeft", "paddingInline", "paddingInlineStart", "paddingInlineEnd", "paddingBlock", "paddingBlockStart", "paddingBlockEnd", "margin", "marginTop", "marginRight", "marginBottom", "marginLeft", "marginInline", "marginInlineStart", "marginInlineEnd", "marginBlock", "marginBlockStart", "marginBlockEnd", "gap", "rowGap", "columnGap", "inset", "insetInline", "insetInlineStart", "insetInlineEnd", "insetBlock", "insetBlockStart", "insetBlockEnd", "top", "right", "bottom", "left"];
|
|
27
|
+
declare const SIZE_PROPS: readonly ["width", "height", "minWidth", "minHeight", "maxWidth", "maxHeight", "flexBasis", "inlineSize", "blockSize", "minInlineSize", "minBlockSize", "maxInlineSize", "maxBlockSize"];
|
|
28
|
+
declare const COLOR_PROPS: readonly ["color", "background", "backgroundColor", "borderColor", "borderTopColor", "borderRightColor", "borderBottomColor", "borderLeftColor", "borderInlineColor", "borderInlineStartColor", "borderInlineEndColor", "borderBlockColor", "borderBlockStartColor", "borderBlockEndColor", "outlineColor", "textDecorationColor", "caretColor", "accentColor", "columnRuleColor", "fill", "stroke"];
|
|
29
|
+
declare const RADIUS_PROPS: readonly ["borderRadius", "borderTopLeftRadius", "borderTopRightRadius", "borderBottomLeftRadius", "borderBottomRightRadius", "borderStartStartRadius", "borderStartEndRadius", "borderEndStartRadius", "borderEndEndRadius"];
|
|
30
|
+
declare const BORDER_WIDTH_PROPS: readonly ["borderWidth", "borderTopWidth", "borderRightWidth", "borderBottomWidth", "borderLeftWidth", "borderInlineWidth", "borderBlockWidth", "outlineWidth"];
|
|
31
|
+
declare const UNITLESS_PROPS: readonly ["flexGrow", "flexShrink", "order", "flex", "zoom"];
|
|
32
|
+
export type SpaceProp = (typeof SPACE_PROPS)[number];
|
|
33
|
+
export type SizeProp = (typeof SIZE_PROPS)[number];
|
|
34
|
+
export type ColorProp = (typeof COLOR_PROPS)[number];
|
|
35
|
+
export type RadiusProp = (typeof RADIUS_PROPS)[number];
|
|
36
|
+
export type BorderWidthProp = (typeof BORDER_WIDTH_PROPS)[number];
|
|
37
|
+
export type UnitlessProp = (typeof UNITLESS_PROPS)[number];
|
|
38
|
+
/** Which scale each CSS property draws from. Properties absent here are untouched. */
|
|
39
|
+
export declare const PROP_SCALE: Record<string, Scale>;
|
|
40
|
+
/**
|
|
41
|
+
* Short names for the properties that get typed most, plus the two multi-side
|
|
42
|
+
* ones CSS has no single longhand for. Everything else is written as the real
|
|
43
|
+
* CSS property, so there's no table to memorise.
|
|
44
|
+
*/
|
|
45
|
+
export declare const ALIASES: Record<string, string>;
|
|
46
|
+
/**
|
|
47
|
+
* Colours resolve semantic name → palette scale ref → raw CSS, so `'primary'`
|
|
48
|
+
* and `'accent-9'` and `'#fff'` are all valid and unambiguous.
|
|
49
|
+
*/
|
|
50
|
+
export declare function resolveColor(value: string): string;
|
|
51
|
+
/**
|
|
52
|
+
* Resolve one declaration value against its property's scale. Unknown keys pass
|
|
53
|
+
* through verbatim — token-first, never token-only.
|
|
54
|
+
*/
|
|
55
|
+
export declare function resolveValue(property: string, value: string | number): string;
|
|
56
|
+
/** `paddingInline` → `padding-inline`; custom properties pass through. */
|
|
57
|
+
export declare function cssProperty(name: string): string;
|
|
58
|
+
export {};
|
|
59
|
+
//# sourceMappingURL=scales.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scales.d.ts","sourceRoot":"","sources":["../../src/lib/scales.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { vars } from '@fluixi-ui/tokens/vars';
|
|
2
|
+
const SPACE_PROPS = [
|
|
3
|
+
'padding',
|
|
4
|
+
'paddingTop',
|
|
5
|
+
'paddingRight',
|
|
6
|
+
'paddingBottom',
|
|
7
|
+
'paddingLeft',
|
|
8
|
+
'paddingInline',
|
|
9
|
+
'paddingInlineStart',
|
|
10
|
+
'paddingInlineEnd',
|
|
11
|
+
'paddingBlock',
|
|
12
|
+
'paddingBlockStart',
|
|
13
|
+
'paddingBlockEnd',
|
|
14
|
+
'margin',
|
|
15
|
+
'marginTop',
|
|
16
|
+
'marginRight',
|
|
17
|
+
'marginBottom',
|
|
18
|
+
'marginLeft',
|
|
19
|
+
'marginInline',
|
|
20
|
+
'marginInlineStart',
|
|
21
|
+
'marginInlineEnd',
|
|
22
|
+
'marginBlock',
|
|
23
|
+
'marginBlockStart',
|
|
24
|
+
'marginBlockEnd',
|
|
25
|
+
'gap',
|
|
26
|
+
'rowGap',
|
|
27
|
+
'columnGap',
|
|
28
|
+
'inset',
|
|
29
|
+
'insetInline',
|
|
30
|
+
'insetInlineStart',
|
|
31
|
+
'insetInlineEnd',
|
|
32
|
+
'insetBlock',
|
|
33
|
+
'insetBlockStart',
|
|
34
|
+
'insetBlockEnd',
|
|
35
|
+
'top',
|
|
36
|
+
'right',
|
|
37
|
+
'bottom',
|
|
38
|
+
'left',
|
|
39
|
+
];
|
|
40
|
+
const SIZE_PROPS = [
|
|
41
|
+
'width',
|
|
42
|
+
'height',
|
|
43
|
+
'minWidth',
|
|
44
|
+
'minHeight',
|
|
45
|
+
'maxWidth',
|
|
46
|
+
'maxHeight',
|
|
47
|
+
'flexBasis',
|
|
48
|
+
'inlineSize',
|
|
49
|
+
'blockSize',
|
|
50
|
+
'minInlineSize',
|
|
51
|
+
'minBlockSize',
|
|
52
|
+
'maxInlineSize',
|
|
53
|
+
'maxBlockSize',
|
|
54
|
+
];
|
|
55
|
+
const COLOR_PROPS = [
|
|
56
|
+
'color',
|
|
57
|
+
'background',
|
|
58
|
+
'backgroundColor',
|
|
59
|
+
'borderColor',
|
|
60
|
+
'borderTopColor',
|
|
61
|
+
'borderRightColor',
|
|
62
|
+
'borderBottomColor',
|
|
63
|
+
'borderLeftColor',
|
|
64
|
+
'borderInlineColor',
|
|
65
|
+
'borderInlineStartColor',
|
|
66
|
+
'borderInlineEndColor',
|
|
67
|
+
'borderBlockColor',
|
|
68
|
+
'borderBlockStartColor',
|
|
69
|
+
'borderBlockEndColor',
|
|
70
|
+
'outlineColor',
|
|
71
|
+
'textDecorationColor',
|
|
72
|
+
'caretColor',
|
|
73
|
+
'accentColor',
|
|
74
|
+
'columnRuleColor',
|
|
75
|
+
'fill',
|
|
76
|
+
'stroke',
|
|
77
|
+
];
|
|
78
|
+
const RADIUS_PROPS = [
|
|
79
|
+
'borderRadius',
|
|
80
|
+
'borderTopLeftRadius',
|
|
81
|
+
'borderTopRightRadius',
|
|
82
|
+
'borderBottomLeftRadius',
|
|
83
|
+
'borderBottomRightRadius',
|
|
84
|
+
'borderStartStartRadius',
|
|
85
|
+
'borderStartEndRadius',
|
|
86
|
+
'borderEndStartRadius',
|
|
87
|
+
'borderEndEndRadius',
|
|
88
|
+
];
|
|
89
|
+
const BORDER_WIDTH_PROPS = [
|
|
90
|
+
'borderWidth',
|
|
91
|
+
'borderTopWidth',
|
|
92
|
+
'borderRightWidth',
|
|
93
|
+
'borderBottomWidth',
|
|
94
|
+
'borderLeftWidth',
|
|
95
|
+
'borderInlineWidth',
|
|
96
|
+
'borderBlockWidth',
|
|
97
|
+
'outlineWidth',
|
|
98
|
+
];
|
|
99
|
+
const UNITLESS_PROPS = ['flexGrow', 'flexShrink', 'order', 'flex', 'zoom'];
|
|
100
|
+
function assign(target, props, scale) {
|
|
101
|
+
for (const prop of props)
|
|
102
|
+
target[prop] = scale;
|
|
103
|
+
}
|
|
104
|
+
/** Which scale each CSS property draws from. Properties absent here are untouched. */
|
|
105
|
+
export const PROP_SCALE = (() => {
|
|
106
|
+
const map = {};
|
|
107
|
+
assign(map, SPACE_PROPS, 'space');
|
|
108
|
+
assign(map, SIZE_PROPS, 'size');
|
|
109
|
+
assign(map, COLOR_PROPS, 'color');
|
|
110
|
+
assign(map, RADIUS_PROPS, 'radius');
|
|
111
|
+
assign(map, BORDER_WIDTH_PROPS, 'borderWidth');
|
|
112
|
+
assign(map, UNITLESS_PROPS, 'unitless');
|
|
113
|
+
map['boxShadow'] = 'shadow';
|
|
114
|
+
map['fontSize'] = 'fontSize';
|
|
115
|
+
map['fontWeight'] = 'fontWeight';
|
|
116
|
+
map['lineHeight'] = 'lineHeight';
|
|
117
|
+
map['letterSpacing'] = 'letterSpacing';
|
|
118
|
+
map['fontFamily'] = 'fontFamily';
|
|
119
|
+
map['transitionDuration'] = 'duration';
|
|
120
|
+
map['animationDuration'] = 'duration';
|
|
121
|
+
map['transitionTimingFunction'] = 'easing';
|
|
122
|
+
map['animationTimingFunction'] = 'easing';
|
|
123
|
+
map['opacity'] = 'opacity';
|
|
124
|
+
map['zIndex'] = 'zIndex';
|
|
125
|
+
return map;
|
|
126
|
+
})();
|
|
127
|
+
/**
|
|
128
|
+
* Short names for the properties that get typed most, plus the two multi-side
|
|
129
|
+
* ones CSS has no single longhand for. Everything else is written as the real
|
|
130
|
+
* CSS property, so there's no table to memorise.
|
|
131
|
+
*/
|
|
132
|
+
export const ALIASES = {
|
|
133
|
+
p: 'padding',
|
|
134
|
+
px: 'paddingInline',
|
|
135
|
+
py: 'paddingBlock',
|
|
136
|
+
m: 'margin',
|
|
137
|
+
mx: 'marginInline',
|
|
138
|
+
my: 'marginBlock',
|
|
139
|
+
bg: 'backgroundColor',
|
|
140
|
+
radius: 'borderRadius',
|
|
141
|
+
shadow: 'boxShadow',
|
|
142
|
+
};
|
|
143
|
+
const TOKEN_MAPS = {
|
|
144
|
+
space: vars.space,
|
|
145
|
+
color: vars.color,
|
|
146
|
+
radius: vars.radius,
|
|
147
|
+
shadow: vars.shadow,
|
|
148
|
+
fontSize: vars.fontSize,
|
|
149
|
+
fontWeight: vars.fontWeight,
|
|
150
|
+
lineHeight: vars.lineHeight,
|
|
151
|
+
letterSpacing: vars.letterSpacing,
|
|
152
|
+
fontFamily: vars.fontFamily,
|
|
153
|
+
borderWidth: vars.borderWidth,
|
|
154
|
+
duration: vars.duration,
|
|
155
|
+
easing: vars.easing,
|
|
156
|
+
opacity: vars.opacity,
|
|
157
|
+
zIndex: vars.zIndex,
|
|
158
|
+
};
|
|
159
|
+
/** Palette scale steps (`accent-9`, `gray-11`) and the status solids/tints. */
|
|
160
|
+
const SCALE_REF = /^(?:accent|gray)-(?:[1-9]|1[0-2])$|^(?:danger|success|warning)-(?:solid|fg|subtle|text)$/;
|
|
161
|
+
function camel(name) {
|
|
162
|
+
return name.replace(/-([a-z])/g, (_, char) => char.toUpperCase());
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Colours resolve semantic name → palette scale ref → raw CSS, so `'primary'`
|
|
166
|
+
* and `'accent-9'` and `'#fff'` are all valid and unambiguous.
|
|
167
|
+
*/
|
|
168
|
+
export function resolveColor(value) {
|
|
169
|
+
const semantic = vars.color[value];
|
|
170
|
+
if (semantic)
|
|
171
|
+
return semantic;
|
|
172
|
+
if (SCALE_REF.test(value))
|
|
173
|
+
return `var(--flx-ui-${value})`;
|
|
174
|
+
// Tolerate the kebab spelling of a semantic name (`bg-subtle`), since that's
|
|
175
|
+
// how the same token reads in CSS.
|
|
176
|
+
const kebab = vars.color[camel(value)];
|
|
177
|
+
return kebab ?? value;
|
|
178
|
+
}
|
|
179
|
+
function spaceStep(n) {
|
|
180
|
+
const step = vars.space[String(Math.abs(n))];
|
|
181
|
+
if (!step)
|
|
182
|
+
return undefined;
|
|
183
|
+
return n < 0 ? `calc(${step} * -1)` : step;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Resolve one declaration value against its property's scale. Unknown keys pass
|
|
187
|
+
* through verbatim — token-first, never token-only.
|
|
188
|
+
*/
|
|
189
|
+
export function resolveValue(property, value) {
|
|
190
|
+
const scale = PROP_SCALE[property];
|
|
191
|
+
if (typeof value === 'number') {
|
|
192
|
+
if (!scale)
|
|
193
|
+
return String(value);
|
|
194
|
+
switch (scale) {
|
|
195
|
+
case 'space': {
|
|
196
|
+
// An off-scale step has no token to reference, so degrade to pixels
|
|
197
|
+
// rather than emitting a `var()` that resolves to nothing.
|
|
198
|
+
return spaceStep(value) ?? `${value}px`;
|
|
199
|
+
}
|
|
200
|
+
case 'size':
|
|
201
|
+
case 'radius':
|
|
202
|
+
case 'borderWidth':
|
|
203
|
+
case 'fontSize':
|
|
204
|
+
case 'letterSpacing':
|
|
205
|
+
return value === 0 ? '0' : `${value}px`;
|
|
206
|
+
case 'duration':
|
|
207
|
+
return `${value}ms`;
|
|
208
|
+
default:
|
|
209
|
+
return String(value);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
if (scale === 'color')
|
|
213
|
+
return resolveColor(value);
|
|
214
|
+
const map = scale ? TOKEN_MAPS[scale] : undefined;
|
|
215
|
+
return map?.[value] ?? value;
|
|
216
|
+
}
|
|
217
|
+
/** `paddingInline` → `padding-inline`; custom properties pass through. */
|
|
218
|
+
export function cssProperty(name) {
|
|
219
|
+
if (name.startsWith('--'))
|
|
220
|
+
return name;
|
|
221
|
+
return name.replace(/[A-Z]/g, (char) => `-${char.toLowerCase()}`);
|
|
222
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A selector key expands to either a selector pattern (where `&` stands for the
|
|
3
|
+
* element) or a whole at-rule prelude that wraps the declarations. The set is
|
|
4
|
+
* closed on purpose: you can't reach an app's own class names from `fx`, so a
|
|
5
|
+
* component can never couple itself to consumer CSS.
|
|
6
|
+
*/
|
|
7
|
+
export type SelectorEntry = {
|
|
8
|
+
selector: string;
|
|
9
|
+
} | {
|
|
10
|
+
atRule: string;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* State keys expand to the library's real conventions, not just the CSS pseudo —
|
|
14
|
+
* headless parts signal state through `data-*`/ARIA, so a `:disabled`-only
|
|
15
|
+
* mapping would miss most of them.
|
|
16
|
+
*
|
|
17
|
+
* The theme keys match the element itself as well as its descendants, so a
|
|
18
|
+
* scoped `data-theme` region works the same as a page-level one.
|
|
19
|
+
*/
|
|
20
|
+
export declare const SELECTORS: {
|
|
21
|
+
_hover: {
|
|
22
|
+
selector: string;
|
|
23
|
+
};
|
|
24
|
+
_active: {
|
|
25
|
+
selector: string;
|
|
26
|
+
};
|
|
27
|
+
_focus: {
|
|
28
|
+
selector: string;
|
|
29
|
+
};
|
|
30
|
+
_focusVisible: {
|
|
31
|
+
selector: string;
|
|
32
|
+
};
|
|
33
|
+
_focusWithin: {
|
|
34
|
+
selector: string;
|
|
35
|
+
};
|
|
36
|
+
_disabled: {
|
|
37
|
+
selector: string;
|
|
38
|
+
};
|
|
39
|
+
_checked: {
|
|
40
|
+
selector: string;
|
|
41
|
+
};
|
|
42
|
+
_selected: {
|
|
43
|
+
selector: string;
|
|
44
|
+
};
|
|
45
|
+
_expanded: {
|
|
46
|
+
selector: string;
|
|
47
|
+
};
|
|
48
|
+
_open: {
|
|
49
|
+
selector: string;
|
|
50
|
+
};
|
|
51
|
+
_invalid: {
|
|
52
|
+
selector: string;
|
|
53
|
+
};
|
|
54
|
+
_before: {
|
|
55
|
+
selector: string;
|
|
56
|
+
};
|
|
57
|
+
_after: {
|
|
58
|
+
selector: string;
|
|
59
|
+
};
|
|
60
|
+
_placeholder: {
|
|
61
|
+
selector: string;
|
|
62
|
+
};
|
|
63
|
+
_selection: {
|
|
64
|
+
selector: string;
|
|
65
|
+
};
|
|
66
|
+
_first: {
|
|
67
|
+
selector: string;
|
|
68
|
+
};
|
|
69
|
+
_last: {
|
|
70
|
+
selector: string;
|
|
71
|
+
};
|
|
72
|
+
_odd: {
|
|
73
|
+
selector: string;
|
|
74
|
+
};
|
|
75
|
+
_even: {
|
|
76
|
+
selector: string;
|
|
77
|
+
};
|
|
78
|
+
_notFirst: {
|
|
79
|
+
selector: string;
|
|
80
|
+
};
|
|
81
|
+
_notLast: {
|
|
82
|
+
selector: string;
|
|
83
|
+
};
|
|
84
|
+
_children: {
|
|
85
|
+
selector: string;
|
|
86
|
+
};
|
|
87
|
+
_dark: {
|
|
88
|
+
selector: string;
|
|
89
|
+
};
|
|
90
|
+
_light: {
|
|
91
|
+
selector: string;
|
|
92
|
+
};
|
|
93
|
+
_hc: {
|
|
94
|
+
selector: string;
|
|
95
|
+
};
|
|
96
|
+
_rtl: {
|
|
97
|
+
selector: string;
|
|
98
|
+
};
|
|
99
|
+
_motionReduce: {
|
|
100
|
+
atRule: string;
|
|
101
|
+
};
|
|
102
|
+
_motionSafe: {
|
|
103
|
+
atRule: string;
|
|
104
|
+
};
|
|
105
|
+
};
|
|
106
|
+
export type SelectorKey = keyof typeof SELECTORS;
|
|
107
|
+
type Assert<T extends true> = T;
|
|
108
|
+
/**
|
|
109
|
+
* Build-time guard, not API. If `SelectorKey` ever widens to `string`, `FxObject`'s
|
|
110
|
+
* selector block collapses into an index signature that swallows every property —
|
|
111
|
+
* and nothing else in the package would fail to compile.
|
|
112
|
+
*/
|
|
113
|
+
export type SelectorKeysAreClosed = Assert<string extends SelectorKey ? false : true>;
|
|
114
|
+
export declare function isSelectorKey(key: string): key is SelectorKey;
|
|
115
|
+
export declare function selectorEntry(key: SelectorKey): SelectorEntry;
|
|
116
|
+
/** Split a selector list on commas that aren't inside brackets or parens. */
|
|
117
|
+
export declare function splitSelectorList(pattern: string): string[];
|
|
118
|
+
/**
|
|
119
|
+
* Compose a nested pattern onto the selectors resolved so far — each `&` in the
|
|
120
|
+
* inner pattern becomes an outer selector, crossing multi-part lists.
|
|
121
|
+
*
|
|
122
|
+
* Nesting order shows up in the emitted text (`&:where(dark):hover` vs
|
|
123
|
+
* `&:hover:where(dark)`). The two match identically, but they hash apart, so the
|
|
124
|
+
* same styling written both ways costs two rules. Not worth canonicalising.
|
|
125
|
+
*/
|
|
126
|
+
export declare function composeSelectors(outer: string[], pattern: string): string[];
|
|
127
|
+
export {};
|
|
128
|
+
//# sourceMappingURL=selectors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selectors.d.ts","sourceRoot":"","sources":["../../src/lib/selectors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtE;;;;;;;GAOG;AACH,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmCmB,CAAC;AAE1C,MAAM,MAAM,WAAW,GAAG,MAAM,OAAO,SAAS,CAAC;AAEjD,KAAK,MAAM,CAAC,CAAC,SAAS,IAAI,IAAI,CAAC,CAAC;AAEhC;;;;GAIG;AACH,MAAM,MAAM,qBAAqB,GAAG,MAAM,CAAC,MAAM,SAAS,WAAW,GAAG,KAAK,GAAG,IAAI,CAAC,CAAC;AAEtF,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,IAAI,WAAW,CAE7D;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,WAAW,GAAG,aAAa,CAE7D;AAED,6EAA6E;AAC7E,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAe3D;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAO3E"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State keys expand to the library's real conventions, not just the CSS pseudo —
|
|
3
|
+
* headless parts signal state through `data-*`/ARIA, so a `:disabled`-only
|
|
4
|
+
* mapping would miss most of them.
|
|
5
|
+
*
|
|
6
|
+
* The theme keys match the element itself as well as its descendants, so a
|
|
7
|
+
* scoped `data-theme` region works the same as a page-level one.
|
|
8
|
+
*/
|
|
9
|
+
export const SELECTORS = {
|
|
10
|
+
_hover: { selector: '&:hover' },
|
|
11
|
+
_active: { selector: '&:active' },
|
|
12
|
+
_focus: { selector: '&:focus' },
|
|
13
|
+
_focusVisible: { selector: '&:focus-visible' },
|
|
14
|
+
_focusWithin: { selector: '&:focus-within' },
|
|
15
|
+
_disabled: { selector: '&:disabled, &[disabled], &[data-disabled]' },
|
|
16
|
+
_checked: { selector: "&:checked, &[data-state='checked'], &[aria-checked='true']" },
|
|
17
|
+
_selected: { selector: "&[data-selected], &[aria-selected='true']" },
|
|
18
|
+
_expanded: { selector: "&[data-expanded], &[aria-expanded='true']" },
|
|
19
|
+
_open: { selector: "&[data-state='open'], &[data-open]" },
|
|
20
|
+
_invalid: { selector: "&:invalid, &[data-invalid], &[aria-invalid='true']" },
|
|
21
|
+
_before: { selector: '&::before' },
|
|
22
|
+
_after: { selector: '&::after' },
|
|
23
|
+
_placeholder: { selector: '&::placeholder' },
|
|
24
|
+
_selection: { selector: '&::selection' },
|
|
25
|
+
_first: { selector: '&:first-child' },
|
|
26
|
+
_last: { selector: '&:last-child' },
|
|
27
|
+
_odd: { selector: '&:nth-child(odd)' },
|
|
28
|
+
_even: { selector: '&:nth-child(even)' },
|
|
29
|
+
_notFirst: { selector: '&:not(:first-child)' },
|
|
30
|
+
_notLast: { selector: '&:not(:last-child)' },
|
|
31
|
+
_children: { selector: '& > *' },
|
|
32
|
+
_dark: { selector: "&:where([data-theme='dark'], [data-theme='dark'] *)" },
|
|
33
|
+
_light: { selector: "&:where([data-theme='light'], [data-theme='light'] *)" },
|
|
34
|
+
_hc: { selector: "&:where([data-theme='high-contrast'], [data-theme='high-contrast'] *)" },
|
|
35
|
+
_rtl: { selector: "&:where([dir='rtl'], [dir='rtl'] *)" },
|
|
36
|
+
_motionReduce: { atRule: '@media (prefers-reduced-motion: reduce)' },
|
|
37
|
+
_motionSafe: { atRule: '@media (prefers-reduced-motion: no-preference)' },
|
|
38
|
+
// `satisfies` rather than an annotation — an annotation would widen the keys to
|
|
39
|
+
// `string`, and `SelectorKey` needs the literals to stay a closed union.
|
|
40
|
+
};
|
|
41
|
+
export function isSelectorKey(key) {
|
|
42
|
+
return key.charCodeAt(0) === 95 /* _ */ && key in SELECTORS;
|
|
43
|
+
}
|
|
44
|
+
export function selectorEntry(key) {
|
|
45
|
+
return SELECTORS[key];
|
|
46
|
+
}
|
|
47
|
+
/** Split a selector list on commas that aren't inside brackets or parens. */
|
|
48
|
+
export function splitSelectorList(pattern) {
|
|
49
|
+
const parts = [];
|
|
50
|
+
let depth = 0;
|
|
51
|
+
let start = 0;
|
|
52
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
53
|
+
const char = pattern[i];
|
|
54
|
+
if (char === '(' || char === '[')
|
|
55
|
+
depth++;
|
|
56
|
+
else if (char === ')' || char === ']')
|
|
57
|
+
depth--;
|
|
58
|
+
else if (char === ',' && depth === 0) {
|
|
59
|
+
parts.push(pattern.slice(start, i).trim());
|
|
60
|
+
start = i + 1;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
parts.push(pattern.slice(start).trim());
|
|
64
|
+
return parts;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Compose a nested pattern onto the selectors resolved so far — each `&` in the
|
|
68
|
+
* inner pattern becomes an outer selector, crossing multi-part lists.
|
|
69
|
+
*
|
|
70
|
+
* Nesting order shows up in the emitted text (`&:where(dark):hover` vs
|
|
71
|
+
* `&:hover:where(dark)`). The two match identically, but they hash apart, so the
|
|
72
|
+
* same styling written both ways costs two rules. Not worth canonicalising.
|
|
73
|
+
*/
|
|
74
|
+
export function composeSelectors(outer, pattern) {
|
|
75
|
+
const inner = splitSelectorList(pattern);
|
|
76
|
+
const composed = [];
|
|
77
|
+
for (const parent of outer) {
|
|
78
|
+
for (const part of inner)
|
|
79
|
+
composed.push(part.split('&').join(parent));
|
|
80
|
+
}
|
|
81
|
+
return composed;
|
|
82
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { FxResult } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Insert a resolved object's rules, once per distinct rule body. Objects that
|
|
4
|
+
* fitted entirely inline never reach the stylesheet at all.
|
|
5
|
+
*/
|
|
6
|
+
export declare function insertFx(result: FxResult): void;
|
|
7
|
+
/**
|
|
8
|
+
* Everything inserted so far, for a server render to emit into `<head>`. Render
|
|
9
|
+
* the result as `<style data-fx-sheet data-fx-tokens="…">`; the client adopts it.
|
|
10
|
+
*/
|
|
11
|
+
export declare function collectFx(): {
|
|
12
|
+
css: string;
|
|
13
|
+
tokens: string;
|
|
14
|
+
};
|
|
15
|
+
/** Drop all inserted rules. For tests and for reusing a server runtime per request. */
|
|
16
|
+
export declare function resetFx(): void;
|
|
17
|
+
//# sourceMappingURL=sheet.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sheet.d.ts","sourceRoot":"","sources":["../../src/lib/sheet.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAmC3C;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,QAAQ,GAAG,IAAI,CAgB/C;AAMD;;;GAGG;AACH,wBAAgB,SAAS,IAAI;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAK3D;AAED,uFAAuF;AACvF,wBAAgB,OAAO,IAAI,IAAI,CAK9B"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { LAYER_ORDER } from '@fluixi-ui/tokens/css';
|
|
2
|
+
const SHEET_ATTRIBUTE = 'data-fx-sheet';
|
|
3
|
+
const TOKENS_ATTRIBUTE = 'data-fx-tokens';
|
|
4
|
+
const inserted = new Set();
|
|
5
|
+
const collected = [];
|
|
6
|
+
let sheet = null;
|
|
7
|
+
/**
|
|
8
|
+
* Find or create the sheet. A server-rendered sheet is adopted rather than
|
|
9
|
+
* duplicated, and the tokens it already carries are seeded so hydration doesn't
|
|
10
|
+
* re-insert rules that are on the page.
|
|
11
|
+
*/
|
|
12
|
+
function styleElement() {
|
|
13
|
+
if (sheet && sheet.isConnected)
|
|
14
|
+
return sheet;
|
|
15
|
+
const existing = document.head.querySelector(`style[${SHEET_ATTRIBUTE}]`);
|
|
16
|
+
if (existing) {
|
|
17
|
+
for (const token of (existing.getAttribute(TOKENS_ATTRIBUTE) ?? '').split(',')) {
|
|
18
|
+
if (token)
|
|
19
|
+
inserted.add(token);
|
|
20
|
+
}
|
|
21
|
+
sheet = existing;
|
|
22
|
+
return sheet;
|
|
23
|
+
}
|
|
24
|
+
sheet = document.createElement('style');
|
|
25
|
+
sheet.setAttribute(SHEET_ATTRIBUTE, '');
|
|
26
|
+
// Redeclaring the order is a no-op, but it makes the layer position of this
|
|
27
|
+
// sheet deterministic no matter which stylesheet the document loaded first.
|
|
28
|
+
sheet.appendChild(document.createTextNode(LAYER_ORDER));
|
|
29
|
+
document.head.appendChild(sheet);
|
|
30
|
+
return sheet;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Insert a resolved object's rules, once per distinct rule body. Objects that
|
|
34
|
+
* fitted entirely inline never reach the stylesheet at all.
|
|
35
|
+
*/
|
|
36
|
+
export function insertFx(result) {
|
|
37
|
+
if (!result.token || !result.rules)
|
|
38
|
+
return;
|
|
39
|
+
if (typeof document === 'undefined') {
|
|
40
|
+
if (inserted.has(result.token))
|
|
41
|
+
return;
|
|
42
|
+
inserted.add(result.token);
|
|
43
|
+
collected.push(wrap(result.rules));
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const element = styleElement();
|
|
47
|
+
if (inserted.has(result.token))
|
|
48
|
+
return;
|
|
49
|
+
inserted.add(result.token);
|
|
50
|
+
collected.push(wrap(result.rules));
|
|
51
|
+
element.appendChild(document.createTextNode(wrap(result.rules)));
|
|
52
|
+
element.setAttribute(TOKENS_ATTRIBUTE, [...inserted].join(','));
|
|
53
|
+
}
|
|
54
|
+
function wrap(rules) {
|
|
55
|
+
return `@layer flx.fx{${rules}}`;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Everything inserted so far, for a server render to emit into `<head>`. Render
|
|
59
|
+
* the result as `<style data-fx-sheet data-fx-tokens="…">`; the client adopts it.
|
|
60
|
+
*/
|
|
61
|
+
export function collectFx() {
|
|
62
|
+
return {
|
|
63
|
+
css: collected.length ? `${LAYER_ORDER}${collected.join('')}` : '',
|
|
64
|
+
tokens: [...inserted].join(','),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/** Drop all inserted rules. For tests and for reusing a server runtime per request. */
|
|
68
|
+
export function resetFx() {
|
|
69
|
+
inserted.clear();
|
|
70
|
+
collected.length = 0;
|
|
71
|
+
if (sheet && sheet.isConnected)
|
|
72
|
+
sheet.remove();
|
|
73
|
+
sheet = null;
|
|
74
|
+
}
|