@yahoo/uds-create-config 4.6.0 → 4.7.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/configs/CanvasConfig.d.ts +900 -80
- package/dist/configs/react-native-system.d.ts +603 -55
- package/dist/configs/system.d.ts +603 -55
- package/dist/configs/system.js +7 -1
- package/dist/entities/system/Component.d.ts +480 -39
- package/dist/entities/system/Component.js +264 -93
- package/dist/entities/system/File.d.ts +1 -1
- package/dist/entities/system/LinkedSystem.d.ts +2 -2
- package/dist/entities/system/Operation.d.ts +1 -1
- package/dist/entities/system/StyleProperty.d.ts +8 -0
- package/dist/entities/system/StyleProperty.js +12 -1
- package/dist/entities/system/Tool.d.ts +1 -1
- package/dist/entities/system/component-module.js +1 -1
- package/dist/entities/system/defineComponent.d.ts +45 -7
- package/dist/entities/system/defineComponent.js +52 -25
- package/dist/framework/Config.d.ts +2 -2
- package/dist/framework/Config.js +59 -7
- package/dist/framework/class-names.d.ts +4 -2
- package/dist/framework/class-names.js +56 -17
- package/dist/framework/defineDerivedEntity.d.ts +1 -1
- package/dist/framework/defineEntity.d.ts +2 -2
- package/dist/framework/defineSubEntity.d.ts +3 -2
- package/dist/framework/preview-store.d.ts +19 -0
- package/dist/framework/preview-store.js +18 -0
- package/dist/framework/projections.d.ts +28 -8
- package/dist/framework/projections.js +50 -16
- package/dist/framework/prop-surface.d.ts +21 -1
- package/dist/framework/prop-surface.js +99 -21
- package/dist/framework/render-spec.d.ts +103 -3
- package/dist/framework/render-spec.js +285 -37
- package/dist/framework/schema-version.d.ts +1 -1
- package/dist/framework/schema-version.js +5 -5
- package/dist/framework/utils/refs.js +10 -1
- package/dist/framework/validate-spec.d.ts +2 -0
- package/dist/framework/validate-spec.js +13 -0
- package/dist/index.d.ts +14 -11
- package/dist/index.js +6 -5
- package/dist/migrations/2.0.0/v1-artifact.d.ts +305 -29
- package/dist/migrations/20260919051027_prop_conditions_and_anatomy_positions_at_rest.d.ts +22 -0
- package/dist/migrations/20260919051027_prop_conditions_and_anatomy_positions_at_rest.js +10 -0
- package/dist/migrations/20260920004019_preview_conditions_keyed.d.ts +27 -0
- package/dist/migrations/20260920004019_preview_conditions_keyed.js +148 -0
- package/dist/migrations/20260920161309_prefixed_token_group.d.ts +12 -0
- package/dist/migrations/20260920161309_prefixed_token_group.js +10 -0
- package/dist/renderer/wrapRegistry.js +8 -2
- package/dist/renderer/wrappers/component-refs.d.ts +14 -0
- package/dist/renderer/wrappers/component-refs.js +79 -0
- package/dist/renderer/wrappers/hex-normalize.d.ts +5 -7
- package/dist/renderer/wrappers/hex-normalize.js +11 -10
- package/dist/spec/cell.d.ts +171 -0
- package/dist/spec/cell.js +361 -0
- package/dist/spec/detach.d.ts +125 -0
- package/dist/spec/detach.js +511 -0
- package/dist/spec/empty-node-slots.d.ts +11 -23
- package/dist/spec/empty-node-slots.js +12 -39
- package/dist/spec/index.d.ts +5 -3
- package/dist/spec/index.js +3 -3
- package/dist/spec/instance.d.ts +77 -0
- package/dist/spec/instance.js +80 -0
- package/dist/spec/preview-content.d.ts +95 -0
- package/dist/spec/preview-content.js +149 -0
- package/dist/spec/preview-elements.d.ts +54 -15
- package/dist/spec/preview-elements.js +109 -29
- package/dist/spec/prop-conditions.d.ts +28 -0
- package/dist/spec/prop-conditions.js +39 -0
- package/dist/spec/slot.d.ts +206 -0
- package/dist/spec/slot.js +542 -0
- package/dist/spec/visibility.d.ts +82 -0
- package/dist/spec/visibility.js +109 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +18 -2
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A component's preview content is one keyed collection, `preview`, shaped like `styles`: the
|
|
3
|
+
* `previewProps` every cell read become the `*` entry (`{ "when": null, "props": { ... } }`), and each
|
|
4
|
+
* `previewWhen` list entry sits under the canonical spelling of its condition (`type=header`,
|
|
5
|
+
* `size=xs&type=default`), the key a style rule is stored under. The collection is a sub-entity from
|
|
6
|
+
* this version, so one entry is addressed by `sub-update` and `sub-delete`, a prop rename moves the
|
|
7
|
+
* names an entry holds, and deleting a prop or a value drops the entries conditioned on it.
|
|
8
|
+
*
|
|
9
|
+
* `up` folds both stored fields; list entries spelling one condition collapse onto the later one,
|
|
10
|
+
* which is the entry the reader answered with before. `upPatch` folds the same fields on a component
|
|
11
|
+
* patch: a whole `previewProps` or `previewWhen` value, and a `previewProps.<prop>` field path.
|
|
12
|
+
* Neither field was ever a sub-collection, so no stored sub-op names them.
|
|
13
|
+
*
|
|
14
|
+
* Deletable once no stored artifact predates it.
|
|
15
|
+
*/
|
|
16
|
+
import type { SchemaMigration } from '../framework/schema-version';
|
|
17
|
+
export declare const PREVIEW_CONDITIONS_KEYED_VERSION = 20260920004019;
|
|
18
|
+
/**
|
|
19
|
+
* The `preview` collection the two old fields fold into, over whatever `preview` already holds: the
|
|
20
|
+
* `*` entry from `previewProps`, one keyed entry per `previewWhen` list item.
|
|
21
|
+
*/
|
|
22
|
+
export declare function foldedPreview({ preview, previewProps, previewWhen, }: {
|
|
23
|
+
preview?: unknown;
|
|
24
|
+
previewProps?: unknown;
|
|
25
|
+
previewWhen?: unknown;
|
|
26
|
+
}): Record<string, unknown>;
|
|
27
|
+
export declare const previewConditionsKeyedMigration: SchemaMigration;
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { isPlainObject } from "../framework/utils/field-path.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
//#region src/migrations/20260920004019_preview_conditions_keyed.ts
|
|
4
|
+
/**
|
|
5
|
+
* A component's preview content is one keyed collection, `preview`, shaped like `styles`: the
|
|
6
|
+
* `previewProps` every cell read become the `*` entry (`{ "when": null, "props": { ... } }`), and each
|
|
7
|
+
* `previewWhen` list entry sits under the canonical spelling of its condition (`type=header`,
|
|
8
|
+
* `size=xs&type=default`), the key a style rule is stored under. The collection is a sub-entity from
|
|
9
|
+
* this version, so one entry is addressed by `sub-update` and `sub-delete`, a prop rename moves the
|
|
10
|
+
* names an entry holds, and deleting a prop or a value drops the entries conditioned on it.
|
|
11
|
+
*
|
|
12
|
+
* `up` folds both stored fields; list entries spelling one condition collapse onto the later one,
|
|
13
|
+
* which is the entry the reader answered with before. `upPatch` folds the same fields on a component
|
|
14
|
+
* patch: a whole `previewProps` or `previewWhen` value, and a `previewProps.<prop>` field path.
|
|
15
|
+
* Neither field was ever a sub-collection, so no stored sub-op names them.
|
|
16
|
+
*
|
|
17
|
+
* Deletable once no stored artifact predates it.
|
|
18
|
+
*/
|
|
19
|
+
const PREVIEW_CONDITIONS_KEYED_VERSION = 20260920004019;
|
|
20
|
+
const UNCONDITIONAL = "*";
|
|
21
|
+
const before = z.looseObject({ items: z.looseObject({ component: z.record(z.string(), z.looseObject({
|
|
22
|
+
previewProps: z.record(z.string(), z.unknown()).optional(),
|
|
23
|
+
previewWhen: z.array(z.unknown()).optional()
|
|
24
|
+
})).optional() }).optional() });
|
|
25
|
+
const after = z.looseObject({ items: z.looseObject({ component: z.record(z.string(), z.looseObject({ preview: z.record(z.string(), z.unknown()).optional() })).optional() }).optional() });
|
|
26
|
+
/** A condition value read to the name it spells: a ref's last path segment, else the value itself. */
|
|
27
|
+
function leafOf(value) {
|
|
28
|
+
if (isPlainObject(value) && typeof value.__ref === "string") {
|
|
29
|
+
const path = /^[^:]*:(.*)$/.exec(value.__ref)?.[1] ?? value.__ref;
|
|
30
|
+
return /([^/]*)$/.exec(path)?.[1] ?? path;
|
|
31
|
+
}
|
|
32
|
+
return String(value);
|
|
33
|
+
}
|
|
34
|
+
/** The key an entry is stored under, frozen here: the condition's entries sorted by prop. */
|
|
35
|
+
function keyOf(when) {
|
|
36
|
+
const entries = Object.entries(isPlainObject(when) ? when : {}).filter(([, value]) => value !== void 0 && value !== null).sort(([a], [b]) => a.localeCompare(b));
|
|
37
|
+
return entries.length === 0 ? UNCONDITIONAL : entries.map(([prop, value]) => `${prop}=${leafOf(value)}`).join("&");
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The `preview` collection the two old fields fold into, over whatever `preview` already holds: the
|
|
41
|
+
* `*` entry from `previewProps`, one keyed entry per `previewWhen` list item.
|
|
42
|
+
*/
|
|
43
|
+
function foldedPreview({ preview, previewProps, previewWhen }) {
|
|
44
|
+
const keyed = isPlainObject(preview) ? { ...preview } : {};
|
|
45
|
+
if (isPlainObject(previewProps)) keyed[UNCONDITIONAL] = {
|
|
46
|
+
when: null,
|
|
47
|
+
props: previewProps
|
|
48
|
+
};
|
|
49
|
+
if (Array.isArray(previewWhen)) for (const entry of previewWhen) {
|
|
50
|
+
if (!isPlainObject(entry)) continue;
|
|
51
|
+
keyed[keyOf(entry.when)] = entry;
|
|
52
|
+
}
|
|
53
|
+
return keyed;
|
|
54
|
+
}
|
|
55
|
+
/** Whether a body still spells preview content the old way. */
|
|
56
|
+
function hasOldPreview(body) {
|
|
57
|
+
return body.previewProps !== void 0 || body.previewWhen !== void 0;
|
|
58
|
+
}
|
|
59
|
+
/** A body with its old preview fields folded into `preview`. */
|
|
60
|
+
function foldedBody(body) {
|
|
61
|
+
const { previewProps, previewWhen, ...rest } = body;
|
|
62
|
+
const preview = foldedPreview({
|
|
63
|
+
preview: rest.preview,
|
|
64
|
+
previewProps,
|
|
65
|
+
previewWhen
|
|
66
|
+
});
|
|
67
|
+
return Object.keys(preview).length > 0 ? {
|
|
68
|
+
...rest,
|
|
69
|
+
preview
|
|
70
|
+
} : rest;
|
|
71
|
+
}
|
|
72
|
+
/** A component body, or a pointer's `extend` delta, with its preview fields folded. */
|
|
73
|
+
function keyedBody(body) {
|
|
74
|
+
if (!isPlainObject(body)) return body;
|
|
75
|
+
let next = body;
|
|
76
|
+
if (hasOldPreview(body)) next = foldedBody(body);
|
|
77
|
+
if (isPlainObject(body.extend) && hasOldPreview(body.extend)) next = {
|
|
78
|
+
...next,
|
|
79
|
+
extend: foldedBody(body.extend)
|
|
80
|
+
};
|
|
81
|
+
return next;
|
|
82
|
+
}
|
|
83
|
+
/** A component patch's `data`, its old preview fields folded: whole values become entries, and a
|
|
84
|
+
* `previewProps.<prop>` field path becomes `preview.*.props.<prop>`. */
|
|
85
|
+
function foldedPatchData(data) {
|
|
86
|
+
const next = {};
|
|
87
|
+
let previewProps;
|
|
88
|
+
let previewWhen;
|
|
89
|
+
for (const [field, value] of Object.entries(data)) {
|
|
90
|
+
if (field === "previewProps") {
|
|
91
|
+
if (isPlainObject(value)) previewProps = value;
|
|
92
|
+
else if (value === null) next["preview.*"] = null;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (field === "previewWhen") {
|
|
96
|
+
previewWhen = value;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (field.startsWith("previewProps.")) {
|
|
100
|
+
next[`preview.*.props.${field.slice(13)}`] = value;
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
next[field] = value;
|
|
104
|
+
}
|
|
105
|
+
if (previewProps !== void 0) next["preview.*"] = {
|
|
106
|
+
when: null,
|
|
107
|
+
props: previewProps
|
|
108
|
+
};
|
|
109
|
+
if (Array.isArray(previewWhen)) for (const [key, entry] of Object.entries(foldedPreview({ previewWhen }))) next[`preview.${key}`] = entry;
|
|
110
|
+
return next;
|
|
111
|
+
}
|
|
112
|
+
const previewConditionsKeyedMigration = {
|
|
113
|
+
version: PREVIEW_CONDITIONS_KEYED_VERSION,
|
|
114
|
+
up(json) {
|
|
115
|
+
const items = json.items;
|
|
116
|
+
if (!isPlainObject(items) || !isPlainObject(items.component)) return json;
|
|
117
|
+
let changed = false;
|
|
118
|
+
const component = {};
|
|
119
|
+
for (const [path, body] of Object.entries(items.component)) {
|
|
120
|
+
const next = keyedBody(body);
|
|
121
|
+
if (next !== body) changed = true;
|
|
122
|
+
component[path] = next;
|
|
123
|
+
}
|
|
124
|
+
return changed ? {
|
|
125
|
+
...json,
|
|
126
|
+
items: {
|
|
127
|
+
...items,
|
|
128
|
+
component
|
|
129
|
+
}
|
|
130
|
+
} : json;
|
|
131
|
+
},
|
|
132
|
+
upPatch(patch) {
|
|
133
|
+
if (patch.kind !== "component") return patch;
|
|
134
|
+
const { operation } = patch;
|
|
135
|
+
if ((operation === "create" || operation === "update" || operation === "replace") && isPlainObject(patch.data)) {
|
|
136
|
+
const data = patch.data;
|
|
137
|
+
return Object.keys(data).some((field) => field === "previewWhen" || field.startsWith("previewProps")) ? {
|
|
138
|
+
...patch,
|
|
139
|
+
data: foldedPatchData(data)
|
|
140
|
+
} : patch;
|
|
141
|
+
}
|
|
142
|
+
return patch;
|
|
143
|
+
},
|
|
144
|
+
fromSchema: before,
|
|
145
|
+
toSchema: after
|
|
146
|
+
};
|
|
147
|
+
//#endregion
|
|
148
|
+
export { PREVIEW_CONDITIONS_KEYED_VERSION, previewConditionsKeyedMigration };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A style property's `values` take one more form: a prefixed token group, `{ value: <group ref>,
|
|
3
|
+
* prefix: "canvas" }`, which offers every token of the group under `<prefix>/<name>` so a group bound
|
|
4
|
+
* beside others reads as its own. Additive — no stored value reshapes, so `up` is the identity — but
|
|
5
|
+
* the `values` member schema is what the wire gate hashes, and a build at the previous version would
|
|
6
|
+
* strip the new form on write.
|
|
7
|
+
*
|
|
8
|
+
* Deletable once no stored artifact predates it.
|
|
9
|
+
*/
|
|
10
|
+
import type { SchemaMigration } from '../framework/schema-version';
|
|
11
|
+
export declare const PREFIXED_TOKEN_GROUP_VERSION = 20260920161309;
|
|
12
|
+
export declare const prefixedTokenGroupMigration: SchemaMigration;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
//#region src/migrations/20260920161309_prefixed_token_group.ts
|
|
2
|
+
const PREFIXED_TOKEN_GROUP_VERSION = 20260920161309;
|
|
3
|
+
const prefixedTokenGroupMigration = {
|
|
4
|
+
version: PREFIXED_TOKEN_GROUP_VERSION,
|
|
5
|
+
up(json) {
|
|
6
|
+
return json;
|
|
7
|
+
}
|
|
8
|
+
};
|
|
9
|
+
//#endregion
|
|
10
|
+
export { PREFIXED_TOKEN_GROUP_VERSION, prefixedTokenGroupMigration };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { FragmentRenderer } from "./primitives/FragmentRenderer.js";
|
|
2
2
|
import { SlotRenderer } from "./primitives/SlotRenderer.js";
|
|
3
|
+
import { wrapComponentRefs } from "./wrappers/component-refs.js";
|
|
3
4
|
import { wrapEventBridge } from "./wrappers/event-bridge.js";
|
|
4
5
|
import { wrapNormalizeHex } from "./wrappers/hex-normalize.js";
|
|
5
6
|
import { aliasHtmlTypes } from "./wrappers/html-aliases.js";
|
|
@@ -18,15 +19,20 @@ import { wrapInlineStyleProps } from "./wrappers/inline-styles.js";
|
|
|
18
19
|
* its outer wrappers haven't yet seen the element.
|
|
19
20
|
*/
|
|
20
21
|
function wrapRegistry(reg, descriptor) {
|
|
21
|
-
|
|
22
|
+
const base = {
|
|
22
23
|
...reg,
|
|
23
24
|
Fragment: reg.Fragment ?? FragmentRenderer,
|
|
24
25
|
Slot: reg.Slot ?? SlotRenderer
|
|
25
|
-
}
|
|
26
|
+
};
|
|
27
|
+
const stylePropNames = descriptor?.stylePropClasses ? new Set([...Object.keys(descriptor.stylePropClasses), ...Object.values(descriptor.cssPropertyToStyleProp ?? {})]) : void 0;
|
|
28
|
+
const holder = {};
|
|
29
|
+
const final = aliasHtmlTypes(wrapInlineStyleProps(wrapNormalizeHex(wrapEventBridge(wrapComponentRefs(base, (type) => holder.registry?.[type])), stylePropNames), {
|
|
26
30
|
cssPropertyToStyleProp: descriptor?.cssPropertyToStyleProp,
|
|
27
31
|
layoutComponents: descriptor?.layoutComponents,
|
|
28
32
|
textComponents: descriptor?.textComponents
|
|
29
33
|
}), descriptor?.stylePropClasses);
|
|
34
|
+
holder.registry = final;
|
|
35
|
+
return final;
|
|
30
36
|
}
|
|
31
37
|
//#endregion
|
|
32
38
|
export { wrapRegistry };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ComponentRegistry, ComponentRenderer } from '@json-render/react';
|
|
2
|
+
/**
|
|
3
|
+
* A component handed as a prop VALUE (`icon={DotsThreeIcon}`) crosses the config as a bare ref,
|
|
4
|
+
* `{ __ref: 'icon:phosphor/DotsThreeIcon' }`, keyed the way the registry keys the component. This
|
|
5
|
+
* wrapper hands the receiving component the component back: one that renders the registry's
|
|
6
|
+
* renderer for that key with whatever props it is called with, so `<Glyph size={12} />` inside
|
|
7
|
+
* `Icon` paints the glyph at 12px. It runs after json-render has resolved the element's props, which
|
|
8
|
+
* is the only point a ref can be told from ordinary data.
|
|
9
|
+
*
|
|
10
|
+
* `resolve` reads the FINAL registry, so the revived component goes through the whole wrapper stack
|
|
11
|
+
* like any spec element. One component per ref, so the receiving component's element type is stable
|
|
12
|
+
* across renders and React keeps the glyph mounted.
|
|
13
|
+
*/
|
|
14
|
+
export declare function wrapComponentRefs(reg: ComponentRegistry, resolve: (type: string) => ComponentRenderer | undefined): ComponentRegistry;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { isComponentRef } from "../../spec/preview-elements.js";
|
|
2
|
+
import React from "react";
|
|
3
|
+
//#region src/renderer/wrappers/component-refs.tsx
|
|
4
|
+
/** A revived ref is called by the component that received it, outside any spec event routing. */
|
|
5
|
+
const NO_EVENT = {
|
|
6
|
+
emit: () => {},
|
|
7
|
+
shouldPreventDefault: false,
|
|
8
|
+
bound: false
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Whether anything under a value is a stored component ref. Stops at a live React element: walking
|
|
12
|
+
* its owner Fiber reaches a circular structure, and an element never holds an un-revived ref.
|
|
13
|
+
*/
|
|
14
|
+
function hasRefDeep(value) {
|
|
15
|
+
if (typeof value !== "object" || value === null) return false;
|
|
16
|
+
if ("$$typeof" in value) return false;
|
|
17
|
+
if (isComponentRef(value)) return true;
|
|
18
|
+
return (Array.isArray(value) ? value : Object.values(value)).some(hasRefDeep);
|
|
19
|
+
}
|
|
20
|
+
function reviveDeep(value, revive) {
|
|
21
|
+
if (typeof value !== "object" || value === null) return value;
|
|
22
|
+
if ("$$typeof" in value) return value;
|
|
23
|
+
if (isComponentRef(value)) return revive(value);
|
|
24
|
+
if (Array.isArray(value)) return value.map((item) => reviveDeep(item, revive));
|
|
25
|
+
const out = {};
|
|
26
|
+
for (const [key, member] of Object.entries(value)) out[key] = reviveDeep(member, revive);
|
|
27
|
+
return out;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* A component handed as a prop VALUE (`icon={DotsThreeIcon}`) crosses the config as a bare ref,
|
|
31
|
+
* `{ __ref: 'icon:phosphor/DotsThreeIcon' }`, keyed the way the registry keys the component. This
|
|
32
|
+
* wrapper hands the receiving component the component back: one that renders the registry's
|
|
33
|
+
* renderer for that key with whatever props it is called with, so `<Glyph size={12} />` inside
|
|
34
|
+
* `Icon` paints the glyph at 12px. It runs after json-render has resolved the element's props, which
|
|
35
|
+
* is the only point a ref can be told from ordinary data.
|
|
36
|
+
*
|
|
37
|
+
* `resolve` reads the FINAL registry, so the revived component goes through the whole wrapper stack
|
|
38
|
+
* like any spec element. One component per ref, so the receiving component's element type is stable
|
|
39
|
+
* across renders and React keeps the glyph mounted.
|
|
40
|
+
*/
|
|
41
|
+
function wrapComponentRefs(reg, resolve) {
|
|
42
|
+
const revived = /* @__PURE__ */ new Map();
|
|
43
|
+
const revive = (ref) => {
|
|
44
|
+
const type = ref.__ref;
|
|
45
|
+
const cached = revived.get(type);
|
|
46
|
+
if (cached) return cached;
|
|
47
|
+
function RevivedRef(props) {
|
|
48
|
+
const Renderer = resolve(type);
|
|
49
|
+
if (!Renderer) return null;
|
|
50
|
+
return React.createElement(Renderer, {
|
|
51
|
+
element: {
|
|
52
|
+
type,
|
|
53
|
+
props
|
|
54
|
+
},
|
|
55
|
+
emit: () => {},
|
|
56
|
+
on: () => NO_EVENT
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
RevivedRef.displayName = `RevivedRef(${type})`;
|
|
60
|
+
revived.set(type, RevivedRef);
|
|
61
|
+
return RevivedRef;
|
|
62
|
+
};
|
|
63
|
+
const wrapped = { ...reg };
|
|
64
|
+
const makeWrapper = (Comp) => function ComponentRefsWrapper(renderProps) {
|
|
65
|
+
const props = renderProps.element.props;
|
|
66
|
+
if (!props || !hasRefDeep(props)) return React.createElement(Comp, renderProps);
|
|
67
|
+
return React.createElement(Comp, {
|
|
68
|
+
...renderProps,
|
|
69
|
+
element: {
|
|
70
|
+
...renderProps.element,
|
|
71
|
+
props: reviveDeep(props, revive)
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
};
|
|
75
|
+
for (const [name, Comp] of Object.entries(reg)) wrapped[name] = makeWrapper(Comp);
|
|
76
|
+
return wrapped;
|
|
77
|
+
}
|
|
78
|
+
//#endregion
|
|
79
|
+
export { wrapComponentRefs };
|
|
@@ -2,13 +2,11 @@ import { ComponentRegistry } from "@json-render/react";
|
|
|
2
2
|
|
|
3
3
|
//#region src/renderer/wrappers/hex-normalize.d.ts
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* the value falls through to the default token-lookup path and the
|
|
10
|
-
* styling silently fails.
|
|
5
|
+
* A raw hex on a style prop (`bg="#1f1f1f"`, or a `$state` resolved to one) is an off-scale value,
|
|
6
|
+
* spelled `[#1f1f1f]` for the class it compiles to; unwrapped it falls through to the token lookup
|
|
7
|
+
* and styles nothing. A value prop (`swatchColor`, `spotColor`) carries the color itself, so with
|
|
8
|
+
* the style prop names known only those are wrapped; without them every hex string is.
|
|
11
9
|
*/
|
|
12
|
-
declare function wrapNormalizeHex(reg: ComponentRegistry): ComponentRegistry;
|
|
10
|
+
declare function wrapNormalizeHex(reg: ComponentRegistry, styleProps?: ReadonlySet<string>): ComponentRegistry;
|
|
13
11
|
//#endregion
|
|
14
12
|
export { wrapNormalizeHex };
|
|
@@ -2,23 +2,24 @@ import React from "react";
|
|
|
2
2
|
//#region src/renderer/wrappers/hex-normalize.tsx
|
|
3
3
|
const RAW_HEX_RE = /^#[0-9a-fA-F]{3,8}$/;
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* the value falls through to the default token-lookup path and the
|
|
10
|
-
* styling silently fails.
|
|
5
|
+
* A raw hex on a style prop (`bg="#1f1f1f"`, or a `$state` resolved to one) is an off-scale value,
|
|
6
|
+
* spelled `[#1f1f1f]` for the class it compiles to; unwrapped it falls through to the token lookup
|
|
7
|
+
* and styles nothing. A value prop (`swatchColor`, `spotColor`) carries the color itself, so with
|
|
8
|
+
* the style prop names known only those are wrapped; without them every hex string is.
|
|
11
9
|
*/
|
|
12
|
-
function wrapNormalizeHex(reg) {
|
|
10
|
+
function wrapNormalizeHex(reg, styleProps) {
|
|
13
11
|
const wrapped = { ...reg };
|
|
14
12
|
const makeHexNormalizeWrapper = (Comp) => function HexNormalizeWrapper(renderProps) {
|
|
15
13
|
const props = renderProps.element.props;
|
|
16
14
|
if (!props) return React.createElement(Comp, renderProps);
|
|
17
15
|
let patched = false;
|
|
18
16
|
const newProps = { ...props };
|
|
19
|
-
for (const [propName, value] of Object.entries(newProps))
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
for (const [propName, value] of Object.entries(newProps)) {
|
|
18
|
+
if (styleProps && !styleProps.has(propName)) continue;
|
|
19
|
+
if (typeof value === "string" && RAW_HEX_RE.test(value)) {
|
|
20
|
+
newProps[propName] = `[${value}]`;
|
|
21
|
+
patched = true;
|
|
22
|
+
}
|
|
22
23
|
}
|
|
23
24
|
if (!patched) return React.createElement(Comp, renderProps);
|
|
24
25
|
return React.createElement(Comp, {
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { ConfigEdit } from "../framework/config-op.js";
|
|
2
|
+
import { Ref } from "../framework/utils/refs.js";
|
|
3
|
+
import { Config } from "../framework/Config.js";
|
|
4
|
+
import { IndexedPreviewCondition } from "../framework/projections.js";
|
|
5
|
+
import { PreviewElement } from "./preview-elements.js";
|
|
6
|
+
import { PreviewContentAddress, PreviewValueSource } from "./preview-content.js";
|
|
7
|
+
import { Slot, SlotCell } from "./slot.js";
|
|
8
|
+
import { SurfacePropCondition } from "../framework/prop-surface.js";
|
|
9
|
+
|
|
10
|
+
//#region src/spec/cell.d.ts
|
|
11
|
+
/**
|
|
12
|
+
* The stored condition in force for a layer at a cell: a style rule or a preview entry, keyed as
|
|
13
|
+
* stored. Styles and content authored at the cell both write under it.
|
|
14
|
+
*/
|
|
15
|
+
interface GoverningCondition {
|
|
16
|
+
readonly source: 'style' | 'preview';
|
|
17
|
+
readonly key: string;
|
|
18
|
+
/** The condition as leaves: a stored ref reads as the value name it points at. */
|
|
19
|
+
readonly when: Record<string, unknown>;
|
|
20
|
+
}
|
|
21
|
+
declare class Cell {
|
|
22
|
+
private readonly config;
|
|
23
|
+
/** The component the cell shows. */
|
|
24
|
+
readonly component: string;
|
|
25
|
+
/** The axis values the cell pins; empty for the component's own row. */
|
|
26
|
+
readonly coordinate: SlotCell;
|
|
27
|
+
private memoDefaults;
|
|
28
|
+
private memoConditional;
|
|
29
|
+
private memoProps;
|
|
30
|
+
private memoEntry;
|
|
31
|
+
private memoHolding;
|
|
32
|
+
private memoOwn;
|
|
33
|
+
private memoOwnConditions;
|
|
34
|
+
private readonly slotViews;
|
|
35
|
+
/** Built by {@link cellAt}; a surface reaches one through `Component.cell`. */
|
|
36
|
+
constructor(config: Config, component: string, coordinate: SlotCell);
|
|
37
|
+
/** What every cell starts from: the declared defaults, `defaultProps`, then the `*` preview entry. */
|
|
38
|
+
get defaults(): Record<string, unknown>;
|
|
39
|
+
/** What the conditional `preview` entries that hold for this cell add, later entries over earlier. */
|
|
40
|
+
get conditional(): Record<string, unknown>;
|
|
41
|
+
/** The values in force: the defaults, what holds for the cell, then the coordinate itself. */
|
|
42
|
+
get props(): Record<string, unknown>;
|
|
43
|
+
/** The `preview` entry whose condition is exactly this coordinate, or `undefined`. */
|
|
44
|
+
get entry(): IndexedPreviewCondition | undefined;
|
|
45
|
+
/** The conditional `preview` entries that hold for this cell, in stored order. */
|
|
46
|
+
get holding(): IndexedPreviewCondition[];
|
|
47
|
+
/**
|
|
48
|
+
* The props this cell shows as its own: the ones a condition reveals under its values, and the
|
|
49
|
+
* ones its holding preview entries set. What every cell shares belongs to the component's own
|
|
50
|
+
* row, so a cell repeats none of it.
|
|
51
|
+
*/
|
|
52
|
+
get own(): ReadonlySet<string>;
|
|
53
|
+
/**
|
|
54
|
+
* The conditions the own props are shown under, each once. A gated prop is shown under its own
|
|
55
|
+
* `when`, which can be wider than the cell (`type = color` on a `color, xs` cell). A prop the
|
|
56
|
+
* cell's preview pins has no `when` of its own, so the coordinate is what shows it.
|
|
57
|
+
*/
|
|
58
|
+
get ownConditions(): readonly SurfacePropCondition[];
|
|
59
|
+
/** Whether the coordinate is the component's defaults, so its preview is every tile's. A forced
|
|
60
|
+
* modifier is a state the defaults never show, so a cell with one is not the defaults. */
|
|
61
|
+
isDefaults(modifiers?: readonly string[]): boolean;
|
|
62
|
+
/** Whether a stored condition holds at this cell, by the same test a style rule is applied with. */
|
|
63
|
+
holds(when: Record<string, unknown> | null | undefined): boolean;
|
|
64
|
+
/**
|
|
65
|
+
* The condition in force for `layer` at this cell, or `null` when only the base holds and an edit
|
|
66
|
+
* is this cell's alone.
|
|
67
|
+
*
|
|
68
|
+
* The candidates are the conditions that hold here and speak only of the axes the coordinate pins,
|
|
69
|
+
* so a reach can be spelled as cells of the grid: the style rules whose bag names the layer, the
|
|
70
|
+
* preview entries that set a prop routed into the layer, and every preview entry holding at the
|
|
71
|
+
* cell, since an entry such as `type = color` is what defines that row. A condition with a list
|
|
72
|
+
* value is not a coordinate and is left out. The most specific wins; at equal specificity a
|
|
73
|
+
* preview entry beats a style rule, since an entry is what defines a row, one that reaches the
|
|
74
|
+
* layer beats one that does not, and a later stored one beats an earlier.
|
|
75
|
+
*
|
|
76
|
+
* A preview content key (`messages__children__2`) names an element a slot's content placed, so the
|
|
77
|
+
* entries filling that slot are what reach it.
|
|
78
|
+
*/
|
|
79
|
+
governing(layer?: string | null): GoverningCondition | null;
|
|
80
|
+
/** The condition an edit to `layer` at this cell writes under: {@link governing}'s, else the
|
|
81
|
+
* coordinate itself. */
|
|
82
|
+
whenFor(layer?: string | null): Record<string, unknown>;
|
|
83
|
+
/** The stored field the value this cell shows for `prop` comes from, or `undefined` when the
|
|
84
|
+
* component stores none. */
|
|
85
|
+
sourceOf(prop: string): PreviewValueSource | undefined;
|
|
86
|
+
/** The component's slots, each read and written at this coordinate. */
|
|
87
|
+
slots(): readonly CellSlot[];
|
|
88
|
+
/** The slot a prop names, read and written at this coordinate; `undefined` when the prop is not one. */
|
|
89
|
+
slot(prop: string): CellSlot | undefined;
|
|
90
|
+
private view;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* A slot as one cell shows it: the route and acceptance of the {@link Slot}, with its content read
|
|
94
|
+
* and written at the cell's coordinate, so a gesture on a cell never hands the coordinate back.
|
|
95
|
+
*/
|
|
96
|
+
declare class CellSlot {
|
|
97
|
+
private readonly slot;
|
|
98
|
+
private readonly coordinate;
|
|
99
|
+
constructor(slot: Slot, coordinate: SlotCell);
|
|
100
|
+
get component(): string;
|
|
101
|
+
/** The prop the content arrives as on the instance. */
|
|
102
|
+
get from(): string;
|
|
103
|
+
/** The layer that holds the content. */
|
|
104
|
+
get layer(): string;
|
|
105
|
+
/** The prop the content lands under on the layer's element. */
|
|
106
|
+
get into(): string;
|
|
107
|
+
/** The refs the slot accepts; empty when unconstrained. */
|
|
108
|
+
get accepts(): readonly string[];
|
|
109
|
+
get isChildren(): boolean;
|
|
110
|
+
/** Whether the slot takes an element — see {@link Slot.takes}. */
|
|
111
|
+
takes(element: string | Ref): boolean;
|
|
112
|
+
/** The elements this cell shows in the slot, in order. */
|
|
113
|
+
get content(): PreviewElement[];
|
|
114
|
+
/** The stored field this cell reads the content from, or `undefined` when nothing is stored. */
|
|
115
|
+
get source(): PreviewValueSource | undefined;
|
|
116
|
+
/** Append to the content under this coordinate — see {@link Slot.append}. */
|
|
117
|
+
append(element: PreviewElement): ConfigEdit[];
|
|
118
|
+
/** The content with one element placed at an address — see {@link Slot.placed}. */
|
|
119
|
+
placed({
|
|
120
|
+
address,
|
|
121
|
+
element
|
|
122
|
+
}: {
|
|
123
|
+
address: PreviewContentAddress;
|
|
124
|
+
element: PreviewElement;
|
|
125
|
+
}): PreviewElement[] | undefined;
|
|
126
|
+
/** The element at an address — see {@link Slot.elementAt}. */
|
|
127
|
+
elementAt(address: PreviewContentAddress): PreviewElement | undefined;
|
|
128
|
+
/** The element at one address moved to another — see {@link Slot.moveAt}. */
|
|
129
|
+
moveAt({
|
|
130
|
+
from,
|
|
131
|
+
to
|
|
132
|
+
}: {
|
|
133
|
+
from: PreviewContentAddress;
|
|
134
|
+
to: PreviewContentAddress;
|
|
135
|
+
}): ConfigEdit[];
|
|
136
|
+
/** A copy of the element at an address, placed after it — see {@link Slot.duplicateAt}. */
|
|
137
|
+
duplicateAt(address: PreviewContentAddress): ConfigEdit[];
|
|
138
|
+
/** Write one element at an address — see {@link Slot.insertAt}. */
|
|
139
|
+
insertAt({
|
|
140
|
+
address,
|
|
141
|
+
element
|
|
142
|
+
}: {
|
|
143
|
+
address: PreviewContentAddress;
|
|
144
|
+
element: PreviewElement;
|
|
145
|
+
}): ConfigEdit[];
|
|
146
|
+
/** Swap one element in place — see {@link Slot.replaceAt}. */
|
|
147
|
+
replaceAt({
|
|
148
|
+
address,
|
|
149
|
+
element
|
|
150
|
+
}: {
|
|
151
|
+
address: PreviewContentAddress;
|
|
152
|
+
element: PreviewElement;
|
|
153
|
+
}): ConfigEdit[];
|
|
154
|
+
/** Take one element out — see {@link Slot.removeAt}. */
|
|
155
|
+
removeAt(address: PreviewContentAddress): ConfigEdit[];
|
|
156
|
+
/** Store the whole content under this coordinate — see {@link Slot.set}. */
|
|
157
|
+
set(elements: readonly PreviewElement[]): ConfigEdit[];
|
|
158
|
+
}
|
|
159
|
+
/** The component under a coordinate. Always answers: a path naming no component reads as a cell
|
|
160
|
+
* that stores nothing. The same config, path and coordinate hand back the same cell. */
|
|
161
|
+
declare function cellAt({
|
|
162
|
+
config,
|
|
163
|
+
path,
|
|
164
|
+
cell
|
|
165
|
+
}: {
|
|
166
|
+
config: Config;
|
|
167
|
+
path: string;
|
|
168
|
+
cell: SlotCell;
|
|
169
|
+
}): Cell;
|
|
170
|
+
//#endregion
|
|
171
|
+
export { Cell, CellSlot, GoverningCondition, cellAt };
|