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