form-builder-pro 1.3.7 → 1.3.9
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/dist/index.css +49 -9
- package/dist/index.d.mts +52 -4
- package/dist/index.d.ts +52 -4
- package/dist/index.js +1002 -448
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1002 -449
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4076,39 +4076,12 @@ var FormSchemaValidation = external_exports.object({
|
|
|
4076
4076
|
}))
|
|
4077
4077
|
});
|
|
4078
4078
|
|
|
4079
|
-
// node_modules/.pnpm/zustand@4.5.7_react@19.2.4/node_modules/zustand/esm/vanilla.mjs
|
|
4080
|
-
var createStoreImpl = (createState) => {
|
|
4081
|
-
let state;
|
|
4082
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
4083
|
-
const setState = (partial, replace) => {
|
|
4084
|
-
const nextState = typeof partial === "function" ? partial(state) : partial;
|
|
4085
|
-
if (!Object.is(nextState, state)) {
|
|
4086
|
-
const previousState = state;
|
|
4087
|
-
state = (replace != null ? replace : typeof nextState !== "object" || nextState === null) ? nextState : Object.assign({}, state, nextState);
|
|
4088
|
-
listeners.forEach((listener) => listener(state, previousState));
|
|
4089
|
-
}
|
|
4090
|
-
};
|
|
4091
|
-
const getState = () => state;
|
|
4092
|
-
const getInitialState = () => initialState;
|
|
4093
|
-
const subscribe = (listener) => {
|
|
4094
|
-
listeners.add(listener);
|
|
4095
|
-
return () => listeners.delete(listener);
|
|
4096
|
-
};
|
|
4097
|
-
const destroy2 = () => {
|
|
4098
|
-
if ((undefined ? undefined.MODE : void 0) !== "production") {
|
|
4099
|
-
console.warn(
|
|
4100
|
-
"[DEPRECATED] The `destroy` method will be unsupported in a future version. Instead use unsubscribe function returned by subscribe. Everything will be garbage-collected if store is garbage-collected."
|
|
4101
|
-
);
|
|
4102
|
-
}
|
|
4103
|
-
listeners.clear();
|
|
4104
|
-
};
|
|
4105
|
-
const api = { setState, getState, getInitialState, subscribe, destroy: destroy2 };
|
|
4106
|
-
const initialState = state = createState(setState, getState, api);
|
|
4107
|
-
return api;
|
|
4108
|
-
};
|
|
4109
|
-
var createStore = (createState) => createState ? createStoreImpl(createState) : createStoreImpl;
|
|
4110
|
-
|
|
4111
4079
|
// src/core/constants.ts
|
|
4080
|
+
var LOOKUP_SOURCE_TYPE_OPTIONS = [
|
|
4081
|
+
{ value: "MODULE", label: "Module" },
|
|
4082
|
+
{ value: "MASTER_TYPE", label: "Master Type" },
|
|
4083
|
+
{ value: "SETTINGS", label: "Settings Entity" }
|
|
4084
|
+
];
|
|
4112
4085
|
var generateId = () => Math.random().toString(36).substring(2, 9);
|
|
4113
4086
|
var FIELD_TYPES = [
|
|
4114
4087
|
{ type: "text", label: "Text Input", icon: "Type" },
|
|
@@ -4317,6 +4290,38 @@ var REGEX_PRESETS = [
|
|
|
4317
4290
|
}
|
|
4318
4291
|
];
|
|
4319
4292
|
|
|
4293
|
+
// node_modules/.pnpm/zustand@4.5.7_react@19.2.4/node_modules/zustand/esm/vanilla.mjs
|
|
4294
|
+
var createStoreImpl = (createState) => {
|
|
4295
|
+
let state;
|
|
4296
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
4297
|
+
const setState = (partial, replace) => {
|
|
4298
|
+
const nextState = typeof partial === "function" ? partial(state) : partial;
|
|
4299
|
+
if (!Object.is(nextState, state)) {
|
|
4300
|
+
const previousState = state;
|
|
4301
|
+
state = (replace != null ? replace : typeof nextState !== "object" || nextState === null) ? nextState : Object.assign({}, state, nextState);
|
|
4302
|
+
listeners.forEach((listener) => listener(state, previousState));
|
|
4303
|
+
}
|
|
4304
|
+
};
|
|
4305
|
+
const getState = () => state;
|
|
4306
|
+
const getInitialState = () => initialState;
|
|
4307
|
+
const subscribe = (listener) => {
|
|
4308
|
+
listeners.add(listener);
|
|
4309
|
+
return () => listeners.delete(listener);
|
|
4310
|
+
};
|
|
4311
|
+
const destroy2 = () => {
|
|
4312
|
+
if ((undefined ? undefined.MODE : void 0) !== "production") {
|
|
4313
|
+
console.warn(
|
|
4314
|
+
"[DEPRECATED] The `destroy` method will be unsupported in a future version. Instead use unsubscribe function returned by subscribe. Everything will be garbage-collected if store is garbage-collected."
|
|
4315
|
+
);
|
|
4316
|
+
}
|
|
4317
|
+
listeners.clear();
|
|
4318
|
+
};
|
|
4319
|
+
const api = { setState, getState, getInitialState, subscribe, destroy: destroy2 };
|
|
4320
|
+
const initialState = state = createState(setState, getState, api);
|
|
4321
|
+
return api;
|
|
4322
|
+
};
|
|
4323
|
+
var createStore = (createState) => createState ? createStoreImpl(createState) : createStoreImpl;
|
|
4324
|
+
|
|
4320
4325
|
// src/utils/clone.ts
|
|
4321
4326
|
var cloneForm = (schema) => {
|
|
4322
4327
|
return {
|
|
@@ -4696,6 +4701,8 @@ function transformField(field) {
|
|
|
4696
4701
|
transformed.enabled = field.enabled;
|
|
4697
4702
|
if (field.visible !== void 0)
|
|
4698
4703
|
transformed.visible = field.visible;
|
|
4704
|
+
if (field.isUnique !== void 0)
|
|
4705
|
+
transformed.isUnique = field.isUnique;
|
|
4699
4706
|
if (field.isd !== void 0)
|
|
4700
4707
|
transformed.isd = field.isd;
|
|
4701
4708
|
if (field.imageUrl !== void 0)
|
|
@@ -4766,7 +4773,30 @@ function transformField(field) {
|
|
|
4766
4773
|
var cleanFormSchema = (schema) => {
|
|
4767
4774
|
const cleanField = transformField;
|
|
4768
4775
|
let sections = [];
|
|
4769
|
-
if (schema.
|
|
4776
|
+
if (schema.sections && Array.isArray(schema.sections)) {
|
|
4777
|
+
sections = schema.sections;
|
|
4778
|
+
} else if (schema.groups && Array.isArray(schema.groups)) {
|
|
4779
|
+
const rootFields = schema.fields && Array.isArray(schema.fields) ? schema.fields : [];
|
|
4780
|
+
const rootById = new Map(rootFields.map((f) => [String(f.id), f]));
|
|
4781
|
+
sections = schema.groups.map((group, index2) => {
|
|
4782
|
+
let fieldList = group.fields;
|
|
4783
|
+
if (Array.isArray(group.fieldIds) && group.fieldIds.length > 0 && rootById.size > 0) {
|
|
4784
|
+
fieldList = group.fieldIds.map((id) => rootById.get(String(id))).filter(Boolean);
|
|
4785
|
+
}
|
|
4786
|
+
if (!fieldList)
|
|
4787
|
+
fieldList = [];
|
|
4788
|
+
return {
|
|
4789
|
+
...group,
|
|
4790
|
+
id: group.id || `section-${index2}`,
|
|
4791
|
+
title: group.title || group.name || `Section ${index2 + 1}`,
|
|
4792
|
+
name: group.name ?? group.title,
|
|
4793
|
+
fields: fieldList,
|
|
4794
|
+
order: group.order !== void 0 ? group.order : index2,
|
|
4795
|
+
isExpanded: group.isExpanded !== void 0 ? group.isExpanded : true,
|
|
4796
|
+
expanded: group.expanded !== void 0 ? group.expanded : group.isExpanded !== false
|
|
4797
|
+
};
|
|
4798
|
+
});
|
|
4799
|
+
} else if (schema.fields && Array.isArray(schema.fields) && schema.fields.length > 0) {
|
|
4770
4800
|
sections = [{
|
|
4771
4801
|
id: schema.id ? `section-${schema.id}` : "section-1",
|
|
4772
4802
|
title: schema.formName || schema.title || "Form Fields",
|
|
@@ -4774,16 +4804,6 @@ var cleanFormSchema = (schema) => {
|
|
|
4774
4804
|
order: 0,
|
|
4775
4805
|
isExpanded: true
|
|
4776
4806
|
}];
|
|
4777
|
-
} else if (schema.sections && Array.isArray(schema.sections)) {
|
|
4778
|
-
sections = schema.sections;
|
|
4779
|
-
} else if (schema.groups && Array.isArray(schema.groups)) {
|
|
4780
|
-
sections = schema.groups.map((group, index2) => ({
|
|
4781
|
-
id: group.id || `section-${index2}`,
|
|
4782
|
-
title: group.title || group.name || `Section ${index2 + 1}`,
|
|
4783
|
-
fields: group.fields || [],
|
|
4784
|
-
order: group.order !== void 0 ? group.order : index2,
|
|
4785
|
-
isExpanded: group.isExpanded !== void 0 ? group.isExpanded : true
|
|
4786
|
-
}));
|
|
4787
4807
|
}
|
|
4788
4808
|
return {
|
|
4789
4809
|
id: schema.id,
|
|
@@ -4791,26 +4811,37 @@ var cleanFormSchema = (schema) => {
|
|
|
4791
4811
|
formName: schema.formName || schema.formId || schema.id,
|
|
4792
4812
|
layout: schema.layout || { type: "grid", columns: 12, gap: "16px" },
|
|
4793
4813
|
// Preserve form-level layout or set default
|
|
4794
|
-
sections: sections.map((section, sectionIndex) =>
|
|
4795
|
-
|
|
4796
|
-
|
|
4797
|
-
|
|
4798
|
-
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
|
|
4803
|
-
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
|
|
4807
|
-
|
|
4808
|
-
|
|
4809
|
-
|
|
4810
|
-
|
|
4811
|
-
|
|
4812
|
-
|
|
4813
|
-
|
|
4814
|
+
sections: sections.map((section, sectionIndex) => {
|
|
4815
|
+
const order = section.order !== void 0 ? section.order : sectionIndex;
|
|
4816
|
+
return {
|
|
4817
|
+
id: section.id || `section-${sectionIndex}`,
|
|
4818
|
+
title: section.title || `Section ${sectionIndex + 1}`,
|
|
4819
|
+
name: section.name ?? section.title,
|
|
4820
|
+
description: section.description ?? null,
|
|
4821
|
+
fields: (section.fields || []).map((field, fieldIndex) => {
|
|
4822
|
+
const cleaned = cleanField(field);
|
|
4823
|
+
if (cleaned.order === void 0) {
|
|
4824
|
+
cleaned.order = fieldIndex;
|
|
4825
|
+
}
|
|
4826
|
+
return cleaned;
|
|
4827
|
+
}),
|
|
4828
|
+
isExpanded: section.isExpanded !== void 0 ? section.isExpanded : true,
|
|
4829
|
+
expanded: section.expanded !== void 0 ? section.expanded : section.isExpanded !== false,
|
|
4830
|
+
columns: section.columns,
|
|
4831
|
+
order,
|
|
4832
|
+
layout: section.layout || { type: "grid", columns: section.columns || 12, gap: "16px" },
|
|
4833
|
+
css: section.css,
|
|
4834
|
+
position: section.position ?? { row: order, column: 0, width: 12, order },
|
|
4835
|
+
visible: section.visible !== false,
|
|
4836
|
+
collapsible: section.collapsible !== false,
|
|
4837
|
+
parentGroupId: section.parentGroupId ?? null,
|
|
4838
|
+
repeatable: section.repeatable === true,
|
|
4839
|
+
dataKey: section.dataKey ?? null,
|
|
4840
|
+
addButtonLabel: section.addButtonLabel ?? null,
|
|
4841
|
+
minInstances: section.minInstances ?? null,
|
|
4842
|
+
maxInstances: section.maxInstances ?? null
|
|
4843
|
+
};
|
|
4844
|
+
})
|
|
4814
4845
|
};
|
|
4815
4846
|
};
|
|
4816
4847
|
function convertValidationArrayToObject(validation) {
|
|
@@ -4855,7 +4886,7 @@ function convertWidthToSpan(width, totalColumns = 12) {
|
|
|
4855
4886
|
const widthNum = parseWidth(width);
|
|
4856
4887
|
return Math.max(1, Math.min(12, Math.round(widthNum / 100 * totalColumns)));
|
|
4857
4888
|
}
|
|
4858
|
-
function fieldToPayload(field) {
|
|
4889
|
+
function fieldToPayload(field, opts) {
|
|
4859
4890
|
let outputType = field.type;
|
|
4860
4891
|
let outputValidations = field.validations ? { ...field.validations } : void 0;
|
|
4861
4892
|
if (field.type === "text" && isEmailLikeField(field)) {
|
|
@@ -4874,6 +4905,9 @@ function fieldToPayload(field) {
|
|
|
4874
4905
|
// Model key for binding (API / host app)
|
|
4875
4906
|
order: field.order !== void 0 ? field.order : 0
|
|
4876
4907
|
};
|
|
4908
|
+
if (opts?.groupId) {
|
|
4909
|
+
payload.groupId = opts.groupId;
|
|
4910
|
+
}
|
|
4877
4911
|
if (field.layout?.span !== void 0) {
|
|
4878
4912
|
payload.layout = {
|
|
4879
4913
|
row: field.layout.row ?? 0,
|
|
@@ -4931,6 +4965,8 @@ function fieldToPayload(field) {
|
|
|
4931
4965
|
payload.enabled = field.enabled;
|
|
4932
4966
|
if (field.visible !== void 0)
|
|
4933
4967
|
payload.visible = field.visible;
|
|
4968
|
+
if (field.isUnique !== void 0)
|
|
4969
|
+
payload.isUnique = field.isUnique;
|
|
4934
4970
|
if (field.css !== void 0)
|
|
4935
4971
|
payload.css = field.css;
|
|
4936
4972
|
if (field.optionSource !== void 0)
|
|
@@ -5027,20 +5063,48 @@ function fieldToPayload(field) {
|
|
|
5027
5063
|
}
|
|
5028
5064
|
return payload;
|
|
5029
5065
|
}
|
|
5066
|
+
function sectionToGroupPayload(section, index2) {
|
|
5067
|
+
const order = section.order !== void 0 ? section.order : index2;
|
|
5068
|
+
const pos = section.position ?? { row: order, column: 0, width: 12, order };
|
|
5069
|
+
const width = Math.max(1, Math.min(12, pos.width ?? 12));
|
|
5070
|
+
return {
|
|
5071
|
+
id: section.id,
|
|
5072
|
+
name: section.name ?? section.title,
|
|
5073
|
+
description: section.description ?? null,
|
|
5074
|
+
position: {
|
|
5075
|
+
row: pos.row ?? 0,
|
|
5076
|
+
column: pos.column ?? 0,
|
|
5077
|
+
width,
|
|
5078
|
+
order: pos.order ?? order
|
|
5079
|
+
},
|
|
5080
|
+
fieldIds: section.fields.map((f) => f.id),
|
|
5081
|
+
expanded: section.expanded !== void 0 ? section.expanded : section.isExpanded !== false,
|
|
5082
|
+
visible: section.visible !== false,
|
|
5083
|
+
collapsible: section.collapsible !== false,
|
|
5084
|
+
parentGroupId: section.parentGroupId ?? null,
|
|
5085
|
+
repeatable: section.repeatable === true,
|
|
5086
|
+
dataKey: section.dataKey ?? null,
|
|
5087
|
+
addButtonLabel: section.addButtonLabel ?? null,
|
|
5088
|
+
minInstances: section.minInstances ?? null,
|
|
5089
|
+
maxInstances: section.maxInstances ?? null,
|
|
5090
|
+
css: section.css
|
|
5091
|
+
};
|
|
5092
|
+
}
|
|
5030
5093
|
var builderToPlatform = (builderSchema) => {
|
|
5094
|
+
const fieldsFlat = [];
|
|
5095
|
+
const groups = builderSchema.sections.map((section, sectionIndex) => {
|
|
5096
|
+
section.fields.forEach((f) => {
|
|
5097
|
+
fieldsFlat.push(fieldToPayload(f, { groupId: section.id }));
|
|
5098
|
+
});
|
|
5099
|
+
return sectionToGroupPayload(section, sectionIndex);
|
|
5100
|
+
});
|
|
5031
5101
|
return {
|
|
5032
5102
|
id: builderSchema.id,
|
|
5033
5103
|
title: builderSchema.title,
|
|
5034
5104
|
formName: builderSchema.formName,
|
|
5035
5105
|
layout: builderSchema.layout || { type: "grid", columns: 12, gap: "16px" },
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
title: section.title,
|
|
5039
|
-
order: section.order !== void 0 ? section.order : sectionIndex,
|
|
5040
|
-
layout: section.layout || { type: "grid", columns: section.columns || 12, gap: "16px" },
|
|
5041
|
-
css: section.css,
|
|
5042
|
-
fields: section.fields.map(fieldToPayload)
|
|
5043
|
-
}))
|
|
5106
|
+
fields: fieldsFlat,
|
|
5107
|
+
groups
|
|
5044
5108
|
};
|
|
5045
5109
|
};
|
|
5046
5110
|
var platformToBuilder = (platformSchema) => {
|
|
@@ -5061,6 +5125,77 @@ function getValidationConfigForAngular(validations) {
|
|
|
5061
5125
|
};
|
|
5062
5126
|
}
|
|
5063
5127
|
|
|
5128
|
+
// src/utils/sectionHierarchy.ts
|
|
5129
|
+
function effectiveParentId(section, sectionIds) {
|
|
5130
|
+
const p = section.parentGroupId;
|
|
5131
|
+
if (!p || !sectionIds.has(p))
|
|
5132
|
+
return null;
|
|
5133
|
+
return p;
|
|
5134
|
+
}
|
|
5135
|
+
function getRootSections(sections) {
|
|
5136
|
+
const ids = new Set(sections.map((s) => s.id));
|
|
5137
|
+
return sections.filter((s) => effectiveParentId(s, ids) === null);
|
|
5138
|
+
}
|
|
5139
|
+
function getChildSections(sections, parentId, excludeSectionId) {
|
|
5140
|
+
const ids = new Set(sections.map((s) => s.id));
|
|
5141
|
+
return sections.filter((s) => {
|
|
5142
|
+
return effectiveParentId(s, ids) === parentId;
|
|
5143
|
+
}).sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
5144
|
+
}
|
|
5145
|
+
function getDescendantSectionIds(sections, rootId) {
|
|
5146
|
+
const byParent = /* @__PURE__ */ new Map();
|
|
5147
|
+
sections.forEach((s) => {
|
|
5148
|
+
const p = s.parentGroupId;
|
|
5149
|
+
if (p) {
|
|
5150
|
+
if (!byParent.has(p))
|
|
5151
|
+
byParent.set(p, []);
|
|
5152
|
+
byParent.get(p).push(s.id);
|
|
5153
|
+
}
|
|
5154
|
+
});
|
|
5155
|
+
const out = /* @__PURE__ */ new Set();
|
|
5156
|
+
const stack = [...byParent.get(rootId) || []];
|
|
5157
|
+
while (stack.length) {
|
|
5158
|
+
const id = stack.pop();
|
|
5159
|
+
if (out.has(id))
|
|
5160
|
+
continue;
|
|
5161
|
+
out.add(id);
|
|
5162
|
+
(byParent.get(id) || []).forEach((c) => stack.push(c));
|
|
5163
|
+
}
|
|
5164
|
+
return out;
|
|
5165
|
+
}
|
|
5166
|
+
function wouldCreateParentCycle(sections, sectionId, newParentId) {
|
|
5167
|
+
if (newParentId === sectionId)
|
|
5168
|
+
return true;
|
|
5169
|
+
return getDescendantSectionIds(sections, sectionId).has(newParentId);
|
|
5170
|
+
}
|
|
5171
|
+
function getValidParentSectionIds(sections, sectionId) {
|
|
5172
|
+
const descendants = getDescendantSectionIds(sections, sectionId);
|
|
5173
|
+
return sections.map((s) => s.id).filter((id) => id !== sectionId && !descendants.has(id));
|
|
5174
|
+
}
|
|
5175
|
+
function siblingsForParent(sections, sectionIds, parentId, excludeSectionId) {
|
|
5176
|
+
return sections.filter((s) => {
|
|
5177
|
+
if (s.id === excludeSectionId)
|
|
5178
|
+
return false;
|
|
5179
|
+
const eff = effectiveParentId(s, sectionIds);
|
|
5180
|
+
if (parentId === null)
|
|
5181
|
+
return eff === null;
|
|
5182
|
+
return eff === parentId;
|
|
5183
|
+
});
|
|
5184
|
+
}
|
|
5185
|
+
function nextSiblingOrder(sections, sectionId, parentId) {
|
|
5186
|
+
const sectionIds = new Set(sections.map((s) => s.id));
|
|
5187
|
+
const siblings = siblingsForParent(sections, sectionIds, parentId, sectionId);
|
|
5188
|
+
if (siblings.length === 0)
|
|
5189
|
+
return 0;
|
|
5190
|
+
return Math.max(...siblings.map((s) => s.order ?? 0)) + 1;
|
|
5191
|
+
}
|
|
5192
|
+
function getNextRootOrder(sections) {
|
|
5193
|
+
const roots = getRootSections(sections);
|
|
5194
|
+
if (roots.length === 0)
|
|
5195
|
+
return 0;
|
|
5196
|
+
return Math.max(...roots.map((r) => r.order ?? 0)) + 1;
|
|
5197
|
+
}
|
|
5198
|
+
|
|
5064
5199
|
// src/core/useFormStore.ts
|
|
5065
5200
|
var INITIAL_SCHEMA = {
|
|
5066
5201
|
id: "form_1",
|
|
@@ -5071,6 +5206,7 @@ var INITIAL_SCHEMA = {
|
|
|
5071
5206
|
var formStore = createStore((set, get) => ({
|
|
5072
5207
|
schema: INITIAL_SCHEMA,
|
|
5073
5208
|
selectedFieldId: null,
|
|
5209
|
+
selectedSectionId: null,
|
|
5074
5210
|
history: [INITIAL_SCHEMA],
|
|
5075
5211
|
historyIndex: 0,
|
|
5076
5212
|
isPreviewMode: false,
|
|
@@ -5388,6 +5524,8 @@ var formStore = createStore((set, get) => ({
|
|
|
5388
5524
|
importSection: (section) => {
|
|
5389
5525
|
const { schema, history, historyIndex } = get();
|
|
5390
5526
|
const clonedSection = cloneSection(section);
|
|
5527
|
+
clonedSection.parentGroupId = null;
|
|
5528
|
+
clonedSection.order = getNextRootOrder(schema.sections);
|
|
5391
5529
|
const newSchema = { ...schema, sections: [...schema.sections, clonedSection] };
|
|
5392
5530
|
set({
|
|
5393
5531
|
schema: newSchema,
|
|
@@ -5397,15 +5535,26 @@ var formStore = createStore((set, get) => ({
|
|
|
5397
5535
|
},
|
|
5398
5536
|
addSection: () => {
|
|
5399
5537
|
const { schema, history, historyIndex } = get();
|
|
5538
|
+
const order = getNextRootOrder(schema.sections);
|
|
5400
5539
|
const newSection = {
|
|
5401
5540
|
id: generateId(),
|
|
5402
|
-
title: `Section ${
|
|
5541
|
+
title: `Section ${order + 1}`,
|
|
5542
|
+
name: `Section ${order + 1}`,
|
|
5403
5543
|
fields: [],
|
|
5404
5544
|
columns: 1,
|
|
5405
5545
|
// Legacy - prefer layout.columns
|
|
5406
5546
|
layout: { type: "grid", columns: 12, gap: "16px" },
|
|
5407
|
-
order
|
|
5408
|
-
|
|
5547
|
+
order,
|
|
5548
|
+
position: { row: order, column: 0, width: 12, order },
|
|
5549
|
+
expanded: true,
|
|
5550
|
+
visible: true,
|
|
5551
|
+
collapsible: true,
|
|
5552
|
+
parentGroupId: null,
|
|
5553
|
+
repeatable: false,
|
|
5554
|
+
dataKey: null,
|
|
5555
|
+
addButtonLabel: null,
|
|
5556
|
+
minInstances: null,
|
|
5557
|
+
maxInstances: null
|
|
5409
5558
|
};
|
|
5410
5559
|
const newSchema = { ...schema, sections: [...schema.sections, newSection] };
|
|
5411
5560
|
set({
|
|
@@ -5415,24 +5564,74 @@ var formStore = createStore((set, get) => ({
|
|
|
5415
5564
|
});
|
|
5416
5565
|
},
|
|
5417
5566
|
removeSection: (sectionId) => {
|
|
5418
|
-
const { schema, history, historyIndex } = get();
|
|
5567
|
+
const { schema, history, historyIndex, selectedSectionId } = get();
|
|
5419
5568
|
const newSchema = {
|
|
5420
5569
|
...schema,
|
|
5421
|
-
sections: schema.sections.filter((s) => s.id !== sectionId)
|
|
5570
|
+
sections: schema.sections.filter((s) => s.id !== sectionId).map((s) => s.parentGroupId === sectionId ? { ...s, parentGroupId: null } : s)
|
|
5422
5571
|
};
|
|
5423
5572
|
set({
|
|
5424
5573
|
schema: newSchema,
|
|
5574
|
+
selectedSectionId: selectedSectionId === sectionId ? null : selectedSectionId,
|
|
5425
5575
|
history: [...history.slice(0, historyIndex + 1), newSchema],
|
|
5426
5576
|
historyIndex: historyIndex + 1
|
|
5427
5577
|
});
|
|
5428
5578
|
},
|
|
5429
5579
|
updateSection: (sectionId, updates) => {
|
|
5430
5580
|
const { schema, history, historyIndex } = get();
|
|
5581
|
+
let processedUpdates = { ...updates };
|
|
5582
|
+
if (updates.parentGroupId !== void 0) {
|
|
5583
|
+
const newParent = updates.parentGroupId;
|
|
5584
|
+
if (newParent === null || newParent === "") {
|
|
5585
|
+
processedUpdates.parentGroupId = null;
|
|
5586
|
+
processedUpdates.order = nextSiblingOrder(schema.sections, sectionId, null);
|
|
5587
|
+
} else if (wouldCreateParentCycle(schema.sections, sectionId, newParent)) {
|
|
5588
|
+
delete processedUpdates.parentGroupId;
|
|
5589
|
+
} else {
|
|
5590
|
+
processedUpdates.parentGroupId = newParent;
|
|
5591
|
+
processedUpdates.order = nextSiblingOrder(schema.sections, sectionId, newParent);
|
|
5592
|
+
}
|
|
5593
|
+
}
|
|
5431
5594
|
const newSchema = {
|
|
5432
5595
|
...schema,
|
|
5433
|
-
sections: schema.sections.map(
|
|
5434
|
-
(s
|
|
5435
|
-
|
|
5596
|
+
sections: schema.sections.map((s) => {
|
|
5597
|
+
if (s.id !== sectionId)
|
|
5598
|
+
return s;
|
|
5599
|
+
let mergedUpdates = { ...processedUpdates };
|
|
5600
|
+
if (processedUpdates.css !== void 0) {
|
|
5601
|
+
const styleKeyPassed = processedUpdates.css && "style" in processedUpdates.css;
|
|
5602
|
+
let newStyle;
|
|
5603
|
+
if (styleKeyPassed) {
|
|
5604
|
+
if (processedUpdates.css.style === void 0 || processedUpdates.css.style === null) {
|
|
5605
|
+
newStyle = void 0;
|
|
5606
|
+
} else if (typeof processedUpdates.css.style === "object") {
|
|
5607
|
+
const onlyStyleInUpdate = !("class" in processedUpdates.css) || processedUpdates.css.class === void 0;
|
|
5608
|
+
if (onlyStyleInUpdate) {
|
|
5609
|
+
newStyle = processedUpdates.css.style;
|
|
5610
|
+
} else {
|
|
5611
|
+
newStyle = { ...s.css?.style || {}, ...processedUpdates.css.style };
|
|
5612
|
+
}
|
|
5613
|
+
} else {
|
|
5614
|
+
newStyle = processedUpdates.css.style;
|
|
5615
|
+
}
|
|
5616
|
+
} else {
|
|
5617
|
+
newStyle = s.css?.style;
|
|
5618
|
+
}
|
|
5619
|
+
mergedUpdates.css = {
|
|
5620
|
+
...s.css || {},
|
|
5621
|
+
...processedUpdates.css,
|
|
5622
|
+
style: newStyle
|
|
5623
|
+
};
|
|
5624
|
+
if (!mergedUpdates.css.class)
|
|
5625
|
+
delete mergedUpdates.css.class;
|
|
5626
|
+
if (!mergedUpdates.css.style || Object.keys(mergedUpdates.css.style).length === 0) {
|
|
5627
|
+
delete mergedUpdates.css.style;
|
|
5628
|
+
}
|
|
5629
|
+
if (Object.keys(mergedUpdates.css).length === 0) {
|
|
5630
|
+
mergedUpdates.css = void 0;
|
|
5631
|
+
}
|
|
5632
|
+
}
|
|
5633
|
+
return { ...s, ...mergedUpdates };
|
|
5634
|
+
})
|
|
5436
5635
|
};
|
|
5437
5636
|
set({
|
|
5438
5637
|
schema: newSchema,
|
|
@@ -5442,14 +5641,23 @@ var formStore = createStore((set, get) => ({
|
|
|
5442
5641
|
},
|
|
5443
5642
|
moveSection: (oldIndex2, newIndex2) => {
|
|
5444
5643
|
const { schema, history, historyIndex } = get();
|
|
5445
|
-
const
|
|
5446
|
-
|
|
5447
|
-
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5644
|
+
const roots = getRootSections(schema.sections).sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
5645
|
+
if (oldIndex2 < 0 || oldIndex2 >= roots.length || newIndex2 < 0 || newIndex2 >= roots.length || oldIndex2 === newIndex2) {
|
|
5646
|
+
return;
|
|
5647
|
+
}
|
|
5648
|
+
const reorderedRoots = [...roots];
|
|
5649
|
+
const [moved2] = reorderedRoots.splice(oldIndex2, 1);
|
|
5650
|
+
reorderedRoots.splice(newIndex2, 0, moved2);
|
|
5651
|
+
const orderByRootId = new Map(reorderedRoots.map((r, i) => [r.id, i]));
|
|
5652
|
+
const rootIdSet = new Set(reorderedRoots.map((r) => r.id));
|
|
5653
|
+
const nonRoots = schema.sections.filter((s) => !rootIdSet.has(s.id));
|
|
5654
|
+
const newSections = [
|
|
5655
|
+
...reorderedRoots.map((r) => {
|
|
5656
|
+
const full = schema.sections.find((s) => s.id === r.id);
|
|
5657
|
+
return { ...full, order: orderByRootId.get(r.id) };
|
|
5658
|
+
}),
|
|
5659
|
+
...nonRoots
|
|
5660
|
+
];
|
|
5453
5661
|
const newSchema = { ...schema, sections: newSections };
|
|
5454
5662
|
set({
|
|
5455
5663
|
schema: newSchema,
|
|
@@ -5595,7 +5803,14 @@ var formStore = createStore((set, get) => ({
|
|
|
5595
5803
|
historyIndex: historyIndex + 1
|
|
5596
5804
|
});
|
|
5597
5805
|
},
|
|
5598
|
-
selectField: (fieldId) => set({
|
|
5806
|
+
selectField: (fieldId) => set({
|
|
5807
|
+
selectedFieldId: fieldId,
|
|
5808
|
+
selectedSectionId: fieldId ? null : null
|
|
5809
|
+
}),
|
|
5810
|
+
selectSection: (sectionId) => set({
|
|
5811
|
+
selectedSectionId: sectionId,
|
|
5812
|
+
selectedFieldId: null
|
|
5813
|
+
}),
|
|
5599
5814
|
moveField: (fieldId, targetSectionId, newIndex2) => {
|
|
5600
5815
|
const { schema, history, historyIndex } = get();
|
|
5601
5816
|
let field;
|
|
@@ -9563,37 +9778,70 @@ var FieldWrapper = class {
|
|
|
9563
9778
|
};
|
|
9564
9779
|
|
|
9565
9780
|
// src/builder/Section.ts
|
|
9566
|
-
var
|
|
9567
|
-
|
|
9781
|
+
var SECTION_TITLE_DEBOUNCE_MS = 300;
|
|
9782
|
+
var sectionTitleUpdateTimeouts = /* @__PURE__ */ new Map();
|
|
9783
|
+
var Section = class _Section {
|
|
9784
|
+
constructor(section, isSelectedField, selectedSectionId, allSections, depth = 0) {
|
|
9568
9785
|
__publicField(this, "container");
|
|
9569
9786
|
__publicField(this, "section");
|
|
9787
|
+
__publicField(this, "allSections");
|
|
9570
9788
|
__publicField(this, "isSelectedField");
|
|
9789
|
+
__publicField(this, "selectedSectionId");
|
|
9790
|
+
__publicField(this, "depth");
|
|
9571
9791
|
this.section = section;
|
|
9792
|
+
this.allSections = allSections;
|
|
9572
9793
|
this.isSelectedField = isSelectedField;
|
|
9794
|
+
this.selectedSectionId = selectedSectionId;
|
|
9795
|
+
this.depth = depth;
|
|
9573
9796
|
this.container = this.render();
|
|
9574
9797
|
}
|
|
9575
9798
|
getElement() {
|
|
9576
9799
|
return this.container;
|
|
9577
9800
|
}
|
|
9578
9801
|
render() {
|
|
9802
|
+
const sectionVisible = this.section.visible !== false;
|
|
9803
|
+
const isSelectedSection = this.section.id === this.selectedSectionId;
|
|
9804
|
+
const marginClass = this.depth > 0 ? "mb-4" : "mb-6";
|
|
9579
9805
|
const sectionEl = createElement("div", {
|
|
9580
|
-
className:
|
|
9581
|
-
"data-id": this.section.id
|
|
9806
|
+
className: `${marginClass} rounded-lg border bg-white dark:bg-gray-900 shadow-sm transition-all border-[#e9e9e9] ${!sectionVisible ? "opacity-50" : ""} ${isSelectedSection ? "ring-2 ring-[#635bff]" : ""} ${this.depth > 0 ? "shadow-inner" : ""}`,
|
|
9807
|
+
"data-id": this.section.id,
|
|
9808
|
+
"data-section-id": this.section.id
|
|
9809
|
+
});
|
|
9810
|
+
const header = createElement("div", {
|
|
9811
|
+
className: "flex items-center justify-between p-2 border-b border-gray-100 bg-white dark:border-gray-800 bg-gray-50 dark:bg-gray-800/50 rounded-t-lg cursor-pointer",
|
|
9812
|
+
onclick: () => {
|
|
9813
|
+
formStore.getState().selectSection(this.section.id);
|
|
9814
|
+
}
|
|
9582
9815
|
});
|
|
9583
|
-
const
|
|
9584
|
-
const
|
|
9585
|
-
|
|
9816
|
+
const headerLeft = createElement("div", { className: "flex items-center flex-1 min-w-0" });
|
|
9817
|
+
const dragHandle = createElement("div", { className: "cursor-move mr-3 text-gray-400 hover:text-gray-600 section-handle flex-shrink-0" }, [getIcon("GripVertical", 20)]);
|
|
9818
|
+
dragHandle.addEventListener("mousedown", (e) => e.stopPropagation());
|
|
9819
|
+
dragHandle.addEventListener("click", (e) => e.stopPropagation());
|
|
9820
|
+
headerLeft.appendChild(dragHandle);
|
|
9586
9821
|
headerLeft.appendChild(createElement("input", {
|
|
9587
|
-
className: "bg-transparent font-semibold text-gray-700 dark:text-gray-200 focus:outline-none focus:border-b border-blue-500",
|
|
9822
|
+
className: "bg-transparent font-semibold text-gray-700 dark:text-gray-200 focus:outline-none focus:border-b border-blue-500 min-w-0 flex-1",
|
|
9588
9823
|
value: this.section.title,
|
|
9589
9824
|
"data-focus-id": `section-title-${this.section.id}`,
|
|
9590
|
-
|
|
9825
|
+
onclick: (e) => e.stopPropagation(),
|
|
9826
|
+
oninput: (e) => {
|
|
9827
|
+
const sid = this.section.id;
|
|
9828
|
+
const value = e.target.value;
|
|
9829
|
+
const existing = sectionTitleUpdateTimeouts.get(sid);
|
|
9830
|
+
if (existing)
|
|
9831
|
+
clearTimeout(existing);
|
|
9832
|
+
const timeoutId = setTimeout(() => {
|
|
9833
|
+
sectionTitleUpdateTimeouts.delete(sid);
|
|
9834
|
+
formStore.getState().updateSection(sid, { title: value, name: value });
|
|
9835
|
+
}, SECTION_TITLE_DEBOUNCE_MS);
|
|
9836
|
+
sectionTitleUpdateTimeouts.set(sid, timeoutId);
|
|
9837
|
+
}
|
|
9591
9838
|
}));
|
|
9592
9839
|
header.appendChild(headerLeft);
|
|
9593
9840
|
const actions = createElement("div", { className: "flex items-center space-x-1" });
|
|
9594
9841
|
const colSelect = createElement("select", {
|
|
9595
9842
|
className: "text-xs border rounded bg-transparent mr-2 p-1 text-gray-600",
|
|
9596
9843
|
title: "Section Columns",
|
|
9844
|
+
onclick: (e) => e.stopPropagation(),
|
|
9597
9845
|
onchange: (e) => {
|
|
9598
9846
|
formStore.getState().updateSection(this.section.id, { columns: parseInt(e.target.value) });
|
|
9599
9847
|
}
|
|
@@ -9604,7 +9852,8 @@ var Section = class {
|
|
|
9604
9852
|
actions.appendChild(colSelect);
|
|
9605
9853
|
actions.appendChild(createElement("button", {
|
|
9606
9854
|
className: "text-gray-600 hover:text-red-500 transition-colors p-1",
|
|
9607
|
-
onclick: () => {
|
|
9855
|
+
onclick: (e) => {
|
|
9856
|
+
e.stopPropagation();
|
|
9608
9857
|
if (confirm("Delete this section and all its fields?")) {
|
|
9609
9858
|
formStore.getState().removeSection(this.section.id);
|
|
9610
9859
|
}
|
|
@@ -9648,6 +9897,25 @@ var Section = class {
|
|
|
9648
9897
|
fieldsGrid.appendChild(FieldWrapper.render(field, isSelected));
|
|
9649
9898
|
});
|
|
9650
9899
|
sectionEl.appendChild(fieldsGrid);
|
|
9900
|
+
const childSections = getChildSections(this.allSections, this.section.id);
|
|
9901
|
+
if (childSections.length > 0) {
|
|
9902
|
+
const nestedWrap = createElement("div", {
|
|
9903
|
+
className: this.depth === 0 ? "px-4 pb-4 pt-0 border-t border-dashed border-gray-200 dark:border-gray-700" : "pl-3 ml-2 mt-3 pt-3 border-l-2 border-[#635bff]/35 dark:border-[#635bff]/50 rounded-bl-md"
|
|
9904
|
+
});
|
|
9905
|
+
const nestedList = createElement("div", { className: "space-y-4" });
|
|
9906
|
+
childSections.forEach((child) => {
|
|
9907
|
+
const childComponent = new _Section(
|
|
9908
|
+
child,
|
|
9909
|
+
this.isSelectedField,
|
|
9910
|
+
this.selectedSectionId,
|
|
9911
|
+
this.allSections,
|
|
9912
|
+
this.depth + 1
|
|
9913
|
+
);
|
|
9914
|
+
nestedList.appendChild(childComponent.getElement());
|
|
9915
|
+
});
|
|
9916
|
+
nestedWrap.appendChild(nestedList);
|
|
9917
|
+
sectionEl.appendChild(nestedWrap);
|
|
9918
|
+
}
|
|
9651
9919
|
this.initFieldSortable(fieldsGrid);
|
|
9652
9920
|
return sectionEl;
|
|
9653
9921
|
}
|
|
@@ -9665,7 +9933,6 @@ var Section = class {
|
|
|
9665
9933
|
onAdd: (evt) => {
|
|
9666
9934
|
const item = evt.item;
|
|
9667
9935
|
const type = item.getAttribute("data-type");
|
|
9668
|
-
evt.from.getAttribute("data-section-id");
|
|
9669
9936
|
const toSectionId = this.section.id;
|
|
9670
9937
|
if (type && !item.hasAttribute("data-id")) {
|
|
9671
9938
|
item.remove();
|
|
@@ -9709,12 +9976,14 @@ var Section = class {
|
|
|
9709
9976
|
|
|
9710
9977
|
// src/builder/SectionList.ts
|
|
9711
9978
|
var SectionList = class {
|
|
9712
|
-
constructor(schema, selectedFieldId) {
|
|
9979
|
+
constructor(schema, selectedFieldId, selectedSectionId) {
|
|
9713
9980
|
__publicField(this, "container");
|
|
9714
9981
|
__publicField(this, "schema");
|
|
9715
9982
|
__publicField(this, "selectedFieldId");
|
|
9983
|
+
__publicField(this, "selectedSectionId");
|
|
9716
9984
|
this.schema = schema;
|
|
9717
9985
|
this.selectedFieldId = selectedFieldId;
|
|
9986
|
+
this.selectedSectionId = selectedSectionId;
|
|
9718
9987
|
this.container = this.render();
|
|
9719
9988
|
}
|
|
9720
9989
|
getElement() {
|
|
@@ -9737,13 +10006,18 @@ var SectionList = class {
|
|
|
9737
10006
|
placeholder.appendChild(createElement("div", { className: "text-sm text-gray-400", text: 'Drag fields from the sidebar or click "Add Section" below.' }));
|
|
9738
10007
|
listContainer.appendChild(placeholder);
|
|
9739
10008
|
}
|
|
9740
|
-
const
|
|
10009
|
+
const rootSections = getRootSections(this.schema.sections).sort((a, b) => {
|
|
9741
10010
|
const orderA = a.order !== void 0 ? a.order : 0;
|
|
9742
10011
|
const orderB = b.order !== void 0 ? b.order : 0;
|
|
9743
10012
|
return orderA - orderB;
|
|
9744
10013
|
});
|
|
9745
|
-
|
|
9746
|
-
const sectionComponent = new Section(
|
|
10014
|
+
rootSections.forEach((section) => {
|
|
10015
|
+
const sectionComponent = new Section(
|
|
10016
|
+
section,
|
|
10017
|
+
(id) => id === this.selectedFieldId,
|
|
10018
|
+
this.selectedSectionId,
|
|
10019
|
+
this.schema.sections
|
|
10020
|
+
);
|
|
9747
10021
|
listContainer.appendChild(sectionComponent.getElement());
|
|
9748
10022
|
});
|
|
9749
10023
|
this.initSectionSortable(listContainer, hasNoSections);
|
|
@@ -9992,7 +10266,17 @@ var FormBuilder = class {
|
|
|
9992
10266
|
const schemaHash = JSON.stringify({
|
|
9993
10267
|
sections: state.schema.sections.map((s) => ({
|
|
9994
10268
|
id: s.id,
|
|
9995
|
-
|
|
10269
|
+
title: s.title,
|
|
10270
|
+
description: s.description,
|
|
10271
|
+
visible: s.visible,
|
|
10272
|
+
position: s.position,
|
|
10273
|
+
expanded: s.expanded,
|
|
10274
|
+
collapsible: s.collapsible,
|
|
10275
|
+
parentGroupId: s.parentGroupId,
|
|
10276
|
+
repeatable: s.repeatable,
|
|
10277
|
+
minInstances: s.minInstances,
|
|
10278
|
+
maxInstances: s.maxInstances,
|
|
10279
|
+
css: s.css,
|
|
9996
10280
|
fields: s.fields.map((f) => ({
|
|
9997
10281
|
id: f.id,
|
|
9998
10282
|
type: f.type,
|
|
@@ -10003,6 +10287,7 @@ var FormBuilder = class {
|
|
|
10003
10287
|
}))
|
|
10004
10288
|
})),
|
|
10005
10289
|
selectedField: state.selectedFieldId,
|
|
10290
|
+
selectedSection: state.selectedSectionId,
|
|
10006
10291
|
isPreviewMode: state.isPreviewMode
|
|
10007
10292
|
});
|
|
10008
10293
|
if (schemaHash !== this.lastRenderedSchemaHash || previewModeChanged) {
|
|
@@ -10427,7 +10712,7 @@ var FormBuilder = class {
|
|
|
10427
10712
|
}
|
|
10428
10713
|
});
|
|
10429
10714
|
inner.appendChild(formNameInput);
|
|
10430
|
-
const sectionList = new SectionList(state.schema, state.selectedFieldId);
|
|
10715
|
+
const sectionList = new SectionList(state.schema, state.selectedFieldId, state.selectedSectionId);
|
|
10431
10716
|
inner.appendChild(sectionList.getElement());
|
|
10432
10717
|
const addSectionBtn = createElement("button", {
|
|
10433
10718
|
className: "w-full mt-6 py-3 dark:border-gray-700 rounded-md text-sm text-gray-500 bg-[#635bff] max-w-[140px] text-white transition-colors flex items-center justify-center font-medium",
|
|
@@ -10463,78 +10748,548 @@ var FormBuilder = class {
|
|
|
10463
10748
|
});
|
|
10464
10749
|
return container;
|
|
10465
10750
|
}
|
|
10466
|
-
|
|
10467
|
-
|
|
10468
|
-
|
|
10469
|
-
|
|
10470
|
-
|
|
10471
|
-
|
|
10751
|
+
/**
|
|
10752
|
+
* Shared 1–12 grid span control (Field Settings + Group Settings).
|
|
10753
|
+
*/
|
|
10754
|
+
createGridSpanSelector(options) {
|
|
10755
|
+
const layoutGroup = createElement("div", { className: "layout-span-group" });
|
|
10756
|
+
const layoutLabelRow = createElement("div", { className: "flex items-center justify-between mb-2" });
|
|
10757
|
+
layoutLabelRow.appendChild(createElement("label", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", text: "Grid Span" }));
|
|
10758
|
+
const spanValueDisplay = createElement("span", {
|
|
10759
|
+
className: "span-value-badge px-2 py-0.5 text-xs font-semibold bg-blue-100 text-blue-700 dark:bg-blue-900 dark:text-blue-200 rounded-full",
|
|
10760
|
+
text: `${options.currentSpan}/12`,
|
|
10761
|
+
id: options.badgeElementId
|
|
10762
|
+
});
|
|
10763
|
+
layoutLabelRow.appendChild(spanValueDisplay);
|
|
10764
|
+
layoutGroup.appendChild(layoutLabelRow);
|
|
10765
|
+
const spanButtonsContainer = createElement("div", { className: "grid grid-cols-6 gap-2 mt-2" });
|
|
10766
|
+
for (let span = 1; span <= 12; span++) {
|
|
10767
|
+
const isActive = options.currentSpan === span;
|
|
10768
|
+
const spanBtn = createElement("button", {
|
|
10769
|
+
type: "button",
|
|
10770
|
+
className: `span-preset-btn px-2 py-1.5 text-xs rounded transition-colors cursor-pointer ${isActive ? "bg-[#e7e7ff] text-[#635bff] font-semibold" : "bg-white border-2 border-[#e7e7ff] dark:bg-gray-800 text-gray-600 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-700"}`,
|
|
10771
|
+
text: `${span}`,
|
|
10772
|
+
title: `${span} column${span > 1 ? "s" : ""} (${Math.round(span / 12 * 100)}%)`
|
|
10773
|
+
});
|
|
10774
|
+
spanBtn.addEventListener("click", (e) => {
|
|
10775
|
+
e.preventDefault();
|
|
10776
|
+
e.stopPropagation();
|
|
10777
|
+
options.onSelect(span);
|
|
10778
|
+
});
|
|
10779
|
+
spanButtonsContainer.appendChild(spanBtn);
|
|
10472
10780
|
}
|
|
10473
|
-
|
|
10474
|
-
|
|
10475
|
-
|
|
10476
|
-
|
|
10477
|
-
|
|
10478
|
-
|
|
10479
|
-
|
|
10480
|
-
const
|
|
10481
|
-
|
|
10482
|
-
|
|
10483
|
-
|
|
10484
|
-
|
|
10485
|
-
|
|
10486
|
-
"data-focus-id": `field-label-${selectedField.id}`,
|
|
10487
|
-
oninput: (e) => {
|
|
10488
|
-
const fieldId2 = selectedField.id;
|
|
10489
|
-
const value = e.target.value;
|
|
10490
|
-
const existing = labelUpdateTimeouts.get(fieldId2);
|
|
10491
|
-
if (existing)
|
|
10492
|
-
clearTimeout(existing);
|
|
10493
|
-
const timeoutId = setTimeout(() => {
|
|
10494
|
-
labelUpdateTimeouts.delete(fieldId2);
|
|
10495
|
-
formStore.getState().updateField(fieldId2, { label: value });
|
|
10496
|
-
}, LABEL_DEBOUNCE_MS);
|
|
10497
|
-
labelUpdateTimeouts.set(fieldId2, timeoutId);
|
|
10781
|
+
layoutGroup.appendChild(spanButtonsContainer);
|
|
10782
|
+
return layoutGroup;
|
|
10783
|
+
}
|
|
10784
|
+
/**
|
|
10785
|
+
* Shared Styling block (Padding, Background, Alignment, CSS class, Advanced CSS) — same as Field Settings.
|
|
10786
|
+
*/
|
|
10787
|
+
appendSharedStylingSection(body, target, focusState) {
|
|
10788
|
+
const cssHeader = createElement("h3", { className: "text-xs font-semibold text-gray-500 uppercase tracking-wider mb-3 mt-6", text: "Styling" });
|
|
10789
|
+
body.appendChild(cssHeader);
|
|
10790
|
+
const getEntity = () => {
|
|
10791
|
+
const st = formStore.getState();
|
|
10792
|
+
if (target.kind === "field") {
|
|
10793
|
+
return st.schema.sections.flatMap((s) => s.fields).find((f) => f.id === target.fieldId);
|
|
10498
10794
|
}
|
|
10499
|
-
|
|
10500
|
-
|
|
10501
|
-
|
|
10502
|
-
const
|
|
10503
|
-
|
|
10504
|
-
|
|
10505
|
-
|
|
10506
|
-
const
|
|
10507
|
-
|
|
10508
|
-
|
|
10509
|
-
|
|
10510
|
-
|
|
10511
|
-
|
|
10512
|
-
|
|
10513
|
-
|
|
10514
|
-
|
|
10515
|
-
|
|
10516
|
-
|
|
10517
|
-
|
|
10518
|
-
|
|
10519
|
-
formStore.getState().updateField(selectedField.id, updates);
|
|
10520
|
-
this.render();
|
|
10795
|
+
return st.schema.sections.find((s) => s.id === target.sectionId);
|
|
10796
|
+
};
|
|
10797
|
+
const getStyleValue = (prop) => {
|
|
10798
|
+
const ent = getEntity();
|
|
10799
|
+
return ent?.css?.style?.[prop] || "";
|
|
10800
|
+
};
|
|
10801
|
+
const updateStyleProp = (prop, value) => {
|
|
10802
|
+
const ent = getEntity();
|
|
10803
|
+
if (!ent)
|
|
10804
|
+
return;
|
|
10805
|
+
const currentStyle = ent.css?.style || {};
|
|
10806
|
+
const newStyle = { ...currentStyle };
|
|
10807
|
+
if (value) {
|
|
10808
|
+
newStyle[prop] = value;
|
|
10809
|
+
} else {
|
|
10810
|
+
delete newStyle[prop];
|
|
10811
|
+
}
|
|
10812
|
+
const updatePayload = {
|
|
10813
|
+
css: {
|
|
10814
|
+
style: Object.keys(newStyle).length > 0 ? newStyle : void 0
|
|
10521
10815
|
}
|
|
10522
|
-
}
|
|
10523
|
-
|
|
10524
|
-
|
|
10525
|
-
|
|
10526
|
-
|
|
10527
|
-
|
|
10528
|
-
|
|
10529
|
-
|
|
10530
|
-
|
|
10531
|
-
|
|
10532
|
-
|
|
10533
|
-
|
|
10534
|
-
|
|
10535
|
-
|
|
10536
|
-
|
|
10537
|
-
|
|
10816
|
+
};
|
|
10817
|
+
if (target.kind === "field") {
|
|
10818
|
+
formStore.getState().updateField(target.fieldId, updatePayload);
|
|
10819
|
+
} else {
|
|
10820
|
+
formStore.getState().updateSection(target.sectionId, updatePayload);
|
|
10821
|
+
}
|
|
10822
|
+
};
|
|
10823
|
+
const entityId = target.kind === "field" ? target.fieldId : target.sectionId;
|
|
10824
|
+
const paddingGroup = createElement("div", { className: "mb-3" });
|
|
10825
|
+
paddingGroup.appendChild(createElement("label", { className: "block text-sm font-normal text-gray-700 dark:text-gray-300 mb-1", text: "Padding" }));
|
|
10826
|
+
const paddingSelect = createElement("select", {
|
|
10827
|
+
className: "w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-md bg-white dark:bg-gray-800 text-sm",
|
|
10828
|
+
onchange: (e) => {
|
|
10829
|
+
updateStyleProp("padding", e.target.value);
|
|
10830
|
+
}
|
|
10831
|
+
});
|
|
10832
|
+
const paddingOptions = [
|
|
10833
|
+
{ value: "", label: "None" },
|
|
10834
|
+
{ value: "4px", label: "4px - Tight" },
|
|
10835
|
+
{ value: "8px", label: "8px - Normal" },
|
|
10836
|
+
{ value: "12px", label: "12px - Comfortable" },
|
|
10837
|
+
{ value: "16px", label: "16px - Spacious" },
|
|
10838
|
+
{ value: "24px", label: "24px - Large" }
|
|
10839
|
+
];
|
|
10840
|
+
paddingOptions.forEach((opt) => {
|
|
10841
|
+
paddingSelect.appendChild(createElement("option", { value: opt.value, text: opt.label, selected: getStyleValue("padding") === opt.value }));
|
|
10842
|
+
});
|
|
10843
|
+
paddingGroup.appendChild(paddingSelect);
|
|
10844
|
+
body.appendChild(paddingGroup);
|
|
10845
|
+
const bgColorGroup = createElement("div", { className: "mb-3" });
|
|
10846
|
+
bgColorGroup.appendChild(createElement("label", { className: "block text-sm font-normal text-gray-700 dark:text-gray-300 mb-1", text: "Background Color" }));
|
|
10847
|
+
const bgColorRow = createElement("div", { className: "flex items-center gap-2" });
|
|
10848
|
+
const bgColorInput = createElement("input", {
|
|
10849
|
+
type: "color",
|
|
10850
|
+
className: "w-10 h-10 rounded border border-gray-300 cursor-pointer",
|
|
10851
|
+
value: getStyleValue("backgroundColor") || "#ffffff",
|
|
10852
|
+
onchange: (e) => {
|
|
10853
|
+
const color = e.target.value;
|
|
10854
|
+
updateStyleProp("backgroundColor", color === "#ffffff" ? "" : color);
|
|
10855
|
+
}
|
|
10856
|
+
});
|
|
10857
|
+
const bgColorClear = createElement("button", {
|
|
10858
|
+
type: "button",
|
|
10859
|
+
className: "px-2 py-1 text-xs text-gray-600 hover:text-gray-800 border border-gray-300 rounded",
|
|
10860
|
+
text: "Clear",
|
|
10861
|
+
onclick: () => {
|
|
10862
|
+
bgColorInput.value = "#ffffff";
|
|
10863
|
+
updateStyleProp("backgroundColor", "");
|
|
10864
|
+
}
|
|
10865
|
+
});
|
|
10866
|
+
bgColorRow.appendChild(bgColorInput);
|
|
10867
|
+
bgColorRow.appendChild(bgColorClear);
|
|
10868
|
+
bgColorGroup.appendChild(bgColorRow);
|
|
10869
|
+
body.appendChild(bgColorGroup);
|
|
10870
|
+
const alignGroup = createElement("div", { className: "mb-3" });
|
|
10871
|
+
alignGroup.appendChild(createElement("label", { className: "block text-sm font-normal text-gray-700 dark:text-gray-300 mb-1", text: "Text Alignment" }));
|
|
10872
|
+
const alignButtonsRow = createElement("div", { className: "flex gap-1" });
|
|
10873
|
+
const alignments = [
|
|
10874
|
+
{ value: "left", icon: "AlignLeft" },
|
|
10875
|
+
{ value: "center", icon: "AlignCenter" },
|
|
10876
|
+
{ value: "right", icon: "AlignRight" }
|
|
10877
|
+
];
|
|
10878
|
+
const currentAlign = getStyleValue("textAlign") || "left";
|
|
10879
|
+
alignments.forEach((align) => {
|
|
10880
|
+
const isActive = currentAlign === align.value;
|
|
10881
|
+
const btn = createElement("button", {
|
|
10882
|
+
type: "button",
|
|
10883
|
+
className: `p-2 rounded border ${isActive ? "border-blue-500 bg-blue-50 text-blue-600" : "border-gray-300 text-gray-600 hover:bg-gray-50"}`,
|
|
10884
|
+
title: `Align ${align.value}`,
|
|
10885
|
+
onclick: () => {
|
|
10886
|
+
const newValue = align.value === "left" ? "" : align.value;
|
|
10887
|
+
updateStyleProp("textAlign", newValue);
|
|
10888
|
+
}
|
|
10889
|
+
}, [getIcon(align.icon, 16)]);
|
|
10890
|
+
alignButtonsRow.appendChild(btn);
|
|
10891
|
+
});
|
|
10892
|
+
alignGroup.appendChild(alignButtonsRow);
|
|
10893
|
+
body.appendChild(alignGroup);
|
|
10894
|
+
const cssClassGroup = createElement("div", { className: "mb-3" });
|
|
10895
|
+
cssClassGroup.appendChild(createElement("label", { className: "block text-sm font-normal text-gray-700 dark:text-gray-300 mb-1", text: "Custom CSS Class" }));
|
|
10896
|
+
cssClassGroup.appendChild(createElement("input", {
|
|
10897
|
+
type: "text",
|
|
10898
|
+
className: "w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-md bg-transparent text-sm",
|
|
10899
|
+
value: getEntity()?.css?.class || "",
|
|
10900
|
+
placeholder: "e.g. my-custom-class",
|
|
10901
|
+
"data-focus-id": `${target.kind}-css-class-${entityId}`,
|
|
10902
|
+
oninput: (e) => {
|
|
10903
|
+
const cssClass = e.target.value;
|
|
10904
|
+
const ent = getEntity();
|
|
10905
|
+
if (!ent)
|
|
10906
|
+
return;
|
|
10907
|
+
if (target.kind === "field") {
|
|
10908
|
+
formStore.getState().updateField(target.fieldId, {
|
|
10909
|
+
css: { class: cssClass || void 0 }
|
|
10910
|
+
});
|
|
10911
|
+
} else {
|
|
10912
|
+
formStore.getState().updateSection(target.sectionId, {
|
|
10913
|
+
css: { class: cssClass || void 0 }
|
|
10914
|
+
});
|
|
10915
|
+
}
|
|
10916
|
+
}
|
|
10917
|
+
}));
|
|
10918
|
+
body.appendChild(cssClassGroup);
|
|
10919
|
+
const isPanelExpanded = advancedCssPanelState.get(entityId) || false;
|
|
10920
|
+
const advancedToggleGroup = createElement("div", { className: "mb-3" });
|
|
10921
|
+
const advancedToggle = createElement("button", {
|
|
10922
|
+
type: "button",
|
|
10923
|
+
className: "text-xs text-blue-600 hover:text-blue-700 flex items-center gap-1",
|
|
10924
|
+
onclick: () => {
|
|
10925
|
+
const advancedPanel2 = document.getElementById(`advanced-css-${entityId}`);
|
|
10926
|
+
if (advancedPanel2) {
|
|
10927
|
+
advancedPanel2.classList.toggle("hidden");
|
|
10928
|
+
const isHidden = advancedPanel2.classList.contains("hidden");
|
|
10929
|
+
advancedToggle.textContent = isHidden ? "\u25B6 Show Advanced CSS" : "\u25BC Hide Advanced CSS";
|
|
10930
|
+
advancedCssPanelState.set(entityId, !isHidden);
|
|
10931
|
+
}
|
|
10932
|
+
}
|
|
10933
|
+
});
|
|
10934
|
+
advancedToggle.textContent = isPanelExpanded ? "\u25BC Hide Advanced CSS" : "\u25B6 Show Advanced CSS";
|
|
10935
|
+
advancedToggleGroup.appendChild(advancedToggle);
|
|
10936
|
+
body.appendChild(advancedToggleGroup);
|
|
10937
|
+
const advancedPanel = createElement("div", {
|
|
10938
|
+
className: isPanelExpanded ? "mb-3" : "mb-3 hidden",
|
|
10939
|
+
id: `advanced-css-${entityId}`
|
|
10940
|
+
});
|
|
10941
|
+
advancedPanel.appendChild(createElement("label", { className: "block text-xs font-medium text-gray-600 dark:text-gray-400 mb-1", text: "Raw CSS Style (JSON)" }));
|
|
10942
|
+
const cssStyleId = `${target.kind}-css-style-${entityId}`;
|
|
10943
|
+
const freshEnt = getEntity();
|
|
10944
|
+
let initialCssStyleValue = freshEnt?.css?.style ? JSON.stringify(freshEnt.css.style, null, 2) : "";
|
|
10945
|
+
const preservedValue = focusState?.id === cssStyleId ? focusState.value : void 0;
|
|
10946
|
+
if (preservedValue !== void 0) {
|
|
10947
|
+
initialCssStyleValue = preservedValue;
|
|
10948
|
+
}
|
|
10949
|
+
const cssStyleTextarea = createElement("textarea", {
|
|
10950
|
+
className: "w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-md bg-transparent text-xs font-mono",
|
|
10951
|
+
rows: 3,
|
|
10952
|
+
placeholder: '{"padding": "8px", "backgroundColor": "#f0f0f0"}',
|
|
10953
|
+
"data-focus-id": cssStyleId,
|
|
10954
|
+
"data-was-focused": "false",
|
|
10955
|
+
onfocus: (e) => {
|
|
10956
|
+
const textarea = e.target;
|
|
10957
|
+
textarea.setAttribute("data-was-focused", "true");
|
|
10958
|
+
const ent = getEntity();
|
|
10959
|
+
if (!textarea.value.trim() && ent?.css?.style && Object.keys(ent.css.style).length > 0) {
|
|
10960
|
+
textarea.value = JSON.stringify(ent.css.style, null, 2);
|
|
10961
|
+
}
|
|
10962
|
+
},
|
|
10963
|
+
oninput: (e) => {
|
|
10964
|
+
const styleText = e.target.value;
|
|
10965
|
+
const ent = getEntity();
|
|
10966
|
+
if (!ent)
|
|
10967
|
+
return;
|
|
10968
|
+
try {
|
|
10969
|
+
if (styleText.trim()) {
|
|
10970
|
+
const styleObj = JSON.parse(styleText);
|
|
10971
|
+
if (target.kind === "field") {
|
|
10972
|
+
formStore.getState().updateField(target.fieldId, {
|
|
10973
|
+
css: { ...ent.css, style: styleObj }
|
|
10974
|
+
});
|
|
10975
|
+
} else {
|
|
10976
|
+
formStore.getState().updateSection(target.sectionId, {
|
|
10977
|
+
css: { ...ent.css, style: styleObj }
|
|
10978
|
+
});
|
|
10979
|
+
}
|
|
10980
|
+
}
|
|
10981
|
+
} catch {
|
|
10982
|
+
}
|
|
10983
|
+
},
|
|
10984
|
+
onblur: (e) => {
|
|
10985
|
+
const textarea = e.target;
|
|
10986
|
+
const styleText = textarea.value;
|
|
10987
|
+
const wasFocused = textarea.getAttribute("data-was-focused") === "true";
|
|
10988
|
+
if (!wasFocused)
|
|
10989
|
+
return;
|
|
10990
|
+
textarea.setAttribute("data-was-focused", "false");
|
|
10991
|
+
setTimeout(() => {
|
|
10992
|
+
if (!document.body.contains(textarea))
|
|
10993
|
+
return;
|
|
10994
|
+
const advPanel = document.getElementById(`advanced-css-${entityId}`);
|
|
10995
|
+
const isPanelVisible = advPanel && !advPanel.classList.contains("hidden");
|
|
10996
|
+
if (!isPanelVisible)
|
|
10997
|
+
return;
|
|
10998
|
+
const ent = getEntity();
|
|
10999
|
+
if (!ent)
|
|
11000
|
+
return;
|
|
11001
|
+
try {
|
|
11002
|
+
if (styleText.trim()) {
|
|
11003
|
+
const styleObj = JSON.parse(styleText);
|
|
11004
|
+
if (target.kind === "field") {
|
|
11005
|
+
formStore.getState().updateField(target.fieldId, {
|
|
11006
|
+
css: { ...ent.css, style: styleObj }
|
|
11007
|
+
});
|
|
11008
|
+
} else {
|
|
11009
|
+
formStore.getState().updateSection(target.sectionId, {
|
|
11010
|
+
css: { ...ent.css, style: styleObj }
|
|
11011
|
+
});
|
|
11012
|
+
}
|
|
11013
|
+
} else if (ent.css?.style && Object.keys(ent.css.style).length > 0) {
|
|
11014
|
+
textarea.value = JSON.stringify(ent.css.style, null, 2);
|
|
11015
|
+
} else {
|
|
11016
|
+
if (target.kind === "field") {
|
|
11017
|
+
formStore.getState().updateField(target.fieldId, {
|
|
11018
|
+
css: { ...ent.css, style: void 0 }
|
|
11019
|
+
});
|
|
11020
|
+
} else {
|
|
11021
|
+
formStore.getState().updateSection(target.sectionId, {
|
|
11022
|
+
css: { ...ent.css, style: void 0 }
|
|
11023
|
+
});
|
|
11024
|
+
}
|
|
11025
|
+
}
|
|
11026
|
+
} catch {
|
|
11027
|
+
if (ent.css?.style) {
|
|
11028
|
+
textarea.value = JSON.stringify(ent.css.style, null, 2);
|
|
11029
|
+
}
|
|
11030
|
+
}
|
|
11031
|
+
}, 0);
|
|
11032
|
+
}
|
|
11033
|
+
});
|
|
11034
|
+
cssStyleTextarea.value = initialCssStyleValue;
|
|
11035
|
+
advancedPanel.appendChild(cssStyleTextarea);
|
|
11036
|
+
body.appendChild(advancedPanel);
|
|
11037
|
+
}
|
|
11038
|
+
renderGroupSettingsPanel(section, allSections, focusState) {
|
|
11039
|
+
const panel = createElement("div", { className: " dark:bg-gray-900 flex flex-col h-full overflow-y-auto" });
|
|
11040
|
+
const header = createElement("div", { className: "flex items-center justify-between p-4 border-b border-gray-200 dark:border-gray-800" });
|
|
11041
|
+
header.appendChild(createElement("h2", { className: "font-semibold text-gray-900 dark:text-white", text: "Group Settings" }));
|
|
11042
|
+
header.appendChild(createElement("button", {
|
|
11043
|
+
className: "text-gray-500 hover:text-gray-700",
|
|
11044
|
+
onclick: () => formStore.getState().selectSection(null)
|
|
11045
|
+
}, [getIcon("X", 20)]));
|
|
11046
|
+
panel.appendChild(header);
|
|
11047
|
+
const body = createElement("div", { className: "flex-1 overflow-y-auto p-4 px-2 space-y-3", id: "config-panel-body" });
|
|
11048
|
+
const sectionId = section.id;
|
|
11049
|
+
const sectionHeading = (text) => createElement("h3", { className: "text-xs font-semibold text-gray-500 uppercase tracking-wider mb-3 mt-2", text });
|
|
11050
|
+
body.appendChild(sectionHeading("Basic"));
|
|
11051
|
+
const labelGroup = createElement("div");
|
|
11052
|
+
labelGroup.appendChild(createElement("label", { className: "block text-sm font-normal text-gray-700 dark:text-gray-300 mb-1", text: "Group Label" }));
|
|
11053
|
+
labelGroup.appendChild(createElement("input", {
|
|
11054
|
+
className: "w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-md bg-transparent",
|
|
11055
|
+
value: section.name ?? section.title,
|
|
11056
|
+
"data-focus-id": `group-label-${sectionId}`,
|
|
11057
|
+
oninput: (e) => {
|
|
11058
|
+
const v = e.target.value;
|
|
11059
|
+
const existing = labelUpdateTimeouts.get(`group-${sectionId}`);
|
|
11060
|
+
if (existing)
|
|
11061
|
+
clearTimeout(existing);
|
|
11062
|
+
const timeoutId = setTimeout(() => {
|
|
11063
|
+
labelUpdateTimeouts.delete(`group-${sectionId}`);
|
|
11064
|
+
formStore.getState().updateSection(sectionId, { title: v, name: v });
|
|
11065
|
+
}, LABEL_DEBOUNCE_MS);
|
|
11066
|
+
labelUpdateTimeouts.set(`group-${sectionId}`, timeoutId);
|
|
11067
|
+
}
|
|
11068
|
+
}));
|
|
11069
|
+
body.appendChild(labelGroup);
|
|
11070
|
+
const descGroup = createElement("div");
|
|
11071
|
+
descGroup.appendChild(createElement("label", { className: "block text-sm font-normal text-gray-700 dark:text-gray-300 mb-1", text: "Description" }));
|
|
11072
|
+
descGroup.appendChild(createElement("input", {
|
|
11073
|
+
className: "w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-md bg-transparent",
|
|
11074
|
+
value: section.description ?? "",
|
|
11075
|
+
placeholder: "Optional",
|
|
11076
|
+
"data-focus-id": `group-desc-${sectionId}`,
|
|
11077
|
+
oninput: (e) => {
|
|
11078
|
+
const v = e.target.value;
|
|
11079
|
+
formStore.getState().updateSection(sectionId, { description: v || null });
|
|
11080
|
+
}
|
|
11081
|
+
}));
|
|
11082
|
+
body.appendChild(descGroup);
|
|
11083
|
+
body.appendChild(sectionHeading("Layout"));
|
|
11084
|
+
const order = section.order ?? 0;
|
|
11085
|
+
const pos = section.position ?? { width: 12};
|
|
11086
|
+
const currentWidth = Math.max(1, Math.min(12, pos.width ?? 12));
|
|
11087
|
+
body.appendChild(
|
|
11088
|
+
this.createGridSpanSelector({
|
|
11089
|
+
currentSpan: currentWidth,
|
|
11090
|
+
badgeElementId: `group-span-badge-${sectionId}`,
|
|
11091
|
+
onSelect: (span) => {
|
|
11092
|
+
const st = formStore.getState().schema.sections.find((s) => s.id === sectionId);
|
|
11093
|
+
if (!st)
|
|
11094
|
+
return;
|
|
11095
|
+
const p = st.position ?? { row: order, column: 0, width: 12, order: st.order ?? order };
|
|
11096
|
+
formStore.getState().updateSection(sectionId, {
|
|
11097
|
+
position: { ...p, width: span, order: p.order ?? st.order ?? order }
|
|
11098
|
+
});
|
|
11099
|
+
}
|
|
11100
|
+
})
|
|
11101
|
+
);
|
|
11102
|
+
body.appendChild(sectionHeading("Fields in group"));
|
|
11103
|
+
const fieldRows = createElement("div", { className: "rounded-md border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800/40 p-2 space-y-1 text-sm text-gray-700 dark:text-gray-300" });
|
|
11104
|
+
if (section.fields.length === 0) {
|
|
11105
|
+
fieldRows.appendChild(createElement("div", { className: "text-gray-400 italic text-xs py-1", text: "No fields added yet" }));
|
|
11106
|
+
} else {
|
|
11107
|
+
section.fields.forEach((f) => {
|
|
11108
|
+
const display = f.displayName || f.label || f.id;
|
|
11109
|
+
fieldRows.appendChild(createElement("div", { className: "truncate", text: display }));
|
|
11110
|
+
});
|
|
11111
|
+
}
|
|
11112
|
+
body.appendChild(fieldRows);
|
|
11113
|
+
body.appendChild(sectionHeading("Display options"));
|
|
11114
|
+
body.appendChild(
|
|
11115
|
+
this.createCheckboxField(
|
|
11116
|
+
"Visible",
|
|
11117
|
+
section.visible !== false,
|
|
11118
|
+
(checked) => formStore.getState().updateSection(sectionId, { visible: checked }),
|
|
11119
|
+
`group-visible-${sectionId}`
|
|
11120
|
+
)
|
|
11121
|
+
);
|
|
11122
|
+
body.appendChild(
|
|
11123
|
+
this.createCheckboxField(
|
|
11124
|
+
"Show side/down arrow to expand/collapse",
|
|
11125
|
+
section.collapsible !== false,
|
|
11126
|
+
(checked) => formStore.getState().updateSection(sectionId, { collapsible: checked }),
|
|
11127
|
+
`group-collapsible-${sectionId}`
|
|
11128
|
+
)
|
|
11129
|
+
);
|
|
11130
|
+
body.appendChild(sectionHeading("Parent group"));
|
|
11131
|
+
const parentGroup = createElement("div", { className: "mb-2" });
|
|
11132
|
+
parentGroup.appendChild(createElement("label", { className: "block text-sm font-normal text-gray-700 dark:text-gray-300 mb-1", text: "Parent Group" }));
|
|
11133
|
+
const parentSelect = createElement("select", {
|
|
11134
|
+
className: "w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-md bg-transparent",
|
|
11135
|
+
value: section.parentGroupId || "",
|
|
11136
|
+
onchange: (e) => {
|
|
11137
|
+
const v = e.target.value;
|
|
11138
|
+
const selfId = sectionId;
|
|
11139
|
+
if (!v || v === selfId) {
|
|
11140
|
+
formStore.getState().updateSection(selfId, { parentGroupId: null });
|
|
11141
|
+
} else {
|
|
11142
|
+
formStore.getState().updateSection(selfId, { parentGroupId: v });
|
|
11143
|
+
}
|
|
11144
|
+
}
|
|
11145
|
+
});
|
|
11146
|
+
parentSelect.appendChild(createElement("option", { value: "", text: "None", selected: !section.parentGroupId }));
|
|
11147
|
+
const validParentIds = new Set(getValidParentSectionIds(allSections, sectionId));
|
|
11148
|
+
allSections.forEach((g) => {
|
|
11149
|
+
if (!validParentIds.has(g.id))
|
|
11150
|
+
return;
|
|
11151
|
+
const label = g.name ?? g.title;
|
|
11152
|
+
parentSelect.appendChild(
|
|
11153
|
+
createElement("option", {
|
|
11154
|
+
value: g.id,
|
|
11155
|
+
text: label,
|
|
11156
|
+
selected: section.parentGroupId === g.id
|
|
11157
|
+
})
|
|
11158
|
+
);
|
|
11159
|
+
});
|
|
11160
|
+
parentGroup.appendChild(parentSelect);
|
|
11161
|
+
body.appendChild(parentGroup);
|
|
11162
|
+
body.appendChild(sectionHeading("Repeatable group"));
|
|
11163
|
+
body.appendChild(
|
|
11164
|
+
this.createCheckboxField(
|
|
11165
|
+
"Allow multiple instances",
|
|
11166
|
+
section.repeatable === true,
|
|
11167
|
+
(checked) => formStore.getState().updateSection(sectionId, { repeatable: checked }),
|
|
11168
|
+
`group-repeatable-${sectionId}`
|
|
11169
|
+
)
|
|
11170
|
+
);
|
|
11171
|
+
if (section.repeatable === true) {
|
|
11172
|
+
const minG = createElement("div", { className: "mb-2" });
|
|
11173
|
+
minG.appendChild(createElement("label", { className: "block text-sm font-normal text-gray-700 dark:text-gray-300 mb-1", text: "Min Instances" }));
|
|
11174
|
+
minG.appendChild(createElement("input", {
|
|
11175
|
+
type: "number",
|
|
11176
|
+
className: "w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-md bg-transparent",
|
|
11177
|
+
value: section.minInstances != null ? String(section.minInstances) : "",
|
|
11178
|
+
placeholder: "e.g. 1",
|
|
11179
|
+
"data-focus-id": `group-min-inst-${sectionId}`,
|
|
11180
|
+
oninput: (e) => {
|
|
11181
|
+
const raw = e.target.value;
|
|
11182
|
+
if (raw === "") {
|
|
11183
|
+
formStore.getState().updateSection(sectionId, { minInstances: null });
|
|
11184
|
+
return;
|
|
11185
|
+
}
|
|
11186
|
+
const n = parseInt(raw, 10);
|
|
11187
|
+
formStore.getState().updateSection(sectionId, { minInstances: isNaN(n) ? null : n });
|
|
11188
|
+
}
|
|
11189
|
+
}));
|
|
11190
|
+
body.appendChild(minG);
|
|
11191
|
+
const maxG = createElement("div", { className: "mb-2" });
|
|
11192
|
+
maxG.appendChild(createElement("label", { className: "block text-sm font-normal text-gray-700 dark:text-gray-300 mb-1", text: "Max Instances" }));
|
|
11193
|
+
maxG.appendChild(createElement("input", {
|
|
11194
|
+
type: "number",
|
|
11195
|
+
className: "w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-md bg-transparent",
|
|
11196
|
+
value: section.maxInstances != null ? String(section.maxInstances) : "",
|
|
11197
|
+
placeholder: "Unlimited",
|
|
11198
|
+
"data-focus-id": `group-max-inst-${sectionId}`,
|
|
11199
|
+
oninput: (e) => {
|
|
11200
|
+
const raw = e.target.value;
|
|
11201
|
+
if (raw === "") {
|
|
11202
|
+
formStore.getState().updateSection(sectionId, { maxInstances: null });
|
|
11203
|
+
return;
|
|
11204
|
+
}
|
|
11205
|
+
const n = parseInt(raw, 10);
|
|
11206
|
+
formStore.getState().updateSection(sectionId, { maxInstances: isNaN(n) ? null : n });
|
|
11207
|
+
}
|
|
11208
|
+
}));
|
|
11209
|
+
body.appendChild(maxG);
|
|
11210
|
+
}
|
|
11211
|
+
this.appendSharedStylingSection(body, { kind: "section", sectionId }, focusState);
|
|
11212
|
+
panel.appendChild(body);
|
|
11213
|
+
return panel;
|
|
11214
|
+
}
|
|
11215
|
+
renderConfigPanel(state, focusState = null) {
|
|
11216
|
+
const panel = createElement("div", { className: " dark:bg-gray-900 flex flex-col h-full overflow-y-auto" });
|
|
11217
|
+
if (state.selectedSectionId) {
|
|
11218
|
+
const selectedSection = state.schema.sections.find((s) => s.id === state.selectedSectionId);
|
|
11219
|
+
if (selectedSection) {
|
|
11220
|
+
return this.renderGroupSettingsPanel(selectedSection, state.schema.sections, focusState);
|
|
11221
|
+
}
|
|
11222
|
+
}
|
|
11223
|
+
const selectedField = state.schema.sections.flatMap((s) => s.fields).find((f) => f.id === state.selectedFieldId);
|
|
11224
|
+
if (!selectedField) {
|
|
11225
|
+
panel.appendChild(createElement("div", { className: "p-4 text-center text-gray-500", text: "Select a field or section to configure" }));
|
|
11226
|
+
return panel;
|
|
11227
|
+
}
|
|
11228
|
+
const header = createElement("div", { className: "flex items-center justify-between p-4 border-b border-gray-200 dark:border-gray-800" });
|
|
11229
|
+
header.appendChild(createElement("h2", { className: "font-semibold text-gray-900 dark:text-white", text: "Field Settings" }));
|
|
11230
|
+
header.appendChild(createElement("button", {
|
|
11231
|
+
className: "text-gray-500 hover:text-gray-700",
|
|
11232
|
+
onclick: () => formStore.getState().selectField(null)
|
|
11233
|
+
}, [getIcon("X", 20)]));
|
|
11234
|
+
panel.appendChild(header);
|
|
11235
|
+
const body = createElement("div", { className: "flex-1 overflow-y-auto p-4 px-2 space-y-3", id: "config-panel-body" });
|
|
11236
|
+
const labelGroup = createElement("div");
|
|
11237
|
+
labelGroup.appendChild(createElement("label", { className: "block text-sm font-normal text-gray-700 dark:text-gray-300 mb-1", text: "Label" }));
|
|
11238
|
+
labelGroup.appendChild(createElement("input", {
|
|
11239
|
+
className: "w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-md bg-transparent",
|
|
11240
|
+
value: selectedField.label,
|
|
11241
|
+
"data-focus-id": `field-label-${selectedField.id}`,
|
|
11242
|
+
oninput: (e) => {
|
|
11243
|
+
const fieldId2 = selectedField.id;
|
|
11244
|
+
const value = e.target.value;
|
|
11245
|
+
const existing = labelUpdateTimeouts.get(fieldId2);
|
|
11246
|
+
if (existing)
|
|
11247
|
+
clearTimeout(existing);
|
|
11248
|
+
const timeoutId = setTimeout(() => {
|
|
11249
|
+
labelUpdateTimeouts.delete(fieldId2);
|
|
11250
|
+
formStore.getState().updateField(fieldId2, { label: value });
|
|
11251
|
+
}, LABEL_DEBOUNCE_MS);
|
|
11252
|
+
labelUpdateTimeouts.set(fieldId2, timeoutId);
|
|
11253
|
+
}
|
|
11254
|
+
}));
|
|
11255
|
+
body.appendChild(labelGroup);
|
|
11256
|
+
if (selectedField.type === "number") {
|
|
11257
|
+
const valueSourceHeader = createElement("h3", { className: "text-xs font-semibold text-gray-500 uppercase tracking-wider mb-2 mt-4", text: "Value Source" });
|
|
11258
|
+
body.appendChild(valueSourceHeader);
|
|
11259
|
+
const valueSourceGroup = createElement("div", { className: "mb-3" });
|
|
11260
|
+
valueSourceGroup.appendChild(createElement("label", { className: "block text-sm font-normal text-gray-700 dark:text-gray-300 mb-1", text: "Source" }));
|
|
11261
|
+
const valueSourceSelect = createElement("select", {
|
|
11262
|
+
className: "w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-md bg-transparent",
|
|
11263
|
+
value: selectedField.valueSource || "manual",
|
|
11264
|
+
onchange: (e) => {
|
|
11265
|
+
const source = e.target.value;
|
|
11266
|
+
const updates = { valueSource: source };
|
|
11267
|
+
if (source === "manual") {
|
|
11268
|
+
updates.formula = void 0;
|
|
11269
|
+
updates.dependencies = void 0;
|
|
11270
|
+
} else if (source === "formula") {
|
|
11271
|
+
updates.formula = selectedField.formula || "";
|
|
11272
|
+
updates.dependencies = selectedField.dependencies || [];
|
|
11273
|
+
}
|
|
11274
|
+
formStore.getState().updateField(selectedField.id, updates);
|
|
11275
|
+
this.render();
|
|
11276
|
+
}
|
|
11277
|
+
});
|
|
11278
|
+
valueSourceSelect.appendChild(createElement("option", { value: "manual", text: "Manual", selected: (selectedField.valueSource || "manual") === "manual" }));
|
|
11279
|
+
valueSourceSelect.appendChild(createElement("option", { value: "formula", text: "Formula", selected: selectedField.valueSource === "formula" }));
|
|
11280
|
+
valueSourceGroup.appendChild(valueSourceSelect);
|
|
11281
|
+
body.appendChild(valueSourceGroup);
|
|
11282
|
+
if (selectedField.valueSource === "formula") {
|
|
11283
|
+
const schema = formStore.getState().schema;
|
|
11284
|
+
const numericFields = getNumericFieldsForFormula(schema, selectedField.id);
|
|
11285
|
+
const availableIds = numericFields.map((f) => f.id);
|
|
11286
|
+
const availableNames = numericFields.map((f) => f.fieldName);
|
|
11287
|
+
const formulaGroup = createElement("div", { className: "mb-3" });
|
|
11288
|
+
formulaGroup.appendChild(createElement("label", { className: "block text-sm font-normal text-gray-700 dark:text-gray-300 mb-1", text: "Formula" }));
|
|
11289
|
+
const formulaInput = createElement("input", {
|
|
11290
|
+
type: "text",
|
|
11291
|
+
className: "w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-md bg-transparent font-mono text-sm",
|
|
11292
|
+
value: selectedField.formula || "",
|
|
10538
11293
|
placeholder: "e.g. quantity * price",
|
|
10539
11294
|
"data-focus-id": `field-formula-${selectedField.id}`,
|
|
10540
11295
|
oninput: (e) => {
|
|
@@ -10670,48 +11425,25 @@ var FormBuilder = class {
|
|
|
10670
11425
|
imageGroup.appendChild(btnRow);
|
|
10671
11426
|
body.appendChild(imageGroup);
|
|
10672
11427
|
}
|
|
10673
|
-
const layoutGroup = createElement("div", { className: "layout-span-group" });
|
|
10674
|
-
const layoutLabelRow = createElement("div", { className: "flex items-center justify-between mb-2" });
|
|
10675
|
-
layoutLabelRow.appendChild(createElement("label", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", text: "Grid Span" }));
|
|
10676
11428
|
const currentSpan = selectedField.layout?.span !== void 0 ? selectedField.layout.span : Math.max(1, Math.min(12, Math.round(parseWidth(selectedField.width || "100%") / 100 * 12)));
|
|
10677
|
-
const spanValueDisplay = createElement("span", {
|
|
10678
|
-
className: "span-value-badge px-2 py-0.5 text-xs font-semibold bg-blue-100 text-blue-700 dark:bg-blue-900 dark:text-blue-200 rounded-full",
|
|
10679
|
-
text: `${currentSpan}/12`,
|
|
10680
|
-
id: `span-value-${selectedField.id}`
|
|
10681
|
-
// const widthValueDisplay = createElement('span', {
|
|
10682
|
-
// className: 'width-value-badge px-2 py-0.5 text-xs font-semibold bg-[#019FA2] text-white dark:bg-blue-900 dark:text-blue-200 rounded-full',
|
|
10683
|
-
// text: `${currentWidth}%`,
|
|
10684
|
-
// id: `width-value-${selectedField.id}`
|
|
10685
|
-
});
|
|
10686
|
-
layoutLabelRow.appendChild(spanValueDisplay);
|
|
10687
|
-
layoutGroup.appendChild(layoutLabelRow);
|
|
10688
|
-
const spanButtonsContainer = createElement("div", { className: "grid grid-cols-6 gap-2 mt-2" });
|
|
10689
11429
|
const fieldId = selectedField.id;
|
|
10690
|
-
|
|
10691
|
-
|
|
10692
|
-
|
|
10693
|
-
|
|
10694
|
-
|
|
10695
|
-
|
|
10696
|
-
|
|
10697
|
-
|
|
10698
|
-
|
|
10699
|
-
|
|
10700
|
-
|
|
10701
|
-
|
|
10702
|
-
|
|
10703
|
-
|
|
10704
|
-
const layout = field.layout || { row: 0, column: 0 };
|
|
10705
|
-
state2.updateField(fieldId, {
|
|
10706
|
-
layout: { ...layout, span },
|
|
10707
|
-
width: Math.round(span / 12 * 100)
|
|
10708
|
-
});
|
|
11430
|
+
body.appendChild(
|
|
11431
|
+
this.createGridSpanSelector({
|
|
11432
|
+
currentSpan,
|
|
11433
|
+
badgeElementId: `span-value-${fieldId}`,
|
|
11434
|
+
onSelect: (span) => {
|
|
11435
|
+
const st = formStore.getState();
|
|
11436
|
+
const field = st.schema.sections.flatMap((s) => s.fields).find((f) => f.id === fieldId);
|
|
11437
|
+
if (field) {
|
|
11438
|
+
const layout = field.layout || { row: 0, column: 0 };
|
|
11439
|
+
st.updateField(fieldId, {
|
|
11440
|
+
layout: { ...layout, span },
|
|
11441
|
+
width: Math.round(span / 12 * 100)
|
|
11442
|
+
});
|
|
11443
|
+
}
|
|
10709
11444
|
}
|
|
10710
|
-
})
|
|
10711
|
-
|
|
10712
|
-
}
|
|
10713
|
-
layoutGroup.appendChild(spanButtonsContainer);
|
|
10714
|
-
body.appendChild(layoutGroup);
|
|
11445
|
+
})
|
|
11446
|
+
);
|
|
10715
11447
|
body.appendChild(this.createCheckboxField(
|
|
10716
11448
|
"Required",
|
|
10717
11449
|
!!selectedField.required || !!selectedField.validations?.required,
|
|
@@ -10736,6 +11468,12 @@ var FormBuilder = class {
|
|
|
10736
11468
|
(checked) => formStore.getState().updateField(selectedField.id, { visible: checked }),
|
|
10737
11469
|
`visible-${selectedField.id}`
|
|
10738
11470
|
));
|
|
11471
|
+
body.appendChild(this.createCheckboxField(
|
|
11472
|
+
"Unique",
|
|
11473
|
+
selectedField.isUnique === true,
|
|
11474
|
+
(checked) => formStore.getState().updateField(selectedField.id, { isUnique: checked }),
|
|
11475
|
+
`unique-${selectedField.id}`
|
|
11476
|
+
));
|
|
10739
11477
|
if (selectedField.type === "name_generator") {
|
|
10740
11478
|
const ngHeader = createElement("h3", { className: "text-xs font-semibold text-gray-500 uppercase tracking-wider mb-3 mt-6", text: "Name Generator Settings" });
|
|
10741
11479
|
body.appendChild(ngHeader);
|
|
@@ -10803,9 +11541,9 @@ var FormBuilder = class {
|
|
|
10803
11541
|
oninput: (e) => formStore.getState().updateField(selectedField.id, { nameGeneratorSuffix: e.target.value })
|
|
10804
11542
|
}));
|
|
10805
11543
|
body.appendChild(suffixGroup);
|
|
10806
|
-
const
|
|
10807
|
-
|
|
10808
|
-
|
|
11544
|
+
const paddingGroup = createElement("div", { className: "mb-3" });
|
|
11545
|
+
paddingGroup.appendChild(createElement("label", { className: "block text-sm font-normal text-gray-700 dark:text-gray-300 mb-1", text: "ID Padding" }));
|
|
11546
|
+
paddingGroup.appendChild(createElement("input", {
|
|
10809
11547
|
type: "number",
|
|
10810
11548
|
className: "w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-md bg-transparent",
|
|
10811
11549
|
value: String(selectedField.nameGeneratorIdPadding ?? 4),
|
|
@@ -10817,7 +11555,7 @@ var FormBuilder = class {
|
|
|
10817
11555
|
formStore.getState().updateField(selectedField.id, { nameGeneratorIdPadding: isNaN(val) ? 4 : Math.max(1, Math.min(10, val)) });
|
|
10818
11556
|
}
|
|
10819
11557
|
}));
|
|
10820
|
-
body.appendChild(
|
|
11558
|
+
body.appendChild(paddingGroup);
|
|
10821
11559
|
}
|
|
10822
11560
|
if (selectedField.type === "binary_choice") {
|
|
10823
11561
|
const bcHeader = createElement("h3", { className: "text-xs font-semibold text-gray-500 uppercase tracking-wider mb-3 mt-6", text: "Yes/No Toggle Settings" });
|
|
@@ -11107,8 +11845,14 @@ var FormBuilder = class {
|
|
|
11107
11845
|
this.render();
|
|
11108
11846
|
}
|
|
11109
11847
|
});
|
|
11110
|
-
|
|
11111
|
-
|
|
11848
|
+
const currentLookupSourceType = selectedField.lookupSourceType || "MODULE";
|
|
11849
|
+
LOOKUP_SOURCE_TYPE_OPTIONS.forEach((opt) => {
|
|
11850
|
+
lookupSourceTypeSelect.appendChild(createElement("option", {
|
|
11851
|
+
value: opt.value,
|
|
11852
|
+
text: opt.label,
|
|
11853
|
+
selected: currentLookupSourceType === opt.value
|
|
11854
|
+
}));
|
|
11855
|
+
});
|
|
11112
11856
|
lookupSourceTypeGroup.appendChild(lookupSourceTypeSelect);
|
|
11113
11857
|
body.appendChild(lookupSourceTypeGroup);
|
|
11114
11858
|
if (selectedField.lookupSourceType === "MODULE") {
|
|
@@ -11147,6 +11891,42 @@ var FormBuilder = class {
|
|
|
11147
11891
|
});
|
|
11148
11892
|
lookupSourceGroup.appendChild(lookupSourceSelect);
|
|
11149
11893
|
body.appendChild(lookupSourceGroup);
|
|
11894
|
+
} else if (selectedField.lookupSourceType === "SETTINGS") {
|
|
11895
|
+
const settingsEntities = this.options.settingsEntities || [];
|
|
11896
|
+
const lookupSourceGroup = createElement("div", { className: "mb-4" });
|
|
11897
|
+
lookupSourceGroup.appendChild(createElement("label", { className: "block text-sm font-normal text-gray-700 dark:text-gray-300 mb-1", text: "Source Key" }));
|
|
11898
|
+
const lookupSourceSelect = createElement("select", {
|
|
11899
|
+
className: "w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-md bg-transparent",
|
|
11900
|
+
value: selectedField.lookupSource || "",
|
|
11901
|
+
onchange: (e) => {
|
|
11902
|
+
const lookupSource = e.target.value;
|
|
11903
|
+
formStore.getState().updateField(selectedField.id, { lookupSource: lookupSource || void 0 });
|
|
11904
|
+
if (lookupSource) {
|
|
11905
|
+
formStore.getState().updateField(selectedField.id, {
|
|
11906
|
+
lookupValueField: void 0,
|
|
11907
|
+
lookupLabelField: void 0
|
|
11908
|
+
});
|
|
11909
|
+
}
|
|
11910
|
+
if (this.options.onLookupSourceChange && lookupSource) {
|
|
11911
|
+
this.options.onLookupSourceChange({
|
|
11912
|
+
fieldId: selectedField.id,
|
|
11913
|
+
lookupSourceType: "SETTINGS",
|
|
11914
|
+
lookupSource
|
|
11915
|
+
});
|
|
11916
|
+
}
|
|
11917
|
+
this.render();
|
|
11918
|
+
}
|
|
11919
|
+
});
|
|
11920
|
+
lookupSourceSelect.appendChild(createElement("option", { value: "", text: "Select Settings Entity", selected: !selectedField.lookupSource }));
|
|
11921
|
+
settingsEntities.forEach((ent) => {
|
|
11922
|
+
lookupSourceSelect.appendChild(createElement("option", {
|
|
11923
|
+
value: ent.value,
|
|
11924
|
+
text: ent.label,
|
|
11925
|
+
selected: selectedField.lookupSource === ent.value
|
|
11926
|
+
}));
|
|
11927
|
+
});
|
|
11928
|
+
lookupSourceGroup.appendChild(lookupSourceSelect);
|
|
11929
|
+
body.appendChild(lookupSourceGroup);
|
|
11150
11930
|
} else if (selectedField.lookupSourceType === "MASTER_TYPE") {
|
|
11151
11931
|
const masterTypes = formStore.getState().masterTypes;
|
|
11152
11932
|
const activeMasterTypes = masterTypes.filter((mt) => mt.active === true);
|
|
@@ -11989,234 +12769,7 @@ var FormBuilder = class {
|
|
|
11989
12769
|
}
|
|
11990
12770
|
}
|
|
11991
12771
|
}
|
|
11992
|
-
|
|
11993
|
-
body.appendChild(cssHeader);
|
|
11994
|
-
const getStyleValue = (prop) => selectedField.css?.style?.[prop] || "";
|
|
11995
|
-
const updateStyleProp = (prop, value) => {
|
|
11996
|
-
const state2 = formStore.getState();
|
|
11997
|
-
const freshField = state2.schema.sections.flatMap((s) => s.fields).find((f) => f.id === selectedField.id);
|
|
11998
|
-
if (!freshField) {
|
|
11999
|
-
return;
|
|
12000
|
-
}
|
|
12001
|
-
const currentStyle = freshField.css?.style || {};
|
|
12002
|
-
const newStyle = { ...currentStyle };
|
|
12003
|
-
if (value) {
|
|
12004
|
-
newStyle[prop] = value;
|
|
12005
|
-
} else {
|
|
12006
|
-
delete newStyle[prop];
|
|
12007
|
-
}
|
|
12008
|
-
const updatePayload = {
|
|
12009
|
-
css: {
|
|
12010
|
-
style: Object.keys(newStyle).length > 0 ? newStyle : void 0
|
|
12011
|
-
// Do NOT spread freshField.css here - let updateField preserve class
|
|
12012
|
-
}
|
|
12013
|
-
};
|
|
12014
|
-
state2.updateField(selectedField.id, updatePayload);
|
|
12015
|
-
};
|
|
12016
|
-
const paddingGroup = createElement("div", { className: "mb-3" });
|
|
12017
|
-
paddingGroup.appendChild(createElement("label", { className: "block text-sm font-normal text-gray-700 dark:text-gray-300 mb-1", text: "Padding" }));
|
|
12018
|
-
const paddingSelect = createElement("select", {
|
|
12019
|
-
className: "w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-md bg-white dark:bg-gray-800 text-sm",
|
|
12020
|
-
onchange: (e) => {
|
|
12021
|
-
updateStyleProp("padding", e.target.value);
|
|
12022
|
-
}
|
|
12023
|
-
});
|
|
12024
|
-
const paddingOptions = [
|
|
12025
|
-
{ value: "", label: "None" },
|
|
12026
|
-
{ value: "4px", label: "4px - Tight" },
|
|
12027
|
-
{ value: "8px", label: "8px - Normal" },
|
|
12028
|
-
{ value: "12px", label: "12px - Comfortable" },
|
|
12029
|
-
{ value: "16px", label: "16px - Spacious" },
|
|
12030
|
-
{ value: "24px", label: "24px - Large" }
|
|
12031
|
-
];
|
|
12032
|
-
paddingOptions.forEach((opt) => {
|
|
12033
|
-
paddingSelect.appendChild(createElement("option", { value: opt.value, text: opt.label, selected: getStyleValue("padding") === opt.value }));
|
|
12034
|
-
});
|
|
12035
|
-
paddingGroup.appendChild(paddingSelect);
|
|
12036
|
-
body.appendChild(paddingGroup);
|
|
12037
|
-
const bgColorGroup = createElement("div", { className: "mb-3" });
|
|
12038
|
-
bgColorGroup.appendChild(createElement("label", { className: "block text-sm font-normal text-gray-700 dark:text-gray-300 mb-1", text: "Background Color" }));
|
|
12039
|
-
const bgColorRow = createElement("div", { className: "flex items-center gap-2" });
|
|
12040
|
-
const bgColorInput = createElement("input", {
|
|
12041
|
-
type: "color",
|
|
12042
|
-
className: "w-10 h-10 rounded border border-gray-300 cursor-pointer",
|
|
12043
|
-
value: getStyleValue("backgroundColor") || "#ffffff",
|
|
12044
|
-
onchange: (e) => {
|
|
12045
|
-
const color = e.target.value;
|
|
12046
|
-
updateStyleProp("backgroundColor", color === "#ffffff" ? "" : color);
|
|
12047
|
-
}
|
|
12048
|
-
});
|
|
12049
|
-
const bgColorClear = createElement("button", {
|
|
12050
|
-
type: "button",
|
|
12051
|
-
className: "px-2 py-1 text-xs text-gray-600 hover:text-gray-800 border border-gray-300 rounded",
|
|
12052
|
-
text: "Clear",
|
|
12053
|
-
onclick: () => {
|
|
12054
|
-
bgColorInput.value = "#ffffff";
|
|
12055
|
-
updateStyleProp("backgroundColor", "");
|
|
12056
|
-
}
|
|
12057
|
-
});
|
|
12058
|
-
bgColorRow.appendChild(bgColorInput);
|
|
12059
|
-
bgColorRow.appendChild(bgColorClear);
|
|
12060
|
-
bgColorGroup.appendChild(bgColorRow);
|
|
12061
|
-
body.appendChild(bgColorGroup);
|
|
12062
|
-
const alignGroup = createElement("div", { className: "mb-3" });
|
|
12063
|
-
alignGroup.appendChild(createElement("label", { className: "block text-sm font-normal text-gray-700 dark:text-gray-300 mb-1", text: "Text Alignment" }));
|
|
12064
|
-
const alignButtonsRow = createElement("div", { className: "flex gap-1" });
|
|
12065
|
-
const alignments = [
|
|
12066
|
-
{ value: "left", icon: "AlignLeft" },
|
|
12067
|
-
{ value: "center", icon: "AlignCenter" },
|
|
12068
|
-
{ value: "right", icon: "AlignRight" }
|
|
12069
|
-
];
|
|
12070
|
-
const currentAlign = getStyleValue("textAlign") || "left";
|
|
12071
|
-
alignments.forEach((align) => {
|
|
12072
|
-
const isActive = currentAlign === align.value;
|
|
12073
|
-
const btn = createElement("button", {
|
|
12074
|
-
type: "button",
|
|
12075
|
-
className: `p-2 rounded border ${isActive ? "border-blue-500 bg-blue-50 text-blue-600" : "border-gray-300 text-gray-600 hover:bg-gray-50"}`,
|
|
12076
|
-
title: `Align ${align.value}`,
|
|
12077
|
-
onclick: () => {
|
|
12078
|
-
const newValue = align.value === "left" ? "" : align.value;
|
|
12079
|
-
updateStyleProp("textAlign", newValue);
|
|
12080
|
-
}
|
|
12081
|
-
}, [getIcon(align.icon, 16)]);
|
|
12082
|
-
alignButtonsRow.appendChild(btn);
|
|
12083
|
-
});
|
|
12084
|
-
alignGroup.appendChild(alignButtonsRow);
|
|
12085
|
-
body.appendChild(alignGroup);
|
|
12086
|
-
const cssClassGroup = createElement("div", { className: "mb-3" });
|
|
12087
|
-
cssClassGroup.appendChild(createElement("label", { className: "block text-sm font-normal text-gray-700 dark:text-gray-300 mb-1", text: "Custom CSS Class" }));
|
|
12088
|
-
cssClassGroup.appendChild(createElement("input", {
|
|
12089
|
-
type: "text",
|
|
12090
|
-
className: "w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-md bg-transparent text-sm",
|
|
12091
|
-
value: selectedField.css?.class || "",
|
|
12092
|
-
placeholder: "e.g. my-custom-class",
|
|
12093
|
-
"data-focus-id": `field-css-class-${selectedField.id}`,
|
|
12094
|
-
oninput: (e) => {
|
|
12095
|
-
const cssClass = e.target.value;
|
|
12096
|
-
const state2 = formStore.getState();
|
|
12097
|
-
const freshField = state2.schema.sections.flatMap((s) => s.fields).find((f) => f.id === selectedField.id);
|
|
12098
|
-
if (!freshField)
|
|
12099
|
-
return;
|
|
12100
|
-
state2.updateField(selectedField.id, {
|
|
12101
|
-
css: {
|
|
12102
|
-
class: cssClass || void 0
|
|
12103
|
-
// Do NOT spread freshField.css here - let updateField preserve style
|
|
12104
|
-
}
|
|
12105
|
-
});
|
|
12106
|
-
}
|
|
12107
|
-
}));
|
|
12108
|
-
body.appendChild(cssClassGroup);
|
|
12109
|
-
const isPanelExpanded = advancedCssPanelState.get(selectedField.id) || false;
|
|
12110
|
-
const advancedToggleGroup = createElement("div", { className: "mb-3" });
|
|
12111
|
-
const advancedToggle = createElement("button", {
|
|
12112
|
-
type: "button",
|
|
12113
|
-
className: "text-xs text-blue-600 hover:text-blue-700 flex items-center gap-1",
|
|
12114
|
-
onclick: () => {
|
|
12115
|
-
const advancedPanel2 = document.getElementById(`advanced-css-${selectedField.id}`);
|
|
12116
|
-
if (advancedPanel2) {
|
|
12117
|
-
advancedPanel2.classList.toggle("hidden");
|
|
12118
|
-
const isHidden = advancedPanel2.classList.contains("hidden");
|
|
12119
|
-
advancedToggle.textContent = isHidden ? "\u25B6 Show Advanced CSS" : "\u25BC Hide Advanced CSS";
|
|
12120
|
-
advancedCssPanelState.set(selectedField.id, !isHidden);
|
|
12121
|
-
}
|
|
12122
|
-
}
|
|
12123
|
-
});
|
|
12124
|
-
advancedToggle.textContent = isPanelExpanded ? "\u25BC Hide Advanced CSS" : "\u25B6 Show Advanced CSS";
|
|
12125
|
-
advancedToggleGroup.appendChild(advancedToggle);
|
|
12126
|
-
body.appendChild(advancedToggleGroup);
|
|
12127
|
-
const advancedPanel = createElement("div", {
|
|
12128
|
-
className: isPanelExpanded ? "mb-3" : "mb-3 hidden",
|
|
12129
|
-
id: `advanced-css-${selectedField.id}`
|
|
12130
|
-
});
|
|
12131
|
-
advancedPanel.appendChild(createElement("label", { className: "block text-xs font-medium text-gray-600 dark:text-gray-400 mb-1", text: "Raw CSS Style (JSON)" }));
|
|
12132
|
-
const cssStyleId = `field-css-style-${selectedField.id}`;
|
|
12133
|
-
const freshState = formStore.getState();
|
|
12134
|
-
const freshFieldForInit = freshState.schema.sections.flatMap((s) => s.fields).find((f) => f.id === selectedField.id);
|
|
12135
|
-
const fieldForCssStyle = freshFieldForInit || selectedField;
|
|
12136
|
-
let initialCssStyleValue = fieldForCssStyle.css?.style ? JSON.stringify(fieldForCssStyle.css.style, null, 2) : "";
|
|
12137
|
-
const preservedValue = focusState?.id === cssStyleId ? focusState.value : void 0;
|
|
12138
|
-
if (preservedValue !== void 0) {
|
|
12139
|
-
initialCssStyleValue = preservedValue;
|
|
12140
|
-
}
|
|
12141
|
-
const cssStyleTextarea = createElement("textarea", {
|
|
12142
|
-
className: "w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-md bg-transparent text-xs font-mono",
|
|
12143
|
-
rows: 3,
|
|
12144
|
-
placeholder: '{"padding": "8px", "backgroundColor": "#f0f0f0"}',
|
|
12145
|
-
"data-focus-id": cssStyleId,
|
|
12146
|
-
"data-was-focused": "false",
|
|
12147
|
-
onfocus: (e) => {
|
|
12148
|
-
const textarea = e.target;
|
|
12149
|
-
textarea.setAttribute("data-was-focused", "true");
|
|
12150
|
-
const state2 = formStore.getState();
|
|
12151
|
-
const freshField = state2.schema.sections.flatMap((s) => s.fields).find((f) => f.id === selectedField.id);
|
|
12152
|
-
if (!textarea.value.trim() && freshField?.css?.style && Object.keys(freshField.css.style).length > 0) {
|
|
12153
|
-
const styleString = JSON.stringify(freshField.css.style, null, 2);
|
|
12154
|
-
textarea.value = styleString;
|
|
12155
|
-
}
|
|
12156
|
-
},
|
|
12157
|
-
oninput: (e) => {
|
|
12158
|
-
const styleText = e.target.value;
|
|
12159
|
-
const state2 = formStore.getState();
|
|
12160
|
-
const freshField = state2.schema.sections.flatMap((s) => s.fields).find((f) => f.id === selectedField.id);
|
|
12161
|
-
if (!freshField)
|
|
12162
|
-
return;
|
|
12163
|
-
try {
|
|
12164
|
-
if (styleText.trim()) {
|
|
12165
|
-
const styleObj = JSON.parse(styleText);
|
|
12166
|
-
state2.updateField(selectedField.id, {
|
|
12167
|
-
css: { ...freshField.css, style: styleObj }
|
|
12168
|
-
});
|
|
12169
|
-
}
|
|
12170
|
-
} catch (err) {
|
|
12171
|
-
}
|
|
12172
|
-
},
|
|
12173
|
-
onblur: (e) => {
|
|
12174
|
-
const textarea = e.target;
|
|
12175
|
-
const styleText = textarea.value;
|
|
12176
|
-
const wasFocused = textarea.getAttribute("data-was-focused") === "true";
|
|
12177
|
-
if (!wasFocused) {
|
|
12178
|
-
return;
|
|
12179
|
-
}
|
|
12180
|
-
textarea.setAttribute("data-was-focused", "false");
|
|
12181
|
-
setTimeout(() => {
|
|
12182
|
-
if (!document.body.contains(textarea)) {
|
|
12183
|
-
return;
|
|
12184
|
-
}
|
|
12185
|
-
const advPanel = document.getElementById(`advanced-css-${selectedField.id}`);
|
|
12186
|
-
const isPanelVisible = advPanel && !advPanel.classList.contains("hidden");
|
|
12187
|
-
if (!isPanelVisible) {
|
|
12188
|
-
return;
|
|
12189
|
-
}
|
|
12190
|
-
const state2 = formStore.getState();
|
|
12191
|
-
const freshField = state2.schema.sections.flatMap((s) => s.fields).find((f) => f.id === selectedField.id);
|
|
12192
|
-
if (!freshField)
|
|
12193
|
-
return;
|
|
12194
|
-
try {
|
|
12195
|
-
if (styleText.trim()) {
|
|
12196
|
-
const styleObj = JSON.parse(styleText);
|
|
12197
|
-
state2.updateField(selectedField.id, {
|
|
12198
|
-
css: { ...freshField.css, style: styleObj }
|
|
12199
|
-
});
|
|
12200
|
-
} else {
|
|
12201
|
-
if (freshField.css?.style && Object.keys(freshField.css.style).length > 0) {
|
|
12202
|
-
textarea.value = JSON.stringify(freshField.css.style, null, 2);
|
|
12203
|
-
} else {
|
|
12204
|
-
state2.updateField(selectedField.id, {
|
|
12205
|
-
css: { ...freshField.css, style: void 0 }
|
|
12206
|
-
});
|
|
12207
|
-
}
|
|
12208
|
-
}
|
|
12209
|
-
} catch (err) {
|
|
12210
|
-
if (freshField.css?.style) {
|
|
12211
|
-
textarea.value = JSON.stringify(freshField.css.style, null, 2);
|
|
12212
|
-
}
|
|
12213
|
-
}
|
|
12214
|
-
}, 0);
|
|
12215
|
-
}
|
|
12216
|
-
});
|
|
12217
|
-
cssStyleTextarea.value = initialCssStyleValue;
|
|
12218
|
-
advancedPanel.appendChild(cssStyleTextarea);
|
|
12219
|
-
body.appendChild(advancedPanel);
|
|
12772
|
+
this.appendSharedStylingSection(body, { kind: "field", fieldId: selectedField.id }, focusState);
|
|
12220
12773
|
panel.appendChild(body);
|
|
12221
12774
|
return panel;
|
|
12222
12775
|
}
|
|
@@ -12273,6 +12826,7 @@ sortablejs/modular/sortable.esm.js:
|
|
|
12273
12826
|
exports.FormBuilder = FormBuilder;
|
|
12274
12827
|
exports.FormRenderer = FormRenderer;
|
|
12275
12828
|
exports.FormSchemaValidation = FormSchemaValidation;
|
|
12829
|
+
exports.LOOKUP_SOURCE_TYPE_OPTIONS = LOOKUP_SOURCE_TYPE_OPTIONS;
|
|
12276
12830
|
exports.builderToPlatform = builderToPlatform;
|
|
12277
12831
|
exports.cleanFormSchema = cleanFormSchema;
|
|
12278
12832
|
exports.convertValidationObjectToArray = convertValidationObjectToArray;
|