@cratis/scene.components 4.13.0 → 4.15.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 (34) hide show
  1. package/cratisComponentsDescriptors.ts +5 -0
  2. package/cratisComponentsPackage.ts +3 -1
  3. package/dist/cjs/cratisComponentsDescriptors.js +18 -0
  4. package/dist/cjs/cratisComponentsDescriptors.js.map +1 -1
  5. package/dist/cjs/cratisComponentsPackage.js +5 -2
  6. package/dist/cjs/cratisComponentsPackage.js.map +1 -1
  7. package/dist/cjs/forms/CommandFormLayoutEditor.js +73 -0
  8. package/dist/cjs/forms/CommandFormLayoutEditor.js.map +1 -0
  9. package/dist/cjs/forms/CommandFormRuntime.js +5 -3
  10. package/dist/cjs/forms/CommandFormRuntime.js.map +1 -1
  11. package/dist/cjs/forms/SceneCommandForm.js +5 -1
  12. package/dist/cjs/forms/SceneCommandForm.js.map +1 -1
  13. package/dist/esm/cratisComponentsDescriptors.d.ts.map +1 -1
  14. package/dist/esm/cratisComponentsDescriptors.js +18 -0
  15. package/dist/esm/cratisComponentsDescriptors.js.map +1 -1
  16. package/dist/esm/cratisComponentsPackage.d.ts.map +1 -1
  17. package/dist/esm/cratisComponentsPackage.js +5 -2
  18. package/dist/esm/cratisComponentsPackage.js.map +1 -1
  19. package/dist/esm/forms/CommandFormLayoutEditor.d.ts +3 -0
  20. package/dist/esm/forms/CommandFormLayoutEditor.d.ts.map +1 -0
  21. package/dist/esm/forms/CommandFormLayoutEditor.js +71 -0
  22. package/dist/esm/forms/CommandFormLayoutEditor.js.map +1 -0
  23. package/dist/esm/forms/CommandFormRuntime.d.ts +2 -1
  24. package/dist/esm/forms/CommandFormRuntime.d.ts.map +1 -1
  25. package/dist/esm/forms/CommandFormRuntime.js +6 -4
  26. package/dist/esm/forms/CommandFormRuntime.js.map +1 -1
  27. package/dist/esm/forms/SceneCommandForm.d.ts.map +1 -1
  28. package/dist/esm/forms/SceneCommandForm.js +6 -2
  29. package/dist/esm/forms/SceneCommandForm.js.map +1 -1
  30. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  31. package/forms/CommandFormLayoutEditor.tsx +49 -0
  32. package/forms/CommandFormRuntime.tsx +9 -5
  33. package/forms/SceneCommandForm.tsx +2 -0
  34. package/package.json +7 -7
@@ -0,0 +1,49 @@
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 { KeyboardEvent, useState } from 'react';
5
+ import { CommandFormLayout, SceneEditKind } from '@cratis/scene.model';
6
+ import { CommandFormLayoutKey, applyCommandFormLayoutKey } from '@cratis/scene.engine';
7
+ import { DesignTimePropertyEditorProps } from '@cratis/scene.react';
8
+ import { commandFormLayout } from './commandFormLayout';
9
+
10
+ const layoutKeys = new Set<string>(Object.values(CommandFormLayoutKey));
11
+
12
+ /**
13
+ * Arranges a command form's fields from the keyboard. Each placed field is a focusable cell: the arrow keys
14
+ * move it one cell, and Shift with an arrow key resizes it. A valid change is submitted as one canonical
15
+ * edit of the `layout` property; an invalid one is announced and the committed layout is kept.
16
+ */
17
+ export function CommandFormLayoutEditor({ context, property, value, setValue }: DesignTimePropertyEditorProps) {
18
+ const layout = commandFormLayout({ layout: value });
19
+ const [problem, setProblem] = useState<string>();
20
+ if (!layout) return <p role='note'>{property.label}: no layout is authored yet.</p>;
21
+
22
+ const onKeyDown = (field: string) => (event: KeyboardEvent<HTMLButtonElement>) => {
23
+ if (!layoutKeys.has(event.key)) return;
24
+ event.preventDefault();
25
+ const draft = applyCommandFormLayoutKey(layout, field, event.key as CommandFormLayoutKey, event.shiftKey, layout.placements.map(placement => placement.field));
26
+ if (!draft) return;
27
+ if (!draft.isValid) {
28
+ setProblem(draft.diagnostics[0].message);
29
+ return;
30
+ }
31
+
32
+ setProblem(undefined);
33
+ commit(draft.draft);
34
+ };
35
+
36
+ const commit = (next: CommandFormLayout) => {
37
+ setValue(next);
38
+ context.submitEdits([{ kind: SceneEditKind.SetProperty, nodeId: context.element.id, path: property.path, value: next }]);
39
+ };
40
+
41
+ return <div role='group' aria-label={property.label} aria-describedby={`${context.element.id}-layout-help`}>
42
+ <p id={`${context.element.id}-layout-help`}>Arrow keys move a field. Shift and an arrow key resize it.</p>
43
+ {layout.placements.map(placement => <button key={placement.field} type='button' onKeyDown={onKeyDown(placement.field)}
44
+ aria-label={`${placement.field}: row ${placement.row}, column ${placement.column}, spans ${placement.columnSpan ?? 1} column(s) and ${placement.rowSpan ?? 1} row(s)`}>
45
+ {placement.field}
46
+ </button>)}
47
+ {problem && <p role='alert'>{problem}</p>}
48
+ </div>;
49
+ }
@@ -4,7 +4,7 @@
4
4
  import type { ComponentType, ReactNode } from 'react';
5
5
  import { AutoCommandForm } from '@cratis/components/CommandForm';
6
6
  import { CommandForm } from '@cratis/arc.react/commands';
7
- import { formWidthToCss, normalizeCommandFormLayout, validateCommandFormLayout } from '@cratis/scene.engine';
7
+ import { formWidthToCss, normalizeCommandFormLayout, stackCommandFormLayout, validateCommandFormLayout } from '@cratis/scene.engine';
8
8
  import { Guid } from '@cratis/fundamentals';
9
9
  import type { BoundConstructor } from '../bindings';
10
10
  import type { CommandInput } from './commandInputs';
@@ -19,6 +19,9 @@ interface CommandFormRuntimeProps {
19
19
  submitLabel: string;
20
20
  columns?: number;
21
21
  layout?: CommandFormLayout;
22
+
23
+ /** At a compact width the fields stack in one column, in reading order. */
24
+ compact?: boolean;
22
25
  }
23
26
 
24
27
  // Runtime bindings erase the proxy's keys; the optional footer is a Components >=4.13 contract.
@@ -63,17 +66,18 @@ function capability(command: BoundConstructor, inputs?: CommandInput[], exclude?
63
66
  }
64
67
 
65
68
  /** Private lazy entry point: importing the Scene registry must not import Arc. */
66
- export default function CommandFormRuntime({ command, inputs, exclude, submitLabel, columns = 1, layout }: CommandFormRuntimeProps) {
69
+ export default function CommandFormRuntime({ command, inputs, exclude, submitLabel, columns = 1, layout, compact = false }: CommandFormRuntimeProps) {
67
70
  const error = capability(command, inputs, exclude);
68
71
  if (error) return <div role='alert'>{error}</div>;
69
72
  // The registry's never-argument constructor is erased; Arc creates this checked proxy with no args.
70
73
  if (inputs) {
71
- const formLayout = normalizeCommandFormLayout(layout, inputs);
72
- const diagnostics = validateCommandFormLayout(formLayout, inputs.map(input => input.property));
74
+ const authoredLayout = normalizeCommandFormLayout(layout, inputs);
75
+ const diagnostics = validateCommandFormLayout(authoredLayout, inputs.map(input => input.property));
73
76
  if (diagnostics.length) return <div role='alert'>{diagnostics[0].message}</div>;
77
+ const formLayout = compact ? stackCommandFormLayout(authoredLayout) : authoredLayout;
74
78
  const columnsCss = formLayout.columns.map(column => formWidthToCss(column.width) ?? 'minmax(0, 1fr)').join(' ') || `repeat(${Math.max(1, columns)}, minmax(0, 1fr))`;
75
79
  return <CommandForm command={command as unknown as new () => object} showTitles={false}>
76
- <div style={{ display: 'grid', gridTemplateColumns: columnsCss, columnGap: formWidthToCss(formLayout.columnGap) ?? '1rem', rowGap: formWidthToCss(formLayout.rowGap) ?? '1rem' }}>
80
+ <div data-scene-form-columns={formLayout.columns.length} style={{ display: 'grid', gridTemplateColumns: columnsCss, columnGap: formWidthToCss(formLayout.columnGap) ?? '1rem', rowGap: formWidthToCss(formLayout.rowGap) ?? '1rem' }}>
77
81
  {inputs.map(input => <ExplicitCommandField key={input.property} input={input} placement={formLayout.placements.find(placement => placement.field === input.property) ?? input.placement} />)}
78
82
  </div>
79
83
  <CommandFormSubmit label={submitLabel} />
@@ -2,6 +2,7 @@
2
2
  // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
3
 
4
4
  import { lazy } from 'react';
5
+ import { WidthSizeClass } from '@cratis/scene.model';
5
6
  import { RegisteredComponentProps } from '@cratis/scene.react';
6
7
  import { ArcRuntimeBoundary, BindingKind, MissingBinding, resolveElementBinding } from '../bindings';
7
8
  import type { BoundConstructor } from '../bindings';
@@ -46,6 +47,7 @@ export function SceneCommandForm({ element }: RegisteredComponentProps) {
46
47
  submitLabel={stringProperty(element.properties, 'submitLabel') ?? 'Submit'}
47
48
  {...(numberProperty(element.properties, 'columns') === undefined ? {} : { columns: numberProperty(element.properties, 'columns') })}
48
49
  {...(commandFormLayout(element.properties) === undefined ? {} : { layout: commandFormLayout(element.properties) })}
50
+ {...(stringProperty(element.properties, 'widthSizeClass') === WidthSizeClass.Compact ? { compact: true } : {})}
49
51
  />
50
52
  </ArcRuntimeBoundary>
51
53
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cratis/scene.components",
3
- "version": "4.13.0",
3
+ "version": "4.15.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.13.0",
58
- "@cratis/scene.model": "4.13.0",
59
- "@cratis/scene.react": "4.13.0"
57
+ "@cratis/scene.engine": "4.15.0",
58
+ "@cratis/scene.model": "4.15.0",
59
+ "@cratis/scene.react": "4.15.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.13.0",
66
- "@cratis/scene.model": "4.13.0",
67
- "@cratis/scene.react": "4.13.0",
65
+ "@cratis/scene.engine": "4.15.0",
66
+ "@cratis/scene.model": "4.15.0",
67
+ "@cratis/scene.react": "4.15.0",
68
68
  "@storybook/addon-links": "^10.6.1",
69
69
  "@storybook/react": "^10.6.1",
70
70
  "@storybook/react-vite": "^10.6.1",