@object-ui/core 3.0.3 → 3.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/.turbo/turbo-build.log +1 -1
- package/dist/actions/ActionEngine.d.ts +98 -0
- package/dist/actions/ActionEngine.js +222 -0
- package/dist/actions/UndoManager.d.ts +80 -0
- package/dist/actions/UndoManager.js +193 -0
- package/dist/actions/index.d.ts +2 -0
- package/dist/actions/index.js +2 -0
- package/dist/adapters/ApiDataSource.d.ts +2 -1
- package/dist/adapters/ApiDataSource.js +25 -0
- package/dist/adapters/ValueDataSource.d.ts +2 -1
- package/dist/adapters/ValueDataSource.js +99 -3
- package/dist/data-scope/ViewDataProvider.d.ts +143 -0
- package/dist/data-scope/ViewDataProvider.js +153 -0
- package/dist/data-scope/index.d.ts +1 -0
- package/dist/data-scope/index.js +1 -0
- package/dist/evaluator/ExpressionEvaluator.d.ts +7 -0
- package/dist/evaluator/ExpressionEvaluator.js +19 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/protocols/DndProtocol.d.ts +84 -0
- package/dist/protocols/DndProtocol.js +113 -0
- package/dist/protocols/KeyboardProtocol.d.ts +93 -0
- package/dist/protocols/KeyboardProtocol.js +108 -0
- package/dist/protocols/NotificationProtocol.d.ts +71 -0
- package/dist/protocols/NotificationProtocol.js +99 -0
- package/dist/protocols/ResponsiveProtocol.d.ts +73 -0
- package/dist/protocols/ResponsiveProtocol.js +158 -0
- package/dist/protocols/SharingProtocol.d.ts +71 -0
- package/dist/protocols/SharingProtocol.js +124 -0
- package/dist/protocols/index.d.ts +12 -0
- package/dist/protocols/index.js +12 -0
- package/dist/utils/debug-collector.d.ts +59 -0
- package/dist/utils/debug-collector.js +73 -0
- package/dist/utils/debug.d.ts +37 -2
- package/dist/utils/debug.js +62 -3
- package/dist/utils/expand-fields.d.ts +40 -0
- package/dist/utils/expand-fields.js +68 -0
- package/dist/utils/extract-records.d.ts +16 -0
- package/dist/utils/extract-records.js +32 -0
- package/dist/utils/normalize-quick-filter.d.ts +29 -0
- package/dist/utils/normalize-quick-filter.js +66 -0
- package/package.json +3 -3
- package/src/__tests__/protocols/DndProtocol.test.ts +186 -0
- package/src/__tests__/protocols/KeyboardProtocol.test.ts +177 -0
- package/src/__tests__/protocols/NotificationProtocol.test.ts +142 -0
- package/src/__tests__/protocols/ResponsiveProtocol.test.ts +176 -0
- package/src/__tests__/protocols/SharingProtocol.test.ts +188 -0
- package/src/actions/ActionEngine.ts +268 -0
- package/src/actions/UndoManager.ts +215 -0
- package/src/actions/__tests__/ActionEngine.test.ts +206 -0
- package/src/actions/__tests__/UndoManager.test.ts +320 -0
- package/src/actions/index.ts +2 -0
- package/src/adapters/ApiDataSource.ts +27 -0
- package/src/adapters/ValueDataSource.ts +109 -3
- package/src/adapters/__tests__/ValueDataSource.test.ts +147 -0
- package/src/data-scope/ViewDataProvider.ts +282 -0
- package/src/data-scope/__tests__/ViewDataProvider.test.ts +270 -0
- package/src/data-scope/index.ts +8 -0
- package/src/evaluator/ExpressionEvaluator.ts +22 -0
- package/src/evaluator/__tests__/ExpressionEvaluator.test.ts +31 -1
- package/src/index.ts +5 -0
- package/src/protocols/DndProtocol.ts +184 -0
- package/src/protocols/KeyboardProtocol.ts +185 -0
- package/src/protocols/NotificationProtocol.ts +159 -0
- package/src/protocols/ResponsiveProtocol.ts +210 -0
- package/src/protocols/SharingProtocol.ts +185 -0
- package/src/protocols/index.ts +13 -0
- package/src/utils/__tests__/debug-collector.test.ts +102 -0
- package/src/utils/__tests__/debug.test.ts +52 -1
- package/src/utils/__tests__/expand-fields.test.ts +120 -0
- package/src/utils/__tests__/extract-records.test.ts +50 -0
- package/src/utils/__tests__/normalize-quick-filter.test.ts +123 -0
- package/src/utils/debug-collector.ts +100 -0
- package/src/utils/debug.ts +87 -6
- package/src/utils/expand-fields.ts +76 -0
- package/src/utils/extract-records.ts +33 -0
- package/src/utils/normalize-quick-filter.ts +78 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -17,6 +17,64 @@ function getRecordId(record, idField) {
|
|
|
17
17
|
return record[idField];
|
|
18
18
|
return record._id ?? record.id;
|
|
19
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Evaluate an AST-format filter node against a record.
|
|
22
|
+
* Supports conditions like ['field', 'op', value] and logical
|
|
23
|
+
* combinations like ['and', ...conditions] or ['or', ...conditions].
|
|
24
|
+
*/
|
|
25
|
+
function matchesASTFilter(record, filterNode) {
|
|
26
|
+
if (!filterNode || filterNode.length === 0)
|
|
27
|
+
return true;
|
|
28
|
+
const head = filterNode[0];
|
|
29
|
+
// Logical operators: ['and', ...conditions] or ['or', ...conditions]
|
|
30
|
+
if (head === 'and') {
|
|
31
|
+
return filterNode.slice(1).every((sub) => matchesASTFilter(record, sub));
|
|
32
|
+
}
|
|
33
|
+
if (head === 'or') {
|
|
34
|
+
return filterNode.slice(1).some((sub) => matchesASTFilter(record, sub));
|
|
35
|
+
}
|
|
36
|
+
// Condition node: [field, operator, value]
|
|
37
|
+
if (filterNode.length === 3 && typeof head === 'string') {
|
|
38
|
+
const [field, operator, target] = filterNode;
|
|
39
|
+
const value = record[field];
|
|
40
|
+
switch (operator) {
|
|
41
|
+
case '=':
|
|
42
|
+
return value === target;
|
|
43
|
+
case '!=':
|
|
44
|
+
return value !== target;
|
|
45
|
+
case '>':
|
|
46
|
+
return value > target;
|
|
47
|
+
case '>=':
|
|
48
|
+
return value >= target;
|
|
49
|
+
case '<':
|
|
50
|
+
return value < target;
|
|
51
|
+
case '<=':
|
|
52
|
+
return value <= target;
|
|
53
|
+
case 'in':
|
|
54
|
+
return Array.isArray(target) && target.includes(value);
|
|
55
|
+
case 'not in':
|
|
56
|
+
case 'notin': // alias used by convertFiltersToAST
|
|
57
|
+
return Array.isArray(target) && !target.includes(value);
|
|
58
|
+
case 'contains': {
|
|
59
|
+
const lv = typeof value === 'string' ? value.toLowerCase() : '';
|
|
60
|
+
return typeof value === 'string' && lv.includes(String(target).toLowerCase());
|
|
61
|
+
}
|
|
62
|
+
case 'notcontains': {
|
|
63
|
+
const lv = typeof value === 'string' ? value.toLowerCase() : '';
|
|
64
|
+
return typeof value === 'string' && !lv.includes(String(target).toLowerCase());
|
|
65
|
+
}
|
|
66
|
+
case 'startswith': {
|
|
67
|
+
const lv = typeof value === 'string' ? value.toLowerCase() : '';
|
|
68
|
+
return typeof value === 'string' && lv.startsWith(String(target).toLowerCase());
|
|
69
|
+
}
|
|
70
|
+
case 'between':
|
|
71
|
+
return Array.isArray(target) && target.length === 2 && value >= target[0] && value <= target[1];
|
|
72
|
+
default:
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
20
78
|
/**
|
|
21
79
|
* Simple in-memory filter evaluation.
|
|
22
80
|
* Supports flat key-value equality and basic operators ($gt, $gte, $lt, $lte, $ne, $in).
|
|
@@ -168,9 +226,14 @@ export class ValueDataSource {
|
|
|
168
226
|
// -----------------------------------------------------------------------
|
|
169
227
|
async find(_resource, params) {
|
|
170
228
|
let result = [...this.items];
|
|
171
|
-
// Filter
|
|
172
|
-
if (params?.$filter
|
|
173
|
-
|
|
229
|
+
// Filter — support both MongoDB-style objects and AST-format arrays
|
|
230
|
+
if (params?.$filter) {
|
|
231
|
+
if (Array.isArray(params.$filter) && params.$filter.length > 0) {
|
|
232
|
+
result = result.filter((r) => matchesASTFilter(r, params.$filter));
|
|
233
|
+
}
|
|
234
|
+
else if (!Array.isArray(params.$filter) && Object.keys(params.$filter).length > 0) {
|
|
235
|
+
result = result.filter((r) => matchesFilter(r, params.$filter));
|
|
236
|
+
}
|
|
174
237
|
}
|
|
175
238
|
// Search (simple text search across all string fields)
|
|
176
239
|
if (params?.$search) {
|
|
@@ -273,6 +336,39 @@ export class ValueDataSource {
|
|
|
273
336
|
async getApp(_appId) {
|
|
274
337
|
return null;
|
|
275
338
|
}
|
|
339
|
+
async aggregate(_resource, params) {
|
|
340
|
+
const { field, function: aggFn, groupBy } = params;
|
|
341
|
+
const groups = {};
|
|
342
|
+
for (const record of this.items) {
|
|
343
|
+
const key = String(record[groupBy] ?? 'Unknown');
|
|
344
|
+
if (!groups[key])
|
|
345
|
+
groups[key] = [];
|
|
346
|
+
groups[key].push(record);
|
|
347
|
+
}
|
|
348
|
+
return Object.entries(groups).map(([key, group]) => {
|
|
349
|
+
const values = group.map(r => Number(r[field]) || 0);
|
|
350
|
+
let result;
|
|
351
|
+
switch (aggFn) {
|
|
352
|
+
case 'count':
|
|
353
|
+
result = group.length;
|
|
354
|
+
break;
|
|
355
|
+
case 'avg':
|
|
356
|
+
result = values.length > 0 ? values.reduce((a, b) => a + b, 0) / values.length : 0;
|
|
357
|
+
break;
|
|
358
|
+
case 'min':
|
|
359
|
+
result = values.length > 0 ? Math.min(...values) : 0;
|
|
360
|
+
break;
|
|
361
|
+
case 'max':
|
|
362
|
+
result = values.length > 0 ? Math.max(...values) : 0;
|
|
363
|
+
break;
|
|
364
|
+
case 'sum':
|
|
365
|
+
default:
|
|
366
|
+
result = values.reduce((a, b) => a + b, 0);
|
|
367
|
+
break;
|
|
368
|
+
}
|
|
369
|
+
return { [groupBy]: key, [field]: result };
|
|
370
|
+
});
|
|
371
|
+
}
|
|
276
372
|
// -----------------------------------------------------------------------
|
|
277
373
|
// Extra utilities
|
|
278
374
|
// -----------------------------------------------------------------------
|
|
@@ -0,0 +1,143 @@
|
|
|
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
|
+
* @object-ui/core - View Data Provider
|
|
10
|
+
*
|
|
11
|
+
* Resolves @objectstack/spec ViewData discriminated union into
|
|
12
|
+
* a unified data access interface. Supports three data modes:
|
|
13
|
+
*
|
|
14
|
+
* 1. 'object' — Metadata-driven via ObjectStack API (object name → fields/records)
|
|
15
|
+
* 2. 'api' — Custom REST API endpoints (read/write URLs)
|
|
16
|
+
* 3. 'value' — Static data (pre-loaded items array)
|
|
17
|
+
*
|
|
18
|
+
* @module data-scope
|
|
19
|
+
* @packageDocumentation
|
|
20
|
+
*/
|
|
21
|
+
/** ViewData union — matches @objectstack/spec ViewDataSchema */
|
|
22
|
+
export type ViewDataConfig = {
|
|
23
|
+
provider: 'object';
|
|
24
|
+
object: string;
|
|
25
|
+
} | {
|
|
26
|
+
provider: 'api';
|
|
27
|
+
read: {
|
|
28
|
+
url: string;
|
|
29
|
+
method?: string;
|
|
30
|
+
headers?: Record<string, string>;
|
|
31
|
+
};
|
|
32
|
+
write?: {
|
|
33
|
+
url: string;
|
|
34
|
+
method?: string;
|
|
35
|
+
headers?: Record<string, string>;
|
|
36
|
+
};
|
|
37
|
+
} | {
|
|
38
|
+
provider: 'value';
|
|
39
|
+
items: any[];
|
|
40
|
+
};
|
|
41
|
+
/** Element-level data source — matches @objectstack/spec ElementDataSourceSchema */
|
|
42
|
+
export interface ElementDataSourceConfig {
|
|
43
|
+
object: string;
|
|
44
|
+
view?: string;
|
|
45
|
+
filter?: Record<string, any>;
|
|
46
|
+
sort?: Array<{
|
|
47
|
+
field: string;
|
|
48
|
+
order: 'asc' | 'desc';
|
|
49
|
+
}>;
|
|
50
|
+
limit?: number;
|
|
51
|
+
}
|
|
52
|
+
/** Fetcher interface — consumers provide the actual fetch implementation */
|
|
53
|
+
export interface DataFetcher {
|
|
54
|
+
/** Fetch records for an object */
|
|
55
|
+
fetchRecords(object: string, options?: {
|
|
56
|
+
filter?: Record<string, any>;
|
|
57
|
+
sort?: Array<{
|
|
58
|
+
field: string;
|
|
59
|
+
order: 'asc' | 'desc';
|
|
60
|
+
}>;
|
|
61
|
+
limit?: number;
|
|
62
|
+
offset?: number;
|
|
63
|
+
fields?: string[];
|
|
64
|
+
}): Promise<{
|
|
65
|
+
records: any[];
|
|
66
|
+
total: number;
|
|
67
|
+
}>;
|
|
68
|
+
/** Fetch object metadata (fields, etc.) */
|
|
69
|
+
fetchMetadata?(object: string): Promise<{
|
|
70
|
+
name: string;
|
|
71
|
+
label?: string;
|
|
72
|
+
fields: Array<{
|
|
73
|
+
name: string;
|
|
74
|
+
label?: string;
|
|
75
|
+
type: string;
|
|
76
|
+
required?: boolean;
|
|
77
|
+
}>;
|
|
78
|
+
}>;
|
|
79
|
+
/** Fetch from a custom API URL */
|
|
80
|
+
fetchUrl?(url: string, options?: {
|
|
81
|
+
method?: string;
|
|
82
|
+
headers?: Record<string, string>;
|
|
83
|
+
body?: any;
|
|
84
|
+
}): Promise<any>;
|
|
85
|
+
}
|
|
86
|
+
/** Resolved data result */
|
|
87
|
+
export interface ResolvedData {
|
|
88
|
+
/** Data records */
|
|
89
|
+
records: any[];
|
|
90
|
+
/** Total record count (for pagination) */
|
|
91
|
+
total: number;
|
|
92
|
+
/** Object metadata (if available) */
|
|
93
|
+
metadata?: {
|
|
94
|
+
name: string;
|
|
95
|
+
label?: string;
|
|
96
|
+
fields: Array<{
|
|
97
|
+
name: string;
|
|
98
|
+
label?: string;
|
|
99
|
+
type: string;
|
|
100
|
+
required?: boolean;
|
|
101
|
+
}>;
|
|
102
|
+
};
|
|
103
|
+
/** Provider type */
|
|
104
|
+
provider: 'object' | 'api' | 'value';
|
|
105
|
+
/** Loading state */
|
|
106
|
+
loading: boolean;
|
|
107
|
+
/** Error */
|
|
108
|
+
error?: string;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Resolves ViewData configuration into actual data via a pluggable fetcher.
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* ```ts
|
|
115
|
+
* const provider = new ViewDataProvider(myFetcher);
|
|
116
|
+
* const data = await provider.resolve({ provider: 'object', object: 'Account' });
|
|
117
|
+
* ```
|
|
118
|
+
*/
|
|
119
|
+
export declare class ViewDataProvider {
|
|
120
|
+
private fetcher;
|
|
121
|
+
constructor(fetcher?: DataFetcher);
|
|
122
|
+
/** Set the data fetcher implementation */
|
|
123
|
+
setFetcher(fetcher: DataFetcher): void;
|
|
124
|
+
/** Resolve ViewData config into actual data */
|
|
125
|
+
resolve(config: ViewDataConfig, options?: {
|
|
126
|
+
filter?: Record<string, any>;
|
|
127
|
+
sort?: Array<{
|
|
128
|
+
field: string;
|
|
129
|
+
order: 'asc' | 'desc';
|
|
130
|
+
}>;
|
|
131
|
+
limit?: number;
|
|
132
|
+
offset?: number;
|
|
133
|
+
fields?: string[];
|
|
134
|
+
}): Promise<ResolvedData>;
|
|
135
|
+
/** Resolve static value data */
|
|
136
|
+
private resolveValue;
|
|
137
|
+
/** Resolve API-based data */
|
|
138
|
+
private resolveApi;
|
|
139
|
+
/** Resolve object-based data (metadata-driven) */
|
|
140
|
+
private resolveObject;
|
|
141
|
+
/** Resolve element-level data source */
|
|
142
|
+
resolveElementDataSource(config: ElementDataSourceConfig): Promise<ResolvedData>;
|
|
143
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
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
|
+
/** Extract records array from various common API response shapes */
|
|
9
|
+
function extractRecords(data) {
|
|
10
|
+
if (Array.isArray(data))
|
|
11
|
+
return data;
|
|
12
|
+
if (Array.isArray(data?.records))
|
|
13
|
+
return data.records;
|
|
14
|
+
if (Array.isArray(data?.data))
|
|
15
|
+
return data.data;
|
|
16
|
+
if (Array.isArray(data?.items))
|
|
17
|
+
return data.items;
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Resolves ViewData configuration into actual data via a pluggable fetcher.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```ts
|
|
25
|
+
* const provider = new ViewDataProvider(myFetcher);
|
|
26
|
+
* const data = await provider.resolve({ provider: 'object', object: 'Account' });
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export class ViewDataProvider {
|
|
30
|
+
constructor(fetcher) {
|
|
31
|
+
Object.defineProperty(this, "fetcher", {
|
|
32
|
+
enumerable: true,
|
|
33
|
+
configurable: true,
|
|
34
|
+
writable: true,
|
|
35
|
+
value: null
|
|
36
|
+
});
|
|
37
|
+
this.fetcher = fetcher ?? null;
|
|
38
|
+
}
|
|
39
|
+
/** Set the data fetcher implementation */
|
|
40
|
+
setFetcher(fetcher) {
|
|
41
|
+
this.fetcher = fetcher;
|
|
42
|
+
}
|
|
43
|
+
/** Resolve ViewData config into actual data */
|
|
44
|
+
async resolve(config, options) {
|
|
45
|
+
switch (config.provider) {
|
|
46
|
+
case 'value':
|
|
47
|
+
return this.resolveValue(config);
|
|
48
|
+
case 'api':
|
|
49
|
+
return this.resolveApi(config);
|
|
50
|
+
case 'object':
|
|
51
|
+
return this.resolveObject(config, options);
|
|
52
|
+
default:
|
|
53
|
+
return {
|
|
54
|
+
records: [],
|
|
55
|
+
total: 0,
|
|
56
|
+
provider: 'value',
|
|
57
|
+
loading: false,
|
|
58
|
+
error: `Unknown data provider: ${config.provider}`,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/** Resolve static value data */
|
|
63
|
+
resolveValue(config) {
|
|
64
|
+
const items = Array.isArray(config.items) ? config.items : [];
|
|
65
|
+
return {
|
|
66
|
+
records: items,
|
|
67
|
+
total: items.length,
|
|
68
|
+
provider: 'value',
|
|
69
|
+
loading: false,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/** Resolve API-based data */
|
|
73
|
+
async resolveApi(config) {
|
|
74
|
+
if (!this.fetcher?.fetchUrl) {
|
|
75
|
+
return {
|
|
76
|
+
records: [],
|
|
77
|
+
total: 0,
|
|
78
|
+
provider: 'api',
|
|
79
|
+
loading: false,
|
|
80
|
+
error: 'No fetchUrl implementation available for API data provider',
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
const data = await this.fetcher.fetchUrl(config.read.url, {
|
|
85
|
+
method: config.read.method,
|
|
86
|
+
headers: config.read.headers,
|
|
87
|
+
});
|
|
88
|
+
// Handle common response shapes
|
|
89
|
+
const records = extractRecords(data);
|
|
90
|
+
const total = data?.total ?? data?.count ?? records.length;
|
|
91
|
+
return {
|
|
92
|
+
records,
|
|
93
|
+
total,
|
|
94
|
+
provider: 'api',
|
|
95
|
+
loading: false,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
return {
|
|
100
|
+
records: [],
|
|
101
|
+
total: 0,
|
|
102
|
+
provider: 'api',
|
|
103
|
+
loading: false,
|
|
104
|
+
error: error.message,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/** Resolve object-based data (metadata-driven) */
|
|
109
|
+
async resolveObject(config, options) {
|
|
110
|
+
if (!this.fetcher) {
|
|
111
|
+
return {
|
|
112
|
+
records: [],
|
|
113
|
+
total: 0,
|
|
114
|
+
provider: 'object',
|
|
115
|
+
loading: false,
|
|
116
|
+
error: 'No data fetcher configured for object data provider',
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
try {
|
|
120
|
+
// Fetch metadata if available
|
|
121
|
+
let metadata;
|
|
122
|
+
if (this.fetcher.fetchMetadata) {
|
|
123
|
+
metadata = await this.fetcher.fetchMetadata(config.object);
|
|
124
|
+
}
|
|
125
|
+
// Fetch records
|
|
126
|
+
const result = await this.fetcher.fetchRecords(config.object, options);
|
|
127
|
+
return {
|
|
128
|
+
records: result.records,
|
|
129
|
+
total: result.total,
|
|
130
|
+
metadata,
|
|
131
|
+
provider: 'object',
|
|
132
|
+
loading: false,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
return {
|
|
137
|
+
records: [],
|
|
138
|
+
total: 0,
|
|
139
|
+
provider: 'object',
|
|
140
|
+
loading: false,
|
|
141
|
+
error: error.message,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/** Resolve element-level data source */
|
|
146
|
+
async resolveElementDataSource(config) {
|
|
147
|
+
return this.resolve({ provider: 'object', object: config.object }, {
|
|
148
|
+
filter: config.filter,
|
|
149
|
+
sort: config.sort,
|
|
150
|
+
limit: config.limit,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
@@ -8,3 +8,4 @@
|
|
|
8
8
|
* @packageDocumentation
|
|
9
9
|
*/
|
|
10
10
|
export { DataScopeManager, defaultDataScopeManager, type RowLevelFilter, type DataScopeConfig, } from './DataScopeManager.js';
|
|
11
|
+
export { ViewDataProvider, type ViewDataConfig, type ElementDataSourceConfig, type DataFetcher, type ResolvedData, } from './ViewDataProvider.js';
|
package/dist/data-scope/index.js
CHANGED
|
@@ -125,3 +125,10 @@ export declare function evaluateExpression(expression: string | boolean | number
|
|
|
125
125
|
* Convenience function to evaluate a condition
|
|
126
126
|
*/
|
|
127
127
|
export declare function evaluateCondition(condition: string | boolean | undefined, context?: Record<string, any>): boolean;
|
|
128
|
+
/**
|
|
129
|
+
* Convenience function to evaluate a plain condition string against a data record.
|
|
130
|
+
* Supports both template expressions (e.g., '${data.amount > 1000}') and
|
|
131
|
+
* plain expressions (e.g., "status == 'overdue'").
|
|
132
|
+
* Record fields are available both directly (status) and namespaced (data.status).
|
|
133
|
+
*/
|
|
134
|
+
export declare function evaluatePlainCondition(condition: string, record: Record<string, any>): boolean;
|
|
@@ -242,3 +242,22 @@ export function evaluateCondition(condition, context = {}) {
|
|
|
242
242
|
const evaluator = new ExpressionEvaluator(context, globalCache, globalFormulas);
|
|
243
243
|
return evaluator.evaluateCondition(condition);
|
|
244
244
|
}
|
|
245
|
+
/**
|
|
246
|
+
* Convenience function to evaluate a plain condition string against a data record.
|
|
247
|
+
* Supports both template expressions (e.g., '${data.amount > 1000}') and
|
|
248
|
+
* plain expressions (e.g., "status == 'overdue'").
|
|
249
|
+
* Record fields are available both directly (status) and namespaced (data.status).
|
|
250
|
+
*/
|
|
251
|
+
export function evaluatePlainCondition(condition, record) {
|
|
252
|
+
const evaluator = new ExpressionEvaluator({ ...record, data: record }, globalCache, globalFormulas);
|
|
253
|
+
try {
|
|
254
|
+
const isTemplate = /\$\{/.test(condition);
|
|
255
|
+
const result = isTemplate
|
|
256
|
+
? evaluator.evaluate(condition, { throwOnError: true })
|
|
257
|
+
: evaluator.evaluateExpression(condition);
|
|
258
|
+
return result === true;
|
|
259
|
+
}
|
|
260
|
+
catch {
|
|
261
|
+
return false;
|
|
262
|
+
}
|
|
263
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -13,6 +13,9 @@ export * from './registry/WidgetRegistry.js';
|
|
|
13
13
|
export * from './validation/index.js';
|
|
14
14
|
export * from './builder/schema-builder.js';
|
|
15
15
|
export * from './utils/filter-converter.js';
|
|
16
|
+
export * from './utils/normalize-quick-filter.js';
|
|
17
|
+
export * from './utils/extract-records.js';
|
|
18
|
+
export * from './utils/expand-fields.js';
|
|
16
19
|
export * from './evaluator/index.js';
|
|
17
20
|
export * from './actions/index.js';
|
|
18
21
|
export * from './query/index.js';
|
|
@@ -21,3 +24,5 @@ export * from './theme/index.js';
|
|
|
21
24
|
export * from './data-scope/index.js';
|
|
22
25
|
export * from './errors/index.js';
|
|
23
26
|
export * from './utils/debug.js';
|
|
27
|
+
export * from './utils/debug-collector.js';
|
|
28
|
+
export * from './protocols/index.js';
|
package/dist/index.js
CHANGED
|
@@ -12,6 +12,9 @@ export * from './registry/WidgetRegistry.js';
|
|
|
12
12
|
export * from './validation/index.js';
|
|
13
13
|
export * from './builder/schema-builder.js';
|
|
14
14
|
export * from './utils/filter-converter.js';
|
|
15
|
+
export * from './utils/normalize-quick-filter.js';
|
|
16
|
+
export * from './utils/extract-records.js';
|
|
17
|
+
export * from './utils/expand-fields.js';
|
|
15
18
|
export * from './evaluator/index.js';
|
|
16
19
|
export * from './actions/index.js';
|
|
17
20
|
export * from './query/index.js';
|
|
@@ -20,3 +23,5 @@ export * from './theme/index.js';
|
|
|
20
23
|
export * from './data-scope/index.js';
|
|
21
24
|
export * from './errors/index.js';
|
|
22
25
|
export * from './utils/debug.js';
|
|
26
|
+
export * from './utils/debug-collector.js';
|
|
27
|
+
export * from './protocols/index.js';
|
|
@@ -0,0 +1,84 @@
|
|
|
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
|
+
* @object-ui/core - DnD Protocol Bridge
|
|
10
|
+
*
|
|
11
|
+
* Converts spec-aligned DnD configuration schemas into runtime-usable
|
|
12
|
+
* component props and CSS constraint styles for drag-and-drop interactions.
|
|
13
|
+
*
|
|
14
|
+
* @module protocols/DndProtocol
|
|
15
|
+
* @packageDocumentation
|
|
16
|
+
*/
|
|
17
|
+
import type { DndConfig, DragItem, DropZone, DragConstraint } from '@object-ui/types';
|
|
18
|
+
/** Fully resolved DnD configuration with all defaults applied. */
|
|
19
|
+
export interface ResolvedDndConfig {
|
|
20
|
+
enabled: boolean;
|
|
21
|
+
sortable: boolean;
|
|
22
|
+
autoScroll: boolean;
|
|
23
|
+
touchDelay: number;
|
|
24
|
+
dragItem?: DragItem;
|
|
25
|
+
dropZone?: DropZone;
|
|
26
|
+
}
|
|
27
|
+
/** Component props for a draggable element. */
|
|
28
|
+
export interface DragItemProps {
|
|
29
|
+
draggable: boolean;
|
|
30
|
+
'aria-roledescription': string;
|
|
31
|
+
'aria-label'?: string;
|
|
32
|
+
'aria-describedby'?: string;
|
|
33
|
+
role: string;
|
|
34
|
+
'data-drag-type': string;
|
|
35
|
+
'data-drag-handle': string;
|
|
36
|
+
'data-drag-disabled': string;
|
|
37
|
+
}
|
|
38
|
+
/** Component props for a droppable area. */
|
|
39
|
+
export interface DropZoneProps {
|
|
40
|
+
'aria-dropeffect': string;
|
|
41
|
+
'aria-label'?: string;
|
|
42
|
+
'aria-describedby'?: string;
|
|
43
|
+
role: string;
|
|
44
|
+
'data-drop-accept': string;
|
|
45
|
+
'data-drop-max-items'?: number;
|
|
46
|
+
'data-drop-highlight': string;
|
|
47
|
+
}
|
|
48
|
+
/** CSS constraint styles for drag movement. */
|
|
49
|
+
export interface DragConstraintStyles {
|
|
50
|
+
position: string;
|
|
51
|
+
touchAction: string;
|
|
52
|
+
[key: string]: string | number | undefined;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Resolve a DnD configuration by applying spec defaults to missing fields.
|
|
56
|
+
*
|
|
57
|
+
* @param config - Partial DnD configuration from the spec
|
|
58
|
+
* @returns Fully resolved DnD configuration
|
|
59
|
+
*/
|
|
60
|
+
export declare function resolveDndConfig(config: DndConfig): ResolvedDndConfig;
|
|
61
|
+
/**
|
|
62
|
+
* Convert a spec DragItem to component props suitable for a draggable element.
|
|
63
|
+
* Produces `draggable`, ARIA attributes, and data attributes for DnD libraries.
|
|
64
|
+
*
|
|
65
|
+
* @param item - DragItem configuration from the spec
|
|
66
|
+
* @returns Component props object for a draggable element
|
|
67
|
+
*/
|
|
68
|
+
export declare function createDragItemProps(item: DragItem): DragItemProps;
|
|
69
|
+
/**
|
|
70
|
+
* Convert a spec DropZone to component props suitable for a droppable area.
|
|
71
|
+
* Produces ARIA attributes and data attributes for DnD libraries.
|
|
72
|
+
*
|
|
73
|
+
* @param zone - DropZone configuration from the spec
|
|
74
|
+
* @returns Component props object for a droppable area
|
|
75
|
+
*/
|
|
76
|
+
export declare function createDropZoneProps(zone: DropZone): DropZoneProps;
|
|
77
|
+
/**
|
|
78
|
+
* Resolve a DragConstraint into CSS style properties that restrict
|
|
79
|
+
* drag movement along an axis or within bounds.
|
|
80
|
+
*
|
|
81
|
+
* @param constraint - DragConstraint configuration from the spec
|
|
82
|
+
* @returns CSS styles object for constraining drag movement
|
|
83
|
+
*/
|
|
84
|
+
export declare function resolveDragConstraints(constraint: DragConstraint): DragConstraintStyles;
|