@aglyn/aglyn 1.0.0-beta.172 → 1.0.0-beta.173
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/package.json +10 -10
- package/src/lib/app-utils/compose-layout-props.d.ts +6 -2
- package/src/lib/app-utils/compose-layout-props.js +8 -3
- package/src/lib/app-utils/compose-layout-props.js.map +1 -1
- package/src/lib/app-utils/compose-reusable-components.d.ts +85 -1
- package/src/lib/app-utils/compose-reusable-components.js +137 -11
- package/src/lib/app-utils/compose-reusable-components.js.map +1 -1
- package/src/lib/app-utils/docs-index.generated.js +9 -2
- package/src/lib/app-utils/docs-index.generated.js.map +1 -1
- package/src/lib/app-utils/element-color-scheme.d.ts +43 -0
- package/src/lib/app-utils/element-color-scheme.js +41 -0
- package/src/lib/app-utils/element-color-scheme.js.map +1 -0
- package/src/lib/app-utils/layout-style-overrides.d.ts +106 -0
- package/src/lib/app-utils/layout-style-overrides.js +158 -0
- package/src/lib/app-utils/layout-style-overrides.js.map +1 -0
- package/src/lib/app-utils/server.d.ts +2 -0
- package/src/lib/app-utils/server.js +4 -0
- package/src/lib/app-utils/server.js.map +1 -1
- package/src/lib/canvas-manager/canvas-manager.d.ts +5 -0
- package/src/lib/canvas-manager/canvas-manager.js +7 -0
- package/src/lib/canvas-manager/canvas-manager.js.map +1 -1
- package/src/lib/foundation/definitions/platform.types.d.ts +9 -0
- package/src/lib/foundation/definitions/platform.types.js.map +1 -1
- package/src/lib/types/nodes.d.ts +12 -0
- package/src/lib/types/nodes.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aglyn/aglyn",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.173",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"homepage": "https://aglyn.com",
|
|
6
6
|
"repository": {
|
|
@@ -33,15 +33,15 @@
|
|
|
33
33
|
"./package.json": "./package.json"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@aglyn/shared-data-enums": "1.0.0-beta.
|
|
37
|
-
"@aglyn/shared-data-mdi": "1.0.0-beta.
|
|
38
|
-
"@aglyn/shared-data-types": "1.0.0-beta.
|
|
39
|
-
"@aglyn/shared-util-email": "1.0.0-beta.
|
|
40
|
-
"@aglyn/shared-util-http": "1.0.0-beta.
|
|
41
|
-
"@aglyn/shared-util-logger": "1.0.0-beta.
|
|
42
|
-
"@aglyn/shared-util-timestamp": "1.0.0-beta.
|
|
43
|
-
"@aglyn/shared-util-tools": "1.0.0-beta.
|
|
44
|
-
"@aglyn/shared-util-vendor": "1.0.0-beta.
|
|
36
|
+
"@aglyn/shared-data-enums": "1.0.0-beta.173",
|
|
37
|
+
"@aglyn/shared-data-mdi": "1.0.0-beta.173",
|
|
38
|
+
"@aglyn/shared-data-types": "1.0.0-beta.173",
|
|
39
|
+
"@aglyn/shared-util-email": "1.0.0-beta.173",
|
|
40
|
+
"@aglyn/shared-util-http": "1.0.0-beta.173",
|
|
41
|
+
"@aglyn/shared-util-logger": "1.0.0-beta.173",
|
|
42
|
+
"@aglyn/shared-util-timestamp": "1.0.0-beta.173",
|
|
43
|
+
"@aglyn/shared-util-tools": "1.0.0-beta.173",
|
|
44
|
+
"@aglyn/shared-util-vendor": "1.0.0-beta.173",
|
|
45
45
|
"@data-driven-forms/react-form-renderer": "^4.2.0",
|
|
46
46
|
"@msgpack/msgpack": "^3.1.3",
|
|
47
47
|
"@types/unist": "^3.0.3",
|
|
@@ -61,11 +61,15 @@ export declare function applyLayoutProps<N extends AglynNodeSchema = AglynNodeSc
|
|
|
61
61
|
/**
|
|
62
62
|
* A screen grafted through its whole layout chain, innermost first, with each
|
|
63
63
|
* layout's properties applied from the screen's values for that layout
|
|
64
|
-
* (`layoutPropValues`, keyed by layout id)
|
|
64
|
+
* (`layoutPropValues`, keyed by layout id), and then the screen's per-page
|
|
65
|
+
* restyling of that layout's elements (`layoutStyleOverrides`, AGL-3286) —
|
|
66
|
+
* after the properties, so a page's style wins over whatever a property
|
|
67
|
+
* bound, and before the graft, so an element the layout hides for this page
|
|
68
|
+
* is simply not there to be styled.
|
|
65
69
|
*
|
|
66
70
|
* The one composition the published page, the besigner's Preview and its
|
|
67
71
|
* layout chrome all run, so none of them can disagree about which value a
|
|
68
72
|
* layout renders with.
|
|
69
73
|
*/
|
|
70
|
-
export declare function composeLayoutChainWithProps<N extends AglynNodeSchema = AglynNodeSchema>(chain: ReadonlyArray<LayoutChainEntry<N> | null | undefined>, screenNodes: NormalizedNodes<N>, layoutPropValues?: unknown): NormalizedNodes<N>;
|
|
74
|
+
export declare function composeLayoutChainWithProps<N extends AglynNodeSchema = AglynNodeSchema>(chain: ReadonlyArray<LayoutChainEntry<N> | null | undefined>, screenNodes: NormalizedNodes<N>, layoutPropValues?: unknown, layoutStyleOverrides?: unknown): NormalizedNodes<N>;
|
|
71
75
|
export default composeLayoutChainWithProps;
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
*/ import { NODE_ROOT_ID } from "../canvas-manager/canvas-manager.js";
|
|
17
17
|
import { composeLayoutChainAndScreenNodes } from "./compose-layout-nodes.js";
|
|
18
18
|
import { applyDeclaredProps } from "./compose-reusable-components.js";
|
|
19
|
+
import { applyLayoutStyleOverrides, layoutStyleOverridesFor } from "./layout-style-overrides.js";
|
|
19
20
|
/**
|
|
20
21
|
* Screen-version key holding the screen's values for the layouts it renders
|
|
21
22
|
* inside, beside the `layoutId` binding. Persisted in screen version documents
|
|
@@ -48,13 +49,17 @@ import { applyDeclaredProps } from "./compose-reusable-components.js";
|
|
|
48
49
|
/**
|
|
49
50
|
* A screen grafted through its whole layout chain, innermost first, with each
|
|
50
51
|
* layout's properties applied from the screen's values for that layout
|
|
51
|
-
* (`layoutPropValues`, keyed by layout id)
|
|
52
|
+
* (`layoutPropValues`, keyed by layout id), and then the screen's per-page
|
|
53
|
+
* restyling of that layout's elements (`layoutStyleOverrides`, AGL-3286) —
|
|
54
|
+
* after the properties, so a page's style wins over whatever a property
|
|
55
|
+
* bound, and before the graft, so an element the layout hides for this page
|
|
56
|
+
* is simply not there to be styled.
|
|
52
57
|
*
|
|
53
58
|
* The one composition the published page, the besigner's Preview and its
|
|
54
59
|
* layout chrome all run, so none of them can disagree about which value a
|
|
55
60
|
* layout renders with.
|
|
56
|
-
*/ export function composeLayoutChainWithProps(chain, screenNodes, layoutPropValues) {
|
|
57
|
-
return composeLayoutChainAndScreenNodes(chain.map((entry)=>applyLayoutProps(entry == null ? void 0 : entry.nodes, entry == null ? void 0 : entry.props, layoutPropValuesFor(layoutPropValues, entry == null ? void 0 : entry.layoutId))), screenNodes);
|
|
61
|
+
*/ export function composeLayoutChainWithProps(chain, screenNodes, layoutPropValues, layoutStyleOverrides) {
|
|
62
|
+
return composeLayoutChainAndScreenNodes(chain.map((entry)=>applyLayoutStyleOverrides(applyLayoutProps(entry == null ? void 0 : entry.nodes, entry == null ? void 0 : entry.props, layoutPropValuesFor(layoutPropValues, entry == null ? void 0 : entry.layoutId)), layoutStyleOverridesFor(layoutStyleOverrides, entry == null ? void 0 : entry.layoutId))), screenNodes);
|
|
58
63
|
}
|
|
59
64
|
export default composeLayoutChainWithProps;
|
|
60
65
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/compose-layout-props.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type {\n AglynNodeSchema,\n NodeId,\n ReusableComponentProp,\n} from '../foundation'\nimport { NODE_ROOT_ID } from '../canvas-manager/canvas-manager'\nimport { composeLayoutChainAndScreenNodes } from './compose-layout-nodes'\nimport { applyDeclaredProps } from './compose-reusable-components'\n\n/**\n * Layout properties (AGL-2893): a shared layout declares properties exactly as\n * a reusable component does, binds its own nodes to them with `{{prop.*}}`,\n * and each screen rendering inside it sets the values.\n *\n * The values are applied to each layout's OWN nodes before the screen is\n * grafted into its slot, so a `{{prop.*}}` the screen's own copy happens to\n * contain is never touched, and a component the layout places receives the\n * values it is handed before it is grafted.\n */\n\ntype NormalizedNodes<N extends AglynNodeSchema> = Record<NodeId, N>\n\n/**\n * Screen-version key holding the screen's values for the layouts it renders\n * inside, beside the `layoutId` binding. Persisted in screen version documents\n * — never rename.\n */\nexport const SCREEN_LAYOUT_PROP_VALUES_KEY = 'layoutPropValues'\n\n/** One layout of a screen's chain, as composition reads it. */\nexport interface LayoutChainEntry<N extends AglynNodeSchema = AglynNodeSchema> {\n /** The layout document's id, which a screen's values are keyed by. */\n layoutId?: string | null\n /** The published version's nodes. */\n nodes?: NormalizedNodes<N> | null\n /** The published version's declared properties. */\n props?: ReusableComponentProp[] | null\n}\n\n/**\n * A screen's values for one layout of its chain, or `undefined` where it set\n * none. Anything that is not a map of values is read as none.\n */\nexport function layoutPropValuesFor(\n layoutPropValues: unknown,\n layoutId: string | null | undefined,\n): Record<string, unknown> | undefined {\n if (!layoutId || !layoutPropValues || typeof layoutPropValues !== 'object') {\n return undefined\n }\n const values = (layoutPropValues as Record<string, unknown>)[layoutId]\n return values && typeof values === 'object' && !Array.isArray(values)\n ? (values as Record<string, unknown>)\n : undefined\n}\n\n/**\n * A layout's nodes with its declared properties applied for one screen: each\n * `{{prop.*}}` takes the screen's value or the property's default, a field\n * bound to one property receives the value its kind holds, and the parts the\n * screen hides (`hideIf` / `hideUnless`) are pruned — the sequence a reusable\n * component's graft runs, over the layout's root.\n *\n * A layout with no root is returned as it is, for the reason\n * `composeLayoutAndScreenNodes` gives: a broken layout degrades to the bare\n * screen rather than taking a page down.\n */\nexport function applyLayoutProps<N extends AglynNodeSchema = AglynNodeSchema>(\n nodes: NormalizedNodes<N> | null | undefined,\n props: ReusableComponentProp[] | null | undefined,\n values: Readonly<Record<string, unknown>> | null | undefined,\n): NormalizedNodes<N> | undefined {\n if (!nodes || !nodes[NODE_ROOT_ID]) return nodes ?? undefined\n return applyDeclaredProps(nodes, props, values, NODE_ROOT_ID)\n}\n\n/**\n * A screen grafted through its whole layout chain, innermost first, with each\n * layout's properties applied from the screen's values for that layout\n * (`layoutPropValues`, keyed by layout id).\n *\n * The one composition the published page, the besigner's Preview and its\n * layout chrome all run, so none of them can disagree about which value a\n * layout renders with.\n */\nexport function composeLayoutChainWithProps<\n N extends AglynNodeSchema = AglynNodeSchema,\n>(\n chain: ReadonlyArray<LayoutChainEntry<N> | null | undefined>,\n screenNodes: NormalizedNodes<N>,\n layoutPropValues?: unknown,\n): NormalizedNodes<N> {\n return composeLayoutChainAndScreenNodes(\n chain.map((entry) =>\n applyLayoutProps(\n
|
|
1
|
+
{"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/compose-layout-props.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type {\n AglynNodeSchema,\n NodeId,\n ReusableComponentProp,\n} from '../foundation'\nimport { NODE_ROOT_ID } from '../canvas-manager/canvas-manager'\nimport { composeLayoutChainAndScreenNodes } from './compose-layout-nodes'\nimport { applyDeclaredProps } from './compose-reusable-components'\nimport {\n applyLayoutStyleOverrides,\n layoutStyleOverridesFor,\n} from './layout-style-overrides'\n\n/**\n * Layout properties (AGL-2893): a shared layout declares properties exactly as\n * a reusable component does, binds its own nodes to them with `{{prop.*}}`,\n * and each screen rendering inside it sets the values.\n *\n * The values are applied to each layout's OWN nodes before the screen is\n * grafted into its slot, so a `{{prop.*}}` the screen's own copy happens to\n * contain is never touched, and a component the layout places receives the\n * values it is handed before it is grafted.\n */\n\ntype NormalizedNodes<N extends AglynNodeSchema> = Record<NodeId, N>\n\n/**\n * Screen-version key holding the screen's values for the layouts it renders\n * inside, beside the `layoutId` binding. Persisted in screen version documents\n * — never rename.\n */\nexport const SCREEN_LAYOUT_PROP_VALUES_KEY = 'layoutPropValues'\n\n/** One layout of a screen's chain, as composition reads it. */\nexport interface LayoutChainEntry<N extends AglynNodeSchema = AglynNodeSchema> {\n /** The layout document's id, which a screen's values are keyed by. */\n layoutId?: string | null\n /** The published version's nodes. */\n nodes?: NormalizedNodes<N> | null\n /** The published version's declared properties. */\n props?: ReusableComponentProp[] | null\n}\n\n/**\n * A screen's values for one layout of its chain, or `undefined` where it set\n * none. Anything that is not a map of values is read as none.\n */\nexport function layoutPropValuesFor(\n layoutPropValues: unknown,\n layoutId: string | null | undefined,\n): Record<string, unknown> | undefined {\n if (!layoutId || !layoutPropValues || typeof layoutPropValues !== 'object') {\n return undefined\n }\n const values = (layoutPropValues as Record<string, unknown>)[layoutId]\n return values && typeof values === 'object' && !Array.isArray(values)\n ? (values as Record<string, unknown>)\n : undefined\n}\n\n/**\n * A layout's nodes with its declared properties applied for one screen: each\n * `{{prop.*}}` takes the screen's value or the property's default, a field\n * bound to one property receives the value its kind holds, and the parts the\n * screen hides (`hideIf` / `hideUnless`) are pruned — the sequence a reusable\n * component's graft runs, over the layout's root.\n *\n * A layout with no root is returned as it is, for the reason\n * `composeLayoutAndScreenNodes` gives: a broken layout degrades to the bare\n * screen rather than taking a page down.\n */\nexport function applyLayoutProps<N extends AglynNodeSchema = AglynNodeSchema>(\n nodes: NormalizedNodes<N> | null | undefined,\n props: ReusableComponentProp[] | null | undefined,\n values: Readonly<Record<string, unknown>> | null | undefined,\n): NormalizedNodes<N> | undefined {\n if (!nodes || !nodes[NODE_ROOT_ID]) return nodes ?? undefined\n return applyDeclaredProps(nodes, props, values, NODE_ROOT_ID)\n}\n\n/**\n * A screen grafted through its whole layout chain, innermost first, with each\n * layout's properties applied from the screen's values for that layout\n * (`layoutPropValues`, keyed by layout id), and then the screen's per-page\n * restyling of that layout's elements (`layoutStyleOverrides`, AGL-3286) —\n * after the properties, so a page's style wins over whatever a property\n * bound, and before the graft, so an element the layout hides for this page\n * is simply not there to be styled.\n *\n * The one composition the published page, the besigner's Preview and its\n * layout chrome all run, so none of them can disagree about which value a\n * layout renders with.\n */\nexport function composeLayoutChainWithProps<\n N extends AglynNodeSchema = AglynNodeSchema,\n>(\n chain: ReadonlyArray<LayoutChainEntry<N> | null | undefined>,\n screenNodes: NormalizedNodes<N>,\n layoutPropValues?: unknown,\n layoutStyleOverrides?: unknown,\n): NormalizedNodes<N> {\n return composeLayoutChainAndScreenNodes(\n chain.map((entry) =>\n applyLayoutStyleOverrides(\n applyLayoutProps(\n entry?.nodes,\n entry?.props,\n layoutPropValuesFor(layoutPropValues, entry?.layoutId),\n ),\n layoutStyleOverridesFor(layoutStyleOverrides, entry?.layoutId),\n ),\n ),\n screenNodes,\n )\n}\n\nexport default composeLayoutChainWithProps\n"],"names":["NODE_ROOT_ID","composeLayoutChainAndScreenNodes","applyDeclaredProps","applyLayoutStyleOverrides","layoutStyleOverridesFor","SCREEN_LAYOUT_PROP_VALUES_KEY","layoutPropValuesFor","layoutPropValues","layoutId","undefined","values","Array","isArray","applyLayoutProps","nodes","props","composeLayoutChainWithProps","chain","screenNodes","layoutStyleOverrides","map","entry"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAOD,SAASA,YAAY,QAAQ,sCAAkC;AAC/D,SAASC,gCAAgC,QAAQ,4BAAwB;AACzE,SAASC,kBAAkB,QAAQ,mCAA+B;AAClE,SACEC,yBAAyB,EACzBC,uBAAuB,QAClB,8BAA0B;AAejC;;;;CAIC,GACD,OAAO,MAAMC,gCAAgC,mBAAkB;AAY/D;;;CAGC,GACD,OAAO,SAASC,oBACdC,gBAAyB,EACzBC,QAAmC;IAEnC,IAAI,CAACA,YAAY,CAACD,oBAAoB,OAAOA,qBAAqB,UAAU;QAC1E,OAAOE;IACT;IACA,MAAMC,SAAS,AAACH,gBAA4C,CAACC,SAAS;IACtE,OAAOE,UAAU,OAAOA,WAAW,YAAY,CAACC,MAAMC,OAAO,CAACF,UACzDA,SACDD;AACN;AAEA;;;;;;;;;;CAUC,GACD,OAAO,SAASI,iBACdC,KAA4C,EAC5CC,KAAiD,EACjDL,MAA4D;IAE5D,IAAI,CAACI,SAAS,CAACA,KAAK,CAACd,aAAa,EAAE,OAAOc,gBAAAA,QAASL;IACpD,OAAOP,mBAAmBY,OAAOC,OAAOL,QAAQV;AAClD;AAEA;;;;;;;;;;;;CAYC,GACD,OAAO,SAASgB,4BAGdC,KAA4D,EAC5DC,WAA+B,EAC/BX,gBAA0B,EAC1BY,oBAA8B;IAE9B,OAAOlB,iCACLgB,MAAMG,GAAG,CAAC,CAACC,QACTlB,0BACEU,iBACEQ,yBAAAA,MAAOP,KAAK,EACZO,yBAAAA,MAAON,KAAK,EACZT,oBAAoBC,kBAAkBc,yBAAAA,MAAOb,QAAQ,IAEvDJ,wBAAwBe,sBAAsBE,yBAAAA,MAAOb,QAAQ,KAGjEU;AAEJ;AAEA,eAAeF,4BAA2B"}
|
|
@@ -17,11 +17,53 @@
|
|
|
17
17
|
import type { AglynNodeSchema, NodeId, ReusableComponentIcon, ReusableComponentProp } from '../foundation';
|
|
18
18
|
export * from './reusable-component-keys';
|
|
19
19
|
export * from './reusable-prop-values';
|
|
20
|
+
/**
|
|
21
|
+
* Whether a node is a placed form that names a form entity — the second kind
|
|
22
|
+
* of node that stands for a tree stored on another document (see
|
|
23
|
+
* {@link PlacementKind}). A `form` node with no `formId` is an ordinary form
|
|
24
|
+
* whose fields are its own children, and has no override layer.
|
|
25
|
+
*
|
|
26
|
+
* The ref test matches the graft's own resolution rule (a non-empty string),
|
|
27
|
+
* so the editor and the renderer agree about which forms are placements.
|
|
28
|
+
*/
|
|
29
|
+
export declare function isPlacedFormNode(node: {
|
|
30
|
+
componentId?: string;
|
|
31
|
+
props?: unknown;
|
|
32
|
+
} | undefined | null): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Whether a node is a placement that GRAFTS a definition — a reusable
|
|
35
|
+
* component instance or a placed form — and therefore carries the two
|
|
36
|
+
* per-placement override layers (`styleOverrides`, `attrOverrides`, AGL-3285).
|
|
37
|
+
*
|
|
38
|
+
* One predicate for both kinds on purpose: the graft already expands them in
|
|
39
|
+
* one pass, and an override layer that one kind honoured and the other
|
|
40
|
+
* silently ignored would be the canvas/Preview/tenant disagreement this file
|
|
41
|
+
* exists to prevent.
|
|
42
|
+
*/
|
|
43
|
+
export declare function isDefinitionPlacement(node: {
|
|
44
|
+
componentId?: string;
|
|
45
|
+
props?: unknown;
|
|
46
|
+
} | undefined | null): boolean;
|
|
47
|
+
/**
|
|
48
|
+
* The tree a placement grafts, from whichever map its kind reads — the
|
|
49
|
+
* component definitions for an instance, the published form designs for a
|
|
50
|
+
* placed form — or `undefined` when it is not a placement or its ref resolves
|
|
51
|
+
* to nothing (AGL-3285). The editor panels use it to offer the same targets
|
|
52
|
+
* the graft will consult.
|
|
53
|
+
*/
|
|
54
|
+
export declare function placementDefinitionFor<N extends AglynNodeSchema = AglynNodeSchema>(node: {
|
|
55
|
+
componentId?: string;
|
|
56
|
+
props?: unknown;
|
|
57
|
+
} | undefined | null, sources: {
|
|
58
|
+
definitions?: Record<string, ReusableComponentTree<N> | undefined>;
|
|
59
|
+
formDesigns?: Record<string, Pick<ReusableComponentTree<N>, 'rootId' | 'nodes'> | undefined>;
|
|
60
|
+
}): Pick<ReusableComponentTree<N>, 'rootId' | 'nodes'> | undefined;
|
|
20
61
|
/**
|
|
21
62
|
* Every per-instance style override an instance node carries, keyed the way
|
|
22
63
|
* it is stored (AGL-1332): {@link STYLE_OVERRIDES_ROOT_KEY} for the
|
|
23
64
|
* component root, and a DEFINITION-internal node id for each overridden
|
|
24
|
-
* leaf. Empty for a node that is not
|
|
65
|
+
* leaf. Empty for a node that is not a placement ({@link
|
|
66
|
+
* isDefinitionPlacement}: an instance or a placed form) or carries none.
|
|
25
67
|
*
|
|
26
68
|
* Keyed on the definition's own id, never the grafted `cmp__…` id: the
|
|
27
69
|
* graft id is DERIVED from the instance id, so keying on it would break the
|
|
@@ -39,6 +81,7 @@ export * from './reusable-prop-values';
|
|
|
39
81
|
*/
|
|
40
82
|
export declare function getInstanceStyleOverrides(node: {
|
|
41
83
|
componentId?: string;
|
|
84
|
+
props?: unknown;
|
|
42
85
|
styleOverrides?: unknown;
|
|
43
86
|
} | undefined | null): Record<string, Record<string, unknown>>;
|
|
44
87
|
/**
|
|
@@ -48,8 +91,48 @@ export declare function getInstanceStyleOverrides(node: {
|
|
|
48
91
|
*/
|
|
49
92
|
export declare function getInstanceRootStyleOverride(node: {
|
|
50
93
|
componentId?: string;
|
|
94
|
+
props?: unknown;
|
|
51
95
|
styleOverrides?: unknown;
|
|
52
96
|
} | undefined | null): Record<string, unknown> | undefined;
|
|
97
|
+
/**
|
|
98
|
+
* Props a placed form's attribute overrides must never address, on any node
|
|
99
|
+
* of its design (AGL-3285).
|
|
100
|
+
*
|
|
101
|
+
* A form is edited once and every page placing it follows — that is the
|
|
102
|
+
* whole point of the form entity. Its submission list, its dataset columns,
|
|
103
|
+
* its consent tick and its routing are all keyed to what the ENTITY declares,
|
|
104
|
+
* so a page that renamed a field, retyped it, made it optional, rewrote its
|
|
105
|
+
* choices or re-pointed where the form sends would file answers the form's
|
|
106
|
+
* own schema does not describe, from one page only, with nothing on the
|
|
107
|
+
* Forms page saying so. Per page, a form may LOOK different; it may not
|
|
108
|
+
* collect something different.
|
|
109
|
+
*
|
|
110
|
+
* - The form node's identity and submission config: `formId`, `formName`,
|
|
111
|
+
* `datasetId`, `datasetName`, and the after-submit outcome
|
|
112
|
+
* (`afterSubmit`, `redirectScreenId`, `redirectUrl`, `revealNodeId`).
|
|
113
|
+
* - Each field's contract: `fieldName` (the submission key),
|
|
114
|
+
* `datasetFieldId`, `fieldType`, `options` (a ticked option's TEXT is the
|
|
115
|
+
* posted value, and the consent check reads it) and `required`.
|
|
116
|
+
* - Element attributes a browser submission reads directly: `name`, `type`
|
|
117
|
+
* (a submit button made a plain button no longer submits), `value`,
|
|
118
|
+
* `action`, `method`, and `disabled` (a disabled input is not posted).
|
|
119
|
+
* - The hide directives: a hidden field is pruned from the page, so its
|
|
120
|
+
* value is never sent.
|
|
121
|
+
*
|
|
122
|
+
* Label, placeholder, help text, the submit label and success message, and
|
|
123
|
+
* everything about styling stay overridable.
|
|
124
|
+
*/
|
|
125
|
+
export declare const PLACED_FORM_REFUSED_ATTR_PROPS: ReadonlySet<string>;
|
|
126
|
+
/**
|
|
127
|
+
* Whether `prop` may never be an attribute override on this placement — `sx`
|
|
128
|
+
* on every placement, plus {@link PLACED_FORM_REFUSED_ATTR_PROPS} on a placed
|
|
129
|
+
* form. The render layer and the editor's writer both ask this, so a refused
|
|
130
|
+
* prop can neither be offered nor take effect.
|
|
131
|
+
*/
|
|
132
|
+
export declare function isAttrOverrideRefused(placement: {
|
|
133
|
+
componentId?: string;
|
|
134
|
+
props?: unknown;
|
|
135
|
+
} | undefined | null, prop: string): boolean;
|
|
53
136
|
/**
|
|
54
137
|
* Every per-instance ATTRIBUTE override an instance node carries, keyed the
|
|
55
138
|
* way {@link getInstanceStyleOverrides} keys styles — `root` for the
|
|
@@ -62,6 +145,7 @@ export declare function getInstanceRootStyleOverride(node: {
|
|
|
62
145
|
*/
|
|
63
146
|
export declare function getInstanceAttrOverrides(node: {
|
|
64
147
|
componentId?: string;
|
|
148
|
+
props?: unknown;
|
|
65
149
|
attrOverrides?: unknown;
|
|
66
150
|
} | undefined | null): Record<string, Record<string, unknown>>;
|
|
67
151
|
/** One styleable target inside an instance — see {@link listInstanceStyleTargets}. */
|
|
@@ -20,6 +20,7 @@ import { iconPathPropName } from "@aglyn/shared-data-mdi/utils/icon-path-prop-na
|
|
|
20
20
|
import { reusablePropValueClass } from "../foundation/definitions/property-kinds.js";
|
|
21
21
|
import { COMPONENT_NODE_ID_PREFIX, COMPONENT_PROP_TOKEN_PREFIX, INSTANCE_CARRIED_NODE_FIELDS, INSTANCE_CARRIED_PROPS, NODE_HIDE_IF_PROP, NODE_HIDE_UNLESS_PROP, REUSABLE_INSTANCE_COMPONENT_ID, REUSABLE_INSTANCE_PROP_VALUES_KEY, STYLE_OVERRIDES_ROOT_KEY } from "./reusable-component-keys.js";
|
|
22
22
|
import { REPEAT_DIRECTIVE_PROPS, REPEAT_SELF_PROP, repeatKey, withoutRepeatDirective } from "./expand-repeatables.js";
|
|
23
|
+
import { FORM_COMPONENT_ID, FORM_ID_PROP } from "./forms.js";
|
|
23
24
|
import { mergeNodeSx } from "./merge-node-sx.js";
|
|
24
25
|
import { hasUrlScheme, isSafeNodeUrl, NODE_URL_PROPS } from "./node-url-policy.js";
|
|
25
26
|
import { readInstanceIconValue, readYesNoValue, resolveReusablePropValues } from "./reusable-prop-values.js";
|
|
@@ -36,11 +37,60 @@ const isStyleRecord = (value)=>typeof value === 'object' && value !== null && !A
|
|
|
36
37
|
...new Set(names)
|
|
37
38
|
].join(' ') : undefined;
|
|
38
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Whether a node is a placed form that names a form entity — the second kind
|
|
42
|
+
* of node that stands for a tree stored on another document (see
|
|
43
|
+
* {@link PlacementKind}). A `form` node with no `formId` is an ordinary form
|
|
44
|
+
* whose fields are its own children, and has no override layer.
|
|
45
|
+
*
|
|
46
|
+
* The ref test matches the graft's own resolution rule (a non-empty string),
|
|
47
|
+
* so the editor and the renderer agree about which forms are placements.
|
|
48
|
+
*/ export function isPlacedFormNode(node) {
|
|
49
|
+
var _node_props;
|
|
50
|
+
if ((node == null ? void 0 : node.componentId) !== FORM_COMPONENT_ID) return false;
|
|
51
|
+
const formId = (_node_props = node.props) == null ? void 0 : _node_props[FORM_ID_PROP];
|
|
52
|
+
return typeof formId === 'string' && formId !== '';
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Whether a node is a placement that GRAFTS a definition — a reusable
|
|
56
|
+
* component instance or a placed form — and therefore carries the two
|
|
57
|
+
* per-placement override layers (`styleOverrides`, `attrOverrides`, AGL-3285).
|
|
58
|
+
*
|
|
59
|
+
* One predicate for both kinds on purpose: the graft already expands them in
|
|
60
|
+
* one pass, and an override layer that one kind honoured and the other
|
|
61
|
+
* silently ignored would be the canvas/Preview/tenant disagreement this file
|
|
62
|
+
* exists to prevent.
|
|
63
|
+
*/ export function isDefinitionPlacement(node) {
|
|
64
|
+
return (node == null ? void 0 : node.componentId) === REUSABLE_INSTANCE_COMPONENT_ID || isPlacedFormNode(node);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* The tree a placement grafts, from whichever map its kind reads — the
|
|
68
|
+
* component definitions for an instance, the published form designs for a
|
|
69
|
+
* placed form — or `undefined` when it is not a placement or its ref resolves
|
|
70
|
+
* to nothing (AGL-3285). The editor panels use it to offer the same targets
|
|
71
|
+
* the graft will consult.
|
|
72
|
+
*/ export function placementDefinitionFor(node, sources) {
|
|
73
|
+
const props = node == null ? void 0 : node.props;
|
|
74
|
+
if ((node == null ? void 0 : node.componentId) === REUSABLE_INSTANCE_COMPONENT_ID) {
|
|
75
|
+
var _sources_definitions;
|
|
76
|
+
const refId = props == null ? void 0 : props['refId'];
|
|
77
|
+
return typeof refId === 'string' && refId ? (_sources_definitions = sources.definitions) == null ? void 0 : _sources_definitions[refId] : undefined;
|
|
78
|
+
}
|
|
79
|
+
if (isPlacedFormNode(node)) {
|
|
80
|
+
var _sources_formDesigns, _design_nodes;
|
|
81
|
+
const design = (_sources_formDesigns = sources.formDesigns) == null ? void 0 : _sources_formDesigns[props == null ? void 0 : props[FORM_ID_PROP]];
|
|
82
|
+
// The graft's resolution rule: a design whose root is not among its own
|
|
83
|
+
// nodes grafts nothing, so it offers nothing to override either.
|
|
84
|
+
return (design == null ? void 0 : design.rootId) && ((_design_nodes = design.nodes) == null ? void 0 : _design_nodes[design.rootId]) ? design : undefined;
|
|
85
|
+
}
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
39
88
|
/**
|
|
40
89
|
* Every per-instance style override an instance node carries, keyed the way
|
|
41
90
|
* it is stored (AGL-1332): {@link STYLE_OVERRIDES_ROOT_KEY} for the
|
|
42
91
|
* component root, and a DEFINITION-internal node id for each overridden
|
|
43
|
-
* leaf. Empty for a node that is not
|
|
92
|
+
* leaf. Empty for a node that is not a placement ({@link
|
|
93
|
+
* isDefinitionPlacement}: an instance or a placed form) or carries none.
|
|
44
94
|
*
|
|
45
95
|
* Keyed on the definition's own id, never the grafted `cmp__…` id: the
|
|
46
96
|
* graft id is DERIVED from the instance id, so keying on it would break the
|
|
@@ -56,7 +106,7 @@ const isStyleRecord = (value)=>typeof value === 'object' && value !== null && !A
|
|
|
56
106
|
* {@link composeReusableComponentNodes}, so they cannot disagree about
|
|
57
107
|
* what an override means.
|
|
58
108
|
*/ export function getInstanceStyleOverrides(node) {
|
|
59
|
-
if ((node
|
|
109
|
+
if (!isDefinitionPlacement(node)) return {};
|
|
60
110
|
const overrides = node == null ? void 0 : node.styleOverrides;
|
|
61
111
|
if (!isStyleRecord(overrides)) return {};
|
|
62
112
|
const clean = {};
|
|
@@ -117,6 +167,65 @@ const isStyleRecord = (value)=>typeof value === 'object' && value !== null && !A
|
|
|
117
167
|
*/ const ATTR_OVERRIDE_REFUSED_PROPS = new Set([
|
|
118
168
|
'sx'
|
|
119
169
|
]);
|
|
170
|
+
/**
|
|
171
|
+
* Props a placed form's attribute overrides must never address, on any node
|
|
172
|
+
* of its design (AGL-3285).
|
|
173
|
+
*
|
|
174
|
+
* A form is edited once and every page placing it follows — that is the
|
|
175
|
+
* whole point of the form entity. Its submission list, its dataset columns,
|
|
176
|
+
* its consent tick and its routing are all keyed to what the ENTITY declares,
|
|
177
|
+
* so a page that renamed a field, retyped it, made it optional, rewrote its
|
|
178
|
+
* choices or re-pointed where the form sends would file answers the form's
|
|
179
|
+
* own schema does not describe, from one page only, with nothing on the
|
|
180
|
+
* Forms page saying so. Per page, a form may LOOK different; it may not
|
|
181
|
+
* collect something different.
|
|
182
|
+
*
|
|
183
|
+
* - The form node's identity and submission config: `formId`, `formName`,
|
|
184
|
+
* `datasetId`, `datasetName`, and the after-submit outcome
|
|
185
|
+
* (`afterSubmit`, `redirectScreenId`, `redirectUrl`, `revealNodeId`).
|
|
186
|
+
* - Each field's contract: `fieldName` (the submission key),
|
|
187
|
+
* `datasetFieldId`, `fieldType`, `options` (a ticked option's TEXT is the
|
|
188
|
+
* posted value, and the consent check reads it) and `required`.
|
|
189
|
+
* - Element attributes a browser submission reads directly: `name`, `type`
|
|
190
|
+
* (a submit button made a plain button no longer submits), `value`,
|
|
191
|
+
* `action`, `method`, and `disabled` (a disabled input is not posted).
|
|
192
|
+
* - The hide directives: a hidden field is pruned from the page, so its
|
|
193
|
+
* value is never sent.
|
|
194
|
+
*
|
|
195
|
+
* Label, placeholder, help text, the submit label and success message, and
|
|
196
|
+
* everything about styling stay overridable.
|
|
197
|
+
*/ export const PLACED_FORM_REFUSED_ATTR_PROPS = new Set([
|
|
198
|
+
FORM_ID_PROP,
|
|
199
|
+
'formName',
|
|
200
|
+
'datasetId',
|
|
201
|
+
'datasetName',
|
|
202
|
+
'afterSubmit',
|
|
203
|
+
'redirectScreenId',
|
|
204
|
+
'redirectUrl',
|
|
205
|
+
'revealNodeId',
|
|
206
|
+
'fieldName',
|
|
207
|
+
'datasetFieldId',
|
|
208
|
+
'fieldType',
|
|
209
|
+
'options',
|
|
210
|
+
'required',
|
|
211
|
+
'name',
|
|
212
|
+
'type',
|
|
213
|
+
'value',
|
|
214
|
+
'action',
|
|
215
|
+
'method',
|
|
216
|
+
'disabled',
|
|
217
|
+
NODE_HIDE_IF_PROP,
|
|
218
|
+
NODE_HIDE_UNLESS_PROP
|
|
219
|
+
]);
|
|
220
|
+
/**
|
|
221
|
+
* Whether `prop` may never be an attribute override on this placement — `sx`
|
|
222
|
+
* on every placement, plus {@link PLACED_FORM_REFUSED_ATTR_PROPS} on a placed
|
|
223
|
+
* form. The render layer and the editor's writer both ask this, so a refused
|
|
224
|
+
* prop can neither be offered nor take effect.
|
|
225
|
+
*/ export function isAttrOverrideRefused(placement, prop) {
|
|
226
|
+
if (ATTR_OVERRIDE_REFUSED_PROPS.has(prop)) return true;
|
|
227
|
+
return isPlacedFormNode(placement) && PLACED_FORM_REFUSED_ATTR_PROPS.has(prop);
|
|
228
|
+
}
|
|
120
229
|
/**
|
|
121
230
|
* Every per-instance ATTRIBUTE override an instance node carries, keyed the
|
|
122
231
|
* way {@link getInstanceStyleOverrides} keys styles — `root` for the
|
|
@@ -127,7 +236,7 @@ const isStyleRecord = (value)=>typeof value === 'object' && value !== null && !A
|
|
|
127
236
|
* that reached storage before a refusal existed (or by any path that did not
|
|
128
237
|
* go through the panel) still cannot take effect.
|
|
129
238
|
*/ export function getInstanceAttrOverrides(node) {
|
|
130
|
-
if ((node
|
|
239
|
+
if (!isDefinitionPlacement(node)) return {};
|
|
131
240
|
const overrides = node == null ? void 0 : node.attrOverrides;
|
|
132
241
|
if (!isStyleRecord(overrides)) return {};
|
|
133
242
|
const clean = {};
|
|
@@ -135,7 +244,7 @@ const isStyleRecord = (value)=>typeof value === 'object' && value !== null && !A
|
|
|
135
244
|
if (!key || !isStyleRecord(value)) continue;
|
|
136
245
|
const slice = {};
|
|
137
246
|
for (const [prop, propValue] of Object.entries(value)){
|
|
138
|
-
if (
|
|
247
|
+
if (isAttrOverrideRefused(node, prop)) continue;
|
|
139
248
|
slice[prop] = propValue;
|
|
140
249
|
}
|
|
141
250
|
if (Object.keys(slice).length > 0) clean[key] = slice;
|
|
@@ -850,6 +959,9 @@ function hasVisibilityDirective(props) {
|
|
|
850
959
|
// Per-leaf is not a nicety: a component's text leaves usually set
|
|
851
960
|
// their OWN colour, so a root-level `color` never reaches them and an
|
|
852
961
|
// instance given a white background renders white-on-white (AGL-1318).
|
|
962
|
+
//
|
|
963
|
+
// A placed form carries the same two layers (AGL-3285), so one page can
|
|
964
|
+
// restyle a field's label without editing the form for every page.
|
|
853
965
|
const styleOverrides = getInstanceStyleOverrides(instanceNode);
|
|
854
966
|
// Attribute overrides (AGL-1899) ride the same graft for the same
|
|
855
967
|
// reason: this is the one point where per-instance scope exists, so
|
|
@@ -909,6 +1021,7 @@ function hasVisibilityDirective(props) {
|
|
|
909
1021
|
const rootNamesItself = Boolean(grafting) && refIdOf(definitionRoot) === grafting;
|
|
910
1022
|
const graftedRoot = next[graftedRootId];
|
|
911
1023
|
if (rootNamesItself && graftedRoot) {
|
|
1024
|
+
var _instanceNode_props;
|
|
912
1025
|
const adopted = Array.isArray(graftedRoot.nodes) ? graftedRoot.nodes : [];
|
|
913
1026
|
for (const childId of adopted){
|
|
914
1027
|
if (next[childId]) {
|
|
@@ -918,11 +1031,24 @@ function hasVisibilityDirective(props) {
|
|
|
918
1031
|
}
|
|
919
1032
|
}
|
|
920
1033
|
delete next[graftedRootId];
|
|
921
|
-
|
|
1034
|
+
/*
|
|
1035
|
+
* The root's own override slices land on the instance, because the
|
|
1036
|
+
* instance IS the root here (AGL-3285). The grafted root carried them
|
|
1037
|
+
* and has just been dropped; without this a placed form's `root`
|
|
1038
|
+
* target would save and never render. Same precedence as the merge
|
|
1039
|
+
* branch below: the slice first, the placement's own `sx` over it.
|
|
1040
|
+
*/ const rootSx = styleOverrides[STYLE_OVERRIDES_ROOT_KEY];
|
|
1041
|
+
const rootAttrs = attrOverrides[STYLE_OVERRIDES_ROOT_KEY];
|
|
1042
|
+
const unwrappedSx = rootSx ? mergeNodeSx(rootSx, instanceNode.sx) : undefined;
|
|
1043
|
+
next[instanceId] = _extends({}, instanceNode, unwrappedSx === undefined ? {} : {
|
|
1044
|
+
sx: unwrappedSx
|
|
1045
|
+
}, rootAttrs ? {
|
|
1046
|
+
props: _extends({}, (_instanceNode_props = instanceNode.props) != null ? _instanceNode_props : {}, rootAttrs)
|
|
1047
|
+
} : {}, {
|
|
922
1048
|
nodes: adopted
|
|
923
1049
|
});
|
|
924
1050
|
} else if (graftedRoot) {
|
|
925
|
-
var _graftedRoot_props,
|
|
1051
|
+
var _graftedRoot_props, _instanceNode_props1;
|
|
926
1052
|
/*
|
|
927
1053
|
* THE INSTANCE AND THE DEFINITION'S ROOT ARE ONE ELEMENT (AGL-2521).
|
|
928
1054
|
*
|
|
@@ -979,8 +1105,8 @@ function hasVisibilityDirective(props) {
|
|
|
979
1105
|
];
|
|
980
1106
|
const carried = {};
|
|
981
1107
|
for (const key of INSTANCE_CARRIED_PROPS){
|
|
982
|
-
var
|
|
983
|
-
const value = (
|
|
1108
|
+
var _instanceNode_props2;
|
|
1109
|
+
const value = (_instanceNode_props2 = instanceNode.props) == null ? void 0 : _instanceNode_props2[key];
|
|
984
1110
|
if (value !== undefined) carried[key] = value;
|
|
985
1111
|
}
|
|
986
1112
|
/*
|
|
@@ -998,8 +1124,8 @@ function hasVisibilityDirective(props) {
|
|
|
998
1124
|
*/ const repeatsPlacement = repeatKey(instanceNode) !== '';
|
|
999
1125
|
if (repeatsPlacement) {
|
|
1000
1126
|
for (const key of REPEAT_DIRECTIVE_PROPS){
|
|
1001
|
-
var
|
|
1002
|
-
const value = (
|
|
1127
|
+
var _instanceNode_props3;
|
|
1128
|
+
const value = (_instanceNode_props3 = instanceNode.props) == null ? void 0 : _instanceNode_props3[key];
|
|
1003
1129
|
if (value !== undefined) carried[key] = value;
|
|
1004
1130
|
}
|
|
1005
1131
|
carried[REPEAT_SELF_PROP] = true;
|
|
@@ -1014,7 +1140,7 @@ function hasVisibilityDirective(props) {
|
|
|
1014
1140
|
* element answering to the placement's id, which after the merge is
|
|
1015
1141
|
* this one. Keeping either list alone drops the other author's names,
|
|
1016
1142
|
* so the two are joined, each name once, in `props` and on the node.
|
|
1017
|
-
*/ const propsClassName = joinClassNames((_graftedRoot_props = graftedRoot.props) == null ? void 0 : _graftedRoot_props['className'], (
|
|
1143
|
+
*/ const propsClassName = joinClassNames((_graftedRoot_props = graftedRoot.props) == null ? void 0 : _graftedRoot_props['className'], (_instanceNode_props1 = instanceNode.props) == null ? void 0 : _instanceNode_props1['className']);
|
|
1018
1144
|
if (propsClassName) carried['className'] = propsClassName;
|
|
1019
1145
|
const nodeClassName = joinClassNames(graftedRoot.className, instanceNode.className);
|
|
1020
1146
|
// The override slices and the reference are DOCUMENT state living on
|