@pixel-point/toolcraft 0.0.17 → 0.0.18
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 +1 -1
- package/src/generate-test-doc-assertions.mjs +58 -68
- package/src/generate-test-doc-assertions.test.mjs +43 -0
- package/src/generate-test-doc-policy-assertions.mjs +92 -0
- package/src/generate-test-file-assertions.mjs +26 -75
- package/src/generate-test-product-iteration.test.mjs +17 -0
- package/src/generate-test-runtime-boundary-assertions.mjs +158 -0
- package/src/generate-test-standalone-assertions.mjs +10 -0
- package/templates/runtime/contracts/component-contracts.choices.test.ts +47 -3
- package/templates/runtime/contracts/component-contracts.choices.ts +48 -3
- package/templates/runtime/contracts/component-contracts.inputs.test.ts +6 -6
- package/templates/runtime/contracts/component-contracts.inputs.ts +6 -6
- package/templates/runtime/contracts/component-contracts.media-custom.test.ts +8 -2
- package/templates/runtime/contracts/component-contracts.media-custom.ts +8 -2
- package/templates/runtime/contracts/component-contracts.runtime-acceptance.test.ts +4 -4
- package/templates/runtime/contracts/component-contracts.runtime-labels.test.ts +7 -1
- package/templates/runtime/contracts/component-contracts.runtime-panels.test.ts +3 -0
- package/templates/runtime/contracts/component-contracts.runtime-shell.test.ts +3 -0
- package/templates/runtime/contracts/component-contracts.runtime.ts +12 -8
- package/templates/runtime/contracts/component-contracts.test.ts +2 -0
- package/templates/runtime/contracts/decision-contracts.test.ts +18 -0
- package/templates/runtime/contracts/decision-contracts.ts +10 -10
- package/templates/runtime/export/export-background.ts +0 -19
- package/templates/runtime/export/export.test.ts +45 -42
- package/templates/runtime/export/index.ts +44 -16
- package/templates/runtime/index.ts +2 -0
- package/templates/runtime/model-import/model-import-limits.ts +15 -4
- package/templates/runtime/react/app-shell/runtime-public-api.test.tsx +11 -1
- package/templates/runtime/react/app-shell/toolcraft-app.scene-export.test.tsx +29 -0
- package/templates/runtime/react/app-shell/toolcraft-app.tsx +18 -8
- package/templates/runtime/react/canvas/canvas-shell-rendering.test.tsx +76 -0
- package/templates/runtime/react/canvas/canvas-shell-test-utils.tsx +13 -1
- package/templates/runtime/react/canvas/canvas-shell.tsx +16 -1
- package/templates/runtime/react/canvas/product-scene-surface.test.tsx +195 -0
- package/templates/runtime/react/canvas/product-scene-surface.tsx +155 -0
- package/templates/runtime/react/canvas/runtime-scene-export.ts +1 -1
- package/templates/runtime/react/controls-panel/__tests__/control-conditions.test.ts +61 -0
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.collection-actions.test.tsx +89 -0
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.control-visibility.test.tsx +40 -19
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.scene-export-actions.test.tsx +1 -1
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.source-collection.test.tsx +62 -0
- package/templates/runtime/react/controls-panel/__tests__/controls-panel.source-color-bank.test.tsx +67 -0
- package/templates/runtime/react/controls-panel/actions/controls-panel-actions.ts +2 -4
- package/templates/runtime/react/controls-panel/actions/export-action-runner.ts +10 -4
- package/templates/runtime/react/controls-panel/conditions/control-conditions.ts +32 -119
- package/templates/runtime/react/controls-panel/layout/controls-panel-control-group.tsx +5 -2
- package/templates/runtime/react/controls-panel/layout/controls-panel-layout.tsx +10 -8
- package/templates/runtime/react/controls-panel/layout/controls-panel-section.tsx +3 -3
- package/templates/runtime/react/controls-panel/renderers/controls-panel-collection-item-fields.test.tsx +63 -0
- package/templates/runtime/react/controls-panel/renderers/controls-panel-collection-item-fields.tsx +138 -0
- package/templates/runtime/react/controls-panel/renderers/controls-panel-collection-items.tsx +104 -0
- package/templates/runtime/react/controls-panel/renderers/controls-panel-collection-renderer.tsx +115 -236
- package/templates/runtime/react/controls-panel/renderers/controls-panel-file-drop-item-controls.test.tsx +95 -0
- package/templates/runtime/react/controls-panel/renderers/controls-panel-file-drop-item-controls.tsx +82 -0
- package/templates/runtime/react/controls-panel/renderers/controls-panel-media-renderer.test.tsx +73 -1
- package/templates/runtime/react/controls-panel/renderers/controls-panel-media-renderer.tsx +50 -1
- package/templates/runtime/react/controls-panel/renderers/controls-panel-renderer-registry.test.ts +1 -1
- package/templates/runtime/react/controls-panel/renderers/controls-panel-renderer-registry.ts +2 -1
- package/templates/runtime/react/controls-panel/values/controls-panel-collection-values.ts +38 -6
- package/templates/runtime/react/controls-panel/values/controls-panel-value-labels.ts +1 -0
- package/templates/runtime/react/index.ts +4 -0
- package/templates/runtime/react/model-rendering/model-export.ts +1 -1
- package/templates/runtime/react/orientation-gizmo/orientation-gizmo-selection.ts +5 -5
- package/templates/runtime/react/orientation-gizmo/use-toolcraft-orientation-control-selection.ts +2 -2
- package/templates/runtime/scene/scene-bounds.test.ts +23 -0
- package/templates/runtime/scene/scene-bounds.ts +31 -17
- package/templates/runtime/schema/app-capabilities.test.ts +4 -0
- package/templates/runtime/schema/collection-item-controls.ts +29 -0
- package/templates/runtime/schema/control-applicability.test.ts +127 -0
- package/templates/runtime/schema/control-applicability.ts +433 -0
- package/templates/runtime/schema/control-schema-normalization.ts +177 -11
- package/templates/runtime/schema/controls-panel-actions.ts +2 -2
- package/templates/runtime/schema/controls-panel-section-layout.ts +14 -9
- package/templates/runtime/schema/controls-panel-section-roundtrip.test.ts +59 -0
- package/templates/runtime/schema/define-toolcraft.collection-actions-item-controls.test.ts +127 -0
- package/templates/runtime/schema/define-toolcraft.control-sections.test.ts +10 -4
- package/templates/runtime/schema/define-toolcraft.file-drop-item-controls.test.ts +98 -0
- package/templates/runtime/schema/define-toolcraft.file-drop-normalization.test.ts +27 -0
- package/templates/runtime/schema/define-toolcraft.segmented-control-fit.test.ts +20 -2
- package/templates/runtime/schema/define-toolcraft.setup-canvas.test.ts +16 -3
- package/templates/runtime/schema/define-toolcraft.source-collection.test.ts +79 -0
- package/templates/runtime/schema/runtime-setup-section.ts +24 -5
- package/templates/runtime/schema/types.ts +68 -13
- package/templates/starter/AGENTS.md +12 -10
- package/templates/starter/docs/toolcraft/acceptance-testing.md +17 -31
- package/templates/starter/docs/toolcraft/agent-worklog.md +15 -0
- package/templates/starter/docs/toolcraft/assembly-workflow.md +10 -3
- package/templates/starter/docs/toolcraft/component-rules.md +8 -6
- package/templates/starter/docs/toolcraft/core/control-selection.md +9 -3
- package/templates/starter/docs/toolcraft/core/layout.md +7 -7
- package/templates/starter/docs/toolcraft/core/media-upload.md +2 -0
- package/templates/starter/docs/toolcraft/core/runtime-boundary.md +13 -2
- package/templates/starter/docs/toolcraft/core/setup-export.md +33 -7
- package/templates/starter/docs/toolcraft/custom-controls.md +6 -4
- package/templates/starter/docs/toolcraft/decision-contract.md +3 -1
- package/templates/starter/docs/toolcraft/renderer-technique.md +4 -0
- package/templates/starter/docs/toolcraft/schema-reference.md +21 -19
- package/templates/starter/e2e/app-browser-control-applicability-requirements.spec.ts +163 -0
- package/templates/starter/e2e/app-browser-infinity-canvas-evidence.spec.ts +7 -2
- package/templates/starter/e2e/app-browser-orientation-evidence.spec.ts +174 -21
- package/templates/starter/e2e/app-browser-runtime-requirements.spec.ts +1 -33
- package/templates/starter/e2e/app-browser-semantic-evidence.spec.ts +61 -15
- package/templates/starter/e2e/{browser-conditional-output-evidence-helpers.ts → browser-background-output-evidence.ts} +0 -68
- package/templates/starter/e2e/browser-control-applicability-evidence.ts +114 -0
- package/templates/starter/e2e/browser-infinity-canvas-evidence.ts +20 -11
- package/templates/starter/e2e/browser-orientation-gizmo-evidence-helpers.ts +4 -0
- package/templates/starter/e2e/browser-orientation-gizmo-live-preconditions.ts +86 -0
- package/templates/starter/e2e/browser-runtime-applicability-requirements.ts +84 -0
- package/templates/starter/e2e/browser-runtime-evidence-reporter.ts +9 -2
- package/templates/starter/e2e/browser-runtime-evidence-requirements.ts +29 -26
- package/templates/starter/e2e/browser-semantic-evidence-test-helpers.ts +25 -0
- package/templates/starter/scripts/toolcraft-contract-manifest.json +1 -0
- package/templates/starter/scripts/toolcraft-product-boundary-ast.mjs +85 -115
- package/templates/starter/scripts/toolcraft-product-boundary-module-policy.mjs +101 -0
- package/templates/starter/scripts/toolcraft-product-boundary.test.mjs +0 -72
- package/templates/starter/scripts/toolcraft-product-control-bindings.mjs +182 -0
- package/templates/starter/scripts/toolcraft-product-control-boundary.mjs +146 -0
- package/templates/starter/scripts/toolcraft-product-control-boundary.test.mjs +186 -0
- package/templates/starter/scripts/toolcraft-product-export-boundary.mjs +6 -17
- package/templates/starter/scripts/toolcraft-product-export-boundary.test.mjs +196 -0
- package/templates/starter/scripts/toolcraft-typescript-source-evidence-structure.test.mjs +20 -0
- package/templates/starter/scripts/toolcraft-typescript-source-evidence.mjs +1 -0
- package/templates/starter/scripts/toolcraft-unavailable-resource-evidence-facade.test.mjs +2 -0
- package/templates/starter/src/app/acceptance/artifact-export-intent.ts +130 -0
- package/templates/starter/src/app/acceptance/conditions.ts +1 -1
- package/templates/starter/src/app/acceptance/control-acceptance-context.ts +2 -2
- package/templates/starter/src/app/acceptance/control-acceptance-coverage.ts +6 -20
- package/templates/starter/src/app/acceptance/control-acceptance-kind-rules.ts +24 -5
- package/templates/starter/src/app/acceptance/control-acceptance-policy.ts +2 -13
- package/templates/starter/src/app/acceptance/control-applicability-cases.test.ts +321 -0
- package/templates/starter/src/app/acceptance/control-applicability-cases.ts +290 -0
- package/templates/starter/src/app/acceptance/control-applicability.ts +332 -0
- package/templates/starter/src/app/acceptance/control-layout-dependency-rules.ts +20 -9
- package/templates/starter/src/app/acceptance/control-layout-entity-rules.ts +4 -6
- package/templates/starter/src/app/acceptance/control-layout-model.ts +4 -4
- package/templates/starter/src/app/acceptance/control-layout-section-rules.ts +3 -10
- package/templates/starter/src/app/acceptance/control-parts.ts +8 -0
- package/templates/starter/src/app/acceptance/control-section-entity-cohesion.test.ts +215 -0
- package/templates/starter/src/app/acceptance/control-section-entity-cohesion.ts +132 -0
- package/templates/starter/src/app/acceptance/control-section-inventory.test.ts +87 -0
- package/templates/starter/src/app/acceptance/control-section-inventory.ts +74 -44
- package/templates/starter/src/app/acceptance/custom-controls.ts +11 -3
- package/templates/starter/src/app/acceptance/inline-layout.ts +64 -9
- package/templates/starter/src/app/acceptance/media-upload.ts +5 -1
- package/templates/starter/src/app/acceptance/orientation-gizmo.ts +18 -11
- package/templates/starter/src/app/acceptance/output-background-rules.ts +2 -2
- package/templates/starter/src/app/acceptance/output-export-layout-rules.ts +18 -10
- package/templates/starter/src/app/acceptance/output-export-model.ts +54 -11
- package/templates/starter/src/app/acceptance/output-export.ts +22 -10
- package/templates/starter/src/app/acceptance/output-video-export-rules.ts +99 -0
- package/templates/starter/src/app/acceptance/types.ts +23 -8
- package/templates/starter/src/app/acceptance/validate-coverage.ts +19 -2
- package/templates/starter/src/app/starter-acceptance.artifact-export-intent.test.ts +239 -0
- package/templates/starter/src/app/starter-acceptance.background-export.test.ts +33 -0
- package/templates/starter/src/app/starter-acceptance.canvas-sizing.test.ts +16 -1
- package/templates/starter/src/app/starter-acceptance.compound-part-coverage.test.ts +65 -0
- package/templates/starter/src/app/starter-acceptance.contract-fixtures.test.ts +41 -0
- package/templates/starter/src/app/starter-acceptance.contract-fixtures.ts +112 -16
- package/templates/starter/src/app/starter-acceptance.control-applicability.test.ts +224 -0
- package/templates/starter/src/app/starter-acceptance.control-order.test.ts +58 -3
- package/templates/starter/src/app/starter-acceptance.control-state.test.ts +0 -111
- package/templates/starter/src/app/starter-acceptance.custom-control-collections.test.ts +3 -1
- package/templates/starter/src/app/starter-acceptance.custom-control-valid.test.ts +1 -1
- package/templates/starter/src/app/starter-acceptance.export-test-utils.ts +22 -0
- package/templates/starter/src/app/{starter-acceptance.image-video-export-settings.test.ts → starter-acceptance.image-export-settings.test.ts} +37 -9
- package/templates/starter/src/app/starter-acceptance.interaction-ownership.test.ts +12 -0
- package/templates/starter/src/app/starter-acceptance.media-upload.fixtures.ts +11 -1
- package/templates/starter/src/app/starter-acceptance.media-upload.test.ts +28 -0
- package/templates/starter/src/app/starter-acceptance.output-export-intent.test.ts +384 -0
- package/templates/starter/src/app/starter-acceptance.output-export-rules.test.ts +4 -0
- package/templates/starter/src/app/starter-acceptance.product-output-export.test.ts +112 -105
- package/templates/starter/src/app/starter-acceptance.product-readiness.test.ts +12 -0
- package/templates/starter/src/app/starter-acceptance.section-cohesion.test.ts +1 -1
- package/templates/starter/src/app/starter-acceptance.section-dependencies.test.ts +35 -24
- package/templates/starter/src/app/starter-acceptance.section-size.test.ts +72 -0
- package/templates/starter/src/app/starter-acceptance.ts +11 -2
- package/templates/starter/src/app/starter-acceptance.video-export-settings.test.ts +336 -0
- package/templates/starter/src/app/starter-acceptance.view-interaction.test.ts +8 -0
- package/templates/starter/src/app/test-evidence/browser-runtime-contract.ts +2 -2
- package/templates/starter/src/routes/index.tsx +1 -0
- package/templates/ui/components/composites/tabs.tsx +2 -2
- package/templates/ui/components/controls/collection-actions/collection-item-groups.test.tsx +38 -0
- package/templates/ui/components/controls/collection-actions/collection-item-groups.tsx +36 -0
- package/templates/ui/components/controls/collection-actions/index.ts +2 -0
- package/templates/ui/components/controls/file-drop/file-drop-collection-control.tsx +137 -0
- package/templates/ui/components/controls/file-drop/file-drop-control.test.tsx +189 -0
- package/templates/ui/components/controls/file-drop/file-drop-control.tsx +81 -88
- package/templates/ui/components/controls/file-drop/file-drop-presentation.tsx +131 -9
- package/templates/ui/components/controls/file-drop/file-drop-types.ts +36 -22
- package/templates/ui/components/controls/file-drop/index.ts +1 -0
- package/templates/ui/components/controls/index.ts +3 -0
- package/templates/ui/components/controls/range-slider/range-slider-control.tsx +1 -1
- package/templates/ui/components/controls/range-slider/range-slider-value.test.ts +14 -0
- package/templates/ui/components/controls/range-slider/range-slider-value.ts +3 -2
- package/templates/starter/src/app/acceptance/condition-exclusivity.test.ts +0 -60
- package/templates/starter/src/app/acceptance/condition-exclusivity.ts +0 -221
package/templates/runtime/react/controls-panel/__tests__/controls-panel.control-visibility.test.tsx
CHANGED
|
@@ -136,7 +136,7 @@ describe("ControlsPanel conditional control visibility", () => {
|
|
|
136
136
|
).toHaveLength(0);
|
|
137
137
|
});
|
|
138
138
|
|
|
139
|
-
it("renders only controls
|
|
139
|
+
it("renders only applicable controls and preserves hidden values", async () => {
|
|
140
140
|
const schema = defineToolcraft({
|
|
141
141
|
canvas: { enabled: true },
|
|
142
142
|
panels: {
|
|
@@ -145,6 +145,7 @@ describe("ControlsPanel conditional control visibility", () => {
|
|
|
145
145
|
{
|
|
146
146
|
controls: {
|
|
147
147
|
identityMode: {
|
|
148
|
+
applicability: { mode: "always" },
|
|
148
149
|
defaultValue: "text",
|
|
149
150
|
label: "Identity",
|
|
150
151
|
options: [
|
|
@@ -155,24 +156,30 @@ describe("ControlsPanel conditional control visibility", () => {
|
|
|
155
156
|
type: "segmented",
|
|
156
157
|
},
|
|
157
158
|
partnerName: {
|
|
159
|
+
applicability: {
|
|
160
|
+
all: [
|
|
161
|
+
{ equals: "text", target: "coBrand.identityMode" },
|
|
162
|
+
{ equals: true, target: "coBrand.partnerEnabled" },
|
|
163
|
+
],
|
|
164
|
+
mode: "conditional",
|
|
165
|
+
},
|
|
158
166
|
defaultValue: "tRPC",
|
|
159
167
|
label: "Partner",
|
|
160
168
|
target: "coBrand.partnerName",
|
|
161
169
|
type: "text",
|
|
162
|
-
visibleWhen: {
|
|
163
|
-
equals: "text",
|
|
164
|
-
target: "coBrand.identityMode",
|
|
165
|
-
},
|
|
166
170
|
},
|
|
167
171
|
partnerLogo: {
|
|
172
|
+
applicability: {
|
|
173
|
+
all: [
|
|
174
|
+
{ equals: "logo", target: "coBrand.identityMode" },
|
|
175
|
+
{ equals: true, target: "coBrand.partnerEnabled" },
|
|
176
|
+
],
|
|
177
|
+
mode: "conditional",
|
|
178
|
+
},
|
|
168
179
|
defaultValue: "logo.svg",
|
|
169
180
|
label: "Partner logo",
|
|
170
181
|
target: "coBrand.partnerLogo",
|
|
171
182
|
type: "text",
|
|
172
|
-
visibleWhen: {
|
|
173
|
-
equals: "logo",
|
|
174
|
-
target: "coBrand.identityMode",
|
|
175
|
-
},
|
|
176
183
|
},
|
|
177
184
|
},
|
|
178
185
|
title: "Partner Lockup",
|
|
@@ -183,7 +190,9 @@ describe("ControlsPanel conditional control visibility", () => {
|
|
|
183
190
|
},
|
|
184
191
|
});
|
|
185
192
|
|
|
186
|
-
renderControlsPanelWithSchema(schema
|
|
193
|
+
renderControlsPanelWithSchema(schema, {}, {
|
|
194
|
+
values: { "coBrand.partnerEnabled": true },
|
|
195
|
+
});
|
|
187
196
|
|
|
188
197
|
const partnerInput = screen.getByDisplayValue("tRPC");
|
|
189
198
|
|
|
@@ -208,7 +217,7 @@ describe("ControlsPanel conditional control visibility", () => {
|
|
|
208
217
|
});
|
|
209
218
|
});
|
|
210
219
|
|
|
211
|
-
it("renders count-dependent controls through numeric
|
|
220
|
+
it("renders count-dependent controls through numeric applicability", () => {
|
|
212
221
|
const schema = defineToolcraft({
|
|
213
222
|
canvas: { enabled: true },
|
|
214
223
|
panels: {
|
|
@@ -217,6 +226,7 @@ describe("ControlsPanel conditional control visibility", () => {
|
|
|
217
226
|
{
|
|
218
227
|
controls: {
|
|
219
228
|
shadeCount: {
|
|
229
|
+
applicability: { mode: "always" },
|
|
220
230
|
defaultValue: 2,
|
|
221
231
|
label: "Shades",
|
|
222
232
|
max: 5,
|
|
@@ -227,30 +237,41 @@ describe("ControlsPanel conditional control visibility", () => {
|
|
|
227
237
|
variant: "discrete",
|
|
228
238
|
},
|
|
229
239
|
shade1: {
|
|
240
|
+
applicability: { mode: "always" },
|
|
230
241
|
defaultValue: { hex: "#FFFFFF" },
|
|
231
242
|
label: "Shade 1",
|
|
232
243
|
target: "shapes.color1",
|
|
233
244
|
type: "color",
|
|
234
245
|
},
|
|
235
246
|
shade2: {
|
|
247
|
+
applicability: {
|
|
248
|
+
all: [
|
|
249
|
+
{
|
|
250
|
+
greaterThanOrEqual: 2,
|
|
251
|
+
target: "shapes.shadeCount",
|
|
252
|
+
},
|
|
253
|
+
],
|
|
254
|
+
mode: "conditional",
|
|
255
|
+
},
|
|
236
256
|
defaultValue: { hex: "#DDDDDD" },
|
|
237
257
|
label: "Shade 2",
|
|
238
258
|
target: "shapes.color2",
|
|
239
259
|
type: "color",
|
|
240
|
-
visibleWhen: {
|
|
241
|
-
greaterThanOrEqual: 2,
|
|
242
|
-
target: "shapes.shadeCount",
|
|
243
|
-
},
|
|
244
260
|
},
|
|
245
261
|
shade3: {
|
|
262
|
+
applicability: {
|
|
263
|
+
all: [
|
|
264
|
+
{
|
|
265
|
+
greaterThanOrEqual: 3,
|
|
266
|
+
target: "shapes.shadeCount",
|
|
267
|
+
},
|
|
268
|
+
],
|
|
269
|
+
mode: "conditional",
|
|
270
|
+
},
|
|
246
271
|
defaultValue: { hex: "#BBBBBB" },
|
|
247
272
|
label: "Shade 3",
|
|
248
273
|
target: "shapes.color3",
|
|
249
274
|
type: "color",
|
|
250
|
-
visibleWhen: {
|
|
251
|
-
greaterThanOrEqual: 3,
|
|
252
|
-
target: "shapes.shadeCount",
|
|
253
|
-
},
|
|
254
275
|
},
|
|
255
276
|
},
|
|
256
277
|
title: "Shapes",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ToolcraftSceneExportError } from "../../../export";
|
|
1
|
+
import { ToolcraftSceneExportError } from "../../../export/export-frame";
|
|
2
2
|
import { defaultToolcraftRuntimeSceneVisibility } from "../../../scene";
|
|
3
3
|
import type { ToolcraftActionSchema } from "../../../schema/types";
|
|
4
4
|
import {
|
package/templates/runtime/react/controls-panel/__tests__/controls-panel.source-collection.test.tsx
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
defineToolcraft,
|
|
4
|
+
fireEvent,
|
|
5
|
+
renderControlsPanelWithSchema,
|
|
6
|
+
screen,
|
|
7
|
+
} from "../testing/controls-panel-test-utils";
|
|
8
|
+
|
|
9
|
+
describe("ControlsPanel source collection", () => {
|
|
10
|
+
it("renders source-sized built-in color controls without cardinality actions", () => {
|
|
11
|
+
const schema = defineToolcraft({
|
|
12
|
+
canvas: { enabled: false },
|
|
13
|
+
panels: {
|
|
14
|
+
controls: {
|
|
15
|
+
sections: [
|
|
16
|
+
{
|
|
17
|
+
controls: {
|
|
18
|
+
sections: {
|
|
19
|
+
defaultValue: [{ hex: "#DFFF1A" }, { hex: "#8CFF3A" }],
|
|
20
|
+
itemControl: {
|
|
21
|
+
label: "Section",
|
|
22
|
+
type: "color",
|
|
23
|
+
},
|
|
24
|
+
label: "Sections",
|
|
25
|
+
target: "fills.sections",
|
|
26
|
+
type: "sourceCollection",
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
title: "Fill Sections",
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
title: "Controls",
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const { container } = renderControlsPanelWithSchema(schema);
|
|
38
|
+
|
|
39
|
+
expect(screen.queryByRole("button", { name: /Add/u })).toBeNull();
|
|
40
|
+
expect(screen.queryByRole("button", { name: /Remove/u })).toBeNull();
|
|
41
|
+
expect(screen.getByRole("button", { name: "Pick Section 1" })).toBeTruthy();
|
|
42
|
+
expect(screen.getByRole("textbox", { name: "Section 2 hex" })).toBeTruthy();
|
|
43
|
+
expect(
|
|
44
|
+
container.querySelector('[data-slot="source-collection-items-grid"]')
|
|
45
|
+
?.children,
|
|
46
|
+
).toHaveLength(2);
|
|
47
|
+
|
|
48
|
+
const secondColor = screen.getByRole("textbox", { name: "Section 2 hex" });
|
|
49
|
+
|
|
50
|
+
fireEvent.change(secondColor, { target: { value: "#123456" } });
|
|
51
|
+
fireEvent.blur(secondColor);
|
|
52
|
+
|
|
53
|
+
const values = JSON.parse(
|
|
54
|
+
screen.getByTestId("values-json").textContent ?? "{}",
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
expect(values["fills.sections"]).toEqual([
|
|
58
|
+
{ hex: "#DFFF1A" },
|
|
59
|
+
{ hex: "#123456" },
|
|
60
|
+
]);
|
|
61
|
+
});
|
|
62
|
+
});
|
package/templates/runtime/react/controls-panel/__tests__/controls-panel.source-color-bank.test.tsx
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
defineToolcraft,
|
|
4
|
+
fireEvent,
|
|
5
|
+
renderControlsPanelWithSchema,
|
|
6
|
+
screen,
|
|
7
|
+
} from "../testing/controls-panel-test-utils";
|
|
8
|
+
|
|
9
|
+
const sourceColors = Array.from({ length: 19 }, (_, index) => ({
|
|
10
|
+
hex: index === 18 ? "#654321" : "#DFFF1A",
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
describe("ControlsPanel source color bank", () => {
|
|
14
|
+
it("keeps a source-sized badge palette on built-in color controls", () => {
|
|
15
|
+
const schema = defineToolcraft({
|
|
16
|
+
canvas: { enabled: false },
|
|
17
|
+
panels: {
|
|
18
|
+
controls: {
|
|
19
|
+
sections: [
|
|
20
|
+
{
|
|
21
|
+
controls: {
|
|
22
|
+
sections: {
|
|
23
|
+
defaultValue: sourceColors,
|
|
24
|
+
itemControl: {
|
|
25
|
+
label: "Section",
|
|
26
|
+
type: "color",
|
|
27
|
+
},
|
|
28
|
+
label: "Sections",
|
|
29
|
+
target: "fills.sections",
|
|
30
|
+
type: "sourceCollection",
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
title: "Fill Sections",
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
title: "Controls",
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const { container } = renderControlsPanelWithSchema(schema);
|
|
42
|
+
|
|
43
|
+
expect(
|
|
44
|
+
screen.getAllByRole("button", { name: /^Pick Section \d+$/u }),
|
|
45
|
+
).toHaveLength(19);
|
|
46
|
+
expect(screen.getByRole("button", { name: "Pick Section 19" })).toBeTruthy();
|
|
47
|
+
expect(screen.queryByRole("button", { name: "Pick Section 20" })).toBeNull();
|
|
48
|
+
expect(screen.queryByRole("button", { name: /Add/u })).toBeNull();
|
|
49
|
+
expect(screen.queryByRole("button", { name: /Remove/u })).toBeNull();
|
|
50
|
+
expect(
|
|
51
|
+
container.querySelector('[data-slot="source-collection-items-grid"]')
|
|
52
|
+
?.children,
|
|
53
|
+
).toHaveLength(19);
|
|
54
|
+
|
|
55
|
+
const lastColor = screen.getByRole("textbox", { name: "Section 19 hex" });
|
|
56
|
+
fireEvent.change(lastColor, { target: { value: "#123456" } });
|
|
57
|
+
fireEvent.blur(lastColor);
|
|
58
|
+
|
|
59
|
+
const values = JSON.parse(
|
|
60
|
+
screen.getByTestId("values-json").textContent ?? "{}",
|
|
61
|
+
);
|
|
62
|
+
expect(values["fills.sections"]).toEqual([
|
|
63
|
+
...sourceColors.slice(0, 18),
|
|
64
|
+
{ hex: "#123456" },
|
|
65
|
+
]);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
@@ -2,10 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
ToolcraftSceneExportError,
|
|
8
|
-
} from "../../../export";
|
|
5
|
+
import { ToolcraftArtifactExportError } from "../../../export/export-error";
|
|
6
|
+
import { ToolcraftSceneExportError } from "../../../export/export-frame";
|
|
9
7
|
import type { ToolcraftRendererPipelineClient } from "../../../rendering";
|
|
10
8
|
import { defaultToolcraftRuntimeSceneVisibility } from "../../../scene";
|
|
11
9
|
import type {
|
|
@@ -1,10 +1,16 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ToolcraftArtifactExportRequest,
|
|
3
|
+
} from "../../../export/artifact-export-request";
|
|
4
|
+
import type { ToolcraftExportFrame } from "../../../export/export-frame";
|
|
1
5
|
import {
|
|
2
6
|
exportToolcraftImageArtifact,
|
|
7
|
+
} from "../../../export/image-artifact-export";
|
|
8
|
+
import type {
|
|
9
|
+
ToolcraftProductExportRenderer,
|
|
10
|
+
} from "../../../export/product-export-renderer";
|
|
11
|
+
import {
|
|
3
12
|
exportToolcraftVideoArtifact,
|
|
4
|
-
|
|
5
|
-
type ToolcraftExportFrame,
|
|
6
|
-
type ToolcraftProductExportRenderer,
|
|
7
|
-
} from "../../../export";
|
|
13
|
+
} from "../../../export/video-artifact-export";
|
|
8
14
|
import type { ToolcraftRendererPipelineClient } from "../../../rendering";
|
|
9
15
|
import type {
|
|
10
16
|
ToolcraftProductSceneBoundsProvider,
|
|
@@ -3,67 +3,21 @@ import {
|
|
|
3
3
|
isToolcraftTimelinePanelExtendedTarget,
|
|
4
4
|
isToolcraftTimelinePanelVisibleTarget,
|
|
5
5
|
} from "../../../schema/runtime-targets";
|
|
6
|
+
import {
|
|
7
|
+
doesToolcraftApplicabilityMatch,
|
|
8
|
+
doesToolcraftPredicateMatchValue,
|
|
9
|
+
getToolcraftApplicabilityTargets,
|
|
10
|
+
} from "../../../schema/control-applicability";
|
|
6
11
|
import type {
|
|
7
12
|
ToolcraftControlConditionSchema,
|
|
8
|
-
|
|
9
|
-
|
|
13
|
+
ResolvedToolcraftControlSchema,
|
|
14
|
+
ResolvedToolcraftControlSectionSchema,
|
|
10
15
|
} from "../../../schema/types";
|
|
11
16
|
import type { ToolcraftState } from "../../../state/types";
|
|
12
17
|
import { readToolcraftCanvasRuntimeTarget } from "../../../state/canvas-frame";
|
|
13
18
|
|
|
14
|
-
function valuesEqual(first: unknown, second: unknown): boolean {
|
|
15
|
-
if (Object.is(first, second)) {
|
|
16
|
-
return true;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
if (
|
|
20
|
-
(typeof first !== "object" || first === null) &&
|
|
21
|
-
(typeof second !== "object" || second === null)
|
|
22
|
-
) {
|
|
23
|
-
return false;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
try {
|
|
27
|
-
return JSON.stringify(first) === JSON.stringify(second);
|
|
28
|
-
} catch {
|
|
29
|
-
return false;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function valuesInclude(value: unknown, options: readonly unknown[]): boolean {
|
|
34
|
-
return options.some((option) => valuesEqual(value, option));
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function readComparableNumber(value: unknown): number | null {
|
|
38
|
-
if (typeof value === "number" && Number.isFinite(value)) {
|
|
39
|
-
return value;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (typeof value === "string") {
|
|
43
|
-
const numberValue = Number(value.trim());
|
|
44
|
-
|
|
45
|
-
return Number.isFinite(numberValue) ? numberValue : null;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return null;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function compareConditionNumbers(
|
|
52
|
-
value: unknown,
|
|
53
|
-
expected: number | undefined,
|
|
54
|
-
comparator: (value: number, expected: number) => boolean,
|
|
55
|
-
): boolean | null {
|
|
56
|
-
if (typeof expected !== "number" || !Number.isFinite(expected)) {
|
|
57
|
-
return null;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const numberValue = readComparableNumber(value);
|
|
61
|
-
|
|
62
|
-
return numberValue === null ? false : comparator(numberValue, expected);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
19
|
function getControlDefaultValueByTarget(
|
|
66
|
-
sections: readonly
|
|
20
|
+
sections: readonly ResolvedToolcraftControlSectionSchema[],
|
|
67
21
|
target: string,
|
|
68
22
|
): unknown {
|
|
69
23
|
for (const section of sections) {
|
|
@@ -119,22 +73,28 @@ export function getToolcraftConditionTargets(
|
|
|
119
73
|
}
|
|
120
74
|
|
|
121
75
|
export function getToolcraftControlConditionTargets(
|
|
122
|
-
control:
|
|
76
|
+
control: ResolvedToolcraftControlSchema,
|
|
123
77
|
): string[] {
|
|
124
|
-
return
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
78
|
+
return [
|
|
79
|
+
...new Set([
|
|
80
|
+
...getToolcraftApplicabilityTargets(
|
|
81
|
+
control.applicability,
|
|
82
|
+
),
|
|
83
|
+
...getToolcraftConditionTargets(control.disabledWhen),
|
|
84
|
+
]),
|
|
85
|
+
];
|
|
128
86
|
}
|
|
129
87
|
|
|
130
88
|
export function getToolcraftControlVisibilityTargets(
|
|
131
|
-
control:
|
|
89
|
+
control: ResolvedToolcraftControlSchema,
|
|
132
90
|
): string[] {
|
|
133
|
-
return
|
|
91
|
+
return getToolcraftApplicabilityTargets(
|
|
92
|
+
control.applicability,
|
|
93
|
+
);
|
|
134
94
|
}
|
|
135
95
|
|
|
136
96
|
export function getToolcraftSectionVisibilityTargets(
|
|
137
|
-
section:
|
|
97
|
+
section: ResolvedToolcraftControlSectionSchema,
|
|
138
98
|
): string[] {
|
|
139
99
|
return getToolcraftConditionTargets(section.visibleWhen);
|
|
140
100
|
}
|
|
@@ -143,63 +103,15 @@ export function toolcraftConditionMatches(
|
|
|
143
103
|
state: ToolcraftState,
|
|
144
104
|
condition: ToolcraftControlConditionSchema,
|
|
145
105
|
): boolean {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
if ("equals" in condition) {
|
|
150
|
-
matches.push(valuesEqual(value, condition.equals));
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
if ("notEquals" in condition) {
|
|
154
|
-
matches.push(!valuesEqual(value, condition.notEquals));
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
if (Array.isArray(condition.oneOf)) {
|
|
158
|
-
matches.push(valuesInclude(value, condition.oneOf));
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
if (Array.isArray(condition.notOneOf)) {
|
|
162
|
-
matches.push(!valuesInclude(value, condition.notOneOf));
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
const greaterThan = compareConditionNumbers(
|
|
166
|
-
value,
|
|
167
|
-
condition.greaterThan,
|
|
168
|
-
(numberValue, expected) => numberValue > expected,
|
|
169
|
-
);
|
|
170
|
-
const greaterThanOrEqual = compareConditionNumbers(
|
|
171
|
-
value,
|
|
172
|
-
condition.greaterThanOrEqual,
|
|
173
|
-
(numberValue, expected) => numberValue >= expected,
|
|
106
|
+
return doesToolcraftPredicateMatchValue(
|
|
107
|
+
condition,
|
|
108
|
+
getToolcraftTargetValue(state, condition.target),
|
|
174
109
|
);
|
|
175
|
-
const lessThan = compareConditionNumbers(
|
|
176
|
-
value,
|
|
177
|
-
condition.lessThan,
|
|
178
|
-
(numberValue, expected) => numberValue < expected,
|
|
179
|
-
);
|
|
180
|
-
const lessThanOrEqual = compareConditionNumbers(
|
|
181
|
-
value,
|
|
182
|
-
condition.lessThanOrEqual,
|
|
183
|
-
(numberValue, expected) => numberValue <= expected,
|
|
184
|
-
);
|
|
185
|
-
|
|
186
|
-
for (const match of [
|
|
187
|
-
greaterThan,
|
|
188
|
-
greaterThanOrEqual,
|
|
189
|
-
lessThan,
|
|
190
|
-
lessThanOrEqual,
|
|
191
|
-
]) {
|
|
192
|
-
if (match !== null) {
|
|
193
|
-
matches.push(match);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
return matches.length > 0 && matches.every(Boolean);
|
|
198
110
|
}
|
|
199
111
|
|
|
200
112
|
export function isToolcraftSectionVisible(
|
|
201
113
|
state: ToolcraftState,
|
|
202
|
-
section:
|
|
114
|
+
section: ResolvedToolcraftControlSectionSchema,
|
|
203
115
|
): boolean {
|
|
204
116
|
return section.visibleWhen
|
|
205
117
|
? toolcraftConditionMatches(state, section.visibleWhen)
|
|
@@ -208,16 +120,17 @@ export function isToolcraftSectionVisible(
|
|
|
208
120
|
|
|
209
121
|
export function isToolcraftControlVisible(
|
|
210
122
|
state: ToolcraftState,
|
|
211
|
-
control:
|
|
123
|
+
control: ResolvedToolcraftControlSchema,
|
|
212
124
|
): boolean {
|
|
213
|
-
return
|
|
214
|
-
|
|
215
|
-
|
|
125
|
+
return doesToolcraftApplicabilityMatch(
|
|
126
|
+
control.applicability,
|
|
127
|
+
(target) => getToolcraftTargetValue(state, target),
|
|
128
|
+
);
|
|
216
129
|
}
|
|
217
130
|
|
|
218
131
|
export function isToolcraftControlDisabled(
|
|
219
132
|
state: ToolcraftState,
|
|
220
|
-
control:
|
|
133
|
+
control: ResolvedToolcraftControlSchema,
|
|
221
134
|
): boolean {
|
|
222
135
|
if (control.disabled) {
|
|
223
136
|
return true;
|
|
@@ -29,7 +29,7 @@ import { LiveCustomControl } from "../live-custom-control";
|
|
|
29
29
|
import type { ActionControlRunAction } from "../renderers/controls-panel-action-renderer";
|
|
30
30
|
import { renderActionControl } from "../renderers/controls-panel-action-renderer";
|
|
31
31
|
import { renderBasicControl } from "../renderers/controls-panel-basic-renderers";
|
|
32
|
-
import {
|
|
32
|
+
import { renderCollectionControl } from "../renderers/controls-panel-collection-renderer";
|
|
33
33
|
import {
|
|
34
34
|
renderCompoundColorGroup,
|
|
35
35
|
renderCompoundControl,
|
|
@@ -419,7 +419,7 @@ export const ControlsPanelControlGroup = React.memo(
|
|
|
419
419
|
node = null;
|
|
420
420
|
break;
|
|
421
421
|
case "collection":
|
|
422
|
-
node =
|
|
422
|
+
node = renderCollectionControl({
|
|
423
423
|
control,
|
|
424
424
|
name,
|
|
425
425
|
setControlValue,
|
|
@@ -448,7 +448,10 @@ export const ControlsPanelControlGroup = React.memo(
|
|
|
448
448
|
id,
|
|
449
449
|
layerSelectionEnabled: !mediaPreviewEnabled,
|
|
450
450
|
mediaAssets: mediaPreviewEnabled ? (selection.mediaAssets ?? []) : [],
|
|
451
|
+
name,
|
|
452
|
+
setControlValue,
|
|
451
453
|
sourceAssetCoordinator,
|
|
454
|
+
value,
|
|
452
455
|
});
|
|
453
456
|
break;
|
|
454
457
|
case "settings":
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
|
|
5
5
|
import type {
|
|
6
|
-
|
|
6
|
+
ResolvedToolcraftControlSectionSchema,
|
|
7
|
+
ResolvedToolcraftControlSchema,
|
|
7
8
|
ToolcraftControlSchema,
|
|
8
9
|
} from "../../../schema/types";
|
|
9
10
|
import {
|
|
@@ -12,7 +13,7 @@ import {
|
|
|
12
13
|
} from "../../../schema/runtime-targets";
|
|
13
14
|
import { getToolcraftControlRendererKind } from "../renderers/controls-panel-renderer-registry";
|
|
14
15
|
|
|
15
|
-
export type ControlEntry = [string,
|
|
16
|
+
export type ControlEntry = [string, ResolvedToolcraftControlSchema];
|
|
16
17
|
|
|
17
18
|
export type ControlRenderGroup =
|
|
18
19
|
| { entries: readonly ControlEntry[]; kind: "colorGroup" }
|
|
@@ -41,6 +42,7 @@ const hiddenDiscreteMarkerCount = 2;
|
|
|
41
42
|
const sectionedCompoundControlTypes = new Set([
|
|
42
43
|
"channelMixer",
|
|
43
44
|
"collectionActions",
|
|
45
|
+
"sourceCollection",
|
|
44
46
|
"fontPicker",
|
|
45
47
|
"gradient",
|
|
46
48
|
"palette",
|
|
@@ -87,7 +89,7 @@ export function getControlName(
|
|
|
87
89
|
|
|
88
90
|
export function getControlsRecord(
|
|
89
91
|
entries: readonly ControlEntry[],
|
|
90
|
-
): Record<string,
|
|
92
|
+
): Record<string, ResolvedToolcraftControlSchema> {
|
|
91
93
|
return Object.fromEntries(entries);
|
|
92
94
|
}
|
|
93
95
|
|
|
@@ -151,7 +153,7 @@ export function getControlRenderGroupIds(
|
|
|
151
153
|
}
|
|
152
154
|
|
|
153
155
|
export function countControlsByType(
|
|
154
|
-
sections: readonly
|
|
156
|
+
sections: readonly ResolvedToolcraftControlSectionSchema[],
|
|
155
157
|
type: string,
|
|
156
158
|
): number {
|
|
157
159
|
return sections.reduce(
|
|
@@ -186,8 +188,8 @@ export function isColorOnlySection(entries: readonly ControlEntry[]): boolean {
|
|
|
186
188
|
}
|
|
187
189
|
|
|
188
190
|
export function getRenderedControlsSectionTitle(
|
|
189
|
-
section:
|
|
190
|
-
):
|
|
191
|
+
section: ResolvedToolcraftControlSectionSchema,
|
|
192
|
+
): ResolvedToolcraftControlSectionSchema["title"] {
|
|
191
193
|
return isStickyFooterActionSection(section) ? undefined : section.title;
|
|
192
194
|
}
|
|
193
195
|
|
|
@@ -196,7 +198,7 @@ export function isRuntimeSetupSection({
|
|
|
196
198
|
section,
|
|
197
199
|
}: {
|
|
198
200
|
entries: readonly ControlEntry[];
|
|
199
|
-
section:
|
|
201
|
+
section: ResolvedToolcraftControlSectionSchema;
|
|
200
202
|
}): boolean {
|
|
201
203
|
return (
|
|
202
204
|
section.title === "Setup" &&
|
|
@@ -205,7 +207,7 @@ export function isRuntimeSetupSection({
|
|
|
205
207
|
}
|
|
206
208
|
|
|
207
209
|
function isStickyFooterActionSection(
|
|
208
|
-
section:
|
|
210
|
+
section: ResolvedToolcraftControlSectionSchema,
|
|
209
211
|
): boolean {
|
|
210
212
|
return (
|
|
211
213
|
section.actionGroup !== undefined &&
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
TooltipTrigger,
|
|
11
11
|
} from "@repo/ui";
|
|
12
12
|
|
|
13
|
-
import type {
|
|
13
|
+
import type { ResolvedToolcraftControlSectionSchema } from "../../../schema/types";
|
|
14
14
|
import type { ToolcraftStoreDependency } from "../../../state/toolcraft-external-store-dependencies";
|
|
15
15
|
import type {
|
|
16
16
|
ToolcraftCommand,
|
|
@@ -56,7 +56,7 @@ export type ControlsPanelSectionGroupsArgs = {
|
|
|
56
56
|
headerKeyframeTarget: string | null;
|
|
57
57
|
keyframesSupported: boolean;
|
|
58
58
|
runAction: ActionControlRunAction;
|
|
59
|
-
section:
|
|
59
|
+
section: ResolvedToolcraftControlSectionSchema;
|
|
60
60
|
setControlValue: ControlsPanelSetControlValue;
|
|
61
61
|
vectorPadShape: "compact" | "square";
|
|
62
62
|
};
|
|
@@ -66,7 +66,7 @@ export type ControlsPanelSectionProps = Omit<
|
|
|
66
66
|
"headerKeyframeTarget"
|
|
67
67
|
> & {
|
|
68
68
|
"data-toolcraft-panel-section": "";
|
|
69
|
-
actionGroup:
|
|
69
|
+
actionGroup: ResolvedToolcraftControlSectionSchema["actionGroup"];
|
|
70
70
|
collapsed: boolean;
|
|
71
71
|
onCollapsedChange: (sectionCollapseKey: string, collapsed: boolean) => void;
|
|
72
72
|
sectionCollapseKey: string;
|