@strato-admin/cloudscape 0.1.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Admin.d.ts +6 -2
- package/dist/Admin.js +14 -8
- package/dist/RecordLink.js +5 -4
- package/dist/Settings.d.ts +17 -0
- package/dist/Settings.js +14 -0
- package/dist/button/BulkDeleteButton.d.ts +2 -1
- package/dist/button/BulkDeleteButton.js +17 -11
- package/dist/button/Button.d.ts +2 -1
- package/dist/button/CancelButton.d.ts +6 -0
- package/dist/button/CancelButton.js +10 -0
- package/dist/button/CreateButton.js +9 -8
- package/dist/button/DeleteButton.d.ts +13 -0
- package/dist/button/DeleteButton.js +36 -0
- package/dist/button/EditButton.d.ts +1 -1
- package/dist/button/EditButton.js +10 -10
- package/dist/button/SaveButton.js +2 -2
- package/dist/button/index.d.ts +2 -0
- package/dist/button/index.js +2 -0
- package/dist/collection-hooks/interfaces.d.ts +7 -3
- package/dist/collection-hooks/useCollection.d.ts +1 -1
- package/dist/collection-hooks/useCollection.js +15 -10
- package/dist/create/Create.d.ts +9 -17
- package/dist/create/Create.js +40 -12
- package/dist/create/CreateHeader.d.ts +2 -2
- package/dist/create/CreateHeader.js +4 -5
- package/dist/defaults.d.ts +6 -0
- package/dist/defaults.js +21 -0
- package/dist/detail/Detail.d.ts +33 -0
- package/dist/detail/Detail.js +22 -0
- package/dist/detail/DetailHeader.d.ts +11 -0
- package/dist/detail/{ShowHeader.js → DetailHeader.js} +7 -5
- package/dist/detail/DetailHub.d.ts +27 -0
- package/dist/detail/DetailHub.js +63 -0
- package/dist/detail/KeyValuePairs.d.ts +7 -1
- package/dist/detail/KeyValuePairs.js +14 -8
- package/dist/detail/index.d.ts +3 -2
- package/dist/detail/index.js +3 -2
- package/dist/edit/Edit.d.ts +8 -19
- package/dist/edit/Edit.js +48 -12
- package/dist/edit/EditHeader.d.ts +2 -2
- package/dist/edit/EditHeader.js +5 -4
- package/dist/field/ArrayField.d.ts +26 -10
- package/dist/field/ArrayField.js +38 -10
- package/dist/field/BadgeField.d.ts +1 -1
- package/dist/field/BadgeField.js +1 -1
- package/dist/field/BooleanField.d.ts +1 -1
- package/dist/field/BooleanField.js +2 -2
- package/dist/field/CurrencyField.d.ts +1 -1
- package/dist/field/CurrencyField.js +1 -1
- package/dist/field/DateField.d.ts +1 -1
- package/dist/field/DateField.js +1 -1
- package/dist/field/IdField.d.ts +1 -1
- package/dist/field/IdField.js +3 -3
- package/dist/field/NumberField.d.ts +1 -1
- package/dist/field/NumberField.js +1 -1
- package/dist/field/ReferenceField.d.ts +1 -1
- package/dist/field/ReferenceField.js +4 -2
- package/dist/field/ReferenceManyField.d.ts +35 -4
- package/dist/field/ReferenceManyField.js +17 -4
- package/dist/field/StatusIndicatorField.d.ts +1 -1
- package/dist/field/StatusIndicatorField.js +6 -5
- package/dist/field/TextField.d.ts +1 -1
- package/dist/field/TextField.js +1 -1
- package/dist/field/types.d.ts +9 -9
- package/dist/form/Form.d.ts +12 -2
- package/dist/form/Form.js +10 -16
- package/dist/form/index.d.ts +1 -1
- package/dist/form/index.js +1 -1
- package/dist/hooks/useSchemaFields.d.ts +22 -0
- package/dist/hooks/useSchemaFields.js +45 -0
- package/dist/i18n/Message.d.ts +15 -0
- package/dist/i18n/Message.js +19 -0
- package/dist/i18n/RecordMessage.d.ts +14 -0
- package/dist/i18n/RecordMessage.js +16 -0
- package/dist/i18n/index.d.ts +3 -0
- package/dist/i18n/index.js +2 -0
- package/dist/i18n/types.d.ts +19 -0
- package/dist/i18n/types.js +1 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.js +5 -1
- package/dist/input/ArrayInput.d.ts +33 -0
- package/dist/input/{AttributeEditor.js → ArrayInput.js} +18 -11
- package/dist/input/AutocompleteInput.d.ts +1 -1
- package/dist/input/AutocompleteInput.js +3 -3
- package/dist/input/BooleanInput.d.ts +6 -0
- package/dist/input/BooleanInput.js +23 -0
- package/dist/input/CommonInputProps.d.ts +6 -0
- package/dist/input/CommonInputProps.js +6 -0
- package/dist/input/FieldTitle.js +4 -4
- package/dist/input/FormField.js +12 -3
- package/dist/input/FormFieldContext.d.ts +1 -1
- package/dist/input/NumberInput.d.ts +1 -1
- package/dist/input/NumberInput.js +3 -3
- package/dist/input/ReferenceInput.d.ts +1 -1
- package/dist/input/ReferenceInput.js +22 -12
- package/dist/input/SelectInput.d.ts +1 -1
- package/dist/input/SelectInput.js +3 -3
- package/dist/input/SliderInput.d.ts +1 -1
- package/dist/input/SliderInput.js +4 -4
- package/dist/input/TextAreaInput.d.ts +1 -1
- package/dist/input/TextAreaInput.js +3 -3
- package/dist/input/TextInput.d.ts +1 -1
- package/dist/input/TextInput.js +6 -12
- package/dist/input/index.d.ts +2 -1
- package/dist/input/index.js +2 -1
- package/dist/input/types.d.ts +33 -2
- package/dist/layout/AppLayout.js +6 -3
- package/dist/layout/Notifications.d.ts +1 -0
- package/dist/layout/Notifications.js +51 -0
- package/dist/layout/Ready.d.ts +6 -0
- package/dist/layout/Ready.js +24 -0
- package/dist/layout/TopNavigation.d.ts +4 -2
- package/dist/layout/TopNavigation.js +7 -7
- package/dist/layout/index.d.ts +2 -0
- package/dist/layout/index.js +2 -0
- package/dist/list/Cards.d.ts +31 -4
- package/dist/list/Cards.js +81 -10
- package/dist/list/List.d.ts +9 -12
- package/dist/list/List.js +41 -11
- package/dist/list/Table.d.ts +8 -4
- package/dist/list/Table.js +55 -55
- package/dist/list/TableHeader.d.ts +2 -2
- package/dist/list/TableHeader.js +4 -5
- package/dist/theme/ThemeManager.js +1 -1
- package/package.json +8 -5
- package/src/Admin.tsx +35 -18
- package/src/RecordLink.stories.tsx +1 -1
- package/src/RecordLink.tsx +5 -4
- package/src/Settings.tsx +16 -0
- package/src/__mocks__/ra-core.tsx +83 -0
- package/src/__mocks__/strato-core.tsx +36 -42
- package/src/button/BulkDeleteButton.test.tsx +17 -4
- package/src/button/BulkDeleteButton.tsx +24 -29
- package/src/button/Button.tsx +31 -2
- package/src/button/CancelButton.tsx +20 -0
- package/src/button/CreateButton.tsx +12 -10
- package/src/button/DeleteButton.tsx +96 -0
- package/src/button/EditButton.tsx +13 -12
- package/src/button/SaveButton.tsx +2 -3
- package/src/button/index.ts +2 -0
- package/src/collection-hooks/interfaces.ts +7 -3
- package/src/collection-hooks/useCollection.test.ts +115 -2
- package/src/collection-hooks/useCollection.ts +15 -10
- package/src/create/Create.test.tsx +3 -3
- package/src/create/Create.tsx +68 -37
- package/src/create/CreateHeader.tsx +6 -10
- package/src/defaults.tsx +28 -0
- package/src/detail/Detail-CollectionFields.test.tsx +84 -0
- package/src/detail/Detail.test.tsx +91 -0
- package/src/detail/Detail.tsx +48 -0
- package/src/detail/{ShowHeader.test.tsx → DetailHeader.test.tsx} +11 -9
- package/src/detail/DetailHeader.tsx +42 -0
- package/src/detail/DetailHub.tsx +88 -0
- package/src/detail/KeyValuePairs.test.tsx +2 -2
- package/src/detail/KeyValuePairs.tsx +25 -18
- package/src/detail/index.ts +3 -2
- package/src/edit/Edit.test.tsx +7 -5
- package/src/edit/Edit.tsx +92 -40
- package/src/edit/EditHeader.tsx +7 -5
- package/src/field/ArrayField.tsx +57 -11
- package/src/field/BadgeField.tsx +2 -3
- package/src/field/BooleanField.test.tsx +2 -3
- package/src/field/BooleanField.tsx +3 -3
- package/src/field/CurrencyField.tsx +1 -1
- package/src/field/DateField.tsx +1 -1
- package/src/field/IdField.test.tsx +8 -20
- package/src/field/IdField.tsx +5 -20
- package/src/field/NumberField.tsx +1 -1
- package/src/field/ReferenceField.test.tsx +15 -6
- package/src/field/ReferenceField.tsx +10 -7
- package/src/field/ReferenceManyField.test.tsx +55 -10
- package/src/field/ReferenceManyField.tsx +84 -13
- package/src/field/StatusIndicatorField.test.tsx +7 -21
- package/src/field/StatusIndicatorField.tsx +8 -20
- package/src/field/TextField.tsx +1 -1
- package/src/field/types.ts +12 -13
- package/src/form/Form.test.tsx +8 -4
- package/src/form/Form.tsx +24 -19
- package/src/form/index.ts +1 -1
- package/src/hooks/useSchemaFields.ts +89 -0
- package/src/i18n/Message.tsx +22 -0
- package/src/i18n/RecordMessage.tsx +22 -0
- package/src/i18n/index.ts +3 -0
- package/src/i18n/types.ts +19 -0
- package/src/index.ts +5 -1
- package/src/input/ArrayInput.test.tsx +81 -0
- package/src/input/{AttributeEditor.tsx → ArrayInput.tsx} +36 -18
- package/src/input/AutocompleteInput.test.tsx +2 -4
- package/src/input/AutocompleteInput.tsx +9 -11
- package/src/input/BooleanInput.tsx +42 -0
- package/src/input/CommonInputProps.tsx +8 -0
- package/src/input/FieldTitle.tsx +3 -15
- package/src/input/FormField.tsx +78 -67
- package/src/input/FormFieldContext.ts +1 -1
- package/src/input/NumberInput.tsx +10 -7
- package/src/input/ReferenceInput.test.tsx +12 -2
- package/src/input/ReferenceInput.tsx +32 -14
- package/src/input/SelectInput.tsx +14 -17
- package/src/input/SliderInput.test.tsx +2 -3
- package/src/input/SliderInput.tsx +48 -38
- package/src/input/TextAreaInput.tsx +10 -6
- package/src/input/TextInput.test.tsx +2 -4
- package/src/input/TextInput.tsx +35 -20
- package/src/input/index.ts +2 -1
- package/src/input/types.ts +40 -8
- package/src/layout/AppLayout.test.tsx +23 -3
- package/src/layout/AppLayout.tsx +11 -8
- package/src/layout/Notifications.test.tsx +102 -0
- package/src/layout/Notifications.tsx +61 -0
- package/src/layout/Ready.tsx +123 -0
- package/src/layout/TopNavigation.test.tsx +2 -3
- package/src/layout/TopNavigation.tsx +9 -8
- package/src/layout/index.ts +2 -0
- package/src/list/Cards.test.tsx +320 -0
- package/src/list/Cards.tsx +146 -16
- package/src/list/List.tsx +87 -26
- package/src/list/Table.test.tsx +40 -5
- package/src/list/Table.tsx +89 -98
- package/src/list/TableHeader.test.tsx +15 -11
- package/src/list/TableHeader.tsx +6 -8
- package/src/theme/ThemeManager.tsx +1 -1
- package/dist/__mocks__/strato-core.js +0 -50
- package/dist/__mocks__to__delete/strato-core.js +0 -50
- package/dist/detail/Show.d.ts +0 -39
- package/dist/detail/Show.js +0 -40
- package/dist/detail/ShowHeader.d.ts +0 -7
- package/dist/input/AttributeEditor.d.ts +0 -25
- package/src/detail/Show.test.tsx +0 -96
- package/src/detail/Show.tsx +0 -104
- package/src/detail/ShowHeader.tsx +0 -35
- package/src/input/AttributeEditor.test.tsx +0 -147
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import React, { useMemo } from 'react';
|
|
3
|
-
import { useInput, RecordContextProvider, useResourceContext } from '@strato-admin/core';
|
|
3
|
+
import { useInput, RecordContextProvider, useResourceContext } from '@strato-admin/ra-core';
|
|
4
4
|
import { useFieldArray, useFormContext } from 'react-hook-form';
|
|
5
5
|
import CloudscapeAttributeEditor from '@cloudscape-design/components/attribute-editor';
|
|
6
6
|
import Box from '@cloudscape-design/components/box';
|
|
@@ -10,11 +10,14 @@ import FormField from './FormField';
|
|
|
10
10
|
import { FormFieldContext, useFormFieldContext } from './FormFieldContext';
|
|
11
11
|
export const Item = (_props) => {
|
|
12
12
|
// This is a placeholder component used to collect props.
|
|
13
|
-
// The actual rendering is handled by the
|
|
13
|
+
// The actual rendering is handled by the ArrayInput.
|
|
14
14
|
return null;
|
|
15
15
|
};
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
/**
|
|
17
|
+
* ArrayInput component that uses Cloudscape's AttributeEditor to edit arrays of objects.
|
|
18
|
+
*/
|
|
19
|
+
export const ArrayInput = (props) => {
|
|
20
|
+
const { children, label, source: sourceProp, validate, defaultValue, addButtonText = 'Add item', removeButtonText = 'Remove item', empty, disableAddButton, hideAddButton, gridLayout, ...rest } = props;
|
|
18
21
|
const { control } = useFormContext();
|
|
19
22
|
const contextValue = useFormFieldContext();
|
|
20
23
|
const resource = useResourceContext();
|
|
@@ -45,12 +48,16 @@ export const AttributeEditor = (props) => {
|
|
|
45
48
|
const childSource = childProps.source;
|
|
46
49
|
const childLabel = childProps.label;
|
|
47
50
|
const childValidate = childProps.validate;
|
|
51
|
+
const childDescription = childProps.description;
|
|
52
|
+
const childInfo = childProps.info;
|
|
48
53
|
// Determine if the field is required by checking validators
|
|
49
54
|
const isRequired = Array.isArray(childValidate)
|
|
50
|
-
? childValidate.some((v) => v.isRequired)
|
|
51
|
-
: childValidate?.isRequired || childProps.isRequired;
|
|
55
|
+
? childValidate.some((v) => v.isRequired || v.name === 'required')
|
|
56
|
+
: childValidate?.isRequired || childValidate?.name === 'required' || childProps.isRequired;
|
|
52
57
|
return {
|
|
53
|
-
label: _jsx(FieldTitle, { label: childLabel, source: childSource, resource: resource, isRequired: isRequired }),
|
|
58
|
+
label: (_jsx(FieldTitle, { label: childLabel, source: childSource, resource: childProps.resource || resource, isRequired: isRequired })),
|
|
59
|
+
description: childDescription,
|
|
60
|
+
info: childInfo,
|
|
54
61
|
control: (item, index) => {
|
|
55
62
|
const prefixedSource = `${source}.${index}.${childSource}`;
|
|
56
63
|
let content;
|
|
@@ -65,16 +72,16 @@ export const AttributeEditor = (props) => {
|
|
|
65
72
|
label: false,
|
|
66
73
|
});
|
|
67
74
|
}
|
|
68
|
-
return
|
|
75
|
+
return _jsx(RecordContextProvider, { value: item, children: content });
|
|
69
76
|
},
|
|
70
77
|
};
|
|
71
78
|
})?.filter(Boolean);
|
|
72
79
|
}, [children, source, resource]);
|
|
73
|
-
const inner = (_jsx(FormFieldContext.Provider, { value: undefined, children: _jsx(CloudscapeAttributeEditor, { items: fields, definition: definition, onAddButtonClick: handleAdd, onRemoveButtonClick: ({ detail: { itemIndex } }) => handleRemove(itemIndex), empty: empty || (_jsx(Box, { textAlign: "center", color: "inherit", children: "No items added yet." })), addButtonText: addButtonText, removeButtonText: removeButtonText, disableAddButton: disableAddButton, hideAddButton: hideAddButton }) }));
|
|
80
|
+
const inner = (_jsx(FormFieldContext.Provider, { value: undefined, children: _jsx(CloudscapeAttributeEditor, { items: fields, definition: definition, onAddButtonClick: handleAdd, onRemoveButtonClick: ({ detail: { itemIndex } }) => handleRemove(itemIndex), empty: empty || (_jsx(Box, { textAlign: "center", color: "inherit", children: "No items added yet." })), addButtonText: addButtonText, removeButtonText: removeButtonText, disableAddButton: disableAddButton, hideAddButton: hideAddButton, gridLayout: gridLayout }) }));
|
|
74
81
|
if (contextValue) {
|
|
75
82
|
return inner;
|
|
76
83
|
}
|
|
77
84
|
return (_jsx(FormFieldContext.Provider, { value: { ...inputState, source }, children: _jsx(FormField, { ...props, source: source, children: inner }) }));
|
|
78
85
|
};
|
|
79
|
-
|
|
80
|
-
export default
|
|
86
|
+
ArrayInput.Item = Item;
|
|
87
|
+
export default ArrayInput;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AutosuggestProps as CloudscapeAutosuggestProps } from '@cloudscape-design/components/autosuggest';
|
|
2
2
|
import { InputProps } from './types';
|
|
3
|
-
export interface AutocompleteInputProps extends
|
|
3
|
+
export interface AutocompleteInputProps extends InputProps, Pick<CloudscapeAutosuggestProps, 'placeholder' | 'disabled' | 'readOnly' | 'enteredTextLabel'> {
|
|
4
4
|
choices?: Array<{
|
|
5
5
|
id: string | number;
|
|
6
6
|
[key: string]: any;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import React, { useState, useEffect, useMemo } from 'react';
|
|
3
|
-
import { useInput, useResourceContext, useChoicesContext, useGetRecordRepresentation
|
|
3
|
+
import { useInput, useResourceContext, useChoicesContext, useGetRecordRepresentation } from '@strato-admin/ra-core';
|
|
4
4
|
import CloudscapeAutosuggest from '@cloudscape-design/components/autosuggest';
|
|
5
5
|
import { FormField } from './FormField';
|
|
6
6
|
import { FormFieldContext, useFormFieldContext } from './FormFieldContext';
|
|
7
7
|
export const AutocompleteInput = (props) => {
|
|
8
|
-
const { label, source, defaultValue, validate, choices: choicesProp, ...rest } = props;
|
|
8
|
+
const { label, source, defaultValue, validate, choices: choicesProp, placeholder, disabled, readOnly, enteredTextLabel, ...rest } = props;
|
|
9
9
|
const resource = useResourceContext();
|
|
10
10
|
const { allChoices, isPending, setFilters } = useChoicesContext(props);
|
|
11
11
|
const getRecordRepresentation = useGetRecordRepresentation(resource);
|
|
@@ -58,7 +58,7 @@ export const AutocompleteInput = (props) => {
|
|
|
58
58
|
const handleSelect = ({ detail }) => {
|
|
59
59
|
field.onChange(detail.value);
|
|
60
60
|
};
|
|
61
|
-
const inner = (_jsx(CloudscapeAutosuggest, {
|
|
61
|
+
const inner = (_jsx(CloudscapeAutosuggest, { id: id, placeholder: placeholder, disabled: disabled, readOnly: readOnly, enteredTextLabel: enteredTextLabel, options: options, value: filterValue, statusType: isPending ? 'loading' : 'finished', expandToViewport: true, onChange: handleChange, onSelect: handleSelect, onBlur: () => field.onBlur() }));
|
|
62
62
|
if (context) {
|
|
63
63
|
return inner;
|
|
64
64
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { ToggleProps } from '@cloudscape-design/components/toggle';
|
|
2
|
+
import { InputProps } from './types';
|
|
3
|
+
export interface BooleanInputProps extends InputProps, Pick<ToggleProps, 'disabled' | 'readOnly' | 'children' | 'ariaLabel'> {
|
|
4
|
+
}
|
|
5
|
+
export declare const BooleanInput: (props: BooleanInputProps) => import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
export default BooleanInput;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useInput } from '@strato-admin/ra-core';
|
|
3
|
+
import Toggle from '@cloudscape-design/components/toggle';
|
|
4
|
+
import { FormField } from './FormField';
|
|
5
|
+
import { FormFieldContext, useFormFieldContext } from './FormFieldContext';
|
|
6
|
+
export const BooleanInput = (props) => {
|
|
7
|
+
const { label, source, defaultValue, validate, disabled, readOnly, children, ariaLabel, ...rest } = props;
|
|
8
|
+
const context = useFormFieldContext();
|
|
9
|
+
const inputState = context ??
|
|
10
|
+
useInput({
|
|
11
|
+
source,
|
|
12
|
+
defaultValue: defaultValue ?? false,
|
|
13
|
+
validate,
|
|
14
|
+
...rest,
|
|
15
|
+
});
|
|
16
|
+
const { field } = inputState;
|
|
17
|
+
const inner = (_jsx(Toggle, { disabled: disabled, readOnly: readOnly, ariaLabel: ariaLabel, checked: !!field.value, onChange: (event) => field.onChange(event.detail.checked), children: children }));
|
|
18
|
+
if (context) {
|
|
19
|
+
return inner;
|
|
20
|
+
}
|
|
21
|
+
return (_jsx(FormFieldContext.Provider, { value: inputState, children: _jsx(FormField, { ...props, children: inner }) }));
|
|
22
|
+
};
|
|
23
|
+
export default BooleanInput;
|
package/dist/input/FieldTitle.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { jsx as _jsx
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import React from 'react';
|
|
3
|
-
import { useTranslate, useResourceDefinitions } from '@strato-admin/core';
|
|
3
|
+
import { useTranslate, useResourceDefinitions } from '@strato-admin/ra-core';
|
|
4
4
|
import { humanize } from 'inflection';
|
|
5
5
|
export const FieldTitle = (props) => {
|
|
6
|
-
const { resource, source, label
|
|
6
|
+
const { resource, source, label } = props;
|
|
7
7
|
const translate = useTranslate();
|
|
8
8
|
const definitions = useResourceDefinitions();
|
|
9
9
|
const labelString = React.useMemo(() => {
|
|
@@ -24,6 +24,6 @@ export const FieldTitle = (props) => {
|
|
|
24
24
|
_: defaultLabel,
|
|
25
25
|
});
|
|
26
26
|
}, [label, translate, resource, source, definitions]);
|
|
27
|
-
return (
|
|
27
|
+
return _jsx("span", { children: labelString });
|
|
28
28
|
};
|
|
29
29
|
export default FieldTitle;
|
package/dist/input/FormField.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import React from 'react';
|
|
3
|
-
import { useInput, useResourceContext, ValidationError } from '@strato-admin/core';
|
|
3
|
+
import { useInput, useResourceContext, ValidationError, useTranslate } from '@strato-admin/ra-core';
|
|
4
4
|
import CloudscapeFormField from '@cloudscape-design/components/form-field';
|
|
5
5
|
import { FieldTitle } from './FieldTitle';
|
|
6
6
|
import { FormFieldContext, useFormFieldContext } from './FormFieldContext';
|
|
7
7
|
export const FormField = (props) => {
|
|
8
8
|
const { children, label, source, defaultValue, validate, description, constraintText, info, secondaryControl, stretch, i18nStrings, ...rest } = props;
|
|
9
9
|
const resource = useResourceContext();
|
|
10
|
+
const translate = useTranslate();
|
|
10
11
|
const context = useFormFieldContext();
|
|
11
12
|
const inputState = context ??
|
|
12
13
|
useInput({
|
|
@@ -26,7 +27,15 @@ export const FormField = (props) => {
|
|
|
26
27
|
const { id, fieldState: { isTouched, invalid, error }, formState: { isSubmitted }, isRequired, } = inputState;
|
|
27
28
|
const errorToProcess = error?.message || error?.root?.message;
|
|
28
29
|
const errorText = (isTouched || isSubmitted) && invalid && typeof errorToProcess === 'string' ? (_jsx(ValidationError, { error: errorToProcess })) : undefined;
|
|
29
|
-
const
|
|
30
|
+
const finalConstraintText = React.useMemo(() => {
|
|
31
|
+
if (isRequired)
|
|
32
|
+
return constraintText;
|
|
33
|
+
const optionalLabel = `(${translate('optional')})`;
|
|
34
|
+
if (!constraintText)
|
|
35
|
+
return optionalLabel;
|
|
36
|
+
return (_jsxs(_Fragment, { children: [constraintText, " (", optionalLabel, ")"] }));
|
|
37
|
+
}, [isRequired, constraintText, translate]);
|
|
38
|
+
const content = (_jsx(CloudscapeFormField, { id: id, label: label === false ? undefined : (_jsx(FieldTitle, { label: label, source: source, resource: resource, isRequired: isRequired })), description: label === false ? undefined : description, constraintText: label === false ? undefined : finalConstraintText, info: info, secondaryControl: secondaryControl, stretch: stretch, i18nStrings: i18nStrings, errorText: errorText, children: children }));
|
|
30
39
|
if (context) {
|
|
31
40
|
return content;
|
|
32
41
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { InputProps as CloudscapeInputProps } from '@cloudscape-design/components/input';
|
|
2
2
|
import { InputProps } from './types';
|
|
3
|
-
export interface NumberInputProps extends
|
|
3
|
+
export interface NumberInputProps extends InputProps, Pick<CloudscapeInputProps, 'placeholder' | 'disabled' | 'readOnly' | 'autoFocus' | 'step'> {
|
|
4
4
|
type?: CloudscapeInputProps['type'];
|
|
5
5
|
}
|
|
6
6
|
export declare const NumberInput: (props: NumberInputProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useInput } from '@strato-admin/core';
|
|
2
|
+
import { useInput } from '@strato-admin/ra-core';
|
|
3
3
|
import CloudscapeInput from '@cloudscape-design/components/input';
|
|
4
4
|
import { FormField } from './FormField';
|
|
5
5
|
import { FormFieldContext, useFormFieldContext } from './FormFieldContext';
|
|
6
6
|
export const NumberInput = (props) => {
|
|
7
|
-
const { label, source, defaultValue, validate, type = 'number', ...rest } = props;
|
|
7
|
+
const { label, source, defaultValue, validate, type = 'number', placeholder, disabled, readOnly, autoFocus, step, ...rest } = props;
|
|
8
8
|
const context = useFormFieldContext();
|
|
9
9
|
const inputState = context ??
|
|
10
10
|
useInput({
|
|
@@ -18,7 +18,7 @@ export const NumberInput = (props) => {
|
|
|
18
18
|
const floatValue = parseFloat(value);
|
|
19
19
|
field.onChange(isNaN(floatValue) ? null : floatValue);
|
|
20
20
|
};
|
|
21
|
-
const inner = (_jsx(CloudscapeInput, { ...
|
|
21
|
+
const inner = (_jsx(CloudscapeInput, { ...field, id: id, type: type, placeholder: placeholder, disabled: disabled, readOnly: readOnly, autoFocus: autoFocus, step: step, value: field.value?.toString() || '', onChange: (event) => handleChange(event.detail.value), onBlur: () => field.onBlur() }));
|
|
22
22
|
if (context) {
|
|
23
23
|
return inner;
|
|
24
24
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { type ReferenceInputBaseProps } from '@strato-admin/core';
|
|
1
|
+
import { type ReferenceInputBaseProps } from '@strato-admin/ra-core';
|
|
2
2
|
export declare const ReferenceInput: (props: ReferenceInputBaseProps) => import("react/jsx-runtime").JSX.Element;
|
|
3
3
|
export default ReferenceInput;
|
|
@@ -1,25 +1,35 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import React from 'react';
|
|
3
|
-
import { ReferenceInputBase } from '@strato-admin/core';
|
|
4
|
-
import {
|
|
3
|
+
import { ReferenceInputBase, useInput } from '@strato-admin/ra-core';
|
|
4
|
+
import { FormField } from './FormField';
|
|
5
|
+
import { FormFieldContext, useFormFieldContext } from './FormFieldContext';
|
|
5
6
|
import { AutocompleteInput } from './AutocompleteInput';
|
|
6
7
|
export const ReferenceInput = (props) => {
|
|
7
|
-
const { children, source: sourceProp, reference, ...rest } = props;
|
|
8
|
+
const { children, source: sourceProp, reference, isRequired, validate, defaultValue, label, ...rest } = props;
|
|
8
9
|
const context = useFormFieldContext();
|
|
9
10
|
// If we have a context, we use the source from it.
|
|
10
11
|
const source = sourceProp || context?.source;
|
|
11
12
|
if (!source) {
|
|
12
13
|
throw new Error('ReferenceInput requires a source prop or a parent FormField Master');
|
|
13
14
|
}
|
|
15
|
+
const inputState = context ??
|
|
16
|
+
useInput({
|
|
17
|
+
source,
|
|
18
|
+
defaultValue,
|
|
19
|
+
validate,
|
|
20
|
+
isRequired,
|
|
21
|
+
...rest,
|
|
22
|
+
});
|
|
14
23
|
const finalChildren = children || _jsx(AutocompleteInput, { source: source });
|
|
15
|
-
const inner = (_jsx(ReferenceInputBase, { source: source, reference: reference, ...rest, children: React.isValidElement(finalChildren)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
const inner = (_jsx(ReferenceInputBase, { source: source, reference: reference, isRequired: isRequired, ...rest, children: _jsx(FormFieldContext.Provider, { value: inputState, children: React.isValidElement(finalChildren)
|
|
25
|
+
? React.cloneElement(finalChildren, {
|
|
26
|
+
source,
|
|
27
|
+
isRequired,
|
|
28
|
+
})
|
|
29
|
+
: finalChildren }) }));
|
|
30
|
+
if (context) {
|
|
31
|
+
return inner;
|
|
32
|
+
}
|
|
33
|
+
return (_jsx(FormFieldContext.Provider, { value: inputState, children: _jsx(FormField, { ...props, children: inner }) }));
|
|
24
34
|
};
|
|
25
35
|
export default ReferenceInput;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { SelectProps as CloudscapeSelectProps } from '@cloudscape-design/components/select';
|
|
2
2
|
import { InputProps } from './types';
|
|
3
|
-
export interface SelectInputProps extends
|
|
3
|
+
export interface SelectInputProps extends InputProps, Pick<CloudscapeSelectProps, 'filteringType' | 'placeholder' | 'disabled' | 'readOnly'> {
|
|
4
4
|
choices?: Array<{
|
|
5
5
|
id: string | number;
|
|
6
6
|
[key: string]: any;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import React from 'react';
|
|
3
|
-
import { useInput, useResourceContext, useChoicesContext, useGetRecordRepresentation
|
|
3
|
+
import { useInput, useResourceContext, useChoicesContext, useGetRecordRepresentation } from '@strato-admin/ra-core';
|
|
4
4
|
import CloudscapeSelect from '@cloudscape-design/components/select';
|
|
5
5
|
import { FormField } from './FormField';
|
|
6
6
|
import { FormFieldContext, useFormFieldContext } from './FormFieldContext';
|
|
7
7
|
export const SelectInput = (props) => {
|
|
8
|
-
const { label, source, defaultValue, validate, choices: choicesProp, emptyText = '-', ...rest } = props;
|
|
8
|
+
const { label, source, defaultValue, validate, choices: choicesProp, emptyText = '-', filteringType, placeholder, disabled, readOnly, ...rest } = props;
|
|
9
9
|
const resource = useResourceContext();
|
|
10
10
|
const { allChoices, isPending } = useChoicesContext(props);
|
|
11
11
|
const getRecordRepresentation = useGetRecordRepresentation(resource);
|
|
@@ -35,7 +35,7 @@ export const SelectInput = (props) => {
|
|
|
35
35
|
}
|
|
36
36
|
return option.value === String(field.value);
|
|
37
37
|
}) || null;
|
|
38
|
-
const inner = (_jsx(CloudscapeSelect, {
|
|
38
|
+
const inner = (_jsx(CloudscapeSelect, { id: id, filteringType: filteringType, placeholder: placeholder, disabled: disabled, readOnly: readOnly, options: options, selectedOption: selectedOption, statusType: isPending ? 'loading' : 'finished', expandToViewport: true, onChange: ({ detail }) => {
|
|
39
39
|
const value = detail.selectedOption.value === '__EMPTY__' ? null : detail.selectedOption.value;
|
|
40
40
|
field.onChange(value);
|
|
41
41
|
}, onBlur: () => field.onBlur() }));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { SliderProps as CloudscapeSliderProps } from '@cloudscape-design/components/slider';
|
|
2
2
|
import { InputProps } from './types';
|
|
3
|
-
export interface SliderInputProps extends
|
|
3
|
+
export interface SliderInputProps extends InputProps, Partial<Pick<CloudscapeSliderProps, 'min' | 'max'>>, Pick<CloudscapeSliderProps, 'step' | 'disabled' | 'readOnly' | 'valueFormatter' | 'tickMarks' | 'referenceValues' | 'hideFillLine' | 'ariaLabel' | 'ariaDescription'> {
|
|
4
4
|
}
|
|
5
5
|
export declare const SliderInput: (props: SliderInputProps) => import("react/jsx-runtime").JSX.Element;
|
|
6
6
|
export default SliderInput;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useInput } from '@strato-admin/core';
|
|
2
|
+
import { useInput } from '@strato-admin/ra-core';
|
|
3
3
|
import CloudscapeSlider from '@cloudscape-design/components/slider';
|
|
4
4
|
import { FormField } from './FormField';
|
|
5
5
|
import { FormFieldContext, useFormFieldContext } from './FormFieldContext';
|
|
6
6
|
export const SliderInput = (props) => {
|
|
7
|
-
const { label, source, defaultValue, validate, ...rest } = props;
|
|
7
|
+
const { label, source, defaultValue, validate, min, max, step, disabled, readOnly, valueFormatter, tickMarks, referenceValues, hideFillLine, ariaLabel, ariaDescription, ...rest } = props;
|
|
8
8
|
const context = useFormFieldContext();
|
|
9
9
|
const inputState = context ??
|
|
10
10
|
useInput({
|
|
@@ -15,8 +15,8 @@ export const SliderInput = (props) => {
|
|
|
15
15
|
});
|
|
16
16
|
const { id, field } = inputState;
|
|
17
17
|
// Cloudscape Slider requires a number value
|
|
18
|
-
const value = typeof field.value === 'number' ? field.value :
|
|
19
|
-
const inner = (_jsx(CloudscapeSlider, {
|
|
18
|
+
const value = typeof field.value === 'number' ? field.value : (min ?? 0);
|
|
19
|
+
const inner = (_jsx(CloudscapeSlider, { id: id, min: min ?? 0, max: max ?? 100, step: step, disabled: disabled, readOnly: readOnly, valueFormatter: valueFormatter, tickMarks: tickMarks, referenceValues: referenceValues, hideFillLine: hideFillLine, ariaLabel: ariaLabel, ariaDescription: ariaDescription, value: value, onChange: (event) => field.onChange(event.detail.value) }));
|
|
20
20
|
if (context) {
|
|
21
21
|
return inner;
|
|
22
22
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { TextareaProps as CloudscapeTextareaProps } from '@cloudscape-design/components/textarea';
|
|
2
2
|
import { InputProps } from './types';
|
|
3
|
-
export interface TextAreaInputProps extends
|
|
3
|
+
export interface TextAreaInputProps extends InputProps, Pick<CloudscapeTextareaProps, 'placeholder' | 'disabled' | 'readOnly' | 'rows' | 'autoFocus' | 'spellcheck'> {
|
|
4
4
|
}
|
|
5
5
|
export declare const TextAreaInput: (props: TextAreaInputProps) => import("react/jsx-runtime").JSX.Element;
|
|
6
6
|
export default TextAreaInput;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useInput } from '@strato-admin/core';
|
|
2
|
+
import { useInput } from '@strato-admin/ra-core';
|
|
3
3
|
import CloudscapeTextarea from '@cloudscape-design/components/textarea';
|
|
4
4
|
import { FormField } from './FormField';
|
|
5
5
|
import { FormFieldContext, useFormFieldContext } from './FormFieldContext';
|
|
6
6
|
export const TextAreaInput = (props) => {
|
|
7
|
-
const { label, source, defaultValue, validate, ...rest } = props;
|
|
7
|
+
const { label, source, defaultValue, validate, placeholder, disabled, readOnly, rows, autoFocus, spellcheck, ...rest } = props;
|
|
8
8
|
const context = useFormFieldContext();
|
|
9
9
|
const inputState = context ??
|
|
10
10
|
useInput({
|
|
@@ -14,7 +14,7 @@ export const TextAreaInput = (props) => {
|
|
|
14
14
|
...rest,
|
|
15
15
|
});
|
|
16
16
|
const { id, field } = inputState;
|
|
17
|
-
const inner = (_jsx(CloudscapeTextarea, { ...
|
|
17
|
+
const inner = (_jsx(CloudscapeTextarea, { ...field, id: id, placeholder: placeholder, disabled: disabled, readOnly: readOnly, rows: rows, autoFocus: autoFocus, spellcheck: spellcheck, value: field.value || '', onChange: (event) => field.onChange(event.detail.value), onBlur: () => field.onBlur() }));
|
|
18
18
|
if (context) {
|
|
19
19
|
return inner;
|
|
20
20
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { InputProps as CloudscapeInputProps } from '@cloudscape-design/components/input';
|
|
2
2
|
import { InputProps } from './types';
|
|
3
|
-
export interface TextInputProps extends
|
|
3
|
+
export interface TextInputProps extends InputProps, Pick<CloudscapeInputProps, 'placeholder' | 'disabled' | 'readOnly' | 'autoFocus' | 'autoComplete' | 'spellcheck' | 'inputMode'> {
|
|
4
4
|
type?: CloudscapeInputProps['type'];
|
|
5
5
|
}
|
|
6
6
|
export declare const TextInput: (props: TextInputProps) => import("react/jsx-runtime").JSX.Element;
|
package/dist/input/TextInput.js
CHANGED
|
@@ -1,23 +1,17 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useInput } from '@strato-admin/core';
|
|
2
|
+
import { useInput } from '@strato-admin/ra-core';
|
|
3
3
|
import CloudscapeInput from '@cloudscape-design/components/input';
|
|
4
4
|
import { FormField } from './FormField';
|
|
5
5
|
import { FormFieldContext, useFormFieldContext } from './FormFieldContext';
|
|
6
6
|
export const TextInput = (props) => {
|
|
7
|
-
const { label, source, defaultValue, validate, type = 'text',
|
|
7
|
+
const { label, source, defaultValue, validate, description, constraintText, info, secondaryControl, type = 'text', placeholder, disabled, readOnly, autoFocus, autoComplete, spellcheck, inputMode, } = props;
|
|
8
8
|
const context = useFormFieldContext();
|
|
9
|
-
const inputState = context ??
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
defaultValue,
|
|
13
|
-
validate,
|
|
14
|
-
...rest,
|
|
15
|
-
});
|
|
16
|
-
const { id, field } = inputState;
|
|
17
|
-
const inner = (_jsx(CloudscapeInput, { ...rest, ...field, id: id, type: type, value: field.value || '', onChange: (event) => field.onChange(event.detail.value), onBlur: () => field.onBlur() }));
|
|
9
|
+
const inputState = context ?? useInput({ source, defaultValue, validate });
|
|
10
|
+
const { field } = inputState;
|
|
11
|
+
const inner = (_jsx(CloudscapeInput, { ...field, type: type, placeholder: placeholder, disabled: disabled, readOnly: readOnly, autoFocus: autoFocus, autoComplete: autoComplete, spellcheck: spellcheck, inputMode: inputMode, value: field.value || '', onChange: (event) => field.onChange(event.detail.value), onBlur: () => field.onBlur() }));
|
|
18
12
|
if (context) {
|
|
19
13
|
return inner;
|
|
20
14
|
}
|
|
21
|
-
return (_jsx(FormFieldContext.Provider, { value: inputState, children: _jsx(FormField, {
|
|
15
|
+
return (_jsx(FormFieldContext.Provider, { value: inputState, children: _jsx(FormField, { source: source, label: label, description: description, constraintText: constraintText, info: info, secondaryControl: secondaryControl, children: inner }) }));
|
|
22
16
|
};
|
|
23
17
|
export default TextInput;
|
package/dist/input/index.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
export * from './types';
|
|
2
|
+
export * from './BooleanInput';
|
|
2
3
|
export * from './TextInput';
|
|
3
4
|
export * from './TextAreaInput';
|
|
4
5
|
export * from './NumberInput';
|
|
5
|
-
export * from './
|
|
6
|
+
export * from './ArrayInput';
|
|
6
7
|
export * from './SelectInput';
|
|
7
8
|
export * from './AutocompleteInput';
|
|
8
9
|
export * from './ReferenceInput';
|
package/dist/input/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
export * from './types';
|
|
2
|
+
export * from './BooleanInput';
|
|
2
3
|
export * from './TextInput';
|
|
3
4
|
export * from './TextAreaInput';
|
|
4
5
|
export * from './NumberInput';
|
|
5
|
-
export * from './
|
|
6
|
+
export * from './ArrayInput';
|
|
6
7
|
export * from './SelectInput';
|
|
7
8
|
export * from './AutocompleteInput';
|
|
8
9
|
export * from './ReferenceInput';
|
package/dist/input/types.d.ts
CHANGED
|
@@ -1,6 +1,37 @@
|
|
|
1
|
-
import
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { InputProps as InputPropsBase, Validator } from '@strato-admin/ra-core';
|
|
2
3
|
import { FormFieldProps as CloudscapeFormFieldProps } from '@cloudscape-design/components/form-field';
|
|
3
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Common props shared by all Strato Admin input components.
|
|
6
|
+
*/
|
|
7
|
+
export interface StratoCommonInputProps<T = any> {
|
|
8
|
+
/** The field name in the record. Used to read and write the value. */
|
|
9
|
+
source: string;
|
|
10
|
+
/** Override the auto-generated label. Pass `false` to hide the label entirely. */
|
|
4
11
|
label?: string | false;
|
|
12
|
+
/** The initial value when no record value exists. */
|
|
13
|
+
defaultValue?: any;
|
|
14
|
+
/** Validation rule or array of rules. Use built-in validators (`required()`, `minLength()`, etc.) or provide a custom function. */
|
|
15
|
+
validate?: Validator | Validator[];
|
|
16
|
+
/** Transforms the record value before passing it to the input (record → input). */
|
|
17
|
+
format?: (value: T) => any;
|
|
18
|
+
/** Transforms the input value before saving to the record (input → record). */
|
|
19
|
+
parse?: (value: any) => T;
|
|
20
|
+
/** When `true`, the value is visible but cannot be changed. */
|
|
21
|
+
readOnly?: boolean;
|
|
22
|
+
/** When `true`, the input is non-interactive and grayed out. */
|
|
23
|
+
disabled?: boolean;
|
|
24
|
+
/** Helper text displayed below the label. */
|
|
25
|
+
description?: React.ReactNode;
|
|
26
|
+
/** Additional constraint text. Appended with "(optional)" when the field is not required. */
|
|
27
|
+
constraintText?: React.ReactNode;
|
|
28
|
+
/** Info link displayed next to the label (Cloudscape `info` slot). */
|
|
29
|
+
info?: React.ReactNode;
|
|
30
|
+
/** Secondary control displayed to the right of the input (Cloudscape `secondaryControl` slot). */
|
|
31
|
+
secondaryControl?: React.ReactNode;
|
|
32
|
+
/** When `true`, the form field stretches to fill its container width. */
|
|
33
|
+
stretch?: boolean;
|
|
34
|
+
}
|
|
35
|
+
export interface StratoInputProps<T = any> extends StratoCommonInputProps<T>, Omit<InputPropsBase<T>, 'label' | 'source' | 'defaultValue' | 'validate' | 'format' | 'parse' | 'readOnly' | 'disabled'>, Pick<CloudscapeFormFieldProps, 'i18nStrings'> {
|
|
5
36
|
}
|
|
6
37
|
export type InputProps<T = any> = StratoInputProps<T>;
|
package/dist/layout/AppLayout.js
CHANGED
|
@@ -2,9 +2,10 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
|
|
|
2
2
|
import { useState } from 'react';
|
|
3
3
|
import CloudscapeAppLayout from '@cloudscape-design/components/app-layout';
|
|
4
4
|
import SideNavigation from '@cloudscape-design/components/side-navigation';
|
|
5
|
-
import { useResourceDefinitions, useDefaultTitle, useGetResourceLabel } from '@strato-admin/core';
|
|
5
|
+
import { useResourceDefinitions, useDefaultTitle, useGetResourceLabel } from '@strato-admin/ra-core';
|
|
6
6
|
import { useNavigate } from 'react-router-dom';
|
|
7
7
|
import { TopNavigation } from './TopNavigation';
|
|
8
|
+
import { Notifications } from './Notifications';
|
|
8
9
|
import ThemeManager from '../theme/ThemeManager';
|
|
9
10
|
export const AppLayout = ({ children, header, title }) => {
|
|
10
11
|
const resources = useResourceDefinitions();
|
|
@@ -13,7 +14,9 @@ export const AppLayout = ({ children, header, title }) => {
|
|
|
13
14
|
const defaultTitle = useDefaultTitle();
|
|
14
15
|
const [navigationOpen, setNavigationOpen] = useState(true);
|
|
15
16
|
const appTitle = title ?? (typeof defaultTitle === 'string' ? defaultTitle : '');
|
|
16
|
-
const items = Object.values(resources)
|
|
17
|
+
const items = Object.values(resources)
|
|
18
|
+
.filter((resource) => resource.hasList)
|
|
19
|
+
.map((resource) => ({
|
|
17
20
|
type: 'link',
|
|
18
21
|
text: getResourceLabel(resource.name),
|
|
19
22
|
href: `/${resource.name}`,
|
|
@@ -33,6 +36,6 @@ export const AppLayout = ({ children, header, title }) => {
|
|
|
33
36
|
event.preventDefault();
|
|
34
37
|
navigate(event.detail.href);
|
|
35
38
|
}
|
|
36
|
-
} }), content: _jsx("div", { children: children }) })] }));
|
|
39
|
+
} }), notifications: _jsx(Notifications, {}), content: _jsx("div", { children: children }) })] }));
|
|
37
40
|
};
|
|
38
41
|
export default AppLayout;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const Notifications: () => import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useRef } from 'react';
|
|
3
|
+
import Flashbar from '@cloudscape-design/components/flashbar';
|
|
4
|
+
import { useNotificationContext, useTranslate } from '@strato-admin/ra-core';
|
|
5
|
+
const DEFAULT_AUTO_HIDE_MS = null;
|
|
6
|
+
let idCounter = 0;
|
|
7
|
+
const stableIds = new WeakMap();
|
|
8
|
+
const getStableId = (obj) => {
|
|
9
|
+
if (!stableIds.has(obj))
|
|
10
|
+
stableIds.set(obj, String(++idCounter));
|
|
11
|
+
return stableIds.get(obj);
|
|
12
|
+
};
|
|
13
|
+
export const Notifications = () => {
|
|
14
|
+
const { notifications, setNotifications } = useNotificationContext();
|
|
15
|
+
const translate = useTranslate();
|
|
16
|
+
const timers = useRef(new Map());
|
|
17
|
+
const dismiss = useCallback((notification) => {
|
|
18
|
+
const id = getStableId(notification);
|
|
19
|
+
clearTimeout(timers.current.get(id));
|
|
20
|
+
timers.current.delete(id);
|
|
21
|
+
setNotifications((prev) => prev.filter((n) => n !== notification));
|
|
22
|
+
}, [setNotifications]);
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
notifications.forEach((notification) => {
|
|
25
|
+
const id = getStableId(notification);
|
|
26
|
+
if (timers.current.has(id))
|
|
27
|
+
return;
|
|
28
|
+
const ms = notification.notificationOptions?.autoHideDuration ?? DEFAULT_AUTO_HIDE_MS;
|
|
29
|
+
if (ms === null)
|
|
30
|
+
return;
|
|
31
|
+
timers.current.set(id, setTimeout(() => dismiss(notification), ms));
|
|
32
|
+
});
|
|
33
|
+
return () => {
|
|
34
|
+
timers.current.forEach((timer) => clearTimeout(timer));
|
|
35
|
+
timers.current.clear();
|
|
36
|
+
};
|
|
37
|
+
}, [notifications, dismiss]);
|
|
38
|
+
if (notifications.length === 0)
|
|
39
|
+
return null;
|
|
40
|
+
const items = notifications.map((notification) => {
|
|
41
|
+
const { message, type, notificationOptions } = notification;
|
|
42
|
+
return {
|
|
43
|
+
id: getStableId(notification),
|
|
44
|
+
type,
|
|
45
|
+
content: typeof message === 'string' ? translate(message, notificationOptions?.messageArgs) : message,
|
|
46
|
+
dismissible: true,
|
|
47
|
+
onDismiss: () => dismiss(notification),
|
|
48
|
+
};
|
|
49
|
+
});
|
|
50
|
+
return _jsx(Flashbar, { items: items });
|
|
51
|
+
};
|