@open-mercato/ui 0.9.1-develop.7498.1.08d233a6cc → 0.9.1-develop.7513.1.ae2715553f
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/backend/CrudForm.js +56 -36
- package/dist/backend/CrudForm.js.map +2 -2
- package/dist/backend/DataTable.js +40 -14
- package/dist/backend/DataTable.js.map +2 -2
- package/dist/backend/columns/ColumnChooserPanel.js +4 -3
- package/dist/backend/columns/ColumnChooserPanel.js.map +2 -2
- package/dist/backend/crud/injectedFieldGroups.js +50 -0
- package/dist/backend/crud/injectedFieldGroups.js.map +7 -0
- package/dist/backend/filters/FilterFieldPicker.js +3 -2
- package/dist/backend/filters/FilterFieldPicker.js.map +2 -2
- package/dist/backend/utils/api.js +25 -4
- package/dist/backend/utils/api.js.map +2 -2
- package/dist/backend/utils/groupLabels.js +16 -0
- package/dist/backend/utils/groupLabels.js.map +7 -0
- package/dist/theme/QueryProvider.js +7 -1
- package/dist/theme/QueryProvider.js.map +2 -2
- package/package.json +3 -3
- package/src/backend/CrudForm.tsx +68 -45
- package/src/backend/DataTable.tsx +54 -14
- package/src/backend/__tests__/CrudForm.fieldInjection.test.tsx +85 -4
- package/src/backend/__tests__/DataTable.injectedColumnPlacement.test.tsx +146 -0
- package/src/backend/columns/ColumnChooserPanel.tsx +4 -3
- package/src/backend/columns/__tests__/ColumnChooserPanel.groupLabels.test.tsx +72 -0
- package/src/backend/crud/__tests__/injectedFieldGroups.test.ts +90 -0
- package/src/backend/crud/injectedFieldGroups.ts +75 -0
- package/src/backend/filters/FilterFieldPicker.tsx +3 -2
- package/src/backend/filters/__tests__/FilterFieldPicker.groupLabels.test.tsx +77 -0
- package/src/backend/utils/__tests__/api.test.ts +67 -0
- package/src/backend/utils/api.ts +33 -4
- package/src/backend/utils/groupLabels.ts +26 -0
- package/src/theme/QueryProvider.tsx +7 -1
- package/src/theme/__tests__/QueryProvider.i18n.test.tsx +40 -0
package/.turbo/turbo-build.log
CHANGED
package/dist/backend/CrudForm.js
CHANGED
|
@@ -98,7 +98,7 @@ import { CollapsibleGroup } from "./crud/CollapsibleGroup.js";
|
|
|
98
98
|
import { SortableGroupHandleProvider } from "./crud/SortableGroupHandle.js";
|
|
99
99
|
import { useGroupOrder } from "./crud/useGroupOrder.js";
|
|
100
100
|
import { InjectedField } from "./injection/InjectedField.js";
|
|
101
|
-
import {
|
|
101
|
+
import { placeInjectedFieldsInGroups } from "./crud/injectedFieldGroups.js";
|
|
102
102
|
import { evaluateInjectedVisibility } from "./injection/visibility-utils.js";
|
|
103
103
|
import { ComponentReplacementHandles } from "@open-mercato/shared/modules/widgets/component-registry";
|
|
104
104
|
import { crudFormExtensionSpotId, extensionSpotChildId } from "@open-mercato/shared/modules/widgets/extension-points";
|
|
@@ -173,6 +173,21 @@ function buildResolvedEntityIdsKey(entityId, entityIds) {
|
|
|
173
173
|
}
|
|
174
174
|
return list.join("\0");
|
|
175
175
|
}
|
|
176
|
+
function renderNoInjectedFieldWidget() {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
async function skipInjectedFieldDeleteEvent() {
|
|
180
|
+
}
|
|
181
|
+
const INJECTED_FIELD_DELETE_EVENT_DEFAULTS = {
|
|
182
|
+
onBeforeDelete: skipInjectedFieldDeleteEvent,
|
|
183
|
+
onDelete: skipInjectedFieldDeleteEvent,
|
|
184
|
+
onAfterDelete: skipInjectedFieldDeleteEvent
|
|
185
|
+
};
|
|
186
|
+
function injectionWidgetPriority(widget) {
|
|
187
|
+
const placementPriority = widget.placement?.priority;
|
|
188
|
+
if (typeof placementPriority === "number") return placementPriority;
|
|
189
|
+
return typeof widget.module.metadata.priority === "number" ? widget.module.metadata.priority : 0;
|
|
190
|
+
}
|
|
176
191
|
function mergeByKey(primary, legacy, keyOf) {
|
|
177
192
|
if (!legacy.length) return primary;
|
|
178
193
|
const seen = new Set(primary.map(keyOf));
|
|
@@ -754,7 +769,26 @@ function CrudForm({
|
|
|
754
769
|
() => mergeByKey(primaryFieldWidgets, legacyFieldWidgets, (w) => w.metadata.id),
|
|
755
770
|
[primaryFieldWidgets, legacyFieldWidgets]
|
|
756
771
|
);
|
|
757
|
-
const
|
|
772
|
+
const injectionEventWidgets = React.useMemo(() => {
|
|
773
|
+
const fieldEventWidgets = [];
|
|
774
|
+
for (const widget of injectedFieldWidgets) {
|
|
775
|
+
if (!("fields" in widget) || !widget.eventHandlers) continue;
|
|
776
|
+
fieldEventWidgets.push({
|
|
777
|
+
widgetId: widget.metadata.id,
|
|
778
|
+
module: {
|
|
779
|
+
metadata: widget.metadata,
|
|
780
|
+
eventHandlers: { ...INJECTED_FIELD_DELETE_EVENT_DEFAULTS, ...widget.eventHandlers },
|
|
781
|
+
Widget: renderNoInjectedFieldWidget
|
|
782
|
+
},
|
|
783
|
+
moduleId: widget.moduleId,
|
|
784
|
+
key: widget.key,
|
|
785
|
+
placement: widget.placement
|
|
786
|
+
});
|
|
787
|
+
}
|
|
788
|
+
if (!fieldEventWidgets.length) return injectionWidgets;
|
|
789
|
+
return mergeByKey(injectionWidgets, fieldEventWidgets, (widget) => widget.widgetId).map((widget, index) => ({ widget, index })).sort((left, right) => injectionWidgetPriority(right.widget) - injectionWidgetPriority(left.widget) || left.index - right.index).map((entry) => entry.widget);
|
|
790
|
+
}, [injectionWidgets, injectedFieldWidgets]);
|
|
791
|
+
const { triggerEvent: triggerInjectionEvent } = useInjectionSpotEvents(resolvedInjectionSpotId ?? "", injectionEventWidgets);
|
|
758
792
|
const extendedInjectionEventsEnabled = CRUDFORM_EXTENDED_EVENTS_ENABLED && Boolean(resolvedInjectionSpotId);
|
|
759
793
|
const widgetRequiredFieldIds = React.useMemo(() => {
|
|
760
794
|
const ids = /* @__PURE__ */ new Set();
|
|
@@ -1360,6 +1394,24 @@ function CrudForm({
|
|
|
1360
1394
|
}
|
|
1361
1395
|
return definitions;
|
|
1362
1396
|
}, [injectedFieldWidgets]);
|
|
1397
|
+
const injectedFieldPlacements = React.useMemo(() => {
|
|
1398
|
+
const placements = /* @__PURE__ */ new Map();
|
|
1399
|
+
for (const widget of injectedFieldWidgets) {
|
|
1400
|
+
if (!("fields" in widget)) continue;
|
|
1401
|
+
for (const field of widget.fields ?? []) {
|
|
1402
|
+
placements.set(field.id, widget.placement);
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
return placements;
|
|
1406
|
+
}, [injectedFieldWidgets]);
|
|
1407
|
+
const groupsWithInjectedFields = React.useMemo(() => {
|
|
1408
|
+
if (!groups || groups.length === 0 || injectedFieldDefinitions.length === 0) return groups;
|
|
1409
|
+
return placeInjectedFieldsInGroups(groups, injectedFieldDefinitions, injectedFieldPlacements, (definition) => {
|
|
1410
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1411
|
+
logger.warn("Injected field targets a group that does not exist; appended to the last plain-field group", { fieldId: definition.id, group: definition.group });
|
|
1412
|
+
}
|
|
1413
|
+
});
|
|
1414
|
+
}, [groups, injectedFieldDefinitions, injectedFieldPlacements]);
|
|
1363
1415
|
const injectedFieldContext = React.useMemo(() => {
|
|
1364
1416
|
const recordValues = values;
|
|
1365
1417
|
const organizationId = typeof recordValues.organizationId === "string" ? recordValues.organizationId : null;
|
|
@@ -1406,22 +1458,15 @@ function CrudForm({
|
|
|
1406
1458
|
}
|
|
1407
1459
|
return (group.fields ?? []).map((entry) => typeof entry === "string" ? entry : entry.id);
|
|
1408
1460
|
};
|
|
1409
|
-
const declaredGroups = groups ?? [];
|
|
1410
1461
|
const hidden = /* @__PURE__ */ new Set();
|
|
1411
1462
|
const visible = /* @__PURE__ */ new Set();
|
|
1412
|
-
for (const group of
|
|
1463
|
+
for (const group of groupsWithInjectedFields ?? []) {
|
|
1413
1464
|
const target = hiddenGroupIdSet.has(group.id) ? hidden : visible;
|
|
1414
1465
|
for (const fieldId of groupFieldIds(group)) target.add(fieldId);
|
|
1415
1466
|
}
|
|
1416
|
-
const declaredGroupIds = new Set(declaredGroups.map((group) => group.id));
|
|
1417
|
-
const fallbackGroupId = declaredGroups[declaredGroups.length - 1]?.id;
|
|
1418
|
-
for (const definition of injectedFieldDefinitions) {
|
|
1419
|
-
const targetGroupId = definition.group && declaredGroupIds.has(definition.group) ? definition.group : fallbackGroupId;
|
|
1420
|
-
if (targetGroupId && hiddenGroupIdSet.has(targetGroupId)) hidden.add(definition.id);
|
|
1421
|
-
}
|
|
1422
1467
|
for (const fieldId of visible) hidden.delete(fieldId);
|
|
1423
1468
|
return hidden;
|
|
1424
|
-
}, [cfFields,
|
|
1469
|
+
}, [cfFields, groupsWithInjectedFields, hiddenGroupIdSet, placedCustomFieldIds]);
|
|
1425
1470
|
const hiddenInjectedFieldIds = React.useMemo(() => {
|
|
1426
1471
|
const hidden = /* @__PURE__ */ new Set();
|
|
1427
1472
|
for (const definition of injectedFieldDefinitions) {
|
|
@@ -1628,31 +1673,6 @@ function CrudForm({
|
|
|
1628
1673
|
pairs.sort((a, b) => b.priority - a.priority);
|
|
1629
1674
|
return pairs.map((p) => p.group);
|
|
1630
1675
|
}, [injectionWidgets, injectionContext, pending, setValues, values]);
|
|
1631
|
-
const groupsWithInjectedFields = React.useMemo(() => {
|
|
1632
|
-
if (!groups || groups.length === 0 || injectedFieldDefinitions.length === 0) return groups;
|
|
1633
|
-
const cloned = groups.map((group) => ({ ...group, fields: [...group.fields ?? []] }));
|
|
1634
|
-
const fallbackIndex = cloned.length - 1;
|
|
1635
|
-
for (const definition of injectedFieldDefinitions) {
|
|
1636
|
-
const targetIndex = cloned.findIndex((group) => group.id === definition.group);
|
|
1637
|
-
const index = targetIndex >= 0 ? targetIndex : fallbackIndex;
|
|
1638
|
-
if (targetIndex < 0 && process.env.NODE_ENV !== "production") {
|
|
1639
|
-
logger.warn("Injected field targets a group that does not exist; appended to last group", { fieldId: definition.id, group: definition.group });
|
|
1640
|
-
}
|
|
1641
|
-
if (index < 0) continue;
|
|
1642
|
-
const fieldEntries = cloned[index].fields ?? [];
|
|
1643
|
-
const alreadyPresent = fieldEntries.some((entry) => {
|
|
1644
|
-
const entryId = typeof entry === "string" ? entry : entry.id;
|
|
1645
|
-
return entryId === definition.id;
|
|
1646
|
-
});
|
|
1647
|
-
cloned[index].fields = alreadyPresent ? fieldEntries : insertByInjectionPlacement(
|
|
1648
|
-
fieldEntries,
|
|
1649
|
-
definition.id,
|
|
1650
|
-
definition.placement,
|
|
1651
|
-
(entry) => typeof entry === "string" ? entry : entry.id
|
|
1652
|
-
);
|
|
1653
|
-
}
|
|
1654
|
-
return cloned;
|
|
1655
|
-
}, [groups, injectedFieldDefinitions]);
|
|
1656
1676
|
const shouldAutoGroup = (!groupsWithInjectedFields || groupsWithInjectedFields.length === 0) && injectionGroupCards.length > 0;
|
|
1657
1677
|
const declaredGroupsForLayout = React.useMemo(() => {
|
|
1658
1678
|
const baseGroups = groupsWithInjectedFields && groupsWithInjectedFields.length ? groupsWithInjectedFields : [];
|