@cratis/components 0.1.9 → 0.1.10

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 (101) hide show
  1. package/dist/cjs/PivotViewer/PivotViewer.css +1258 -0
  2. package/dist/cjs/PivotViewer/components/Spinner.css +77 -0
  3. package/dist/cjs/TimeMachine/EventsView.css +213 -0
  4. package/dist/cjs/TimeMachine/TimeMachine.css +567 -0
  5. package/dist/esm/PivotViewer/PivotViewer.css +1258 -0
  6. package/dist/esm/PivotViewer/components/Spinner.css +77 -0
  7. package/dist/esm/TimeMachine/EventsView.css +213 -0
  8. package/dist/esm/TimeMachine/TimeMachine.css +567 -0
  9. package/package.json +3 -4
  10. package/.storybook/main.ts +0 -24
  11. package/CommandDialog/CommandDialog.stories.tsx +0 -25
  12. package/CommandDialog/CommandDialog.tsx +0 -161
  13. package/CommandDialog/index.ts +0 -4
  14. package/CommandForm/CommandForm.stories.tsx +0 -24
  15. package/CommandForm/CommandForm.tsx +0 -266
  16. package/CommandForm/CommandFormField.tsx +0 -27
  17. package/CommandForm/CommandFormFields.tsx +0 -142
  18. package/CommandForm/DatePickerField.tsx +0 -57
  19. package/CommandForm/DropdownField.tsx +0 -65
  20. package/CommandForm/InputTextField.tsx +0 -62
  21. package/CommandForm/SliderField.tsx +0 -68
  22. package/CommandForm/index.ts +0 -10
  23. package/Common/ErrorBoundary.stories.tsx +0 -10
  24. package/Common/ErrorBoundary.tsx +0 -41
  25. package/Common/FormElement.stories.tsx +0 -10
  26. package/Common/FormElement.tsx +0 -20
  27. package/Common/Page.stories.tsx +0 -10
  28. package/Common/Page.tsx +0 -21
  29. package/Common/index.ts +0 -6
  30. package/DataPage/DataPage.stories.tsx +0 -10
  31. package/DataPage/DataPage.tsx +0 -191
  32. package/DataPage/index.ts +0 -4
  33. package/DataTables/DataTableForObservableQuery.stories.tsx +0 -10
  34. package/DataTables/DataTableForObservableQuery.tsx +0 -97
  35. package/DataTables/DataTableForQuery.stories.tsx +0 -10
  36. package/DataTables/DataTableForQuery.tsx +0 -97
  37. package/DataTables/index.ts +0 -5
  38. package/Dialogs/BusyIndicatorDialog.stories.tsx +0 -26
  39. package/Dialogs/BusyIndicatorDialog.tsx +0 -26
  40. package/Dialogs/ConfirmationDialog.stories.tsx +0 -36
  41. package/Dialogs/ConfirmationDialog.tsx +0 -75
  42. package/Dialogs/index.ts +0 -5
  43. package/Dropdown/Dropdown.tsx +0 -23
  44. package/Dropdown/index.ts +0 -4
  45. package/PivotViewer/PivotViewer.stories.tsx +0 -24
  46. package/PivotViewer/PivotViewer.tsx +0 -791
  47. package/PivotViewer/components/AxisLabels.tsx +0 -69
  48. package/PivotViewer/components/DetailPanel.tsx +0 -108
  49. package/PivotViewer/components/FilterPanel.tsx +0 -189
  50. package/PivotViewer/components/FilterPanelContainer.tsx +0 -10
  51. package/PivotViewer/components/PivotCanvas.tsx +0 -660
  52. package/PivotViewer/components/PivotViewerMain.tsx +0 -229
  53. package/PivotViewer/components/RangeHistogramFilter.tsx +0 -220
  54. package/PivotViewer/components/Spinner.tsx +0 -21
  55. package/PivotViewer/components/Toolbar.tsx +0 -130
  56. package/PivotViewer/components/ToolbarContainer.tsx +0 -10
  57. package/PivotViewer/components/index.ts +0 -12
  58. package/PivotViewer/components/pivot/animation.ts +0 -108
  59. package/PivotViewer/components/pivot/buckets.ts +0 -152
  60. package/PivotViewer/components/pivot/colorResolver.ts +0 -67
  61. package/PivotViewer/components/pivot/constants.ts +0 -46
  62. package/PivotViewer/components/pivot/sprites.ts +0 -265
  63. package/PivotViewer/components/pivot/visibility.ts +0 -319
  64. package/PivotViewer/constants.ts +0 -9
  65. package/PivotViewer/engine/layout.ts +0 -149
  66. package/PivotViewer/engine/pivot.worker.ts +0 -86
  67. package/PivotViewer/engine/store.ts +0 -437
  68. package/PivotViewer/engine/types.ts +0 -255
  69. package/PivotViewer/hooks/index.ts +0 -13
  70. package/PivotViewer/hooks/useContainerDimensions.ts +0 -45
  71. package/PivotViewer/hooks/useDimensionState.ts +0 -53
  72. package/PivotViewer/hooks/useFilterOptions.ts +0 -36
  73. package/PivotViewer/hooks/useFilterPanelDrag.ts +0 -49
  74. package/PivotViewer/hooks/useFilterState.ts +0 -106
  75. package/PivotViewer/hooks/useFilteredData.ts +0 -119
  76. package/PivotViewer/hooks/usePanning.ts +0 -163
  77. package/PivotViewer/hooks/usePivotEngine.ts +0 -252
  78. package/PivotViewer/hooks/useSelectedItem.ts +0 -402
  79. package/PivotViewer/hooks/useWheelZoom.ts +0 -114
  80. package/PivotViewer/hooks/useZoomState.ts +0 -34
  81. package/PivotViewer/index.ts +0 -7
  82. package/PivotViewer/types.ts +0 -59
  83. package/PivotViewer/utils/animations.ts +0 -249
  84. package/PivotViewer/utils/constants.ts +0 -20
  85. package/PivotViewer/utils/index.ts +0 -6
  86. package/PivotViewer/utils/selection.ts +0 -292
  87. package/PivotViewer/utils/utils.ts +0 -259
  88. package/TimeMachine/EventsView.stories.tsx +0 -10
  89. package/TimeMachine/EventsView.tsx +0 -119
  90. package/TimeMachine/Properties.stories.tsx +0 -10
  91. package/TimeMachine/Properties.tsx +0 -98
  92. package/TimeMachine/ReadModelView.stories.tsx +0 -10
  93. package/TimeMachine/ReadModelView.tsx +0 -143
  94. package/TimeMachine/TimeMachine.stories.tsx +0 -10
  95. package/TimeMachine/TimeMachine.tsx +0 -244
  96. package/TimeMachine/index.ts +0 -8
  97. package/TimeMachine/types.ts +0 -23
  98. package/global.d.ts +0 -11
  99. package/index.ts +0 -22
  100. package/useOverlayZIndex.ts +0 -32
  101. package/vite.config.ts +0 -80
@@ -1,161 +0,0 @@
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 { ICommandResult } from '@cratis/arc/commands';
5
- import { Constructor } from '@cratis/fundamentals';
6
- import { Dialog } from 'primereact/dialog';
7
- import { Button } from 'primereact/button';
8
- import React, { createContext, useContext } from 'react';
9
- import { CommandForm, useCommandFormContext, BeforeExecuteCallback } from '../CommandForm/CommandForm';
10
- import { useCommandInstance } from '../CommandForm/CommandForm';
11
-
12
- export type FieldValidator<TCommand> = (command: TCommand, fieldName: string, oldValue: unknown, newValue: unknown) => string | undefined;
13
- export type FieldChangeCallback<TCommand> = (command: TCommand, fieldName: string, oldValue: unknown, newValue: unknown) => void;
14
-
15
- export interface CommandDialogProps<TCommand, TResponse = object> {
16
- command: Constructor<TCommand>;
17
- initialValues?: Partial<TCommand>;
18
- currentValues?: Partial<TCommand> | undefined;
19
- visible: boolean;
20
- header: string;
21
- confirmLabel?: string;
22
- cancelLabel?: string;
23
- confirmIcon?: string;
24
- cancelIcon?: string;
25
- onConfirm: (result: ICommandResult<TResponse>) => void | Promise<void>;
26
- onCancel: () => void;
27
- onFieldValidate?: FieldValidator<TCommand>;
28
- onFieldChange?: FieldChangeCallback<TCommand>;
29
- onBeforeExecute?: BeforeExecuteCallback<TCommand>;
30
- children?: React.ReactNode;
31
- style?: React.CSSProperties;
32
- width?: string;
33
- }
34
-
35
- interface CommandDialogContextValue<TCommand = unknown> {
36
- onSuccess: (result: ICommandResult<any>) => void | Promise<void>;
37
- onCancel: () => void;
38
- confirmLabel: string;
39
- cancelLabel: string;
40
- confirmIcon: string;
41
- cancelIcon: string;
42
- onFieldValidate?: FieldValidator<TCommand>;
43
- onFieldChange?: FieldChangeCallback<TCommand>;
44
- onBeforeExecute?: BeforeExecuteCallback<TCommand>;
45
- }
46
-
47
- const CommandDialogContext = createContext<CommandDialogContextValue<unknown> | undefined>(undefined);
48
-
49
- export const useCommandDialogContext = <TCommand = unknown,>() => {
50
- const context = useContext(CommandDialogContext);
51
- if (!context) {
52
- throw new Error('useCommandDialogContext must be used within a CommandDialog');
53
- }
54
- return context as CommandDialogContextValue<TCommand>;
55
- };
56
-
57
- const CommandDialogFormContent = () => {
58
- const command = useCommandInstance();
59
- const { setCommandResult, setCommandValues, isValid, onBeforeExecute } = useCommandFormContext();
60
- const { onSuccess: onConfirm, onCancel, confirmLabel, cancelLabel, confirmIcon, cancelIcon } = useCommandDialogContext();
61
-
62
- const handleConfirm = async () => {
63
- if (onBeforeExecute) {
64
- const transformedValues = onBeforeExecute(command);
65
- setCommandValues(transformedValues);
66
- }
67
- const result = await command.execute();
68
- if (result.isSuccess) {
69
- await onConfirm(result);
70
- } else {
71
- setCommandResult(result);
72
- }
73
- };
74
-
75
- const handleCancel = () => {
76
- onCancel();
77
- };
78
-
79
- return (
80
- <>
81
- <div className="card flex flex-wrap justify-content-center gap-3 mt-8">
82
- <Button label={confirmLabel} icon={confirmIcon} onClick={handleConfirm} disabled={!isValid} />
83
- <Button label={cancelLabel} icon={cancelIcon} severity='secondary' onClick={handleCancel} />
84
- </div>
85
- </>
86
- );
87
- };
88
-
89
- const CommandDialogFieldsWrapper = (props: { children: React.ReactNode }) => {
90
- React.Children.forEach(props.children, child => {
91
- if (React.isValidElement(child)) {
92
- const component = child.type as any;
93
- if (component.displayName !== 'CommandFormField') {
94
- throw new Error(`Only CommandFormField components are allowed as children of CommandDialog.Fields. Got: ${component.displayName || component.name || 'Unknown'}`);
95
- }
96
- }
97
- });
98
-
99
- return (
100
- <CommandForm.Fields>
101
- {props.children}
102
- </CommandForm.Fields>
103
- );
104
- };
105
-
106
- const CommandDialogComponent = <TCommand extends object = any, TResponse = object>(props: CommandDialogProps<TCommand, TResponse>) => {
107
- const {
108
- command,
109
- initialValues,
110
- currentValues,
111
- visible,
112
- header,
113
- confirmLabel = 'Confirm',
114
- cancelLabel = 'Cancel',
115
- confirmIcon = 'pi pi-check',
116
- cancelIcon = 'pi pi-times',
117
- onConfirm,
118
- onCancel,
119
- onFieldValidate,
120
- onFieldChange,
121
- onBeforeExecute,
122
- children,
123
- style = { width: '50vw' },
124
- width
125
- } = props;
126
-
127
- const dialogStyle = width ? { ...style, width } : style;
128
-
129
- const contextValue: CommandDialogContextValue<TCommand> = {
130
- onSuccess: onConfirm,
131
- onCancel,
132
- confirmLabel,
133
- cancelLabel,
134
- confirmIcon,
135
- cancelIcon,
136
- onFieldValidate,
137
- onFieldChange,
138
- onBeforeExecute
139
- };
140
-
141
- return (
142
- <Dialog
143
- header={header}
144
- visible={visible}
145
- style={dialogStyle}
146
- onHide={onCancel}
147
- contentStyle={{ overflow: 'visible' }}
148
- >
149
- <CommandDialogContext.Provider value={contextValue}>
150
- <CommandForm command={command} initialValues={initialValues} currentValues={currentValues} onFieldValidate={onFieldValidate} onFieldChange={onFieldChange} onBeforeExecute={onBeforeExecute}>
151
- {children}
152
- <CommandDialogFormContent />
153
- </CommandForm>
154
- </CommandDialogContext.Provider>
155
- </Dialog>
156
- );
157
- };
158
-
159
- CommandDialogComponent.Fields = CommandDialogFieldsWrapper;
160
-
161
- export const CommandDialog = CommandDialogComponent;
@@ -1,4 +0,0 @@
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
- export * from './CommandDialog';
@@ -1,24 +0,0 @@
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 React from 'react';
5
- import { Meta, StoryObj } from '@storybook/react';
6
- import { CommandForm } from './CommandForm';
7
-
8
- const meta: Meta<typeof CommandForm> = {
9
- title: 'CommandForm/CommandForm',
10
- component: CommandForm,
11
- };
12
-
13
- export default meta;
14
- type Story = StoryObj<typeof CommandForm>;
15
- export const Default: Story = {
16
- args: {
17
-
18
- },
19
- render: (args) => (
20
- <div className="storybook-wrapper">
21
- <CommandForm {...args} />
22
- </div>
23
- )
24
- };
@@ -1,266 +0,0 @@
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 { CommandFormFields, ColumnInfo } from './CommandFormFields';
5
- import { Constructor } from '@cratis/fundamentals';
6
- import { useCommand, SetCommandValues } from '@cratis/arc.react/commands';
7
- import { ICommandResult } from '@cratis/arc/commands';
8
- import React, { createContext, useContext, useMemo, useState, useCallback } from 'react';
9
- import type { CommandFormFieldProps } from './CommandFormField';
10
- import { Panel } from 'primereact/panel';
11
-
12
- export type BeforeExecuteCallback<TCommand> = (values: TCommand) => TCommand;
13
-
14
- export interface CommandFormProps<TCommand extends object> {
15
- command: Constructor<TCommand>;
16
- initialValues?: Partial<TCommand>;
17
- currentValues?: Partial<TCommand> | undefined;
18
- onFieldValidate?: (command: TCommand, fieldName: string, oldValue: unknown, newValue: unknown) => string | undefined;
19
- onFieldChange?: (command: TCommand, fieldName: string, oldValue: unknown, newValue: unknown) => void;
20
- onBeforeExecute?: BeforeExecuteCallback<TCommand>;
21
- children?: React.ReactNode;
22
- }
23
-
24
- interface CommandFormContextValue<TCommand> {
25
- command: Constructor<TCommand>;
26
- commandInstance: TCommand;
27
- setCommandValues: SetCommandValues<TCommand>;
28
- commandResult?: ICommandResult<unknown>;
29
- setCommandResult: (result: ICommandResult<unknown>) => void;
30
- getFieldError: (propertyName: string) => string | undefined;
31
- isValid: boolean;
32
- setFieldValidity: (fieldName: string, isValid: boolean) => void;
33
- onFieldValidate?: (command: TCommand, fieldName: string, oldValue: unknown, newValue: unknown) => string | undefined;
34
- onFieldChange?: (command: TCommand, fieldName: string, oldValue: unknown, newValue: unknown) => void;
35
- onBeforeExecute?: BeforeExecuteCallback<TCommand>;
36
- customFieldErrors: Record<string, string>;
37
- setCustomFieldError: (fieldName: string, error: string | undefined) => void;
38
- }
39
-
40
- const CommandFormContext = createContext<CommandFormContextValue<any> | undefined>(undefined);
41
-
42
- export const useCommandFormContext = <TCommand,>() => {
43
- const context = useContext(CommandFormContext);
44
- if (!context) {
45
- throw new Error('useCommandFormContext must be used within a CommandForm');
46
- }
47
- return context as CommandFormContextValue<TCommand>;
48
- };
49
-
50
- // Hook to get just the command instance for easier access
51
- export const useCommandInstance = <TCommand = any>() => {
52
- const { commandInstance } = useCommandFormContext<any>();
53
- return commandInstance as TCommand;
54
- };
55
-
56
- // Hook to get setCommandResult for easier access
57
- export const useSetCommandResult = () => {
58
- const { setCommandResult } = useCommandFormContext();
59
- return setCommandResult;
60
- };
61
-
62
- const CommandFormFieldsWrapper = (props: { children: React.ReactNode }) => {
63
- React.Children.forEach(props.children, child => {
64
- if (React.isValidElement(child)) {
65
- const component = child.type as any;
66
- if (component.displayName !== 'CommandFormField') {
67
- throw new Error(`Only CommandFormField components are allowed as children of CommandForm.Fields. Got: ${component.displayName || component.name || 'Unknown'}`);
68
- }
69
- }
70
- });
71
-
72
- return <></>;
73
- };
74
-
75
- CommandFormFieldsWrapper.displayName = 'CommandFormFieldsWrapper';
76
-
77
- const getCommandFormFields = <TCommand,>(props: { children?: React.ReactNode }): { fieldsOrColumns: React.ReactElement[] | ColumnInfo[], otherChildren: React.ReactNode[], initialValuesFromFields: Partial<TCommand> } => {
78
- if (!props.children) {
79
- return { fieldsOrColumns: [], otherChildren: [], initialValuesFromFields: {} };
80
- }
81
- let fields: React.ReactElement<CommandFormFieldProps<any>>[] = [];
82
- const columns: ColumnInfo[] = [];
83
- let hasColumns = false;
84
- const otherChildren: React.ReactNode[] = [];
85
- let initialValuesFromFields: Partial<TCommand> = {};
86
-
87
- const extractInitialValue = (field: React.ReactElement) => {
88
- const fieldProps = field.props as any;
89
- if (fieldProps.currentValue !== undefined && fieldProps.value) {
90
- const propertyAccessor = fieldProps.value;
91
- const propertyName = getPropertyNameFromAccessor(propertyAccessor);
92
- if (propertyName) {
93
- initialValuesFromFields = { ...initialValuesFromFields, [propertyName]: fieldProps.currentValue } as Partial<TCommand>;
94
- }
95
- }
96
- };
97
-
98
- React.Children.toArray(props.children).forEach(child => {
99
- if (!React.isValidElement(child)) {
100
- otherChildren.push(child);
101
- return;
102
- }
103
-
104
- const component = child.type as any;
105
-
106
- // Check if child is a CommandFormColumn
107
- if (component.displayName === 'CommandFormColumn') {
108
- hasColumns = true;
109
- const childProps = child.props as { children?: React.ReactNode };
110
- const columnFields = React.Children.toArray(childProps.children).filter(child => {
111
- if (React.isValidElement(child)) {
112
- const comp = child.type as any;
113
- if (comp.displayName === 'CommandFormField') {
114
- extractInitialValue(child as React.ReactElement);
115
- return true;
116
- }
117
- }
118
- return false;
119
- }) as React.ReactElement[];
120
- columns.push({ fields: columnFields as React.ReactElement<CommandFormFieldProps<any>>[] });
121
- }
122
- // Check if child is a CommandFormField (direct child)
123
- else if (component.displayName === 'CommandFormField') {
124
- extractInitialValue(child as React.ReactElement);
125
- fields.push(child as React.ReactElement<CommandFormFieldProps<any>>);
126
- }
127
- // Check if child is Fields wrapper (backwards compatibility)
128
- else if (component === CommandFormFieldsWrapper || component.displayName === 'CommandFormFieldsWrapper') {
129
- const childProps = child.props as { children: React.ReactNode };
130
- const relevantChildren = React.Children.toArray(childProps.children).filter(child => {
131
- if (React.isValidElement(child)) {
132
- const component = child.type as any;
133
- if (component.displayName === 'CommandFormField') {
134
- extractInitialValue(child as React.ReactElement);
135
- return true;
136
- }
137
- }
138
- return false;
139
- }) as React.ReactElement[];
140
- fields = [...fields, ...(relevantChildren as React.ReactElement<CommandFormFieldProps<any>>[])];
141
- }
142
- // Everything else is not a field, keep it as other children
143
- else {
144
- otherChildren.push(child);
145
- }
146
- });
147
-
148
- return { fieldsOrColumns: hasColumns ? columns : fields, otherChildren, initialValuesFromFields };
149
- };
150
-
151
- // Helper function to extract property name from accessor function
152
- function getPropertyNameFromAccessor<T>(accessor: (obj: T) => unknown): string {
153
- const fnStr = accessor.toString();
154
- const match = fnStr.match(/\.([a-zA-Z_$][a-zA-Z0-9_$]*)/);
155
- return match ? match[1] : '';
156
- }
157
-
158
- const CommandFormComponent = <TCommand extends object = any>(props: CommandFormProps<TCommand>) => {
159
- const { fieldsOrColumns, otherChildren, initialValuesFromFields } = useMemo(() => getCommandFormFields<TCommand>(props), [props.children]);
160
-
161
- // Extract matching properties from currentValues
162
- const valuesFromCurrentValues = useMemo(() => {
163
- if (!props.currentValues) return {};
164
-
165
- const tempCommand = new props.command();
166
- const commandProperties = (tempCommand as any).properties || [];
167
- const extracted: Partial<TCommand> = {};
168
-
169
- commandProperties.forEach((propertyName: string) => {
170
- if ((props.currentValues as any)[propertyName] !== undefined) {
171
- (extracted as any)[propertyName] = (props.currentValues as any)[propertyName];
172
- }
173
- });
174
-
175
- return extracted;
176
- }, [props.currentValues, props.command]);
177
-
178
- // Merge initialValues prop with values extracted from field currentValue props and currentValues
179
- const mergedInitialValues = useMemo(() => ({
180
- ...valuesFromCurrentValues,
181
- ...initialValuesFromFields,
182
- ...props.initialValues
183
- }), [valuesFromCurrentValues, initialValuesFromFields, props.initialValues]);
184
-
185
- // useCommand returns [instance, setter] for the typed command. Provide generics so commandInstance is TCommand.
186
- const [commandInstance, setCommandValues] = useCommand<any>(props.command as Constructor<any>, mergedInitialValues as Partial<any>);
187
- const [commandResult, setCommandResult] = useState<ICommandResult<unknown> | undefined>(undefined);
188
- const [fieldValidities, setFieldValidities] = useState<Record<string, boolean>>({});
189
- const [customFieldErrors, setCustomFieldErrors] = useState<Record<string, string>>({});
190
-
191
- // Update command values when mergedInitialValues changes (e.g., when data loads asynchronously)
192
- React.useEffect(() => {
193
- if (mergedInitialValues && Object.keys(mergedInitialValues).length > 0) {
194
- setCommandValues(mergedInitialValues as TCommand);
195
- }
196
- }, [mergedInitialValues, setCommandValues]);
197
-
198
- const isValid = Object.values(fieldValidities).every(valid => valid);
199
-
200
- const setFieldValidity = useCallback((fieldName: string, isFieldValid: boolean) => {
201
- setFieldValidities(prev => ({ ...prev, [fieldName]: isFieldValid }));
202
- }, []);
203
-
204
- const setCustomFieldError = useCallback((fieldName: string, error: string | undefined) => {
205
- setCustomFieldErrors(prev => {
206
- if (error === undefined) {
207
- const newErrors = { ...prev };
208
- delete newErrors[fieldName];
209
- return newErrors;
210
- }
211
- return { ...prev, [fieldName]: error };
212
- });
213
- }, []);
214
-
215
- const getFieldError = (propertyName: string): string | undefined => {
216
- // Check custom field errors first
217
- if (customFieldErrors[propertyName]) {
218
- return customFieldErrors[propertyName];
219
- }
220
-
221
- if (!commandResult || !commandResult.validationResults) {
222
- return undefined;
223
- }
224
-
225
- for (const validationResult of commandResult.validationResults) {
226
- if (validationResult.members && validationResult.members.includes(propertyName)) {
227
- return validationResult.message;
228
- }
229
- }
230
-
231
- return undefined;
232
- };
233
-
234
- const exceptionMessages = commandResult?.exceptionMessages || [];
235
- const hasColumns = fieldsOrColumns.length > 0 && 'fields' in fieldsOrColumns[0];
236
-
237
- return (
238
- <CommandFormContext.Provider value={{ command: props.command, commandInstance, setCommandValues, commandResult, setCommandResult, getFieldError, isValid, setFieldValidity, onFieldValidate: props.onFieldValidate, onFieldChange: props.onFieldChange, onBeforeExecute: props.onBeforeExecute, customFieldErrors, setCustomFieldError }}>
239
- <CommandFormFields fields={hasColumns ? undefined : (fieldsOrColumns as React.ReactElement<CommandFormFieldProps<any>>[])} columns={hasColumns ? fieldsOrColumns as ColumnInfo[] : undefined} />
240
- {exceptionMessages.length > 0 && (
241
- <div className="card flex flex-row gap-3 mt-3">
242
- <Panel header="The server responded with" className="w-full">
243
- <ul>
244
- {exceptionMessages.map((msg, idx) => (
245
- <li key={idx}>{msg}</li>
246
- ))}
247
- </ul>
248
- </Panel>
249
- </div>
250
- )}
251
- {otherChildren}
252
- </CommandFormContext.Provider>
253
- );
254
- };
255
-
256
- const CommandFormColumnComponent = (_props: { children: React.ReactNode }) => {
257
- void _props;
258
- return <></>;
259
- };
260
-
261
- CommandFormColumnComponent.displayName = 'CommandFormColumn';
262
-
263
- CommandFormComponent.Fields = CommandFormFieldsWrapper;
264
- CommandFormComponent.Column = CommandFormColumnComponent;
265
-
266
- export const CommandForm = CommandFormComponent;
@@ -1,27 +0,0 @@
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 type { PropertyAccessor } from '@cratis/fundamentals';
5
-
6
- export interface CommandFormFieldProps<TCommand = unknown> {
7
- icon?: React.ReactElement;
8
- /** Accessor function that selects a property on the command, e.g. c => c.name */
9
- value?: PropertyAccessor<TCommand>;
10
- /** Current value for the property (injected by CommandFormFields) */
11
- currentValue?: unknown;
12
- /** Called when the field value changes (injected by CommandFormFields) */
13
- onValueChange?: (value: unknown) => void;
14
- onChange?: (value: unknown) => void;
15
- required?: boolean;
16
- title?: string;
17
- description?: string;
18
- propertyDescriptor?: unknown;
19
- fieldName?: string;
20
- }
21
-
22
- export const CommandFormField = <TCommand,>(_props?: CommandFormFieldProps<TCommand>) => {
23
- void _props;
24
- return <></>;
25
- };
26
-
27
- CommandFormField.displayName = 'CommandFormField';
@@ -1,142 +0,0 @@
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 { useCommandFormContext } from './CommandForm';
5
- import React from 'react';
6
- import { Tooltip } from 'primereact/tooltip';
7
- import type { CommandFormFieldProps } from './CommandFormField';
8
-
9
- export interface ColumnInfo {
10
- fields: React.ReactElement<CommandFormFieldProps<any>>[];
11
- }
12
-
13
- export interface CommandFormFieldsProps {
14
- fields?: React.ReactElement<CommandFormFieldProps<any>>[];
15
- columns?: ColumnInfo[];
16
- }
17
-
18
- // Separate component for each field to prevent re-rendering all fields
19
- const CommandFormFieldWrapper = ({ field, index }: { field: React.ReactElement<CommandFormFieldProps<any>>; index: number }) => {
20
- const context = useCommandFormContext<any>();
21
- const fieldProps = field.props as CommandFormFieldProps<any>;
22
- const propertyAccessor = fieldProps.value;
23
-
24
- // Get the property name from the accessor function
25
- const propertyName = propertyAccessor ? getPropertyName(propertyAccessor) : '';
26
-
27
- // Get the current value from the command instance
28
- const currentValue = propertyName ? (context.commandInstance as any)?.[propertyName] : undefined;
29
-
30
- // Get the error message for this field, if any
31
- const errorMessage = propertyName ? context.getFieldError(propertyName) : undefined;
32
-
33
- // Get the property descriptor for this field from the command instance
34
- const propertyDescriptor = propertyName && (context.commandInstance as any)?.propertyDescriptors
35
- ? (context.commandInstance as any).propertyDescriptors.find((pd: any) => pd.name === propertyName)
36
- : undefined;
37
-
38
- // Clone the field element with the current value and onChange handler
39
- const clonedField = React.cloneElement(field as React.ReactElement, {
40
- ...fieldProps,
41
- currentValue,
42
- propertyDescriptor,
43
- fieldName: propertyName,
44
- onValueChange: (value: unknown) => {
45
- if (propertyName) {
46
- const oldValue = currentValue;
47
-
48
- // Call custom field validator if provided
49
- if (context.onFieldValidate) {
50
- const validationError = context.onFieldValidate(context.commandInstance as any, propertyName, oldValue, value);
51
- context.setCustomFieldError(propertyName, validationError);
52
- }
53
-
54
- context.setCommandValues({ [propertyName]: value } as any);
55
-
56
- // Call field change callback if provided
57
- if (context.onFieldChange) {
58
- context.onFieldChange(context.commandInstance as any, propertyName, oldValue, value);
59
- }
60
- }
61
- fieldProps.onChange?.(value as any);
62
- },
63
- required: fieldProps.required ?? true,
64
- invalid: !!errorMessage
65
- } as any);
66
-
67
- const tooltipId = fieldProps.description ? `tooltip-${propertyName}-${index}` : undefined;
68
-
69
- return (
70
- <div style={{ width: '100%' }}>
71
- <div className="p-inputgroup" style={{ width: '100%' }}>
72
- {fieldProps.description && (
73
- <Tooltip target={`.${tooltipId}`} content={fieldProps.description} />
74
- )}
75
- {fieldProps.icon && (
76
- <span className={`p-inputgroup-addon ${tooltipId || ''}`}>
77
- {fieldProps.icon}
78
- </span>
79
- )}
80
- {clonedField}
81
- </div>
82
- {errorMessage && (
83
- <small className="p-error block mt-1">{errorMessage}</small>
84
- )}
85
- </div>
86
- );
87
- };
88
-
89
- export const CommandFormFields = (props: CommandFormFieldsProps) => {
90
- const { fields, columns } = props;
91
-
92
- // Render columns if provided
93
- if (columns && columns.length > 0) {
94
- return (
95
- <div className="card flex flex-column md:flex-row gap-3">
96
- {columns.map((column, columnIndex) => (
97
- <div key={`column-${columnIndex}`} className="flex flex-column gap-3 flex-1">
98
- {column.fields.map((field, index) => {
99
- const fieldProps = field.props as CommandFormFieldProps<any>;
100
- const propertyAccessor = fieldProps.value;
101
- const propertyName = propertyAccessor ? getPropertyName(propertyAccessor) : `field-${columnIndex}-${index}`;
102
-
103
- return (
104
- <CommandFormFieldWrapper
105
- key={propertyName}
106
- field={field}
107
- index={index}
108
- />
109
- );
110
- })}
111
- </div>
112
- ))}
113
- </div>
114
- );
115
- }
116
-
117
- // Render fields (single column layout)
118
- return (
119
- <div style={{ display: 'flex', flexDirection: 'column', gap: '1rem', width: '100%' }}>
120
- {(fields || []).map((field, index) => {
121
- const fieldProps = field.props as CommandFormFieldProps<any>;
122
- const propertyAccessor = fieldProps.value;
123
- const propertyName = propertyAccessor ? getPropertyName(propertyAccessor) : `field-${index}`;
124
-
125
- return (
126
- <CommandFormFieldWrapper
127
- key={propertyName}
128
- field={field}
129
- index={index}
130
- />
131
- );
132
- })}
133
- </div>
134
- );
135
- };
136
-
137
- // Helper function to extract property name from accessor function
138
- function getPropertyName<T>(accessor: (obj: T) => unknown): string {
139
- const fnStr = accessor.toString();
140
- const match = fnStr.match(/\.([a-zA-Z_$][a-zA-Z0-9_$]*)/);
141
- return match ? match[1] : '';
142
- }
@@ -1,57 +0,0 @@
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 { PropertyAccessor } from '@cratis/fundamentals';
5
- import { PropertyDescriptor } from '@cratis/arc/reflection';
6
- import React, { useState, useEffect } from 'react';
7
- import { InputText } from 'primereact/inputtext';
8
- import { useCommandFormContext } from './CommandForm';
9
-
10
- export interface DatePickerFieldProps<TCommand> {
11
- icon?: React.ReactElement;
12
- value: PropertyAccessor<TCommand>;
13
- onChange?: (value: unknown) => void;
14
- currentValue?: string;
15
- onValueChange?: (value: string) => void;
16
- required?: boolean;
17
- title?: string;
18
- description?: string;
19
- propertyDescriptor?: PropertyDescriptor;
20
- fieldName?: string;
21
- }
22
-
23
- export const DatePickerField = <TCommand,>(props: DatePickerFieldProps<TCommand>) => {
24
- const [localValue, setLocalValue] = useState(props.currentValue || '');
25
- const required = props.required ?? true;
26
- const isValid = !required || localValue.trim().length > 0;
27
- const { setFieldValidity } = useCommandFormContext();
28
-
29
- useEffect(() => {
30
- setLocalValue(props.currentValue || '');
31
- }, [props.currentValue]);
32
-
33
- useEffect(() => {
34
- if (props.fieldName) {
35
- setFieldValidity(props.fieldName, isValid);
36
- }
37
- }, [isValid, props.fieldName, setFieldValidity]);
38
-
39
- const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
40
- const newValue = e.target.value;
41
- setLocalValue(newValue);
42
- props.onValueChange?.(newValue);
43
- };
44
-
45
- return (
46
- <InputText
47
- type="date"
48
- value={localValue}
49
- onChange={handleChange}
50
- required={required}
51
- invalid={!isValid}
52
- placeholder={props.title}
53
- />
54
- );
55
- };
56
-
57
- DatePickerField.displayName = 'CommandFormField';