@rakun-kit/manager-react 1.4.5 → 1.4.6
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/README.md +196 -196
- package/dist/cjs/components/editor/themes/editor-theme.css +91 -91
- package/dist/cjs/components/ui/sortable.js +4 -4
- package/dist/cjs/router/dashboard/[contentType]/[edit]/_fields/DynamicDataControl.js +346 -76
- package/dist/cjs/router/dashboard/[contentType]/[edit]/_fields/DynamicDataControl.js.map +1 -1
- package/dist/cjs/state/theme.js +12 -12
- package/dist/esm/components/editor/themes/editor-theme.css +91 -91
- package/dist/esm/components/ui/sortable.js +4 -4
- package/dist/esm/router/dashboard/[contentType]/[edit]/_fields/DynamicDataControl.d.ts +20 -4
- package/dist/esm/router/dashboard/[contentType]/[edit]/_fields/DynamicDataControl.d.ts.map +1 -1
- package/dist/esm/router/dashboard/[contentType]/[edit]/_fields/DynamicDataControl.js +343 -75
- package/dist/esm/router/dashboard/[contentType]/[edit]/_fields/DynamicDataControl.js.map +1 -1
- package/dist/esm/state/theme.js +12 -12
- package/dist/styles.css +18 -14
- package/dist/styles.css.d.ts +3 -3
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +192 -192
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
'use client';
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.DynamicDataControl = exports.sourceFieldOptions = exports.isDynamicFieldEnabled = void 0;
|
|
4
|
+
exports.DynamicDataControl = exports.buildFilter = exports.readFilterState = exports.sourceFieldOptions = exports.isDynamicFieldEnabled = void 0;
|
|
5
5
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
6
6
|
const client_1 = require("@rakun-kit/core/client");
|
|
7
7
|
const lucide_react_1 = require("lucide-react");
|
|
@@ -134,12 +134,24 @@ const nestedSourceFieldOptions = ({ contentType, prefix = '', targetField, depth
|
|
|
134
134
|
return [];
|
|
135
135
|
if (field.config.type === 'Relation' && depth < 3) {
|
|
136
136
|
const relationContentType = field.contentType;
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
137
|
+
const idOption = isCompatibleSourceKind('string', targetField)
|
|
138
|
+
? [
|
|
139
|
+
{
|
|
140
|
+
label: fieldLabel(`${path}._id`),
|
|
141
|
+
value: `${path}._id`,
|
|
142
|
+
kind: 'string',
|
|
143
|
+
},
|
|
144
|
+
]
|
|
145
|
+
: [];
|
|
146
|
+
return [
|
|
147
|
+
...idOption,
|
|
148
|
+
...nestedSourceFieldOptions({
|
|
149
|
+
contentType: relationContentType,
|
|
150
|
+
prefix: path,
|
|
151
|
+
targetField,
|
|
152
|
+
depth: depth + 1,
|
|
153
|
+
}),
|
|
154
|
+
];
|
|
143
155
|
}
|
|
144
156
|
if (field.config.type === 'File') {
|
|
145
157
|
const fieldOption = areFieldKindsCompatible(field, targetField)
|
|
@@ -205,17 +217,55 @@ const sourceContentTypeLabel = (source, documentContentTypeName) => source.conte
|
|
|
205
217
|
? 'Current document'
|
|
206
218
|
: source.contentType;
|
|
207
219
|
const getSourceContentTypes = (contentTypes) => contentTypes.filter((sourceContentType) => (0, client_1.isDynamicDataSourceContentTypeAllowed)(sourceContentType));
|
|
208
|
-
const
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
220
|
+
const operatorByMongoOperator = {
|
|
221
|
+
$eq: 'equals',
|
|
222
|
+
$ne: 'notEquals',
|
|
223
|
+
$contains: 'contains',
|
|
224
|
+
$in: 'in',
|
|
225
|
+
$nin: 'notIn',
|
|
226
|
+
$gt: 'greaterThan',
|
|
227
|
+
$gte: 'greaterThanOrEqual',
|
|
228
|
+
$lt: 'lessThan',
|
|
229
|
+
$lte: 'lessThanOrEqual',
|
|
230
|
+
};
|
|
231
|
+
const readFilterOperand = (value) => {
|
|
232
|
+
if (isRecord(value) &&
|
|
233
|
+
typeof value[client_1.DYNAMIC_QUERY_CURRENT_VALUE_KEY] === 'string') {
|
|
234
|
+
return {
|
|
235
|
+
value: value[client_1.DYNAMIC_QUERY_CURRENT_VALUE_KEY],
|
|
236
|
+
valueSource: 'current',
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
return {
|
|
240
|
+
value: Array.isArray(value) ? value.join(', ') : String(value ?? ''),
|
|
241
|
+
};
|
|
242
|
+
};
|
|
243
|
+
const filterConditionFromEntry = (field, value) => {
|
|
213
244
|
if (value === true)
|
|
214
245
|
return { field, operator: 'true', value: '' };
|
|
215
246
|
if (value === false)
|
|
216
247
|
return { field, operator: 'false', value: '' };
|
|
217
|
-
|
|
218
|
-
|
|
248
|
+
const directOperand = readFilterOperand(value);
|
|
249
|
+
if (directOperand.valueSource === 'current') {
|
|
250
|
+
return { field, operator: 'equals', ...directOperand };
|
|
251
|
+
}
|
|
252
|
+
if (isRecord(value)) {
|
|
253
|
+
if (typeof value.$exists === 'boolean') {
|
|
254
|
+
return {
|
|
255
|
+
field,
|
|
256
|
+
operator: value.$exists ? 'exists' : 'notExists',
|
|
257
|
+
value: '',
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
const operatorEntry = Object.entries(value).find(([operator]) => operator in operatorByMongoOperator);
|
|
261
|
+
if (operatorEntry) {
|
|
262
|
+
const [operator, operatorValue] = operatorEntry;
|
|
263
|
+
return {
|
|
264
|
+
field,
|
|
265
|
+
operator: operatorByMongoOperator[operator],
|
|
266
|
+
...readFilterOperand(operatorValue),
|
|
267
|
+
};
|
|
268
|
+
}
|
|
219
269
|
}
|
|
220
270
|
return {
|
|
221
271
|
field,
|
|
@@ -223,31 +273,124 @@ const readFilterState = (filter) => {
|
|
|
223
273
|
value: typeof value === 'string' ? value : String(value ?? ''),
|
|
224
274
|
};
|
|
225
275
|
};
|
|
226
|
-
const
|
|
227
|
-
if (!
|
|
228
|
-
return
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
276
|
+
const readFilterState = (filter) => {
|
|
277
|
+
if (!filter)
|
|
278
|
+
return { combinator: 'and', conditions: [] };
|
|
279
|
+
const logicalEntry = ['$and', '$or'].find((key) => Array.isArray(filter[key]));
|
|
280
|
+
if (logicalEntry) {
|
|
281
|
+
const conditions = filter[logicalEntry].flatMap((item) => isRecord(item)
|
|
282
|
+
? Object.entries(item)
|
|
283
|
+
.filter(([field]) => !field.startsWith('$'))
|
|
284
|
+
.map(([field, value]) => filterConditionFromEntry(field, value))
|
|
285
|
+
: []);
|
|
286
|
+
return {
|
|
287
|
+
combinator: logicalEntry === '$or' ? 'or' : 'and',
|
|
288
|
+
conditions,
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
return {
|
|
292
|
+
combinator: 'and',
|
|
293
|
+
conditions: Object.entries(filter)
|
|
294
|
+
.filter(([field]) => !field.startsWith('$'))
|
|
295
|
+
.map(([field, value]) => filterConditionFromEntry(field, value)),
|
|
296
|
+
};
|
|
297
|
+
};
|
|
298
|
+
exports.readFilterState = readFilterState;
|
|
299
|
+
const operatorNeedsValue = (operator) => !['true', 'false', 'exists', 'notExists'].includes(operator);
|
|
300
|
+
const parseFilterValue = (condition, fieldOptions) => {
|
|
301
|
+
const kind = fieldOptions.find((option) => option.value === condition.field)?.kind;
|
|
302
|
+
const parseValue = (value) => kind === 'number' && value.trim() !== '' && Number.isFinite(Number(value))
|
|
303
|
+
? Number(value)
|
|
304
|
+
: value.trim();
|
|
305
|
+
if (condition.operator === 'in' || condition.operator === 'notIn') {
|
|
306
|
+
return condition.value
|
|
307
|
+
.split(',')
|
|
308
|
+
.map((value) => value.trim())
|
|
309
|
+
.filter(Boolean)
|
|
310
|
+
.map(parseValue);
|
|
311
|
+
}
|
|
312
|
+
return parseValue(condition.value);
|
|
313
|
+
};
|
|
314
|
+
const buildFilterCondition = (condition, fieldOptions) => {
|
|
315
|
+
if (!condition.field)
|
|
234
316
|
return undefined;
|
|
235
|
-
if (
|
|
236
|
-
return { [
|
|
317
|
+
if (condition.operator === 'true')
|
|
318
|
+
return { [condition.field]: true };
|
|
319
|
+
if (condition.operator === 'false')
|
|
320
|
+
return { [condition.field]: false };
|
|
321
|
+
if (condition.operator === 'exists') {
|
|
322
|
+
return { [condition.field]: { $exists: true } };
|
|
237
323
|
}
|
|
238
|
-
|
|
324
|
+
if (condition.operator === 'notExists') {
|
|
325
|
+
return { [condition.field]: { $exists: false } };
|
|
326
|
+
}
|
|
327
|
+
if (!condition.value.trim())
|
|
328
|
+
return undefined;
|
|
329
|
+
const value = condition.valueSource === 'current'
|
|
330
|
+
? { [client_1.DYNAMIC_QUERY_CURRENT_VALUE_KEY]: condition.value }
|
|
331
|
+
: parseFilterValue(condition, fieldOptions);
|
|
332
|
+
const mongoOperator = {
|
|
333
|
+
notEquals: '$ne',
|
|
334
|
+
contains: '$contains',
|
|
335
|
+
in: '$in',
|
|
336
|
+
notIn: '$nin',
|
|
337
|
+
greaterThan: '$gt',
|
|
338
|
+
greaterThanOrEqual: '$gte',
|
|
339
|
+
lessThan: '$lt',
|
|
340
|
+
lessThanOrEqual: '$lte',
|
|
341
|
+
};
|
|
342
|
+
const operator = mongoOperator[condition.operator];
|
|
343
|
+
return operator
|
|
344
|
+
? { [condition.field]: { [operator]: value } }
|
|
345
|
+
: { [condition.field]: value };
|
|
346
|
+
};
|
|
347
|
+
const buildFilter = (state, fieldOptions = []) => {
|
|
348
|
+
const conditions = state.conditions.flatMap((condition) => {
|
|
349
|
+
const builtCondition = buildFilterCondition(condition, fieldOptions);
|
|
350
|
+
return builtCondition ? [builtCondition] : [];
|
|
351
|
+
});
|
|
352
|
+
if (conditions.length === 0)
|
|
353
|
+
return undefined;
|
|
354
|
+
if (conditions.length === 1)
|
|
355
|
+
return conditions[0];
|
|
356
|
+
return { [state.combinator === 'or' ? '$or' : '$and']: conditions };
|
|
239
357
|
};
|
|
358
|
+
exports.buildFilter = buildFilter;
|
|
240
359
|
const filterSummary = (filter) => {
|
|
241
|
-
const state = readFilterState(filter);
|
|
242
|
-
if (
|
|
360
|
+
const state = (0, exports.readFilterState)(filter);
|
|
361
|
+
if (state.conditions.length === 0)
|
|
243
362
|
return '';
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
363
|
+
const first = state.conditions[0];
|
|
364
|
+
const operatorLabels = {
|
|
365
|
+
equals: '=',
|
|
366
|
+
notEquals: '!=',
|
|
367
|
+
contains: 'contains',
|
|
368
|
+
in: 'in',
|
|
369
|
+
notIn: 'not in',
|
|
370
|
+
greaterThan: '>',
|
|
371
|
+
greaterThanOrEqual: '>=',
|
|
372
|
+
lessThan: '<',
|
|
373
|
+
lessThanOrEqual: '<=',
|
|
374
|
+
true: '= true',
|
|
375
|
+
false: '= false',
|
|
376
|
+
exists: 'is set',
|
|
377
|
+
notExists: 'is not set',
|
|
378
|
+
};
|
|
379
|
+
const firstSummary = [
|
|
380
|
+
first.field,
|
|
381
|
+
operatorLabels[first.operator],
|
|
382
|
+
operatorNeedsValue(first.operator)
|
|
383
|
+
? first.valueSource === 'current'
|
|
384
|
+
? `Current document.${first.value}`
|
|
385
|
+
: first.value
|
|
386
|
+
: '',
|
|
387
|
+
]
|
|
388
|
+
.filter(Boolean)
|
|
389
|
+
.join(' ');
|
|
390
|
+
const remaining = state.conditions.length - 1;
|
|
391
|
+
return remaining > 0
|
|
392
|
+
? `${firstSummary} + ${remaining} ${remaining === 1 ? 'condition' : 'conditions'}`
|
|
393
|
+
: firstSummary;
|
|
251
394
|
};
|
|
252
395
|
const bindingSummary = ({ list, fieldBinding, listBinding, documentContentTypeName, }) => {
|
|
253
396
|
if (list) {
|
|
@@ -382,6 +525,57 @@ const getSortFieldOptions = (contentType) => Object.entries(contentType.fields).
|
|
|
382
525
|
isSingleSelect;
|
|
383
526
|
return isSortable ? [{ label: fieldLabel(name), value: name, kind }] : [];
|
|
384
527
|
});
|
|
528
|
+
const filterOperatorOptions = {
|
|
529
|
+
equals: { label: 'Equals', value: 'equals' },
|
|
530
|
+
notEquals: { label: 'Does not equal', value: 'notEquals' },
|
|
531
|
+
contains: { label: 'Contains', value: 'contains' },
|
|
532
|
+
in: { label: 'Is one of', value: 'in' },
|
|
533
|
+
notIn: { label: 'Is not one of', value: 'notIn' },
|
|
534
|
+
greaterThan: { label: 'Greater than', value: 'greaterThan' },
|
|
535
|
+
greaterThanOrEqual: {
|
|
536
|
+
label: 'Greater than or equal',
|
|
537
|
+
value: 'greaterThanOrEqual',
|
|
538
|
+
},
|
|
539
|
+
lessThan: { label: 'Less than', value: 'lessThan' },
|
|
540
|
+
lessThanOrEqual: {
|
|
541
|
+
label: 'Less than or equal',
|
|
542
|
+
value: 'lessThanOrEqual',
|
|
543
|
+
},
|
|
544
|
+
true: { label: 'Is true', value: 'true' },
|
|
545
|
+
false: { label: 'Is false', value: 'false' },
|
|
546
|
+
exists: { label: 'Is set', value: 'exists' },
|
|
547
|
+
notExists: { label: 'Is not set', value: 'notExists' },
|
|
548
|
+
};
|
|
549
|
+
const getFilterOperatorOptions = (kind) => {
|
|
550
|
+
const operators = kind === 'boolean'
|
|
551
|
+
? ['true', 'false', 'exists', 'notExists']
|
|
552
|
+
: kind === 'number' || kind === 'date'
|
|
553
|
+
? [
|
|
554
|
+
'equals',
|
|
555
|
+
'notEquals',
|
|
556
|
+
'greaterThan',
|
|
557
|
+
'greaterThanOrEqual',
|
|
558
|
+
'lessThan',
|
|
559
|
+
'lessThanOrEqual',
|
|
560
|
+
'in',
|
|
561
|
+
'notIn',
|
|
562
|
+
'exists',
|
|
563
|
+
'notExists',
|
|
564
|
+
]
|
|
565
|
+
: kind === 'string' || kind === 'richText'
|
|
566
|
+
? [
|
|
567
|
+
'equals',
|
|
568
|
+
'notEquals',
|
|
569
|
+
'contains',
|
|
570
|
+
'in',
|
|
571
|
+
'notIn',
|
|
572
|
+
'exists',
|
|
573
|
+
'notExists',
|
|
574
|
+
]
|
|
575
|
+
: ['equals', 'notEquals', 'exists', 'notExists'];
|
|
576
|
+
return operators.map((operator) => filterOperatorOptions[operator]);
|
|
577
|
+
};
|
|
578
|
+
const defaultFilterOperator = (kind) => (kind === 'boolean' ? 'true' : 'equals');
|
|
385
579
|
const createRelatedCollectionSource = ({ contentType, currentSource, targetField, }) => {
|
|
386
580
|
const relation = getRelatedRelationOptions(contentType, currentSource)[0];
|
|
387
581
|
const path = getRelatedPathOptions(contentType, targetField)[0]?.value;
|
|
@@ -462,10 +656,10 @@ const MappingSourceEditor = ({ contentTypes, currentSource, targetField, source,
|
|
|
462
656
|
})
|
|
463
657
|
: undefined, children: [(0, jsx_runtime_1.jsx)(select_1.SelectTrigger, { className: 'w-full', children: (0, jsx_runtime_1.jsx)(select_1.SelectValue, {}) }), (0, jsx_runtime_1.jsxs)(select_1.SelectContent, { children: [(0, jsx_runtime_1.jsx)(select_1.SelectItem, { value: 'asc', children: "Ascending" }), (0, jsx_runtime_1.jsx)(select_1.SelectItem, { value: 'desc', children: "Descending" })] })] })] })] })] }));
|
|
464
658
|
};
|
|
465
|
-
const ListBindingEditor = ({ contentTypes, field, binding, onChange, }) => {
|
|
659
|
+
const ListBindingEditor = ({ contentTypes, documentContentType, field, binding, onChange, }) => {
|
|
466
660
|
const [sourceType, setSourceType] = (0, react_1.useState)(binding?.contentType || '');
|
|
467
661
|
const [itemName, setItemName] = (0, react_1.useState)(binding?.itemName || field.fields[0]?.name || '');
|
|
468
|
-
const [filterState, setFilterState] = (0, react_1.useState)(readFilterState(binding?.query?.filter));
|
|
662
|
+
const [filterState, setFilterState] = (0, react_1.useState)((0, exports.readFilterState)(binding?.query?.filter));
|
|
469
663
|
const [openMappingField, setOpenMappingField] = (0, react_1.useState)(null);
|
|
470
664
|
const selectedSource = contentTypes.find((ct) => ct.name === sourceType);
|
|
471
665
|
const targetContentType = getListTargetContentType(field, itemName);
|
|
@@ -478,6 +672,15 @@ const ListBindingEditor = ({ contentTypes, field, binding, onChange, }) => {
|
|
|
478
672
|
const filterFieldOptions = selectedSource
|
|
479
673
|
? (0, exports.sourceFieldOptions)(selectedSource).filter((item) => item.value !== '$href')
|
|
480
674
|
: [];
|
|
675
|
+
const currentDocumentFieldOptions = [
|
|
676
|
+
{ label: '_id', value: '_id', kind: 'string' },
|
|
677
|
+
...(0, exports.sourceFieldOptions)(documentContentType).filter((item) => item.value !== '$href' &&
|
|
678
|
+
item.kind !== 'object' &&
|
|
679
|
+
item.kind !== 'array'),
|
|
680
|
+
];
|
|
681
|
+
const sortEntry = Object.entries(binding?.query?.options?.sort ?? {})[0];
|
|
682
|
+
const sortField = sortEntry?.[0] ?? '';
|
|
683
|
+
const sortDirection = sortEntry?.[1] ?? 'desc';
|
|
481
684
|
const emit = (patch) => {
|
|
482
685
|
const next = {
|
|
483
686
|
contentType: sourceType,
|
|
@@ -497,57 +700,124 @@ const ListBindingEditor = ({ contentTypes, field, binding, onChange, }) => {
|
|
|
497
700
|
emit({
|
|
498
701
|
query: {
|
|
499
702
|
...binding?.query,
|
|
500
|
-
filter: buildFilter(nextFilterState),
|
|
703
|
+
filter: (0, exports.buildFilter)(nextFilterState, filterFieldOptions),
|
|
501
704
|
},
|
|
502
705
|
});
|
|
503
706
|
};
|
|
707
|
+
const updateFilterCondition = (index, nextCondition) => {
|
|
708
|
+
const conditions = [...filterState.conditions];
|
|
709
|
+
conditions[index] = nextCondition;
|
|
710
|
+
updateFilter({ ...filterState, conditions });
|
|
711
|
+
};
|
|
504
712
|
return ((0, jsx_runtime_1.jsxs)("div", { className: 'grid gap-3', children: [(0, jsx_runtime_1.jsx)(PanelSection, { title: 'Collection', children: (0, jsx_runtime_1.jsxs)("div", { className: 'grid gap-3 md:grid-cols-2', children: [(0, jsx_runtime_1.jsxs)(label_1.Label, { className: 'grid gap-1.5', children: ["Source", (0, jsx_runtime_1.jsxs)(select_1.Select, { disabled: contentTypes.length === 0, value: sourceType, onValueChange: (value) => {
|
|
505
713
|
setSourceType(value);
|
|
506
|
-
|
|
714
|
+
const nextFilterState = {
|
|
715
|
+
combinator: 'and',
|
|
716
|
+
conditions: [],
|
|
717
|
+
};
|
|
718
|
+
setFilterState(nextFilterState);
|
|
719
|
+
emit({
|
|
720
|
+
contentType: value,
|
|
721
|
+
map: {},
|
|
722
|
+
query: {
|
|
723
|
+
options: {
|
|
724
|
+
limit: binding?.query?.options?.limit ?? 10,
|
|
725
|
+
},
|
|
726
|
+
},
|
|
727
|
+
});
|
|
507
728
|
}, children: [(0, jsx_runtime_1.jsx)(select_1.SelectTrigger, { className: 'w-full', children: (0, jsx_runtime_1.jsx)(select_1.SelectValue, { placeholder: contentTypes.length > 0 ? 'Content type' : 'No content types' }) }), (0, jsx_runtime_1.jsx)(select_1.SelectContent, { children: (0, jsx_runtime_1.jsx)(select_1.SelectGroup, { children: contentTypes.map((ct) => ((0, jsx_runtime_1.jsx)(select_1.SelectItem, { value: ct.name, children: ct.name }, ct.name))) }) })] })] }), (0, jsx_runtime_1.jsxs)(label_1.Label, { className: 'grid gap-1.5', children: ["Item", (0, jsx_runtime_1.jsxs)(select_1.Select, { disabled: !sourceType || field.fields.length === 0, value: itemName, onValueChange: (value) => {
|
|
508
729
|
setItemName(value);
|
|
509
730
|
emit({ itemName: value, map: {} });
|
|
510
|
-
}, children: [(0, jsx_runtime_1.jsx)(select_1.SelectTrigger, { className: 'w-full', children: (0, jsx_runtime_1.jsx)(select_1.SelectValue, { placeholder: field.fields.length > 0 ? 'Item type' : 'No item types' }) }), (0, jsx_runtime_1.jsx)(select_1.SelectContent, { children: (0, jsx_runtime_1.jsx)(select_1.SelectGroup, { children: field.fields.map((item) => ((0, jsx_runtime_1.jsx)(select_1.SelectItem, { value: item.name, children: item.name }, item.name))) }) })] })] })] }) }), (0, jsx_runtime_1.
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
731
|
+
}, children: [(0, jsx_runtime_1.jsx)(select_1.SelectTrigger, { className: 'w-full', children: (0, jsx_runtime_1.jsx)(select_1.SelectValue, { placeholder: field.fields.length > 0 ? 'Item type' : 'No item types' }) }), (0, jsx_runtime_1.jsx)(select_1.SelectContent, { children: (0, jsx_runtime_1.jsx)(select_1.SelectGroup, { children: field.fields.map((item) => ((0, jsx_runtime_1.jsx)(select_1.SelectItem, { value: item.name, children: item.name }, item.name))) }) })] })] })] }) }), (0, jsx_runtime_1.jsxs)(PanelSection, { title: 'Query', children: [(0, jsx_runtime_1.jsxs)("div", { className: 'grid grid-cols-[minmax(5rem,0.4fr)_minmax(0,1.2fr)_minmax(8rem,0.6fr)] items-end gap-3', children: [(0, jsx_runtime_1.jsxs)(label_1.Label, { className: 'grid gap-1.5', children: ["Limit", (0, jsx_runtime_1.jsx)(input_1.Input, { type: 'number', min: 1, max: 100, value: String(binding?.query?.options?.limit ?? 10), onChange: (event) => emit({
|
|
732
|
+
query: {
|
|
733
|
+
...binding?.query,
|
|
734
|
+
options: {
|
|
735
|
+
...binding?.query?.options,
|
|
736
|
+
limit: Math.min(100, Math.max(1, Number(event.target.value || 10))),
|
|
737
|
+
},
|
|
516
738
|
},
|
|
517
|
-
},
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
739
|
+
}) })] }), (0, jsx_runtime_1.jsxs)(label_1.Label, { className: 'grid min-w-0 gap-1.5', children: ["Sort by", (0, jsx_runtime_1.jsxs)(select_1.Select, { disabled: sortFieldOptions.length === 0, value: sortField || '__none__', onValueChange: (value) => emit({
|
|
740
|
+
query: {
|
|
741
|
+
...binding?.query,
|
|
742
|
+
options: {
|
|
743
|
+
...binding?.query?.options,
|
|
744
|
+
sort: value === '__none__'
|
|
745
|
+
? undefined
|
|
746
|
+
: { [value]: sortDirection },
|
|
747
|
+
},
|
|
524
748
|
},
|
|
525
|
-
},
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
749
|
+
}), children: [(0, jsx_runtime_1.jsx)(select_1.SelectTrigger, { className: 'w-full', children: (0, jsx_runtime_1.jsx)(select_1.SelectValue, { placeholder: sortFieldOptions.length > 0
|
|
750
|
+
? 'Sort by field'
|
|
751
|
+
: 'No sortable fields' }) }), (0, jsx_runtime_1.jsx)(select_1.SelectContent, { children: (0, jsx_runtime_1.jsxs)(select_1.SelectGroup, { children: [(0, jsx_runtime_1.jsx)(select_1.SelectItem, { value: '__none__', children: "No sort" }), sortFieldOptions.map((item) => ((0, jsx_runtime_1.jsx)(select_1.SelectItem, { value: item.value, children: item.label }, item.value)))] }) })] })] }), (0, jsx_runtime_1.jsxs)(label_1.Label, { className: 'grid gap-1.5', children: ["Direction", (0, jsx_runtime_1.jsxs)(select_1.Select, { disabled: !sortField, value: sortDirection, onValueChange: (direction) => sortField
|
|
752
|
+
? emit({
|
|
753
|
+
query: {
|
|
754
|
+
...binding?.query,
|
|
755
|
+
options: {
|
|
756
|
+
...binding?.query?.options,
|
|
757
|
+
sort: {
|
|
758
|
+
[sortField]: direction,
|
|
759
|
+
},
|
|
760
|
+
},
|
|
761
|
+
},
|
|
762
|
+
})
|
|
763
|
+
: undefined, children: [(0, jsx_runtime_1.jsx)(select_1.SelectTrigger, { className: 'w-full', children: (0, jsx_runtime_1.jsx)(select_1.SelectValue, {}) }), (0, jsx_runtime_1.jsxs)(select_1.SelectContent, { children: [(0, jsx_runtime_1.jsx)(select_1.SelectItem, { value: 'asc', children: "Ascending" }), (0, jsx_runtime_1.jsx)(select_1.SelectItem, { value: 'desc', children: "Descending" })] })] })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: 'grid gap-3 rounded-md border border-border bg-background/60 p-3', children: [(0, jsx_runtime_1.jsxs)("div", { className: 'flex flex-wrap items-center justify-between gap-2', children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { className: 'text-sm font-medium', children: "Conditions" }), (0, jsx_runtime_1.jsx)("div", { className: 'text-xs text-muted-foreground', children: "Filter the records used to build this list." })] }), (0, jsx_runtime_1.jsxs)("div", { className: 'flex items-center gap-2', children: [filterState.conditions.length > 1 ? ((0, jsx_runtime_1.jsxs)(select_1.Select, { value: filterState.combinator, onValueChange: (combinator) => updateFilter({
|
|
764
|
+
...filterState,
|
|
765
|
+
combinator: combinator,
|
|
766
|
+
}), children: [(0, jsx_runtime_1.jsx)(select_1.SelectTrigger, { className: 'h-8 w-36', children: (0, jsx_runtime_1.jsx)(select_1.SelectValue, {}) }), (0, jsx_runtime_1.jsxs)(select_1.SelectContent, { children: [(0, jsx_runtime_1.jsx)(select_1.SelectItem, { value: 'and', children: "Match all" }), (0, jsx_runtime_1.jsx)(select_1.SelectItem, { value: 'or', children: "Match any" })] })] })) : null, (0, jsx_runtime_1.jsxs)(button_1.Button, { type: 'button', size: 'sm', variant: 'outline', disabled: filterFieldOptions.length === 0 ||
|
|
767
|
+
filterState.conditions.length >= 25, onClick: () => updateFilter({
|
|
538
768
|
...filterState,
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
})
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
769
|
+
conditions: [
|
|
770
|
+
...filterState.conditions,
|
|
771
|
+
{ field: '', operator: 'equals', value: '' },
|
|
772
|
+
],
|
|
773
|
+
}), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { className: 'h-4 w-4' }), "Add condition"] })] })] }), filterState.conditions.length === 0 ? ((0, jsx_runtime_1.jsx)("div", { className: 'rounded-md border border-dashed border-border px-3 py-5 text-center text-sm text-muted-foreground', children: "No conditions. All records from this collection will match." })) : ((0, jsx_runtime_1.jsx)("div", { className: 'grid gap-2', children: filterState.conditions.map((condition, index) => {
|
|
774
|
+
const fieldOption = filterFieldOptions.find((option) => option.value === condition.field);
|
|
775
|
+
const operatorOptions = getFilterOperatorOptions(fieldOption?.kind);
|
|
776
|
+
const needsValue = operatorNeedsValue(condition.operator);
|
|
777
|
+
const currentValueOptions = currentDocumentFieldOptions.filter((option) => !fieldOption || option.kind === fieldOption.kind);
|
|
778
|
+
const allowsCurrentValue = condition.operator !== 'in' &&
|
|
779
|
+
condition.operator !== 'notIn' &&
|
|
780
|
+
currentValueOptions.length > 0;
|
|
781
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: 'grid items-center gap-2 rounded-md border border-border bg-background p-2 sm:grid-cols-[1.2fr_1fr_1.8fr_auto]', children: [(0, jsx_runtime_1.jsxs)(select_1.Select, { disabled: filterFieldOptions.length === 0, value: condition.field, onValueChange: (value) => {
|
|
782
|
+
const kind = filterFieldOptions.find((option) => option.value === value)?.kind;
|
|
783
|
+
updateFilterCondition(index, {
|
|
784
|
+
field: value,
|
|
785
|
+
operator: defaultFilterOperator(kind),
|
|
786
|
+
value: '',
|
|
787
|
+
});
|
|
788
|
+
}, children: [(0, jsx_runtime_1.jsx)(select_1.SelectTrigger, { className: 'w-full', children: (0, jsx_runtime_1.jsx)(select_1.SelectValue, { placeholder: 'Field' }) }), (0, jsx_runtime_1.jsx)(select_1.SelectContent, { children: (0, jsx_runtime_1.jsx)(select_1.SelectGroup, { children: filterFieldOptions.map((item) => ((0, jsx_runtime_1.jsx)(select_1.SelectItem, { value: item.value, children: item.label }, item.value))) }) })] }), (0, jsx_runtime_1.jsxs)(select_1.Select, { disabled: !condition.field, value: condition.operator, onValueChange: (operator) => updateFilterCondition(index, {
|
|
789
|
+
...condition,
|
|
790
|
+
operator: operator,
|
|
791
|
+
valueSource: operator === 'in' || operator === 'notIn'
|
|
792
|
+
? 'literal'
|
|
793
|
+
: condition.valueSource,
|
|
794
|
+
value: operatorNeedsValue(operator)
|
|
795
|
+
? operator === 'in' || operator === 'notIn'
|
|
796
|
+
? ''
|
|
797
|
+
: condition.value
|
|
798
|
+
: '',
|
|
799
|
+
}), children: [(0, jsx_runtime_1.jsx)(select_1.SelectTrigger, { className: 'w-full', children: (0, jsx_runtime_1.jsx)(select_1.SelectValue, { placeholder: 'Operator' }) }), (0, jsx_runtime_1.jsx)(select_1.SelectContent, { children: (0, jsx_runtime_1.jsx)(select_1.SelectGroup, { children: operatorOptions.map((option) => ((0, jsx_runtime_1.jsx)(select_1.SelectItem, { value: option.value, children: option.label }, option.value))) }) })] }), needsValue ? ((0, jsx_runtime_1.jsxs)("div", { className: 'grid grid-cols-[minmax(7rem,0.7fr)_minmax(0,1fr)] gap-2', children: [(0, jsx_runtime_1.jsxs)(select_1.Select, { value: condition.valueSource ?? 'literal', onValueChange: (valueSource) => updateFilterCondition(index, {
|
|
800
|
+
...condition,
|
|
801
|
+
valueSource: valueSource,
|
|
802
|
+
value: '',
|
|
803
|
+
}), children: [(0, jsx_runtime_1.jsx)(select_1.SelectTrigger, { className: 'w-full', "aria-label": 'Value source', children: (0, jsx_runtime_1.jsx)(select_1.SelectValue, {}) }), (0, jsx_runtime_1.jsxs)(select_1.SelectContent, { children: [(0, jsx_runtime_1.jsx)(select_1.SelectItem, { value: 'literal', children: "Fixed value" }), allowsCurrentValue ? ((0, jsx_runtime_1.jsx)(select_1.SelectItem, { value: 'current', children: "Current document" })) : null] })] }), condition.valueSource === 'current' ? ((0, jsx_runtime_1.jsxs)(select_1.Select, { disabled: !condition.field, value: condition.value, onValueChange: (value) => updateFilterCondition(index, {
|
|
804
|
+
...condition,
|
|
805
|
+
value,
|
|
806
|
+
}), children: [(0, jsx_runtime_1.jsx)(select_1.SelectTrigger, { className: 'w-full', children: (0, jsx_runtime_1.jsx)(select_1.SelectValue, { placeholder: 'Current field' }) }), (0, jsx_runtime_1.jsx)(select_1.SelectContent, { children: (0, jsx_runtime_1.jsx)(select_1.SelectGroup, { children: currentValueOptions.map((option) => ((0, jsx_runtime_1.jsx)(select_1.SelectItem, { value: option.value, children: option.label }, option.value))) }) })] })) : ((0, jsx_runtime_1.jsx)(input_1.Input, { type: fieldOption?.kind === 'number' &&
|
|
807
|
+
condition.operator !== 'in' &&
|
|
808
|
+
condition.operator !== 'notIn'
|
|
809
|
+
? 'number'
|
|
810
|
+
: 'text', value: condition.value, disabled: !condition.field, placeholder: condition.operator === 'in' ||
|
|
811
|
+
condition.operator === 'notIn'
|
|
812
|
+
? 'Comma-separated values'
|
|
813
|
+
: 'Value', onChange: (event) => updateFilterCondition(index, {
|
|
814
|
+
...condition,
|
|
815
|
+
value: event.target.value,
|
|
816
|
+
}) }))] })) : ((0, jsx_runtime_1.jsx)("div", { className: 'h-9 rounded-md border border-dashed border-border bg-muted/30' })), (0, jsx_runtime_1.jsxs)(button_1.Button, { type: 'button', size: 'icon', variant: 'ghost', className: 'size-9 text-muted-foreground hover:text-destructive', onClick: () => updateFilter({
|
|
547
817
|
...filterState,
|
|
548
|
-
|
|
549
|
-
})
|
|
550
|
-
|
|
818
|
+
conditions: filterState.conditions.filter((_, conditionIndex) => conditionIndex !== index),
|
|
819
|
+
}), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Trash2, { className: 'h-4 w-4' }), (0, jsx_runtime_1.jsx)("span", { className: 'sr-only', children: "Remove condition" })] })] }, `${index}-${condition.field}`));
|
|
820
|
+
}) }))] })] }), (0, jsx_runtime_1.jsx)(PanelSection, { title: 'Mapping', children: (0, jsx_runtime_1.jsx)("div", { className: 'grid gap-2', children: targetFields.map(([targetField, targetFieldConfig]) => {
|
|
551
821
|
const source = binding?.map?.[targetField];
|
|
552
822
|
const summary = mappingSourceSummary(source, selectedSource?.name ?? 'Source');
|
|
553
823
|
const isOpen = openMappingField === targetField;
|
|
@@ -646,7 +916,7 @@ const DynamicDataControl = ({ contentType, documentContentType, fieldName, field
|
|
|
646
916
|
setOpen(false);
|
|
647
917
|
};
|
|
648
918
|
const trigger = ((0, jsx_runtime_1.jsxs)("div", { className: 'flex min-w-0 items-center gap-1.5', children: [(0, jsx_runtime_1.jsxs)(tooltip_1.Tooltip, { children: [(0, jsx_runtime_1.jsx)(tooltip_1.TooltipTrigger, { asChild: true, children: (0, jsx_runtime_1.jsx)("button", { type: 'button', className: 'min-w-0 cursor-pointer rounded-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring', onClick: () => setOpen((value) => !value), children: bound && summary ? ((0, jsx_runtime_1.jsxs)(badge_1.Badge, { variant: 'secondary', className: 'max-w-80 min-w-0 gap-1.5 rounded-md px-2 py-1 hover:bg-secondary/80', children: [list ? ((0, jsx_runtime_1.jsx)(lucide_react_1.ListFilter, { className: 'h-3.5 w-3.5 shrink-0' })) : ((0, jsx_runtime_1.jsx)(lucide_react_1.Link2, { className: 'h-3.5 w-3.5 shrink-0' })), (0, jsx_runtime_1.jsx)("span", { className: 'truncate', children: summary })] })) : ((0, jsx_runtime_1.jsxs)(badge_1.Badge, { variant: 'outline', className: 'gap-1.5 rounded-md px-2 py-1 text-muted-foreground', children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Cable, { className: 'h-3.5 w-3.5' }), "Not linked"] })) }) }), (0, jsx_runtime_1.jsx)(tooltip_1.TooltipContent, { side: 'top', sideOffset: 6, children: triggerLabel })] }), bound ? ((0, jsx_runtime_1.jsxs)(tooltip_1.Tooltip, { children: [(0, jsx_runtime_1.jsx)(tooltip_1.TooltipTrigger, { asChild: true, children: (0, jsx_runtime_1.jsxs)(button_1.Button, { type: 'button', size: 'icon', variant: 'ghost', className: 'size-6', onClick: clearBinding, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.X, { className: 'h-4 w-4' }), (0, jsx_runtime_1.jsx)("span", { className: 'sr-only', children: "Clear dynamic data" })] }) }), (0, jsx_runtime_1.jsx)(tooltip_1.TooltipContent, { side: 'top', sideOffset: 6, children: "Clear dynamic data link" })] })) : null] }));
|
|
649
|
-
const editor = list ? ((0, jsx_runtime_1.jsx)(ListBindingEditor, { contentTypes: sourceContentTypes, field: field, binding: listBinding, onChange: updateListBinding })) : ((0, jsx_runtime_1.jsx)(FieldBindingEditor, { contentTypes: fieldSourceContentTypes, documentContentType: currentDocumentContentType, currentDocumentSourceEnabled: currentDocumentSourceEnabled, targetField: field, binding: fieldBinding, onChange: updateFieldBinding }));
|
|
919
|
+
const editor = list ? ((0, jsx_runtime_1.jsx)(ListBindingEditor, { contentTypes: sourceContentTypes, documentContentType: currentDocumentContentType, field: field, binding: listBinding, onChange: updateListBinding })) : ((0, jsx_runtime_1.jsx)(FieldBindingEditor, { contentTypes: fieldSourceContentTypes, documentContentType: currentDocumentContentType, currentDocumentSourceEnabled: currentDocumentSourceEnabled, targetField: field, binding: fieldBinding, onChange: updateFieldBinding }));
|
|
650
920
|
const panel = isOpen ? ((0, jsx_runtime_1.jsx)("div", { className: 'rounded-md border border-border bg-background p-4 shadow-sm', children: editor })) : null;
|
|
651
921
|
const dialog = ((0, jsx_runtime_1.jsx)(dialog_1.Dialog, { open: isOpen, onOpenChange: (value) => {
|
|
652
922
|
if (value) {
|