@fougere/admin 0.3.0-alpha.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 +21 -0
- package/README.md +71 -0
- package/dist/dashboard.d.ts +74 -0
- package/dist/dashboard.d.ts.map +1 -0
- package/dist/dashboard.js +334 -0
- package/dist/dashboard.js.map +1 -0
- package/dist/extensions.d.ts +27 -0
- package/dist/extensions.d.ts.map +1 -0
- package/dist/extensions.js +74 -0
- package/dist/extensions.js.map +1 -0
- package/dist/facets.d.ts +60 -0
- package/dist/facets.d.ts.map +1 -0
- package/dist/facets.js +39 -0
- package/dist/facets.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/provider.d.ts +163 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +205 -0
- package/dist/provider.js.map +1 -0
- package/dist/react.d.ts +70 -0
- package/dist/react.d.ts.map +1 -0
- package/dist/react.js +235 -0
- package/dist/react.js.map +1 -0
- package/dist/resources.d.ts +69 -0
- package/dist/resources.d.ts.map +1 -0
- package/dist/resources.js +92 -0
- package/dist/resources.js.map +1 -0
- package/dist/runtime.d.ts +22 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +36 -0
- package/dist/runtime.js.map +1 -0
- package/dist/theme.d.ts +10 -0
- package/dist/theme.d.ts.map +1 -0
- package/dist/theme.js +419 -0
- package/dist/theme.js.map +1 -0
- package/dist/topology-page.d.ts +8 -0
- package/dist/topology-page.d.ts.map +1 -0
- package/dist/topology-page.js +69 -0
- package/dist/topology-page.js.map +1 -0
- package/dist/topology.d.ts +46 -0
- package/dist/topology.d.ts.map +1 -0
- package/dist/topology.js +58 -0
- package/dist/topology.js.map +1 -0
- package/package.json +76 -0
package/dist/react.d.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The rendering half — a column becomes a field, a form field becomes an input.
|
|
3
|
+
*
|
|
4
|
+
* Two maps and a loop. Everything that DECIDES lives in `resources.ts`; what is here
|
|
5
|
+
* only spells the decision in react-admin's vocabulary, which is why this file has no
|
|
6
|
+
* conditional beyond the two lookups: a `render` that had to be interpreted would mean
|
|
7
|
+
* the interpretation belonged upstream, in the projection that produced it.
|
|
8
|
+
*/
|
|
9
|
+
import { Admin } from 'react-admin';
|
|
10
|
+
import { type ComponentProps, type ComponentType, type ReactElement } from 'react';
|
|
11
|
+
import type { AdminExtension } from './extensions.js';
|
|
12
|
+
import { type AdminResource } from './resources.js';
|
|
13
|
+
import type { Fetcher } from '@fougere/app/client';
|
|
14
|
+
import { type FormField, type TableColumn } from '@fougere/app/client';
|
|
15
|
+
export { FougereTopology, type FougereTopologyProps } from './topology-page.js';
|
|
16
|
+
import { type FougereDashboardExtension } from './dashboard.js';
|
|
17
|
+
export { FougereAppBar, FougereLayout, FougereMark, fougereDarkTheme, fougereLightTheme, } from './theme.js';
|
|
18
|
+
export { FOUGERE_DASHBOARD_WIDGETS, FougereContentIcon, FougereDashboard, FougereUsersIcon, applyDashboardExtensions, useFougereDashboard, type FougereDashboardContextValue, type FougereDashboardExtension, type FougereDashboardMetrics, type FougereDashboardResource, type FougereDashboardWidget, type FougereDashboardZone, type FougereResourceOptions, } from './dashboard.js';
|
|
19
|
+
export interface ReactAdminFieldContext {
|
|
20
|
+
resource: AdminResource;
|
|
21
|
+
column: TableColumn;
|
|
22
|
+
/** The maintained Fougere renderer. Call it to wrap rather than replace it. */
|
|
23
|
+
defaultRender(): ReactElement;
|
|
24
|
+
}
|
|
25
|
+
export interface ReactAdminInputContext {
|
|
26
|
+
resource: AdminResource;
|
|
27
|
+
field: FormField;
|
|
28
|
+
defaultRender(): ReactElement;
|
|
29
|
+
}
|
|
30
|
+
export type ReactAdminFieldRenderer = (context: ReactAdminFieldContext) => ReactElement;
|
|
31
|
+
export type ReactAdminInputRenderer = (context: ReactAdminInputContext) => ReactElement;
|
|
32
|
+
export interface ReactAdminRenderers {
|
|
33
|
+
/** Exact `resource.field` keys. Unmentioned and future fields keep the default renderer. */
|
|
34
|
+
fields?: Record<string, ReactAdminFieldRenderer>;
|
|
35
|
+
inputs?: Record<string, ReactAdminInputRenderer>;
|
|
36
|
+
}
|
|
37
|
+
export interface ReactAdminResourceComponents {
|
|
38
|
+
list?: ComponentType;
|
|
39
|
+
show?: ComponentType;
|
|
40
|
+
edit?: ComponentType;
|
|
41
|
+
create?: ComponentType;
|
|
42
|
+
icon?: ComponentType;
|
|
43
|
+
}
|
|
44
|
+
export interface ResourceRenderOptions {
|
|
45
|
+
renderers?: ReactAdminRenderers;
|
|
46
|
+
components?: ReactAdminResourceComponents;
|
|
47
|
+
}
|
|
48
|
+
/** One door, as the four pages the card says it serves. */
|
|
49
|
+
export declare function resourceFor(r: AdminResource, options?: ResourceRenderOptions): ReactElement;
|
|
50
|
+
/**
|
|
51
|
+
* The whole back-office — derived defaults first, deltas and renderers second.
|
|
52
|
+
*
|
|
53
|
+
* `<Admin>` accepts a function child returning a promise of resources, so the menu is
|
|
54
|
+
* built at LOAD time from `rpc.discover` rather than at build time from a generator.
|
|
55
|
+
* Which is what lets this bundle be compiled once and shipped: no entity of the host
|
|
56
|
+
* app enters it, so there is nothing per-project left to build.
|
|
57
|
+
*/
|
|
58
|
+
type BaseAdminProps = ComponentProps<typeof Admin>;
|
|
59
|
+
export type FougereAdminProps = Omit<BaseAdminProps, 'children' | 'dataProvider'> & {
|
|
60
|
+
endpoint?: string;
|
|
61
|
+
fetcher?: Fetcher;
|
|
62
|
+
extensions?: readonly AdminExtension[];
|
|
63
|
+
renderers?: ReactAdminRenderers;
|
|
64
|
+
/** Add, move, resize, replace or hide widgets without snapshotting the dashboard. */
|
|
65
|
+
dashboardExtensions?: readonly FougereDashboardExtension[];
|
|
66
|
+
/** Explicit page-level escape hatches, scoped to one resource and one view. */
|
|
67
|
+
resourceComponents?: Record<string, ReactAdminResourceComponents>;
|
|
68
|
+
};
|
|
69
|
+
export declare function FougereAdmin({ endpoint, fetcher, extensions, renderers, dashboardExtensions, resourceComponents, theme, darkTheme, layout, dashboard: dashboardOverride, ...adminProps }: FougereAdminProps): ReactElement;
|
|
70
|
+
//# sourceMappingURL=react.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.tsx"],"names":[],"mappings":"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"}
|
package/dist/react.js
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* The rendering half — a column becomes a field, a form field becomes an input.
|
|
5
|
+
*
|
|
6
|
+
* Two maps and a loop. Everything that DECIDES lives in `resources.ts`; what is here
|
|
7
|
+
* only spells the decision in react-admin's vocabulary, which is why this file has no
|
|
8
|
+
* conditional beyond the two lookups: a `render` that had to be interpreted would mean
|
|
9
|
+
* the interpretation belonged upstream, in the projection that produced it.
|
|
10
|
+
*/
|
|
11
|
+
import { Admin, Resource, Datagrid, List, SimpleForm, Edit, Create, Show, SimpleShowLayout, TextField, NumberField, BooleanField, DateField, ReferenceField, FunctionField, TextInput, NumberInput, BooleanInput, DateTimeInput, SelectInput, Toolbar, SaveButton, EditButton, DeleteButton, WrapperField, CustomRoutes, useTranslate, useRecordContext, useDataProvider, useRefresh, useNotify, } from 'react-admin';
|
|
12
|
+
import { Route } from 'react-router-dom';
|
|
13
|
+
import { Box, Button, Card, CardContent, Dialog, DialogActions, DialogContent, DialogTitle, TextField as MuiTextField, Typography } from '@mui/material';
|
|
14
|
+
import { cloneElement, useMemo, useState } from 'react';
|
|
15
|
+
import { createAdminRuntime } from './runtime.js';
|
|
16
|
+
import { actionsOf } from './resources.js';
|
|
17
|
+
import { formFieldsOf } from '@fougere/app/client';
|
|
18
|
+
import { reconstruct } from '@fougere/schema';
|
|
19
|
+
import { FougereLayout, fougereDarkTheme, fougereLightTheme } from './theme.js';
|
|
20
|
+
import { FougereTopology } from './topology-page.js';
|
|
21
|
+
export { FougereTopology } from './topology-page.js';
|
|
22
|
+
import { FougereContentIcon, FougereDashboard, FougereUsersIcon, } from './dashboard.js';
|
|
23
|
+
export { FougereAppBar, FougereLayout, FougereMark, fougereDarkTheme, fougereLightTheme, } from './theme.js';
|
|
24
|
+
export { FOUGERE_DASHBOARD_WIDGETS, FougereContentIcon, FougereDashboard, FougereUsersIcon, applyDashboardExtensions, useFougereDashboard, } from './dashboard.js';
|
|
25
|
+
const FIELDS = {
|
|
26
|
+
text: TextField, number: NumberField, boolean: BooleanField, date: DateField,
|
|
27
|
+
};
|
|
28
|
+
const labelOf = (t, field) => t(field.labelKey, { _: field.label });
|
|
29
|
+
function defaultFieldFor(column, t) {
|
|
30
|
+
if (column.render === 'link') {
|
|
31
|
+
// `to` is the target's registration key, which is also its resource name — the
|
|
32
|
+
// card writes both with `registrationKeyOf`, so no mapping is needed here.
|
|
33
|
+
// No child on purpose: with one, `ReferenceField` prints whatever the child names —
|
|
34
|
+
// here the foreign id, twice over. Without one it renders the target's
|
|
35
|
+
// `recordRepresentation`, which is the human name, and brings a loading and an error
|
|
36
|
+
// state for free.
|
|
37
|
+
return (_jsx(ReferenceField, { source: column.name, reference: column.to, label: labelOf(t, column) }, column.name));
|
|
38
|
+
}
|
|
39
|
+
if (column.render === 'json') {
|
|
40
|
+
return (_jsx(FunctionField, { source: column.name, label: labelOf(t, column), render: (row) => JSON.stringify(row[column.name]) }, column.name));
|
|
41
|
+
}
|
|
42
|
+
const Field = FIELDS[column.render];
|
|
43
|
+
return _jsx(Field, { source: column.name, label: labelOf(t, column) }, column.name);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The bounds ride along under the names a browser already enforces — the same
|
|
47
|
+
* `attrs` a Vue form spreads onto its input. The judge reads the same shape either
|
|
48
|
+
* way; stating them here only means the refusal arrives while typing.
|
|
49
|
+
*/
|
|
50
|
+
function defaultInputFor(field, t) {
|
|
51
|
+
if (field.control === 'select') {
|
|
52
|
+
return (_jsx(SelectInput, { source: field.name, label: labelOf(t, field), isRequired: field.required, choices: (field.options ?? []).map((id) => ({ id, name: id })) }));
|
|
53
|
+
}
|
|
54
|
+
const common = {
|
|
55
|
+
source: field.name,
|
|
56
|
+
label: labelOf(t, field),
|
|
57
|
+
isRequired: field.required,
|
|
58
|
+
defaultValue: field.default,
|
|
59
|
+
};
|
|
60
|
+
if (field.control === 'number') {
|
|
61
|
+
return _jsx(NumberInput, { ...common, min: field.attrs?.min, max: field.attrs?.max });
|
|
62
|
+
}
|
|
63
|
+
if (field.control === 'boolean')
|
|
64
|
+
return _jsx(BooleanInput, { ...common });
|
|
65
|
+
if (field.control === 'date')
|
|
66
|
+
return _jsx(DateTimeInput, { ...common });
|
|
67
|
+
const { minlength, maxlength, ...attrs } = field.attrs ?? {};
|
|
68
|
+
return (_jsx(TextInput, { ...common, type: field.control, slotProps: {
|
|
69
|
+
htmlInput: {
|
|
70
|
+
...attrs,
|
|
71
|
+
...(minlength !== undefined ? { minLength: minlength } : {}),
|
|
72
|
+
...(maxlength !== undefined ? { maxLength: maxlength } : {}),
|
|
73
|
+
},
|
|
74
|
+
} }));
|
|
75
|
+
}
|
|
76
|
+
function fieldFor(resource, column, t, renderers) {
|
|
77
|
+
const defaultRender = () => defaultFieldFor(column, t);
|
|
78
|
+
const renderer = renderers?.fields?.[`${resource.name}.${column.name}`];
|
|
79
|
+
const rendered = renderer ? renderer({ resource, column, defaultRender }) : defaultRender();
|
|
80
|
+
return cloneElement(rendered, { key: column.name });
|
|
81
|
+
}
|
|
82
|
+
function inputFor(resource, field, t, renderers) {
|
|
83
|
+
const defaultRender = () => defaultInputFor(field, t);
|
|
84
|
+
const renderer = renderers?.inputs?.[`${resource.name}.${field.name}`];
|
|
85
|
+
const rendered = renderer ? renderer({ resource, field, defaultRender }) : defaultRender();
|
|
86
|
+
return cloneElement(rendered, { key: field.name });
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* A business operation, as a button.
|
|
90
|
+
*
|
|
91
|
+
* This is the only thing the panel has that a generic CRUD admin has not, and until now
|
|
92
|
+
* it had no reader at all: `AdminResource.operations` fed the stats widget and nothing
|
|
93
|
+
* else. In the demo that was demonstrable — `publish` is announced by the card, the
|
|
94
|
+
* extension gives it a label and a confirmation sentence, and there was no way to publish
|
|
95
|
+
* an article from the back-office.
|
|
96
|
+
*
|
|
97
|
+
* Three things are derived and none is declared per entity: WHICH ops (everything the
|
|
98
|
+
* door serves beyond the five verbs), WHETHER it asks for input (`op.input` — then the
|
|
99
|
+
* dialog's form is `formFieldsOf` over the reconstructed schema, the same projection an
|
|
100
|
+
* ordinary form uses), and WHETHER it confirms (`op.confirm`, from an extension).
|
|
101
|
+
*/
|
|
102
|
+
function OperationButton({ resource, operation, }) {
|
|
103
|
+
const record = useRecordContext();
|
|
104
|
+
const dataProvider = useDataProvider();
|
|
105
|
+
const refresh = useRefresh();
|
|
106
|
+
const notify = useNotify();
|
|
107
|
+
const t = useTranslate();
|
|
108
|
+
const [open, setOpen] = useState(false);
|
|
109
|
+
const [busy, setBusy] = useState(false);
|
|
110
|
+
const [values, setValues] = useState({});
|
|
111
|
+
const fields = useMemo(() => (operation.input ? formFieldsOf(reconstruct(operation.input), operation.name) : []), [operation]);
|
|
112
|
+
const asks = fields.length > 0 || !!operation.confirm;
|
|
113
|
+
const label = t(operation.label, { _: operation.label });
|
|
114
|
+
const run = async () => {
|
|
115
|
+
setBusy(true);
|
|
116
|
+
try {
|
|
117
|
+
await dataProvider.invoke(resource.name, {
|
|
118
|
+
op: operation.name,
|
|
119
|
+
...(record?.id !== undefined ? { id: record.id } : {}),
|
|
120
|
+
...(fields.length ? { data: values } : {}),
|
|
121
|
+
});
|
|
122
|
+
notify(t('fougere.admin.action.done', { _: '%{name} done', name: label }), { type: 'success' });
|
|
123
|
+
setOpen(false);
|
|
124
|
+
refresh();
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
notify(error?.message ?? String(error), { type: 'error' });
|
|
128
|
+
}
|
|
129
|
+
finally {
|
|
130
|
+
setBusy(false);
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
return (_jsxs(_Fragment, { children: [_jsx(Button, { size: "small", onClick: () => (asks ? setOpen(true) : void run()), disabled: busy, children: label }), _jsxs(Dialog, { open: open, onClose: () => setOpen(false), fullWidth: true, maxWidth: "xs", children: [_jsx(DialogTitle, { children: label }), _jsxs(DialogContent, { children: [operation.confirm && (_jsx(Typography, { variant: "body2", color: "text.secondary", sx: { mb: fields.length ? 2 : 0 }, children: t(operation.confirm, { _: operation.confirm }) })), fields.map((field) => (_jsx(MuiTextField, { label: t(field.labelKey, { _: field.label }), required: field.required, fullWidth: true, margin: "dense", onChange: (event) => setValues((v) => ({ ...v, [field.name]: event.target.value })) }, field.name)))] }), _jsxs(DialogActions, { children: [_jsx(Button, { onClick: () => setOpen(false), children: t('ra.action.cancel', { _: 'Cancel' }) }), _jsx(Button, { variant: "contained", onClick: () => void run(), disabled: busy, children: label })] })] })] }));
|
|
134
|
+
}
|
|
135
|
+
/** Row-level actions: the verbs the door serves, then everything else it serves. */
|
|
136
|
+
function RowActions({ resource }) {
|
|
137
|
+
const actions = useMemo(() => actionsOf(resource.operations), [resource]);
|
|
138
|
+
return (_jsxs(Box, { sx: { display: 'flex', gap: .5, justifyContent: 'flex-end' }, children: [actions.filter((op) => op.kind === 'command').map((op) => (_jsx(OperationButton, { resource: resource, operation: op }, op.name))), resource.can.edit && _jsx(EditButton, {}), resource.can.delete && _jsx(DeleteButton, {})] }));
|
|
139
|
+
}
|
|
140
|
+
const listFor = (r, renderers) => function ResourceList() {
|
|
141
|
+
const t = useTranslate();
|
|
142
|
+
return (_jsx(List, { children: _jsxs(Datagrid, { rowClick: r.can.show ? 'show' : r.can.edit ? 'edit' : false, bulkActionButtons: r.can.delete ? undefined : false, children: [r.columns.map((column) => fieldFor(r, column, t, renderers)), _jsx(WrapperField, { label: "", source: "__actions", children: _jsx(RowActions, { resource: r }) })] }) }));
|
|
143
|
+
};
|
|
144
|
+
const showFor = (r, renderers) => function ResourceShow() {
|
|
145
|
+
const t = useTranslate();
|
|
146
|
+
return (_jsx(Show, { children: _jsx(SimpleShowLayout, { children: r.columns.map((column) => fieldFor(r, column, t, renderers)) }) }));
|
|
147
|
+
};
|
|
148
|
+
/**
|
|
149
|
+
* `pessimistic`, and the reason is the provider's own work.
|
|
150
|
+
*
|
|
151
|
+
* react-admin edits `undoable` by default: it redirects at once, sends the call five
|
|
152
|
+
* seconds later, and reports a failure in a toast — by which time the form is unmounted.
|
|
153
|
+
* `useEditController` says it outright (`if (!hasValidationErrors || mutationMode !==
|
|
154
|
+
* 'pessimistic')`), so the per-field refusals `asAdminError` builds from
|
|
155
|
+
* `VALIDATION_FAILED` were computed, sent, and thrown away on every edit. Create was
|
|
156
|
+
* always pessimistic, which is why the same form judged differently depending on whether
|
|
157
|
+
* the row existed.
|
|
158
|
+
*/
|
|
159
|
+
const editFor = (r, renderers) => function ResourceEdit() {
|
|
160
|
+
const t = useTranslate();
|
|
161
|
+
return (_jsx(Edit, { mutationMode: "pessimistic", children: _jsx(SimpleForm, { toolbar: r.can.delete ? undefined : _jsx(Toolbar, { children: _jsx(SaveButton, {}) }), children: r.fields.map((field) => inputFor(r, field, t, renderers)) }) }));
|
|
162
|
+
};
|
|
163
|
+
const createFor = (r, renderers) => function ResourceCreate() {
|
|
164
|
+
const t = useTranslate();
|
|
165
|
+
return _jsx(Create, { children: _jsx(SimpleForm, { children: r.fields.map((field) => inputFor(r, field, t, renderers)) }) });
|
|
166
|
+
};
|
|
167
|
+
/** One door, as the four pages the card says it serves. */
|
|
168
|
+
export function resourceFor(r, options = {}) {
|
|
169
|
+
const { renderers, components = {} } = options;
|
|
170
|
+
const resourceOptions = {
|
|
171
|
+
label: r.label,
|
|
172
|
+
primary: r.primary,
|
|
173
|
+
facets: r.facets,
|
|
174
|
+
// The frond it belongs to and what it answers — the card said both, and a widget
|
|
175
|
+
// reporting on the app's own shape had no other way to reach them.
|
|
176
|
+
frond: r.frond,
|
|
177
|
+
operations: r.operations.map(({ name, kind }) => ({ name, kind })),
|
|
178
|
+
fieldCount: r.columns.length,
|
|
179
|
+
};
|
|
180
|
+
return (_jsx(Resource, { name: r.name, list: components.list ?? (r.can.list ? listFor(r, renderers) : undefined), show: components.show ?? (r.can.show ? showFor(r, renderers) : undefined), edit: components.edit ?? (r.can.edit ? editFor(r, renderers) : undefined), create: components.create ?? (r.can.create ? createFor(r, renderers) : undefined), icon: components.icon ?? (r.facets.users
|
|
181
|
+
? FougereUsersIcon
|
|
182
|
+
: r.facets.editorial
|
|
183
|
+
? FougereContentIcon
|
|
184
|
+
: undefined), options: resourceOptions,
|
|
185
|
+
// What names a row everywhere react-admin needs a name: the Show/Edit page title,
|
|
186
|
+
// an autocomplete label, the target of a reference. The declared facet says which
|
|
187
|
+
// field that is; the key is the fallback, and it was the only value used before —
|
|
188
|
+
// so every one of those places printed `post_1`.
|
|
189
|
+
recordRepresentation: r.facets.editorial?.title
|
|
190
|
+
?? r.facets.users?.name
|
|
191
|
+
?? r.primary }, r.name));
|
|
192
|
+
}
|
|
193
|
+
const EMPTY_EXTENSIONS = [];
|
|
194
|
+
/**
|
|
195
|
+
* What the panel shows when it could not ask what to show.
|
|
196
|
+
*
|
|
197
|
+
* The card IS the application as far as this bundle is concerned, so a refused
|
|
198
|
+
* `rpc.discover` leaves nothing to render — and react-admin's async children have no
|
|
199
|
+
* rejection path of their own: the promise rejects, `status` stays `'loading'`, and the
|
|
200
|
+
* operator watches a spinner with no message and no way out. That is the only state
|
|
201
|
+
* where the panel says nothing at all, so it is the one worth building by hand.
|
|
202
|
+
*/
|
|
203
|
+
function DiscoveryError({ error, onRetry }) {
|
|
204
|
+
const t = useTranslate();
|
|
205
|
+
const label = (key, fallback) => t(`fougere.admin.${key}`, { _: fallback });
|
|
206
|
+
return (_jsx(Box, { sx: { display: 'grid', placeItems: 'center', minHeight: '60vh', p: 3 }, children: _jsx(Card, { sx: { maxWidth: 460, width: '100%' }, children: _jsxs(CardContent, { sx: { p: 3, '&:last-child': { pb: 3 } }, children: [_jsx(Typography, { variant: "h3", sx: { mb: 1 }, children: label('error.discoveryTitle', 'This app did not answer') }), _jsx(Typography, { variant: "body2", color: "text.secondary", children: label('error.discoveryBody', 'The panel asks the app what it hosts before it can render anything. Check that the endpoint serves a Fougere app, and that it is running.') }), _jsx(Typography, { variant: "caption", component: "pre", sx: { mt: 2, p: 1.5, borderRadius: 1.5, bgcolor: 'action.hover', color: 'text.secondary', whiteSpace: 'pre-wrap', overflowWrap: 'anywhere' }, children: error?.message ?? String(error) }), _jsx(Button, { variant: "contained", onClick: onRetry, sx: { mt: 2.5 }, children: label('error.retry', 'Try again') })] }) }) }));
|
|
207
|
+
}
|
|
208
|
+
export function FougereAdmin({ endpoint, fetcher, extensions = EMPTY_EXTENSIONS, renderers, dashboardExtensions, resourceComponents, theme = fougereLightTheme, darkTheme = fougereDarkTheme, layout = FougereLayout, dashboard: dashboardOverride, ...adminProps }) {
|
|
209
|
+
// A retry rebuilds the runtime, which is what makes it a retry: the previous one
|
|
210
|
+
// holds a cleared slot, but react-admin only re-runs its async child when the tree
|
|
211
|
+
// changes, and this key is the change.
|
|
212
|
+
const [attempt, setAttempt] = useState(0);
|
|
213
|
+
const runtime = useMemo(() => createAdminRuntime({ endpoint, fetcher, extensions }), [endpoint, fetcher, extensions, attempt]);
|
|
214
|
+
const dashboard = useMemo(() => {
|
|
215
|
+
if (dashboardOverride !== undefined)
|
|
216
|
+
return dashboardOverride;
|
|
217
|
+
const DerivedDashboard = () => _jsx(FougereDashboard, { extensions: dashboardExtensions });
|
|
218
|
+
return DerivedDashboard;
|
|
219
|
+
}, [dashboardOverride, dashboardExtensions]);
|
|
220
|
+
return (_jsx(Admin, { ...adminProps, dataProvider: runtime.dataProvider, theme: theme, darkTheme: darkTheme, layout: layout, dashboard: dashboard, children: async () => {
|
|
221
|
+
try {
|
|
222
|
+
return [
|
|
223
|
+
...(await runtime.load()).resources.map((resource) => resourceFor(resource, {
|
|
224
|
+
renderers,
|
|
225
|
+
components: resourceComponents?.[resource.name],
|
|
226
|
+
})),
|
|
227
|
+
_jsx(CustomRoutes, { children: _jsx(Route, { path: "/topology", element: _jsx(FougereTopology, { ...(endpoint ? { endpoint } : {}), ...(fetcher ? { fetcher } : {}) }) }) }, "fougere.routes"),
|
|
228
|
+
];
|
|
229
|
+
}
|
|
230
|
+
catch (error) {
|
|
231
|
+
return (_jsx(CustomRoutes, { children: _jsx(Route, { path: "*", element: _jsx(DiscoveryError, { error: error, onRetry: () => setAttempt((n) => n + 1) }) }) }));
|
|
232
|
+
}
|
|
233
|
+
} }));
|
|
234
|
+
}
|
|
235
|
+
//# sourceMappingURL=react.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.js","sourceRoot":"","sources":["../src/react.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { type Fetcher, type FormField, type TableColumn } from '@fougere/app/client';
|
|
2
|
+
import { type CardOp, type IdentityCard } from '@fougere/core/contract';
|
|
3
|
+
import type { ResourceKey } from './provider.js';
|
|
4
|
+
import type { AdminFacets } from './facets.js';
|
|
5
|
+
/** One operation as the admin meets it, before a renderer decides its widget. */
|
|
6
|
+
export interface AdminOperation extends CardOp {
|
|
7
|
+
/** Display fallback. An extension may replace it without renaming the call. */
|
|
8
|
+
label: string;
|
|
9
|
+
/** Optional confirmation sentence, interpreted by renderers that support actions. */
|
|
10
|
+
confirm?: string;
|
|
11
|
+
}
|
|
12
|
+
/** One door, everything the UI needs to render it. */
|
|
13
|
+
export interface AdminResource extends ResourceKey {
|
|
14
|
+
/** The frond it belongs to — the menu groups by it, as the card does. */
|
|
15
|
+
frond: string;
|
|
16
|
+
/** Display fallback. The registration key in `name` never changes. */
|
|
17
|
+
label: string;
|
|
18
|
+
/**
|
|
19
|
+
* Declared semantic notions, empty until an extension states one. Nothing is
|
|
20
|
+
* inferred: what a closed set's member MEANS is not in its shape.
|
|
21
|
+
*/
|
|
22
|
+
facets: AdminFacets;
|
|
23
|
+
/** What a row shows in a list, and what a reference points at. */
|
|
24
|
+
columns: TableColumn[];
|
|
25
|
+
/** What a create/edit form is made of, with its browser-enforced bounds. */
|
|
26
|
+
fields: FormField[];
|
|
27
|
+
/** Every callable operation, including the five CRUD verbs. */
|
|
28
|
+
operations: AdminOperation[];
|
|
29
|
+
/**
|
|
30
|
+
* Which of the five the door actually serves. A door answering only `list` gets a
|
|
31
|
+
* list and no buttons — the card says so, so the UI never offers what would 404.
|
|
32
|
+
*/
|
|
33
|
+
can: {
|
|
34
|
+
list: boolean;
|
|
35
|
+
show: boolean;
|
|
36
|
+
create: boolean;
|
|
37
|
+
edit: boolean;
|
|
38
|
+
delete: boolean;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The five verbs react-admin already has pages for. Everything else a door serves is a
|
|
43
|
+
* business operation, and gets a button rather than a page.
|
|
44
|
+
*/
|
|
45
|
+
export declare const CRUD_OPS: readonly ['list', 'findById', 'create', 'update', 'delete'];
|
|
46
|
+
/** What a door serves beyond CRUD — the only thing this panel has that a generic one has not. */
|
|
47
|
+
export declare function actionsOf(operations: readonly AdminOperation[]): AdminOperation[];
|
|
48
|
+
/** Capabilities are a reading of the visible operations, never a second list to maintain. */
|
|
49
|
+
export declare function capabilitiesOf(operations: ReadonlyArray<Pick<AdminOperation, 'name'>>): AdminResource['can'];
|
|
50
|
+
/**
|
|
51
|
+
* A door with no schema is not a resource.
|
|
52
|
+
*
|
|
53
|
+
* The card omits `schema` when nothing is stored under that name, and that is
|
|
54
|
+
* ordinary — a health check, a search across several shapes. There is no table to
|
|
55
|
+
* draw for it, and no form. It stays reachable by hand; it is simply not furniture.
|
|
56
|
+
*/
|
|
57
|
+
export declare function resourcesOf(card: IdentityCard): AdminResource[];
|
|
58
|
+
/** The provider's index — the half of a resource it needs, keyed by door name. */
|
|
59
|
+
export declare function keysOf(resources: AdminResource[]): Record<string, ResourceKey>;
|
|
60
|
+
/**
|
|
61
|
+
* Ask the running app what it hosts.
|
|
62
|
+
*
|
|
63
|
+
* `rpc.discover` travels on the same wire as every other call — it is a reserved op,
|
|
64
|
+
* not a second endpoint — so a back-office needs no configuration to find its
|
|
65
|
+
* subject, and gets the same answer whether the frond is in this process or behind
|
|
66
|
+
* an address in `remotes:`.
|
|
67
|
+
*/
|
|
68
|
+
export declare function fetchCard(endpoint?: string, fetcher?: Fetcher): Promise<IdentityCard>;
|
|
69
|
+
//# sourceMappingURL=resources.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resources.d.ts","sourceRoot":"","sources":["../src/resources.ts"],"names":[],"mappings":"AAYA,OAAO,EAML,KAAK,OAAO,EACZ,KAAK,SAAS,EACd,KAAK,WAAW,EACjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAoB,KAAK,MAAM,EAAE,KAAK,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC1F,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,iFAAiF;AACjF,MAAM,WAAW,cAAe,SAAQ,MAAM;IAC5C,+EAA+E;IAC/E,KAAK,EAAE,MAAM,CAAC;IACd,qFAAqF;IACrF,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,sDAAsD;AACtD,MAAM,WAAW,aAAc,SAAQ,WAAW;IAChD,yEAAyE;IACzE,KAAK,EAAE,MAAM,CAAC;IACd,sEAAsE;IACtE,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,MAAM,EAAE,WAAW,CAAC;IACpB,kEAAkE;IAClE,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,4EAA4E;IAC5E,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,+DAA+D;IAC/D,UAAU,EAAE,cAAc,EAAE,CAAC;IAC7B;;;OAGG;IACH,GAAG,EAAE;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,IAAI,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,OAAO,CAAC;QAAC,IAAI,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC;CACxF;AAOD;;;GAGG;AACH,eAAO,MAAM,QAAQ,YAAI,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAU,CAAC;AAEpF,iGAAiG;AACjG,wBAAgB,SAAS,CAAC,UAAU,EAAE,SAAS,cAAc,EAAE,GAAG,cAAc,EAAE,CAEjF;AAED,6FAA6F;AAC7F,wBAAgB,cAAc,CAAC,UAAU,EAAE,aAAa,CAAC,IAAI,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC,GAAG,aAAa,CAAC,KAAK,CAAC,CAS5G;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,YAAY,GAAG,aAAa,EAAE,CA4B/D;AAED,kFAAkF;AAClF,wBAAgB,MAAM,CAAC,SAAS,EAAE,aAAa,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAE9E;AAED;;;;;;;GAOG;AACH,wBAAsB,SAAS,CAC7B,QAAQ,SAAgB,EACxB,OAAO,GAAE,OAAwB,GAChC,OAAO,CAAC,YAAY,CAAC,CAOvB"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the app hosts, read as a back-office.
|
|
3
|
+
*
|
|
4
|
+
* The card (`rpc.discover`) already answers every question a back-office asks: which
|
|
5
|
+
* doors exist, what each one stores, and which of the five verbs it serves. So there
|
|
6
|
+
* is nothing to generate and nothing to configure — a new entity appears in the menu
|
|
7
|
+
* because the card grew a door, not because a file was written.
|
|
8
|
+
*
|
|
9
|
+
* Two projections of the same shape, and they are the pair `form.ts` already draws:
|
|
10
|
+
* `tableColumnsOf` for what a list SHOWS, `formFieldsOf` for what a form SUPPLIES.
|
|
11
|
+
*/
|
|
12
|
+
import { reconstruct, primaryFieldOf } from '@fougere/schema';
|
|
13
|
+
import { CALL_ENDPOINT, fetcher as browserFetcher, formFieldsOf, sendCall, tableColumnsOf, } from '@fougere/app/client';
|
|
14
|
+
import { EMPTY_INVOCATION } from '@fougere/core/contract';
|
|
15
|
+
function labelOf(name) {
|
|
16
|
+
const words = name.replace(/([a-z0-9])([A-Z])/g, '$1 $2').replace(/[-_]+/g, ' ');
|
|
17
|
+
return words.charAt(0).toUpperCase() + words.slice(1);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The five verbs react-admin already has pages for. Everything else a door serves is a
|
|
21
|
+
* business operation, and gets a button rather than a page.
|
|
22
|
+
*/
|
|
23
|
+
export const CRUD_OPS = ['list', 'findById', 'create', 'update', 'delete'];
|
|
24
|
+
/** What a door serves beyond CRUD — the only thing this panel has that a generic one has not. */
|
|
25
|
+
export function actionsOf(operations) {
|
|
26
|
+
return operations.filter((op) => !CRUD_OPS.includes(op.name));
|
|
27
|
+
}
|
|
28
|
+
/** Capabilities are a reading of the visible operations, never a second list to maintain. */
|
|
29
|
+
export function capabilitiesOf(operations) {
|
|
30
|
+
const serves = new Set(operations.map((op) => op.name));
|
|
31
|
+
return {
|
|
32
|
+
list: serves.has('list'),
|
|
33
|
+
show: serves.has('findById'),
|
|
34
|
+
create: serves.has('create'),
|
|
35
|
+
edit: serves.has('update'),
|
|
36
|
+
delete: serves.has('delete'),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* A door with no schema is not a resource.
|
|
41
|
+
*
|
|
42
|
+
* The card omits `schema` when nothing is stored under that name, and that is
|
|
43
|
+
* ordinary — a health check, a search across several shapes. There is no table to
|
|
44
|
+
* draw for it, and no form. It stays reachable by hand; it is simply not furniture.
|
|
45
|
+
*/
|
|
46
|
+
export function resourcesOf(card) {
|
|
47
|
+
const out = [];
|
|
48
|
+
for (const frond of card.fronds) {
|
|
49
|
+
for (const door of frond.doors) {
|
|
50
|
+
if (!door.schema)
|
|
51
|
+
continue;
|
|
52
|
+
const entity = reconstruct(door.schema);
|
|
53
|
+
const primary = primaryFieldOf(entity.getFields());
|
|
54
|
+
// No primary means no row identity — a list could be drawn, but nothing could be
|
|
55
|
+
// opened, edited or deleted. Refusing here is the same answer `primaryFieldOf`
|
|
56
|
+
// gives by not defaulting to 'id': the caller decides, and this caller declines.
|
|
57
|
+
if (!primary)
|
|
58
|
+
continue;
|
|
59
|
+
const operations = door.ops.map((op) => ({ ...op, label: labelOf(op.name) }));
|
|
60
|
+
const columns = tableColumnsOf(entity, door.name);
|
|
61
|
+
const fields = formFieldsOf(entity, door.name);
|
|
62
|
+
out.push({
|
|
63
|
+
name: door.name,
|
|
64
|
+
frond: frond.name,
|
|
65
|
+
label: labelOf(door.name),
|
|
66
|
+
facets: {},
|
|
67
|
+
primary,
|
|
68
|
+
columns,
|
|
69
|
+
fields,
|
|
70
|
+
operations,
|
|
71
|
+
can: capabilitiesOf(operations),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return out;
|
|
76
|
+
}
|
|
77
|
+
/** The provider's index — the half of a resource it needs, keyed by door name. */
|
|
78
|
+
export function keysOf(resources) {
|
|
79
|
+
return Object.fromEntries(resources.map((r) => [r.name, { name: r.name, primary: r.primary }]));
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Ask the running app what it hosts.
|
|
83
|
+
*
|
|
84
|
+
* `rpc.discover` travels on the same wire as every other call — it is a reserved op,
|
|
85
|
+
* not a second endpoint — so a back-office needs no configuration to find its
|
|
86
|
+
* subject, and gets the same answer whether the frond is in this process or behind
|
|
87
|
+
* an address in `remotes:`.
|
|
88
|
+
*/
|
|
89
|
+
export async function fetchCard(endpoint = CALL_ENDPOINT, fetcher = browserFetcher) {
|
|
90
|
+
return await sendCall(fetcher, { entity: 'rpc', op: 'discover' }, EMPTY_INVOCATION, endpoint);
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=resources.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resources.js","sourceRoot":"","sources":["../src/resources.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** One card request, shared by resource discovery and every provider operation. */
|
|
2
|
+
import { type Fetcher } from '@fougere/app/client';
|
|
3
|
+
import { type AdminExtension } from './extensions.js';
|
|
4
|
+
import { type FougereDataProvider } from './provider.js';
|
|
5
|
+
import { type AdminResource } from './resources.js';
|
|
6
|
+
export interface AdminRuntimeOptions {
|
|
7
|
+
endpoint?: string;
|
|
8
|
+
fetcher?: Fetcher;
|
|
9
|
+
extensions?: readonly AdminExtension[];
|
|
10
|
+
}
|
|
11
|
+
export interface LoadedAdmin {
|
|
12
|
+
resources: AdminResource[];
|
|
13
|
+
provider: FougereDataProvider;
|
|
14
|
+
}
|
|
15
|
+
export interface AdminRuntime {
|
|
16
|
+
/** Loads and caches the card-derived model. */
|
|
17
|
+
load(): Promise<LoadedAdmin>;
|
|
18
|
+
/** Synchronous shell required by react-admin; delegates after `load()` resolves. */
|
|
19
|
+
dataProvider: FougereDataProvider;
|
|
20
|
+
}
|
|
21
|
+
export declare function createAdminRuntime(options?: AdminRuntimeOptions): AdminRuntime;
|
|
22
|
+
//# sourceMappingURL=runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA,mFAAmF;AACnF,OAAO,EAGL,KAAK,OAAO,EACb,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAwB,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAC5E,OAAO,EAGL,KAAK,mBAAmB,EACzB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAkC,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEpF,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,SAAS,cAAc,EAAE,CAAC;CACxC;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3B,QAAQ,EAAE,mBAAmB,CAAC;CAC/B;AAED,MAAM,WAAW,YAAY;IAC3B,+CAA+C;IAC/C,IAAI,IAAI,OAAO,CAAC,WAAW,CAAC,CAAC;IAC7B,oFAAoF;IACpF,YAAY,EAAE,mBAAmB,CAAC;CACnC;AAED,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,mBAAwB,GAAG,YAAY,CAmClF"}
|
package/dist/runtime.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** One card request, shared by resource discovery and every provider operation. */
|
|
2
|
+
import { CALL_ENDPOINT, fetcher as browserFetcher, } from '@fougere/app/client';
|
|
3
|
+
import { applyAdminExtensions } from './extensions.js';
|
|
4
|
+
import { createDataProvider, createLazyDataProvider, } from './provider.js';
|
|
5
|
+
import { fetchCard, keysOf, resourcesOf } from './resources.js';
|
|
6
|
+
export function createAdminRuntime(options = {}) {
|
|
7
|
+
const { endpoint = CALL_ENDPOINT, fetcher = browserFetcher, extensions = [], } = options;
|
|
8
|
+
let loading;
|
|
9
|
+
/**
|
|
10
|
+
* One card request, shared — and a REFUSED one is forgotten.
|
|
11
|
+
*
|
|
12
|
+
* `loading ??=` alone memoizes the rejection too, so a receiver that was down when the
|
|
13
|
+
* panel opened stays down for the life of the tab: every later attempt awaits the same
|
|
14
|
+
* dead promise and there is no path back. Clearing the slot on failure is what makes a
|
|
15
|
+
* retry mean anything, and the rejection is re-thrown so the caller can say so — a
|
|
16
|
+
* discovery that fails silently leaves react-admin on its loading page forever, which
|
|
17
|
+
* is the one state where the panel tells the operator nothing at all.
|
|
18
|
+
*/
|
|
19
|
+
const load = () => loading ??= fetchCard(endpoint, fetcher)
|
|
20
|
+
.then((card) => {
|
|
21
|
+
const resources = applyAdminExtensions(resourcesOf(card), extensions);
|
|
22
|
+
return {
|
|
23
|
+
resources,
|
|
24
|
+
provider: createDataProvider({ resources: keysOf(resources), endpoint, fetcher }),
|
|
25
|
+
};
|
|
26
|
+
})
|
|
27
|
+
.catch((error) => {
|
|
28
|
+
loading = undefined;
|
|
29
|
+
throw error;
|
|
30
|
+
});
|
|
31
|
+
return {
|
|
32
|
+
load,
|
|
33
|
+
dataProvider: createLazyDataProvider(async () => (await load()).provider),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA,mFAAmF;AACnF,OAAO,EACL,aAAa,EACb,OAAO,IAAI,cAAc,GAE1B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,oBAAoB,EAAuB,MAAM,iBAAiB,CAAC;AAC5E,OAAO,EACL,kBAAkB,EAClB,sBAAsB,GAEvB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAsB,MAAM,gBAAgB,CAAC;AAoBpF,MAAM,UAAU,kBAAkB,CAAC,OAAO,GAAwB,EAAE;IAClE,MAAM,EACJ,QAAQ,GAAG,aAAa,EACxB,OAAO,GAAG,cAAc,EACxB,UAAU,GAAG,EAAE,GAChB,GAAG,OAAO,CAAC;IACZ,IAAI,OAAyC,CAAC;IAE9C;;;;;;;;;OASG;IACH,MAAM,IAAI,GAAG,GAAyB,EAAE,CAAC,OAAO,KAAK,SAAS,CAAC,QAAQ,EAAE,OAAO,CAAC;SAC9E,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE;QACb,MAAM,SAAS,GAAG,oBAAoB,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,UAAU,CAAC,CAAC;QACtE,OAAO;YACL,SAAS;YACT,QAAQ,EAAE,kBAAkB,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;SAClF,CAAC;IACJ,CAAC,CAAC;SACD,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;QACxB,OAAO,GAAG,SAAS,CAAC;QACpB,MAAM,KAAK,CAAC;IACd,CAAC,CAAC,CAAC;IAEL,OAAO;QACL,IAAI;QACJ,YAAY,EAAE,sBAAsB,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC;KAC1E,CAAC;AACJ,CAAC"}
|
package/dist/theme.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ReactElement } from 'react';
|
|
2
|
+
import { type AppBarProps, type LayoutProps, type RaThemeOptions } from 'react-admin';
|
|
3
|
+
export declare const fougereLightTheme: RaThemeOptions;
|
|
4
|
+
export declare const fougereDarkTheme: RaThemeOptions;
|
|
5
|
+
export declare function FougereMark({ size }: {
|
|
6
|
+
size?: number;
|
|
7
|
+
}): ReactElement;
|
|
8
|
+
export declare function FougereAppBar(props: AppBarProps): ReactElement;
|
|
9
|
+
export declare function FougereLayout(props: LayoutProps): ReactElement;
|
|
10
|
+
//# sourceMappingURL=theme.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../src/theme.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAC1C,OAAO,EAQL,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,cAAc,EACpB,MAAM,aAAa,CAAC;AAkXrB,eAAO,MAAM,iBAAiB,gBAA8B,CAAC;AAC7D,eAAO,MAAM,gBAAgB,gBAA6B,CAAC;AA2E3D,wBAAgB,WAAW,CAAC,EAAE,IAAS,EAAE,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,YAAY,CAiB1E;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,WAAW,GAAG,YAAY,CAmB9D;AA4BD,wBAAgB,aAAa,CAAC,KAAK,EAAE,WAAW,GAAG,YAAY,CAE9D"}
|