@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.
Files changed (231) hide show
  1. package/dist/Admin.d.ts +6 -2
  2. package/dist/Admin.js +14 -8
  3. package/dist/RecordLink.js +5 -4
  4. package/dist/Settings.d.ts +17 -0
  5. package/dist/Settings.js +14 -0
  6. package/dist/button/BulkDeleteButton.d.ts +2 -1
  7. package/dist/button/BulkDeleteButton.js +17 -11
  8. package/dist/button/Button.d.ts +2 -1
  9. package/dist/button/CancelButton.d.ts +6 -0
  10. package/dist/button/CancelButton.js +10 -0
  11. package/dist/button/CreateButton.js +9 -8
  12. package/dist/button/DeleteButton.d.ts +13 -0
  13. package/dist/button/DeleteButton.js +36 -0
  14. package/dist/button/EditButton.d.ts +1 -1
  15. package/dist/button/EditButton.js +10 -10
  16. package/dist/button/SaveButton.js +2 -2
  17. package/dist/button/index.d.ts +2 -0
  18. package/dist/button/index.js +2 -0
  19. package/dist/collection-hooks/interfaces.d.ts +7 -3
  20. package/dist/collection-hooks/useCollection.d.ts +1 -1
  21. package/dist/collection-hooks/useCollection.js +15 -10
  22. package/dist/create/Create.d.ts +9 -17
  23. package/dist/create/Create.js +40 -12
  24. package/dist/create/CreateHeader.d.ts +2 -2
  25. package/dist/create/CreateHeader.js +4 -5
  26. package/dist/defaults.d.ts +6 -0
  27. package/dist/defaults.js +21 -0
  28. package/dist/detail/Detail.d.ts +33 -0
  29. package/dist/detail/Detail.js +22 -0
  30. package/dist/detail/DetailHeader.d.ts +11 -0
  31. package/dist/detail/{ShowHeader.js → DetailHeader.js} +7 -5
  32. package/dist/detail/DetailHub.d.ts +27 -0
  33. package/dist/detail/DetailHub.js +63 -0
  34. package/dist/detail/KeyValuePairs.d.ts +7 -1
  35. package/dist/detail/KeyValuePairs.js +14 -8
  36. package/dist/detail/index.d.ts +3 -2
  37. package/dist/detail/index.js +3 -2
  38. package/dist/edit/Edit.d.ts +8 -19
  39. package/dist/edit/Edit.js +48 -12
  40. package/dist/edit/EditHeader.d.ts +2 -2
  41. package/dist/edit/EditHeader.js +5 -4
  42. package/dist/field/ArrayField.d.ts +26 -10
  43. package/dist/field/ArrayField.js +38 -10
  44. package/dist/field/BadgeField.d.ts +1 -1
  45. package/dist/field/BadgeField.js +1 -1
  46. package/dist/field/BooleanField.d.ts +1 -1
  47. package/dist/field/BooleanField.js +2 -2
  48. package/dist/field/CurrencyField.d.ts +1 -1
  49. package/dist/field/CurrencyField.js +1 -1
  50. package/dist/field/DateField.d.ts +1 -1
  51. package/dist/field/DateField.js +1 -1
  52. package/dist/field/IdField.d.ts +1 -1
  53. package/dist/field/IdField.js +3 -3
  54. package/dist/field/NumberField.d.ts +1 -1
  55. package/dist/field/NumberField.js +1 -1
  56. package/dist/field/ReferenceField.d.ts +1 -1
  57. package/dist/field/ReferenceField.js +4 -2
  58. package/dist/field/ReferenceManyField.d.ts +35 -4
  59. package/dist/field/ReferenceManyField.js +17 -4
  60. package/dist/field/StatusIndicatorField.d.ts +1 -1
  61. package/dist/field/StatusIndicatorField.js +6 -5
  62. package/dist/field/TextField.d.ts +1 -1
  63. package/dist/field/TextField.js +1 -1
  64. package/dist/field/types.d.ts +9 -9
  65. package/dist/form/Form.d.ts +12 -2
  66. package/dist/form/Form.js +10 -16
  67. package/dist/form/index.d.ts +1 -1
  68. package/dist/form/index.js +1 -1
  69. package/dist/hooks/useSchemaFields.d.ts +22 -0
  70. package/dist/hooks/useSchemaFields.js +45 -0
  71. package/dist/i18n/Message.d.ts +15 -0
  72. package/dist/i18n/Message.js +19 -0
  73. package/dist/i18n/RecordMessage.d.ts +14 -0
  74. package/dist/i18n/RecordMessage.js +16 -0
  75. package/dist/i18n/index.d.ts +3 -0
  76. package/dist/i18n/index.js +2 -0
  77. package/dist/i18n/types.d.ts +19 -0
  78. package/dist/i18n/types.js +1 -0
  79. package/dist/index.d.ts +5 -1
  80. package/dist/index.js +5 -1
  81. package/dist/input/ArrayInput.d.ts +33 -0
  82. package/dist/input/{AttributeEditor.js → ArrayInput.js} +18 -11
  83. package/dist/input/AutocompleteInput.d.ts +1 -1
  84. package/dist/input/AutocompleteInput.js +3 -3
  85. package/dist/input/BooleanInput.d.ts +6 -0
  86. package/dist/input/BooleanInput.js +23 -0
  87. package/dist/input/CommonInputProps.d.ts +6 -0
  88. package/dist/input/CommonInputProps.js +6 -0
  89. package/dist/input/FieldTitle.js +4 -4
  90. package/dist/input/FormField.js +12 -3
  91. package/dist/input/FormFieldContext.d.ts +1 -1
  92. package/dist/input/NumberInput.d.ts +1 -1
  93. package/dist/input/NumberInput.js +3 -3
  94. package/dist/input/ReferenceInput.d.ts +1 -1
  95. package/dist/input/ReferenceInput.js +22 -12
  96. package/dist/input/SelectInput.d.ts +1 -1
  97. package/dist/input/SelectInput.js +3 -3
  98. package/dist/input/SliderInput.d.ts +1 -1
  99. package/dist/input/SliderInput.js +4 -4
  100. package/dist/input/TextAreaInput.d.ts +1 -1
  101. package/dist/input/TextAreaInput.js +3 -3
  102. package/dist/input/TextInput.d.ts +1 -1
  103. package/dist/input/TextInput.js +6 -12
  104. package/dist/input/index.d.ts +2 -1
  105. package/dist/input/index.js +2 -1
  106. package/dist/input/types.d.ts +33 -2
  107. package/dist/layout/AppLayout.js +6 -3
  108. package/dist/layout/Notifications.d.ts +1 -0
  109. package/dist/layout/Notifications.js +51 -0
  110. package/dist/layout/Ready.d.ts +6 -0
  111. package/dist/layout/Ready.js +24 -0
  112. package/dist/layout/TopNavigation.d.ts +4 -2
  113. package/dist/layout/TopNavigation.js +7 -7
  114. package/dist/layout/index.d.ts +2 -0
  115. package/dist/layout/index.js +2 -0
  116. package/dist/list/Cards.d.ts +31 -4
  117. package/dist/list/Cards.js +81 -10
  118. package/dist/list/List.d.ts +9 -12
  119. package/dist/list/List.js +41 -11
  120. package/dist/list/Table.d.ts +8 -4
  121. package/dist/list/Table.js +55 -55
  122. package/dist/list/TableHeader.d.ts +2 -2
  123. package/dist/list/TableHeader.js +4 -5
  124. package/dist/theme/ThemeManager.js +1 -1
  125. package/package.json +8 -5
  126. package/src/Admin.tsx +35 -18
  127. package/src/RecordLink.stories.tsx +1 -1
  128. package/src/RecordLink.tsx +5 -4
  129. package/src/Settings.tsx +16 -0
  130. package/src/__mocks__/ra-core.tsx +83 -0
  131. package/src/__mocks__/strato-core.tsx +36 -42
  132. package/src/button/BulkDeleteButton.test.tsx +17 -4
  133. package/src/button/BulkDeleteButton.tsx +24 -29
  134. package/src/button/Button.tsx +31 -2
  135. package/src/button/CancelButton.tsx +20 -0
  136. package/src/button/CreateButton.tsx +12 -10
  137. package/src/button/DeleteButton.tsx +96 -0
  138. package/src/button/EditButton.tsx +13 -12
  139. package/src/button/SaveButton.tsx +2 -3
  140. package/src/button/index.ts +2 -0
  141. package/src/collection-hooks/interfaces.ts +7 -3
  142. package/src/collection-hooks/useCollection.test.ts +115 -2
  143. package/src/collection-hooks/useCollection.ts +15 -10
  144. package/src/create/Create.test.tsx +3 -3
  145. package/src/create/Create.tsx +68 -37
  146. package/src/create/CreateHeader.tsx +6 -10
  147. package/src/defaults.tsx +28 -0
  148. package/src/detail/Detail-CollectionFields.test.tsx +84 -0
  149. package/src/detail/Detail.test.tsx +91 -0
  150. package/src/detail/Detail.tsx +48 -0
  151. package/src/detail/{ShowHeader.test.tsx → DetailHeader.test.tsx} +11 -9
  152. package/src/detail/DetailHeader.tsx +42 -0
  153. package/src/detail/DetailHub.tsx +88 -0
  154. package/src/detail/KeyValuePairs.test.tsx +2 -2
  155. package/src/detail/KeyValuePairs.tsx +25 -18
  156. package/src/detail/index.ts +3 -2
  157. package/src/edit/Edit.test.tsx +7 -5
  158. package/src/edit/Edit.tsx +92 -40
  159. package/src/edit/EditHeader.tsx +7 -5
  160. package/src/field/ArrayField.tsx +57 -11
  161. package/src/field/BadgeField.tsx +2 -3
  162. package/src/field/BooleanField.test.tsx +2 -3
  163. package/src/field/BooleanField.tsx +3 -3
  164. package/src/field/CurrencyField.tsx +1 -1
  165. package/src/field/DateField.tsx +1 -1
  166. package/src/field/IdField.test.tsx +8 -20
  167. package/src/field/IdField.tsx +5 -20
  168. package/src/field/NumberField.tsx +1 -1
  169. package/src/field/ReferenceField.test.tsx +15 -6
  170. package/src/field/ReferenceField.tsx +10 -7
  171. package/src/field/ReferenceManyField.test.tsx +55 -10
  172. package/src/field/ReferenceManyField.tsx +84 -13
  173. package/src/field/StatusIndicatorField.test.tsx +7 -21
  174. package/src/field/StatusIndicatorField.tsx +8 -20
  175. package/src/field/TextField.tsx +1 -1
  176. package/src/field/types.ts +12 -13
  177. package/src/form/Form.test.tsx +8 -4
  178. package/src/form/Form.tsx +24 -19
  179. package/src/form/index.ts +1 -1
  180. package/src/hooks/useSchemaFields.ts +89 -0
  181. package/src/i18n/Message.tsx +22 -0
  182. package/src/i18n/RecordMessage.tsx +22 -0
  183. package/src/i18n/index.ts +3 -0
  184. package/src/i18n/types.ts +19 -0
  185. package/src/index.ts +5 -1
  186. package/src/input/ArrayInput.test.tsx +81 -0
  187. package/src/input/{AttributeEditor.tsx → ArrayInput.tsx} +36 -18
  188. package/src/input/AutocompleteInput.test.tsx +2 -4
  189. package/src/input/AutocompleteInput.tsx +9 -11
  190. package/src/input/BooleanInput.tsx +42 -0
  191. package/src/input/CommonInputProps.tsx +8 -0
  192. package/src/input/FieldTitle.tsx +3 -15
  193. package/src/input/FormField.tsx +78 -67
  194. package/src/input/FormFieldContext.ts +1 -1
  195. package/src/input/NumberInput.tsx +10 -7
  196. package/src/input/ReferenceInput.test.tsx +12 -2
  197. package/src/input/ReferenceInput.tsx +32 -14
  198. package/src/input/SelectInput.tsx +14 -17
  199. package/src/input/SliderInput.test.tsx +2 -3
  200. package/src/input/SliderInput.tsx +48 -38
  201. package/src/input/TextAreaInput.tsx +10 -6
  202. package/src/input/TextInput.test.tsx +2 -4
  203. package/src/input/TextInput.tsx +35 -20
  204. package/src/input/index.ts +2 -1
  205. package/src/input/types.ts +40 -8
  206. package/src/layout/AppLayout.test.tsx +23 -3
  207. package/src/layout/AppLayout.tsx +11 -8
  208. package/src/layout/Notifications.test.tsx +102 -0
  209. package/src/layout/Notifications.tsx +61 -0
  210. package/src/layout/Ready.tsx +123 -0
  211. package/src/layout/TopNavigation.test.tsx +2 -3
  212. package/src/layout/TopNavigation.tsx +9 -8
  213. package/src/layout/index.ts +2 -0
  214. package/src/list/Cards.test.tsx +320 -0
  215. package/src/list/Cards.tsx +146 -16
  216. package/src/list/List.tsx +87 -26
  217. package/src/list/Table.test.tsx +40 -5
  218. package/src/list/Table.tsx +89 -98
  219. package/src/list/TableHeader.test.tsx +15 -11
  220. package/src/list/TableHeader.tsx +6 -8
  221. package/src/theme/ThemeManager.tsx +1 -1
  222. package/dist/__mocks__/strato-core.js +0 -50
  223. package/dist/__mocks__to__delete/strato-core.js +0 -50
  224. package/dist/detail/Show.d.ts +0 -39
  225. package/dist/detail/Show.js +0 -40
  226. package/dist/detail/ShowHeader.d.ts +0 -7
  227. package/dist/input/AttributeEditor.d.ts +0 -25
  228. package/src/detail/Show.test.tsx +0 -96
  229. package/src/detail/Show.tsx +0 -104
  230. package/src/detail/ShowHeader.tsx +0 -35
  231. package/src/input/AttributeEditor.test.tsx +0 -147
@@ -0,0 +1,21 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Message } from './i18n/Message';
3
+ import { Table } from './list';
4
+ import { DetailHub } from './detail';
5
+ /**
6
+ * The framework's last-resort default values for all Admin-level configurable settings.
7
+ * Override any of these via the `settings` prop on <Admin>.
8
+ */
9
+ export const FRAMEWORK_DEFAULTS = {
10
+ listComponent: Table,
11
+ detailComponent: DetailHub,
12
+ editSuccessMessage: _jsx(Message, { children: "Element updated" }),
13
+ deleteSuccessMessage: _jsx(Message, { children: "Element deleted" }),
14
+ bulkDeleteSuccessMessage: (countDeleted) => (_jsx(Message, { vars: { countDeleted }, children: '{countDeleted, plural, one {# element deleted} other {# elements deleted}}' })),
15
+ mutationMode: 'pessimistic',
16
+ createRedirect: 'list',
17
+ editRedirect: 'detail',
18
+ listPageSize: 25,
19
+ listPageSizes: [10, 25, 50, 100],
20
+ listPageSizeLabel: (pageSize) => (_jsx(Message, { vars: { pageSize }, children: '{pageSize, plural, one {# item} other {# items}}' })),
21
+ };
@@ -0,0 +1,33 @@
1
+ import React from 'react';
2
+ import { ShowBaseProps } from '@strato-admin/ra-core';
3
+ export interface DetailProps extends ShowBaseProps {
4
+ /**
5
+ * The title of the detail view.
6
+ */
7
+ title?: string;
8
+ /**
9
+ * The description of the detail view.
10
+ */
11
+ description?: string;
12
+ /**
13
+ * Custom fields or components to display in the detail view.
14
+ */
15
+ children?: React.ReactNode;
16
+ /**
17
+ * Custom actions to display in the header.
18
+ */
19
+ actions?: React.ReactNode;
20
+ }
21
+ /**
22
+ * A detail view component that displays a single record's details.
23
+ * It uses the detailComponent defined in the schema (defaults to DetailHub)
24
+ * to organize fields into sections.
25
+ *
26
+ * @example
27
+ * <Detail>
28
+ * <TextField source="name" />
29
+ * <TextField source="description" />
30
+ * </Detail>
31
+ */
32
+ export declare const Detail: (props: DetailProps) => import("react/jsx-runtime").JSX.Element;
33
+ export default Detail;
@@ -0,0 +1,22 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { ShowContextProvider, useShowController } from '@strato-admin/ra-core';
3
+ import { useResourceSchema } from '@strato-admin/core';
4
+ import DetailHub from './DetailHub';
5
+ /**
6
+ * A detail view component that displays a single record's details.
7
+ * It uses the detailComponent defined in the schema (defaults to DetailHub)
8
+ * to organize fields into sections.
9
+ *
10
+ * @example
11
+ * <Detail>
12
+ * <TextField source="name" />
13
+ * <TextField source="description" />
14
+ * </Detail>
15
+ */
16
+ export const Detail = (props) => {
17
+ const { children, ...rest } = props;
18
+ const { queryOptions, detailComponent: DetailComponent = DetailHub } = useResourceSchema(props.resource);
19
+ const controllerProps = useShowController({ queryOptions, ...rest });
20
+ return (_jsx(ShowContextProvider, { value: controllerProps, children: _jsx(DetailComponent, { ...props }) }));
21
+ };
22
+ export default Detail;
@@ -0,0 +1,11 @@
1
+ import React from 'react';
2
+ import { HeaderProps } from '@cloudscape-design/components/header';
3
+ export interface DetailHeaderProps extends Pick<HeaderProps, 'variant' | 'counter' | 'actions' | 'description' | 'info' | 'headingTagOverride'> {
4
+ title?: React.ReactNode;
5
+ }
6
+ /** @deprecated Use DetailHeader instead */
7
+ export type ShowHeaderProps = DetailHeaderProps;
8
+ /** @deprecated Use DetailHeader instead */
9
+ export declare const ShowHeader: (props: DetailHeaderProps) => import("react/jsx-runtime").JSX.Element;
10
+ export declare const DetailHeader: ({ title, actions, description, counter, info, variant, headingTagOverride }: DetailHeaderProps) => import("react/jsx-runtime").JSX.Element;
11
+ export default DetailHeader;
@@ -2,9 +2,11 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import React from 'react';
3
3
  import Header from '@cloudscape-design/components/header';
4
4
  import SpaceBetween from '@cloudscape-design/components/space-between';
5
- import { useShowContext, useTranslate } from '@strato-admin/core';
5
+ import { useShowContext, useTranslate } from '@strato-admin/ra-core';
6
6
  import { EditButton } from '../button/EditButton';
7
- export const ShowHeader = ({ title, actions, ...props }) => {
7
+ /** @deprecated Use DetailHeader instead */
8
+ export const ShowHeader = (props) => _jsx(DetailHeader, { ...props });
9
+ export const DetailHeader = ({ title, actions, description, counter, info, variant = 'h2', headingTagOverride }) => {
8
10
  const translate = useTranslate();
9
11
  const { record, defaultTitle } = useShowContext();
10
12
  const headerTitle = React.useMemo(() => {
@@ -13,7 +15,7 @@ export const ShowHeader = ({ title, actions, ...props }) => {
13
15
  }
14
16
  return defaultTitle;
15
17
  }, [title, defaultTitle, translate]);
16
- const headerActions = actions || (_jsx(SpaceBetween, { direction: "horizontal", size: "xs", children: _jsx(EditButton, { record: record }) }));
17
- return (_jsx(Header, { variant: "h2", ...props, actions: headerActions, children: headerTitle }));
18
+ const headerActions = actions || (_jsx(SpaceBetween, { direction: "horizontal", size: "xs", children: _jsx(EditButton, { record: record, variant: "primary" }) }));
19
+ return (_jsx(Header, { variant: variant, actions: headerActions, description: description, counter: counter, info: info, headingTagOverride: headingTagOverride, children: headerTitle }));
18
20
  };
19
- export default ShowHeader;
21
+ export default DetailHeader;
@@ -0,0 +1,27 @@
1
+ import { ReactNode } from 'react';
2
+ import { RaRecord } from '@strato-admin/ra-core';
3
+ export interface DetailHubProps {
4
+ title?: string;
5
+ description?: string;
6
+ children?: ReactNode;
7
+ actions?: ReactNode;
8
+ }
9
+ /**
10
+ * A detail view component that organizes fields into sections based on their type.
11
+ * Scalar fields are grouped into a Container with KeyValuePairs, while
12
+ * collection fields (like ReferenceManyField) are displayed as separate sections
13
+ * below the main record details.
14
+ *
15
+ * It also renders the DetailHeader with the record title and actions (like Edit).
16
+ *
17
+ * @example
18
+ * <DetailHub />
19
+ *
20
+ * @example
21
+ * <DetailHub title="Custom Title">
22
+ * <KeyValuePairs columns={3} />
23
+ * <ReferenceManyField reference="comments" target="post_id" />
24
+ * </DetailHub>
25
+ */
26
+ export declare const DetailHub: <RecordType extends RaRecord = RaRecord>(props: DetailHubProps) => import("react/jsx-runtime").JSX.Element | null;
27
+ export default DetailHub;
@@ -0,0 +1,63 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React, { useMemo, isValidElement } from 'react';
3
+ import Container from '@cloudscape-design/components/container';
4
+ import SpaceBetween from '@cloudscape-design/components/space-between';
5
+ import { useTranslate, useShowContext } from '@strato-admin/ra-core';
6
+ import { useResourceSchema } from '@strato-admin/core';
7
+ import { useSchemaFields } from '../hooks/useSchemaFields';
8
+ import KeyValuePairs from './KeyValuePairs';
9
+ import DetailHeader from './DetailHeader';
10
+ /**
11
+ * A detail view component that organizes fields into sections based on their type.
12
+ * Scalar fields are grouped into a Container with KeyValuePairs, while
13
+ * collection fields (like ReferenceManyField) are displayed as separate sections
14
+ * below the main record details.
15
+ *
16
+ * It also renders the DetailHeader with the record title and actions (like Edit).
17
+ *
18
+ * @example
19
+ * <DetailHub />
20
+ *
21
+ * @example
22
+ * <DetailHub title="Custom Title">
23
+ * <KeyValuePairs columns={3} />
24
+ * <ReferenceManyField reference="comments" target="post_id" />
25
+ * </DetailHub>
26
+ */
27
+ export const DetailHub = (props) => {
28
+ const { title, description, children, actions } = props;
29
+ const { record, resource, isLoading } = useShowContext();
30
+ const translate = useTranslate();
31
+ const { label, detailTitle, detailDescription } = useResourceSchema();
32
+ const { getDetailFields } = useSchemaFields();
33
+ const constructedTitle = useMemo(() => label || translate(`resources.${resource}.name`, { smart_count: 1 }), [label, translate, resource]);
34
+ const finalTitle = useMemo(() => {
35
+ if (title)
36
+ return title;
37
+ if (typeof detailTitle === 'function')
38
+ return detailTitle(record);
39
+ if (isValidElement(detailTitle))
40
+ return detailTitle;
41
+ if (detailTitle)
42
+ return translate(detailTitle, record);
43
+ return constructedTitle;
44
+ }, [record, title, detailTitle, translate, constructedTitle]);
45
+ const finalDescription = useMemo(() => {
46
+ if (description)
47
+ return description;
48
+ if (typeof detailDescription === 'function')
49
+ return detailDescription(record);
50
+ if (isValidElement(detailDescription))
51
+ return detailDescription;
52
+ if (detailDescription)
53
+ return translate(detailDescription, record);
54
+ return undefined;
55
+ }, [record, description, detailDescription, translate]);
56
+ const { scalarFields, collectionFields } = useMemo(() => getDetailFields(children), [getDetailFields, children]);
57
+ if (isLoading || !record) {
58
+ return null;
59
+ }
60
+ const hasScalarFields = scalarFields.length > 0;
61
+ return (_jsxs(SpaceBetween, { size: "l", children: [_jsx(DetailHeader, { title: finalTitle, description: finalDescription, actions: actions }), hasScalarFields && (_jsx(Container, { children: React.Children.count(children) > 0 ? (_jsx(SpaceBetween, { size: "l", children: scalarFields })) : (_jsx(KeyValuePairs, {})) })), collectionFields] }));
62
+ };
63
+ export default DetailHub;
@@ -1,9 +1,15 @@
1
1
  import React from 'react';
2
2
  import { type KeyValuePairsProps as CloudscapeKeyValuePairsProps } from '@cloudscape-design/components/key-value-pairs';
3
- import { type RaRecord } from '@strato-admin/core';
3
+ import { type RaRecord } from '@strato-admin/ra-core';
4
4
  export interface KeyValuePairsProps extends Partial<Omit<CloudscapeKeyValuePairsProps, 'items'>> {
5
5
  children?: React.ReactNode;
6
+ /**
7
+ * List of field sources to include in the key-value pairs list.
8
+ */
6
9
  include?: string[];
10
+ /**
11
+ * List of field sources to exclude from the key-value pairs list.
12
+ */
7
13
  exclude?: string[];
8
14
  columns?: number;
9
15
  minColumnWidth?: number;
@@ -1,7 +1,8 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
2
2
  import React from 'react';
3
3
  import CloudscapeKeyValuePairs from '@cloudscape-design/components/key-value-pairs';
4
- import { useResourceContext, useRecordContext, FieldTitle, RecordContextProvider, useFieldSchema, } from '@strato-admin/core';
4
+ import { useRecordContext, FieldTitle, RecordContextProvider } from '@strato-admin/ra-core';
5
+ import { useResourceSchema } from '@strato-admin/core';
5
6
  import TextField from '../field/TextField';
6
7
  /**
7
8
  * KeyValuePairs.Field is a helper component to define a field in a KeyValuePairs component.
@@ -28,20 +29,25 @@ export const KeyValueField = ({ children, source, field: FieldComponent }) => {
28
29
  */
29
30
  export const KeyValuePairs = ({ children, include, exclude, columns = 3, // Default to 3 columns if not specified
30
31
  minColumnWidth, ...props }) => {
31
- const resource = useResourceContext();
32
32
  const record = useRecordContext();
33
- const schemaChildren = useFieldSchema();
33
+ const { resource, fieldSchema: schemaChildren, detailInclude, detailExclude } = useResourceSchema();
34
34
  const finalChildren = React.useMemo(() => {
35
35
  const baseChildren = children || schemaChildren;
36
36
  let result = React.Children.toArray(baseChildren);
37
- if (include) {
38
- result = result.filter((child) => React.isValidElement(child) && include.includes(child.props.source));
37
+ const finalInclude = include || detailInclude;
38
+ const finalExclude = exclude || detailExclude;
39
+ if (finalInclude) {
40
+ result = result.filter((child) => React.isValidElement(child) && finalInclude.includes(child.props.source));
39
41
  }
40
- else if (exclude) {
41
- result = result.filter((child) => React.isValidElement(child) && !exclude.includes(child.props.source));
42
+ else {
43
+ // Filter out fields marked as collection fields by default
44
+ result = result.filter((child) => React.isValidElement(child) && !child.type.isCollectionField);
45
+ if (finalExclude) {
46
+ result = result.filter((child) => React.isValidElement(child) && !finalExclude.includes(child.props.source));
47
+ }
42
48
  }
43
49
  return result;
44
- }, [children, schemaChildren, include, exclude]);
50
+ }, [children, schemaChildren, include, exclude, detailInclude, detailExclude]);
45
51
  const items = React.Children.map(finalChildren, (child) => {
46
52
  if (!React.isValidElement(child)) {
47
53
  return null;
@@ -1,3 +1,4 @@
1
1
  export * from './KeyValuePairs';
2
- export * from './Show';
3
- export * from './ShowHeader';
2
+ export * from './Detail';
3
+ export * from './DetailHub';
4
+ export * from './DetailHeader';
@@ -1,3 +1,4 @@
1
1
  export * from './KeyValuePairs';
2
- export * from './Show';
3
- export * from './ShowHeader';
2
+ export * from './Detail';
3
+ export * from './DetailHub';
4
+ export * from './DetailHeader';
@@ -1,19 +1,14 @@
1
1
  import React from 'react';
2
- import { type RaRecord } from '@strato-admin/core';
3
- export interface EditProps<_RecordType extends RaRecord = RaRecord> {
2
+ import { type RaRecord, type EditBaseProps } from '@strato-admin/ra-core';
3
+ export interface EditProps<RecordType extends RaRecord = RaRecord, ErrorType = Error> extends EditBaseProps<RecordType, ErrorType> {
4
4
  children?: React.ReactNode;
5
- inputSchema?: React.ReactNode;
6
- title?: React.ReactNode;
5
+ title?: React.ReactNode | ((record: RecordType) => React.ReactNode);
6
+ description?: React.ReactNode | ((record: RecordType) => React.ReactNode);
7
7
  actions?: React.ReactNode;
8
- resource?: string;
9
- id?: any;
10
- mutationMode?: 'pessimistic' | 'optimistic' | 'undoable';
11
- mutationOptions?: any;
12
- queryOptions?: any;
13
- redirect?: any;
14
- transform?: any;
15
8
  include?: string[];
16
9
  exclude?: string[];
10
+ saveButtonLabel?: string;
11
+ redirect?: false | 'list' | 'detail';
17
12
  }
18
13
  /**
19
14
  * An Edit component that provides record context and a Cloudscape Container.
@@ -28,15 +23,9 @@ export interface EditProps<_RecordType extends RaRecord = RaRecord> {
28
23
  * @example
29
24
  * // Using InputSchema from context
30
25
  * <Edit include={['name', 'price']} />
31
- *
32
- * @example
33
- * // Passing a custom input schema
34
- * <Edit inputSchema={<InputSchema>...</InputSchema>}>
35
- * <Form />
36
- * </Edit>
37
26
  */
38
27
  export declare const Edit: {
39
- <RecordType extends RaRecord = any>({ children, inputSchema, title, actions, include, exclude, ...props }: EditProps<RecordType>): import("react/jsx-runtime").JSX.Element;
40
- Header: ({ title, actions, ...props }: import("./EditHeader").EditHeaderProps) => import("react/jsx-runtime").JSX.Element;
28
+ <RecordType extends RaRecord = any>({ children, title, actions, description, include, exclude, redirect, saveButtonLabel, ...props }: EditProps<RecordType>): import("react/jsx-runtime").JSX.Element;
29
+ Header: ({ title, actions, description, counter, info, variant, headingTagOverride }: import("./EditHeader").EditHeaderProps) => import("react/jsx-runtime").JSX.Element;
41
30
  };
42
31
  export default Edit;
package/dist/edit/Edit.js CHANGED
@@ -1,15 +1,40 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { EditBase, useEditContext, ResourceSchemaProvider } from '@strato-admin/core';
2
+ import React from 'react';
3
+ import { EditBase, useEditContext, useNotify, useRedirect } from '@strato-admin/ra-core';
4
+ import { ResourceSchemaProvider, useResourceSchema, useConstructedPageTitle, useSettingValue, } from '@strato-admin/core';
3
5
  import Container from '@cloudscape-design/components/container';
4
6
  import { EditHeader } from './EditHeader';
5
7
  import Form from '../form/Form';
6
- const EditUI = ({ children, resource, inputSchema, title, actions, include, exclude, }) => {
8
+ const EditUI = ({ children, title, actions, description, include, exclude, saveButtonLabel, }) => {
7
9
  const { record, isLoading } = useEditContext();
10
+ const { label, editTitle, editDescription } = useResourceSchema();
11
+ const constructedTitle = useConstructedPageTitle('edit', label);
12
+ const finalTitle = React.useMemo(() => {
13
+ if (isLoading || !record) {
14
+ return '';
15
+ }
16
+ const resolvedTitle = title ?? editTitle ?? constructedTitle;
17
+ if (typeof resolvedTitle === 'function') {
18
+ return resolvedTitle(record);
19
+ }
20
+ return resolvedTitle;
21
+ }, [isLoading, record, title, editTitle, constructedTitle]);
22
+ const finalDescription = React.useMemo(() => {
23
+ if (isLoading || !record) {
24
+ return '';
25
+ }
26
+ const resolvedDescription = description ?? editDescription;
27
+ if (typeof resolvedDescription === 'function') {
28
+ return resolvedDescription(record);
29
+ }
30
+ return resolvedDescription;
31
+ }, [isLoading, record, description, editDescription]);
8
32
  if (isLoading || !record) {
9
33
  return null;
10
34
  }
11
- const finalChildren = children || _jsx(Form, { include: include, exclude: exclude });
12
- return (_jsx(ResourceSchemaProvider, { resource: resource, inputSchema: inputSchema, children: _jsx(Container, { header: _jsx(EditHeader, { title: title, actions: actions }), children: finalChildren }) }));
35
+ const finalSaveButtonLabel = saveButtonLabel; // || <Message>Save</Message>
36
+ const finalChildren = children || _jsx(Form, { include: include, exclude: exclude, saveButtonLabel: finalSaveButtonLabel });
37
+ return (_jsx(Container, { header: _jsx(EditHeader, { title: finalTitle, description: finalDescription, actions: actions }), children: finalChildren }));
13
38
  };
14
39
  /**
15
40
  * An Edit component that provides record context and a Cloudscape Container.
@@ -24,15 +49,26 @@ const EditUI = ({ children, resource, inputSchema, title, actions, include, excl
24
49
  * @example
25
50
  * // Using InputSchema from context
26
51
  * <Edit include={['name', 'price']} />
27
- *
28
- * @example
29
- * // Passing a custom input schema
30
- * <Edit inputSchema={<InputSchema>...</InputSchema>}>
31
- * <Form />
32
- * </Edit>
33
52
  */
34
- export const Edit = ({ children, inputSchema, title, actions, include, exclude, ...props }) => {
35
- return (_jsx(EditBase, { ...props, children: _jsx(EditUI, { resource: props.resource, title: title, actions: actions, include: include, exclude: exclude, inputSchema: inputSchema, children: children }) }));
53
+ export const Edit = ({ children, title, actions, description, include, exclude, redirect, saveButtonLabel, ...props }) => {
54
+ const { queryOptions, editTitle } = useResourceSchema(props.resource);
55
+ const resolve = useSettingValue();
56
+ const resolvedRedirect = resolve(redirect, 'editRedirect');
57
+ const editSuccessMessage = resolve(undefined, 'editSuccessMessage');
58
+ const notify = useNotify();
59
+ const redirectFn = useRedirect();
60
+ const mutationOptions = React.useMemo(() => {
61
+ if (!editSuccessMessage || props.mutationOptions?.onSuccess)
62
+ return props.mutationOptions;
63
+ return {
64
+ ...props.mutationOptions,
65
+ onSuccess: (data) => {
66
+ notify(editSuccessMessage, { type: 'info' });
67
+ redirectFn(resolvedRedirect ?? 'detail', props.resource ?? '', data.id, data);
68
+ },
69
+ };
70
+ }, [editSuccessMessage, props.mutationOptions, props.resource, notify, redirectFn, resolvedRedirect]);
71
+ return (_jsx(EditBase, { redirect: resolvedRedirect, queryOptions: queryOptions, ...props, mutationOptions: mutationOptions, children: _jsx(ResourceSchemaProvider, { resource: props.resource, children: _jsx(EditUI, { title: title ?? editTitle ?? undefined, actions: actions, description: description, include: include, exclude: exclude, saveButtonLabel: saveButtonLabel, children: children }) }) }));
36
72
  };
37
73
  Edit.Header = EditHeader;
38
74
  export default Edit;
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import { HeaderProps } from '@cloudscape-design/components/header';
3
- export interface EditHeaderProps extends Omit<HeaderProps, 'children'> {
3
+ export interface EditHeaderProps extends Pick<HeaderProps, 'variant' | 'counter' | 'actions' | 'description' | 'info' | 'headingTagOverride'> {
4
4
  title?: React.ReactNode;
5
5
  }
6
- export declare const EditHeader: ({ title, actions, ...props }: EditHeaderProps) => import("react/jsx-runtime").JSX.Element;
6
+ export declare const EditHeader: ({ title, actions, description, counter, info, variant, headingTagOverride }: EditHeaderProps) => import("react/jsx-runtime").JSX.Element;
7
7
  export default EditHeader;
@@ -2,8 +2,9 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import React from 'react';
3
3
  import Header from '@cloudscape-design/components/header';
4
4
  import SpaceBetween from '@cloudscape-design/components/space-between';
5
- import { useEditContext, useTranslate } from '@strato-admin/core';
6
- export const EditHeader = ({ title, actions, ...props }) => {
5
+ import { useEditContext, useTranslate } from '@strato-admin/ra-core';
6
+ import { DeleteButton } from '../button/DeleteButton';
7
+ export const EditHeader = ({ title, actions, description, counter, info, variant = 'h2', headingTagOverride }) => {
7
8
  const translate = useTranslate();
8
9
  const { defaultTitle } = useEditContext();
9
10
  const headerTitle = React.useMemo(() => {
@@ -12,7 +13,7 @@ export const EditHeader = ({ title, actions, ...props }) => {
12
13
  }
13
14
  return defaultTitle;
14
15
  }, [title, defaultTitle, translate]);
15
- const headerActions = actions || (_jsx(SpaceBetween, { direction: "horizontal", size: "xs" }));
16
- return (_jsx(Header, { variant: "h2", ...props, actions: headerActions, children: headerTitle }));
16
+ const headerActions = actions || (_jsx(SpaceBetween, { direction: "horizontal", size: "xs", children: _jsx(DeleteButton, {}) }));
17
+ return (_jsx(Header, { variant: variant, actions: headerActions, description: description, counter: counter, info: info, headingTagOverride: headingTagOverride, children: headerTitle }));
17
18
  };
18
19
  export default EditHeader;
@@ -1,10 +1,11 @@
1
- import { type RaRecord } from '@strato-admin/core';
1
+ import React from 'react';
2
+ import { type RaRecord } from '@strato-admin/ra-core';
2
3
  import { type FieldProps } from './types';
3
4
  export interface ArrayFieldProps<RecordType extends RaRecord = RaRecord> extends FieldProps<RecordType> {
4
5
  /**
5
- * Thecomponents to render for each item in the array.
6
+ * The components to render for each item in the array.
6
7
  */
7
- children: React.ReactNode;
8
+ children?: React.ReactNode;
8
9
  /**
9
10
  * Number of items per page if pagination is used within the field.
10
11
  */
@@ -15,15 +16,30 @@ export interface ArrayFieldProps<RecordType extends RaRecord = RaRecord> extends
15
16
  * and ResourceContextProvider initialized with the array data and resource name.
16
17
  *
17
18
  * This allows using components that expect a ListContext or ResourceContext
18
- * (like DataTable) to display nested arrays within a record.
19
+ * (like Table) to display nested arrays within a record.
19
20
  *
20
21
  * @example
21
- * <ArrayField source="products" resource="products">
22
- * <DataTable variant="embedded">
23
- * <DataTable.Col source="title" label="Title" />
24
- * <DataTable.NumberCol source="price" label="Price" />
25
- * </DataTable>
22
+ * <ArrayField source="items" label="Items">
23
+ * <ReferenceField source="product_id" reference="products" label="Product" />
24
+ * <NumberField source="quantity" label="Quantity" />
25
+ * <NumberField source="unit_price" label="Price" options={{ style: 'currency', currency: 'USD' }} />
26
26
  * </ArrayField>
27
+ *
28
+ * NOTE ON FILTERING:
29
+ * Client-side filtering via useList (used by the implicit Table) only searches
30
+ * properties directly present in the array items.
31
+ *
32
+ * If you have a <ReferenceField> child, filtering by the referenced record's
33
+ * name (e.g. Product Name) will NOT work by default because that data is fetched
34
+ * asynchronously and is not in the local array.
35
+ *
36
+ * To enable "Deep Filtering", you can:
37
+ * 1. Denormalize your data at the API level to include the labels in the nested items.
38
+ * 2. Augment the 'data' array before passing it to useList by pre-fetching labels
39
+ * for all referenced IDs and injecting them into the items.
27
40
  */
28
- export declare const ArrayField: <RecordType extends RaRecord = any>(props: ArrayFieldProps<RecordType>) => import("react/jsx-runtime").JSX.Element;
41
+ export declare const ArrayField: {
42
+ <RecordType extends RaRecord = any>(props: ArrayFieldProps<RecordType>): import("react/jsx-runtime").JSX.Element;
43
+ isCollectionField: boolean;
44
+ };
29
45
  export default ArrayField;
@@ -1,22 +1,36 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { useRecordContext, useList, ListContextProvider, ResourceContextProvider } from '@strato-admin/core';
1
+ import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import React from 'react';
3
+ import { useRecordContext, useList, ListContextProvider, ResourceContextProvider, } from '@strato-admin/ra-core';
4
+ import Table from '../list/Table';
3
5
  /**
4
6
  * ArrayField component that wraps its children with a ListContextProvider
5
7
  * and ResourceContextProvider initialized with the array data and resource name.
6
8
  *
7
9
  * This allows using components that expect a ListContext or ResourceContext
8
- * (like DataTable) to display nested arrays within a record.
10
+ * (like Table) to display nested arrays within a record.
9
11
  *
10
12
  * @example
11
- * <ArrayField source="products" resource="products">
12
- * <DataTable variant="embedded">
13
- * <DataTable.Col source="title" label="Title" />
14
- * <DataTable.NumberCol source="price" label="Price" />
15
- * </DataTable>
13
+ * <ArrayField source="items" label="Items">
14
+ * <ReferenceField source="product_id" reference="products" label="Product" />
15
+ * <NumberField source="quantity" label="Quantity" />
16
+ * <NumberField source="unit_price" label="Price" options={{ style: 'currency', currency: 'USD' }} />
16
17
  * </ArrayField>
18
+ *
19
+ * NOTE ON FILTERING:
20
+ * Client-side filtering via useList (used by the implicit Table) only searches
21
+ * properties directly present in the array items.
22
+ *
23
+ * If you have a <ReferenceField> child, filtering by the referenced record's
24
+ * name (e.g. Product Name) will NOT work by default because that data is fetched
25
+ * asynchronously and is not in the local array.
26
+ *
27
+ * To enable "Deep Filtering", you can:
28
+ * 1. Denormalize your data at the API level to include the labels in the nested items.
29
+ * 2. Augment the 'data' array before passing it to useList by pre-fetching labels
30
+ * for all referenced IDs and injecting them into the items.
17
31
  */
18
32
  export const ArrayField = (props) => {
19
- const { source, resource, children, perPage = 10 } = props;
33
+ const { source, resource, children, perPage = 10, label = props.source } = props;
20
34
  const record = useRecordContext();
21
35
  const data = (source && record?.[source]) || [];
22
36
  const targetResource = resource || source || '';
@@ -25,6 +39,20 @@ export const ArrayField = (props) => {
25
39
  resource: targetResource,
26
40
  perPage,
27
41
  });
28
- return (_jsx(ResourceContextProvider, { value: targetResource, children: _jsx(ListContextProvider, { value: listContext, children: children }) }));
42
+ return (_jsx(ResourceContextProvider, { value: targetResource, children: _jsx(ListContextProvider, { value: listContext, children: _jsx(ArrayFieldUI, { title: label, children: children }) }) }));
43
+ };
44
+ const ArrayFieldUI = ({ children, title }) => {
45
+ if (!children)
46
+ return null;
47
+ const childrenArray = React.Children.toArray(children);
48
+ // Check if children already contain a Table
49
+ const hasTable = childrenArray.some((child) => React.isValidElement(child) && (child.type === Table || child.type.displayName === 'Table'));
50
+ if (hasTable) {
51
+ return _jsx(_Fragment, { children: children });
52
+ }
53
+ // If children are provided but no Table, wrap them in a Table.
54
+ // This supports the shorthand <ArrayField><TextField source="foo" /></ArrayField>
55
+ return (_jsx(Table, { variant: "embedded", title: title, selectionType: undefined, actions: null, children: children }));
29
56
  };
30
57
  export default ArrayField;
58
+ ArrayField.isCollectionField = true;
@@ -1,5 +1,5 @@
1
1
  import { type BadgeProps } from '@cloudscape-design/components/badge';
2
- import { type RaRecord } from '@strato-admin/core';
2
+ import { type RaRecord } from '@strato-admin/ra-core';
3
3
  import { type FieldProps } from './types';
4
4
  export interface BadgeFieldProps<RecordType extends RaRecord = RaRecord> extends FieldProps<RecordType> {
5
5
  /**
@@ -1,6 +1,6 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
2
2
  import Badge from '@cloudscape-design/components/badge';
3
- import { useFieldValue, useRecordContext } from '@strato-admin/core';
3
+ import { useFieldValue, useRecordContext } from '@strato-admin/ra-core';
4
4
  import RecordLink from '../RecordLink';
5
5
  const BadgeField = (props) => {
6
6
  const { source, record: recordProp, emptyText, link, color = 'grey' } = props;
@@ -1,4 +1,4 @@
1
- import { RaRecord } from '@strato-admin/core';
1
+ import { RaRecord } from '@strato-admin/ra-core';
2
2
  import { type FieldProps } from './types';
3
3
  export type BooleanFieldProps<RecordType extends RaRecord = RaRecord> = FieldProps<RecordType> & {
4
4
  /**
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import StatusIndicator from '@cloudscape-design/components/status-indicator';
3
- import { useFieldValue, useRecordContext, useTranslate } from '@strato-admin/core';
3
+ import { useFieldValue, useRecordContext, useTranslate } from '@strato-admin/ra-core';
4
4
  import RecordLink from '../RecordLink';
5
5
  const BooleanField = (props) => {
6
6
  const { source, record: recordProp, trueLabel, falseLabel, showLabel = false, link } = props;
@@ -8,7 +8,7 @@ const BooleanField = (props) => {
8
8
  const value = useFieldValue({ source: source, record });
9
9
  const translate = useTranslate();
10
10
  const isTrue = !!value;
11
- const content = isTrue ? (_jsx(StatusIndicator, { type: "success", colorOverride: "green", children: showLabel ? (trueLabel ?? translate('ra.boolean.true', { _: 'Yes' })) : null })) : (_jsx(StatusIndicator, { type: "not-started", children: showLabel ? (falseLabel ?? translate('ra.boolean.false', { _: 'No' })) : null }));
11
+ const content = isTrue ? (_jsx(StatusIndicator, { type: "success", colorOverride: "green", children: showLabel ? (trueLabel ?? translate('strato.boolean.true', { _: 'Yes' })) : null })) : (_jsx(StatusIndicator, { type: "not-started", children: showLabel ? (falseLabel ?? translate('strato.boolean.false', { _: 'No' })) : null }));
12
12
  return _jsx(RecordLink, { link: link, children: content });
13
13
  };
14
14
  export default BooleanField;
@@ -1,4 +1,4 @@
1
- import { type RaRecord } from '@strato-admin/core';
1
+ import { type RaRecord } from '@strato-admin/ra-core';
2
2
  import { type FieldProps } from './types';
3
3
  export type CurrencyFieldProps<RecordType extends RaRecord = RaRecord> = FieldProps<RecordType> & {
4
4
  /**
@@ -1,5 +1,5 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
2
- import { useFieldValue, useRecordContext, useLocale } from '@strato-admin/core';
2
+ import { useFieldValue, useRecordContext, useLocale } from '@strato-admin/ra-core';
3
3
  import RecordLink from '../RecordLink';
4
4
  const CurrencyField = (props) => {
5
5
  const { source, record: recordProp, emptyText, options, locales, link, currency } = props;
@@ -1,4 +1,4 @@
1
- import { type RaRecord } from '@strato-admin/core';
1
+ import { type RaRecord } from '@strato-admin/ra-core';
2
2
  import { type FieldProps } from './types';
3
3
  export type DateFieldProps<RecordType extends RaRecord = RaRecord> = FieldProps<RecordType> & {
4
4
  /**