@open-mercato/ui 0.4.6-develop-ce2a0728a5 → 0.4.6-develop-77fa3b7ed8
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/AGENTS.md +16 -0
- package/dist/backend/CrudForm.js +138 -17
- package/dist/backend/CrudForm.js.map +3 -3
- package/dist/backend/DataTable.js +297 -24
- package/dist/backend/DataTable.js.map +3 -3
- package/dist/backend/detail/ActivitiesSection.js +11 -1
- package/dist/backend/detail/ActivitiesSection.js.map +2 -2
- package/dist/backend/detail/AddressesSection.js +11 -1
- package/dist/backend/detail/AddressesSection.js.map +2 -2
- package/dist/backend/detail/AttachmentsSection.js +11 -1
- package/dist/backend/detail/AttachmentsSection.js.map +2 -2
- package/dist/backend/detail/CustomDataSection.js +11 -1
- package/dist/backend/detail/CustomDataSection.js.map +2 -2
- package/dist/backend/detail/DetailFieldsSection.js +11 -1
- package/dist/backend/detail/DetailFieldsSection.js.map +2 -2
- package/dist/backend/detail/NotesSection.js +11 -1
- package/dist/backend/detail/NotesSection.js.map +2 -2
- package/dist/backend/detail/TagsSection.js +11 -1
- package/dist/backend/detail/TagsSection.js.map +2 -2
- package/dist/backend/injection/ComponentOverrideProvider.js +54 -0
- package/dist/backend/injection/ComponentOverrideProvider.js.map +7 -0
- package/dist/backend/injection/InjectedField.js +166 -0
- package/dist/backend/injection/InjectedField.js.map +7 -0
- package/dist/backend/injection/spotIds.js +5 -1
- package/dist/backend/injection/spotIds.js.map +2 -2
- package/dist/backend/injection/useRegisteredComponent.js +89 -0
- package/dist/backend/injection/useRegisteredComponent.js.map +7 -0
- package/dist/backend/injection/visibility-utils.js +29 -0
- package/dist/backend/injection/visibility-utils.js.map +7 -0
- package/package.json +2 -2
- package/src/backend/AGENTS.md +7 -0
- package/src/backend/CrudForm.tsx +144 -16
- package/src/backend/DataTable.tsx +342 -22
- package/src/backend/__tests__/DataTable.extensions.test.tsx +115 -0
- package/src/backend/__tests__/DataTable.namespaces.test.ts +32 -0
- package/src/backend/__tests__/component-replacement.test.tsx +232 -0
- package/src/backend/detail/ActivitiesSection.tsx +17 -1
- package/src/backend/detail/AddressesSection.tsx +17 -1
- package/src/backend/detail/AttachmentsSection.tsx +17 -1
- package/src/backend/detail/CustomDataSection.tsx +17 -1
- package/src/backend/detail/DetailFieldsSection.tsx +17 -1
- package/src/backend/detail/NotesSection.tsx +17 -1
- package/src/backend/detail/TagsSection.tsx +17 -1
- package/src/backend/injection/ComponentOverrideProvider.tsx +65 -0
- package/src/backend/injection/InjectedField.tsx +194 -0
- package/src/backend/injection/spotIds.ts +4 -0
- package/src/backend/injection/useRegisteredComponent.tsx +106 -0
- package/src/backend/injection/visibility-utils.ts +31 -0
package/AGENTS.md
CHANGED
|
@@ -122,11 +122,20 @@ import { IconButton } from '@open-mercato/ui/primitives/icon-button'
|
|
|
122
122
|
## DataTable Guidelines
|
|
123
123
|
|
|
124
124
|
- Use `DataTable` as the default list view.
|
|
125
|
+
- DataTable extension spots include: `data-table:<tableId>:columns`, `:row-actions`, `:bulk-actions`, `:filters` (in addition to `:header`/`:footer`).
|
|
125
126
|
- Populate `columns` with explicit renderers and set `meta.truncate`/`meta.maxWidth` where truncation is needed.
|
|
126
127
|
- For filters, use `FilterBar`/`FilterOverlay` with async option loaders; keep `pageSize` at or below 100.
|
|
127
128
|
- Support exports using `buildCrudExportUrl` and pass `exportOptions` to `DataTable`.
|
|
128
129
|
- Use `RowActions` for per-row actions and include navigation via `onRowClick` or action links.
|
|
129
130
|
- Keep table state (paging, sorting, filters, search) in component state and reload on scope changes.
|
|
131
|
+
- Keep `extensionTableId` stable and deterministic; host pages should not derive it from transient UI state.
|
|
132
|
+
- Render injected row actions and bulk actions through `RowActions`/bulk action handlers so injected actions follow the same guard and i18n behavior as built-ins.
|
|
133
|
+
|
|
134
|
+
## CrudForm Field Injection (UMES Phase G)
|
|
135
|
+
|
|
136
|
+
- `CrudForm` automatically resolves injected field widgets from `crud-form:<entityId>:fields`; always pass a stable `entityId`.
|
|
137
|
+
- Keep host field/group IDs stable so injected fields can target groups deterministically across versions.
|
|
138
|
+
- Use injected fields for cross-module form augmentation; keep core module fields in the base form config.
|
|
130
139
|
|
|
131
140
|
## Menu Injection (UMES Phase A/B)
|
|
132
141
|
|
|
@@ -158,6 +167,13 @@ import { IconButton } from '@open-mercato/ui/primitives/icon-button'
|
|
|
158
167
|
## Component Reuse
|
|
159
168
|
|
|
160
169
|
- Prefer existing UI primitives and backend components from `@open-mercato/ui` before creating new ones.
|
|
170
|
+
- For replacement-aware hosts, expose stable handle IDs (`page:*`, `data-table:*`, `crud-form:*`, `section:*`) so overrides are deterministic.
|
|
161
171
|
- Reference @`.ai/specs/SPEC-001-2026-01-21-ui-reusable-components.md` for the reusable component catalog and usage patterns.
|
|
162
172
|
- For dialogs and forms, keep the interaction model consistent: `Cmd/Ctrl + Enter` to submit, `Escape` to cancel.
|
|
163
173
|
- Favor composable, data-first helpers (custom field helpers, CRUD helpers, filter utilities) over bespoke logic.
|
|
174
|
+
|
|
175
|
+
## Component Replacement (UMES Phase H)
|
|
176
|
+
|
|
177
|
+
- When a host surface is replacement-aware, resolve implementations via `useRegisteredComponent(handle, Fallback)` instead of hardcoded component references.
|
|
178
|
+
- Prefer additive override modes (`wrapper`, `props`) before full `replace`; reserve `replace` for cases where compatibility is preserved.
|
|
179
|
+
- Keep handle IDs stable and document them when introducing new replacement-aware surfaces.
|
package/dist/backend/CrudForm.js
CHANGED
|
@@ -65,12 +65,27 @@ import { useInjectionSpotEvents, InjectionSpot, useInjectionWidgets } from "./in
|
|
|
65
65
|
import { dispatchBackendMutationError } from "./injection/mutationEvents.js";
|
|
66
66
|
import { VersionHistoryAction } from "./version-history/VersionHistoryAction.js";
|
|
67
67
|
import { parseBooleanWithDefault } from "@open-mercato/shared/lib/boolean";
|
|
68
|
+
import { useInjectionDataWidgets } from "./injection/useInjectionDataWidgets.js";
|
|
69
|
+
import { InjectedField } from "./injection/InjectedField.js";
|
|
70
|
+
import { evaluateInjectedVisibility } from "./injection/visibility-utils.js";
|
|
71
|
+
import { ComponentReplacementHandles } from "@open-mercato/shared/modules/widgets/component-registry";
|
|
68
72
|
const EMPTY_OPTIONS = [];
|
|
69
73
|
const FOCUSABLE_SELECTOR = '[data-crud-focus-target], input:not([type="hidden"]):not([disabled]), textarea:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
70
74
|
const CRUDFORM_EXTENDED_EVENTS_ENABLED = parseBooleanWithDefault(
|
|
71
75
|
process.env.NEXT_PUBLIC_OM_CRUDFORM_EXTENDED_EVENTS_ENABLED,
|
|
72
76
|
true
|
|
73
77
|
);
|
|
78
|
+
function readByDotPath(source, path) {
|
|
79
|
+
if (!source || !path) return void 0;
|
|
80
|
+
if (Object.prototype.hasOwnProperty.call(source, path)) return source[path];
|
|
81
|
+
const segments = path.split(".").filter(Boolean);
|
|
82
|
+
let current = source;
|
|
83
|
+
for (const segment of segments) {
|
|
84
|
+
if (!current || typeof current !== "object" || Array.isArray(current)) return void 0;
|
|
85
|
+
current = current[segment];
|
|
86
|
+
}
|
|
87
|
+
return current;
|
|
88
|
+
}
|
|
74
89
|
const FIELDSET_ICON_COMPONENTS = {
|
|
75
90
|
layers: Layers,
|
|
76
91
|
tag: Tag,
|
|
@@ -135,7 +150,8 @@ function CrudForm({
|
|
|
135
150
|
versionHistory,
|
|
136
151
|
contentHeader,
|
|
137
152
|
customFieldsetBindings,
|
|
138
|
-
injectionSpotId
|
|
153
|
+
injectionSpotId,
|
|
154
|
+
replacementHandle
|
|
139
155
|
}) {
|
|
140
156
|
React.useEffect(() => {
|
|
141
157
|
loadGeneratedFieldRegistrations().catch(() => {
|
|
@@ -207,6 +223,11 @@ function CrudForm({
|
|
|
207
223
|
}
|
|
208
224
|
return void 0;
|
|
209
225
|
}, [injectionSpotId, resolvedEntityIds]);
|
|
226
|
+
const resolvedReplacementHandle = React.useMemo(() => {
|
|
227
|
+
if (replacementHandle) return replacementHandle;
|
|
228
|
+
if (resolvedEntityIds.length) return ComponentReplacementHandles.crudForm(resolvedEntityIds[0].replace(/[:]+/g, "."));
|
|
229
|
+
return ComponentReplacementHandles.crudForm("unknown");
|
|
230
|
+
}, [replacementHandle, resolvedEntityIds]);
|
|
210
231
|
const headerInjectionSpotId = resolvedInjectionSpotId ? `${resolvedInjectionSpotId}:header` : void 0;
|
|
211
232
|
const recordId = React.useMemo(() => {
|
|
212
233
|
const raw = values.id;
|
|
@@ -235,6 +256,9 @@ function CrudForm({
|
|
|
235
256
|
context: injectionContext,
|
|
236
257
|
triggerOnLoad: true
|
|
237
258
|
});
|
|
259
|
+
const { widgets: injectedFieldWidgets } = useInjectionDataWidgets(
|
|
260
|
+
resolvedInjectionSpotId ? `${resolvedInjectionSpotId}:fields` : "__disabled__:fields"
|
|
261
|
+
);
|
|
238
262
|
const { triggerEvent: triggerInjectionEvent } = useInjectionSpotEvents(resolvedInjectionSpotId ?? "", injectionWidgets);
|
|
239
263
|
const extendedInjectionEventsEnabled = CRUDFORM_EXTENDED_EVENTS_ENABLED && Boolean(resolvedInjectionSpotId);
|
|
240
264
|
const transformValidationErrors = React.useCallback(
|
|
@@ -745,12 +769,66 @@ function CrudForm({
|
|
|
745
769
|
fieldsetsByEntity,
|
|
746
770
|
resolvedEntityIds
|
|
747
771
|
]);
|
|
772
|
+
const injectedFieldDefinitions = React.useMemo(() => {
|
|
773
|
+
const definitions = [];
|
|
774
|
+
for (const widget of injectedFieldWidgets) {
|
|
775
|
+
if (!("fields" in widget)) continue;
|
|
776
|
+
for (const field of widget.fields ?? []) {
|
|
777
|
+
definitions.push(field);
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
return definitions;
|
|
781
|
+
}, [injectedFieldWidgets]);
|
|
782
|
+
const injectedFieldContext = React.useMemo(() => {
|
|
783
|
+
const recordValues = values;
|
|
784
|
+
const organizationId = typeof recordValues.organizationId === "string" ? recordValues.organizationId : null;
|
|
785
|
+
const tenantId = typeof recordValues.tenantId === "string" ? recordValues.tenantId : null;
|
|
786
|
+
const userId = typeof recordValues.userId === "string" ? recordValues.userId : null;
|
|
787
|
+
return {
|
|
788
|
+
organizationId,
|
|
789
|
+
tenantId,
|
|
790
|
+
userId,
|
|
791
|
+
record: recordValues
|
|
792
|
+
};
|
|
793
|
+
}, [values]);
|
|
794
|
+
const hiddenInjectedFieldIds = React.useMemo(() => {
|
|
795
|
+
const hidden = /* @__PURE__ */ new Set();
|
|
796
|
+
for (const definition of injectedFieldDefinitions) {
|
|
797
|
+
if (!evaluateInjectedVisibility(definition.visibleWhen, values, injectedFieldContext)) {
|
|
798
|
+
hidden.add(definition.id);
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
return hidden;
|
|
802
|
+
}, [injectedFieldContext, injectedFieldDefinitions, values]);
|
|
803
|
+
const injectedFieldIdSet = React.useMemo(
|
|
804
|
+
() => new Set(injectedFieldDefinitions.map((definition) => definition.id)),
|
|
805
|
+
[injectedFieldDefinitions]
|
|
806
|
+
);
|
|
807
|
+
const injectedCrudFields = React.useMemo(() => {
|
|
808
|
+
return injectedFieldDefinitions.map((definition) => ({
|
|
809
|
+
id: definition.id,
|
|
810
|
+
label: definition.label,
|
|
811
|
+
type: "custom",
|
|
812
|
+
readOnly: definition.readOnly,
|
|
813
|
+
component: ({ value, setValue: setValue2, values: formValues }) => /* @__PURE__ */ jsx(
|
|
814
|
+
InjectedField,
|
|
815
|
+
{
|
|
816
|
+
field: definition,
|
|
817
|
+
value,
|
|
818
|
+
onChange: (_, nextValue) => setValue2(nextValue),
|
|
819
|
+
context: injectedFieldContext,
|
|
820
|
+
formData: formValues ?? values
|
|
821
|
+
}
|
|
822
|
+
)
|
|
823
|
+
}));
|
|
824
|
+
}, [injectedFieldContext, injectedFieldDefinitions, values]);
|
|
748
825
|
const allFields = React.useMemo(() => {
|
|
749
|
-
|
|
750
|
-
|
|
826
|
+
const base = [...fields, ...injectedCrudFields];
|
|
827
|
+
if (!cfFields.length) return base;
|
|
828
|
+
const provided = new Set(base.map((f) => f.id));
|
|
751
829
|
const extras = cfFields.filter((f) => !provided.has(f.id));
|
|
752
|
-
return [...
|
|
753
|
-
}, [fields, cfFields]);
|
|
830
|
+
return [...base, ...extras];
|
|
831
|
+
}, [fields, injectedCrudFields, cfFields]);
|
|
754
832
|
const fieldById = React.useMemo(() => {
|
|
755
833
|
return new globalThis.Map(allFields.map((f) => [f.id, f]));
|
|
756
834
|
}, [allFields]);
|
|
@@ -778,12 +856,31 @@ function CrudForm({
|
|
|
778
856
|
pairs.sort((a, b) => b.priority - a.priority);
|
|
779
857
|
return pairs.map((p) => p.group);
|
|
780
858
|
}, [injectionWidgets, injectionContext, pending, setValues, values]);
|
|
781
|
-
const
|
|
859
|
+
const groupsWithInjectedFields = React.useMemo(() => {
|
|
860
|
+
if (!groups || groups.length === 0 || injectedFieldDefinitions.length === 0) return groups;
|
|
861
|
+
const cloned = groups.map((group) => ({ ...group, fields: [...group.fields ?? []] }));
|
|
862
|
+
const fallbackIndex = cloned.length - 1;
|
|
863
|
+
for (const definition of injectedFieldDefinitions) {
|
|
864
|
+
const targetIndex = cloned.findIndex((group) => group.id === definition.group);
|
|
865
|
+
const index = targetIndex >= 0 ? targetIndex : fallbackIndex;
|
|
866
|
+
if (targetIndex < 0 && process.env.NODE_ENV !== "production") {
|
|
867
|
+
console.warn(`[CrudForm] Injected field "${definition.id}" targets group "${definition.group}" which does not exist. Appended to last group.`);
|
|
868
|
+
}
|
|
869
|
+
if (index < 0) continue;
|
|
870
|
+
const fieldEntries = cloned[index].fields ?? [];
|
|
871
|
+
if (!fieldEntries.some((entry) => typeof entry === "string" && entry === definition.id)) {
|
|
872
|
+
fieldEntries.push(definition.id);
|
|
873
|
+
}
|
|
874
|
+
cloned[index].fields = fieldEntries;
|
|
875
|
+
}
|
|
876
|
+
return cloned;
|
|
877
|
+
}, [groups, injectedFieldDefinitions]);
|
|
878
|
+
const shouldAutoGroup = (!groupsWithInjectedFields || groupsWithInjectedFields.length === 0) && injectionGroupCards.length > 0;
|
|
782
879
|
const resolvedGroupsForLayout = React.useMemo(() => {
|
|
783
|
-
const baseGroups =
|
|
880
|
+
const baseGroups = groupsWithInjectedFields && groupsWithInjectedFields.length ? groupsWithInjectedFields : [];
|
|
784
881
|
const autoGroup = shouldAutoGroup ? [{ id: "__auto-fields__", fields: allFields }] : [];
|
|
785
882
|
return [...baseGroups.length ? baseGroups : autoGroup, ...injectionGroupCards];
|
|
786
|
-
}, [allFields,
|
|
883
|
+
}, [allFields, groupsWithInjectedFields, injectionGroupCards, shouldAutoGroup]);
|
|
787
884
|
const useGroupedLayout = resolvedGroupsForLayout.length > 0;
|
|
788
885
|
const stackedInjectionWidgets = React.useMemo(
|
|
789
886
|
() => (injectionWidgets ?? []).filter((widget) => (widget.placement?.kind ?? "stack") === "stack"),
|
|
@@ -977,7 +1074,16 @@ function CrudForm({
|
|
|
977
1074
|
initialValuesSnapshotRef.current = snapshot;
|
|
978
1075
|
let mergedValues = null;
|
|
979
1076
|
setValues((prev) => {
|
|
980
|
-
|
|
1077
|
+
const merged = { ...prev, ...initialValues };
|
|
1078
|
+
for (const definition of injectedFieldDefinitions) {
|
|
1079
|
+
if (merged[definition.id] !== void 0) continue;
|
|
1080
|
+
const extracted = readByDotPath(initialValues, definition.id);
|
|
1081
|
+
if (extracted !== void 0) {
|
|
1082
|
+
;
|
|
1083
|
+
merged[definition.id] = extracted;
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
mergedValues = merged;
|
|
981
1087
|
return mergedValues;
|
|
982
1088
|
});
|
|
983
1089
|
if (!extendedInjectionEventsEnabled || !mergedValues) return;
|
|
@@ -1000,7 +1106,7 @@ function CrudForm({
|
|
|
1000
1106
|
return () => {
|
|
1001
1107
|
cancelled = true;
|
|
1002
1108
|
};
|
|
1003
|
-
}, [extendedInjectionEventsEnabled, initialValues, triggerInjectionEvent]);
|
|
1109
|
+
}, [extendedInjectionEventsEnabled, initialValues, injectedFieldDefinitions, triggerInjectionEvent]);
|
|
1004
1110
|
const buildFieldsetEditorHref = React.useCallback(
|
|
1005
1111
|
(includeViewParam) => {
|
|
1006
1112
|
if (!fieldsetEditorTarget) return null;
|
|
@@ -1044,6 +1150,7 @@ function CrudForm({
|
|
|
1044
1150
|
for (const field of allFields) {
|
|
1045
1151
|
if (!field.required) continue;
|
|
1046
1152
|
if (field.disabled) continue;
|
|
1153
|
+
if (hiddenInjectedFieldIds.has(field.id)) continue;
|
|
1047
1154
|
const v = values[field.id];
|
|
1048
1155
|
const isArray = Array.isArray(v);
|
|
1049
1156
|
const isString = typeof v === "string";
|
|
@@ -1098,9 +1205,17 @@ function CrudForm({
|
|
|
1098
1205
|
} catch {
|
|
1099
1206
|
}
|
|
1100
1207
|
}
|
|
1208
|
+
const widgetValues = { ...values };
|
|
1209
|
+
for (const hiddenId of hiddenInjectedFieldIds) {
|
|
1210
|
+
delete widgetValues[hiddenId];
|
|
1211
|
+
}
|
|
1212
|
+
const coreValues = { ...widgetValues };
|
|
1213
|
+
for (const injectedId of injectedFieldIdSet) {
|
|
1214
|
+
delete coreValues[injectedId];
|
|
1215
|
+
}
|
|
1101
1216
|
let parsedValues;
|
|
1102
1217
|
if (schema) {
|
|
1103
|
-
const res = schema.safeParse(
|
|
1218
|
+
const res = schema.safeParse(coreValues);
|
|
1104
1219
|
if (!res.success) {
|
|
1105
1220
|
const fieldErrors = {};
|
|
1106
1221
|
res.error.issues.forEach((issue) => {
|
|
@@ -1116,14 +1231,20 @@ function CrudForm({
|
|
|
1116
1231
|
}
|
|
1117
1232
|
parsedValues = res.data;
|
|
1118
1233
|
} else {
|
|
1119
|
-
parsedValues =
|
|
1234
|
+
parsedValues = coreValues;
|
|
1120
1235
|
}
|
|
1121
|
-
let submitValues =
|
|
1236
|
+
let submitValues = widgetValues;
|
|
1237
|
+
let coreSubmitValues = parsedValues;
|
|
1122
1238
|
if (extendedInjectionEventsEnabled) {
|
|
1123
1239
|
try {
|
|
1124
1240
|
const result = await triggerInjectionEvent("transformFormData", submitValues, injectionContext);
|
|
1125
1241
|
if (result.data) {
|
|
1126
1242
|
submitValues = result.data;
|
|
1243
|
+
const projectedCoreValues = { ...result.data };
|
|
1244
|
+
for (const injectedId of injectedFieldIdSet) {
|
|
1245
|
+
delete projectedCoreValues[injectedId];
|
|
1246
|
+
}
|
|
1247
|
+
coreSubmitValues = schema ? schema.parse(projectedCoreValues) : projectedCoreValues;
|
|
1127
1248
|
}
|
|
1128
1249
|
} catch (err) {
|
|
1129
1250
|
console.error("[CrudForm] Error in transformFormData:", err);
|
|
@@ -1181,10 +1302,10 @@ function CrudForm({
|
|
|
1181
1302
|
try {
|
|
1182
1303
|
if (injectionRequestHeaders && Object.keys(injectionRequestHeaders).length > 0) {
|
|
1183
1304
|
await withScopedApiRequestHeaders(injectionRequestHeaders, async () => {
|
|
1184
|
-
await onSubmit?.(
|
|
1305
|
+
await onSubmit?.(coreSubmitValues);
|
|
1185
1306
|
});
|
|
1186
1307
|
} else {
|
|
1187
|
-
await onSubmit?.(
|
|
1308
|
+
await onSubmit?.(coreSubmitValues);
|
|
1188
1309
|
}
|
|
1189
1310
|
if (resolvedInjectionSpotId) {
|
|
1190
1311
|
try {
|
|
@@ -1573,7 +1694,7 @@ function CrudForm({
|
|
|
1573
1694
|
const col1Content = renderGroupedCards(col1);
|
|
1574
1695
|
const col2Content = renderGroupedCards(col2);
|
|
1575
1696
|
const hasSecondaryColumn = col2Content.length > 0;
|
|
1576
|
-
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", ref: rootRef, children: [
|
|
1697
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", ref: rootRef, "data-component-handle": resolvedReplacementHandle, children: [
|
|
1577
1698
|
!embedded ? /* @__PURE__ */ jsx(
|
|
1578
1699
|
FormHeader,
|
|
1579
1700
|
{
|
|
@@ -1646,7 +1767,7 @@ function CrudForm({
|
|
|
1646
1767
|
ConfirmDialogElement
|
|
1647
1768
|
] });
|
|
1648
1769
|
}
|
|
1649
|
-
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", ref: rootRef, children: [
|
|
1770
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", ref: rootRef, "data-component-handle": resolvedReplacementHandle, children: [
|
|
1650
1771
|
!embedded ? /* @__PURE__ */ jsx(
|
|
1651
1772
|
FormHeader,
|
|
1652
1773
|
{
|