@uniflowed/stylex 0.0.0-alpha.9 → 0.1.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/index.js +29 -1
- package/native-props.js +40 -0
- package/native.js +40 -0
- package/package.json +14 -4
- package/props.js +8 -1
package/index.js
CHANGED
|
@@ -39,6 +39,32 @@
|
|
|
39
39
|
// configuration flag. The preset is reached by importing it, so a project that
|
|
40
40
|
// does not import it pays nothing, and one that imports half of it keeps half.
|
|
41
41
|
//
|
|
42
|
+
// # The half of a component catalogue that lives here
|
|
43
|
+
//
|
|
44
|
+
// "Not a component library" is a rule about what this package exports, and it
|
|
45
|
+
// was read for a while as a rule about what it is responsible for. It is not.
|
|
46
|
+
// About twenty of the catalogue `@uniflowed/ui` is measured against — Badge,
|
|
47
|
+
// Card, Button, Input, Textarea, Label, Aspect Ratio and the rest — have no
|
|
48
|
+
// behaviour at all: no role, no state machine, no keyboard map. A headless
|
|
49
|
+
// package cannot ship them, because with the styles taken out a `Badge` is a
|
|
50
|
+
// `<span>`. **They are this module's half of the split, and the answer is a
|
|
51
|
+
// style function rather than a component** (ubugeeei-prod/uf#298).
|
|
52
|
+
//
|
|
53
|
+
// `buttonStyles({ tone, size })`, `cardStyles()`, `textStyles({ size, tone })`
|
|
54
|
+
// and `fieldStyles()` are already that answer for four of them, and the shape
|
|
55
|
+
// is the point: a caller spreads `{ className }` onto their own `<button>`, so
|
|
56
|
+
// they keep `type="submit"`, `formAction`, the ref and every attribute a
|
|
57
|
+
// wrapper would have had to remember to forward. Nothing here appears in
|
|
58
|
+
// `@uniflowed/ui`'s types and nothing here renders an element — the rule holds
|
|
59
|
+
// exactly as written; what changed is that the gap is now assigned rather than
|
|
60
|
+
// left between the two packages.
|
|
61
|
+
//
|
|
62
|
+
// `crates/uf_lib/src/ui.rs` records the assignment component by component, and
|
|
63
|
+
// `cargo test -p uf_lib` holds it to this file in both directions: an entry
|
|
64
|
+
// there may not name a style `./preset.js` does not export, and a style
|
|
65
|
+
// exported here must be named by some entry. A `badgeStyles` is the way to
|
|
66
|
+
// close the Badge gap; a `<Badge>` in the other package is not.
|
|
67
|
+
//
|
|
42
68
|
// # Where the token values come from
|
|
43
69
|
//
|
|
44
70
|
// `@uniflowed/brand` owns uf's visual identity — the palette, the type,
|
|
@@ -53,7 +79,9 @@
|
|
|
53
79
|
// # Readiness
|
|
54
80
|
//
|
|
55
81
|
// **Implemented.** `props`, `create`, `defineVars`, `createTheme`, pseudo-class
|
|
56
|
-
// and pseudo-element conditions,
|
|
82
|
+
// and pseudo-element conditions, the state a headless part announces
|
|
83
|
+
// (`:is([aria-selected=true])` — `crates/uf_stylex/src/condition.rs` says why
|
|
84
|
+
// that shape and no wider), at-rule conditions (`@media`, `@supports`),
|
|
57
85
|
// shorthand-versus-longhand ordering, unit inference, the preset tokens, the
|
|
58
86
|
// base layer, and the shipped themes. Every one of them is compiled: the
|
|
59
87
|
// runtime half of this package is `props` and three functions that throw.
|
package/native-props.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
export type NativeStyleProps = { readonly style: { readonly [string]: string | number } };
|
|
4
|
+
|
|
5
|
+
/** Return null for web namespaces; mixing renderer outputs is an error. */
|
|
6
|
+
export function nativeProps(
|
|
7
|
+
styles: $ReadOnlyArray<mixed>,
|
|
8
|
+
nativeOnly: boolean = false,
|
|
9
|
+
): NativeStyleProps | null {
|
|
10
|
+
let native = false;
|
|
11
|
+
let web = false;
|
|
12
|
+
const merged: { [string]: string | number } = {};
|
|
13
|
+
const pending = [...styles];
|
|
14
|
+
for (let index = 0; index < pending.length; index += 1) {
|
|
15
|
+
const value = pending[index];
|
|
16
|
+
if (Array.isArray(value)) {
|
|
17
|
+
pending.splice(index + 1, 0, ...value);
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
if (value == null || typeof value !== "object") continue;
|
|
21
|
+
// Static objects written by the compiler; validate their scalar fields.
|
|
22
|
+
const namespace: { [string]: mixed } = value as $FlowFixMe;
|
|
23
|
+
if (namespace.$$css === true) web = true;
|
|
24
|
+
if (namespace.$$native !== true) {
|
|
25
|
+
if (nativeOnly) throw new Error("StyleX native: expected a compiled native namespace");
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
native = true;
|
|
29
|
+
for (const property of Object.keys(namespace)) {
|
|
30
|
+
if (property === "$$native") continue;
|
|
31
|
+
const entry = namespace[property];
|
|
32
|
+
if (typeof entry !== "string" && typeof entry !== "number") {
|
|
33
|
+
throw new Error(`StyleX native: invalid compiled value for ${property}`);
|
|
34
|
+
}
|
|
35
|
+
merged[property] = entry;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (native && web) throw new Error("StyleX: cannot mix native style objects with web classes");
|
|
39
|
+
return native ? { style: merged } : null;
|
|
40
|
+
}
|
package/native.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import type { ViewStyle, TextStyle, ImageStyle } from "react-native";
|
|
3
|
+
import { nativeRuntimeRequired } from "@uniflowed/core/native";
|
|
4
|
+
import { nativeProps } from "./native-props.js";
|
|
5
|
+
|
|
6
|
+
// Use one property map: a ViewStyle | TextStyle union would accept an invalid
|
|
7
|
+
// fontWeight through its ViewStyle branch, whose optional fields all match.
|
|
8
|
+
export type NativeStyle = $ReadOnly<{
|
|
9
|
+
...$Exact<ViewStyle>,
|
|
10
|
+
...$Exact<TextStyle>,
|
|
11
|
+
...$Exact<ImageStyle>,
|
|
12
|
+
}>;
|
|
13
|
+
export type NativeStyleArgument<Style> =
|
|
14
|
+
| Style
|
|
15
|
+
| false
|
|
16
|
+
| null
|
|
17
|
+
| void
|
|
18
|
+
| $ReadOnlyArray<NativeStyleArgument<Style>>;
|
|
19
|
+
type StyleFields<Value> = Value extends $ReadOnlyArray<infer Item>
|
|
20
|
+
? StyleFields<Item>
|
|
21
|
+
: Value extends (false | null | void)
|
|
22
|
+
? {}
|
|
23
|
+
: Value;
|
|
24
|
+
|
|
25
|
+
/** Compile-time namespaces, checked against the app's React Native styles. */
|
|
26
|
+
export function create<Styles extends { readonly [string]: NativeStyle }>(styles: Styles): Styles {
|
|
27
|
+
return nativeRuntimeRequired("@uniflowed/stylex/native", "stylex.create");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Native-only props retain the authored property types and never add className. */
|
|
31
|
+
export function props<Args extends $ReadOnlyArray<NativeStyleArgument<NativeStyle>>>(
|
|
32
|
+
...styles: Args
|
|
33
|
+
): { readonly style: Partial<StyleFields<Args>> } {
|
|
34
|
+
const result = nativeProps(styles, true);
|
|
35
|
+
// The compiler validates the native subset and preserves every scalar value.
|
|
36
|
+
// The merger copies those fields, but its dynamic keys cannot retain Style.
|
|
37
|
+
return (result ?? { style: {} }) as $FlowFixMe;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const stylex = { create, props };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uniflowed/stylex",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"description": "Flow declarations for @uniflowed/stylex, part of the Unified Toolchain for Flow.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -15,12 +15,22 @@
|
|
|
15
15
|
"./props": "./props.js",
|
|
16
16
|
"./preset": "./preset.js",
|
|
17
17
|
"./theme": "./theme.js",
|
|
18
|
-
"./tokens.stylex.js": "./tokens.stylex.js"
|
|
18
|
+
"./tokens.stylex.js": "./tokens.stylex.js",
|
|
19
|
+
"./native": "./native.js"
|
|
19
20
|
},
|
|
20
21
|
"files": [
|
|
21
|
-
"*.js"
|
|
22
|
+
"*.js",
|
|
23
|
+
"!*.test.js"
|
|
22
24
|
],
|
|
23
25
|
"dependencies": {
|
|
24
|
-
"@uniflowed/core": "0.
|
|
26
|
+
"@uniflowed/core": "0.1.0"
|
|
27
|
+
},
|
|
28
|
+
"peerDependencies": {
|
|
29
|
+
"react-native": ">=0.86.0"
|
|
30
|
+
},
|
|
31
|
+
"peerDependenciesMeta": {
|
|
32
|
+
"react-native": {
|
|
33
|
+
"optional": true
|
|
34
|
+
}
|
|
25
35
|
}
|
|
26
36
|
}
|
package/props.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
// @flow
|
|
2
|
+
|
|
3
|
+
import { nativeProps } from "./native-props.js";
|
|
2
4
|
//
|
|
3
5
|
// `@uniflowed/stylex/props`: the merge, and the only part of StyleX that runs.
|
|
4
6
|
//
|
|
@@ -56,7 +58,10 @@ export type CompiledStyle = {
|
|
|
56
58
|
export type StyleArgument = mixed;
|
|
57
59
|
|
|
58
60
|
/** What `props` hands to an element. */
|
|
59
|
-
export type StyleProps = {
|
|
61
|
+
export type StyleProps = {
|
|
62
|
+
readonly className?: string,
|
|
63
|
+
readonly style?: { readonly [string]: string | number },
|
|
64
|
+
};
|
|
60
65
|
|
|
61
66
|
/** What one property contributed, once the merge has picked a winner. */
|
|
62
67
|
type Winner = string | null | CompiledClasses;
|
|
@@ -73,6 +78,8 @@ type Winner = string | null | CompiledClasses;
|
|
|
73
78
|
* survived.
|
|
74
79
|
*/
|
|
75
80
|
export function props(...styles: $ReadOnlyArray<StyleArgument>): StyleProps {
|
|
81
|
+
const native = nativeProps(styles);
|
|
82
|
+
if (native != null) return native;
|
|
76
83
|
const winners: { [string]: Winner } = {};
|
|
77
84
|
collect(styles, winners);
|
|
78
85
|
|