@object-ui/app-shell 6.0.4 → 6.2.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/CHANGELOG.md +128 -0
- package/README.md +10 -1
- package/dist/console/AppContent.js +24 -2
- package/dist/console/ai/AiChatPage.d.ts +8 -0
- package/dist/console/ai/AiChatPage.js +188 -0
- package/dist/console/ai/ConversationsSidebar.d.ts +7 -0
- package/dist/console/ai/ConversationsSidebar.js +111 -0
- package/dist/console/auth/LoginPage.js +19 -2
- package/dist/console/auth/RegisterPage.js +30 -1
- package/dist/console/marketplace/MarketplaceAccessDenied.js +3 -1
- package/dist/console/marketplace/MarketplaceInstalledPage.js +11 -3
- package/dist/console/marketplace/MarketplacePackagePage.js +83 -17
- package/dist/console/marketplace/MarketplacePage.js +55 -18
- package/dist/console/marketplace/marketplaceApi.d.ts +20 -0
- package/dist/console/marketplace/usePackageL10n.d.ts +38 -0
- package/dist/console/marketplace/usePackageL10n.js +110 -0
- package/dist/console/organizations/CreateWorkspaceDialog.js +29 -1
- package/dist/console/organizations/OrganizationsPage.js +24 -3
- package/dist/context/FavoritesProvider.d.ts +40 -2
- package/dist/context/FavoritesProvider.js +201 -20
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/useChatConversation.d.ts +7 -0
- package/dist/hooks/useChatConversation.js +37 -5
- package/dist/hooks/useConversationList.d.ts +25 -0
- package/dist/hooks/useConversationList.js +131 -0
- package/dist/hooks/useNavPins.d.ts +11 -4
- package/dist/hooks/useNavPins.js +104 -53
- package/dist/index.d.ts +7 -0
- package/dist/index.js +14 -0
- package/dist/layout/AppHeader.js +2 -2
- package/dist/layout/AppSidebar.js +20 -1
- package/dist/layout/UnifiedSidebar.js +1 -1
- package/dist/providers/ExpressionProvider.d.ts +11 -1
- package/dist/providers/ExpressionProvider.js +11 -6
- package/dist/services/builtinComponents.d.ts +1 -0
- package/dist/services/builtinComponents.js +166 -0
- package/dist/services/componentRegistry.d.ts +63 -0
- package/dist/services/componentRegistry.js +36 -0
- package/dist/views/ComponentNavView.d.ts +6 -0
- package/dist/views/ComponentNavView.js +26 -0
- package/dist/views/RecordDetailView.js +72 -0
- package/dist/views/RecordFormPage.js +15 -3
- package/dist/views/SearchResultsPage.js +4 -0
- package/dist/views/metadata-admin/DesignerEditorWrapper.d.ts +58 -0
- package/dist/views/metadata-admin/DesignerEditorWrapper.js +140 -0
- package/dist/views/metadata-admin/DirectoryPage.d.ts +1 -0
- package/dist/views/metadata-admin/DirectoryPage.js +135 -0
- package/dist/views/metadata-admin/LayeredDiff.d.ts +6 -0
- package/dist/views/metadata-admin/LayeredDiff.js +26 -0
- package/dist/views/metadata-admin/PageShell.d.ts +34 -0
- package/dist/views/metadata-admin/PageShell.js +33 -0
- package/dist/views/metadata-admin/PermissionMatrixEditor.d.ts +5 -0
- package/dist/views/metadata-admin/PermissionMatrixEditor.js +288 -0
- package/dist/views/metadata-admin/QuickFind.d.ts +5 -0
- package/dist/views/metadata-admin/QuickFind.js +152 -0
- package/dist/views/metadata-admin/ResourceEditPage.d.ts +7 -0
- package/dist/views/metadata-admin/ResourceEditPage.js +256 -0
- package/dist/views/metadata-admin/ResourceHistoryPage.d.ts +5 -0
- package/dist/views/metadata-admin/ResourceHistoryPage.js +97 -0
- package/dist/views/metadata-admin/ResourceListPage.d.ts +4 -0
- package/dist/views/metadata-admin/ResourceListPage.js +144 -0
- package/dist/views/metadata-admin/ResourceRouter.d.ts +10 -0
- package/dist/views/metadata-admin/ResourceRouter.js +47 -0
- package/dist/views/metadata-admin/SchemaForm.d.ts +99 -0
- package/dist/views/metadata-admin/SchemaForm.js +556 -0
- package/dist/views/metadata-admin/default-schemas.d.ts +6 -0
- package/dist/views/metadata-admin/default-schemas.js +207 -0
- package/dist/views/metadata-admin/i18n.d.ts +33 -0
- package/dist/views/metadata-admin/i18n.js +303 -0
- package/dist/views/metadata-admin/index.d.ts +31 -0
- package/dist/views/metadata-admin/index.js +33 -0
- package/dist/views/metadata-admin/predicate.d.ts +31 -0
- package/dist/views/metadata-admin/predicate.js +150 -0
- package/dist/views/metadata-admin/registry.d.ts +125 -0
- package/dist/views/metadata-admin/registry.js +48 -0
- package/dist/views/metadata-admin/useMetadata.d.ts +37 -0
- package/dist/views/metadata-admin/useMetadata.js +96 -0
- package/dist/views/metadata-admin/widgets.d.ts +68 -0
- package/dist/views/metadata-admin/widgets.js +287 -0
- package/package.json +29 -28
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
3
|
+
/**
|
|
4
|
+
* MetadataResourceListPage — generic list of items for a metadata type
|
|
5
|
+
* (Phase 3c).
|
|
6
|
+
*
|
|
7
|
+
* Reads `/meta/:type`, applies registry-driven columns + search +
|
|
8
|
+
* source/overlay filters, and renders an ObjectGrid-like table.
|
|
9
|
+
* Each row links to its EditPage at `./:name?type=…`.
|
|
10
|
+
*
|
|
11
|
+
* No virtualisation in MVP — metadata lists are typically < 200 items
|
|
12
|
+
* per type, well under the threshold where it'd matter.
|
|
13
|
+
*/
|
|
14
|
+
import * as React from 'react';
|
|
15
|
+
import { Link, useNavigate } from 'react-router-dom';
|
|
16
|
+
import { Plus, Search, RefreshCw } from 'lucide-react';
|
|
17
|
+
import { Button } from '@object-ui/components';
|
|
18
|
+
import { Input } from '@object-ui/components';
|
|
19
|
+
import { Badge } from '@object-ui/components';
|
|
20
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@object-ui/components';
|
|
21
|
+
import { Empty, EmptyTitle, EmptyDescription } from '@object-ui/components';
|
|
22
|
+
import { PageShell } from './PageShell';
|
|
23
|
+
import { useMetadataClient, useMetadataTypes, matchesQuery, } from './useMetadata';
|
|
24
|
+
import { getMetadataResource, resolveResourceConfig, } from './registry';
|
|
25
|
+
import { t, detectLocale } from './i18n';
|
|
26
|
+
export function MetadataResourceListPage({ type }) {
|
|
27
|
+
const navigate = useNavigate();
|
|
28
|
+
const client = useMetadataClient();
|
|
29
|
+
const { entries: typesEntries } = useMetadataTypes(client);
|
|
30
|
+
const entry = typesEntries.find((t) => t.type === type);
|
|
31
|
+
const config = resolveResourceConfig(type, entry);
|
|
32
|
+
// If a fully custom ListPage is registered, render it and bail.
|
|
33
|
+
const customConfig = getMetadataResource(type);
|
|
34
|
+
if (customConfig?.ListPage) {
|
|
35
|
+
const Custom = customConfig.ListPage;
|
|
36
|
+
return _jsx(Custom, { type: type });
|
|
37
|
+
}
|
|
38
|
+
const [items, setItems] = React.useState([]);
|
|
39
|
+
const [loading, setLoading] = React.useState(true);
|
|
40
|
+
const [error, setError] = React.useState(null);
|
|
41
|
+
const [query, setQuery] = React.useState('');
|
|
42
|
+
const [sourceFilter, setSourceFilter] = React.useState('all');
|
|
43
|
+
const [refreshKey, setRefreshKey] = React.useState(0);
|
|
44
|
+
React.useEffect(() => {
|
|
45
|
+
let cancelled = false;
|
|
46
|
+
setLoading(true);
|
|
47
|
+
setError(null);
|
|
48
|
+
(async () => {
|
|
49
|
+
try {
|
|
50
|
+
const list = await client.list(type);
|
|
51
|
+
if (cancelled)
|
|
52
|
+
return;
|
|
53
|
+
const rows = (list ?? []).map((raw) => {
|
|
54
|
+
const item = (raw && typeof raw === 'object' && 'item' in raw ? raw.item : raw) ?? {};
|
|
55
|
+
return {
|
|
56
|
+
raw,
|
|
57
|
+
item,
|
|
58
|
+
source: raw?.source,
|
|
59
|
+
};
|
|
60
|
+
});
|
|
61
|
+
setItems(rows);
|
|
62
|
+
setLoading(false);
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
if (!cancelled) {
|
|
66
|
+
setError(err?.message ?? String(err));
|
|
67
|
+
setLoading(false);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
})();
|
|
71
|
+
return () => {
|
|
72
|
+
cancelled = true;
|
|
73
|
+
};
|
|
74
|
+
}, [client, type, refreshKey]);
|
|
75
|
+
const searchableFields = config.searchableFields ?? ['name', 'label', 'description'];
|
|
76
|
+
const filtered = items.filter((row) => {
|
|
77
|
+
if (!matchesQuery(row.item, query, searchableFields))
|
|
78
|
+
return false;
|
|
79
|
+
if (sourceFilter !== 'all' && row.source && row.source !== sourceFilter)
|
|
80
|
+
return false;
|
|
81
|
+
return true;
|
|
82
|
+
});
|
|
83
|
+
// Compute source counts for the filter dropdown.
|
|
84
|
+
const sourceCounts = React.useMemo(() => {
|
|
85
|
+
const c = { all: items.length, code: 0, overlay: 0, effective: 0 };
|
|
86
|
+
for (const r of items) {
|
|
87
|
+
if (r.source && c[r.source] !== undefined)
|
|
88
|
+
c[r.source]++;
|
|
89
|
+
}
|
|
90
|
+
return c;
|
|
91
|
+
}, [items]);
|
|
92
|
+
const columns = config.listColumns ?? defaultColumns(config.primaryKey ?? 'name');
|
|
93
|
+
const locale = detectLocale();
|
|
94
|
+
// Localise default column labels — registered columns keep their
|
|
95
|
+
// hand-authored labels (consumers may want bespoke wording).
|
|
96
|
+
const localizeColumnLabel = (col) => {
|
|
97
|
+
const tryKey = `engine.list.col.${col.key}`;
|
|
98
|
+
const translated = t(tryKey, locale);
|
|
99
|
+
return translated === tryKey ? col.label : translated;
|
|
100
|
+
};
|
|
101
|
+
return (_jsx(PageShell, { entry: entry ?? { type, label: type }, stats: [
|
|
102
|
+
{ label: t('engine.list.items', locale), value: items.length },
|
|
103
|
+
{ label: t('engine.list.filtered', locale), value: filtered.length },
|
|
104
|
+
], actions: _jsxs(_Fragment, { children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: () => setRefreshKey((k) => k + 1), title: t('engine.list.refresh', locale), children: _jsx(RefreshCw, { className: "h-4 w-4" }) }), entry?.allowOrgOverride && (_jsxs(Button, { size: "sm", onClick: () => navigate(`./new?type=${encodeURIComponent(type)}`), children: [_jsx(Plus, { className: "h-4 w-4 mr-1" }), t('engine.list.create', locale)] }))] }), children: _jsxs("div", { className: "p-6 space-y-4", children: [_jsxs("div", { className: "flex items-center gap-3 flex-wrap", children: [_jsxs("div", { className: "relative flex-1 min-w-[200px] max-w-md", children: [_jsx(Search, { className: "absolute left-2.5 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-muted-foreground" }), _jsx(Input, { className: "pl-8", placeholder: t('engine.list.search', locale), value: query, onChange: (e) => setQuery(e.target.value) })] }), _jsxs(Select, { value: sourceFilter, onValueChange: setSourceFilter, children: [_jsx(SelectTrigger, { className: "w-[180px]", children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsxs(SelectItem, { value: "all", children: [t('engine.list.allSources', locale), " (", sourceCounts.all, ")"] }), _jsxs(SelectItem, { value: "code", children: ["Code (", sourceCounts.code, ")"] }), _jsxs(SelectItem, { value: "overlay", children: ["Overlay (", sourceCounts.overlay, ")"] }), _jsxs(SelectItem, { value: "effective", children: ["Effective (", sourceCounts.effective, ")"] })] })] })] }), loading && (_jsxs("div", { className: "text-sm text-muted-foreground", children: [t('engine.edit.loading', locale), " ", type, "\u2026"] })), error && (_jsx("div", { className: "text-sm text-destructive border border-destructive/30 rounded p-3 bg-destructive/5", children: error })), !loading && !error && filtered.length === 0 && (_jsxs(Empty, { children: [_jsx(EmptyTitle, { children: items.length === 0
|
|
105
|
+
? `No ${type} items registered`
|
|
106
|
+
: `No matches for "${query}"` }), _jsx(EmptyDescription, { children: config.emptyStateHint ??
|
|
107
|
+
(entry?.allowOrgOverride
|
|
108
|
+
? `Click "New" above to create the first ${type}.`
|
|
109
|
+
: `This type is read-only — instances are defined by code artifacts in packages.`) })] })), !loading && filtered.length > 0 && (_jsx("div", { className: "border rounded-lg overflow-hidden", children: _jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { className: "bg-muted/40 text-xs uppercase tracking-wider text-muted-foreground", children: _jsxs("tr", { children: [columns.map((c) => (_jsx("th", { className: "px-3 py-2 text-left font-medium", style: c.width ? { width: c.width } : undefined, children: localizeColumnLabel(c) }, c.key))), _jsx("th", { className: "px-3 py-2 text-right font-medium w-[80px]", children: t('engine.list.col.source', locale) })] }) }), _jsx("tbody", { className: "divide-y", children: filtered.map((row, i) => {
|
|
110
|
+
const pk = config.primaryKey ?? 'name';
|
|
111
|
+
const name = String(row.item[pk] ?? `(unnamed-${i})`);
|
|
112
|
+
return (_jsxs("tr", { className: "hover:bg-accent/50", children: [columns.map((c, ci) => {
|
|
113
|
+
const value = row.item[c.key];
|
|
114
|
+
const cell = c.render ? c.render(value, row.item) : defaultCell(value);
|
|
115
|
+
return (_jsx("td", { className: "px-3 py-2 align-top", children: ci === 0 ? (_jsx(Link, { to: `./${encodeURIComponent(name)}?type=${encodeURIComponent(type)}`, className: "text-primary hover:underline font-mono", children: cell })) : (cell) }, c.key));
|
|
116
|
+
}), _jsx("td", { className: "px-3 py-2 text-right align-top", children: row.source ? (_jsx(Badge, { variant: "outline", className: 'text-[10px] ' +
|
|
117
|
+
(row.source === 'overlay'
|
|
118
|
+
? 'border-emerald-500 text-emerald-700'
|
|
119
|
+
: ''), children: row.source })) : (_jsx("span", { className: "text-muted-foreground text-xs", children: "\u2014" })) })] }, name + i));
|
|
120
|
+
}) })] }) }))] }) }));
|
|
121
|
+
}
|
|
122
|
+
function defaultColumns(primaryKey) {
|
|
123
|
+
return [
|
|
124
|
+
{ key: primaryKey, label: primaryKey, width: '30%' },
|
|
125
|
+
{ key: 'label', label: 'Label', width: '30%' },
|
|
126
|
+
{ key: 'description', label: 'Description' },
|
|
127
|
+
];
|
|
128
|
+
}
|
|
129
|
+
function defaultCell(value) {
|
|
130
|
+
if (value == null || value === '') {
|
|
131
|
+
return _jsx("span", { className: "text-muted-foreground", children: "\u2014" });
|
|
132
|
+
}
|
|
133
|
+
if (typeof value === 'boolean')
|
|
134
|
+
return value ? '✓' : '✗';
|
|
135
|
+
if (typeof value === 'object') {
|
|
136
|
+
try {
|
|
137
|
+
return (_jsx("code", { className: "font-mono text-xs", children: JSON.stringify(value).slice(0, 60) }));
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
return String(value);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return String(value);
|
|
144
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Props forwarded by `ComponentNavView` — it merges URL query-string
|
|
3
|
+
* into props, so we accept both `type` (from `?type=…`) and route
|
|
4
|
+
* nav `params: { type: 'object' }` style.
|
|
5
|
+
*/
|
|
6
|
+
export interface MetadataResourceRouterProps {
|
|
7
|
+
/** Singular metadata type, e.g. 'view'. */
|
|
8
|
+
type?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function MetadataResourceRouter({ type }: MetadataResourceRouterProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useLocation, useSearchParams } from 'react-router-dom';
|
|
3
|
+
import { Empty, EmptyTitle, EmptyDescription } from '@object-ui/components';
|
|
4
|
+
import { MetadataResourceListPage } from './ResourceListPage';
|
|
5
|
+
import { MetadataResourceEditPage } from './ResourceEditPage';
|
|
6
|
+
import { MetadataResourceHistoryPage } from './ResourceHistoryPage';
|
|
7
|
+
export function MetadataResourceRouter({ type }) {
|
|
8
|
+
const location = useLocation();
|
|
9
|
+
const [search] = useSearchParams();
|
|
10
|
+
const resolvedType = type ?? search.get('type') ?? '';
|
|
11
|
+
if (!resolvedType) {
|
|
12
|
+
return (_jsx("div", { className: "p-8 h-full flex items-center justify-center", children: _jsxs(Empty, { children: [_jsx(EmptyTitle, { children: "Missing metadata type" }), _jsxs(EmptyDescription, { children: ["This page expects a ", _jsx("code", { className: "font-mono", children: "?type=" }), " query param (e.g. ", _jsx("code", { className: "font-mono", children: "?type=view" }), ") or a nav-metadata ", _jsxs("code", { className: "font-mono", children: ["params: ", `{ type: 'view' }`] }), "."] })] }) }));
|
|
13
|
+
}
|
|
14
|
+
// Parse the trailing wildcard. The parent route consumed
|
|
15
|
+
// `/apps/:appName/component/metadata/resource`, so what's left in
|
|
16
|
+
// location.pathname AFTER that prefix is our sub-path. We don't have
|
|
17
|
+
// the prefix exactly, but we can find `/resource/` and slice — it's
|
|
18
|
+
// unambiguous because `:ns/:name` are colon-joined → "metadata/resource".
|
|
19
|
+
const subPath = extractSubPath(location.pathname);
|
|
20
|
+
const segments = subPath.split('/').filter(Boolean);
|
|
21
|
+
// No sub-path → list page.
|
|
22
|
+
if (segments.length === 0) {
|
|
23
|
+
return _jsx(MetadataResourceListPage, { type: resolvedType });
|
|
24
|
+
}
|
|
25
|
+
// /new → create page.
|
|
26
|
+
if (segments[0] === 'new' && segments.length === 1) {
|
|
27
|
+
return (_jsx(MetadataResourceEditPage, { type: resolvedType, name: "", createMode: true }, `${resolvedType}:__new__`));
|
|
28
|
+
}
|
|
29
|
+
// /:name/history → history.
|
|
30
|
+
if (segments.length === 2 && segments[1] === 'history') {
|
|
31
|
+
return (_jsx(MetadataResourceHistoryPage, { type: resolvedType, name: decodeURIComponent(segments[0]) }));
|
|
32
|
+
}
|
|
33
|
+
// /:name → edit.
|
|
34
|
+
if (segments.length === 1) {
|
|
35
|
+
return (_jsx(MetadataResourceEditPage, { type: resolvedType, name: decodeURIComponent(segments[0]) }, `${resolvedType}:${segments[0]}`));
|
|
36
|
+
}
|
|
37
|
+
// Unknown sub-path.
|
|
38
|
+
return (_jsx("div", { className: "p-8 h-full flex items-center justify-center", children: _jsxs(Empty, { children: [_jsx(EmptyTitle, { children: "Unknown sub-path" }), _jsxs(EmptyDescription, { children: ["The path ", _jsx("code", { className: "font-mono", children: subPath }), " isn't recognized. Valid forms are ", _jsx("code", { className: "font-mono", children: "/" }), " (list),", ' ', _jsx("code", { className: "font-mono", children: "/new" }), " (create),", ' ', _jsx("code", { className: "font-mono", children: "/:name" }), " (edit),", ' ', _jsx("code", { className: "font-mono", children: "/:name/history" }), "."] })] }) }));
|
|
39
|
+
}
|
|
40
|
+
function extractSubPath(pathname) {
|
|
41
|
+
// Find the literal `/metadata/resource` prefix and return what follows.
|
|
42
|
+
const marker = '/metadata/resource';
|
|
43
|
+
const idx = pathname.indexOf(marker);
|
|
44
|
+
if (idx === -1)
|
|
45
|
+
return '';
|
|
46
|
+
return pathname.slice(idx + marker.length); // includes leading '/' if any
|
|
47
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { type WidgetContext } from './widgets';
|
|
2
|
+
type JsonSchema = Record<string, any>;
|
|
3
|
+
/**
|
|
4
|
+
* Lightweight shape of the spec `FormView` we consume. We deliberately
|
|
5
|
+
* accept `any` for forward compatibility — the spec evolves faster than
|
|
6
|
+
* we want this admin engine to break.
|
|
7
|
+
*/
|
|
8
|
+
export interface FormViewSpec {
|
|
9
|
+
type?: 'simple' | 'tabbed' | 'wizard' | 'split' | 'drawer' | 'modal';
|
|
10
|
+
sections?: FormSectionSpec[];
|
|
11
|
+
}
|
|
12
|
+
export interface FormSectionSpec {
|
|
13
|
+
label?: string;
|
|
14
|
+
description?: string;
|
|
15
|
+
collapsible?: boolean;
|
|
16
|
+
collapsed?: boolean;
|
|
17
|
+
columns?: 1 | 2 | 3 | 4;
|
|
18
|
+
visibleOn?: string | {
|
|
19
|
+
dialect?: string;
|
|
20
|
+
source: string;
|
|
21
|
+
};
|
|
22
|
+
fields: Array<string | FormFieldSpec>;
|
|
23
|
+
}
|
|
24
|
+
export interface FormFieldSpec {
|
|
25
|
+
field: string;
|
|
26
|
+
type?: string;
|
|
27
|
+
options?: Array<{
|
|
28
|
+
label: string;
|
|
29
|
+
value: string;
|
|
30
|
+
color?: string;
|
|
31
|
+
}>;
|
|
32
|
+
reference?: string;
|
|
33
|
+
maxLength?: number;
|
|
34
|
+
minLength?: number;
|
|
35
|
+
min?: number;
|
|
36
|
+
max?: number;
|
|
37
|
+
precision?: number;
|
|
38
|
+
scale?: number;
|
|
39
|
+
multiple?: boolean;
|
|
40
|
+
label?: string;
|
|
41
|
+
placeholder?: string;
|
|
42
|
+
helpText?: string;
|
|
43
|
+
readonly?: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* When true, the field is editable on create but locked once the
|
|
46
|
+
* record exists (e.g. immutable `name` machine identifiers). Combined
|
|
47
|
+
* with `SchemaFormProps.createMode` at render time.
|
|
48
|
+
*/
|
|
49
|
+
immutable?: boolean;
|
|
50
|
+
required?: boolean;
|
|
51
|
+
hidden?: boolean;
|
|
52
|
+
colSpan?: 1 | 2 | 3 | 4;
|
|
53
|
+
widget?: string;
|
|
54
|
+
/** For `type: 'code'` — syntax highlighting language (e.g. 'javascript', 'sql', 'json'). */
|
|
55
|
+
language?: string;
|
|
56
|
+
visibleOn?: string | {
|
|
57
|
+
dialect?: string;
|
|
58
|
+
source: string;
|
|
59
|
+
};
|
|
60
|
+
/** Sub-fields for `composite` (single embedded object) and `repeater`
|
|
61
|
+
* (array of embedded objects) types. Recursive. */
|
|
62
|
+
fields?: Array<string | FormFieldSpec>;
|
|
63
|
+
}
|
|
64
|
+
export interface SchemaFormIssue {
|
|
65
|
+
path: string;
|
|
66
|
+
message: string;
|
|
67
|
+
}
|
|
68
|
+
export interface SchemaFormProps {
|
|
69
|
+
/** JSONSchema for the root object. */
|
|
70
|
+
schema: JsonSchema | undefined;
|
|
71
|
+
/**
|
|
72
|
+
* Optional FormView layout (sections, tabs, widget hints, visibleOn)
|
|
73
|
+
* shipped by the framework alongside `schema`. When present, fields
|
|
74
|
+
* are grouped into sections and visibility predicates are honoured.
|
|
75
|
+
*/
|
|
76
|
+
form?: FormViewSpec;
|
|
77
|
+
/** Current form value. */
|
|
78
|
+
value: Record<string, unknown> | undefined;
|
|
79
|
+
/** Called with the next full value on every change. */
|
|
80
|
+
onChange: (next: Record<string, unknown>) => void;
|
|
81
|
+
/** Inline validation errors, keyed by JSON path. */
|
|
82
|
+
issues?: SchemaFormIssue[];
|
|
83
|
+
/** Field keys to hide (still preserved on save). */
|
|
84
|
+
hiddenFields?: string[];
|
|
85
|
+
/** Preferred top-level field order. */
|
|
86
|
+
fieldOrder?: string[];
|
|
87
|
+
/** Disable all inputs (e.g. when env-var write lock is off). */
|
|
88
|
+
readOnly?: boolean;
|
|
89
|
+
/**
|
|
90
|
+
* True when rendering the "new record" form (no existing item).
|
|
91
|
+
* Used by per-field `immutable: true` flag to allow editing on
|
|
92
|
+
* create but lock the value once the record exists.
|
|
93
|
+
*/
|
|
94
|
+
createMode?: boolean;
|
|
95
|
+
/** Out-of-band data widgets need (object list, etc). */
|
|
96
|
+
widgetContext?: WidgetContext;
|
|
97
|
+
}
|
|
98
|
+
export declare function SchemaForm({ schema, form, value, onChange, issues, hiddenFields, fieldOrder, readOnly, createMode, widgetContext, }: SchemaFormProps): import("react/jsx-runtime").JSX.Element;
|
|
99
|
+
export {};
|