@enerjisaformlibrary/formbuilder-react 1.0.8 → 1.0.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +486 -27
- package/index.cjs +469 -240
- package/index.cjs.map +1 -1
- package/index.js +469 -240
- package/index.js.map +1 -1
- package/package.json +14 -19
- package/styles.css +1 -1
- package/types/client/src/components/form-builder/CanvasField.d.ts.map +1 -1
- package/types/client/src/components/form-builder/PropertiesPanel.d.ts.map +1 -1
- package/types/client/src/components/form-builder/Toolbar.d.ts.map +1 -1
- package/types/client/src/store/formStore.d.ts.map +1 -1
package/index.js
CHANGED
|
@@ -8104,6 +8104,25 @@ const AlignLeft = createLucideIcon("AlignLeft", [
|
|
|
8104
8104
|
*/
|
|
8105
8105
|
|
|
8106
8106
|
|
|
8107
|
+
const BookOpen = createLucideIcon("BookOpen", [
|
|
8108
|
+
["path", { d: "M12 7v14", key: "1akyts" }],
|
|
8109
|
+
[
|
|
8110
|
+
"path",
|
|
8111
|
+
{
|
|
8112
|
+
d: "M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z",
|
|
8113
|
+
key: "ruj8y"
|
|
8114
|
+
}
|
|
8115
|
+
]
|
|
8116
|
+
]);
|
|
8117
|
+
|
|
8118
|
+
/**
|
|
8119
|
+
* @license lucide-react v0.453.0 - ISC
|
|
8120
|
+
*
|
|
8121
|
+
* This source code is licensed under the ISC license.
|
|
8122
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
8123
|
+
*/
|
|
8124
|
+
|
|
8125
|
+
|
|
8107
8126
|
const Box = createLucideIcon("Box", [
|
|
8108
8127
|
[
|
|
8109
8128
|
"path",
|
|
@@ -9414,16 +9433,26 @@ const createEmptyForm = () => ({
|
|
|
9414
9433
|
createdAt: new Date().toISOString(),
|
|
9415
9434
|
updatedAt: new Date().toISOString(),
|
|
9416
9435
|
});
|
|
9436
|
+
const MAX_HISTORY = 50;
|
|
9437
|
+
const pushToHistory = (state) => {
|
|
9438
|
+
const newHistory = state.history.slice(0, state.historyIndex + 1);
|
|
9439
|
+
newHistory.push(JSON.parse(JSON.stringify(state.form)));
|
|
9440
|
+
if (newHistory.length > MAX_HISTORY) {
|
|
9441
|
+
newHistory.shift();
|
|
9442
|
+
}
|
|
9443
|
+
return { history: newHistory, historyIndex: newHistory.length - 1 };
|
|
9444
|
+
};
|
|
9445
|
+
const initialForm = createDemoForm();
|
|
9417
9446
|
const useFormStore = create((set, get) => ({
|
|
9418
|
-
form:
|
|
9447
|
+
form: initialForm,
|
|
9419
9448
|
selection: { type: null },
|
|
9420
9449
|
isPreviewMode: false,
|
|
9421
9450
|
currentStepIndex: 0,
|
|
9422
9451
|
currentLanguage: 'en',
|
|
9423
9452
|
formValues: {},
|
|
9424
9453
|
canvasWidth: 'full',
|
|
9425
|
-
history: [],
|
|
9426
|
-
historyIndex:
|
|
9454
|
+
history: [JSON.parse(JSON.stringify(initialForm))],
|
|
9455
|
+
historyIndex: 0,
|
|
9427
9456
|
setCanvasWidth: (width) => set({ canvasWidth: width }),
|
|
9428
9457
|
undo: () => {
|
|
9429
9458
|
const state = get();
|
|
@@ -9460,75 +9489,87 @@ const useFormStore = create((set, get) => ({
|
|
|
9460
9489
|
selection: { type: null },
|
|
9461
9490
|
formValues: {},
|
|
9462
9491
|
})),
|
|
9463
|
-
clearForm: () =>
|
|
9464
|
-
|
|
9465
|
-
|
|
9466
|
-
|
|
9467
|
-
|
|
9468
|
-
|
|
9492
|
+
clearForm: () => {
|
|
9493
|
+
const newForm = createEmptyForm();
|
|
9494
|
+
return set({
|
|
9495
|
+
form: newForm,
|
|
9496
|
+
selection: { type: null },
|
|
9497
|
+
currentStepIndex: 0,
|
|
9498
|
+
formValues: {},
|
|
9499
|
+
history: [JSON.parse(JSON.stringify(newForm))],
|
|
9500
|
+
historyIndex: 0,
|
|
9501
|
+
});
|
|
9502
|
+
},
|
|
9469
9503
|
loadForm: (form) => set({
|
|
9470
9504
|
form,
|
|
9471
9505
|
selection: { type: null },
|
|
9472
9506
|
currentStepIndex: 0,
|
|
9473
9507
|
formValues: {},
|
|
9508
|
+
history: [JSON.parse(JSON.stringify(form))],
|
|
9509
|
+
historyIndex: 0,
|
|
9474
9510
|
}),
|
|
9475
9511
|
addRow: (columns = 1, stepId) => set((state) => {
|
|
9476
9512
|
const newRow = createEmptyRow(columns);
|
|
9513
|
+
let newForm;
|
|
9477
9514
|
if (state.form.isMultiStep && state.form.steps?.length) {
|
|
9478
9515
|
const targetStepId = stepId || state.form.steps[state.currentStepIndex]?.id;
|
|
9479
|
-
|
|
9480
|
-
form
|
|
9481
|
-
|
|
9482
|
-
|
|
9483
|
-
updatedAt: new Date().toISOString(),
|
|
9484
|
-
},
|
|
9516
|
+
newForm = {
|
|
9517
|
+
...state.form,
|
|
9518
|
+
steps: state.form.steps.map(s => s.id === targetStepId ? { ...s, rows: [...s.rows, newRow] } : s),
|
|
9519
|
+
updatedAt: new Date().toISOString(),
|
|
9485
9520
|
};
|
|
9486
9521
|
}
|
|
9487
|
-
|
|
9488
|
-
|
|
9522
|
+
else {
|
|
9523
|
+
newForm = {
|
|
9489
9524
|
...state.form,
|
|
9490
9525
|
rows: [...state.form.rows, newRow],
|
|
9491
9526
|
updatedAt: new Date().toISOString(),
|
|
9492
|
-
}
|
|
9493
|
-
}
|
|
9527
|
+
};
|
|
9528
|
+
}
|
|
9529
|
+
const historyUpdate = pushToHistory({ form: newForm, history: state.history, historyIndex: state.historyIndex });
|
|
9530
|
+
return { form: newForm, ...historyUpdate };
|
|
9494
9531
|
}),
|
|
9495
9532
|
updateRow: (rowId, updates) => set((state) => {
|
|
9496
9533
|
const updateRows = (rows) => rows.map((row) => (row.id === rowId ? { ...row, ...updates } : row));
|
|
9534
|
+
let newForm;
|
|
9497
9535
|
if (state.form.isMultiStep && state.form.steps?.length) {
|
|
9498
|
-
|
|
9499
|
-
form
|
|
9500
|
-
|
|
9501
|
-
|
|
9502
|
-
updatedAt: new Date().toISOString(),
|
|
9503
|
-
},
|
|
9536
|
+
newForm = {
|
|
9537
|
+
...state.form,
|
|
9538
|
+
steps: state.form.steps.map(s => ({ ...s, rows: updateRows(s.rows) })),
|
|
9539
|
+
updatedAt: new Date().toISOString(),
|
|
9504
9540
|
};
|
|
9505
9541
|
}
|
|
9506
|
-
|
|
9507
|
-
|
|
9542
|
+
else {
|
|
9543
|
+
newForm = {
|
|
9508
9544
|
...state.form,
|
|
9509
9545
|
rows: updateRows(state.form.rows),
|
|
9510
9546
|
updatedAt: new Date().toISOString(),
|
|
9511
|
-
}
|
|
9512
|
-
}
|
|
9547
|
+
};
|
|
9548
|
+
}
|
|
9549
|
+
const historyUpdate = pushToHistory({ form: newForm, history: state.history, historyIndex: state.historyIndex });
|
|
9550
|
+
return { form: newForm, ...historyUpdate };
|
|
9513
9551
|
}),
|
|
9514
9552
|
deleteRow: (rowId) => set((state) => {
|
|
9515
9553
|
const filterRows = (rows) => rows.filter((row) => row.id !== rowId);
|
|
9554
|
+
let newForm;
|
|
9516
9555
|
if (state.form.isMultiStep && state.form.steps?.length) {
|
|
9517
|
-
|
|
9518
|
-
form
|
|
9519
|
-
|
|
9520
|
-
|
|
9521
|
-
updatedAt: new Date().toISOString(),
|
|
9522
|
-
},
|
|
9523
|
-
selection: state.selection.rowId === rowId ? { type: null } : state.selection,
|
|
9556
|
+
newForm = {
|
|
9557
|
+
...state.form,
|
|
9558
|
+
steps: state.form.steps.map(s => ({ ...s, rows: filterRows(s.rows) })),
|
|
9559
|
+
updatedAt: new Date().toISOString(),
|
|
9524
9560
|
};
|
|
9525
9561
|
}
|
|
9526
|
-
|
|
9527
|
-
|
|
9562
|
+
else {
|
|
9563
|
+
newForm = {
|
|
9528
9564
|
...state.form,
|
|
9529
9565
|
rows: filterRows(state.form.rows),
|
|
9530
9566
|
updatedAt: new Date().toISOString(),
|
|
9531
|
-
}
|
|
9567
|
+
};
|
|
9568
|
+
}
|
|
9569
|
+
const historyUpdate = pushToHistory({ form: newForm, history: state.history, historyIndex: state.historyIndex });
|
|
9570
|
+
return {
|
|
9571
|
+
form: newForm,
|
|
9572
|
+
...historyUpdate,
|
|
9532
9573
|
selection: state.selection.rowId === rowId ? { type: null } : state.selection,
|
|
9533
9574
|
};
|
|
9534
9575
|
}),
|
|
@@ -9539,18 +9580,19 @@ const useFormStore = create((set, get) => ({
|
|
|
9539
9580
|
newRows.splice(toIndex, 0, removed);
|
|
9540
9581
|
return newRows;
|
|
9541
9582
|
};
|
|
9583
|
+
let newForm;
|
|
9542
9584
|
if (state.form.isMultiStep && stepId) {
|
|
9543
|
-
|
|
9544
|
-
form
|
|
9545
|
-
|
|
9546
|
-
|
|
9547
|
-
updatedAt: new Date().toISOString(),
|
|
9548
|
-
},
|
|
9585
|
+
newForm = {
|
|
9586
|
+
...state.form,
|
|
9587
|
+
steps: state.form.steps?.map(s => s.id === stepId ? { ...s, rows: moveInRows(s.rows) } : s),
|
|
9588
|
+
updatedAt: new Date().toISOString(),
|
|
9549
9589
|
};
|
|
9550
9590
|
}
|
|
9551
|
-
|
|
9552
|
-
|
|
9553
|
-
}
|
|
9591
|
+
else {
|
|
9592
|
+
newForm = { ...state.form, rows: moveInRows(state.form.rows), updatedAt: new Date().toISOString() };
|
|
9593
|
+
}
|
|
9594
|
+
const historyUpdate = pushToHistory({ form: newForm, history: state.history, historyIndex: state.historyIndex });
|
|
9595
|
+
return { form: newForm, ...historyUpdate };
|
|
9554
9596
|
}),
|
|
9555
9597
|
addColumn: (rowId) => set((state) => {
|
|
9556
9598
|
const updateRows = (rows) => rows.map((row) => {
|
|
@@ -9566,22 +9608,23 @@ const useFormStore = create((set, get) => ({
|
|
|
9566
9608
|
],
|
|
9567
9609
|
};
|
|
9568
9610
|
});
|
|
9611
|
+
let newForm;
|
|
9569
9612
|
if (state.form.isMultiStep && state.form.steps?.length) {
|
|
9570
|
-
|
|
9571
|
-
form
|
|
9572
|
-
|
|
9573
|
-
|
|
9574
|
-
updatedAt: new Date().toISOString(),
|
|
9575
|
-
},
|
|
9613
|
+
newForm = {
|
|
9614
|
+
...state.form,
|
|
9615
|
+
steps: state.form.steps.map(s => ({ ...s, rows: updateRows(s.rows) })),
|
|
9616
|
+
updatedAt: new Date().toISOString(),
|
|
9576
9617
|
};
|
|
9577
9618
|
}
|
|
9578
|
-
|
|
9579
|
-
|
|
9619
|
+
else {
|
|
9620
|
+
newForm = {
|
|
9580
9621
|
...state.form,
|
|
9581
9622
|
rows: updateRows(state.form.rows),
|
|
9582
9623
|
updatedAt: new Date().toISOString(),
|
|
9583
|
-
}
|
|
9584
|
-
}
|
|
9624
|
+
};
|
|
9625
|
+
}
|
|
9626
|
+
const historyUpdate = pushToHistory({ form: newForm, history: state.history, historyIndex: state.historyIndex });
|
|
9627
|
+
return { form: newForm, ...historyUpdate };
|
|
9585
9628
|
}),
|
|
9586
9629
|
updateColumn: (rowId, columnId, updates) => set((state) => {
|
|
9587
9630
|
const updateRows = (rows) => rows.map((row) => row.id === rowId
|
|
@@ -9590,22 +9633,23 @@ const useFormStore = create((set, get) => ({
|
|
|
9590
9633
|
columns: row.columns.map((col) => col.id === columnId ? { ...col, ...updates } : col),
|
|
9591
9634
|
}
|
|
9592
9635
|
: row);
|
|
9636
|
+
let newForm;
|
|
9593
9637
|
if (state.form.isMultiStep && state.form.steps?.length) {
|
|
9594
|
-
|
|
9595
|
-
form
|
|
9596
|
-
|
|
9597
|
-
|
|
9598
|
-
updatedAt: new Date().toISOString(),
|
|
9599
|
-
},
|
|
9638
|
+
newForm = {
|
|
9639
|
+
...state.form,
|
|
9640
|
+
steps: state.form.steps.map(s => ({ ...s, rows: updateRows(s.rows) })),
|
|
9641
|
+
updatedAt: new Date().toISOString(),
|
|
9600
9642
|
};
|
|
9601
9643
|
}
|
|
9602
|
-
|
|
9603
|
-
|
|
9644
|
+
else {
|
|
9645
|
+
newForm = {
|
|
9604
9646
|
...state.form,
|
|
9605
9647
|
rows: updateRows(state.form.rows),
|
|
9606
9648
|
updatedAt: new Date().toISOString(),
|
|
9607
|
-
}
|
|
9608
|
-
}
|
|
9649
|
+
};
|
|
9650
|
+
}
|
|
9651
|
+
const historyUpdate = pushToHistory({ form: newForm, history: state.history, historyIndex: state.historyIndex });
|
|
9652
|
+
return { form: newForm, ...historyUpdate };
|
|
9609
9653
|
}),
|
|
9610
9654
|
deleteColumn: (rowId, columnId) => set((state) => {
|
|
9611
9655
|
const updateRows = (rows) => rows.map((row) => {
|
|
@@ -9620,22 +9664,25 @@ const useFormStore = create((set, get) => ({
|
|
|
9620
9664
|
columns: newColumns.map((col) => ({ ...col, width })),
|
|
9621
9665
|
};
|
|
9622
9666
|
});
|
|
9667
|
+
let newForm;
|
|
9623
9668
|
if (state.form.isMultiStep && state.form.steps?.length) {
|
|
9624
|
-
|
|
9625
|
-
form
|
|
9626
|
-
|
|
9627
|
-
|
|
9628
|
-
updatedAt: new Date().toISOString(),
|
|
9629
|
-
},
|
|
9630
|
-
selection: state.selection.columnId === columnId ? { type: null } : state.selection,
|
|
9669
|
+
newForm = {
|
|
9670
|
+
...state.form,
|
|
9671
|
+
steps: state.form.steps.map(s => ({ ...s, rows: updateRows(s.rows) })),
|
|
9672
|
+
updatedAt: new Date().toISOString(),
|
|
9631
9673
|
};
|
|
9632
9674
|
}
|
|
9633
|
-
|
|
9634
|
-
|
|
9675
|
+
else {
|
|
9676
|
+
newForm = {
|
|
9635
9677
|
...state.form,
|
|
9636
9678
|
rows: updateRows(state.form.rows),
|
|
9637
9679
|
updatedAt: new Date().toISOString(),
|
|
9638
|
-
}
|
|
9680
|
+
};
|
|
9681
|
+
}
|
|
9682
|
+
const historyUpdate = pushToHistory({ form: newForm, history: state.history, historyIndex: state.historyIndex });
|
|
9683
|
+
return {
|
|
9684
|
+
form: newForm,
|
|
9685
|
+
...historyUpdate,
|
|
9639
9686
|
selection: state.selection.columnId === columnId ? { type: null } : state.selection,
|
|
9640
9687
|
};
|
|
9641
9688
|
}),
|
|
@@ -9664,22 +9711,23 @@ const useFormStore = create((set, get) => ({
|
|
|
9664
9711
|
columns: newColumns.map((col) => ({ ...col, width })),
|
|
9665
9712
|
};
|
|
9666
9713
|
});
|
|
9714
|
+
let newForm;
|
|
9667
9715
|
if (state.form.isMultiStep && state.form.steps?.length) {
|
|
9668
|
-
|
|
9669
|
-
form
|
|
9670
|
-
|
|
9671
|
-
|
|
9672
|
-
updatedAt: new Date().toISOString(),
|
|
9673
|
-
},
|
|
9716
|
+
newForm = {
|
|
9717
|
+
...state.form,
|
|
9718
|
+
steps: state.form.steps.map(s => ({ ...s, rows: updateRows(s.rows) })),
|
|
9719
|
+
updatedAt: new Date().toISOString(),
|
|
9674
9720
|
};
|
|
9675
9721
|
}
|
|
9676
|
-
|
|
9677
|
-
|
|
9722
|
+
else {
|
|
9723
|
+
newForm = {
|
|
9678
9724
|
...state.form,
|
|
9679
9725
|
rows: updateRows(state.form.rows),
|
|
9680
9726
|
updatedAt: new Date().toISOString(),
|
|
9681
|
-
}
|
|
9682
|
-
}
|
|
9727
|
+
};
|
|
9728
|
+
}
|
|
9729
|
+
const historyUpdate = pushToHistory({ form: newForm, history: state.history, historyIndex: state.historyIndex });
|
|
9730
|
+
return { form: newForm, ...historyUpdate };
|
|
9683
9731
|
}),
|
|
9684
9732
|
addField: (rowId, columnId, field) => set((state) => {
|
|
9685
9733
|
const updateRows = (rows) => rows.map((row) => row.id === rowId
|
|
@@ -9693,22 +9741,23 @@ const useFormStore = create((set, get) => ({
|
|
|
9693
9741
|
: col),
|
|
9694
9742
|
}
|
|
9695
9743
|
: row);
|
|
9744
|
+
let newForm;
|
|
9696
9745
|
if (state.form.isMultiStep && state.form.steps?.length) {
|
|
9697
|
-
|
|
9698
|
-
form
|
|
9699
|
-
|
|
9700
|
-
|
|
9701
|
-
updatedAt: new Date().toISOString(),
|
|
9702
|
-
},
|
|
9746
|
+
newForm = {
|
|
9747
|
+
...state.form,
|
|
9748
|
+
steps: state.form.steps.map(s => ({ ...s, rows: updateRows(s.rows) })),
|
|
9749
|
+
updatedAt: new Date().toISOString(),
|
|
9703
9750
|
};
|
|
9704
9751
|
}
|
|
9705
|
-
|
|
9706
|
-
|
|
9752
|
+
else {
|
|
9753
|
+
newForm = {
|
|
9707
9754
|
...state.form,
|
|
9708
9755
|
rows: updateRows(state.form.rows),
|
|
9709
9756
|
updatedAt: new Date().toISOString(),
|
|
9710
|
-
}
|
|
9711
|
-
}
|
|
9757
|
+
};
|
|
9758
|
+
}
|
|
9759
|
+
const historyUpdate = pushToHistory({ form: newForm, history: state.history, historyIndex: state.historyIndex });
|
|
9760
|
+
return { form: newForm, ...historyUpdate };
|
|
9712
9761
|
}),
|
|
9713
9762
|
addRowWithField: (field) => set((state) => {
|
|
9714
9763
|
const newRow = {
|
|
@@ -9719,25 +9768,26 @@ const useFormStore = create((set, get) => ({
|
|
|
9719
9768
|
fields: [{ ...field, id: nanoid() }],
|
|
9720
9769
|
}],
|
|
9721
9770
|
};
|
|
9771
|
+
let newForm;
|
|
9722
9772
|
if (state.form.isMultiStep && state.form.steps?.length) {
|
|
9723
9773
|
const currentStep = state.form.steps[state.currentStepIndex];
|
|
9724
|
-
|
|
9725
|
-
form
|
|
9726
|
-
|
|
9727
|
-
|
|
9728
|
-
|
|
9729
|
-
|
|
9730
|
-
updatedAt: new Date().toISOString(),
|
|
9731
|
-
},
|
|
9774
|
+
newForm = {
|
|
9775
|
+
...state.form,
|
|
9776
|
+
steps: state.form.steps.map(s => s.id === currentStep.id
|
|
9777
|
+
? { ...s, rows: [...s.rows, newRow] }
|
|
9778
|
+
: s),
|
|
9779
|
+
updatedAt: new Date().toISOString(),
|
|
9732
9780
|
};
|
|
9733
9781
|
}
|
|
9734
|
-
|
|
9735
|
-
|
|
9782
|
+
else {
|
|
9783
|
+
newForm = {
|
|
9736
9784
|
...state.form,
|
|
9737
9785
|
rows: [...state.form.rows, newRow],
|
|
9738
9786
|
updatedAt: new Date().toISOString(),
|
|
9739
|
-
}
|
|
9740
|
-
}
|
|
9787
|
+
};
|
|
9788
|
+
}
|
|
9789
|
+
const historyUpdate = pushToHistory({ form: newForm, history: state.history, historyIndex: state.historyIndex });
|
|
9790
|
+
return { form: newForm, ...historyUpdate };
|
|
9741
9791
|
}),
|
|
9742
9792
|
addFieldToContainerDirect: (rowId, columnId, containerFieldId, field) => set((state) => {
|
|
9743
9793
|
const newField = { ...field, id: nanoid() };
|
|
@@ -9763,22 +9813,23 @@ const useFormStore = create((set, get) => ({
|
|
|
9763
9813
|
: col),
|
|
9764
9814
|
}
|
|
9765
9815
|
: row);
|
|
9816
|
+
let newForm;
|
|
9766
9817
|
if (state.form.isMultiStep && state.form.steps?.length) {
|
|
9767
|
-
|
|
9768
|
-
form
|
|
9769
|
-
|
|
9770
|
-
|
|
9771
|
-
updatedAt: new Date().toISOString(),
|
|
9772
|
-
},
|
|
9818
|
+
newForm = {
|
|
9819
|
+
...state.form,
|
|
9820
|
+
steps: state.form.steps.map(s => ({ ...s, rows: updateRows(s.rows) })),
|
|
9821
|
+
updatedAt: new Date().toISOString(),
|
|
9773
9822
|
};
|
|
9774
9823
|
}
|
|
9775
|
-
|
|
9776
|
-
|
|
9824
|
+
else {
|
|
9825
|
+
newForm = {
|
|
9777
9826
|
...state.form,
|
|
9778
9827
|
rows: updateRows(state.form.rows),
|
|
9779
9828
|
updatedAt: new Date().toISOString(),
|
|
9780
|
-
}
|
|
9781
|
-
}
|
|
9829
|
+
};
|
|
9830
|
+
}
|
|
9831
|
+
const historyUpdate = pushToHistory({ form: newForm, history: state.history, historyIndex: state.historyIndex });
|
|
9832
|
+
return { form: newForm, ...historyUpdate };
|
|
9782
9833
|
}),
|
|
9783
9834
|
addFieldToContainer: (rowId, columnId, containerFieldId, containerRowId, containerColumnId, field) => set((state) => {
|
|
9784
9835
|
const newField = { ...field, id: nanoid() };
|
|
@@ -9814,22 +9865,23 @@ const useFormStore = create((set, get) => ({
|
|
|
9814
9865
|
: col),
|
|
9815
9866
|
}
|
|
9816
9867
|
: row);
|
|
9868
|
+
let newForm;
|
|
9817
9869
|
if (state.form.isMultiStep && state.form.steps?.length) {
|
|
9818
|
-
|
|
9819
|
-
form
|
|
9820
|
-
|
|
9821
|
-
|
|
9822
|
-
updatedAt: new Date().toISOString(),
|
|
9823
|
-
},
|
|
9870
|
+
newForm = {
|
|
9871
|
+
...state.form,
|
|
9872
|
+
steps: state.form.steps.map(s => ({ ...s, rows: updateRows(s.rows) })),
|
|
9873
|
+
updatedAt: new Date().toISOString(),
|
|
9824
9874
|
};
|
|
9825
9875
|
}
|
|
9826
|
-
|
|
9827
|
-
|
|
9876
|
+
else {
|
|
9877
|
+
newForm = {
|
|
9828
9878
|
...state.form,
|
|
9829
9879
|
rows: updateRows(state.form.rows),
|
|
9830
9880
|
updatedAt: new Date().toISOString(),
|
|
9831
|
-
}
|
|
9832
|
-
}
|
|
9881
|
+
};
|
|
9882
|
+
}
|
|
9883
|
+
const historyUpdate = pushToHistory({ form: newForm, history: state.history, historyIndex: state.historyIndex });
|
|
9884
|
+
return { form: newForm, ...historyUpdate };
|
|
9833
9885
|
}),
|
|
9834
9886
|
updateField: (rowId, columnId, fieldId, updates) => set((state) => {
|
|
9835
9887
|
const updateFieldInContainer = (field) => {
|
|
@@ -9894,22 +9946,23 @@ const useFormStore = create((set, get) => ({
|
|
|
9894
9946
|
: col),
|
|
9895
9947
|
}
|
|
9896
9948
|
: row);
|
|
9949
|
+
let newForm;
|
|
9897
9950
|
if (state.form.isMultiStep && state.form.steps?.length) {
|
|
9898
|
-
|
|
9899
|
-
form
|
|
9900
|
-
|
|
9901
|
-
|
|
9902
|
-
updatedAt: new Date().toISOString(),
|
|
9903
|
-
},
|
|
9951
|
+
newForm = {
|
|
9952
|
+
...state.form,
|
|
9953
|
+
steps: state.form.steps.map(s => ({ ...s, rows: updateRows(s.rows) })),
|
|
9954
|
+
updatedAt: new Date().toISOString(),
|
|
9904
9955
|
};
|
|
9905
9956
|
}
|
|
9906
|
-
|
|
9907
|
-
|
|
9957
|
+
else {
|
|
9958
|
+
newForm = {
|
|
9908
9959
|
...state.form,
|
|
9909
9960
|
rows: updateRows(state.form.rows),
|
|
9910
9961
|
updatedAt: new Date().toISOString(),
|
|
9911
|
-
}
|
|
9912
|
-
}
|
|
9962
|
+
};
|
|
9963
|
+
}
|
|
9964
|
+
const historyUpdate = pushToHistory({ form: newForm, history: state.history, historyIndex: state.historyIndex });
|
|
9965
|
+
return { form: newForm, ...historyUpdate };
|
|
9913
9966
|
}),
|
|
9914
9967
|
deleteField: (rowId, columnId, fieldId) => set((state) => {
|
|
9915
9968
|
const updateRows = (rows) => rows.map((row) => row.id === rowId
|
|
@@ -9940,22 +9993,25 @@ const useFormStore = create((set, get) => ({
|
|
|
9940
9993
|
: col),
|
|
9941
9994
|
}
|
|
9942
9995
|
: row);
|
|
9996
|
+
let newForm;
|
|
9943
9997
|
if (state.form.isMultiStep && state.form.steps?.length) {
|
|
9944
|
-
|
|
9945
|
-
form
|
|
9946
|
-
|
|
9947
|
-
|
|
9948
|
-
updatedAt: new Date().toISOString(),
|
|
9949
|
-
},
|
|
9950
|
-
selection: state.selection.fieldId === fieldId ? { type: null } : state.selection,
|
|
9998
|
+
newForm = {
|
|
9999
|
+
...state.form,
|
|
10000
|
+
steps: state.form.steps.map(s => ({ ...s, rows: updateRows(s.rows) })),
|
|
10001
|
+
updatedAt: new Date().toISOString(),
|
|
9951
10002
|
};
|
|
9952
10003
|
}
|
|
9953
|
-
|
|
9954
|
-
|
|
10004
|
+
else {
|
|
10005
|
+
newForm = {
|
|
9955
10006
|
...state.form,
|
|
9956
10007
|
rows: updateRows(state.form.rows),
|
|
9957
10008
|
updatedAt: new Date().toISOString(),
|
|
9958
|
-
}
|
|
10009
|
+
};
|
|
10010
|
+
}
|
|
10011
|
+
const historyUpdate = pushToHistory({ form: newForm, history: state.history, historyIndex: state.historyIndex });
|
|
10012
|
+
return {
|
|
10013
|
+
form: newForm,
|
|
10014
|
+
...historyUpdate,
|
|
9959
10015
|
selection: state.selection.fieldId === fieldId ? { type: null } : state.selection,
|
|
9960
10016
|
};
|
|
9961
10017
|
}),
|
|
@@ -9993,22 +10049,23 @@ const useFormStore = create((set, get) => ({
|
|
|
9993
10049
|
}
|
|
9994
10050
|
return row;
|
|
9995
10051
|
});
|
|
10052
|
+
let newForm;
|
|
9996
10053
|
if (state.form.isMultiStep && state.form.steps?.length) {
|
|
9997
10054
|
let newSteps = state.form.steps.map(s => ({ ...s, rows: extractField(s.rows) }));
|
|
9998
10055
|
if (!movedField)
|
|
9999
10056
|
return state;
|
|
10000
10057
|
newSteps = newSteps.map(s => ({ ...s, rows: insertField(s.rows) }));
|
|
10001
|
-
|
|
10002
|
-
form: { ...state.form, steps: newSteps, updatedAt: new Date().toISOString() },
|
|
10003
|
-
};
|
|
10058
|
+
newForm = { ...state.form, steps: newSteps, updatedAt: new Date().toISOString() };
|
|
10004
10059
|
}
|
|
10005
|
-
|
|
10006
|
-
|
|
10007
|
-
|
|
10008
|
-
|
|
10009
|
-
|
|
10010
|
-
|
|
10011
|
-
}
|
|
10060
|
+
else {
|
|
10061
|
+
const rows = extractField(state.form.rows);
|
|
10062
|
+
if (!movedField)
|
|
10063
|
+
return state;
|
|
10064
|
+
const finalRows = insertField(rows);
|
|
10065
|
+
newForm = { ...state.form, rows: finalRows, updatedAt: new Date().toISOString() };
|
|
10066
|
+
}
|
|
10067
|
+
const historyUpdate = pushToHistory({ form: newForm, history: state.history, historyIndex: state.historyIndex });
|
|
10068
|
+
return { form: newForm, ...historyUpdate };
|
|
10012
10069
|
}),
|
|
10013
10070
|
addStep: () => set((state) => {
|
|
10014
10071
|
const newStep = createEmptyStep(state.form.steps?.length || 0);
|
|
@@ -10021,22 +10078,27 @@ const useFormStore = create((set, get) => ({
|
|
|
10021
10078
|
},
|
|
10022
10079
|
};
|
|
10023
10080
|
}),
|
|
10024
|
-
updateStep: (stepId, updates) => set((state) =>
|
|
10025
|
-
|
|
10081
|
+
updateStep: (stepId, updates) => set((state) => {
|
|
10082
|
+
const newForm = {
|
|
10026
10083
|
...state.form,
|
|
10027
10084
|
steps: state.form.steps?.map(s => s.id === stepId ? { ...s, ...updates } : s),
|
|
10028
10085
|
updatedAt: new Date().toISOString(),
|
|
10029
|
-
}
|
|
10030
|
-
|
|
10086
|
+
};
|
|
10087
|
+
const historyUpdate = pushToHistory({ form: newForm, history: state.history, historyIndex: state.historyIndex });
|
|
10088
|
+
return { form: newForm, ...historyUpdate };
|
|
10089
|
+
}),
|
|
10031
10090
|
deleteStep: (stepId) => set((state) => {
|
|
10032
10091
|
const newSteps = state.form.steps?.filter(s => s.id !== stepId) || [];
|
|
10092
|
+
const newForm = {
|
|
10093
|
+
...state.form,
|
|
10094
|
+
steps: newSteps,
|
|
10095
|
+
isMultiStep: newSteps.length > 0,
|
|
10096
|
+
updatedAt: new Date().toISOString(),
|
|
10097
|
+
};
|
|
10098
|
+
const historyUpdate = pushToHistory({ form: newForm, history: state.history, historyIndex: state.historyIndex });
|
|
10033
10099
|
return {
|
|
10034
|
-
form:
|
|
10035
|
-
|
|
10036
|
-
steps: newSteps,
|
|
10037
|
-
isMultiStep: newSteps.length > 0,
|
|
10038
|
-
updatedAt: new Date().toISOString(),
|
|
10039
|
-
},
|
|
10100
|
+
form: newForm,
|
|
10101
|
+
...historyUpdate,
|
|
10040
10102
|
currentStepIndex: Math.min(state.currentStepIndex, Math.max(0, newSteps.length - 1)),
|
|
10041
10103
|
selection: state.selection.stepId === stepId ? { type: null } : state.selection,
|
|
10042
10104
|
};
|
|
@@ -10045,9 +10107,9 @@ const useFormStore = create((set, get) => ({
|
|
|
10045
10107
|
const steps = [...(state.form.steps || [])];
|
|
10046
10108
|
const [removed] = steps.splice(fromIndex, 1);
|
|
10047
10109
|
steps.splice(toIndex, 0, removed);
|
|
10048
|
-
|
|
10049
|
-
|
|
10050
|
-
};
|
|
10110
|
+
const newForm = { ...state.form, steps, updatedAt: new Date().toISOString() };
|
|
10111
|
+
const historyUpdate = pushToHistory({ form: newForm, history: state.history, historyIndex: state.historyIndex });
|
|
10112
|
+
return { form: newForm, ...historyUpdate };
|
|
10051
10113
|
}),
|
|
10052
10114
|
setCurrentStep: (index) => set((state) => ({
|
|
10053
10115
|
currentStepIndex: Math.max(0, Math.min(index, (state.form.steps?.length || 1) - 1)),
|
|
@@ -10059,33 +10121,30 @@ const useFormStore = create((set, get) => ({
|
|
|
10059
10121
|
currentStepIndex: Math.max(0, state.currentStepIndex - 1),
|
|
10060
10122
|
})),
|
|
10061
10123
|
toggleMultiStep: () => set((state) => {
|
|
10124
|
+
let newForm;
|
|
10062
10125
|
if (state.form.isMultiStep) {
|
|
10063
10126
|
const allRows = state.form.steps?.flatMap(s => s.rows) || [];
|
|
10064
|
-
|
|
10065
|
-
form
|
|
10066
|
-
|
|
10067
|
-
|
|
10068
|
-
|
|
10069
|
-
|
|
10070
|
-
updatedAt: new Date().toISOString(),
|
|
10071
|
-
},
|
|
10072
|
-
currentStepIndex: 0,
|
|
10127
|
+
newForm = {
|
|
10128
|
+
...state.form,
|
|
10129
|
+
isMultiStep: false,
|
|
10130
|
+
rows: [...state.form.rows, ...allRows],
|
|
10131
|
+
steps: [],
|
|
10132
|
+
updatedAt: new Date().toISOString(),
|
|
10073
10133
|
};
|
|
10074
10134
|
}
|
|
10075
10135
|
else {
|
|
10076
10136
|
const step = createEmptyStep(0);
|
|
10077
10137
|
step.rows = [...state.form.rows];
|
|
10078
|
-
|
|
10079
|
-
form
|
|
10080
|
-
|
|
10081
|
-
|
|
10082
|
-
|
|
10083
|
-
|
|
10084
|
-
updatedAt: new Date().toISOString(),
|
|
10085
|
-
},
|
|
10086
|
-
currentStepIndex: 0,
|
|
10138
|
+
newForm = {
|
|
10139
|
+
...state.form,
|
|
10140
|
+
isMultiStep: true,
|
|
10141
|
+
rows: [],
|
|
10142
|
+
steps: [step],
|
|
10143
|
+
updatedAt: new Date().toISOString(),
|
|
10087
10144
|
};
|
|
10088
10145
|
}
|
|
10146
|
+
const historyUpdate = pushToHistory({ form: newForm, history: state.history, historyIndex: state.historyIndex });
|
|
10147
|
+
return { form: newForm, ...historyUpdate, currentStepIndex: 0 };
|
|
10089
10148
|
}),
|
|
10090
10149
|
updateSubmissionConfig: (config) => set((state) => ({
|
|
10091
10150
|
form: {
|
|
@@ -18584,7 +18643,7 @@ function MultiSelectField({ value, onChange, options, disabled, maxItems, classN
|
|
|
18584
18643
|
}
|
|
18585
18644
|
function CanvasField({ field, rowId, columnId, isPreviewMode: externalPreviewMode, getFieldValue: externalGetFieldValue, setFieldValue: externalSetFieldValue, isInsideContainer = false, fieldSize = 'normal' }) {
|
|
18586
18645
|
const store = useFormStore();
|
|
18587
|
-
const { selection, setSelection, deleteField, evaluateCondition, clearFormValues } = store;
|
|
18646
|
+
const { selection, setSelection, deleteField, evaluateCondition, clearFormValues, formValues } = store;
|
|
18588
18647
|
const isPreviewMode = externalPreviewMode ?? store.isPreviewMode;
|
|
18589
18648
|
const getFieldValue = externalGetFieldValue ?? store.getFieldValue;
|
|
18590
18649
|
const setFieldValue = externalSetFieldValue ?? store.setFieldValue;
|
|
@@ -18608,6 +18667,15 @@ function CanvasField({ field, rowId, columnId, isPreviewMode: externalPreviewMod
|
|
|
18608
18667
|
// Reset rating, slider values
|
|
18609
18668
|
setRatingValue(field.props.defaultValue ? parseInt(field.props.defaultValue) || 0 : 0);
|
|
18610
18669
|
setSliderValue([field.props.defaultValue ? parseInt(field.props.defaultValue) || 50 : 50]);
|
|
18670
|
+
// Auto focus when entering preview mode
|
|
18671
|
+
if (field.props.autoFocus) {
|
|
18672
|
+
setTimeout(() => {
|
|
18673
|
+
const input = document.querySelector(`[data-testid^="field-"][data-testid$="-${field.id}"]`);
|
|
18674
|
+
if (input && input.focus) {
|
|
18675
|
+
input.focus();
|
|
18676
|
+
}
|
|
18677
|
+
}, 100);
|
|
18678
|
+
}
|
|
18611
18679
|
}
|
|
18612
18680
|
}, [isPreviewMode]);
|
|
18613
18681
|
const isSelected = selection.type === 'field' &&
|
|
@@ -18640,8 +18708,59 @@ function CanvasField({ field, rowId, columnId, isPreviewMode: externalPreviewMod
|
|
|
18640
18708
|
break;
|
|
18641
18709
|
}
|
|
18642
18710
|
return { visible, enabled, required };
|
|
18643
|
-
}, [field.conditionalLogic, field.validation?.required, evaluateCondition]);
|
|
18644
|
-
|
|
18711
|
+
}, [field.conditionalLogic, field.validation?.required, evaluateCondition, formValues]);
|
|
18712
|
+
const handleChange = useCallback((newValue) => {
|
|
18713
|
+
setLocalValue(newValue);
|
|
18714
|
+
setFieldValue(field.props.key, newValue);
|
|
18715
|
+
if (touched) {
|
|
18716
|
+
setError(validateField(newValue, field.validation, field.type));
|
|
18717
|
+
}
|
|
18718
|
+
}, [touched, field.validation, field.type, field.props.key, setFieldValue]);
|
|
18719
|
+
const handleBlur = useCallback(() => {
|
|
18720
|
+
setTouched(true);
|
|
18721
|
+
setError(validateField(localValue, field.validation, field.type));
|
|
18722
|
+
}, [localValue, field.validation, field.type]);
|
|
18723
|
+
// Execute field actions (onClick, onChange, onFocus, onBlur events)
|
|
18724
|
+
const executeActions = useCallback((eventType) => {
|
|
18725
|
+
if (!isPreviewMode || !field.events)
|
|
18726
|
+
return;
|
|
18727
|
+
const actions = field.events[eventType];
|
|
18728
|
+
if (!actions || actions.length === 0)
|
|
18729
|
+
return;
|
|
18730
|
+
actions.forEach((action) => {
|
|
18731
|
+
if (action.type === 'common') {
|
|
18732
|
+
switch (action.name) {
|
|
18733
|
+
case 'focusField':
|
|
18734
|
+
if (action.args?.targetFieldKey) {
|
|
18735
|
+
setTimeout(() => {
|
|
18736
|
+
const allInputs = document.querySelectorAll('[data-field-key]');
|
|
18737
|
+
allInputs.forEach((el) => {
|
|
18738
|
+
if (el.getAttribute('data-field-key') === action.args?.targetFieldKey) {
|
|
18739
|
+
el.focus();
|
|
18740
|
+
}
|
|
18741
|
+
});
|
|
18742
|
+
}, 50);
|
|
18743
|
+
}
|
|
18744
|
+
break;
|
|
18745
|
+
case 'clearField':
|
|
18746
|
+
setLocalValue('');
|
|
18747
|
+
setFieldValue(field.props.key, '');
|
|
18748
|
+
break;
|
|
18749
|
+
case 'showMessage':
|
|
18750
|
+
if (action.args?.message) {
|
|
18751
|
+
alert(action.args.message);
|
|
18752
|
+
}
|
|
18753
|
+
break;
|
|
18754
|
+
case 'reset':
|
|
18755
|
+
clearFormValues();
|
|
18756
|
+
break;
|
|
18757
|
+
}
|
|
18758
|
+
}
|
|
18759
|
+
});
|
|
18760
|
+
}, [isPreviewMode, field.events, field.props.key, setFieldValue, clearFormValues]);
|
|
18761
|
+
// Early return for hidden fields - MUST be after all hooks
|
|
18762
|
+
// Check both conditional visibility AND explicit hidden property
|
|
18763
|
+
if (isPreviewMode && (!conditionalState.visible || field.props.hidden)) {
|
|
18645
18764
|
return null;
|
|
18646
18765
|
}
|
|
18647
18766
|
const isFieldDisabled = field.props.disabled || !isPreviewMode || !conditionalState.enabled;
|
|
@@ -18656,18 +18775,10 @@ function CanvasField({ field, rowId, columnId, isPreviewMode: externalPreviewMod
|
|
|
18656
18775
|
fieldId: field.id,
|
|
18657
18776
|
});
|
|
18658
18777
|
}
|
|
18659
|
-
|
|
18660
|
-
|
|
18661
|
-
setLocalValue(newValue);
|
|
18662
|
-
setFieldValue(field.props.key, newValue);
|
|
18663
|
-
if (touched) {
|
|
18664
|
-
setError(validateField(newValue, field.validation, field.type));
|
|
18778
|
+
else {
|
|
18779
|
+
executeActions('onClick');
|
|
18665
18780
|
}
|
|
18666
|
-
}
|
|
18667
|
-
const handleBlur = useCallback(() => {
|
|
18668
|
-
setTouched(true);
|
|
18669
|
-
setError(validateField(localValue, field.validation, field.type));
|
|
18670
|
-
}, [localValue, field.validation, field.type]);
|
|
18781
|
+
};
|
|
18671
18782
|
const sizeClasses = {
|
|
18672
18783
|
small: 'h-8 text-sm',
|
|
18673
18784
|
medium: 'h-9',
|
|
@@ -18690,51 +18801,51 @@ function CanvasField({ field, rowId, columnId, isPreviewMode: externalPreviewMod
|
|
|
18690
18801
|
const errorClass = hasError ? 'border-destructive focus-visible:ring-destructive' : '';
|
|
18691
18802
|
switch (field.type) {
|
|
18692
18803
|
case 'input':
|
|
18693
|
-
return (jsx(Input, { placeholder: field.props.placeholder, disabled: isFieldDisabled, readOnly: field.props.readOnly, value: isPreviewMode ? localValue : (field.props.defaultValue || ''), onChange: (e) => isPreviewMode && handleChange(e.target.value), onBlur: handleBlur, tabIndex: tabIndexValue, className: `${inputSize} ${errorClass} ${field.customStyle?.inputClassName || ''}`, style: {
|
|
18804
|
+
return (jsx(Input, { placeholder: field.props.placeholder, disabled: isFieldDisabled, readOnly: field.props.readOnly, autoFocus: isPreviewMode && field.props.autoFocus, value: isPreviewMode ? localValue : (field.props.defaultValue || ''), onChange: (e) => isPreviewMode && handleChange(e.target.value), onBlur: handleBlur, tabIndex: tabIndexValue, className: `${inputSize} ${errorClass} ${field.customStyle?.inputClassName || ''}`, style: {
|
|
18694
18805
|
...(field.customStyle?.color ? { color: field.customStyle.color } : {}),
|
|
18695
18806
|
...(field.customStyle?.backgroundColor ? { backgroundColor: field.customStyle.backgroundColor } : {}),
|
|
18696
|
-
}, "data-testid": `field-input-${field.id}
|
|
18807
|
+
}, "data-testid": `field-input-${field.id}`, "data-field-key": field.props.key }));
|
|
18697
18808
|
case 'password':
|
|
18698
|
-
return (jsx(Input, { type: "password", placeholder: field.props.placeholder, disabled: isFieldDisabled, value: isPreviewMode ? localValue : '', onChange: (e) => isPreviewMode && handleChange(e.target.value), onBlur: handleBlur, tabIndex: tabIndexValue, className: `${inputSize} ${errorClass} ${field.customStyle?.inputClassName || ''}`, style: {
|
|
18809
|
+
return (jsx(Input, { type: "password", placeholder: field.props.placeholder, disabled: isFieldDisabled, autoFocus: isPreviewMode && field.props.autoFocus, value: isPreviewMode ? localValue : '', onChange: (e) => isPreviewMode && handleChange(e.target.value), onBlur: handleBlur, tabIndex: tabIndexValue, className: `${inputSize} ${errorClass} ${field.customStyle?.inputClassName || ''}`, style: {
|
|
18699
18810
|
...(field.customStyle?.color ? { color: field.customStyle.color } : {}),
|
|
18700
18811
|
...(field.customStyle?.backgroundColor ? { backgroundColor: field.customStyle.backgroundColor } : {}),
|
|
18701
|
-
}, "data-testid": `field-password-${field.id}
|
|
18812
|
+
}, "data-testid": `field-password-${field.id}`, "data-field-key": field.props.key }));
|
|
18702
18813
|
case 'phone':
|
|
18703
|
-
return (jsx(Input, { type: "tel", placeholder: field.props.placeholder || '+1 (555) 000-0000', disabled: isFieldDisabled, value: isPreviewMode ? localValue : (field.props.defaultValue || ''), onChange: (e) => isPreviewMode && handleChange(e.target.value), onBlur: handleBlur, tabIndex: tabIndexValue, className: `${inputSize} ${errorClass} ${field.customStyle?.inputClassName || ''}`, style: {
|
|
18814
|
+
return (jsx(Input, { type: "tel", placeholder: field.props.placeholder || '+1 (555) 000-0000', disabled: isFieldDisabled, autoFocus: isPreviewMode && field.props.autoFocus, "data-field-key": field.props.key, value: isPreviewMode ? localValue : (field.props.defaultValue || ''), onChange: (e) => isPreviewMode && handleChange(e.target.value), onBlur: handleBlur, tabIndex: tabIndexValue, className: `${inputSize} ${errorClass} ${field.customStyle?.inputClassName || ''}`, style: {
|
|
18704
18815
|
...(field.customStyle?.color ? { color: field.customStyle.color } : {}),
|
|
18705
18816
|
...(field.customStyle?.backgroundColor ? { backgroundColor: field.customStyle.backgroundColor } : {}),
|
|
18706
18817
|
}, "data-testid": `field-phone-${field.id}` }));
|
|
18707
18818
|
case 'url':
|
|
18708
|
-
return (jsx(Input, { type: "url", placeholder: field.props.placeholder || 'https://example.com', disabled: isFieldDisabled, value: isPreviewMode ? localValue : (field.props.defaultValue || ''), onChange: (e) => isPreviewMode && handleChange(e.target.value), onBlur: handleBlur, tabIndex: tabIndexValue, className: `${inputSize} ${errorClass} ${field.customStyle?.inputClassName || ''}`, style: {
|
|
18819
|
+
return (jsx(Input, { type: "url", placeholder: field.props.placeholder || 'https://example.com', disabled: isFieldDisabled, autoFocus: isPreviewMode && field.props.autoFocus, value: isPreviewMode ? localValue : (field.props.defaultValue || ''), onChange: (e) => isPreviewMode && handleChange(e.target.value), onBlur: handleBlur, tabIndex: tabIndexValue, className: `${inputSize} ${errorClass} ${field.customStyle?.inputClassName || ''}`, style: {
|
|
18709
18820
|
...(field.customStyle?.color ? { color: field.customStyle.color } : {}),
|
|
18710
18821
|
...(field.customStyle?.backgroundColor ? { backgroundColor: field.customStyle.backgroundColor } : {}),
|
|
18711
|
-
}, "data-testid": `field-url-${field.id}
|
|
18822
|
+
}, "data-testid": `field-url-${field.id}`, "data-field-key": field.props.key }));
|
|
18712
18823
|
case 'textarea':
|
|
18713
|
-
return (jsx(Textarea, { placeholder: field.props.placeholder, disabled: isFieldDisabled, readOnly: field.props.readOnly, value: isPreviewMode ? localValue : (field.props.defaultValue || ''), onChange: (e) => isPreviewMode && handleChange(e.target.value), onBlur: handleBlur, tabIndex: tabIndexValue, className: `min-h-[80px] ${errorClass} ${field.customStyle?.inputClassName || ''}`, style: {
|
|
18824
|
+
return (jsx(Textarea, { placeholder: field.props.placeholder, disabled: isFieldDisabled, readOnly: field.props.readOnly, autoFocus: isPreviewMode && field.props.autoFocus, value: isPreviewMode ? localValue : (field.props.defaultValue || ''), onChange: (e) => isPreviewMode && handleChange(e.target.value), onBlur: handleBlur, tabIndex: tabIndexValue, className: `min-h-[80px] ${errorClass} ${field.customStyle?.inputClassName || ''}`, style: {
|
|
18714
18825
|
...(field.customStyle?.color ? { color: field.customStyle.color } : {}),
|
|
18715
18826
|
...(field.customStyle?.backgroundColor ? { backgroundColor: field.customStyle.backgroundColor } : {}),
|
|
18716
|
-
}, "data-testid": `field-textarea-${field.id}
|
|
18827
|
+
}, "data-testid": `field-textarea-${field.id}`, "data-field-key": field.props.key }));
|
|
18717
18828
|
case 'dropdown':
|
|
18718
18829
|
const dropdownOptions = typeof field.props.optionsString === 'string'
|
|
18719
18830
|
? field.props.optionsString.split('\n').filter((o) => o.trim())
|
|
18720
18831
|
: (field.props.optionsString || []);
|
|
18721
|
-
return (jsxs(Select, { disabled: isFieldDisabled, value: isPreviewMode ? localValue : undefined, onValueChange: (v) => isPreviewMode && handleChange(v), children: [jsx(SelectTrigger, { className: `${inputSize} ${errorClass} ${field.customStyle?.inputClassName || ''}`, style: {
|
|
18832
|
+
return (jsxs(Select, { disabled: isFieldDisabled, value: isPreviewMode ? localValue : undefined, onValueChange: (v) => isPreviewMode && handleChange(v), children: [jsx(SelectTrigger, { className: `${inputSize} ${errorClass} ${field.customStyle?.inputClassName || ''}`, tabIndex: tabIndexValue, style: {
|
|
18722
18833
|
...(field.customStyle?.color ? { color: field.customStyle.color } : {}),
|
|
18723
18834
|
...(field.customStyle?.backgroundColor ? { backgroundColor: field.customStyle.backgroundColor } : {}),
|
|
18724
18835
|
}, "data-testid": `field-dropdown-${field.id}`, children: jsx(SelectValue, { placeholder: field.props.placeholder || 'Select...' }) }), jsx(SelectContent, { children: dropdownOptions.map((option, i) => (jsx(SelectItem, { value: option, children: option }, i))) })] }));
|
|
18725
18836
|
case 'checkbox':
|
|
18726
|
-
return (jsxs("div", { className: `flex items-center gap-2 ${field.customStyle?.inputClassName || ''}`, children: [jsx(Checkbox, { disabled: isFieldDisabled, "data-testid": `field-checkbox-${field.id}` }), jsx("span", { className: "text-sm text-foreground", style: {
|
|
18837
|
+
return (jsxs("div", { className: `flex items-center gap-2 ${field.customStyle?.inputClassName || ''}`, children: [jsx(Checkbox, { disabled: isFieldDisabled, tabIndex: tabIndexValue, "data-testid": `field-checkbox-${field.id}` }), jsx("span", { className: "text-sm text-foreground", style: {
|
|
18727
18838
|
...(field.customStyle?.color ? { color: field.customStyle.color } : {}),
|
|
18728
18839
|
}, children: field.props.label })] }));
|
|
18729
18840
|
case 'radio':
|
|
18730
18841
|
const radioOptions = typeof field.props.optionsString === 'string'
|
|
18731
18842
|
? field.props.optionsString.split('\n').filter((o) => o.trim())
|
|
18732
18843
|
: (field.props.optionsString || []);
|
|
18733
|
-
return (jsx(RadioGroup$1, { disabled: isFieldDisabled, className: field.customStyle?.inputClassName || '', children: radioOptions.map((option, i) => (jsxs("div", { className: "flex items-center gap-2", children: [jsx(RadioGroupItem, { value: option, id: `${field.id}-${i}`, "data-testid": `field-radio-${field.id}-${i}` }), jsx(Label$2, { htmlFor: `${field.id}-${i}`, className: "text-sm", style: {
|
|
18844
|
+
return (jsx(RadioGroup$1, { disabled: isFieldDisabled, className: field.customStyle?.inputClassName || '', children: radioOptions.map((option, i) => (jsxs("div", { className: "flex items-center gap-2", children: [jsx(RadioGroupItem, { value: option, id: `${field.id}-${i}`, tabIndex: tabIndexValue, "data-testid": `field-radio-${field.id}-${i}` }), jsx(Label$2, { htmlFor: `${field.id}-${i}`, className: "text-sm", style: {
|
|
18734
18845
|
...(field.customStyle?.color ? { color: field.customStyle.color } : {}),
|
|
18735
18846
|
}, children: option })] }, i))) }));
|
|
18736
18847
|
case 'toggle':
|
|
18737
|
-
return (jsxs("div", { className: `flex items-center gap-2 ${field.customStyle?.inputClassName || ''}`, children: [jsx(Switch, { disabled: isFieldDisabled, "data-testid": `field-toggle-${field.id}` }), jsx("span", { className: "text-sm text-foreground", style: {
|
|
18848
|
+
return (jsxs("div", { className: `flex items-center gap-2 ${field.customStyle?.inputClassName || ''}`, children: [jsx(Switch, { disabled: isFieldDisabled, tabIndex: tabIndexValue, "data-testid": `field-toggle-${field.id}` }), jsx("span", { className: "text-sm text-foreground", style: {
|
|
18738
18849
|
...(field.customStyle?.color ? { color: field.customStyle.color } : {}),
|
|
18739
18850
|
}, children: field.props.label })] }));
|
|
18740
18851
|
case 'multiselect':
|
|
@@ -18743,30 +18854,30 @@ function CanvasField({ field, rowId, columnId, isPreviewMode: externalPreviewMod
|
|
|
18743
18854
|
setFieldValue(field.props.key, v);
|
|
18744
18855
|
}, options: field.props.optionsString || [], disabled: isFieldDisabled, maxItems: field.props.multiSelectConfig?.maxItems, className: field.customStyle?.inputClassName }));
|
|
18745
18856
|
case 'date':
|
|
18746
|
-
return (jsx(Input, { type: "date", disabled: isFieldDisabled, value: isPreviewMode ? localValue : (field.props.defaultValue || ''), onChange: (e) => isPreviewMode && handleChange(e.target.value), onBlur: handleBlur, tabIndex: tabIndexValue, className: `${inputSize} ${errorClass} ${field.customStyle?.inputClassName || ''}`, style: {
|
|
18857
|
+
return (jsx(Input, { type: "date", disabled: isFieldDisabled, autoFocus: isPreviewMode && field.props.autoFocus, value: isPreviewMode ? localValue : (field.props.defaultValue || ''), onChange: (e) => isPreviewMode && handleChange(e.target.value), onBlur: handleBlur, tabIndex: tabIndexValue, className: `${inputSize} ${errorClass} ${field.customStyle?.inputClassName || ''}`, style: {
|
|
18747
18858
|
...(field.customStyle?.color ? { color: field.customStyle.color } : {}),
|
|
18748
18859
|
...(field.customStyle?.backgroundColor ? { backgroundColor: field.customStyle.backgroundColor } : {}),
|
|
18749
|
-
}, "data-testid": `field-date-${field.id}
|
|
18860
|
+
}, "data-testid": `field-date-${field.id}`, "data-field-key": field.props.key }));
|
|
18750
18861
|
case 'time':
|
|
18751
|
-
return (jsx(Input, { type: "time", disabled: isFieldDisabled, value: isPreviewMode ? localValue : (field.props.defaultValue || ''), onChange: (e) => isPreviewMode && handleChange(e.target.value), onBlur: handleBlur, tabIndex: tabIndexValue, className: `${inputSize} ${errorClass} ${field.customStyle?.inputClassName || ''}`, style: {
|
|
18862
|
+
return (jsx(Input, { type: "time", disabled: isFieldDisabled, autoFocus: isPreviewMode && field.props.autoFocus, value: isPreviewMode ? localValue : (field.props.defaultValue || ''), onChange: (e) => isPreviewMode && handleChange(e.target.value), onBlur: handleBlur, tabIndex: tabIndexValue, className: `${inputSize} ${errorClass} ${field.customStyle?.inputClassName || ''}`, style: {
|
|
18752
18863
|
...(field.customStyle?.color ? { color: field.customStyle.color } : {}),
|
|
18753
18864
|
...(field.customStyle?.backgroundColor ? { backgroundColor: field.customStyle.backgroundColor } : {}),
|
|
18754
|
-
}, "data-testid": `field-time-${field.id}
|
|
18865
|
+
}, "data-testid": `field-time-${field.id}`, "data-field-key": field.props.key }));
|
|
18755
18866
|
case 'daterange':
|
|
18756
|
-
return (jsxs("div", { className: `flex gap-2 items-center ${field.customStyle?.inputClassName || ''}`, children: [jsx(Input, { type: "date", disabled: isFieldDisabled, className: inputSize, style: {
|
|
18867
|
+
return (jsxs("div", { className: `flex gap-2 items-center ${field.customStyle?.inputClassName || ''}`, children: [jsx(Input, { type: "date", disabled: isFieldDisabled, tabIndex: tabIndexValue, className: inputSize, style: {
|
|
18757
18868
|
...(field.customStyle?.backgroundColor ? { backgroundColor: field.customStyle.backgroundColor } : {}),
|
|
18758
18869
|
}, "data-testid": `field-daterange-start-${field.id}` }), jsx("span", { className: "text-muted-foreground", style: {
|
|
18759
18870
|
...(field.customStyle?.color ? { color: field.customStyle.color } : {}),
|
|
18760
|
-
}, children: "to" }), jsx(Input, { type: "date", disabled: isFieldDisabled, className: inputSize, style: {
|
|
18871
|
+
}, children: "to" }), jsx(Input, { type: "date", disabled: isFieldDisabled, tabIndex: tabIndexValue !== undefined ? tabIndexValue + 1 : undefined, className: inputSize, style: {
|
|
18761
18872
|
...(field.customStyle?.backgroundColor ? { backgroundColor: field.customStyle.backgroundColor } : {}),
|
|
18762
18873
|
}, "data-testid": `field-daterange-end-${field.id}` })] }));
|
|
18763
18874
|
case 'number':
|
|
18764
|
-
return (jsx(Input, { type: "number", placeholder: field.props.placeholder, disabled: isFieldDisabled, value: isPreviewMode ? localValue : (field.props.defaultValue || ''), onChange: (e) => isPreviewMode && handleChange(e.target.value), onBlur: handleBlur, tabIndex: tabIndexValue, className: `${inputSize} ${errorClass} ${field.customStyle?.inputClassName || ''}`, style: {
|
|
18875
|
+
return (jsx(Input, { type: "number", placeholder: field.props.placeholder, disabled: isFieldDisabled, autoFocus: isPreviewMode && field.props.autoFocus, value: isPreviewMode ? localValue : (field.props.defaultValue || ''), onChange: (e) => isPreviewMode && handleChange(e.target.value), onBlur: handleBlur, tabIndex: tabIndexValue, className: `${inputSize} ${errorClass} ${field.customStyle?.inputClassName || ''}`, style: {
|
|
18765
18876
|
...(field.customStyle?.color ? { color: field.customStyle.color } : {}),
|
|
18766
18877
|
...(field.customStyle?.backgroundColor ? { backgroundColor: field.customStyle.backgroundColor } : {}),
|
|
18767
|
-
}, "data-testid": `field-number-${field.id}
|
|
18878
|
+
}, "data-testid": `field-number-${field.id}`, "data-field-key": field.props.key }));
|
|
18768
18879
|
case 'email':
|
|
18769
|
-
return (jsx(Input, { type: "email", placeholder: field.props.placeholder, disabled: isFieldDisabled, value: isPreviewMode ? localValue : (field.props.defaultValue || ''), onChange: (e) => isPreviewMode && handleChange(e.target.value), onBlur: handleBlur, tabIndex: tabIndexValue, className: `${inputSize} ${errorClass} ${field.customStyle?.inputClassName || ''}`, style: {
|
|
18880
|
+
return (jsx(Input, { type: "email", placeholder: field.props.placeholder, disabled: isFieldDisabled, autoFocus: isPreviewMode && field.props.autoFocus, value: isPreviewMode ? localValue : (field.props.defaultValue || ''), onChange: (e) => isPreviewMode && handleChange(e.target.value), onBlur: handleBlur, tabIndex: tabIndexValue, className: `${inputSize} ${errorClass} ${field.customStyle?.inputClassName || ''}`, "data-field-key": field.props.key, style: {
|
|
18770
18881
|
...(field.customStyle?.color ? { color: field.customStyle.color } : {}),
|
|
18771
18882
|
...(field.customStyle?.backgroundColor ? { backgroundColor: field.customStyle.backgroundColor } : {}),
|
|
18772
18883
|
}, "data-testid": `field-email-${field.id}` }));
|
|
@@ -18792,7 +18903,7 @@ function CanvasField({ field, rowId, columnId, isPreviewMode: externalPreviewMod
|
|
|
18792
18903
|
...(field.customStyle?.backgroundColor ? { backgroundColor: field.customStyle.backgroundColor } : {}),
|
|
18793
18904
|
}, "data-testid": `field-richtext-${field.id}` }));
|
|
18794
18905
|
case 'autocomplete':
|
|
18795
|
-
return (jsx("div", { className: `relative ${field.customStyle?.inputClassName || ''}`, children: jsx(Input, { placeholder: field.props.placeholder || 'Start typing...', disabled: isFieldDisabled, value: isPreviewMode ? localValue : '', onChange: (e) => isPreviewMode && handleChange(e.target.value), className: `${inputSize} ${errorClass}`, style: {
|
|
18906
|
+
return (jsx("div", { className: `relative ${field.customStyle?.inputClassName || ''}`, children: jsx(Input, { placeholder: field.props.placeholder || 'Start typing...', disabled: isFieldDisabled, value: isPreviewMode ? localValue : '', onChange: (e) => isPreviewMode && handleChange(e.target.value), tabIndex: tabIndexValue, className: `${inputSize} ${errorClass}`, style: {
|
|
18796
18907
|
...(field.customStyle?.color ? { color: field.customStyle.color } : {}),
|
|
18797
18908
|
...(field.customStyle?.backgroundColor ? { backgroundColor: field.customStyle.backgroundColor } : {}),
|
|
18798
18909
|
}, "data-testid": `field-autocomplete-${field.id}` }) }));
|
|
@@ -18800,7 +18911,7 @@ function CanvasField({ field, rowId, columnId, isPreviewMode: externalPreviewMod
|
|
|
18800
18911
|
return (jsxs("div", { className: `space-y-2 ${field.customStyle?.inputClassName || ''}`, children: [jsx(Slider, { value: sliderValue, onValueChange: (v) => {
|
|
18801
18912
|
setSliderValue(v);
|
|
18802
18913
|
setFieldValue(field.props.key, v[0]);
|
|
18803
|
-
}, min: field.validation?.min || 0, max: field.validation?.max || 100, step: 1, disabled: isFieldDisabled, "data-testid": `field-slider-${field.id}` }), jsxs("div", { className: "flex justify-between text-xs text-muted-foreground", children: [jsx("span", { children: field.validation?.min || 0 }), jsx("span", { className: "font-medium text-foreground", style: {
|
|
18914
|
+
}, min: field.validation?.min || 0, max: field.validation?.max || 100, step: 1, disabled: isFieldDisabled, tabIndex: tabIndexValue, "data-testid": `field-slider-${field.id}` }), jsxs("div", { className: "flex justify-between text-xs text-muted-foreground", children: [jsx("span", { children: field.validation?.min || 0 }), jsx("span", { className: "font-medium text-foreground", style: {
|
|
18804
18915
|
...(field.customStyle?.color ? { color: field.customStyle.color } : {}),
|
|
18805
18916
|
}, children: sliderValue[0] }), jsx("span", { children: field.validation?.max || 100 })] })] }));
|
|
18806
18917
|
case 'header':
|
|
@@ -19795,15 +19906,30 @@ function FieldProperties() {
|
|
|
19795
19906
|
});
|
|
19796
19907
|
}, className: "w-full gap-2", children: [jsx(Plus, { className: "h-4 w-4" }), "Add Attribute"] })] })] }), jsx(Separator$1, {}), hasOptions && (jsxs("div", { className: "space-y-2", children: [jsx(Label$2, { className: "text-sm", children: "Options" }), jsxs("div", { className: "space-y-2", children: [getOptionsArray().map((option, i) => (jsxs("div", { className: "flex gap-2", children: [jsx(Input, { value: option, onChange: (e) => handleUpdateOption(i, e.target.value), className: "flex-1", "data-testid": `input-option-${i}` }), jsx(Button, { variant: "outline", size: "icon", onClick: () => handleRemoveOption(i), "data-testid": `button-remove-option-${i}`, children: jsx(Trash2, { className: "h-4 w-4" }) })] }, i))), jsxs(Button, { variant: "outline", size: "sm", onClick: handleAddOption, className: "w-full gap-2", "data-testid": "button-add-option", children: [jsx(Plus, { className: "h-4 w-4" }), "Add Option"] })] })] })), field.type === 'button' && (jsxs("div", { className: "space-y-4", children: [jsx(Separator$1, {}), jsx("h4", { className: "text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Button Settings" }), jsxs("div", { className: "space-y-2", children: [jsx(Label$2, { className: "text-sm", children: "Action" }), jsxs(Select, { value: field.props.buttonConfig?.action || 'submit', onValueChange: (value) => handleUpdateProp('buttonConfig', { ...field.props.buttonConfig, action: value }), children: [jsx(SelectTrigger, { children: jsx(SelectValue, {}) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "submit", children: "Submit Form" }), jsx(SelectItem, { value: "reset", children: "Reset Form" }), jsx(SelectItem, { value: "custom", children: "Custom Action" })] })] })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$2, { className: "text-sm", children: "Variant" }), jsxs(Select, { value: field.props.buttonConfig?.variant || 'default', onValueChange: (value) => handleUpdateProp('buttonConfig', { ...field.props.buttonConfig, variant: value }), children: [jsx(SelectTrigger, { children: jsx(SelectValue, {}) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "default", children: "Default" }), jsx(SelectItem, { value: "outline", children: "Outline" }), jsx(SelectItem, { value: "secondary", children: "Secondary" }), jsx(SelectItem, { value: "destructive", children: "Destructive" }), jsx(SelectItem, { value: "ghost", children: "Ghost" })] })] })] })] })), field.type === 'pattern' && (jsxs("div", { className: "space-y-4", children: [jsx(Separator$1, {}), jsx("h4", { className: "text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Pattern Format" }), jsxs("div", { className: "space-y-2", children: [jsx(Label$2, { className: "text-sm", children: "Format Pattern" }), jsx(Input, { value: field.props.patternConfig?.format || '(###) ###-####', onChange: (e) => handleUpdateProp('patternConfig', { ...field.props.patternConfig, format: e.target.value }), placeholder: "(###) ###-####" }), jsx("p", { className: "text-xs text-muted-foreground", children: "Use # for digit placeholders" })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$2, { className: "text-sm", children: "Mask Character" }), jsx(Input, { value: field.props.patternConfig?.mask || '_', onChange: (e) => handleUpdateProp('patternConfig', { ...field.props.patternConfig, mask: e.target.value }), maxLength: 1 })] })] })), field.type === 'qrcode' && (jsxs("div", { className: "space-y-4", children: [jsx(Separator$1, {}), jsx("h4", { className: "text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "QR Code Settings" }), jsxs("div", { className: "space-y-2", children: [jsx(Label$2, { className: "text-sm", children: "QR Value" }), jsx(Input, { value: field.props.qrCodeConfig?.value || 'https://example.com', onChange: (e) => handleUpdateProp('qrCodeConfig', { ...field.props.qrCodeConfig, value: e.target.value }), placeholder: "https://example.com" })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$2, { className: "text-sm", children: "Size (px)" }), jsx(Input, { type: "number", value: field.props.qrCodeConfig?.size || 128, onChange: (e) => handleUpdateProp('qrCodeConfig', { ...field.props.qrCodeConfig, size: parseInt(e.target.value) }), min: 64, max: 512 })] })] })), field.type === 'container' && (jsxs("div", { className: "space-y-4", children: [jsx(Separator$1, {}), jsx("h4", { className: "text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Container Settings" }), jsxs("div", { className: "space-y-2", children: [jsx(Label$2, { className: "text-sm", children: "Direction" }), jsxs(Select, { value: field.props.containerConfig?.direction || 'row', onValueChange: (value) => handleUpdateProp('containerConfig', { ...field.props.containerConfig, direction: value }), children: [jsx(SelectTrigger, { "data-testid": "select-container-direction", children: jsx(SelectValue, {}) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "row", children: "Row (Horizontal)" }), jsx(SelectItem, { value: "column", children: "Column (Vertical)" })] })] })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$2, { className: "text-sm", children: "Inner Field Size (Density)" }), jsxs(Select, { value: field.props.containerConfig?.fieldSize || 'normal', onValueChange: (value) => handleUpdateProp('containerConfig', { ...field.props.containerConfig, fieldSize: value }), children: [jsx(SelectTrigger, { "data-testid": "container-field-size", children: jsx(SelectValue, {}) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "compact", "data-testid": "field-size-compact", children: "Compact (Minimal)" }), jsx(SelectItem, { value: "normal", "data-testid": "field-size-normal", children: "Normal" }), jsx(SelectItem, { value: "comfortable", "data-testid": "field-size-comfortable", children: "Comfortable (Spacious)" })] })] }), jsx("p", { className: "text-xs text-muted-foreground", children: "Controls padding and font sizes for fields inside this container" })] }), jsx(Separator$1, {}), jsx("h5", { className: "text-xs font-medium text-muted-foreground", children: "Spacing & Layout" }), jsxs("div", { className: "space-y-2", children: [jsx(Label$2, { className: "text-sm", children: "Gap (between items)" }), jsx(Input, { type: "number", value: field.props.containerConfig?.gap ?? 4, onChange: (e) => handleUpdateProp('containerConfig', { ...field.props.containerConfig, gap: parseInt(e.target.value) }), min: 0, max: 16 }), jsx("p", { className: "text-xs text-muted-foreground", children: "Space between child elements (0-16)" })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$2, { className: "text-sm", children: "Padding (inner space)" }), jsx(Input, { type: "number", value: field.props.containerConfig?.padding ?? 4, onChange: (e) => handleUpdateProp('containerConfig', { ...field.props.containerConfig, padding: parseInt(e.target.value) }), min: 0, max: 16 }), jsx("p", { className: "text-xs text-muted-foreground", children: "Inner spacing around content (0-16)" })] }), jsx(Separator$1, {}), jsx("h5", { className: "text-xs font-medium text-muted-foreground", children: "Flex Properties" }), jsxs("div", { className: "space-y-2", children: [jsx(Label$2, { className: "text-sm", children: "Justify Content" }), jsxs(Select, { value: field.props.containerConfig?.justifyContent || 'flex-start', onValueChange: (value) => handleUpdateProp('containerConfig', { ...field.props.containerConfig, justifyContent: value }), children: [jsx(SelectTrigger, { "data-testid": "select-justify-content", children: jsx(SelectValue, {}) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "flex-start", children: "Start" }), jsx(SelectItem, { value: "flex-end", children: "End" }), jsx(SelectItem, { value: "center", children: "Center" }), jsx(SelectItem, { value: "space-between", children: "Space Between" }), jsx(SelectItem, { value: "space-around", children: "Space Around" }), jsx(SelectItem, { value: "space-evenly", children: "Space Evenly" })] })] })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$2, { className: "text-sm", children: "Align Items" }), jsxs(Select, { value: field.props.containerConfig?.alignItems || 'stretch', onValueChange: (value) => handleUpdateProp('containerConfig', { ...field.props.containerConfig, alignItems: value }), children: [jsx(SelectTrigger, { "data-testid": "select-align-items", children: jsx(SelectValue, {}) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "flex-start", children: "Start" }), jsx(SelectItem, { value: "flex-end", children: "End" }), jsx(SelectItem, { value: "center", children: "Center" }), jsx(SelectItem, { value: "stretch", children: "Stretch" }), jsx(SelectItem, { value: "baseline", children: "Baseline" })] })] })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$2, { className: "text-sm", children: "Flex Wrap" }), jsxs(Select, { value: field.props.containerConfig?.flexWrap || 'wrap', onValueChange: (value) => handleUpdateProp('containerConfig', { ...field.props.containerConfig, flexWrap: value }), children: [jsx(SelectTrigger, { "data-testid": "select-flex-wrap", children: jsx(SelectValue, {}) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "wrap", children: "Wrap" }), jsx(SelectItem, { value: "nowrap", children: "No Wrap" })] })] })] }), jsxs("div", { className: "flex items-center justify-between", children: [jsx(Label$2, { className: "text-sm", children: "Show Border" }), jsx(Switch, { checked: field.props.containerConfig?.border || false, onCheckedChange: (checked) => handleUpdateProp('containerConfig', { ...field.props.containerConfig, border: checked }) })] }), jsx(Separator$1, {}), jsxs("div", { className: "space-y-3", children: [jsxs("div", { className: "flex items-center justify-between", children: [jsx("h5", { className: "text-sm font-medium", children: "Container Fields" }), jsxs("span", { className: "text-xs text-muted-foreground", children: [(field.props.containerConfig?.fields || []).length, " field(s)"] })] }), jsx("p", { className: "text-xs text-muted-foreground", children: "Drag fields onto the container. Set each field's width in its properties." })] })] })), field.type === 'table' && (jsxs("div", { className: "space-y-4 p-3 bg-muted/30 rounded-md", children: [jsx("h4", { className: "text-sm font-medium", children: "Table Configuration" }), jsxs("div", { className: "space-y-3", children: [jsxs("div", { className: "flex items-center justify-between", children: [jsx(Label$2, { className: "text-sm", children: "Show Row Numbers" }), jsx(Switch, { checked: field.props.tableConfig?.showRowNumbers ?? true, onCheckedChange: (checked) => handleUpdateProp('tableConfig', { ...field.props.tableConfig, showRowNumbers: checked }) })] }), jsxs("div", { className: "flex items-center justify-between", children: [jsx(Label$2, { className: "text-sm", children: "Allow Add Rows" }), jsx(Switch, { checked: field.props.tableConfig?.allowAddRows ?? true, onCheckedChange: (checked) => handleUpdateProp('tableConfig', { ...field.props.tableConfig, allowAddRows: checked }) })] }), jsxs("div", { className: "flex items-center justify-between", children: [jsx(Label$2, { className: "text-sm", children: "Allow Delete Rows" }), jsx(Switch, { checked: field.props.tableConfig?.allowDeleteRows ?? false, onCheckedChange: (checked) => handleUpdateProp('tableConfig', { ...field.props.tableConfig, allowDeleteRows: checked }) })] }), jsxs("div", { className: "flex items-center justify-between", children: [jsx(Label$2, { className: "text-sm", children: "Striped Rows" }), jsx(Switch, { checked: field.props.tableConfig?.striped ?? false, onCheckedChange: (checked) => handleUpdateProp('tableConfig', { ...field.props.tableConfig, striped: checked }) })] }), jsxs("div", { className: "flex items-center justify-between", children: [jsx(Label$2, { className: "text-sm", children: "Bordered" }), jsx(Switch, { checked: field.props.tableConfig?.bordered ?? true, onCheckedChange: (checked) => handleUpdateProp('tableConfig', { ...field.props.tableConfig, bordered: checked }) })] }), jsxs("div", { className: "flex items-center justify-between", children: [jsx(Label$2, { className: "text-sm", children: "Compact" }), jsx(Switch, { checked: field.props.tableConfig?.compact ?? false, onCheckedChange: (checked) => handleUpdateProp('tableConfig', { ...field.props.tableConfig, compact: checked }) })] })] }), jsx(Separator$1, {}), jsxs("div", { className: "grid grid-cols-2 gap-3", children: [jsxs("div", { className: "space-y-2", children: [jsx(Label$2, { className: "text-sm", children: "Min Rows" }), jsx(Input, { type: "number", value: field.props.tableConfig?.minRows ?? 1, onChange: (e) => handleUpdateProp('tableConfig', { ...field.props.tableConfig, minRows: parseInt(e.target.value) || 1 }), min: 1, max: 100 })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$2, { className: "text-sm", children: "Max Rows" }), jsx(Input, { type: "number", value: field.props.tableConfig?.maxRows ?? 100, onChange: (e) => handleUpdateProp('tableConfig', { ...field.props.tableConfig, maxRows: parseInt(e.target.value) || 100 }), min: 1, max: 1000 })] })] }), jsx(Separator$1, {}), jsxs("div", { className: "space-y-3", children: [jsxs("div", { className: "flex items-center justify-between", children: [jsx("h5", { className: "text-sm font-medium", children: "Columns" }), jsxs(Button, { variant: "outline", size: "sm", onClick: () => {
|
|
19797
19908
|
const currentColumns = field.props.tableConfig?.columns || [];
|
|
19909
|
+
const totalColumns = currentColumns.length + 1;
|
|
19910
|
+
const widthPerColumn = Math.floor(100 / totalColumns);
|
|
19911
|
+
const extraWidth = 100 % totalColumns;
|
|
19912
|
+
const updatedColumns = currentColumns.map((c, idx) => ({
|
|
19913
|
+
...c,
|
|
19914
|
+
width: `${widthPerColumn + (idx < extraWidth ? 1 : 0)}%`
|
|
19915
|
+
}));
|
|
19798
19916
|
const newColumn = {
|
|
19799
19917
|
key: `col_${Date.now()}`,
|
|
19800
|
-
label: `Column ${
|
|
19918
|
+
label: `Column ${totalColumns}`,
|
|
19801
19919
|
type: 'text',
|
|
19920
|
+
width: `${widthPerColumn}%`,
|
|
19802
19921
|
};
|
|
19803
|
-
handleUpdateProp('tableConfig', { ...field.props.tableConfig, columns: [...
|
|
19922
|
+
handleUpdateProp('tableConfig', { ...field.props.tableConfig, columns: [...updatedColumns, newColumn] });
|
|
19804
19923
|
}, children: [jsx(Plus, { className: "h-4 w-4 mr-1" }), "Add Column"] })] }), (field.props.tableConfig?.columns || []).map((col, colIndex) => (jsxs("div", { className: "p-3 bg-background rounded-md border border-border space-y-3", children: [jsxs("div", { className: "flex items-center justify-between", children: [jsxs("span", { className: "text-xs font-medium text-muted-foreground", children: ["Column ", colIndex + 1] }), jsx(Button, { variant: "ghost", size: "icon", className: "h-6 w-6", onClick: () => {
|
|
19805
19924
|
const currentColumns = [...(field.props.tableConfig?.columns || [])];
|
|
19806
19925
|
currentColumns.splice(colIndex, 1);
|
|
19926
|
+
if (currentColumns.length > 0) {
|
|
19927
|
+
const widthPerColumn = Math.floor(100 / currentColumns.length);
|
|
19928
|
+
const extraWidth = 100 % currentColumns.length;
|
|
19929
|
+
currentColumns.forEach((c, idx) => {
|
|
19930
|
+
currentColumns[idx] = { ...c, width: `${widthPerColumn + (idx < extraWidth ? 1 : 0)}%` };
|
|
19931
|
+
});
|
|
19932
|
+
}
|
|
19807
19933
|
handleUpdateProp('tableConfig', { ...field.props.tableConfig, columns: currentColumns });
|
|
19808
19934
|
}, children: jsx(Trash2, { className: "h-3 w-3 text-destructive" }) })] }), jsxs("div", { className: "grid grid-cols-2 gap-2", children: [jsxs("div", { className: "space-y-1", children: [jsx(Label$2, { className: "text-xs", children: "Key" }), jsx(Input, { value: col.key, onChange: (e) => {
|
|
19809
19935
|
const currentColumns = [...(field.props.tableConfig?.columns || [])];
|
|
@@ -19817,11 +19943,12 @@ function FieldProperties() {
|
|
|
19817
19943
|
const currentColumns = [...(field.props.tableConfig?.columns || [])];
|
|
19818
19944
|
currentColumns[colIndex] = { ...col, type: value };
|
|
19819
19945
|
handleUpdateProp('tableConfig', { ...field.props.tableConfig, columns: currentColumns });
|
|
19820
|
-
}, children: [jsx(SelectTrigger, { className: "h-8 text-xs", children: jsx(SelectValue, {}) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "text", children: "Text" }), jsx(SelectItem, { value: "number", children: "Number" }), jsx(SelectItem, { value: "email", children: "Email" }), jsx(SelectItem, { value: "date", children: "Date" }), jsx(SelectItem, { value: "select", children: "Select" }), jsx(SelectItem, { value: "checkbox", children: "Checkbox" })] })] })] }), jsxs("div", { className: "space-y-1", children: [jsx(Label$2, { className: "text-xs", children: "Width" }), jsx(Input, { value: col.width || '', onChange: (e) => {
|
|
19946
|
+
}, children: [jsx(SelectTrigger, { className: "h-8 text-xs", children: jsx(SelectValue, {}) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "text", children: "Text" }), jsx(SelectItem, { value: "number", children: "Number" }), jsx(SelectItem, { value: "email", children: "Email" }), jsx(SelectItem, { value: "date", children: "Date" }), jsx(SelectItem, { value: "select", children: "Select" }), jsx(SelectItem, { value: "checkbox", children: "Checkbox" })] })] })] }), jsxs("div", { className: "space-y-1", children: [jsx(Label$2, { className: "text-xs", children: "%Width" }), jsx(Input, { type: "number", value: col.width ? parseInt(col.width) || '' : '', onChange: (e) => {
|
|
19821
19947
|
const currentColumns = [...(field.props.tableConfig?.columns || [])];
|
|
19822
|
-
|
|
19948
|
+
const newWidth = parseInt(e.target.value) || 0;
|
|
19949
|
+
currentColumns[colIndex] = { ...col, width: newWidth > 0 ? `${newWidth}%` : '' };
|
|
19823
19950
|
handleUpdateProp('tableConfig', { ...field.props.tableConfig, columns: currentColumns });
|
|
19824
|
-
}, placeholder: "
|
|
19951
|
+
}, placeholder: "25", min: 1, max: 100, className: "text-xs h-8" })] }), jsxs("div", { className: "space-y-1", children: [jsx(Label$2, { className: "text-xs", children: "Required" }), jsx("div", { className: "flex items-center h-8", children: jsx(Switch, { checked: col.required || false, onCheckedChange: (checked) => {
|
|
19825
19952
|
const currentColumns = [...(field.props.tableConfig?.columns || [])];
|
|
19826
19953
|
currentColumns[colIndex] = { ...col, required: checked };
|
|
19827
19954
|
handleUpdateProp('tableConfig', { ...field.props.tableConfig, columns: currentColumns });
|
|
@@ -19849,7 +19976,12 @@ function FieldProperties() {
|
|
|
19849
19976
|
currentOptions.splice(optIndex, 1);
|
|
19850
19977
|
currentColumns[colIndex] = { ...col, options: currentOptions };
|
|
19851
19978
|
handleUpdateProp('tableConfig', { ...field.props.tableConfig, columns: currentColumns });
|
|
19852
|
-
}, children: jsx(Trash2, { className: "h-3 w-3 text-destructive" }) })] }, optIndex))), (!col.options || col.options.length === 0) && (jsx("p", { className: "text-xs text-muted-foreground italic", children: "Hen\u00FCz se\u00E7enek eklenmedi" }))] }))] }, col
|
|
19979
|
+
}, children: jsx(Trash2, { className: "h-3 w-3 text-destructive" }) })] }, optIndex))), (!col.options || col.options.length === 0) && (jsx("p", { className: "text-xs text-muted-foreground italic", children: "Hen\u00FCz se\u00E7enek eklenmedi" }))] }))] }, `col-${colIndex}`))), (field.props.tableConfig?.columns || []).length > 0 && (() => {
|
|
19980
|
+
const columns = field.props.tableConfig?.columns || [];
|
|
19981
|
+
const totalWidth = columns.reduce((sum, c) => sum + (parseInt(c.width || '0') || 0), 0);
|
|
19982
|
+
const isValid = totalWidth === 100;
|
|
19983
|
+
return (jsxs("div", { className: `flex items-center justify-between p-2 rounded-md text-xs ${isValid ? 'bg-green-500/10 text-green-600 dark:text-green-400' : 'bg-destructive/10 text-destructive'}`, children: [jsxs("span", { className: "font-medium", children: ["Total Width: ", totalWidth, "%"] }), !isValid && (jsx("span", { children: "Must equal 100%" })), isValid && (jsx("span", { children: "\u2713" }))] }));
|
|
19984
|
+
})()] })] })), (() => {
|
|
19853
19985
|
const staticDisplayTypes = ['spacer', 'divider', 'header', 'subheader', 'label', 'paragraph', 'alert', 'image', 'qrcode', 'button'];
|
|
19854
19986
|
if (staticDisplayTypes.includes(field.type))
|
|
19855
19987
|
return null;
|
|
@@ -19860,7 +19992,7 @@ function FieldProperties() {
|
|
|
19860
19992
|
return (jsx("div", { className: "text-center py-8 text-muted-foreground", children: jsx("p", { className: "text-sm", children: "Bu bile\u015Fen i\u00E7in do\u011Frulama kurallar\u0131 gerekli de\u011Fil." }) }));
|
|
19861
19993
|
}
|
|
19862
19994
|
return (jsxs(Fragment, { children: [jsxs("div", { className: "flex items-center gap-2 mb-4", children: [jsx(Settings2, { className: "h-4 w-4 text-muted-foreground" }), jsx("span", { className: "text-sm font-medium", children: "Validation Rules" })] }), jsxs("div", { className: "space-y-3 pb-4", children: [jsxs("div", { className: "flex items-center justify-between", children: [jsx(Label$2, { className: "text-sm", children: "Auto Validate" }), jsx(Switch, { checked: field.validation?.autoValidate || false, onCheckedChange: (checked) => handleUpdateValidation('autoValidate', checked) })] }), jsx("p", { className: "text-xs text-muted-foreground", children: "Validate while typing" }), jsxs("div", { className: "flex items-center justify-between", children: [jsx(Label$2, { className: "text-sm", children: "Validate on Blur" }), jsx(Switch, { checked: field.validation?.validateOnBlur ?? true, onCheckedChange: (checked) => handleUpdateValidation('validateOnBlur', checked) })] }), jsxs("div", { className: "flex items-center justify-between", children: [jsx(Label$2, { className: "text-sm", children: "Validate on Change" }), jsx(Switch, { checked: field.validation?.validateOnChange || false, onCheckedChange: (checked) => handleUpdateValidation('validateOnChange', checked) })] })] }), jsx(Separator$1, {}), jsxs("div", { className: "space-y-2 pt-4", children: [jsx(Label$2, { className: "text-sm", children: "Validation Type" }), jsxs(Select, { value: field.validation?.validationType || '', onValueChange: (value) => handleUpdateValidation('validationType', value || undefined), children: [jsx(SelectTrigger, { children: jsx(SelectValue, { placeholder: "None" }) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "email", children: "Email" }), jsx(SelectItem, { value: "url", children: "URL" }), jsx(SelectItem, { value: "phone", children: "Phone" }), jsx(SelectItem, { value: "custom", children: "Custom" })] })] })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$2, { className: "text-sm", children: "Min Length" }), jsx(Input, { type: "number", min: 0, value: field.validation?.minLength || '', onChange: (e) => handleUpdateValidation('minLength', e.target.value ? parseInt(e.target.value) : undefined), "data-testid": "input-min-length" })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$2, { className: "text-sm", children: "Max Length" }), jsx(Input, { type: "number", min: 0, value: field.validation?.maxLength || '', onChange: (e) => handleUpdateValidation('maxLength', e.target.value ? parseInt(e.target.value) : undefined), "data-testid": "input-max-length" })] }), ['number', 'slider'].includes(field.type) && (jsxs(Fragment, { children: [jsxs("div", { className: "space-y-2", children: [jsx(Label$2, { className: "text-sm", children: "Min Value" }), jsx(Input, { type: "number", value: field.validation?.min ?? '', onChange: (e) => handleUpdateValidation('min', e.target.value ? parseFloat(e.target.value) : undefined), "data-testid": "input-min-value" })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$2, { className: "text-sm", children: "Max Value" }), jsx(Input, { type: "number", value: field.validation?.max ?? '', onChange: (e) => handleUpdateValidation('max', e.target.value ? parseFloat(e.target.value) : undefined), "data-testid": "input-max-value" })] })] })), jsxs("div", { className: "space-y-2", children: [jsx(Label$2, { className: "text-sm", children: "Pattern (Regex)" }), jsx(Input, { value: field.validation?.pattern || '', onChange: (e) => handleUpdateValidation('pattern', e.target.value || undefined), placeholder: "^[a-zA-Z]+$", className: "font-mono text-sm", "data-testid": "input-pattern" })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$2, { className: "text-sm", children: "Custom Error Message" }), jsx(Input, { value: field.validation?.errorMessage || '', onChange: (e) => handleUpdateValidation('errorMessage', e.target.value || undefined), placeholder: "Please enter a valid value", "data-testid": "input-error-message" })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$2, { className: "text-sm", children: "Custom Validation (JS)" }), jsx(Textarea, { value: field.validation?.customValidation || '', onChange: (e) => handleUpdateValidation('customValidation', e.target.value || undefined), placeholder: "return value.length > 5;", className: "font-mono text-sm min-h-[80px]" }), jsx("p", { className: "text-xs text-muted-foreground", children: "Write JavaScript that returns true for valid values" })] }), jsx(Separator$1, {}), jsxs("div", { className: "flex items-center justify-between", children: [jsxs("div", { className: "flex items-center gap-2", children: [jsx(Zap, { className: "h-4 w-4 text-muted-foreground" }), jsx(Label$2, { className: "text-sm font-medium", children: "Conditional Logic" })] }), jsx(Switch, { checked: field.conditionalLogic?.enabled || false, onCheckedChange: (checked) => handleUpdateConditionalLogic({ enabled: checked }) })] }), field.conditionalLogic?.enabled && (jsxs(Fragment, { children: [jsxs("div", { className: "space-y-2", children: [jsx(Label$2, { className: "text-sm", children: "Action" }), jsxs(Select, { value: field.conditionalLogic?.action || 'show', onValueChange: (value) => handleUpdateConditionalLogic({ action: value }), children: [jsx(SelectTrigger, { children: jsx(SelectValue, {}) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "show", children: "Show this field" }), jsx(SelectItem, { value: "hide", children: "Hide this field" }), jsx(SelectItem, { value: "enable", children: "Enable this field" }), jsx(SelectItem, { value: "disable", children: "Disable this field" }), jsx(SelectItem, { value: "require", children: "Make required" })] })] })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$2, { className: "text-sm", children: "When" }), jsxs(Select, { value: field.conditionalLogic?.logicType || 'all', onValueChange: (value) => handleUpdateConditionalLogic({ logicType: value }), children: [jsx(SelectTrigger, { children: jsx(SelectValue, {}) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "all", children: "All conditions are met" }), jsx(SelectItem, { value: "any", children: "Any condition is met" })] })] })] }), jsx(Separator$1, {}), jsxs("div", { className: "space-y-3", children: [jsx(Label$2, { className: "text-sm font-medium", children: "Conditions" }), (field.conditionalLogic?.conditions || []).map((condition, index) => (jsxs("div", { className: "p-3 border border-border rounded-md space-y-3", children: [jsxs("div", { className: "flex items-center justify-between", children: [jsxs(Badge, { variant: "secondary", children: ["Condition ", index + 1] }), jsx(Button, { variant: "ghost", size: "icon", onClick: () => removeCondition(index), children: jsx(Trash2, { className: "h-4 w-4" }) })] }), jsxs(Select, { value: condition.fieldKey, onValueChange: (value) => updateCondition(index, { fieldKey: value }), children: [jsx(SelectTrigger, { children: jsx(SelectValue, { placeholder: "Select field..." }) }), jsx(SelectContent, { children: allFields.map((f) => (jsxs(SelectItem, { value: f.props.key, children: [f.props.label, " (", f.props.key, ")"] }, f.id))) })] }), jsxs(Select, { value: condition.operator, onValueChange: (value) => updateCondition(index, { operator: value }), children: [jsx(SelectTrigger, { children: jsx(SelectValue, {}) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "equals", children: "Equals" }), jsx(SelectItem, { value: "notEquals", children: "Not equals" }), jsx(SelectItem, { value: "contains", children: "Contains" }), jsx(SelectItem, { value: "notContains", children: "Does not contain" }), jsx(SelectItem, { value: "greaterThan", children: "Greater than" }), jsx(SelectItem, { value: "lessThan", children: "Less than" }), jsx(SelectItem, { value: "isEmpty", children: "Is empty" }), jsx(SelectItem, { value: "isNotEmpty", children: "Is not empty" }), jsx(SelectItem, { value: "startsWith", children: "Starts with" }), jsx(SelectItem, { value: "endsWith", children: "Ends with" })] })] }), !['isEmpty', 'isNotEmpty'].includes(condition.operator) && (jsx(Input, { value: condition.value || '', onChange: (e) => updateCondition(index, { value: e.target.value }), placeholder: "Value..." }))] }, index))), jsxs(Button, { variant: "outline", size: "sm", onClick: addCondition, className: "w-full gap-2", children: [jsx(Plus, { className: "h-4 w-4" }), "Add Condition"] })] })] }))] }));
|
|
19863
|
-
})() }), jsxs(TabsContent, { value: "actions", className: "space-y-4 mt-0", children: [jsxs("div", { className: "flex items-center gap-2 mb-4", children: [jsx(MousePointerClick, { className: "h-4 w-4 text-muted-foreground" }), jsx("span", { className: "text-sm font-medium", children: "Event Handlers" })] }), jsx("p", { className: "text-xs text-muted-foreground mb-4", children: "Add actions that trigger on specific events like click, change, focus, or blur." }), ['onClick', 'onChange', 'onFocus', 'onBlur'].map((eventType) => (jsxs(Collapsible, { className: "border border-border rounded-md", children: [jsxs(CollapsibleTrigger, { className: "flex items-center justify-between w-full p-3 hover:bg-muted/50", children: [jsxs("div", { className: "flex items-center gap-2", children: [jsx(Play, { className: "h-3 w-3 text-muted-foreground" }), jsx("span", { className: "text-sm font-medium", children: eventType }), (field.events?.[eventType]?.length || 0) > 0 && (jsx(Badge, { variant: "secondary", className: "text-xs", children: field.events?.[eventType]?.length }))] }), jsx(ChevronDown, { className: "h-4 w-4 text-muted-foreground" })] }), jsxs(CollapsibleContent, { className: "p-3 pt-0 space-y-3", children: [(field.events?.[eventType] || []).map((action, index) => (jsxs("div", { className: "p-3 border border-border rounded-md space-y-3 bg-muted/30", children: [jsxs("div", { className: "flex items-center justify-between", children: [jsxs(Badge, { variant: "outline", children: ["Action ", index + 1] }), jsx(Button, { variant: "ghost", size: "icon", onClick: () => removeEventAction(eventType, index), children: jsx(Trash2, { className: "h-4 w-4" }) })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$2, { className: "text-xs", children: "Type" }), jsxs(Select, { value: action.type, onValueChange: (value) => updateEventAction(eventType, index, { type: value }), children: [jsx(SelectTrigger, { children: jsx(SelectValue, {}) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "common", children: "Common Action" }), jsx(SelectItem, { value: "code", children: "Code Action" }), jsx(SelectItem, { value: "custom", children: "Custom Action" })] })] })] }), action.type === 'common' && (jsxs("div", { className: "space-y-2", children: [jsx(Label$2, { className: "text-xs", children: "Action" }), jsxs(Select, { value: action.name, onValueChange: (value) => updateEventAction(eventType, index, { name: value }), children: [jsx(SelectTrigger, { children: jsx(SelectValue, { placeholder: "Select action..." }) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "validate", children: "Validate Form" }), jsx(SelectItem, { value: "reset", children: "Reset Form" }), jsx(SelectItem, { value: "submit", children: "Submit Form" }), jsx(SelectItem, { value: "clearField", children: "Clear This Field" }), jsx(SelectItem, { value: "focusField", children: "Focus Field" }), jsx(SelectItem, { value: "showMessage", children: "Show Message" })] })] })] })), action.type === 'code' && (jsxs("div", { className: "space-y-2", children: [jsx(Label$2, { className: "text-xs", children: "JavaScript Code" }), jsx(Textarea, { value: action.code || '', onChange: (e) => updateEventAction(eventType, index, { code: e.target.value }), placeholder: "console.log('Action triggered');", className: "font-mono text-xs min-h-[60px]" })] })), action.type === 'custom' && (jsxs("div", { className: "space-y-2", children: [jsx(Label$2, { className: "text-xs", children: "Custom Action Name" }), jsx(Input, { value: action.name, onChange: (e) => updateEventAction(eventType, index, { name: e.target.value }), placeholder: "myCustomAction", className: "font-mono text-sm" }), jsx("p", { className: "text-xs text-muted-foreground", children: "This will call the custom action passed to FormViewer" })] }))] }, index))), jsxs(Button, { variant: "outline", size: "sm", onClick: () => addEventAction(eventType), className: "w-full gap-2", children: [jsx(Plus, { className: "h-4 w-4" }), "Add Action"] })] })] }, eventType)))] }), jsxs(TabsContent, { value: "style", className: "space-y-4 mt-0", children: [jsxs("div", { className: "flex items-center justify-between mb-4", children: [jsx("span", { className: "text-sm font-medium", children: "For device" }), jsxs(Select, { value: field.customStyle?.deviceTarget || 'any', onValueChange: (value) => handleUpdateCustomStyle('deviceTarget', value), children: [jsx(SelectTrigger, { className: "w-32", children: jsx(SelectValue, {}) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "any", children: "Any" }), jsx(SelectItem, { value: "mobile", children: "Mobile" }), jsx(SelectItem, { value: "tablet", children: "Tablet" }), jsx(SelectItem, { value: "desktop", children: "Desktop" })] })] })] }), jsxs("div", { className: "space-y-4", children: [jsx("h4", { className: "text-sm font-semibold", children: "Component" }), jsxs("div", { className: "flex items-center gap-2", children: [jsx(Paintbrush, { className: "h-4 w-4 text-muted-foreground" }), jsx(Label$2, { className: "text-sm w-24", children: "Class Name" }), jsx(Input, { value: field.customStyle?.inputClassName || '', onChange: (e) => handleUpdateCustomStyle('inputClassName', e.target.value), placeholder: "", className: "font-mono text-sm flex-1" }), field.customStyle?.inputClassName && (jsx(Button, { variant: "ghost", size: "icon", onClick: () => handleUpdateCustomStyle('inputClassName', ''), children: jsx(Trash2, { className: "h-4 w-4" }) }))] })] }), jsx(Separator$1, {}), jsxs("div", { className: "space-y-4", children: [jsx("h4", { className: "text-sm font-semibold", children: "Wrapper" }), jsxs("div", { className: "grid grid-cols-2 gap-3", children: [jsxs("div", { className: "space-y-1", children: [jsx(Label$2, { className: "text-xs", children: "Width" }), jsxs("div", { className: "flex gap-1", children: [jsx(Input, { type: "number", value: field.customStyle?.width?.value ?? 100, onChange: (e) => handleUpdateCustomStyle('width', {
|
|
19995
|
+
})() }), jsxs(TabsContent, { value: "actions", className: "space-y-4 mt-0", children: [jsxs("div", { className: "flex items-center gap-2 mb-4", children: [jsx(MousePointerClick, { className: "h-4 w-4 text-muted-foreground" }), jsx("span", { className: "text-sm font-medium", children: "Event Handlers" })] }), jsx("p", { className: "text-xs text-muted-foreground mb-4", children: "Add actions that trigger on specific events like click, change, focus, or blur." }), ['onClick', 'onChange', 'onFocus', 'onBlur'].map((eventType) => (jsxs(Collapsible, { className: "border border-border rounded-md", children: [jsxs(CollapsibleTrigger, { className: "flex items-center justify-between w-full p-3 hover:bg-muted/50", children: [jsxs("div", { className: "flex items-center gap-2", children: [jsx(Play, { className: "h-3 w-3 text-muted-foreground" }), jsx("span", { className: "text-sm font-medium", children: eventType }), (field.events?.[eventType]?.length || 0) > 0 && (jsx(Badge, { variant: "secondary", className: "text-xs", children: field.events?.[eventType]?.length }))] }), jsx(ChevronDown, { className: "h-4 w-4 text-muted-foreground" })] }), jsxs(CollapsibleContent, { className: "p-3 pt-0 space-y-3", children: [(field.events?.[eventType] || []).map((action, index) => (jsxs("div", { className: "p-3 border border-border rounded-md space-y-3 bg-muted/30", children: [jsxs("div", { className: "flex items-center justify-between", children: [jsxs(Badge, { variant: "outline", children: ["Action ", index + 1] }), jsx(Button, { variant: "ghost", size: "icon", onClick: () => removeEventAction(eventType, index), children: jsx(Trash2, { className: "h-4 w-4" }) })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$2, { className: "text-xs", children: "Type" }), jsxs(Select, { value: action.type, onValueChange: (value) => updateEventAction(eventType, index, { type: value }), children: [jsx(SelectTrigger, { children: jsx(SelectValue, {}) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "common", children: "Common Action" }), jsx(SelectItem, { value: "code", children: "Code Action" }), jsx(SelectItem, { value: "custom", children: "Custom Action" })] })] })] }), action.type === 'common' && (jsxs("div", { className: "space-y-2", children: [jsx(Label$2, { className: "text-xs", children: "Action" }), jsxs(Select, { value: action.name, onValueChange: (value) => updateEventAction(eventType, index, { name: value }), children: [jsx(SelectTrigger, { children: jsx(SelectValue, { placeholder: "Select action..." }) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "validate", children: "Validate Form" }), jsx(SelectItem, { value: "reset", children: "Reset Form" }), jsx(SelectItem, { value: "submit", children: "Submit Form" }), jsx(SelectItem, { value: "clearField", children: "Clear This Field" }), jsx(SelectItem, { value: "focusField", children: "Focus Field" }), jsx(SelectItem, { value: "showMessage", children: "Show Message" })] })] }), action.name === 'focusField' && (jsxs("div", { className: "space-y-2 mt-2", children: [jsx(Label$2, { className: "text-xs", children: "Target Field" }), jsxs(Select, { value: action.args?.targetFieldKey || '', onValueChange: (value) => updateEventAction(eventType, index, { args: { ...action.args, targetFieldKey: value } }), children: [jsx(SelectTrigger, { children: jsx(SelectValue, { placeholder: "Select field to focus..." }) }), jsx(SelectContent, { children: allFields.map((f) => (jsx(SelectItem, { value: f.props.key, children: f.props.label || f.props.key }, f.id))) })] })] })), action.name === 'showMessage' && (jsxs("div", { className: "space-y-2 mt-2", children: [jsx(Label$2, { className: "text-xs", children: "Message" }), jsx(Input, { value: action.args?.message || '', onChange: (e) => updateEventAction(eventType, index, { args: { ...action.args, message: e.target.value } }), placeholder: "Enter message to show..." })] }))] })), action.type === 'code' && (jsxs("div", { className: "space-y-2", children: [jsx(Label$2, { className: "text-xs", children: "JavaScript Code" }), jsx(Textarea, { value: action.code || '', onChange: (e) => updateEventAction(eventType, index, { code: e.target.value }), placeholder: "console.log('Action triggered');", className: "font-mono text-xs min-h-[60px]" })] })), action.type === 'custom' && (jsxs("div", { className: "space-y-2", children: [jsx(Label$2, { className: "text-xs", children: "Custom Action Name" }), jsx(Input, { value: action.name, onChange: (e) => updateEventAction(eventType, index, { name: e.target.value }), placeholder: "myCustomAction", className: "font-mono text-sm" }), jsx("p", { className: "text-xs text-muted-foreground", children: "This will call the custom action passed to FormViewer" })] }))] }, index))), jsxs(Button, { variant: "outline", size: "sm", onClick: () => addEventAction(eventType), className: "w-full gap-2", children: [jsx(Plus, { className: "h-4 w-4" }), "Add Action"] })] })] }, eventType)))] }), jsxs(TabsContent, { value: "style", className: "space-y-4 mt-0", children: [jsxs("div", { className: "flex items-center justify-between mb-4", children: [jsx("span", { className: "text-sm font-medium", children: "For device" }), jsxs(Select, { value: field.customStyle?.deviceTarget || 'any', onValueChange: (value) => handleUpdateCustomStyle('deviceTarget', value), children: [jsx(SelectTrigger, { className: "w-32", children: jsx(SelectValue, {}) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "any", children: "Any" }), jsx(SelectItem, { value: "mobile", children: "Mobile" }), jsx(SelectItem, { value: "tablet", children: "Tablet" }), jsx(SelectItem, { value: "desktop", children: "Desktop" })] })] })] }), jsxs("div", { className: "space-y-4", children: [jsx("h4", { className: "text-sm font-semibold", children: "Component" }), jsxs("div", { className: "flex items-center gap-2", children: [jsx(Paintbrush, { className: "h-4 w-4 text-muted-foreground" }), jsx(Label$2, { className: "text-sm w-24", children: "Class Name" }), jsx(Input, { value: field.customStyle?.inputClassName || '', onChange: (e) => handleUpdateCustomStyle('inputClassName', e.target.value), placeholder: "", className: "font-mono text-sm flex-1" }), field.customStyle?.inputClassName && (jsx(Button, { variant: "ghost", size: "icon", onClick: () => handleUpdateCustomStyle('inputClassName', ''), children: jsx(Trash2, { className: "h-4 w-4" }) }))] })] }), jsx(Separator$1, {}), jsxs("div", { className: "space-y-4", children: [jsx("h4", { className: "text-sm font-semibold", children: "Wrapper" }), jsxs("div", { className: "grid grid-cols-2 gap-3", children: [jsxs("div", { className: "space-y-1", children: [jsx(Label$2, { className: "text-xs", children: "Width" }), jsxs("div", { className: "flex gap-1", children: [jsx(Input, { type: "number", value: field.customStyle?.width?.value ?? 100, onChange: (e) => handleUpdateCustomStyle('width', {
|
|
19864
19996
|
...field.customStyle?.width,
|
|
19865
19997
|
value: parseInt(e.target.value) || 100
|
|
19866
19998
|
}), className: "w-16" }), jsxs(Select, { value: field.customStyle?.width?.unit || '%', onValueChange: (value) => handleUpdateCustomStyle('width', {
|
|
@@ -25034,12 +25166,12 @@ function DraggableComponent({ type, label, icon, isCollapsed }) {
|
|
|
25034
25166
|
}
|
|
25035
25167
|
const fieldCategories = {
|
|
25036
25168
|
basic: { label: 'Basic Fields', types: FIELD_TYPES.filter(f => f.category === 'basic') },
|
|
25037
|
-
selection: { label: 'Selection Fields', types: FIELD_TYPES.filter(f => f.category === 'selection') },
|
|
25038
25169
|
advanced: { label: 'Advanced Fields', types: FIELD_TYPES.filter(f => f.category === 'advanced') },
|
|
25170
|
+
selection: { label: 'Selection Fields', types: FIELD_TYPES.filter(f => f.category === 'selection') },
|
|
25039
25171
|
};
|
|
25040
25172
|
function ComponentLibrary({ isCollapsed = false, onToggleCollapse }) {
|
|
25041
25173
|
if (isCollapsed) {
|
|
25042
|
-
return (jsxs("aside", { className: "w-[60px] border-r border-border bg-card shrink-0 flex flex-col", children: [jsx("div", { className: "p-2 border-b border-border", children: jsx(Button, { variant: "ghost", size: "icon", onClick: onToggleCollapse, "data-testid": "button-expand-sidebar", children: jsx(PanelLeft, { className: "h-4 w-4" }) }) }), jsx(ScrollArea, { className: "flex-1", children: jsxs("div", { className: "p-2 space-y-4", children: [jsxs("div", { className: "space-y-1", children: [jsxs(Tooltip, { children: [jsx(TooltipTrigger, { asChild: true, children: jsx("p", { className: "text-[10px] font-semibold uppercase text-muted-foreground text-center cursor-default", children: "LY" }) }), jsx(TooltipContent, { side: "right", children: "Layout" })] }), jsx("div", { className: "grid grid-cols-1 gap-1", children: STRUCTURE_TYPES.map((item) => (jsx(DraggableComponent, { type: item.type, label: item.label, icon: item.icon, isCollapsed: true }, item.type))) })] }), jsxs("div", { className: "space-y-1", children: [jsxs(Tooltip, { children: [jsx(TooltipTrigger, { asChild: true, children: jsx("p", { className: "text-[10px] font-semibold uppercase text-muted-foreground text-center cursor-default", children: "BF" }) }), jsx(TooltipContent, { side: "right", children: "Basic Fields" })] }), jsx("div", { className: "grid grid-cols-1 gap-1", children: fieldCategories.basic.types.map((item) => (jsx(DraggableComponent, { type: item.type, label: item.label, icon: item.icon, isCollapsed: true }, item.type))) })] }), jsxs("div", { className: "space-y-1", children: [jsxs(Tooltip, { children: [jsx(TooltipTrigger, { asChild: true, children: jsx("p", { className: "text-[10px] font-semibold uppercase text-muted-foreground text-center cursor-default", children: "
|
|
25174
|
+
return (jsxs("aside", { className: "w-[60px] border-r border-border bg-card shrink-0 flex flex-col", children: [jsx("div", { className: "p-2 border-b border-border", children: jsx(Button, { variant: "ghost", size: "icon", onClick: onToggleCollapse, "data-testid": "button-expand-sidebar", children: jsx(PanelLeft, { className: "h-4 w-4" }) }) }), jsx(ScrollArea, { className: "flex-1", children: jsxs("div", { className: "p-2 space-y-4", children: [jsxs("div", { className: "space-y-1", children: [jsxs(Tooltip, { children: [jsx(TooltipTrigger, { asChild: true, children: jsx("p", { className: "text-[10px] font-semibold uppercase text-muted-foreground text-center cursor-default", children: "LY" }) }), jsx(TooltipContent, { side: "right", children: "Layout" })] }), jsx("div", { className: "grid grid-cols-1 gap-1", children: STRUCTURE_TYPES.map((item) => (jsx(DraggableComponent, { type: item.type, label: item.label, icon: item.icon, isCollapsed: true }, item.type))) })] }), jsxs("div", { className: "space-y-1", children: [jsxs(Tooltip, { children: [jsx(TooltipTrigger, { asChild: true, children: jsx("p", { className: "text-[10px] font-semibold uppercase text-muted-foreground text-center cursor-default", children: "BF" }) }), jsx(TooltipContent, { side: "right", children: "Basic Fields" })] }), jsx("div", { className: "grid grid-cols-1 gap-1", children: fieldCategories.basic.types.map((item) => (jsx(DraggableComponent, { type: item.type, label: item.label, icon: item.icon, isCollapsed: true }, item.type))) })] }), jsxs("div", { className: "space-y-1", children: [jsxs(Tooltip, { children: [jsx(TooltipTrigger, { asChild: true, children: jsx("p", { className: "text-[10px] font-semibold uppercase text-muted-foreground text-center cursor-default", children: "AD" }) }), jsx(TooltipContent, { side: "right", children: "Advanced Fields" })] }), jsx("div", { className: "grid grid-cols-1 gap-1", children: fieldCategories.advanced.types.map((item) => (jsx(DraggableComponent, { type: item.type, label: item.label, icon: item.icon, isCollapsed: true }, item.type))) })] }), jsxs("div", { className: "space-y-1", children: [jsxs(Tooltip, { children: [jsx(TooltipTrigger, { asChild: true, children: jsx("p", { className: "text-[10px] font-semibold uppercase text-muted-foreground text-center cursor-default", children: "SF" }) }), jsx(TooltipContent, { side: "right", children: "Selection Fields" })] }), jsx("div", { className: "grid grid-cols-1 gap-1", children: fieldCategories.selection.types.map((item) => (jsx(DraggableComponent, { type: item.type, label: item.label, icon: item.icon, isCollapsed: true }, item.type))) })] }), jsxs("div", { className: "space-y-1", children: [jsxs(Tooltip, { children: [jsx(TooltipTrigger, { asChild: true, children: jsx("p", { className: "text-[10px] font-semibold uppercase text-muted-foreground text-center cursor-default", children: "EL" }) }), jsx(TooltipContent, { side: "right", children: "Static Elements" })] }), jsx("div", { className: "grid grid-cols-1 gap-1", children: STATIC_TYPES.map((item) => (jsx(DraggableComponent, { type: item.type, label: item.label, icon: item.icon, isCollapsed: true }, item.type))) })] })] }) })] }));
|
|
25043
25175
|
}
|
|
25044
25176
|
return (jsxs("aside", { className: "w-[200px] border-r border-border bg-card shrink-0 flex flex-col", children: [jsxs("div", { className: "p-2 border-b border-border flex items-center justify-between gap-2", children: [jsx("span", { className: "text-sm font-medium text-foreground pl-1", children: "Components" }), jsx(Button, { variant: "ghost", size: "icon", className: "h-6 w-6 shrink-0", onClick: onToggleCollapse, "data-testid": "button-collapse-sidebar", children: jsx(PanelLeftClose, { className: "h-3.5 w-3.5" }) })] }), jsx(ScrollArea, { className: "flex-1", children: jsx("div", { className: "p-2", children: jsxs(Accordion, { type: "multiple", defaultValue: ['structure', 'basic', 'selection', 'advanced', 'static'], className: "w-full", children: [jsxs(AccordionItem, { value: "structure", className: "border-b-0 bg-muted/50 rounded-md mb-1 px-2", children: [jsxs(AccordionTrigger, { className: "py-1.5 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground hover:no-underline", children: ["Layout (", STRUCTURE_TYPES.length, ")"] }), jsx(AccordionContent, { children: jsx("div", { className: "grid grid-cols-2 gap-1 pb-2", children: STRUCTURE_TYPES.map((item) => (jsx(DraggableComponent, { type: item.type, label: item.label, icon: item.icon }, item.type))) }) })] }), Object.entries(fieldCategories).map(([key, category]) => (jsxs(AccordionItem, { value: key, className: "border-b-0 bg-background dark:bg-card rounded-md mb-1 px-2", children: [jsxs(AccordionTrigger, { className: "py-1.5 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground hover:no-underline", children: [category.label.split(' ')[0], " (", category.types.length, ")"] }), jsx(AccordionContent, { children: jsx("div", { className: "grid grid-cols-2 gap-1 pb-2", children: category.types.map((item) => (jsx(DraggableComponent, { type: item.type, label: item.label, icon: item.icon }, item.type))) }) })] }, key))), jsxs(AccordionItem, { value: "static", className: "border-b-0 bg-background dark:bg-card rounded-md mb-1 px-2", children: [jsxs(AccordionTrigger, { className: "py-1.5 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground hover:no-underline", children: ["Attributes (", STATIC_TYPES.length, ")"] }), jsx(AccordionContent, { children: jsx("div", { className: "grid grid-cols-2 gap-1 pb-2", children: STATIC_TYPES.map((item) => (jsx(DraggableComponent, { type: item.type, label: item.label, icon: item.icon }, item.type))) }) })] })] }) }) })] }));
|
|
25045
25177
|
}
|
|
@@ -28323,7 +28455,9 @@ const DropdownMenuSeparator = React.forwardRef(({ className, ...props }, ref) =>
|
|
|
28323
28455
|
DropdownMenuSeparator.displayName = Separator2.displayName;
|
|
28324
28456
|
|
|
28325
28457
|
function Toolbar({ onOpenJsonViewer }) {
|
|
28326
|
-
const { form, setFormName, isPreviewMode, togglePreviewMode, clearForm, toggleMultiStep, saveVersion, restoreVersion, updateSubmissionConfig, undo, redo,
|
|
28458
|
+
const { form, setFormName, isPreviewMode, togglePreviewMode, clearForm, toggleMultiStep, saveVersion, restoreVersion, updateSubmissionConfig, undo, redo, canvasWidth, setCanvasWidth, historyIndex, history, } = useFormStore();
|
|
28459
|
+
const canUndo = historyIndex > 0;
|
|
28460
|
+
const canRedo = historyIndex < history.length - 1;
|
|
28327
28461
|
const widthOptions = [
|
|
28328
28462
|
{ value: 'compact', label: 'Compact', description: '672px' },
|
|
28329
28463
|
{ value: 'medium', label: 'Medium', description: '896px' },
|
|
@@ -28336,6 +28470,7 @@ function Toolbar({ onOpenJsonViewer }) {
|
|
|
28336
28470
|
const [versionDialogOpen, setVersionDialogOpen] = useState(false);
|
|
28337
28471
|
const [changelog, setChangelog] = useState('');
|
|
28338
28472
|
const [settingsDialogOpen, setSettingsDialogOpen] = useState(false);
|
|
28473
|
+
const [docsDialogOpen, setDocsDialogOpen] = useState(false);
|
|
28339
28474
|
const [isDarkMode, setIsDarkMode] = useState(false);
|
|
28340
28475
|
const { toast } = useToast();
|
|
28341
28476
|
const toggleTheme = () => {
|
|
@@ -28432,7 +28567,101 @@ function Toolbar({ onOpenJsonViewer }) {
|
|
|
28432
28567
|
return (jsxs("header", { className: "h-14 border-b border-border bg-card px-6 flex items-center justify-between gap-4 shrink-0", children: [jsx("div", { className: "flex items-center gap-3", children: isEditing ? (jsxs("div", { className: "flex items-center gap-2", children: [jsx(Input, { value: tempName, onChange: (e) => setTempName(e.target.value), onKeyDown: handleKeyDown, onBlur: handleSaveName, className: "h-8 w-64 text-sm font-medium", autoFocus: true, "data-testid": "input-form-name" }), jsx(Button, { size: "icon", variant: "ghost", onClick: handleSaveName, "data-testid": "button-save-name", children: jsx(Check, { className: "h-4 w-4" }) })] })) : (jsxs("div", { className: "flex items-center gap-2", children: [jsx("h1", { className: "text-lg font-semibold text-foreground", children: form.name }), jsx(Button, { size: "icon", variant: "ghost", onClick: () => {
|
|
28433
28568
|
setTempName(form.name);
|
|
28434
28569
|
setIsEditing(true);
|
|
28435
|
-
}, "data-testid": "button-edit-name", children: jsx(PenLine, { className: "h-4 w-4" }) }), form.isMultiStep && (jsx(Badge, { variant: "secondary", className: "text-xs", children: "Multi-Step" })), form.currentVersion && (jsxs(Badge, { variant: "outline", className: "text-xs", children: ["v", form.currentVersion] }))] })) }), jsxs("div", { className: "flex items-center gap-2", children: [jsxs(Tooltip, { children: [jsx(TooltipTrigger, { asChild: true, children: jsx(Button, { variant: "ghost", size: "icon", onClick: undo, disabled: !canUndo
|
|
28570
|
+
}, "data-testid": "button-edit-name", children: jsx(PenLine, { className: "h-4 w-4" }) }), form.isMultiStep && (jsx(Badge, { variant: "secondary", className: "text-xs", children: "Multi-Step" })), form.currentVersion && (jsxs(Badge, { variant: "outline", className: "text-xs", children: ["v", form.currentVersion] }))] })) }), jsxs("div", { className: "flex items-center gap-2", children: [jsxs(Tooltip, { children: [jsx(TooltipTrigger, { asChild: true, children: jsx(Button, { variant: "ghost", size: "icon", onClick: undo, disabled: !canUndo, "data-testid": "button-undo", children: jsx(Undo2, { className: "h-4 w-4" }) }) }), jsx(TooltipContent, { children: "Undo" })] }), jsxs(Tooltip, { children: [jsx(TooltipTrigger, { asChild: true, children: jsx(Button, { variant: "ghost", size: "icon", onClick: redo, disabled: !canRedo, "data-testid": "button-redo", children: jsx(Redo2, { className: "h-4 w-4" }) }) }), jsx(TooltipContent, { children: "Redo" })] }), jsxs(DropdownMenu, { children: [jsxs(Tooltip, { children: [jsx(TooltipTrigger, { asChild: true, children: jsx(DropdownMenuTrigger, { asChild: true, children: jsx(Button, { variant: "ghost", size: "icon", "data-testid": "button-canvas-width", children: jsx(Maximize2, { className: "h-4 w-4" }) }) }) }), jsx(TooltipContent, { children: "Canvas Width" })] }), jsxs(DropdownMenuContent, { align: "end", children: [jsx(DropdownMenuLabel, { children: "Canvas Width" }), jsx(DropdownMenuSeparator, {}), widthOptions.map((option) => (jsxs(DropdownMenuItem, { onClick: () => setCanvasWidth(option.value), className: "flex justify-between gap-4", "data-testid": `menu-width-${option.value}`, children: [jsx("span", { children: option.label }), jsx("span", { className: "text-muted-foreground text-xs", children: option.description }), canvasWidth === option.value && jsx(Check, { className: "h-4 w-4 ml-2" })] }, option.value)))] })] }), jsx("div", { className: "w-px h-6 bg-border mx-1" }), jsxs(Button, { variant: "default", size: "sm", onClick: handleSaveForm, disabled: isSaving, className: "gap-2", "data-testid": "button-save-form", children: [isSaving ? (jsx(LoaderCircle, { className: "h-4 w-4 animate-spin" })) : (jsx(Save, { className: "h-4 w-4" })), jsx("span", { children: "Save" })] }), jsx(Button, { variant: isPreviewMode ? 'default' : 'outline', size: "sm", onClick: togglePreviewMode, className: "gap-2", "data-testid": "button-toggle-preview", children: isPreviewMode ? (jsxs(Fragment, { children: [jsx(EyeOff, { className: "h-4 w-4" }), jsx("span", { children: "Edit" })] })) : (jsxs(Fragment, { children: [jsx(Eye, { className: "h-4 w-4" }), jsx("span", { children: "Preview" })] })) }), jsx(Button, { variant: form.isMultiStep ? 'default' : 'outline', size: "sm", onClick: toggleMultiStep, className: "gap-2", title: form.isMultiStep ? 'Switch to single page' : 'Switch to multi-step wizard', "data-testid": "button-toggle-multistep", children: jsx(Layers, { className: "h-4 w-4" }) }), jsxs(Dialog, { open: versionDialogOpen, onOpenChange: setVersionDialogOpen, children: [jsx(DialogTrigger, { asChild: true, children: jsx(Button, { variant: "outline", size: "sm", className: "gap-2", "data-testid": "button-version", children: jsx(History, { className: "h-4 w-4" }) }) }), jsxs(DialogContent, { className: "max-w-md", children: [jsxs(DialogHeader, { children: [jsx(DialogTitle, { children: "Version History" }), jsx(DialogDescription, { children: "Save snapshots and restore previous versions of your form." })] }), jsxs("div", { className: "space-y-4", children: [jsxs("div", { className: "space-y-2", children: [jsx(Label$2, { children: "Save New Version" }), jsx(Textarea, { value: changelog, onChange: (e) => setChangelog(e.target.value), placeholder: "What changed in this version?", className: "min-h-[60px]" }), jsxs(Button, { onClick: handleSaveVersion, className: "w-full gap-2", size: "sm", children: [jsx(Save, { className: "h-4 w-4" }), "Save Version ", (form.currentVersion || 0) + 1] })] }), form.versions && form.versions.length > 0 && (jsx(Fragment, { children: jsxs("div", { className: "border-t pt-4", children: [jsx(Label$2, { className: "mb-2 block", children: "Previous Versions" }), jsx(ScrollArea, { className: "h-[200px]", children: jsx("div", { className: "space-y-2", children: [...form.versions].reverse().map((version) => (jsxs("div", { className: "p-3 border rounded-md space-y-2", children: [jsxs("div", { className: "flex items-center justify-between", children: [jsxs("span", { className: "font-medium", children: ["Version ", version.version] }), jsx(Button, { variant: "outline", size: "sm", onClick: () => handleRestoreVersion(version.id, version.version), children: "Restore" })] }), version.changelog && (jsx("p", { className: "text-sm text-muted-foreground", children: version.changelog })), jsx("p", { className: "text-xs text-muted-foreground", children: new Date(version.createdAt).toLocaleString() })] }, version.id))) }) })] }) }))] })] })] }), jsxs(Dialog, { open: docsDialogOpen, onOpenChange: setDocsDialogOpen, children: [jsxs(Tooltip, { children: [jsx(TooltipTrigger, { asChild: true, children: jsx(DialogTrigger, { asChild: true, children: jsx(Button, { variant: "outline", size: "sm", className: "gap-2", "data-testid": "button-docs", children: jsx(BookOpen, { className: "h-4 w-4" }) }) }) }), jsx(TooltipContent, { children: "Documentation" })] }), jsxs(DialogContent, { className: "max-w-4xl max-h-[85vh]", children: [jsxs(DialogHeader, { children: [jsx(DialogTitle, { children: "FormBuilder Documentation" }), jsx(DialogDescription, { children: "Complete guide for the drag-and-drop form builder component" })] }), jsx(ScrollArea, { className: "h-[60vh] pr-4", children: jsxs("div", { className: "prose prose-sm dark:prose-invert max-w-none space-y-6", children: [jsxs("section", { children: [jsx("h3", { className: "text-lg font-semibold border-b pb-2", children: "Quick Start" }), jsx("pre", { className: "bg-muted p-3 rounded text-xs overflow-x-auto text-foreground", children: `import { FormBuilder } from '@enerjisaformlibrary/formbuilder-react';
|
|
28571
|
+
import '@enerjisaformlibrary/formbuilder-react/styles.css';
|
|
28572
|
+
|
|
28573
|
+
function App() {
|
|
28574
|
+
return (
|
|
28575
|
+
<FormBuilder
|
|
28576
|
+
onSave={(form) => saveToDatabase(JSON.stringify(form))}
|
|
28577
|
+
onChange={(form) => console.log('Updated:', form)}
|
|
28578
|
+
theme="light"
|
|
28579
|
+
/>
|
|
28580
|
+
);
|
|
28581
|
+
}` })] }), jsxs("section", { children: [jsx("h3", { className: "text-lg font-semibold border-b pb-2", children: "Field Types (20+)" }), jsxs("div", { className: "grid grid-cols-3 gap-2 text-sm", children: [jsxs("div", { children: [jsx("strong", { children: "Basic:" }), " input, textarea, number, email, password, phone, url"] }), jsxs("div", { children: [jsx("strong", { children: "Selection:" }), " dropdown, checkbox, radio, toggle, multiselect"] }), jsxs("div", { children: [jsx("strong", { children: "Date/Time:" }), " date, time, daterange"] }), jsxs("div", { children: [jsx("strong", { children: "Advanced:" }), " file, signature, rating, richtext, slider, color, autocomplete"] }), jsxs("div", { children: [jsx("strong", { children: "Static:" }), " header, label, paragraph, divider, spacer, alert, image"] }), jsxs("div", { children: [jsx("strong", { children: "Structure:" }), " button, qrcode, pattern, container"] })] })] }), jsxs("section", { children: [jsx("h3", { className: "text-lg font-semibold border-b pb-2", children: "Form Schema Structure" }), jsx("pre", { className: "bg-muted p-3 rounded text-xs overflow-x-auto text-foreground", children: `FormSchema {
|
|
28582
|
+
id: string;
|
|
28583
|
+
name: string;
|
|
28584
|
+
rows: FormRow[]; // Single-page mode
|
|
28585
|
+
steps?: FormStep[]; // Multi-step wizard mode
|
|
28586
|
+
isMultiStep?: boolean;
|
|
28587
|
+
submissionConfig?: { webhookUrl, successMessage, redirectUrl };
|
|
28588
|
+
}
|
|
28589
|
+
|
|
28590
|
+
FormRow → columns[] → FormColumn (width: 1-12) → fields[] → FormField` })] }), jsxs("section", { children: [jsx("h3", { className: "text-lg font-semibold border-b pb-2", children: "FormField Properties" }), jsx("pre", { className: "bg-muted p-3 rounded text-xs overflow-x-auto text-foreground", children: `FormField {
|
|
28591
|
+
id: string;
|
|
28592
|
+
type: 'input' | 'dropdown' | 'checkbox' | ...;
|
|
28593
|
+
props: {
|
|
28594
|
+
key: string; // Unique identifier for form values
|
|
28595
|
+
label?: string;
|
|
28596
|
+
placeholder?: string;
|
|
28597
|
+
defaultValue?: string;
|
|
28598
|
+
tooltip?: string; // Help text on hover
|
|
28599
|
+
autoFocus?: boolean; // Focus on preview mode
|
|
28600
|
+
hidden?: boolean;
|
|
28601
|
+
disabled?: boolean;
|
|
28602
|
+
readOnly?: boolean;
|
|
28603
|
+
size?: 'small' | 'medium' | 'large';
|
|
28604
|
+
};
|
|
28605
|
+
validation?: { required, minLength, maxLength, pattern, min, max };
|
|
28606
|
+
conditionalLogic?: { enabled, action, conditions[] };
|
|
28607
|
+
customStyle?: { containerClassName, labelClassName, inputClassName };
|
|
28608
|
+
events?: { onClick, onChange, onFocus, onBlur };
|
|
28609
|
+
}` })] }), jsxs("section", { children: [jsx("h3", { className: "text-lg font-semibold border-b pb-2", children: "Conditional Logic" }), jsx("p", { className: "text-sm text-muted-foreground mb-2", children: "Show/hide/enable/disable fields based on other field values" }), jsx("pre", { className: "bg-muted p-3 rounded text-xs overflow-x-auto text-foreground", children: `conditionalLogic: {
|
|
28610
|
+
enabled: true,
|
|
28611
|
+
action: 'show' | 'hide' | 'enable' | 'disable' | 'require',
|
|
28612
|
+
conditions: [{
|
|
28613
|
+
field: 'country', // Target field's props.key
|
|
28614
|
+
operator: 'equals' | 'notEquals' | 'contains' | 'isEmpty' | ...,
|
|
28615
|
+
value: 'USA'
|
|
28616
|
+
}],
|
|
28617
|
+
conditionLogic: 'and' | 'or' // How to combine multiple conditions
|
|
28618
|
+
}` })] }), jsxs("section", { children: [jsx("h3", { className: "text-lg font-semibold border-b pb-2", children: "Events & Actions" }), jsx("p", { className: "text-sm text-muted-foreground mb-2", children: "Execute actions on field interactions (preview mode only)" }), jsxs("div", { className: "text-sm mb-2", children: [jsx("strong", { children: "Available Actions:" }), jsxs("ul", { className: "list-disc list-inside mt-1", children: [jsxs("li", { children: [jsx("code", { children: "focusField" }), " - Focus another field (args: targetFieldKey)"] }), jsxs("li", { children: [jsx("code", { children: "clearField" }), " - Clear current field value"] }), jsxs("li", { children: [jsx("code", { children: "showMessage" }), " - Display alert (args: message)"] }), jsxs("li", { children: [jsx("code", { children: "reset" }), " - Reset all form values"] })] })] }), jsx("pre", { className: "bg-muted p-3 rounded text-xs overflow-x-auto text-foreground", children: `events: {
|
|
28619
|
+
onChange: [{
|
|
28620
|
+
type: 'common',
|
|
28621
|
+
name: 'focusField',
|
|
28622
|
+
args: { targetFieldKey: 'nextField' }
|
|
28623
|
+
}]
|
|
28624
|
+
}` })] }), jsxs("section", { children: [jsx("h3", { className: "text-lg font-semibold border-b pb-2", children: "Zustand Store API" }), jsx("pre", { className: "bg-muted p-3 rounded text-xs overflow-x-auto text-foreground", children: `import { useFormStore } from '@enerjisaformlibrary/formbuilder-react';
|
|
28625
|
+
|
|
28626
|
+
const {
|
|
28627
|
+
form, // Current FormSchema
|
|
28628
|
+
formValues, // Values in preview mode
|
|
28629
|
+
previewMode, // boolean
|
|
28630
|
+
loadForm, // Load existing form
|
|
28631
|
+
addRow, deleteRow, // Row operations
|
|
28632
|
+
addField, updateField, // Field operations
|
|
28633
|
+
setFieldValue, // Set value in preview
|
|
28634
|
+
getFormValues, // Get all form values
|
|
28635
|
+
} = useFormStore();` })] }), jsxs("section", { children: [jsx("h3", { className: "text-lg font-semibold border-b pb-2", children: "Container Fields" }), jsx("p", { className: "text-sm text-muted-foreground mb-2", children: "Group fields with nested row/column structure" }), jsx("pre", { className: "bg-muted p-3 rounded text-xs overflow-x-auto text-foreground", children: `{
|
|
28636
|
+
type: 'container',
|
|
28637
|
+
props: {
|
|
28638
|
+
key: 'addressGroup',
|
|
28639
|
+
label: 'Address',
|
|
28640
|
+
containerConfig: {
|
|
28641
|
+
rows: [{
|
|
28642
|
+
id: 'row1',
|
|
28643
|
+
columns: [
|
|
28644
|
+
{ id: 'col1', width: 6, fields: [...] },
|
|
28645
|
+
{ id: 'col2', width: 6, fields: [...] }
|
|
28646
|
+
]
|
|
28647
|
+
}]
|
|
28648
|
+
}
|
|
28649
|
+
}
|
|
28650
|
+
}` })] }), jsxs("section", { children: [jsx("h3", { className: "text-lg font-semibold border-b pb-2", children: "Multi-Step Forms" }), jsx("pre", { className: "bg-muted p-3 rounded text-xs overflow-x-auto text-foreground", children: `{
|
|
28651
|
+
isMultiStep: true,
|
|
28652
|
+
steps: [{
|
|
28653
|
+
id: 'step1',
|
|
28654
|
+
title: 'Personal Info',
|
|
28655
|
+
order: 0,
|
|
28656
|
+
rows: [...],
|
|
28657
|
+
validation: { validateOnNext: true }
|
|
28658
|
+
}, {
|
|
28659
|
+
id: 'step2',
|
|
28660
|
+
title: 'Contact',
|
|
28661
|
+
order: 1,
|
|
28662
|
+
rows: [...]
|
|
28663
|
+
}]
|
|
28664
|
+
}` })] })] }) }), jsx(DialogFooter, { children: jsx(Button, { onClick: () => setDocsDialogOpen(false), children: "Close" }) })] })] }), jsxs(Dialog, { open: settingsDialogOpen, onOpenChange: setSettingsDialogOpen, children: [jsx(DialogTrigger, { asChild: true, children: jsx(Button, { variant: "outline", size: "sm", className: "gap-2", "data-testid": "button-settings", children: jsx(Settings, { className: "h-4 w-4" }) }) }), jsxs(DialogContent, { className: "max-w-lg", children: [jsxs(DialogHeader, { children: [jsx(DialogTitle, { children: "Form Settings" }), jsx(DialogDescription, { children: "Configure submission, webhooks, and other form settings." })] }), jsx("div", { className: "space-y-6", children: jsxs("div", { className: "space-y-4", children: [jsx("h4", { className: "font-medium", children: "Submission Settings" }), jsxs("div", { className: "flex items-center justify-between", children: [jsx(Label$2, { children: "Enable Submission Handler" }), jsx(Switch, { checked: form.submissionConfig?.enabled || false, onCheckedChange: (checked) => updateSubmissionConfig({ enabled: checked }) })] }), form.submissionConfig?.enabled && (jsxs(Fragment, { children: [jsxs("div", { className: "space-y-2", children: [jsx(Label$2, { children: "Webhook URL" }), jsx(Input, { value: form.submissionConfig?.webhookUrl || '', onChange: (e) => updateSubmissionConfig({ webhookUrl: e.target.value }), placeholder: "https://your-api.com/webhook" })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$2, { children: "Success Message" }), jsx(Input, { value: form.submissionConfig?.successMessage || '', onChange: (e) => updateSubmissionConfig({ successMessage: e.target.value }), placeholder: "Thank you for your submission!" })] }), jsxs("div", { className: "space-y-2", children: [jsx(Label$2, { children: "Redirect URL (optional)" }), jsx(Input, { value: form.submissionConfig?.redirectUrl || '', onChange: (e) => updateSubmissionConfig({ redirectUrl: e.target.value }), placeholder: "https://your-site.com/thank-you" })] })] }))] }) }), jsx(DialogFooter, { children: jsx(Button, { onClick: () => setSettingsDialogOpen(false), children: "Done" }) })] })] }), jsxs(DropdownMenu, { children: [jsx(DropdownMenuTrigger, { asChild: true, children: jsx(Button, { variant: "outline", size: "sm", className: "gap-2", "data-testid": "button-more", children: jsx(FileJson, { className: "h-4 w-4" }) }) }), jsxs(DropdownMenuContent, { align: "end", children: [jsx(DropdownMenuLabel, { children: "Export / Import" }), jsx(DropdownMenuSeparator, {}), jsxs(DropdownMenuItem, { onClick: onOpenJsonViewer, children: [jsx(FileJson, { className: "h-4 w-4 mr-2" }), "View JSON Schema"] }), jsxs(DropdownMenuItem, { onClick: handleExportJson, children: [jsx(Download, { className: "h-4 w-4 mr-2" }), "Download JSON"] }), jsx(DropdownMenuItem, { asChild: true, children: jsxs("label", { className: "cursor-pointer flex items-center", children: [jsx(Upload, { className: "h-4 w-4 mr-2" }), "Import JSON", jsx("input", { type: "file", accept: ".json", onChange: handleImportJson, className: "hidden" })] }) })] })] }), jsx(Button, { variant: "outline", size: "sm", onClick: clearForm, className: "gap-2 text-destructive hover:text-destructive", "data-testid": "button-clear-form", children: jsx(Trash2, { className: "h-4 w-4" }) }), jsx("div", { className: "w-px h-6 bg-border mx-1" }), jsxs(Tooltip, { children: [jsx(TooltipTrigger, { asChild: true, children: jsx(Button, { variant: "ghost", size: "icon", onClick: toggleTheme, "data-testid": "button-toggle-theme", children: isDarkMode ? jsx(Sun, { className: "h-4 w-4" }) : jsx(Moon, { className: "h-4 w-4" }) }) }), jsx(TooltipContent, { children: isDarkMode ? 'Light Mode' : 'Dark Mode' })] })] })] }));
|
|
28436
28665
|
}
|
|
28437
28666
|
|
|
28438
28667
|
const iconMap = {
|