@cratis/scene.components 4.8.0 → 4.10.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.
Files changed (61) hide show
  1. package/cratisComponentsDescriptors.ts +53 -1
  2. package/cratisComponentsPackage.ts +23 -0
  3. package/dist/cjs/cratisComponentsDescriptors.js +150 -0
  4. package/dist/cjs/cratisComponentsDescriptors.js.map +1 -1
  5. package/dist/cjs/cratisComponentsPackage.js +40 -2
  6. package/dist/cjs/cratisComponentsPackage.js.map +1 -1
  7. package/dist/cjs/forms/CommandFieldPropertyEditor.js +28 -0
  8. package/dist/cjs/forms/CommandFieldPropertyEditor.js.map +1 -0
  9. package/dist/cjs/forms/CommandFormDesigner.js +20 -0
  10. package/dist/cjs/forms/CommandFormDesigner.js.map +1 -0
  11. package/dist/cjs/forms/CommandFormRuntime.js +11 -4
  12. package/dist/cjs/forms/CommandFormRuntime.js.map +1 -1
  13. package/dist/cjs/forms/ExplicitCommandField.js +13 -7
  14. package/dist/cjs/forms/ExplicitCommandField.js.map +1 -1
  15. package/dist/cjs/forms/SceneCommandForm.js +10 -3
  16. package/dist/cjs/forms/SceneCommandForm.js.map +1 -1
  17. package/dist/cjs/forms/commandInputs.js +8 -2
  18. package/dist/cjs/forms/commandInputs.js.map +1 -1
  19. package/dist/cjs/forms/generateCommandFieldsAction.js +58 -0
  20. package/dist/cjs/forms/generateCommandFieldsAction.js.map +1 -0
  21. package/dist/esm/cratisComponentsDescriptors.d.ts.map +1 -1
  22. package/dist/esm/cratisComponentsDescriptors.js +151 -1
  23. package/dist/esm/cratisComponentsDescriptors.js.map +1 -1
  24. package/dist/esm/cratisComponentsPackage.d.ts.map +1 -1
  25. package/dist/esm/cratisComponentsPackage.js +40 -2
  26. package/dist/esm/cratisComponentsPackage.js.map +1 -1
  27. package/dist/esm/forms/CommandFieldPropertyEditor.d.ts +3 -0
  28. package/dist/esm/forms/CommandFieldPropertyEditor.d.ts.map +1 -0
  29. package/dist/esm/forms/CommandFieldPropertyEditor.js +26 -0
  30. package/dist/esm/forms/CommandFieldPropertyEditor.js.map +1 -0
  31. package/dist/esm/forms/CommandFormDesigner.d.ts +3 -0
  32. package/dist/esm/forms/CommandFormDesigner.d.ts.map +1 -0
  33. package/dist/esm/forms/CommandFormDesigner.js +18 -0
  34. package/dist/esm/forms/CommandFormDesigner.js.map +1 -0
  35. package/dist/esm/forms/CommandFormRuntime.d.ts +2 -1
  36. package/dist/esm/forms/CommandFormRuntime.d.ts.map +1 -1
  37. package/dist/esm/forms/CommandFormRuntime.js +11 -4
  38. package/dist/esm/forms/CommandFormRuntime.js.map +1 -1
  39. package/dist/esm/forms/ExplicitCommandField.d.ts.map +1 -1
  40. package/dist/esm/forms/ExplicitCommandField.js +13 -7
  41. package/dist/esm/forms/ExplicitCommandField.js.map +1 -1
  42. package/dist/esm/forms/SceneCommandForm.d.ts.map +1 -1
  43. package/dist/esm/forms/SceneCommandForm.js +11 -4
  44. package/dist/esm/forms/SceneCommandForm.js.map +1 -1
  45. package/dist/esm/forms/commandInputs.d.ts +2 -0
  46. package/dist/esm/forms/commandInputs.d.ts.map +1 -1
  47. package/dist/esm/forms/commandInputs.js +9 -3
  48. package/dist/esm/forms/commandInputs.js.map +1 -1
  49. package/dist/esm/forms/generateCommandFieldsAction.d.ts +3 -0
  50. package/dist/esm/forms/generateCommandFieldsAction.d.ts.map +1 -0
  51. package/dist/esm/forms/generateCommandFieldsAction.js +56 -0
  52. package/dist/esm/forms/generateCommandFieldsAction.js.map +1 -0
  53. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  54. package/forms/CommandFieldPropertyEditor.tsx +16 -0
  55. package/forms/CommandFormDesigner.tsx +14 -0
  56. package/forms/CommandFormRuntime.tsx +5 -2
  57. package/forms/ExplicitCommandField.tsx +4 -2
  58. package/forms/SceneCommandForm.tsx +5 -3
  59. package/forms/commandInputs.ts +7 -3
  60. package/forms/generateCommandFieldsAction.ts +46 -0
  61. package/package.json +7 -7
@@ -0,0 +1,16 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { SceneEditKind } from '@cratis/scene.model';
5
+ import { DesignTimePropertyEditorProps } from '@cratis/scene.react';
6
+
7
+ /**
8
+ * Package-side editor for a command form property. It writes through the generic Scene edit boundary.
9
+ */
10
+ export function CommandFieldPropertyEditor({ context, property, value, setValue }: DesignTimePropertyEditorProps) {
11
+ return <input aria-label={property.label} value={typeof value === 'string' ? value : ''} onChange={event => {
12
+ const next = event.currentTarget.value;
13
+ setValue(next);
14
+ context.submitEdits([{ kind: SceneEditKind.SetProperty, nodeId: context.element.id, path: property.path, value: next }]);
15
+ }} />;
16
+ }
@@ -0,0 +1,14 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { DesignTimeComponentProps } from '@cratis/scene.react';
5
+
6
+ /**
7
+ * Generic package-side command form designer. It reads only the shared design-time context and submits Scene edits.
8
+ */
9
+ export function CommandFormDesigner({ context }: DesignTimeComponentProps) {
10
+ const canGenerate = context.capabilities['commandForm.generateFields'] === true && context.permissions.edit !== false;
11
+ return <button type='button' disabled={!canGenerate} onClick={() => context.submitAction('Cratis.Components.commandForm.generateFields', [])}>
12
+ Generate fields
13
+ </button>;
14
+ }
@@ -15,6 +15,7 @@ interface CommandFormRuntimeProps {
15
15
  inputs?: CommandInput[];
16
16
  exclude?: string[];
17
17
  submitLabel: string;
18
+ columns?: number;
18
19
  }
19
20
 
20
21
  // Runtime bindings erase the proxy's keys; the optional footer is a Components >=4.13 contract.
@@ -59,12 +60,14 @@ function capability(command: BoundConstructor, inputs?: CommandInput[], exclude?
59
60
  }
60
61
 
61
62
  /** Private lazy entry point: importing the Scene registry must not import Arc. */
62
- export default function CommandFormRuntime({ command, inputs, exclude, submitLabel }: CommandFormRuntimeProps) {
63
+ export default function CommandFormRuntime({ command, inputs, exclude, submitLabel, columns = 1 }: CommandFormRuntimeProps) {
63
64
  const error = capability(command, inputs, exclude);
64
65
  if (error) return <div role='alert'>{error}</div>;
65
66
  // The registry's never-argument constructor is erased; Arc creates this checked proxy with no args.
66
67
  if (inputs) return <CommandForm command={command as unknown as new () => object} showTitles={false}>
67
- {inputs.map(input => <ExplicitCommandField key={input.property} input={input} />)}
68
+ <div style={{ display: 'grid', gridTemplateColumns: `repeat(${Math.max(1, columns)}, minmax(0, 1fr))`, gap: '1rem' }}>
69
+ {inputs.map(input => <ExplicitCommandField key={input.property} input={input} />)}
70
+ </div>
68
71
  <CommandFormSubmit label={submitLabel} />
69
72
  </CommandForm>;
70
73
  return <NativeAutoCommandForm command={command} exclude={exclude} footer={<CommandFormSubmit label={submitLabel} />} />;
@@ -81,6 +81,8 @@ const NativeTextField = asCommandFormField<TextProps>(TextControl, { defaultValu
81
81
 
82
82
  /** Opaque child is self-bound by Arc's native runtime binding, with an exact fieldName. */
83
83
  export function ExplicitCommandField({ input }: { input: CommandInput }) {
84
- return <NativeTextField fieldName={input.property} value={(command: Record<string, unknown>) => command[input.property]}
85
- property={input.property} guid={input.type === 'guid'} label={input.label} title={input.label} />;
84
+ return <div style={{ gridColumn: input.column === undefined ? undefined : `${input.column}`, width: input.width }}>
85
+ <NativeTextField fieldName={input.property} value={(command: Record<string, unknown>) => command[input.property]}
86
+ property={input.property} guid={input.type === 'guid'} label={input.label} title={input.label} />
87
+ </div>;
86
88
  }
@@ -5,7 +5,7 @@ import { lazy } from 'react';
5
5
  import { RegisteredComponentProps } from '@cratis/scene.react';
6
6
  import { ArcRuntimeBoundary, BindingKind, MissingBinding, resolveElementBinding } from '../bindings';
7
7
  import type { BoundConstructor } from '../bindings';
8
- import { stringArrayProperty, stringProperty } from '../properties';
8
+ import { numberProperty, stringArrayProperty, stringProperty, unionProperty } from '../properties';
9
9
  import { commandInputs } from './commandInputs';
10
10
 
11
11
  const CommandFormRuntime = lazy(() => import('./CommandFormRuntime'));
@@ -31,8 +31,9 @@ export function SceneCommandForm({ element }: RegisteredComponentProps) {
31
31
  const { name, target } = resolveElementBinding(element, BindingKind.Command);
32
32
  if (!target) return <MissingBinding element={element} kind={BindingKind.Command} name={name} />;
33
33
 
34
- const inputs = Object.hasOwn(element.properties, 'inputs') ? commandInputs(element.properties) : undefined;
35
- if (Object.hasOwn(element.properties, 'inputs') && !inputs) return <div role='alert'>Invalid command form inputs declaration</div>;
34
+ const mode = unionProperty(element.properties, 'mode', ['auto', 'manual'] as const) ?? (Object.hasOwn(element.properties, 'inputs') ? 'manual' : 'auto');
35
+ const inputs = mode === 'manual' ? commandInputs(element.properties) : undefined;
36
+ if (mode === 'manual' && !inputs) return <div role='alert'>Invalid command form inputs declaration</div>;
36
37
 
37
38
  return (
38
39
  <ArcRuntimeBoundary>
@@ -42,6 +43,7 @@ export function SceneCommandForm({ element }: RegisteredComponentProps) {
42
43
  inputs={inputs}
43
44
  exclude={stringArrayProperty(element.properties, 'exclude')}
44
45
  submitLabel={stringProperty(element.properties, 'submitLabel') ?? 'Submit'}
46
+ {...(numberProperty(element.properties, 'columns') === undefined ? {} : { columns: numberProperty(element.properties, 'columns') })}
45
47
  />
46
48
  </ArcRuntimeBoundary>
47
49
  );
@@ -1,13 +1,15 @@
1
1
  // Copyright (c) Cratis. All rights reserved.
2
2
  // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
3
 
4
- import { arrayProperty, objectProperty, stringProperty } from '../properties';
4
+ import { arrayProperty, numberProperty, objectProperty, stringProperty } from '../properties';
5
5
 
6
6
  /** Exact, case-sensitive names from a generated command's propertyDescriptors. */
7
7
  export interface CommandInput {
8
8
  property: string;
9
9
  type: 'string' | 'guid';
10
10
  label: string;
11
+ column?: number;
12
+ width?: string;
11
13
  }
12
14
 
13
15
  /** An absent declaration selects legacy auto mode; a present but invalid declaration never does. */
@@ -18,12 +20,14 @@ export function commandInputs(properties: Record<string, unknown>): CommandInput
18
20
  const seen = new Set<string>();
19
21
  for (const value of values) {
20
22
  const input = objectProperty({ value }, 'value');
21
- if (!input || Object.keys(input).some(key => !['property', 'type', 'label'].includes(key))) return undefined;
23
+ if (!input || Object.keys(input).some(key => !['property', 'type', 'label', 'column', 'width'].includes(key))) return undefined;
22
24
  const property = stringProperty(input, 'property');
23
25
  const label = stringProperty(input, 'label');
24
26
  if (!property?.trim() || !label?.trim() || (input.type !== 'string' && input.type !== 'guid') || seen.has(property)) return undefined;
25
27
  seen.add(property);
26
- inputs.push({ property, type: input.type, label });
28
+ const column = numberProperty(input, 'column');
29
+ const width = stringProperty(input, 'width');
30
+ inputs.push({ property, type: input.type, label, column, width });
27
31
  }
28
32
  return inputs;
29
33
  }
@@ -0,0 +1,46 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { DesignTimeAction, DesignTimeActionContext, DesignTimeActionResult } from '@cratis/scene.react';
5
+ import { DesignTimeActionPlacement, SceneEditKind } from '@cratis/scene.model';
6
+
7
+ export const generateCommandFieldsAction: DesignTimeAction = {
8
+ descriptor: {
9
+ id: 'Cratis.Components.commandForm.generateFields',
10
+ label: 'Generate fields',
11
+ placement: DesignTimeActionPlacement.Toolbar,
12
+ availability: 'commandForm.canGenerateFields',
13
+ description: 'Creates a deterministic editable field layout from the selected command metadata.',
14
+ },
15
+ isVisible: context => context.element.componentName.endsWith(':commandForm'),
16
+ isEnabled: context => Array.isArray(context.commandMetadata?.properties) && !Array.isArray(context.element.properties.inputs),
17
+ execute: context => generateCommandFields(context),
18
+ };
19
+
20
+ function generateCommandFields(context: DesignTimeActionContext): DesignTimeActionResult {
21
+ const properties = context.commandMetadata?.properties ?? [];
22
+ if (!properties.length) return { edits: [], diagnostics: ['No command metadata was supplied.'] };
23
+ if (Array.isArray(context.element.properties.inputs)) {
24
+ return { edits: [], diagnostics: ['The form already has authored fields; Generate fields will not overwrite them.'] };
25
+ }
26
+
27
+ return {
28
+ diagnostics: [],
29
+ edits: [{
30
+ kind: SceneEditKind.SetProperty,
31
+ nodeId: context.element.id,
32
+ path: 'inputs',
33
+ value: properties.map((property, index) => ({
34
+ property: property.name,
35
+ type: property.type === 'Guid' || property.type === 'guid' ? 'guid' : 'string',
36
+ label: property.label ?? humanize(property.name),
37
+ column: (index % 2) + 1,
38
+ width: '1fr',
39
+ })),
40
+ }],
41
+ };
42
+ }
43
+
44
+ function humanize(name: string): string {
45
+ return name.replace(/([a-z0-9])([A-Z])/g, '$1 $2').replace(/^./, value => value.toUpperCase());
46
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cratis/scene.components",
3
- "version": "4.8.0",
3
+ "version": "4.10.0",
4
4
  "description": "The Cratis Components package: exposes @cratis/components' Arc-bound data, form and dialog composites as Scene components. Built on PrimeReact and Tailwind, and declares both as dependencies.",
5
5
  "keywords": [
6
6
  "cratis",
@@ -54,17 +54,17 @@
54
54
  "build-storybook": "storybook build"
55
55
  },
56
56
  "dependencies": {
57
- "@cratis/scene.engine": "4.8.0",
58
- "@cratis/scene.model": "4.8.0",
59
- "@cratis/scene.react": "4.8.0"
57
+ "@cratis/scene.engine": "4.10.0",
58
+ "@cratis/scene.model": "4.10.0",
59
+ "@cratis/scene.react": "4.10.0"
60
60
  },
61
61
  "devDependencies": {
62
62
  "@cratis/arc": "22.50.5",
63
63
  "@cratis/arc.react": "22.50.5",
64
64
  "@cratis/components": "4.25.0",
65
- "@cratis/scene.engine": "4.8.0",
66
- "@cratis/scene.model": "4.8.0",
67
- "@cratis/scene.react": "4.8.0",
65
+ "@cratis/scene.engine": "4.10.0",
66
+ "@cratis/scene.model": "4.10.0",
67
+ "@cratis/scene.react": "4.10.0",
68
68
  "@storybook/addon-links": "^10.6.1",
69
69
  "@storybook/react": "^10.6.1",
70
70
  "@storybook/react-vite": "^10.6.1",