@strato-admin/core 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Vadim Gubergrits
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,3 @@
1
+ Shadow `@strato-admin/ra-core` for future extencility or replacement of replacement of
2
+ `@strato-admin/ra-core` with a custom implementation. This allows us to have more control over
3
+ the core logic and potentially add features that are not available in `@strato-admin/ra-core`.
File without changes
@@ -0,0 +1 @@
1
+ "use strict";
File without changes
@@ -0,0 +1 @@
1
+ "use strict";
@@ -0,0 +1,3 @@
1
+ export * from '@strato-admin/ra-core';
2
+ export * from './resource';
3
+ export { ResourceContext } from './resource';
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export * from '@strato-admin/ra-core';
2
+ export * from './resource';
3
+ // Resolve ambiguity between ra-core and strato-core resource exports.
4
+ export { ResourceContext } from './resource';
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,296 @@
1
+ /*
2
+ This file contains a subset of react-admin messages used by ra-core directly.
3
+
4
+ Some translations have issues in languages with gender and case. There's no easy
5
+ fix for this. We cannot interpolate the resource name in a message like "Delete
6
+ {name}" and expect it to be grammatically correct in all languages.
7
+
8
+ A workaround requires the translator to torture the phrase in order to place the
9
+ interpolated variable in nominative case. Message with translation issues are
10
+ marked with a comment starting with "Translation issue".
11
+ */
12
+ import { defineMessage } from 'react-intl';
13
+ defineMessage({
14
+ id: 'ra.action.add',
15
+ defaultMessage: 'Add',
16
+ description: 'Label for the button to add a new record',
17
+ });
18
+ defineMessage({
19
+ id: 'ra.action.cancel',
20
+ defaultMessage: 'Cancel',
21
+ description: 'Label for the button to cancel an action',
22
+ });
23
+ defineMessage({
24
+ id: 'ra.action.clear_array_input',
25
+ defaultMessage: 'Clear the list',
26
+ description: 'Label for the button to clear an array input',
27
+ });
28
+ defineMessage({
29
+ id: 'ra.action.close',
30
+ defaultMessage: 'Close',
31
+ description: 'Label for the button to close a dialog or panel',
32
+ });
33
+ defineMessage({
34
+ id: 'ra.action.confirm',
35
+ defaultMessage: 'Confirm',
36
+ description: 'Label for the button to confirm an action',
37
+ });
38
+ defineMessage({
39
+ id: 'ra.action.create',
40
+ defaultMessage: 'Create',
41
+ description: 'Label for the button to create a new record',
42
+ });
43
+ defineMessage({
44
+ id: 'ra.action.delete',
45
+ defaultMessage: 'Delete',
46
+ description: 'Label for the button to delete one or more records',
47
+ });
48
+ defineMessage({
49
+ id: 'ra.action.edit',
50
+ defaultMessage: 'Edit',
51
+ description: 'Label for the button to edit a record',
52
+ });
53
+ defineMessage({
54
+ id: 'ra.action.move_down',
55
+ defaultMessage: 'Move down',
56
+ description: 'Label for the button to move an item down in a list',
57
+ });
58
+ defineMessage({
59
+ id: 'ra.action.move_up',
60
+ defaultMessage: 'Move up',
61
+ description: 'Label for the button to move an item up in a list',
62
+ });
63
+ defineMessage({
64
+ id: 'ra.action.refresh',
65
+ defaultMessage: 'Refresh',
66
+ description: 'Label for the button to refresh the current view',
67
+ });
68
+ defineMessage({
69
+ id: 'ra.action.remove',
70
+ defaultMessage: 'Remove',
71
+ description: 'Label for the button to remove an item',
72
+ });
73
+ defineMessage({
74
+ id: 'ra.action.save',
75
+ defaultMessage: 'Save',
76
+ description: 'Label for the button to save changes',
77
+ });
78
+ defineMessage({
79
+ id: 'ra.action.undo',
80
+ defaultMessage: 'Undo',
81
+ description: 'Label for the button to undo the last action',
82
+ });
83
+ defineMessage({
84
+ id: 'ra.action.update',
85
+ defaultMessage: 'Update',
86
+ description: 'Label for the button to update a record',
87
+ });
88
+ defineMessage({
89
+ id: 'ra.auth.auth_check_error',
90
+ defaultMessage: 'Please login to continue',
91
+ description: 'Message shown when the user needs to login',
92
+ });
93
+ defineMessage({
94
+ id: 'ra.boolean.true',
95
+ defaultMessage: 'Yes',
96
+ description: 'Label for the true boolean value',
97
+ });
98
+ defineMessage({
99
+ id: 'ra.boolean.false',
100
+ defaultMessage: 'No',
101
+ description: 'Label for the false boolean value',
102
+ });
103
+ defineMessage({
104
+ id: 'ra.input.references.all_missing',
105
+ defaultMessage: 'Unable to find references data.',
106
+ description: 'Error message when all references are missing',
107
+ });
108
+ defineMessage({
109
+ id: 'ra.input.references.many_missing',
110
+ defaultMessage: 'At least one of the associated references no longer appears to be available.',
111
+ description: 'Error message when many references are missing',
112
+ });
113
+ defineMessage({
114
+ id: 'ra.input.references.single_missing',
115
+ defaultMessage: 'Associated reference no longer appears to be available.',
116
+ description: 'Error message when a single reference is missing',
117
+ });
118
+ defineMessage({
119
+ id: 'ra.message.clear_array_input',
120
+ defaultMessage: 'Are you sure you want to clear the whole list?',
121
+ description: 'Confirmation message when clearing an array input',
122
+ });
123
+ // Translation issue: {name} gender must agree with "this" (e.g., ce/cette, этот/эту).
124
+ defineMessage({
125
+ id: 'ra.message.delete_content',
126
+ defaultMessage: 'Are you sure you want to delete this {name}?',
127
+ description: 'Confirmation message when deleting a record',
128
+ });
129
+ // Translation issue: {name} might require the Accusative case (e.g., Delete "Usera").
130
+ defineMessage({
131
+ id: 'ra.message.delete_title',
132
+ defaultMessage: 'Delete {name} {recordRepresentation}',
133
+ description: 'Title for the delete confirmation dialog',
134
+ });
135
+ defineMessage({
136
+ id: 'ra.message.invalid_form',
137
+ defaultMessage: 'The form is not valid. Please check for errors',
138
+ description: 'Error message shown when a form is invalid',
139
+ });
140
+ defineMessage({
141
+ id: 'ra.message.select_all_limit_reached',
142
+ defaultMessage: `
143
+ {max, plural,
144
+ one {There are too many elements to select them all. Only the first element was selected.}
145
+ other {There are too many elements to select them all. Only the first # elements were selected.}
146
+ }`,
147
+ description: 'Message shown when select all reaches its limit',
148
+ });
149
+ defineMessage({
150
+ id: 'ra.message.unsaved_changes',
151
+ defaultMessage: "Some of your changes weren't saved. Are you sure you want to ignore them?",
152
+ description: 'Confirmation message when leaving a page with unsaved changes',
153
+ });
154
+ // Translation issue: "update {name}" may require Accusative case; "these" must agree with gender of "items".
155
+ defineMessage({
156
+ id: 'ra.message.update_content',
157
+ defaultMessage: '{smart_count, plural, one {Are you sure you want to update {name} {recordRepresentation}?} other {Are you sure you want to update these # items?}}',
158
+ description: 'Confirmation message when updating one or more records',
159
+ });
160
+ // Translation issue: {name} might require the Accusative case (e.g., Update "Usera").
161
+ defineMessage({
162
+ id: 'ra.message.update_title',
163
+ defaultMessage: '{smart_count, plural, one {Update {name} {recordRepresentation}} other {Update # {name}}}',
164
+ description: 'Title for the update confirmation dialog',
165
+ });
166
+ defineMessage({
167
+ id: 'ra.navigation.next',
168
+ defaultMessage: 'Go to next page',
169
+ description: 'Label for the button to go to the next page',
170
+ });
171
+ defineMessage({
172
+ id: 'ra.navigation.page',
173
+ defaultMessage: 'Go to page {page}',
174
+ description: 'Label for the button to go to a specific page',
175
+ });
176
+ defineMessage({
177
+ id: 'ra.navigation.page_range_info',
178
+ defaultMessage: '{offsetBegin}-{offsetEnd} of {total}',
179
+ description: 'Message showing the current page range and total number of records',
180
+ });
181
+ defineMessage({
182
+ id: 'ra.navigation.previous',
183
+ defaultMessage: 'Go to previous page',
184
+ description: 'Label for the button to go to the previous page',
185
+ });
186
+ defineMessage({
187
+ id: 'ra.notification.created',
188
+ defaultMessage: 'Element created',
189
+ description: 'Notification shown when a record is successfully created',
190
+ });
191
+ defineMessage({
192
+ id: 'ra.notification.data_provider_error',
193
+ defaultMessage: 'dataProvider error. Check the console for details.',
194
+ description: 'Notification shown when a dataProvider error occurs',
195
+ });
196
+ defineMessage({
197
+ id: 'ra.notification.deleted',
198
+ defaultMessage: '{smart_count, plural, one {Element deleted} other {# elements deleted}}',
199
+ description: 'Notification shown when one or more records are successfully deleted',
200
+ });
201
+ defineMessage({
202
+ id: 'ra.notification.http_error',
203
+ defaultMessage: 'Server communication error',
204
+ description: 'Notification shown when an HTTP error occurs',
205
+ });
206
+ defineMessage({
207
+ id: 'ra.notification.item_doesnt_exist',
208
+ defaultMessage: 'Element does not exist',
209
+ description: 'Notification shown when a record does not exist',
210
+ });
211
+ defineMessage({
212
+ id: 'ra.notification.logged_out',
213
+ defaultMessage: 'Your session has ended, please reconnect.',
214
+ description: 'Notification shown when the user is logged out',
215
+ });
216
+ defineMessage({
217
+ id: 'ra.notification.not_authorized',
218
+ defaultMessage: "You're not authorized to access this resource.",
219
+ description: 'Notification shown when the user is not authorized',
220
+ });
221
+ defineMessage({
222
+ id: 'ra.notification.updated',
223
+ defaultMessage: '{smart_count, plural, one {Element updated} other {# elements updated}}',
224
+ description: 'Notification shown when one or more records are successfully updated',
225
+ });
226
+ // Translation issue: {name} might require the Accusative case (e.g., Create "Usera").
227
+ defineMessage({
228
+ id: 'ra.page.create',
229
+ defaultMessage: 'Create {name}',
230
+ description: 'Title for the record creation page',
231
+ });
232
+ defineMessage({
233
+ id: 'ra.page.edit',
234
+ defaultMessage: '{name} {recordRepresentation}',
235
+ description: 'Title for the record edition page',
236
+ });
237
+ defineMessage({
238
+ id: 'ra.page.list',
239
+ defaultMessage: '{name}',
240
+ description: 'Title for the record list page',
241
+ });
242
+ defineMessage({
243
+ id: 'ra.page.show',
244
+ defaultMessage: '{name} {recordRepresentation}',
245
+ description: 'Title for the record show page',
246
+ });
247
+ defineMessage({
248
+ id: 'ra.validation.email',
249
+ defaultMessage: 'Must be a valid email',
250
+ description: 'Validation error message for email inputs',
251
+ });
252
+ defineMessage({
253
+ id: 'ra.validation.maxLength',
254
+ defaultMessage: '{max, plural, one {Must be # character or less} other {Must be # characters or less}}',
255
+ description: 'Validation error message for maxLength',
256
+ });
257
+ defineMessage({
258
+ id: 'ra.validation.maxValue',
259
+ defaultMessage: '{max, plural, other {Must be # or less}}',
260
+ description: 'Validation error message for maxValue',
261
+ });
262
+ defineMessage({
263
+ id: 'ra.validation.minLength',
264
+ defaultMessage: '{min, plural, one {Must be # character at least} other {Must be # characters at least}}',
265
+ description: 'Validation error message for minLength',
266
+ });
267
+ defineMessage({
268
+ id: 'ra.validation.minValue',
269
+ defaultMessage: '{min, plural, other {Must be at least #}}',
270
+ description: 'Validation error message for minValue',
271
+ });
272
+ defineMessage({
273
+ id: 'ra.validation.number',
274
+ defaultMessage: 'Must be a number',
275
+ description: 'Validation error message for number inputs',
276
+ });
277
+ defineMessage({
278
+ id: 'ra.validation.oneOf',
279
+ defaultMessage: 'Must be one of: {options}',
280
+ description: 'Validation error message for oneOf',
281
+ });
282
+ defineMessage({
283
+ id: 'ra.validation.regex',
284
+ defaultMessage: 'Must match a specific format (regexp): {pattern}',
285
+ description: 'Validation error message for regex',
286
+ });
287
+ defineMessage({
288
+ id: 'ra.validation.required',
289
+ defaultMessage: 'Required',
290
+ description: 'Validation error message for required inputs',
291
+ });
292
+ defineMessage({
293
+ id: 'ra.validation.unique',
294
+ defaultMessage: 'Must be unique',
295
+ description: 'Validation error message for unique inputs',
296
+ });
@@ -0,0 +1,18 @@
1
+ import { ReactNode } from 'react';
2
+ /**
3
+ * Context for the field schema.
4
+ * Stores an array of React elements (fields) defined in the FieldSchema.
5
+ */
6
+ export declare const FieldSchemaContext: import("react").Context<ReactNode[]>;
7
+ /**
8
+ * Hook to access the field schema elements.
9
+ */
10
+ export declare const useFieldSchema: () => ReactNode[];
11
+ export interface FieldSchemaProps {
12
+ children: ReactNode;
13
+ }
14
+ /**
15
+ * A marker component to define the fields of a resource.
16
+ * It doesn't render anything by itself, it's used by StratoResource to capture the schema.
17
+ */
18
+ export declare const FieldSchema: (_props: FieldSchemaProps) => null;
@@ -0,0 +1,19 @@
1
+ import { createContext, useContext } from 'react';
2
+ /**
3
+ * Context for the field schema.
4
+ * Stores an array of React elements (fields) defined in the FieldSchema.
5
+ */
6
+ export const FieldSchemaContext = createContext([]);
7
+ /**
8
+ * Hook to access the field schema elements.
9
+ */
10
+ export const useFieldSchema = () => {
11
+ return useContext(FieldSchemaContext);
12
+ };
13
+ /**
14
+ * A marker component to define the fields of a resource.
15
+ * It doesn't render anything by itself, it's used by StratoResource to capture the schema.
16
+ */
17
+ export const FieldSchema = (_props) => {
18
+ return null;
19
+ };
@@ -0,0 +1,18 @@
1
+ import { ReactNode } from 'react';
2
+ /**
3
+ * Context for the input schema.
4
+ * Stores an array of React elements (inputs) defined in the InputSchema.
5
+ */
6
+ export declare const InputSchemaContext: import("react").Context<ReactNode[]>;
7
+ /**
8
+ * Hook to access the input schema elements.
9
+ */
10
+ export declare const useInputSchema: () => ReactNode[];
11
+ export interface InputSchemaProps {
12
+ children: ReactNode;
13
+ }
14
+ /**
15
+ * A marker component to define the form inputs of a resource.
16
+ * It doesn't render anything by itself, it's used by StratoResource to capture the schema.
17
+ */
18
+ export declare const InputSchema: (_props: InputSchemaProps) => null;
@@ -0,0 +1,19 @@
1
+ import { createContext, useContext } from 'react';
2
+ /**
3
+ * Context for the input schema.
4
+ * Stores an array of React elements (inputs) defined in the InputSchema.
5
+ */
6
+ export const InputSchemaContext = createContext([]);
7
+ /**
8
+ * Hook to access the input schema elements.
9
+ */
10
+ export const useInputSchema = () => {
11
+ return useContext(InputSchemaContext);
12
+ };
13
+ /**
14
+ * A marker component to define the form inputs of a resource.
15
+ * It doesn't render anything by itself, it's used by StratoResource to capture the schema.
16
+ */
17
+ export const InputSchema = (_props) => {
18
+ return null;
19
+ };
@@ -0,0 +1,2 @@
1
+ export declare const ResourceContext: import("react").Context<string | undefined>;
2
+ export declare const useResource: () => string | undefined;
@@ -0,0 +1,3 @@
1
+ import { createContext, useContext } from 'react';
2
+ export const ResourceContext = createContext(undefined);
3
+ export const useResource = () => useContext(ResourceContext);
@@ -0,0 +1,27 @@
1
+ import { ReactNode } from 'react';
2
+ import { ResourceProps } from '@strato-admin/ra-core';
3
+ export interface ResourceSchemaProps extends ResourceProps {
4
+ fieldSchema?: ReactNode;
5
+ inputSchema?: ReactNode;
6
+ children?: ReactNode;
7
+ label?: string;
8
+ canCreate?: boolean;
9
+ canEdit?: boolean;
10
+ canDelete?: boolean;
11
+ canShowDetails?: boolean;
12
+ canList?: boolean;
13
+ }
14
+ /**
15
+ * A wrapper around React-Admin's <Resource> that allows defining
16
+ * the field and input schemas via children or props.
17
+ *
18
+ * @example
19
+ * <ResourceSchema name="posts" list={PostList}>
20
+ * <TextField source="title" />
21
+ * </ResourceSchema>
22
+ */
23
+ export declare const ResourceSchema: {
24
+ ({ fieldSchema: explicitFieldSchema, inputSchema: explicitInputSchema, children, label, options, canCreate, canEdit, canDelete, canShowDetails, canList, ...props }: ResourceSchemaProps): import("react/jsx-runtime").JSX.Element;
25
+ raName: string;
26
+ registerResource(props: ResourceSchemaProps): any;
27
+ };
@@ -0,0 +1,69 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Resource } from '@strato-admin/ra-core';
3
+ import { ResourceSchemaProvider } from './ResourceSchemaProvider';
4
+ import { registerGlobalSchemas, useSchemaRegistry, getDefaultResourceComponents, parseUnifiedSchema } from './SchemaRegistry';
5
+ /**
6
+ * A wrapper around React-Admin's <Resource> that allows defining
7
+ * the field and input schemas via children or props.
8
+ *
9
+ * @example
10
+ * <ResourceSchema name="posts" list={PostList}>
11
+ * <TextField source="title" />
12
+ * </ResourceSchema>
13
+ */
14
+ export const ResourceSchema = ({ fieldSchema: explicitFieldSchema, inputSchema: explicitInputSchema, children, label, options, canCreate = true, canEdit = true, canDelete = true, canShowDetails = true, canList = true, ...props }) => {
15
+ const { defaultComponents } = useSchemaRegistry();
16
+ const parsedSchemas = children ? parseUnifiedSchema(children) : {};
17
+ const fieldSchema = explicitFieldSchema || parsedSchemas.fieldSchema;
18
+ const inputSchema = explicitInputSchema || parsedSchemas.inputSchema;
19
+ const mergedOptions = {
20
+ ...options,
21
+ ...(label ? { label } : {}),
22
+ canCreate,
23
+ canEdit,
24
+ canDelete,
25
+ canShowDetails,
26
+ canList,
27
+ };
28
+ const finalProps = {
29
+ ...props,
30
+ list: canList ? (props.list || defaultComponents.list) : undefined,
31
+ create: canCreate ? (props.create || defaultComponents.create) : undefined,
32
+ edit: canEdit ? (props.edit || defaultComponents.edit) : undefined,
33
+ show: canShowDetails ? (props.show || defaultComponents.show) : undefined,
34
+ };
35
+ return (_jsx(ResourceSchemaProvider, { resource: props.name, fieldSchema: fieldSchema, inputSchema: inputSchema, children: _jsx(Resource, { ...finalProps, options: mergedOptions }) }));
36
+ };
37
+ ResourceSchema.raName = 'Resource';
38
+ /**
39
+ * This is called by React-Admin during Admin initialization.
40
+ * We use it to register schemas globally before any component renders.
41
+ */
42
+ ResourceSchema.registerResource = (props) => {
43
+ const { name, fieldSchema: explicitFieldSchema, inputSchema: explicitInputSchema, children, label, options, canCreate = true, canEdit = true, canDelete = true, canShowDetails = true, canList = true, list, create, edit, show, } = props;
44
+ const parsedSchemas = children ? parseUnifiedSchema(children) : {};
45
+ const fieldSchema = explicitFieldSchema || parsedSchemas.fieldSchema;
46
+ const inputSchema = explicitInputSchema || parsedSchemas.inputSchema;
47
+ if (name && (fieldSchema || inputSchema)) {
48
+ registerGlobalSchemas(name, { fieldSchema, inputSchema });
49
+ }
50
+ const defaultComponents = getDefaultResourceComponents();
51
+ const mergedOptions = {
52
+ ...options,
53
+ ...(label ? { label } : {}),
54
+ canCreate,
55
+ canEdit,
56
+ canDelete,
57
+ canShowDetails,
58
+ canList,
59
+ };
60
+ const finalProps = {
61
+ ...props,
62
+ list: canList ? (list || defaultComponents.list) : undefined,
63
+ create: canCreate ? (create || defaultComponents.create) : undefined,
64
+ edit: canEdit ? (edit || defaultComponents.edit) : undefined,
65
+ show: canShowDetails ? (show || defaultComponents.show) : undefined,
66
+ options: mergedOptions,
67
+ };
68
+ return Resource.registerResource(finalProps);
69
+ };
@@ -0,0 +1,15 @@
1
+ import { ReactNode } from 'react';
2
+ export interface ResourceSchemaProviderProps {
3
+ children: ReactNode;
4
+ resource?: string;
5
+ fieldSchema?: ReactNode;
6
+ inputSchema?: ReactNode;
7
+ }
8
+ /**
9
+ * Provides the field and input schemas to its children via context.
10
+ * This is a lightweight version of ResourceSchema that does not register
11
+ * a React-Admin <Resource> or define routes.
12
+ *
13
+ * It can also look up schemas in the central registry if a resource name is provided.
14
+ */
15
+ export declare const ResourceSchemaProvider: ({ children, resource, fieldSchema, inputSchema, }: ResourceSchemaProviderProps) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,71 @@
1
+ import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import React, { Children, useMemo, useContext } from 'react';
3
+ import { FieldSchemaContext } from './FieldSchema';
4
+ import { InputSchemaContext } from './InputSchema';
5
+ import { useSchemaRegistry } from './SchemaRegistry';
6
+ import { ResourceContext } from './ResourceContext';
7
+ /**
8
+ * Provides the field and input schemas to its children via context.
9
+ * This is a lightweight version of ResourceSchema that does not register
10
+ * a React-Admin <Resource> or define routes.
11
+ *
12
+ * It can also look up schemas in the central registry if a resource name is provided.
13
+ */
14
+ export const ResourceSchemaProvider = ({ children, resource, fieldSchema, inputSchema, }) => {
15
+ const { registerSchemas, getSchemas } = useSchemaRegistry();
16
+ const parentResource = useContext(ResourceContext);
17
+ // 1. Register schemas synchronously during render.
18
+ useMemo(() => {
19
+ if (resource && (fieldSchema || inputSchema)) {
20
+ registerSchemas(resource, { fieldSchema, inputSchema });
21
+ }
22
+ }, [resource, fieldSchema, inputSchema, registerSchemas]);
23
+ // 2. Lookup schemas if not provided
24
+ // We MUST check the registry if resource is provided, even if we have some schemas from props,
25
+ // because props might only contain ONE of them (e.g. only fieldSchema).
26
+ const registrySchemas = useMemo(() => {
27
+ const effectiveResource = resource || parentResource;
28
+ if (effectiveResource) {
29
+ return getSchemas(effectiveResource);
30
+ }
31
+ return undefined;
32
+ }, [resource, parentResource, getSchemas]);
33
+ const finalFieldSchema = fieldSchema || registrySchemas?.fieldSchema;
34
+ const finalInputSchema = inputSchema || registrySchemas?.inputSchema;
35
+ // Extract children if the schemas are passed as <FieldSchema> or <InputSchema> elements
36
+ const getChildren = (schema) => {
37
+ if (React.isValidElement(schema)) {
38
+ if ('children' in schema.props) {
39
+ return schema.props.children;
40
+ }
41
+ }
42
+ return schema;
43
+ };
44
+ const fieldChildren = useMemo(() => finalFieldSchema ? Children.toArray(getChildren(finalFieldSchema)) : undefined, [finalFieldSchema]);
45
+ const inputChildren = useMemo(() => finalInputSchema ? Children.toArray(getChildren(finalInputSchema)) : undefined, [finalInputSchema]);
46
+ let content = children;
47
+ // 3. Wrap in new ResourceContext if the resource changed.
48
+ if (resource && resource !== parentResource) {
49
+ content = (_jsx(ResourceContext.Provider, { value: resource, children: content }));
50
+ }
51
+ // 4. Wrap in Schema Providers.
52
+ // We provide a new context if:
53
+ // - The resource changed (prevents leakage)
54
+ // - OR we have explicit schema children to provide
55
+ if (resource && resource !== parentResource) {
56
+ // If resource changed, we MUST provide a context to isolate children.
57
+ // If no schema found, we provide [] ONLY IF we are confident it's a known resource.
58
+ // Actually, providing [] is safer to block leakage, but Table needs to handle it.
59
+ content = (_jsx(FieldSchemaContext.Provider, { value: fieldChildren || [], children: _jsx(InputSchemaContext.Provider, { value: inputChildren || [], children: content }) }));
60
+ }
61
+ else {
62
+ // Resource is the same (or not provided), only wrap if we have NEW schema content
63
+ if (fieldChildren) {
64
+ content = (_jsx(FieldSchemaContext.Provider, { value: fieldChildren, children: content }));
65
+ }
66
+ if (inputChildren) {
67
+ content = (_jsx(InputSchemaContext.Provider, { value: inputChildren, children: content }));
68
+ }
69
+ }
70
+ return _jsx(_Fragment, { children: content });
71
+ };
@@ -0,0 +1,45 @@
1
+ import React, { ReactNode, ComponentType } from 'react';
2
+ export interface ResourceSchemas {
3
+ fieldSchema?: ReactNode;
4
+ inputSchema?: ReactNode;
5
+ }
6
+ export interface DefaultResourceComponents {
7
+ list?: ComponentType<any>;
8
+ create?: ComponentType<any>;
9
+ edit?: ComponentType<any>;
10
+ show?: ComponentType<any>;
11
+ }
12
+ export interface SchemaRegistryContextValue {
13
+ registerSchemas: (resource: string, schemas: ResourceSchemas) => void;
14
+ getSchemas: (resource: string) => ResourceSchemas | undefined;
15
+ defaultComponents: DefaultResourceComponents;
16
+ }
17
+ /**
18
+ * Registers default input components for given field components.
19
+ * This mapping is used by ResourceSchemaProvider to infer forms.
20
+ */
21
+ export declare const registerFieldInputMapping: (mapping: Map<any, any>) => void;
22
+ /**
23
+ * Retrieves the registered default input component for a field.
24
+ */
25
+ export declare const getDefaultInputForField: (fieldComponent: any) => any;
26
+ /**
27
+ * Synchronously registers schemas for a resource.
28
+ * Can be called during render or outside of React.
29
+ */
30
+ export declare const registerGlobalSchemas: (resource: string, schemas: ResourceSchemas) => boolean | undefined;
31
+ /**
32
+ * Parses unified schema children (Field components) into separate
33
+ * fieldSchema and inputSchema arrays.
34
+ */
35
+ export declare const parseUnifiedSchema: (children: React.ReactNode) => ResourceSchemas;
36
+ export declare const getGlobalSchemas: (resource: string) => ResourceSchemas;
37
+ /**
38
+ * Registers default components to be used by ResourceSchema when not explicitly provided.
39
+ */
40
+ export declare const registerDefaultResourceComponents: (components: DefaultResourceComponents) => void;
41
+ export declare const getDefaultResourceComponents: () => DefaultResourceComponents;
42
+ export declare const SchemaRegistryProvider: ({ children }: {
43
+ children: ReactNode;
44
+ }) => import("react/jsx-runtime").JSX.Element;
45
+ export declare const useSchemaRegistry: () => SchemaRegistryContextValue;