@peripheries/renderer 0.24.0 → 0.25.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/README.md +30 -0
- package/dist/catalog.js +1 -1
- package/dist/chunks/{projection-zhu32_KJ.js → projection-e8MHn6Bt.js} +40 -2
- package/dist/chunks/{validate-BcB_PA1k.js → validate-CrHGlwey.js} +18 -0
- package/dist/core.js +8 -4
- package/dist/react.js +59 -11
- package/dist/styles.css +6 -0
- package/dist/types/core/cell-props.d.ts +3 -0
- package/dist/types/core/cells.d.ts +2 -1
- package/dist/types/core/diagnostics.d.ts +4 -0
- package/dist/types/core/index.d.ts +2 -2
- package/dist/types/core/types.d.ts +5 -2
- package/dist/types/react/cell-states.d.ts +4 -1
- package/dist/types/react/cell-view.d.ts +2 -0
- package/dist/types/react/index.d.ts +1 -1
- package/dist/types/react/page-renderer.d.ts +2 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -51,6 +51,11 @@ const runtime = await loadPageData(page, source, { params, baseProps, viewer, co
|
|
|
51
51
|
- `frameProps(cell, contract)`: extra attributes and style on a cell's frame,
|
|
52
52
|
for what the host doesn't apply yet (a print policy, say).
|
|
53
53
|
- `onDiagnostic(d)`: a page configuration problem.
|
|
54
|
+
- `showHiddenCells`: where a page is being built or reviewed (an editor, a
|
|
55
|
+
harness, a preview), a cell its `hide_when_empty` leaves out shows as a
|
|
56
|
+
small "hidden (empty)" placeholder instead of nothing.
|
|
57
|
+
- `showProblemCodes`: a cell's problem notice adds its diagnostic code, for
|
|
58
|
+
staff and editors; without it a viewer reads only the plain wording.
|
|
54
59
|
|
|
55
60
|
A consumer with resolved values instead of a graph (the component repos'
|
|
56
61
|
harness) builds its runtime with `pageRuntimeFromValues(page, { values,
|
|
@@ -70,6 +75,7 @@ names: 0.8.0 dropped the old client-prefixed twins and container names.
|
|
|
70
75
|
| A cell's buckets | `data-hide-when`, `data-show-when`, `data-stack-when`, `data-truncate-when` |
|
|
71
76
|
| A structure section | `data-section`, `data-section-level`, `data-section-kind`, `data-derived-ordinal` |
|
|
72
77
|
| A missing component's notice | `data-missing-component` |
|
|
78
|
+
| A cell `hide_when_empty` left out, shown by `showHiddenCells` | `.cell-hidden`, `data-cell-state="hidden-empty"` |
|
|
73
79
|
|
|
74
80
|
The store specifier and runtime global are in `@peripheries/runtime`'s README.
|
|
75
81
|
The theme reaches a page as CSS, in the `theme` layer; the renderer ignores a
|
|
@@ -77,6 +83,30 @@ bundle's `ThemeProvider` export.
|
|
|
77
83
|
|
|
78
84
|
## Versions
|
|
79
85
|
|
|
86
|
+
- **0.25.0** (2026-10-24): `hide_when_empty`, a cell-level list of prop names.
|
|
87
|
+
- **A cell with `hide_when_empty: ["title", "items"]`** renders nothing, and leaves no gap,
|
|
88
|
+
when every prop it names is empty once bound: null, undefined, text that is blank once
|
|
89
|
+
trimmed, `[]` or `{}`. `0` and `false` are content. A slot or `children` filled by child
|
|
90
|
+
cells is content. Print follows the screen. The check runs before the bundles load, so a
|
|
91
|
+
server render and the browser agree.
|
|
92
|
+
- **A refused, failed or unresolved read is not empty.** A named prop that reads a label whose
|
|
93
|
+
read was refused or failed keeps the cell's error notice. One whose query never ran because
|
|
94
|
+
its own reference resolved to nothing (`QUERY_UNRESOLVED`) shows the same notice instead of
|
|
95
|
+
hiding (`hideWhenEmptyProblem`). A label still loading isn't decided yet (the cell renders).
|
|
96
|
+
- **`RendererEnvironment.showProblemCodes`**: a problem notice adds its diagnostic code
|
|
97
|
+
(`data-cell-code`, `.cell-notice__code`) for staff and editors; a client reads only "This
|
|
98
|
+
part of the page couldn't load." The host sets it in the platform environment and in a
|
|
99
|
+
proposal preview.
|
|
100
|
+
- **`RendererEnvironment.showHiddenCells`**: an editor, a harness or a preview shows a hidden
|
|
101
|
+
cell as a small "hidden (empty)" placeholder (`CellHidden`, `.cell-hidden`).
|
|
102
|
+
- **Validation:** `validatePage` reports `PROP_UNKNOWN` (warning) at
|
|
103
|
+
`content.cells[i].hide_when_empty[j]` for a name the component's `props_schema` doesn't
|
|
104
|
+
declare, and `HIDE_WHEN_EMPTY_INVALID` (error) for anything but a non-empty list of names.
|
|
105
|
+
- A ref cell's node owns `hide_when_empty` (`CONTENT_FIELDS`, via `RENDER_FIELDS`). `/core`
|
|
106
|
+
exports `hiddenWhenEmpty`, `hideWhenEmptyProblem` and `isEmptyPropValue`. `PAGE_RULES` names the key.
|
|
107
|
+
- The renderer's `PageCell` type adds `hide_when_empty?: string[]`; `@peripheries/runtime`'s
|
|
108
|
+
`PageCell` gains it at its next release.
|
|
109
|
+
|
|
80
110
|
- **0.24.0** (2026-10-20): one renderer batch (CX).
|
|
81
111
|
- **`layerName(system)` and `LAYER_ROLES`** in `/core`: a system's state name (its layer's role,
|
|
82
112
|
else a registered layer's own type, `question`, `answer`, …) from the layer catalog, which is
|
package/dist/catalog.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { x as isEach } from "./chunks/query-D6xQbTFK.js";
|
|
2
|
-
import { i as eachFormProblems, s as validatePage } from "./chunks/validate-
|
|
2
|
+
import { i as eachFormProblems, s as validatePage } from "./chunks/validate-CrHGlwey.js";
|
|
3
3
|
import { KNOWLEDGE_BUDGETS, KNOWLEDGE_KEY } from "@peripheries/runtime";
|
|
4
4
|
import Ajv2020 from "ajv/dist/2020.js";
|
|
5
5
|
var ref = (kind, key) => `${kind}:${key}`;
|
|
@@ -16,7 +16,12 @@ var STORE_FIELDS = [
|
|
|
16
16
|
"viewState",
|
|
17
17
|
"live"
|
|
18
18
|
];
|
|
19
|
-
var
|
|
19
|
+
var RENDER_FIELDS = ["hide_when_empty"];
|
|
20
|
+
var CONTENT_FIELDS = [
|
|
21
|
+
...HYDRATED_CELL_FIELDS.filter((f) => f !== "layout"),
|
|
22
|
+
...STORE_FIELDS,
|
|
23
|
+
...RENDER_FIELDS
|
|
24
|
+
];
|
|
20
25
|
function normalizeCell(cell, path = "cell") {
|
|
21
26
|
const diagnostics = [];
|
|
22
27
|
if (cell.ref) return {
|
|
@@ -114,6 +119,39 @@ function failedLabels(cell, data) {
|
|
|
114
119
|
return Boolean(entry?.error) && !entry?.unresolved?.length && !entry?.isLoading && (entry?.data === void 0 || entry?.data === null);
|
|
115
120
|
});
|
|
116
121
|
}
|
|
122
|
+
function isEmptyPropValue(value) {
|
|
123
|
+
if (value === null || value === void 0) return true;
|
|
124
|
+
if (typeof value === "string") return value.trim() === "";
|
|
125
|
+
if (Array.isArray(value)) return value.length === 0;
|
|
126
|
+
if (typeof value === "object") {
|
|
127
|
+
const proto = Object.getPrototypeOf(value);
|
|
128
|
+
return (proto === Object.prototype || proto === null) && Object.keys(value).length === 0;
|
|
129
|
+
}
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
var propLabels = (value) => [...JSON.stringify(value ?? null).matchAll(LABEL_REF)].map((m) => m[1]);
|
|
133
|
+
function hideWhenEmptyProblem(cell, data) {
|
|
134
|
+
const names = cell.hide_when_empty;
|
|
135
|
+
if (!Array.isArray(names)) return null;
|
|
136
|
+
for (const name of names) {
|
|
137
|
+
if (typeof name !== "string") continue;
|
|
138
|
+
for (const label of propLabels(cell.props?.[name])) if (data[label]?.unresolved?.length && !data[label]?.isLoading) return "QUERY_UNRESOLVED";
|
|
139
|
+
}
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
function hiddenWhenEmpty(cell, props, data) {
|
|
143
|
+
const names = cell.hide_when_empty;
|
|
144
|
+
if (!Array.isArray(names) || names.length === 0) return false;
|
|
145
|
+
for (const name of names) {
|
|
146
|
+
if (typeof name !== "string") return false;
|
|
147
|
+
for (const label of propLabels(cell.props?.[name])) {
|
|
148
|
+
const entry = data[label];
|
|
149
|
+
if (entry?.isLoading || entry?.error) return false;
|
|
150
|
+
}
|
|
151
|
+
if (!isEmptyPropValue(props[name])) return false;
|
|
152
|
+
}
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
117
155
|
function assembleCellProps(i) {
|
|
118
156
|
const { style: _fromConfig, ...resolved } = i.resolvedProps;
|
|
119
157
|
const props = { ...resolved };
|
|
@@ -304,4 +342,4 @@ function collectScopeIds(ctx, selections, viewState) {
|
|
|
304
342
|
}
|
|
305
343
|
return [...ids];
|
|
306
344
|
}
|
|
307
|
-
export {
|
|
345
|
+
export { HYDRATED_CELL_FIELDS as C, normalizeCell as D, hasRef as E, resolveCell as O, CONTENT_FIELDS as S, STORE_FIELDS as T, failedLabels as _, projectSelections as a, isEmptyPropValue as b, pageScopeKey as c, resolveComponent as d, cellAttributes as f, assembleCellProps as g, pageContainerStyle as h, projectLive as i, rootCells as k, projectScopeKey as l, documentProfile as m, attachViewStateSetters as n, projectViewState as o, cellPlacementStyle as p, collectScopeIds as r, shallowEqualBundle as s, attachSelectionSetters as t, resolveScopeId as u, hiddenWhenEmpty as v, RENDER_FIELDS as w, projectCellData as x, hideWhenEmptyProblem as y };
|
|
@@ -641,6 +641,16 @@ function validateCell(cell, catalog, path = "cell", ids, options = {}) {
|
|
|
641
641
|
message: `${cell.component} requires ${prop}`
|
|
642
642
|
});
|
|
643
643
|
}
|
|
644
|
+
const listed = schema?.properties;
|
|
645
|
+
if (listed && Array.isArray(cell.hide_when_empty)) cell.hide_when_empty.forEach((prop, i) => {
|
|
646
|
+
if (typeof prop === "string" && prop !== "children" && !(prop in listed)) push({
|
|
647
|
+
severity: "warning",
|
|
648
|
+
code: "PROP_UNKNOWN",
|
|
649
|
+
cellId,
|
|
650
|
+
path: `${path}.hide_when_empty[${i}]`,
|
|
651
|
+
message: `hide_when_empty names ${prop}, which ${cell.component} doesn’t declare`
|
|
652
|
+
});
|
|
653
|
+
});
|
|
644
654
|
const declared = schema?.properties;
|
|
645
655
|
if (declared && schema?.additionalProperties !== true && typeof schema?.additionalProperties !== "object") {
|
|
646
656
|
for (const prop of Object.keys(cell.props ?? {})) if (prop !== "children" && prop !== "style" && !(prop in declared)) push({
|
|
@@ -674,6 +684,14 @@ function validateCell(cell, catalog, path = "cell", ids, options = {}) {
|
|
|
674
684
|
path: `${path}.children`,
|
|
675
685
|
message: `child cell "${child}" is not on the page`
|
|
676
686
|
});
|
|
687
|
+
const hide = cell.hide_when_empty;
|
|
688
|
+
if (hide !== void 0 && (!Array.isArray(hide) || hide.length === 0 || hide.some((p) => typeof p !== "string" || !p))) push({
|
|
689
|
+
severity: "error",
|
|
690
|
+
code: "HIDE_WHEN_EMPTY_INVALID",
|
|
691
|
+
cellId,
|
|
692
|
+
path: `${path}.hide_when_empty`,
|
|
693
|
+
message: "hide_when_empty takes a non-empty list of prop names"
|
|
694
|
+
});
|
|
677
695
|
checkBindings(cell.data?.queries, `${path}.data.queries`, push, cellId, options.template);
|
|
678
696
|
checkActions(cell, path, push);
|
|
679
697
|
checkProjections(cell.props, `${path}.props`, push, cellId);
|
package/dist/core.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { C as normalizeCell,
|
|
1
|
+
import { C as HYDRATED_CELL_FIELDS, D as normalizeCell, E as hasRef, O as resolveCell, S as CONTENT_FIELDS, T as STORE_FIELDS, a as projectSelections, b as isEmptyPropValue, c as pageScopeKey, d as resolveComponent, f as cellAttributes, g as assembleCellProps, h as pageContainerStyle, i as projectLive, k as rootCells, l as projectScopeKey, n as attachViewStateSetters, o as projectViewState, p as cellPlacementStyle, r as collectScopeIds, s as shallowEqualBundle, t as attachSelectionSetters, u as resolveScopeId, v as hiddenWhenEmpty, w as RENDER_FIELDS, x as projectCellData, y as hideWhenEmptyProblem } from "./chunks/projection-e8MHn6Bt.js";
|
|
2
2
|
import { C as passesConditions, S as pageContext, T as settled, _ as EACH, a as VARIABLE_NAMESPACES, b as cellResolutionContext, c as pageVariables, d as resolveDescriptorVariables, f as selectionShape, g as variableReferences, h as variableLabels, i as UnsafeDocumentError, l as prepareDocument, m as validateDescriptor, n as RENDER_FUNCTIONS, o as descriptorShape, p as shapeAt, r as RENDER_FUNCTIONS_AHEAD, s as orderByVariable, t as PAGING_VARIABLES, u as queryKey, v as EACH_DEFAULT_AS, w as resolveReferences, x as isEach, y as cellContext } from "./chunks/query-D6xQbTFK.js";
|
|
3
|
-
import { _ as stateValue, a as nearestNames, b as walkKey, c as selectionIssues, d as needsWalk, f as readField, g as stateNames, h as resolveWalkStart, i as eachFormProblems, l as RESERVED_LABELS, m as referencedLabels, n as STORE_SCOPES, o as validateCell, p as readWalk, r as WALK_DIRECTIONS, s as validatePage, t as RESPONSIVE_BUCKETS, u as mergeStates, v as systemsFromGraphs, x as walkRequest, y as systemsFromProcess } from "./chunks/validate-
|
|
3
|
+
import { _ as stateValue, a as nearestNames, b as walkKey, c as selectionIssues, d as needsWalk, f as readField, g as stateNames, h as resolveWalkStart, i as eachFormProblems, l as RESERVED_LABELS, m as referencedLabels, n as STORE_SCOPES, o as validateCell, p as readWalk, r as WALK_DIRECTIONS, s as validatePage, t as RESPONSIVE_BUCKETS, u as mergeStates, v as systemsFromGraphs, x as walkRequest, y as systemsFromProcess } from "./chunks/validate-CrHGlwey.js";
|
|
4
4
|
import { C as resolveAction, S as projectCellActions, _ as actionBindingKey, a as deriveSectionReferences, b as declaredActions, c as nodePrintStyle, d as referencesFor, f as sectionAnchor, g as usesStructureLayout, h as tableOfContents, i as deriveSectionNumbering, l as normalizeStructure, m as structureIssues, n as SECTION_KINDS, r as deriveElementReferences, s as mergeSectionReferences, t as PAGE_DOCUMENT, u as numberingFor, v as actionProblems, x as normalizeActionDeclaration, y as actionReferences } from "./chunks/structure-CKBtQHJq.js";
|
|
5
5
|
import { a as loadBundlesSettled, i as loadBundles, n as isComponentExport, o as projectModule, r as loadBundle, s as runtimeReady, t as _clearBundleCache } from "./chunks/load-b38vUiZM.js";
|
|
6
6
|
import { a as normalizeRef, i as bundleScope, n as MAIN, o as refKey, r as PUBLIC, t as CORE_SPECIFIER } from "./chunks/refs-Csgh6rXq.js";
|
|
@@ -801,6 +801,10 @@ var DIAGNOSTICS = {
|
|
|
801
801
|
meaning: "A literal prop value fails its schema.",
|
|
802
802
|
fix: "Write the value in the shape `props_schema` gives, or bind it with a whole-string `@label.path` reference."
|
|
803
803
|
},
|
|
804
|
+
HIDE_WHEN_EMPTY_INVALID: {
|
|
805
|
+
meaning: "A cell’s `hide_when_empty` isn’t a list of prop names.",
|
|
806
|
+
fix: "Write `hide_when_empty: [\"title\", \"items\"]`: the props that, all empty once bound (null, blank text, `[]`, `{}`), leave the cell out."
|
|
807
|
+
},
|
|
804
808
|
CELL_UNKNOWN_CHILD: {
|
|
805
809
|
meaning: "`children` names a cell that is not on the page.",
|
|
806
810
|
fix: "List only ids of cells on this page in `children`."
|
|
@@ -1054,7 +1058,7 @@ The host's rules for every page. Components, kits and patterns are in the catalo
|
|
|
1054
1058
|
- Every cell has a unique \`id\`. Inline: \`{ id, component, props, layout? }\`. Managed by a cell node: \`{ id, ref: { graph_node_id }, layout? }\`; its \`component\` and \`props\` are the fallback.
|
|
1055
1059
|
- \`children\` lists child cell ids, rendered inside the cell in place of \`props.children\`.
|
|
1056
1060
|
- A child with \`slot\` fills that \`x-slot\` of its parent; others are \`children\`.
|
|
1057
|
-
- \`conditions.showIf\` / \`hideIf\` test a value
|
|
1061
|
+
- \`conditions.showIf\` / \`hideIf\` test a value for truthiness; \`hide_when_empty: [props]\` hides the cell if all are empty.
|
|
1058
1062
|
- An embed cell \`{ id, embed: { page_id } | { page_type, project_id?, process_id? } }\` renders another published page, one level deep.
|
|
1059
1063
|
|
|
1060
1064
|
## Data and references
|
|
@@ -1132,4 +1136,4 @@ function manifestPageFrom(graphNodeId, data) {
|
|
|
1132
1136
|
}
|
|
1133
1137
|
};
|
|
1134
1138
|
}
|
|
1135
|
-
export { CONTENT_FIELDS, CORE_SPECIFIER, DIAGNOSTICS, EACH, EACH_DEFAULT_AS, HYDRATED_CELL_FIELDS, LAYER_ROLES, LAYOUT_TYPES, MAIN, PAGE_DOCUMENT, PAGE_RULES, PAGING_VARIABLES, PUBLIC, RENDER_FUNCTIONS, RENDER_FUNCTIONS_AHEAD, RESERVED_LABELS, RESPONSIVE_BUCKETS, SECTION_KINDS, STORE_FIELDS, STORE_SCOPES, STRUCTURE_FIELDS, TOOL_OWNED_KEYS, UnsafeDocumentError, VARIABLE_NAMESPACES, WALK_DIRECTIONS, _clearBundleCache, actionBindingKey, actionProblems, actionReferences, assembleCellProps, attachSelectionSetters, attachViewStateSetters, buildDataContext, bundleScope, cellAttributes, cellContext, cellPlacementStyle, cellPropSources, cellResolutionContext, clearScope, collectDeclarations, collectDescriptors, collectScopeIds, collectStatic, createStackStore, declaredActions, deriveElementReferences, deriveSectionNumbering, deriveSectionReferences, descriptorShape, eachFormProblems, editTarget, hasRef, isComponentExport, isEach, layerName, loadBundle, loadBundles, loadBundlesSettled, loadPageData, loadScope, manifestPageFrom, mergeSectionReferences, mergeStates, nearestNames, needsWalk, nodePrintStyle, normalizeActionDeclaration, normalizeCell, normalizeRef, normalizeStructure, numberingFor, orderByVariable, pageBundleRefs, pageBundles, pageContainerStyle, pageContext, pageRuntimeFromValues, pageScopeKey, pageVariables, passesConditions, patchAt, prepareDocument, projectCellActions, projectCellData, projectLive, projectModule, projectScopeKey, projectSelections, projectViewState, queryKey, readField, readWalk, refKey, referencedLabels, referencesFor, resolveAction, resolveCell, resolveComponent, resolveDataSystem, resolveDeclarations, resolveDescriptorVariables, resolveReferences, resolveScopeId, resolveSystems, resolveWalkStart, rootCells, runtimeReady, saveScope, sectionAnchor, selectionIssues, selectionShape, settled, shallowEqualBundle, shapeAt, stateNames, stateValue, structureIssues, systemsFromGraphs, systemsFromProcess, tableOfContents, usesStructureLayout, validateCell, validateDescriptor, validateManifest, validatePage, variableLabels, variableReferences, walkKey, walkRequest };
|
|
1139
|
+
export { CONTENT_FIELDS, CORE_SPECIFIER, DIAGNOSTICS, EACH, EACH_DEFAULT_AS, HYDRATED_CELL_FIELDS, LAYER_ROLES, LAYOUT_TYPES, MAIN, PAGE_DOCUMENT, PAGE_RULES, PAGING_VARIABLES, PUBLIC, RENDER_FIELDS, RENDER_FUNCTIONS, RENDER_FUNCTIONS_AHEAD, RESERVED_LABELS, RESPONSIVE_BUCKETS, SECTION_KINDS, STORE_FIELDS, STORE_SCOPES, STRUCTURE_FIELDS, TOOL_OWNED_KEYS, UnsafeDocumentError, VARIABLE_NAMESPACES, WALK_DIRECTIONS, _clearBundleCache, actionBindingKey, actionProblems, actionReferences, assembleCellProps, attachSelectionSetters, attachViewStateSetters, buildDataContext, bundleScope, cellAttributes, cellContext, cellPlacementStyle, cellPropSources, cellResolutionContext, clearScope, collectDeclarations, collectDescriptors, collectScopeIds, collectStatic, createStackStore, declaredActions, deriveElementReferences, deriveSectionNumbering, deriveSectionReferences, descriptorShape, eachFormProblems, editTarget, hasRef, hiddenWhenEmpty, hideWhenEmptyProblem, isComponentExport, isEach, isEmptyPropValue, layerName, loadBundle, loadBundles, loadBundlesSettled, loadPageData, loadScope, manifestPageFrom, mergeSectionReferences, mergeStates, nearestNames, needsWalk, nodePrintStyle, normalizeActionDeclaration, normalizeCell, normalizeRef, normalizeStructure, numberingFor, orderByVariable, pageBundleRefs, pageBundles, pageContainerStyle, pageContext, pageRuntimeFromValues, pageScopeKey, pageVariables, passesConditions, patchAt, prepareDocument, projectCellActions, projectCellData, projectLive, projectModule, projectScopeKey, projectSelections, projectViewState, queryKey, readField, readWalk, refKey, referencedLabels, referencesFor, resolveAction, resolveCell, resolveComponent, resolveDataSystem, resolveDeclarations, resolveDescriptorVariables, resolveReferences, resolveScopeId, resolveSystems, resolveWalkStart, rootCells, runtimeReady, saveScope, sectionAnchor, selectionIssues, selectionShape, settled, shallowEqualBundle, shapeAt, stateNames, stateValue, structureIssues, systemsFromGraphs, systemsFromProcess, tableOfContents, usesStructureLayout, validateCell, validateDescriptor, validateManifest, validatePage, variableLabels, variableReferences, walkKey, walkRequest };
|
package/dist/react.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { _ as failedLabels, a as projectSelections, d as resolveComponent, f as cellAttributes, g as assembleCellProps, h as pageContainerStyle, i as projectLive, k as rootCells, m as documentProfile, n as attachViewStateSetters, o as projectViewState, p as cellPlacementStyle, s as shallowEqualBundle, t as attachSelectionSetters, u as resolveScopeId, v as hiddenWhenEmpty, x as projectCellData, y as hideWhenEmptyProblem } from "./chunks/projection-e8MHn6Bt.js";
|
|
2
2
|
import { C as passesConditions, S as pageContext, T as settled, b as cellResolutionContext, c as pageVariables, v as EACH_DEFAULT_AS, w as resolveReferences, y as cellContext } from "./chunks/query-D6xQbTFK.js";
|
|
3
3
|
import { a as deriveSectionReferences, b as declaredActions, c as nodePrintStyle, d as referencesFor, f as sectionAnchor, g as usesStructureLayout, i as deriveSectionNumbering, l as normalizeStructure, m as structureIssues, o as isSectionNode, p as sortByOrder, r as deriveElementReferences, s as mergeSectionReferences, t as PAGE_DOCUMENT, u as numberingFor } from "./chunks/structure-CKBtQHJq.js";
|
|
4
4
|
import { a as goToQuestion, c as missing, f as progress, g as visibleSteps, h as stepKind, i as goTo, l as move, m as settle, n as enterAdvances, o as isReadOnlyStatus, p as questionsOf, s as linkTo, t as defaultMode, u as positionFromLink } from "./chunks/forms-DxA9WzCt.js";
|
|
@@ -12,18 +12,34 @@ function CellPlaceholder({ state }) {
|
|
|
12
12
|
"aria-busy": state === "loading"
|
|
13
13
|
});
|
|
14
14
|
}
|
|
15
|
-
function
|
|
15
|
+
function CellHidden() {
|
|
16
|
+
return jsx("div", {
|
|
17
|
+
className: "cell-hidden",
|
|
18
|
+
"data-cell-state": "hidden-empty",
|
|
19
|
+
role: "note",
|
|
20
|
+
children: "hidden (empty)"
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
function CellNotice({ state, component, onRetry, code, showCode, children }) {
|
|
16
24
|
return jsxs("div", {
|
|
17
25
|
className: "cell-notice",
|
|
18
26
|
"data-cell-state": state,
|
|
19
27
|
role: state === "error" ? "alert" : "note",
|
|
20
28
|
...state === "not-found" && component ? { "data-missing-component": component } : {},
|
|
21
|
-
children: [
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
29
|
+
children: [
|
|
30
|
+
children,
|
|
31
|
+
code && showCode && jsxs(Fragment$1, { children: [" ", jsx("code", {
|
|
32
|
+
className: "cell-notice__code",
|
|
33
|
+
"data-cell-code": code,
|
|
34
|
+
children: code
|
|
35
|
+
})] }),
|
|
36
|
+
onRetry && jsxs(Fragment$1, { children: [" ", jsx("button", {
|
|
37
|
+
type: "button",
|
|
38
|
+
className: "cell-notice__retry",
|
|
39
|
+
onClick: onRetry,
|
|
40
|
+
children: "Try again"
|
|
41
|
+
})] })
|
|
42
|
+
]
|
|
27
43
|
});
|
|
28
44
|
}
|
|
29
45
|
var CellErrorBoundary = class extends Component {
|
|
@@ -219,6 +235,23 @@ function mergeActions(a, b) {
|
|
|
219
235
|
...b
|
|
220
236
|
} : void 0;
|
|
221
237
|
}
|
|
238
|
+
function boundProps(cell, ctx, env, injectedProps) {
|
|
239
|
+
const out = { ...resolveReferences(cell.props ?? {}, ctx) };
|
|
240
|
+
for (const id of cell.children ?? []) {
|
|
241
|
+
const child = env.cellsById.get(id);
|
|
242
|
+
if (!child) continue;
|
|
243
|
+
const slot = typeof child.slot === "string" && child.slot ? child.slot : "children";
|
|
244
|
+
if (slot === "item" && cell.props?.each !== void 0) {
|
|
245
|
+
const each = out.each;
|
|
246
|
+
out[slot] = Array.isArray(each) ? each : out[slot];
|
|
247
|
+
} else out[slot] = [...Array.isArray(out[slot]) ? out[slot] : [], id];
|
|
248
|
+
}
|
|
249
|
+
return {
|
|
250
|
+
...out,
|
|
251
|
+
...injectedProps,
|
|
252
|
+
...env.cellProps?.(cell)
|
|
253
|
+
};
|
|
254
|
+
}
|
|
222
255
|
function CellView({ cell, env, nested = false, injectedProps }) {
|
|
223
256
|
const frame = useRef(null);
|
|
224
257
|
const store = useCellStore(cell, env.scope, env.store);
|
|
@@ -305,8 +338,19 @@ function CellView({ cell, env, nested = false, injectedProps }) {
|
|
|
305
338
|
context
|
|
306
339
|
]);
|
|
307
340
|
if (!passesConditions(cell.conditions, ctx)) return null;
|
|
341
|
+
const deciding = Boolean(cell.hide_when_empty?.length && cell.component && !cell.embed && !failedLabels(cell, ctx.data).length);
|
|
342
|
+
const problem = deciding ? hideWhenEmptyProblem(cell, ctx.data) : null;
|
|
343
|
+
const hidden = deciding && !problem && hiddenWhenEmpty(cell, boundProps(cell, ctx, env, injectedProps), ctx.data);
|
|
344
|
+
if (hidden && !env.showHiddenCells) return null;
|
|
308
345
|
let body;
|
|
309
|
-
if (
|
|
346
|
+
if (hidden) body = jsx(CellHidden, {});
|
|
347
|
+
else if (problem) body = jsx(CellNotice, {
|
|
348
|
+
state: "error",
|
|
349
|
+
code: problem,
|
|
350
|
+
showCode: env.showProblemCodes,
|
|
351
|
+
children: "This part of the page couldn't load."
|
|
352
|
+
});
|
|
353
|
+
else if (cell.embed) body = env.renderEmbed?.(cell.id) ?? null;
|
|
310
354
|
else if (!cell.component) body = jsx(CellPlaceholder, { state: "pending" });
|
|
311
355
|
else if (failedLabels(cell, ctx.data).length) body = jsx(CellNotice, {
|
|
312
356
|
state: "error",
|
|
@@ -528,7 +572,7 @@ var warn = (d) => console.warn(`[render] ${d.code}${d.cellId ? ` (cell ${d.cellI
|
|
|
528
572
|
function PageRenderer({ runtime, bundles, params, baseProps, context, viewer, environment }) {
|
|
529
573
|
const { page, cells } = runtime;
|
|
530
574
|
const declared = useMemo(() => new Set(runtime.declared), [runtime.declared]);
|
|
531
|
-
const { core, navigate, store, onDiagnostic = warn, actionsFor, runAction, loadPage, onCellError, onUnresolvedReference, onPropSources, frameProps, cellProps, retry } = environment;
|
|
575
|
+
const { core, navigate, store, onDiagnostic = warn, actionsFor, runAction, loadPage, onCellError, onUnresolvedReference, onPropSources, frameProps, cellProps, retry, showHiddenCells, showProblemCodes } = environment;
|
|
532
576
|
useEffect(() => {
|
|
533
577
|
for (const d of runtime.diagnostics) onDiagnostic(d);
|
|
534
578
|
}, [runtime.diagnostics, onDiagnostic]);
|
|
@@ -556,6 +600,8 @@ function PageRenderer({ runtime, bundles, params, baseProps, context, viewer, en
|
|
|
556
600
|
onDiagnostic,
|
|
557
601
|
frameProps,
|
|
558
602
|
cellProps,
|
|
603
|
+
showHiddenCells,
|
|
604
|
+
showProblemCodes,
|
|
559
605
|
renderEmbed: (cellId) => {
|
|
560
606
|
const embedded = runtime.embeds?.[cellId];
|
|
561
607
|
if (!embedded) return jsx(CellNotice, {
|
|
@@ -607,6 +653,8 @@ function PageRenderer({ runtime, bundles, params, baseProps, context, viewer, en
|
|
|
607
653
|
frameProps,
|
|
608
654
|
cellProps,
|
|
609
655
|
retry,
|
|
656
|
+
showHiddenCells,
|
|
657
|
+
showProblemCodes,
|
|
610
658
|
environment
|
|
611
659
|
]);
|
|
612
660
|
const structured = usesStructureLayout(page.layout);
|
|
@@ -980,4 +1028,4 @@ function bindActions(v, path, latest) {
|
|
|
980
1028
|
if (v && typeof v === "object") return Object.fromEntries(Object.entries(v).map(([k, x]) => [k, bindActions(x, [...path, k], latest)]));
|
|
981
1029
|
return v;
|
|
982
1030
|
}
|
|
983
|
-
export { CellErrorBoundary, CellNotice, CellPlaceholder, CellView, FORM_SAVE_DEBOUNCE_MS, PageRenderer, StructureView, answerInputId, cellLabels, useCellStore, useContainerSize, useFormsRuntime, usePaging, useStructureFacts };
|
|
1031
|
+
export { CellErrorBoundary, CellHidden, CellNotice, CellPlaceholder, CellView, FORM_SAVE_DEBOUNCE_MS, PageRenderer, StructureView, answerInputId, cellLabels, useCellStore, useContainerSize, useFormsRuntime, usePaging, useStructureFacts };
|
package/dist/styles.css
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
--cell-placeholder-bg: var(--color-surface-sunken, #f1f1f1);
|
|
7
7
|
--cell-notice-bg: var(--color-warning-surface, #fff4d6);
|
|
8
8
|
--cell-notice-fg: var(--color-warning, #6b4e00);
|
|
9
|
+
--cell-hidden-border: var(--color-border, #c8c8c8);
|
|
10
|
+
--cell-hidden-fg: var(--color-text-muted, #6b6b6b);
|
|
9
11
|
}
|
|
10
12
|
|
|
11
13
|
[data-page-container] { font-family: var(--font-body, system-ui, sans-serif); color: var(--color-text, inherit); }
|
|
@@ -17,7 +19,9 @@
|
|
|
17
19
|
.cell-placeholder[aria-busy='true'] { animation: cell-pulse 1.5s ease-in-out infinite; }
|
|
18
20
|
@keyframes cell-pulse { 50% { opacity: 0.55; } }
|
|
19
21
|
@media (prefers-reduced-motion: reduce) { .cell-placeholder[aria-busy='true'] { animation: none; } }
|
|
22
|
+
.cell-hidden { padding: 0.125rem 0.5rem; border: 1px dashed var(--cell-hidden-border); border-radius: 0.375rem; color: var(--cell-hidden-fg); font-size: 0.75rem; line-height: 1.5; }
|
|
20
23
|
.cell-notice { padding: 0.75rem 1rem; border-radius: 0.5rem; background: var(--cell-notice-bg); color: var(--cell-notice-fg); }
|
|
24
|
+
.cell-notice__code { font-family: var(--font-mono, ui-monospace, monospace); font-size: 0.75rem; }
|
|
21
25
|
|
|
22
26
|
|
|
23
27
|
[data-show-when] { display: none; }
|
|
@@ -45,6 +49,8 @@
|
|
|
45
49
|
--cell-placeholder-bg: #262626;
|
|
46
50
|
--cell-notice-bg: #3a300f;
|
|
47
51
|
--cell-notice-fg: #f5dc9a;
|
|
52
|
+
--cell-hidden-border: #4a4a4a;
|
|
53
|
+
--cell-hidden-fg: #a3a3a3;
|
|
48
54
|
}
|
|
49
55
|
}
|
|
50
56
|
|
|
@@ -9,6 +9,9 @@ export declare function projectCellData(cell: PageCell, ctx: DataContext): {
|
|
|
9
9
|
status: CellStatus;
|
|
10
10
|
} | null;
|
|
11
11
|
export declare function failedLabels(cell: PageCell, data: DataContext): string[];
|
|
12
|
+
export declare function isEmptyPropValue(value: unknown): boolean;
|
|
13
|
+
export declare function hideWhenEmptyProblem(cell: Pick<PageCell, 'hide_when_empty' | 'props'>, data: DataContext): 'QUERY_UNRESOLVED' | null;
|
|
14
|
+
export declare function hiddenWhenEmpty(cell: Pick<PageCell, 'hide_when_empty' | 'props'>, props: Record<string, unknown>, data: DataContext): boolean;
|
|
12
15
|
export type AssembledInputs = {
|
|
13
16
|
resolvedProps: Record<string, unknown>;
|
|
14
17
|
children: unknown;
|
|
@@ -2,7 +2,8 @@ import type { Diagnostic } from './graph.js';
|
|
|
2
2
|
import type { PageCell } from './types.js';
|
|
3
3
|
export declare const HYDRATED_CELL_FIELDS: readonly ["component", "props", "layout", "conditions", "children", "traversal", "data", "bundle_ref", "actions", "embed"];
|
|
4
4
|
export declare const STORE_FIELDS: readonly ["selections", "viewState", "live"];
|
|
5
|
-
export declare const
|
|
5
|
+
export declare const RENDER_FIELDS: readonly ["hide_when_empty"];
|
|
6
|
+
export declare const CONTENT_FIELDS: readonly [...("component" | "props" | "conditions" | "children" | "traversal" | "data" | "bundle_ref" | "actions" | "embed")[], "selections", "viewState", "live", "hide_when_empty"];
|
|
6
7
|
export type CellSource = 'inline' | 'node' | 'fallback' | 'pending';
|
|
7
8
|
export type ResolvedCell = PageCell & {
|
|
8
9
|
source: CellSource;
|
|
@@ -51,6 +51,10 @@ export declare const DIAGNOSTICS: {
|
|
|
51
51
|
readonly meaning: "A literal prop value fails its schema.";
|
|
52
52
|
readonly fix: "Write the value in the shape `props_schema` gives, or bind it with a whole-string `@label.path` reference.";
|
|
53
53
|
};
|
|
54
|
+
readonly HIDE_WHEN_EMPTY_INVALID: {
|
|
55
|
+
readonly meaning: "A cell’s `hide_when_empty` isn’t a list of prop names.";
|
|
56
|
+
readonly fix: "Write `hide_when_empty: [\"title\", \"items\"]`: the props that, all empty once bound (null, blank text, `[]`, `{}`), leave the cell out.";
|
|
57
|
+
};
|
|
54
58
|
readonly CELL_UNKNOWN_CHILD: {
|
|
55
59
|
readonly meaning: "`children` names a cell that is not on the page.";
|
|
56
60
|
readonly fix: "List only ids of cells on this page in `children`.";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { CONTENT_FIELDS, hasRef, HYDRATED_CELL_FIELDS, normalizeCell, resolveCell, rootCells, STORE_FIELDS, type CellSource, type ResolvedCell } from './cells.js';
|
|
1
|
+
export { CONTENT_FIELDS, hasRef, HYDRATED_CELL_FIELDS, normalizeCell, resolveCell, RENDER_FIELDS, rootCells, STORE_FIELDS, type CellSource, type ResolvedCell } from './cells.js';
|
|
2
2
|
export { buildDataContext, collectDeclarations, collectDescriptors, collectStatic, readField, resolveDeclarations, type ResolvedDeclaration, type StateRead } from './data-context.js';
|
|
3
3
|
export { mergeStates, needsWalk, readWalk, referencedLabels, RESERVED_LABELS, resolveWalkStart, stateNames, stateValue, systemsFromGraphs, systemsFromProcess, walkKey, walkRequest, type Diagnostic, type GraphSystem, type ProcessSystems, type RawWalk, type WalkBinding, type WalkRequest, type WalkStart, } from './graph.js';
|
|
4
4
|
export { LAYER_ROLES, layerName, type LayerType } from './layers.js';
|
|
@@ -14,7 +14,7 @@ export { pageRuntimeFromValues } from './values.js';
|
|
|
14
14
|
export { actionBindingKey, actionProblems, actionReferences, declaredActions, normalizeActionDeclaration, projectCellActions, resolveAction, type RunAction } from './actions.js';
|
|
15
15
|
export type { ActionDeclaration } from '@peripheries/runtime';
|
|
16
16
|
export type * from './types.js';
|
|
17
|
-
export { assembleCellProps, projectCellData, type CellStatus } from './cell-props.js';
|
|
17
|
+
export { assembleCellProps, hiddenWhenEmpty, hideWhenEmptyProblem, isEmptyPropValue, projectCellData, type CellStatus } from './cell-props.js';
|
|
18
18
|
export { cellAttributes, cellPlacementStyle, pageContainerStyle } from './layout.js';
|
|
19
19
|
export { deriveElementReferences, deriveSectionNumbering, deriveSectionReferences, mergeSectionReferences, nodePrintStyle, normalizeStructure, numberingFor, PAGE_DOCUMENT, referencesFor, SECTION_KINDS, sectionAnchor, structureIssues, tableOfContents, usesStructureLayout, type ContentsEntry, type LayoutNode, type SectionNumbering, type SectionReference, type SectionReferences, } from './structure.js';
|
|
20
20
|
export { _clearBundleCache, isComponentExport, loadBundle, loadBundles, loadBundlesSettled, projectModule, runtimeReady, type BundleFiles, type BundleRow, type LoadedBundle, type LoadedBundles, } from './bundles/load.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DeclaredBinding, PageCell, QueryDescriptor } from '@peripheries/runtime';
|
|
1
|
+
import type { DeclaredBinding, PageCell as RuntimePageCell, QueryDescriptor } from '@peripheries/runtime';
|
|
2
2
|
export type DataPage = {
|
|
3
3
|
hasMore: boolean;
|
|
4
4
|
endCursor: string | null;
|
|
@@ -37,7 +37,10 @@ export type DataOrigin = {
|
|
|
37
37
|
kind: 'reserved';
|
|
38
38
|
};
|
|
39
39
|
export type DataContext = Record<string, DataEntry>;
|
|
40
|
-
export type { CellData, CellLayout, DeclaredBinding,
|
|
40
|
+
export type { CellData, CellLayout, DeclaredBinding, ResponsiveBucket } from '@peripheries/runtime';
|
|
41
|
+
export type PageCell = RuntimePageCell & {
|
|
42
|
+
hide_when_empty?: string[];
|
|
43
|
+
};
|
|
41
44
|
export type PageContent = {
|
|
42
45
|
cells?: PageCell[];
|
|
43
46
|
data?: {
|
|
@@ -2,10 +2,13 @@ import { Component, type ReactNode } from 'react';
|
|
|
2
2
|
export declare function CellPlaceholder({ state }: {
|
|
3
3
|
state: 'loading' | 'pending';
|
|
4
4
|
}): import("react").JSX.Element;
|
|
5
|
-
export declare function
|
|
5
|
+
export declare function CellHidden(): import("react").JSX.Element;
|
|
6
|
+
export declare function CellNotice({ state, component, onRetry, code, showCode, children }: {
|
|
6
7
|
state: 'not-found' | 'error';
|
|
7
8
|
component?: string;
|
|
8
9
|
onRetry?: () => void;
|
|
10
|
+
code?: string;
|
|
11
|
+
showCode?: boolean;
|
|
9
12
|
children: ReactNode;
|
|
10
13
|
}): import("react").JSX.Element;
|
|
11
14
|
export declare class CellErrorBoundary extends Component<{
|
|
@@ -38,6 +38,8 @@ export type CellEnvironment = {
|
|
|
38
38
|
onDiagnostic?: (d: Diagnostic) => void;
|
|
39
39
|
frameProps?: (cell: PageCell, contract: ComponentContract | undefined) => FrameProps | undefined;
|
|
40
40
|
cellProps?: (cell: PageCell) => Record<string, unknown> | undefined;
|
|
41
|
+
showHiddenCells?: boolean;
|
|
42
|
+
showProblemCodes?: boolean;
|
|
41
43
|
};
|
|
42
44
|
export type FrameProps = {
|
|
43
45
|
attributes?: Record<string, string>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { CellErrorBoundary, CellNotice, CellPlaceholder } from './cell-states.js';
|
|
1
|
+
export { CellErrorBoundary, CellHidden, CellNotice, CellPlaceholder } from './cell-states.js';
|
|
2
2
|
export { CellView, type CellEnvironment, type FrameProps } from './cell-view.js';
|
|
3
3
|
export { PageRenderer, type PageRendererProps, type RendererEnvironment } from './page-renderer.js';
|
|
4
4
|
export { StructureView, useStructureFacts } from './structure-view.js';
|
|
@@ -23,6 +23,8 @@ export type RendererEnvironment = {
|
|
|
23
23
|
retry?: () => void;
|
|
24
24
|
frameProps?: (cell: PageCell, contract: ComponentContract | undefined) => FrameProps | undefined;
|
|
25
25
|
cellProps?: (cell: PageCell) => Record<string, unknown> | undefined;
|
|
26
|
+
showHiddenCells?: boolean;
|
|
27
|
+
showProblemCodes?: boolean;
|
|
26
28
|
};
|
|
27
29
|
export type PageRendererProps = {
|
|
28
30
|
runtime: PageRuntime;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@peripheries/renderer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.25.0",
|
|
4
4
|
"description": "The one renderer for pages of federated component bundles: a framework-free core, a React renderer, the runtime a page boots with, and golden pages for testing.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": [
|