@object-ui/react 16.1.0 → 17.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 +790 -1
- package/README.md +41 -0
- package/dist/SchemaRenderer.js +12 -2
- package/dist/context/AppShellContext.js +51 -23
- package/dist/context/InlineEditContext.d.ts +64 -0
- package/dist/context/InlineEditContext.js +8 -2
- package/dist/context/NotificationContext.d.ts +245 -17
- package/dist/context/NotificationContext.js +222 -20
- package/dist/context/ThemeContext.js +4 -8
- package/dist/hooks/index.d.ts +2 -1
- package/dist/hooks/index.js +3 -1
- package/dist/hooks/useAnimation.d.ts +40 -4
- package/dist/hooks/useAnimation.js +41 -10
- package/dist/hooks/useCapabilityGate.d.ts +16 -0
- package/dist/hooks/useCapabilityGate.js +60 -0
- package/dist/hooks/useClientNotifications.d.ts +0 -6
- package/dist/hooks/useClientNotifications.js +7 -53
- package/dist/hooks/useFilterScope.d.ts +30 -0
- package/dist/hooks/useFilterScope.js +55 -0
- package/dist/hooks/useNavigationOverlay.d.ts +34 -10
- package/dist/hooks/useNavigationOverlay.js +35 -1
- package/dist/hooks/useOffline.d.ts +51 -33
- package/dist/hooks/useOffline.js +3 -3
- package/dist/hooks/usePageVariables.js +23 -11
- package/dist/hooks/usePerformance.d.ts +3 -3
- package/dist/hooks/usePerformance.js +2 -2
- package/dist/hooks/useRecordSearch.js +98 -4
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/dist/spec-bridge/SpecBridge.d.ts +0 -4
- package/dist/spec-bridge/SpecBridge.js +0 -12
- package/dist/spec-bridge/bridges/list-view.js +5 -11
- package/dist/spec-bridge/index.d.ts +0 -2
- package/dist/spec-bridge/index.js +0 -2
- package/dist/spec-input.d.ts +37 -0
- package/dist/spec-input.js +8 -0
- package/dist/utils/error-message.d.ts +31 -0
- package/dist/utils/error-message.js +49 -0
- package/package.json +8 -8
- package/dist/hooks/useColumnSummary.d.ts +0 -38
- package/dist/hooks/useColumnSummary.js +0 -54
- package/dist/spec-bridge/bridges/dashboard.d.ts +0 -81
- package/dist/spec-bridge/bridges/dashboard.js +0 -121
- package/dist/spec-bridge/bridges/page.d.ts +0 -58
- package/dist/spec-bridge/bridges/page.js +0 -101
|
@@ -7,8 +7,6 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { bridgeListView } from './bridges/list-view';
|
|
9
9
|
import { bridgeFormView } from './bridges/form-view';
|
|
10
|
-
import { bridgePage } from './bridges/page';
|
|
11
|
-
import { bridgeDashboard } from './bridges/dashboard';
|
|
12
10
|
export class SpecBridge {
|
|
13
11
|
constructor(context = {}) {
|
|
14
12
|
Object.defineProperty(this, "bridges", {
|
|
@@ -26,8 +24,6 @@ export class SpecBridge {
|
|
|
26
24
|
this.context = context;
|
|
27
25
|
this.register('list', bridgeListView);
|
|
28
26
|
this.register('form', bridgeFormView);
|
|
29
|
-
this.register('page', bridgePage);
|
|
30
|
-
this.register('dashboard', bridgeDashboard);
|
|
31
27
|
}
|
|
32
28
|
register(specType, bridge) {
|
|
33
29
|
this.bridges.set(specType, bridge);
|
|
@@ -48,14 +44,6 @@ export class SpecBridge {
|
|
|
48
44
|
transformFormView(spec) {
|
|
49
45
|
return this.transform('form', spec);
|
|
50
46
|
}
|
|
51
|
-
/** Transform a Page spec */
|
|
52
|
-
transformPage(spec) {
|
|
53
|
-
return this.transform('page', spec);
|
|
54
|
-
}
|
|
55
|
-
/** Transform a Dashboard spec */
|
|
56
|
-
transformDashboard(spec) {
|
|
57
|
-
return this.transform('dashboard', spec);
|
|
58
|
-
}
|
|
59
47
|
updateContext(ctx) {
|
|
60
48
|
this.context = { ...this.context, ...ctx };
|
|
61
49
|
}
|
|
@@ -134,17 +134,11 @@ export const bridgeListView = (spec, _context) => {
|
|
|
134
134
|
// P1.6 — i18n & ARIA
|
|
135
135
|
if (spec.aria)
|
|
136
136
|
node.aria = spec.aria;
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
if (sharing.type && sharing.enabled == null) {
|
|
144
|
-
sharing.enabled = true;
|
|
145
|
-
}
|
|
146
|
-
node.sharing = sharing;
|
|
147
|
-
}
|
|
137
|
+
// `sharing` is already the spec's `ViewSharing` shape on both sides (#2890) —
|
|
138
|
+
// this used to DOWNGRADE it here, inventing a legacy `visibility` audience and
|
|
139
|
+
// an `enabled` flag that the renderer then had to fold back.
|
|
140
|
+
if (spec.sharing)
|
|
141
|
+
node.sharing = spec.sharing;
|
|
148
142
|
if (spec.hiddenFields)
|
|
149
143
|
node.hiddenFields = spec.hiddenFields;
|
|
150
144
|
if (spec.fieldOrder)
|
|
@@ -9,5 +9,3 @@ export { SpecBridge } from './SpecBridge';
|
|
|
9
9
|
export type { BridgeContext, BridgeFn, ObjectDefLite } from './types';
|
|
10
10
|
export { bridgeListView } from './bridges/list-view';
|
|
11
11
|
export { bridgeFormView } from './bridges/form-view';
|
|
12
|
-
export { bridgePage } from './bridges/page';
|
|
13
|
-
export { bridgeDashboard } from './bridges/dashboard';
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* The AUTHORING ("input") type of a `@objectstack/spec` zod schema.
|
|
10
|
+
*
|
|
11
|
+
* ## Why not `z.infer`
|
|
12
|
+
*
|
|
13
|
+
* `z.infer` is the OUTPUT type — what `.parse()` returns, with every
|
|
14
|
+
* `.default()` already applied and therefore REQUIRED. The hooks in this
|
|
15
|
+
* package are handed configuration an app author wrote by hand, so the input
|
|
16
|
+
* side is the one that describes what they may legally omit. Deriving a config
|
|
17
|
+
* type from `z.infer` turns every defaulted key into a required one and makes
|
|
18
|
+
* the schema's own defaults unusable — the `_input`/`_output` trap the spec
|
|
19
|
+
* symbol guard's header warns about, and the one that already bit
|
|
20
|
+
* `ObjectFieldGroup` in objectui#3169.
|
|
21
|
+
*
|
|
22
|
+
* Rule of thumb: is this module WRITING the metadata, or READING what has
|
|
23
|
+
* already been parsed? Writing → input. Reading → `z.infer`.
|
|
24
|
+
*
|
|
25
|
+
* ## Why it reads `_zod`, not `z.input`
|
|
26
|
+
*
|
|
27
|
+
* `z.input<typeof S>` needs a `zod` import, and `@object-ui/react` deliberately
|
|
28
|
+
* does not depend on zod — it renders plain objects and never parses. The
|
|
29
|
+
* schema's own type already carries both sides in `_zod`, so the input type is
|
|
30
|
+
* reachable through the spec's binding alone, with no new dependency and no
|
|
31
|
+
* copy of the shape.
|
|
32
|
+
*/
|
|
33
|
+
export type SpecAuthoredInput<S extends {
|
|
34
|
+
_zod: {
|
|
35
|
+
input: unknown;
|
|
36
|
+
};
|
|
37
|
+
}> = S['_zod']['input'];
|
|
@@ -12,6 +12,37 @@
|
|
|
12
12
|
* usable can be extracted, so callers fall back to a generic localized string.
|
|
13
13
|
*/
|
|
14
14
|
export declare function extractWriteErrorMessage(err: unknown): string | null;
|
|
15
|
+
/** One field the server rejected, normalised for `form.setError`. */
|
|
16
|
+
export interface WriteFieldError {
|
|
17
|
+
/** Field name, matching the form control's `name`. */
|
|
18
|
+
field: string;
|
|
19
|
+
/** Human-readable reason, already localized by the server when it can be. */
|
|
20
|
+
message: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Per-field errors from a rejected write, or `null` when the failure was not
|
|
24
|
+
* field-scoped.
|
|
25
|
+
*
|
|
26
|
+
* The server has always sent these. `@objectstack/objectql`'s validators throw
|
|
27
|
+
* `{ code: 'VALIDATION_FAILED', fields: [{ field, code, message }] }`, and both
|
|
28
|
+
* the REST layer and the runtime dispatcher serve that as a 400 with `fields[]`
|
|
29
|
+
* intact. What was missing was the last hop: every form caught the rejection,
|
|
30
|
+
* showed one generic toast, and dropped `fields[]` on the floor — so a user told
|
|
31
|
+
* "Validation failed" had to guess WHICH input was wrong, on a surface that
|
|
32
|
+
* already knows how to mark it.
|
|
33
|
+
*
|
|
34
|
+
* Reads three shapes, because the error can arrive from either side of the
|
|
35
|
+
* adapter boundary:
|
|
36
|
+
* - `validationErrors` — a `ValidationError` from `@object-ui/data-objectstack`;
|
|
37
|
+
* - `details.fields` — the raw `@objectstack/client` error, whose `details`
|
|
38
|
+
* falls back to the whole response body;
|
|
39
|
+
* - `fields` — a hand-rolled error of the same shape (the server duck-types
|
|
40
|
+
* these identically, so we do too).
|
|
41
|
+
*
|
|
42
|
+
* Entries without a usable `field` are dropped rather than guessed at: a wrong
|
|
43
|
+
* mark on an innocent input is worse than the generic toast we already show.
|
|
44
|
+
*/
|
|
45
|
+
export declare function extractFieldErrors(err: unknown): WriteFieldError[] | null;
|
|
15
46
|
/**
|
|
16
47
|
* True when a failed write was rejected for authorization reasons (HTTP 403 /
|
|
17
48
|
* `PERMISSION_DENIED` / `FORBIDDEN` / a row-level-security denial). Lets a
|
|
@@ -68,6 +68,55 @@ export function extractWriteErrorMessage(err) {
|
|
|
68
68
|
.trim();
|
|
69
69
|
return clean || null;
|
|
70
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* Per-field errors from a rejected write, or `null` when the failure was not
|
|
73
|
+
* field-scoped.
|
|
74
|
+
*
|
|
75
|
+
* The server has always sent these. `@objectstack/objectql`'s validators throw
|
|
76
|
+
* `{ code: 'VALIDATION_FAILED', fields: [{ field, code, message }] }`, and both
|
|
77
|
+
* the REST layer and the runtime dispatcher serve that as a 400 with `fields[]`
|
|
78
|
+
* intact. What was missing was the last hop: every form caught the rejection,
|
|
79
|
+
* showed one generic toast, and dropped `fields[]` on the floor — so a user told
|
|
80
|
+
* "Validation failed" had to guess WHICH input was wrong, on a surface that
|
|
81
|
+
* already knows how to mark it.
|
|
82
|
+
*
|
|
83
|
+
* Reads three shapes, because the error can arrive from either side of the
|
|
84
|
+
* adapter boundary:
|
|
85
|
+
* - `validationErrors` — a `ValidationError` from `@object-ui/data-objectstack`;
|
|
86
|
+
* - `details.fields` — the raw `@objectstack/client` error, whose `details`
|
|
87
|
+
* falls back to the whole response body;
|
|
88
|
+
* - `fields` — a hand-rolled error of the same shape (the server duck-types
|
|
89
|
+
* these identically, so we do too).
|
|
90
|
+
*
|
|
91
|
+
* Entries without a usable `field` are dropped rather than guessed at: a wrong
|
|
92
|
+
* mark on an innocent input is worse than the generic toast we already show.
|
|
93
|
+
*/
|
|
94
|
+
export function extractFieldErrors(err) {
|
|
95
|
+
const e = err;
|
|
96
|
+
if (!e || typeof e !== 'object')
|
|
97
|
+
return null;
|
|
98
|
+
const raw = (Array.isArray(e.validationErrors) && e.validationErrors) ||
|
|
99
|
+
(Array.isArray(e.details?.fields) && e.details.fields) ||
|
|
100
|
+
(Array.isArray(e.fields) && e.fields) ||
|
|
101
|
+
null;
|
|
102
|
+
if (!raw || !Array.isArray(raw))
|
|
103
|
+
return null;
|
|
104
|
+
const out = [];
|
|
105
|
+
for (const entry of raw) {
|
|
106
|
+
if (!entry || typeof entry !== 'object')
|
|
107
|
+
continue;
|
|
108
|
+
const rec = entry;
|
|
109
|
+
const field = firstString(rec.field, rec.name, rec.path);
|
|
110
|
+
if (!field)
|
|
111
|
+
continue;
|
|
112
|
+
// `code` is the machine enum (`required`, `invalid_option`, …). It is a
|
|
113
|
+
// last resort only: the server builds `message` to be shown verbatim, and
|
|
114
|
+
// an untranslated enum in the UI reads as a bug.
|
|
115
|
+
const message = firstString(rec.message, rec.error, rec.code) ?? '';
|
|
116
|
+
out.push({ field, message });
|
|
117
|
+
}
|
|
118
|
+
return out.length > 0 ? out : null;
|
|
119
|
+
}
|
|
71
120
|
/**
|
|
72
121
|
* True when a failed write was rejected for authorization reasons (HTTP 403 /
|
|
73
122
|
* `PERMISSION_DENIED` / `FORBIDDEN` / a row-level-security denial). Lets a
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@object-ui/react",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "17.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "React bindings and SchemaRenderer component for Object UI",
|
|
@@ -24,12 +24,12 @@
|
|
|
24
24
|
}
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@objectstack/spec": "^
|
|
28
|
-
"react-hook-form": "^7.
|
|
29
|
-
"@object-ui/core": "
|
|
30
|
-
"@object-ui/data-objectstack": "
|
|
31
|
-
"@object-ui/i18n": "
|
|
32
|
-
"@object-ui/types": "
|
|
27
|
+
"@objectstack/spec": "^17.0.0-rc.1",
|
|
28
|
+
"react-hook-form": "^7.83.0",
|
|
29
|
+
"@object-ui/core": "17.2.0",
|
|
30
|
+
"@object-ui/data-objectstack": "17.2.0",
|
|
31
|
+
"@object-ui/i18n": "17.2.0",
|
|
32
|
+
"@object-ui/types": "17.2.0"
|
|
33
33
|
},
|
|
34
34
|
"peerDependencies": {
|
|
35
35
|
"react": "^18.0.0 || ^19.0.0",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"scripts": {
|
|
66
66
|
"build": "tsc",
|
|
67
67
|
"test": "vitest run",
|
|
68
|
-
"type-check": "tsc --noEmit",
|
|
68
|
+
"type-check": "tsc --noEmit && tsc -p tsconfig.typetests.json",
|
|
69
69
|
"lint": "eslint ."
|
|
70
70
|
}
|
|
71
71
|
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ObjectUI
|
|
3
|
-
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
-
*
|
|
5
|
-
* This source code is licensed under the MIT license found in the
|
|
6
|
-
* LICENSE file in the root directory of this source tree.
|
|
7
|
-
*/
|
|
8
|
-
/** Summary function types aligned with ColumnSummarySchema */
|
|
9
|
-
export type SummaryFunction = 'sum' | 'avg' | 'count' | 'min' | 'max' | 'distinct';
|
|
10
|
-
export interface ColumnSummaryConfig {
|
|
11
|
-
/** The field/column to aggregate */
|
|
12
|
-
field: string;
|
|
13
|
-
/** The aggregation function */
|
|
14
|
-
function: SummaryFunction;
|
|
15
|
-
/** Optional label for the summary */
|
|
16
|
-
label?: string;
|
|
17
|
-
/** Number format options */
|
|
18
|
-
format?: Intl.NumberFormatOptions;
|
|
19
|
-
}
|
|
20
|
-
export interface ColumnSummaryResult {
|
|
21
|
-
/** The computed value */
|
|
22
|
-
value: number;
|
|
23
|
-
/** Formatted display string */
|
|
24
|
-
formatted: string;
|
|
25
|
-
/** The label */
|
|
26
|
-
label: string;
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* Hook for computing column-level summaries (SUM, AVG, COUNT, MIN, MAX, DISTINCT).
|
|
30
|
-
* Implements ColumnSummarySchema from @objectstack/spec v2.0.7.
|
|
31
|
-
*
|
|
32
|
-
* @example
|
|
33
|
-
* ```tsx
|
|
34
|
-
* const summary = useColumnSummary(data, { field: 'amount', function: 'sum', label: 'Total' });
|
|
35
|
-
* // summary.formatted => "$12,345.67"
|
|
36
|
-
* ```
|
|
37
|
-
*/
|
|
38
|
-
export declare function useColumnSummary(data: Record<string, unknown>[], config: ColumnSummaryConfig, locale?: string): ColumnSummaryResult;
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ObjectUI
|
|
3
|
-
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
-
*
|
|
5
|
-
* This source code is licensed under the MIT license found in the
|
|
6
|
-
* LICENSE file in the root directory of this source tree.
|
|
7
|
-
*/
|
|
8
|
-
import { useMemo } from 'react';
|
|
9
|
-
/**
|
|
10
|
-
* Hook for computing column-level summaries (SUM, AVG, COUNT, MIN, MAX, DISTINCT).
|
|
11
|
-
* Implements ColumnSummarySchema from @objectstack/spec v2.0.7.
|
|
12
|
-
*
|
|
13
|
-
* @example
|
|
14
|
-
* ```tsx
|
|
15
|
-
* const summary = useColumnSummary(data, { field: 'amount', function: 'sum', label: 'Total' });
|
|
16
|
-
* // summary.formatted => "$12,345.67"
|
|
17
|
-
* ```
|
|
18
|
-
*/
|
|
19
|
-
export function useColumnSummary(data, config, locale) {
|
|
20
|
-
return useMemo(() => {
|
|
21
|
-
const values = data
|
|
22
|
-
.map((row) => {
|
|
23
|
-
const val = row[config.field];
|
|
24
|
-
return typeof val === 'number' ? val : Number(val);
|
|
25
|
-
})
|
|
26
|
-
.filter((v) => !isNaN(v));
|
|
27
|
-
let value;
|
|
28
|
-
switch (config.function) {
|
|
29
|
-
case 'sum':
|
|
30
|
-
value = values.reduce((acc, v) => acc + v, 0);
|
|
31
|
-
break;
|
|
32
|
-
case 'avg':
|
|
33
|
-
value = values.length > 0 ? values.reduce((acc, v) => acc + v, 0) / values.length : 0;
|
|
34
|
-
break;
|
|
35
|
-
case 'count':
|
|
36
|
-
value = data.length;
|
|
37
|
-
break;
|
|
38
|
-
case 'min':
|
|
39
|
-
value = values.length > 0 ? Math.min(...values) : 0;
|
|
40
|
-
break;
|
|
41
|
-
case 'max':
|
|
42
|
-
value = values.length > 0 ? Math.max(...values) : 0;
|
|
43
|
-
break;
|
|
44
|
-
case 'distinct':
|
|
45
|
-
value = new Set(data.map((row) => row[config.field])).size;
|
|
46
|
-
break;
|
|
47
|
-
default:
|
|
48
|
-
value = 0;
|
|
49
|
-
}
|
|
50
|
-
const formatted = new Intl.NumberFormat(locale, config.format).format(value);
|
|
51
|
-
const label = config.label ?? config.function.toUpperCase();
|
|
52
|
-
return { value, formatted, label };
|
|
53
|
-
}, [data, config.field, config.function, config.label, config.format, locale]);
|
|
54
|
-
}
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ObjectUI
|
|
3
|
-
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
-
*
|
|
5
|
-
* This source code is licensed under the MIT license found in the
|
|
6
|
-
* LICENSE file in the root directory of this source tree.
|
|
7
|
-
*/
|
|
8
|
-
import type { BridgeFn } from '../types';
|
|
9
|
-
interface DashboardWidget {
|
|
10
|
-
title?: string;
|
|
11
|
-
description?: string;
|
|
12
|
-
type?: string;
|
|
13
|
-
chartConfig?: any;
|
|
14
|
-
colorVariant?: string;
|
|
15
|
-
actionUrl?: string;
|
|
16
|
-
actionType?: string;
|
|
17
|
-
actionIcon?: string;
|
|
18
|
-
object?: string;
|
|
19
|
-
filter?: any;
|
|
20
|
-
categoryField?: string;
|
|
21
|
-
valueField?: string;
|
|
22
|
-
aggregate?: string;
|
|
23
|
-
measures?: any[];
|
|
24
|
-
layout?: any;
|
|
25
|
-
options?: any;
|
|
26
|
-
responsive?: any;
|
|
27
|
-
aria?: {
|
|
28
|
-
ariaLabel?: string;
|
|
29
|
-
ariaDescribedBy?: string;
|
|
30
|
-
role?: string;
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
interface DashboardHeader {
|
|
34
|
-
showTitle?: boolean;
|
|
35
|
-
showDescription?: boolean;
|
|
36
|
-
actions?: Array<{
|
|
37
|
-
label: string;
|
|
38
|
-
actionUrl?: string;
|
|
39
|
-
actionType?: string;
|
|
40
|
-
icon?: string;
|
|
41
|
-
}>;
|
|
42
|
-
[key: string]: any;
|
|
43
|
-
}
|
|
44
|
-
interface GlobalFilter {
|
|
45
|
-
field: string;
|
|
46
|
-
label?: string;
|
|
47
|
-
type?: string;
|
|
48
|
-
options?: string[];
|
|
49
|
-
optionsFrom?: {
|
|
50
|
-
object: string;
|
|
51
|
-
valueField: string;
|
|
52
|
-
labelField?: string;
|
|
53
|
-
filter?: any;
|
|
54
|
-
};
|
|
55
|
-
defaultValue?: any;
|
|
56
|
-
scope?: string;
|
|
57
|
-
targetWidgets?: string[];
|
|
58
|
-
}
|
|
59
|
-
interface DashboardSpec {
|
|
60
|
-
name?: string;
|
|
61
|
-
label?: string;
|
|
62
|
-
description?: string;
|
|
63
|
-
widgets?: DashboardWidget[];
|
|
64
|
-
globalFilters?: GlobalFilter[];
|
|
65
|
-
header?: DashboardHeader;
|
|
66
|
-
refreshInterval?: number;
|
|
67
|
-
dateRange?: {
|
|
68
|
-
field?: string;
|
|
69
|
-
defaultRange?: string;
|
|
70
|
-
allowCustomRange?: boolean;
|
|
71
|
-
};
|
|
72
|
-
aria?: {
|
|
73
|
-
ariaLabel?: string;
|
|
74
|
-
ariaDescribedBy?: string;
|
|
75
|
-
role?: string;
|
|
76
|
-
};
|
|
77
|
-
performance?: any;
|
|
78
|
-
}
|
|
79
|
-
/** Transforms a Dashboard spec into a dashboard layout SchemaNode */
|
|
80
|
-
export declare const bridgeDashboard: BridgeFn<DashboardSpec>;
|
|
81
|
-
export {};
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ObjectUI
|
|
3
|
-
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
-
*
|
|
5
|
-
* This source code is licensed under the MIT license found in the
|
|
6
|
-
* LICENSE file in the root directory of this source tree.
|
|
7
|
-
*/
|
|
8
|
-
function mapWidget(widget, index) {
|
|
9
|
-
const node = {
|
|
10
|
-
type: widget.type ?? 'chart',
|
|
11
|
-
id: `widget-${index}`,
|
|
12
|
-
};
|
|
13
|
-
if (widget.title)
|
|
14
|
-
node.title = widget.title;
|
|
15
|
-
if (widget.description)
|
|
16
|
-
node.description = widget.description;
|
|
17
|
-
if (widget.chartConfig)
|
|
18
|
-
node.chartConfig = widget.chartConfig;
|
|
19
|
-
if (widget.colorVariant)
|
|
20
|
-
node.colorVariant = widget.colorVariant;
|
|
21
|
-
if (widget.actionUrl)
|
|
22
|
-
node.actionUrl = widget.actionUrl;
|
|
23
|
-
if (widget.actionType)
|
|
24
|
-
node.actionType = widget.actionType;
|
|
25
|
-
if (widget.actionIcon)
|
|
26
|
-
node.actionIcon = widget.actionIcon;
|
|
27
|
-
if (widget.object)
|
|
28
|
-
node.object = widget.object;
|
|
29
|
-
if (widget.filter)
|
|
30
|
-
node.filter = widget.filter;
|
|
31
|
-
if (widget.categoryField)
|
|
32
|
-
node.categoryField = widget.categoryField;
|
|
33
|
-
if (widget.valueField)
|
|
34
|
-
node.valueField = widget.valueField;
|
|
35
|
-
if (widget.aggregate)
|
|
36
|
-
node.aggregate = widget.aggregate;
|
|
37
|
-
if (widget.measures)
|
|
38
|
-
node.measures = widget.measures;
|
|
39
|
-
if (widget.layout)
|
|
40
|
-
node.layout = widget.layout;
|
|
41
|
-
if (widget.options)
|
|
42
|
-
node.options = widget.options;
|
|
43
|
-
if (widget.responsive)
|
|
44
|
-
node.responsive = widget.responsive;
|
|
45
|
-
if (widget.aria)
|
|
46
|
-
node.aria = widget.aria;
|
|
47
|
-
return node;
|
|
48
|
-
}
|
|
49
|
-
function mapGlobalFilters(filters) {
|
|
50
|
-
return {
|
|
51
|
-
type: 'filter-bar',
|
|
52
|
-
id: 'dashboard-filters',
|
|
53
|
-
filters: filters.map((f) => {
|
|
54
|
-
const mapped = { field: f.field };
|
|
55
|
-
if (f.label)
|
|
56
|
-
mapped.label = f.label;
|
|
57
|
-
if (f.type)
|
|
58
|
-
mapped.type = f.type;
|
|
59
|
-
if (f.options)
|
|
60
|
-
mapped.options = f.options;
|
|
61
|
-
if (f.optionsFrom)
|
|
62
|
-
mapped.optionsFrom = f.optionsFrom;
|
|
63
|
-
if (f.defaultValue != null)
|
|
64
|
-
mapped.defaultValue = f.defaultValue;
|
|
65
|
-
if (f.scope)
|
|
66
|
-
mapped.scope = f.scope;
|
|
67
|
-
if (f.targetWidgets)
|
|
68
|
-
mapped.targetWidgets = f.targetWidgets;
|
|
69
|
-
return mapped;
|
|
70
|
-
}),
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
function mapHeader(header) {
|
|
74
|
-
const node = {
|
|
75
|
-
type: 'dashboard-header',
|
|
76
|
-
id: 'dashboard-header',
|
|
77
|
-
...header,
|
|
78
|
-
};
|
|
79
|
-
return node;
|
|
80
|
-
}
|
|
81
|
-
/** Transforms a Dashboard spec into a dashboard layout SchemaNode */
|
|
82
|
-
export const bridgeDashboard = (spec, _context) => {
|
|
83
|
-
const widgetNodes = (spec.widgets ?? []).map(mapWidget);
|
|
84
|
-
const bodyChildren = [];
|
|
85
|
-
if (spec.header) {
|
|
86
|
-
bodyChildren.push(mapHeader(spec.header));
|
|
87
|
-
}
|
|
88
|
-
if (spec.globalFilters && spec.globalFilters.length > 0) {
|
|
89
|
-
bodyChildren.push(mapGlobalFilters(spec.globalFilters));
|
|
90
|
-
}
|
|
91
|
-
// P1.3 — Date range filter
|
|
92
|
-
if (spec.dateRange) {
|
|
93
|
-
bodyChildren.push({
|
|
94
|
-
type: 'date-range-filter',
|
|
95
|
-
id: 'dashboard-date-range',
|
|
96
|
-
...spec.dateRange,
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
bodyChildren.push({
|
|
100
|
-
type: 'widget-grid',
|
|
101
|
-
id: 'dashboard-widgets',
|
|
102
|
-
body: widgetNodes,
|
|
103
|
-
});
|
|
104
|
-
const node = {
|
|
105
|
-
type: 'dashboard',
|
|
106
|
-
id: spec.name,
|
|
107
|
-
body: bodyChildren,
|
|
108
|
-
};
|
|
109
|
-
if (spec.label)
|
|
110
|
-
node.label = spec.label;
|
|
111
|
-
if (spec.description)
|
|
112
|
-
node.description = spec.description;
|
|
113
|
-
if (spec.refreshInterval != null)
|
|
114
|
-
node.refreshInterval = spec.refreshInterval;
|
|
115
|
-
// P1.6 — i18n & ARIA
|
|
116
|
-
if (spec.aria)
|
|
117
|
-
node.aria = spec.aria;
|
|
118
|
-
if (spec.performance)
|
|
119
|
-
node.performance = spec.performance;
|
|
120
|
-
return node;
|
|
121
|
-
};
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ObjectUI
|
|
3
|
-
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
-
*
|
|
5
|
-
* This source code is licensed under the MIT license found in the
|
|
6
|
-
* LICENSE file in the root directory of this source tree.
|
|
7
|
-
*/
|
|
8
|
-
import type { BridgeFn } from '../types';
|
|
9
|
-
interface PageComponent {
|
|
10
|
-
type: string;
|
|
11
|
-
id?: string;
|
|
12
|
-
label?: string;
|
|
13
|
-
properties?: Record<string, any>;
|
|
14
|
-
events?: Record<string, any>;
|
|
15
|
-
style?: Record<string, any>;
|
|
16
|
-
responsiveStyles?: Record<string, any>;
|
|
17
|
-
className?: string;
|
|
18
|
-
/** Canonical conditional-visibility predicate (ADR-0089). */
|
|
19
|
-
visibleWhen?: string;
|
|
20
|
-
/** @deprecated ADR-0089 → `visibleWhen`. */
|
|
21
|
-
visibility?: string;
|
|
22
|
-
dataSource?: any;
|
|
23
|
-
responsive?: any;
|
|
24
|
-
aria?: Record<string, string>;
|
|
25
|
-
}
|
|
26
|
-
interface PageRegion {
|
|
27
|
-
name?: string;
|
|
28
|
-
width?: string;
|
|
29
|
-
components?: PageComponent[];
|
|
30
|
-
}
|
|
31
|
-
interface PageVariable {
|
|
32
|
-
name: string;
|
|
33
|
-
type?: string;
|
|
34
|
-
defaultValue?: any;
|
|
35
|
-
source?: string;
|
|
36
|
-
}
|
|
37
|
-
interface PageSpec {
|
|
38
|
-
name?: string;
|
|
39
|
-
label?: string;
|
|
40
|
-
description?: string;
|
|
41
|
-
icon?: string;
|
|
42
|
-
type?: string;
|
|
43
|
-
variables?: PageVariable[];
|
|
44
|
-
regions?: PageRegion[];
|
|
45
|
-
template?: string;
|
|
46
|
-
object?: string;
|
|
47
|
-
isDefault?: boolean;
|
|
48
|
-
assignedProfiles?: string[];
|
|
49
|
-
interfaceConfig?: any;
|
|
50
|
-
aria?: {
|
|
51
|
-
ariaLabel?: string;
|
|
52
|
-
ariaDescribedBy?: string;
|
|
53
|
-
role?: string;
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
/** Transforms a Page spec into a page layout SchemaNode */
|
|
57
|
-
export declare const bridgePage: BridgeFn<PageSpec>;
|
|
58
|
-
export {};
|