@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 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, at-rule conditions (`@media`, `@supports`),
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.
@@ -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.0.0-alpha.9",
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.0.0-alpha.9"
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 = { readonly className?: string };
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