form-builder-pro 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.css CHANGED
@@ -594,6 +594,42 @@ video {
594
594
  max-width: 1536px;
595
595
  }
596
596
  }
597
+ /* ===== MOBILE-FIRST RESPONSIVE LAYOUT ===== */
598
+ /* On mobile (< 768px): Stack vertically, make sidebars collapsible */
599
+ /* On desktop (≥ 768px): Horizontal layout with fixed sidebars */
600
+ /* Make sidebars collapsible on mobile only */
601
+ @media (max-width: 767px) {
602
+
603
+ /* Toolbox: Collapsible on mobile */
604
+ .form-builder-toolbox-wrapper {
605
+ max-height: 300px;
606
+ overflow-y: auto;
607
+ border-bottom: 1px solid rgb(229 231 235);
608
+ }
609
+
610
+ .form-builder-toolbox-wrapper.collapsed {
611
+ max-height: 48px;
612
+ overflow: hidden;
613
+ }
614
+
615
+ /* Config panel: Collapsible on mobile */
616
+ .form-builder-config-wrapper {
617
+ max-height: 400px;
618
+ overflow-y: auto;
619
+ border-top: 1px solid rgb(229 231 235);
620
+ }
621
+
622
+ .form-builder-config-wrapper.collapsed {
623
+ max-height: 48px;
624
+ overflow: hidden;
625
+ }
626
+
627
+ /* Canvas gets more space on mobile */
628
+ .form-builder-canvas {
629
+ flex: 1;
630
+ min-height: 400px;
631
+ }
632
+ }
597
633
  /* Grid column spans - explicit !important to override Angular styles */
598
634
  .form-builder-field-wrapper.selected-field {
599
635
  border: 2px solid #3b82f6 !important;
@@ -645,6 +681,29 @@ video {
645
681
  grid-template-columns: repeat(12, minmax(0, 1fr)) !important;
646
682
  gap: 1rem !important;
647
683
  }
684
+ /* Responsive grid: fewer columns on mobile */
685
+ @media (max-width: 767px) {
686
+ .form-builder-grid {
687
+ grid-template-columns: repeat(1, minmax(0, 1fr)) !important;
688
+ gap: 0.75rem !important;
689
+ }
690
+
691
+ /* All fields full width on mobile */
692
+ .form-builder-grid .col-span-1,
693
+ .form-builder-grid .col-span-2,
694
+ .form-builder-grid .col-span-3,
695
+ .form-builder-grid .col-span-4,
696
+ .form-builder-grid .col-span-5,
697
+ .form-builder-grid .col-span-6,
698
+ .form-builder-grid .col-span-7,
699
+ .form-builder-grid .col-span-8,
700
+ .form-builder-grid .col-span-9,
701
+ .form-builder-grid .col-span-10,
702
+ .form-builder-grid .col-span-11,
703
+ .form-builder-grid .col-span-12 {
704
+ grid-column: span 1 / span 1 !important;
705
+ }
706
+ }
648
707
  body {
649
708
  font-family: Poppins, sans-serif;
650
709
  }
@@ -751,9 +810,6 @@ body {
751
810
  .aspect-square {
752
811
  aspect-ratio: 1 / 1;
753
812
  }
754
- .h-10 {
755
- height: 2.5rem;
756
- }
757
813
  .h-4 {
758
814
  height: 1rem;
759
815
  }
@@ -778,15 +834,15 @@ body {
778
834
  .min-h-\[80px\] {
779
835
  min-height: 80px;
780
836
  }
837
+ .min-h-touch {
838
+ min-height: 44px;
839
+ }
781
840
  .w-4 {
782
841
  width: 1rem;
783
842
  }
784
843
  .w-5 {
785
844
  width: 1.25rem;
786
845
  }
787
- .w-80 {
788
- width: 20rem;
789
- }
790
846
  .w-9 {
791
847
  width: 2.25rem;
792
848
  }
@@ -846,21 +902,11 @@ body {
846
902
  margin-top: calc(0.75rem * calc(1 - var(--tw-space-y-reverse)));
847
903
  margin-bottom: calc(0.75rem * var(--tw-space-y-reverse));
848
904
  }
849
- .space-y-4 > :not([hidden]) ~ :not([hidden]) {
850
- --tw-space-y-reverse: 0;
851
- margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse)));
852
- margin-bottom: calc(1rem * var(--tw-space-y-reverse));
853
- }
854
905
  .space-y-6 > :not([hidden]) ~ :not([hidden]) {
855
906
  --tw-space-y-reverse: 0;
856
907
  margin-top: calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));
857
908
  margin-bottom: calc(1.5rem * var(--tw-space-y-reverse));
858
909
  }
859
- .space-y-8 > :not([hidden]) ~ :not([hidden]) {
860
- --tw-space-y-reverse: 0;
861
- margin-top: calc(2rem * calc(1 - var(--tw-space-y-reverse)));
862
- margin-bottom: calc(2rem * var(--tw-space-y-reverse));
863
- }
864
910
  .overflow-hidden {
865
911
  overflow: hidden;
866
912
  }
@@ -909,6 +955,9 @@ body {
909
955
  .border-r {
910
956
  border-right-width: 1px;
911
957
  }
958
+ .border-t {
959
+ border-top-width: 1px;
960
+ }
912
961
  .border-dashed {
913
962
  border-style: dashed;
914
963
  }
@@ -1027,10 +1076,6 @@ body {
1027
1076
  .text-center {
1028
1077
  text-align: center;
1029
1078
  }
1030
- .text-2xl {
1031
- font-size: 1.5rem;
1032
- line-height: 2rem;
1033
- }
1034
1079
  .text-lg {
1035
1080
  font-size: 1.125rem;
1036
1081
  line-height: 1.75rem;
@@ -1393,4 +1438,94 @@ body {
1393
1438
  .dark\:hover\:bg-gray-800:hover:is(.dark *) {
1394
1439
  --tw-bg-opacity: 1;
1395
1440
  background-color: rgb(31 41 55 / var(--tw-bg-opacity, 1));
1441
+ }
1442
+
1443
+ @media (min-width: 640px) {
1444
+
1445
+ .sm\:h-5 {
1446
+ height: 1.25rem;
1447
+ }
1448
+
1449
+ .sm\:h-6 {
1450
+ height: 1.5rem;
1451
+ }
1452
+
1453
+ .sm\:w-5 {
1454
+ width: 1.25rem;
1455
+ }
1456
+
1457
+ .sm\:w-6 {
1458
+ width: 1.5rem;
1459
+ }
1460
+
1461
+ .sm\:w-auto {
1462
+ width: auto;
1463
+ }
1464
+
1465
+ .sm\:justify-start {
1466
+ justify-content: flex-start;
1467
+ }
1468
+
1469
+ .sm\:text-base {
1470
+ font-size: 1rem;
1471
+ line-height: 1.5rem;
1472
+ }
1473
+
1474
+ .sm\:text-sm {
1475
+ font-size: 0.875rem;
1476
+ line-height: 1.25rem;
1477
+ }
1478
+ }
1479
+
1480
+ @media (min-width: 768px) {
1481
+
1482
+ .md\:w-80 {
1483
+ width: 20rem;
1484
+ }
1485
+
1486
+ .md\:flex-row {
1487
+ flex-direction: row;
1488
+ }
1489
+
1490
+ .md\:space-y-4 > :not([hidden]) ~ :not([hidden]) {
1491
+ --tw-space-y-reverse: 0;
1492
+ margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse)));
1493
+ margin-bottom: calc(1rem * var(--tw-space-y-reverse));
1494
+ }
1495
+
1496
+ .md\:space-y-8 > :not([hidden]) ~ :not([hidden]) {
1497
+ --tw-space-y-reverse: 0;
1498
+ margin-top: calc(2rem * calc(1 - var(--tw-space-y-reverse)));
1499
+ margin-bottom: calc(2rem * var(--tw-space-y-reverse));
1500
+ }
1501
+
1502
+ .md\:border-b-0 {
1503
+ border-bottom-width: 0px;
1504
+ }
1505
+
1506
+ .md\:border-l {
1507
+ border-left-width: 1px;
1508
+ }
1509
+
1510
+ .md\:border-r {
1511
+ border-right-width: 1px;
1512
+ }
1513
+
1514
+ .md\:border-t-0 {
1515
+ border-top-width: 0px;
1516
+ }
1517
+
1518
+ .md\:p-8 {
1519
+ padding: 2rem;
1520
+ }
1521
+
1522
+ .md\:text-2xl {
1523
+ font-size: 1.5rem;
1524
+ line-height: 2rem;
1525
+ }
1526
+
1527
+ .md\:text-xl {
1528
+ font-size: 1.25rem;
1529
+ line-height: 1.75rem;
1530
+ }
1396
1531
  }
package/dist/index.js CHANGED
@@ -4501,7 +4501,7 @@ var FieldRenderer = class {
4501
4501
  const wrapper = createElement("div", { className: "w-full" });
4502
4502
  if (field.type !== "checkbox") {
4503
4503
  const label = createElement("label", {
4504
- className: "text-sm font-medium leading-none mb-2 block text-gray-900 dark:text-gray-100",
4504
+ className: "text-xs sm:text-sm font-medium leading-none mb-2 block text-gray-900 dark:text-gray-100",
4505
4505
  text: field.label
4506
4506
  });
4507
4507
  if (field.required) {
@@ -4510,7 +4510,7 @@ var FieldRenderer = class {
4510
4510
  wrapper.appendChild(label);
4511
4511
  } else {
4512
4512
  const label = createElement("label", {
4513
- className: "text-sm font-medium leading-none mb-2 block text-gray-900 dark:text-gray-100",
4513
+ className: "text-xs sm:text-sm font-medium leading-none mb-2 block text-gray-900 dark:text-gray-100",
4514
4514
  text: field.label
4515
4515
  });
4516
4516
  if (field.required) {
@@ -4522,7 +4522,7 @@ var FieldRenderer = class {
4522
4522
  switch (field.type) {
4523
4523
  case "textarea":
4524
4524
  input = createElement("textarea", {
4525
- className: "flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
4525
+ className: "flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm sm:text-base ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
4526
4526
  placeholder: field.placeholder,
4527
4527
  value: value || "",
4528
4528
  disabled: readOnly,
@@ -4531,7 +4531,7 @@ var FieldRenderer = class {
4531
4531
  break;
4532
4532
  case "select":
4533
4533
  input = createElement("select", {
4534
- className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
4534
+ className: "flex min-h-touch w-full rounded-md border border-input bg-background px-3 py-2 text-sm sm:text-base ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
4535
4535
  value: value || "",
4536
4536
  disabled: readOnly,
4537
4537
  onchange: (e) => onChange?.(e.target.value)
@@ -4542,10 +4542,10 @@ var FieldRenderer = class {
4542
4542
  });
4543
4543
  break;
4544
4544
  case "checkbox":
4545
- input = createElement("div", { className: "flex items-center h-10" });
4545
+ input = createElement("div", { className: "flex items-center min-h-touch" });
4546
4546
  const checkbox = createElement("input", {
4547
4547
  type: "checkbox",
4548
- className: "h-5 w-5 rounded border-gray-300 text-primary focus:ring-primary cursor-pointer",
4548
+ className: "h-5 w-5 sm:h-6 sm:w-6 rounded border-gray-300 text-primary focus:ring-primary cursor-pointer",
4549
4549
  checked: !!value,
4550
4550
  disabled: readOnly,
4551
4551
  onchange: (e) => onChange?.(e.target.checked)
@@ -4555,18 +4555,18 @@ var FieldRenderer = class {
4555
4555
  case "radio":
4556
4556
  input = createElement("div", { className: "space-y-2" });
4557
4557
  field.options?.forEach((opt) => {
4558
- const radioWrapper = createElement("div", { className: "flex items-center space-x-2" });
4558
+ const radioWrapper = createElement("div", { className: "flex items-center space-x-2 min-h-touch" });
4559
4559
  const radio = createElement("input", {
4560
4560
  type: "radio",
4561
4561
  name: field.id,
4562
4562
  value: opt.value,
4563
4563
  checked: value === opt.value,
4564
4564
  disabled: readOnly,
4565
- className: "aspect-square h-4 w-4 rounded-full border border-primary text-primary ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
4565
+ className: "aspect-square h-4 w-4 sm:h-5 sm:w-5 rounded-full border border-primary text-primary ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
4566
4566
  onchange: (e) => onChange?.(e.target.value)
4567
4567
  });
4568
4568
  const radioLabel = createElement("label", {
4569
- className: "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
4569
+ className: "text-xs sm:text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
4570
4570
  text: opt.label
4571
4571
  });
4572
4572
  radioWrapper.appendChild(radio);
@@ -4577,7 +4577,7 @@ var FieldRenderer = class {
4577
4577
  default:
4578
4578
  input = createElement("input", {
4579
4579
  type: field.type === "phone" ? "tel" : field.type,
4580
- className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
4580
+ className: "flex min-h-touch w-full rounded-md border border-input bg-background px-3 py-2 text-sm sm:text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
4581
4581
  placeholder: field.placeholder,
4582
4582
  value: value || "",
4583
4583
  disabled: readOnly,
@@ -4586,7 +4586,7 @@ var FieldRenderer = class {
4586
4586
  }
4587
4587
  wrapper.appendChild(input);
4588
4588
  if (field.description) {
4589
- wrapper.appendChild(createElement("p", { className: "text-sm text-muted-foreground mt-1", text: field.description }));
4589
+ wrapper.appendChild(createElement("p", { className: "text-xs sm:text-sm text-muted-foreground mt-1", text: field.description }));
4590
4590
  }
4591
4591
  return wrapper;
4592
4592
  }
@@ -4610,11 +4610,11 @@ var FormRenderer = class {
4610
4610
  }
4611
4611
  render() {
4612
4612
  this.container.innerHTML = "";
4613
- const form = createElement("form", { className: "space-y-8" });
4614
- form.appendChild(createElement("h1", { className: "text-2xl font-bold text-gray-900 dark:text-white", text: this.schema.title }));
4613
+ const form = createElement("form", { className: "space-y-6 md:space-y-8" });
4614
+ form.appendChild(createElement("h1", { className: "text-xl md:text-2xl font-bold text-gray-900 dark:text-white", text: this.schema.title }));
4615
4615
  this.schema.sections.forEach((section) => {
4616
- const sectionEl = createElement("div", { className: "space-y-4" });
4617
- sectionEl.appendChild(createElement("h2", { className: "text-xl font-semibold text-gray-800 dark:text-gray-200 border-b pb-2", text: section.title }));
4616
+ const sectionEl = createElement("div", { className: "space-y-3 md:space-y-4" });
4617
+ sectionEl.appendChild(createElement("h2", { className: "text-lg md:text-xl font-semibold text-gray-800 dark:text-gray-200 border-b pb-2", text: section.title }));
4618
4618
  const grid = createElement("div", { className: "form-builder-grid" });
4619
4619
  section.fields.forEach((field) => {
4620
4620
  const fieldWrapper = createElement("div");
@@ -4641,14 +4641,14 @@ var FormRenderer = class {
4641
4641
  });
4642
4642
  const submitBtn = createElement("button", {
4643
4643
  type: "submit",
4644
- className: "px-6 py-2 bg-blue-600 text-white font-medium rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition-colors",
4644
+ className: "w-full sm:w-auto px-6 py-3 min-h-touch bg-blue-600 text-white font-medium rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition-colors",
4645
4645
  text: "Submit"
4646
4646
  });
4647
4647
  form.onsubmit = (e) => {
4648
4648
  e.preventDefault();
4649
4649
  this.onSubmit?.(this.data);
4650
4650
  };
4651
- const btnWrapper = createElement("div", { className: "pt-4" });
4651
+ const btnWrapper = createElement("div", { className: "pt-4 flex justify-center sm:justify-start" });
4652
4652
  btnWrapper.appendChild(submitBtn);
4653
4653
  form.appendChild(btnWrapper);
4654
4654
  this.container.appendChild(form);
@@ -7007,7 +7007,7 @@ var FormBuilder = class {
7007
7007
  this.container.innerHTML = "";
7008
7008
  const wrapper = createElement("div", { className: "flex flex-col h-screen " });
7009
7009
  wrapper.appendChild(this.renderToolbar(state));
7010
- const main = createElement("div", { className: "flex flex-1 overflow-hidden" });
7010
+ const main = createElement("div", { className: "flex flex-col md:flex-row flex-1 overflow-hidden" });
7011
7011
  if (state.isPreviewMode) {
7012
7012
  const previewContainer = createElement("div", { className: "flex-1 p-8 overflow-y-auto bg-white dark:bg-gray-900 flex justify-center" });
7013
7013
  const inner = createElement("div", { className: "w-full max-w-3xl" });
@@ -7015,9 +7015,15 @@ var FormBuilder = class {
7015
7015
  previewContainer.appendChild(inner);
7016
7016
  main.appendChild(previewContainer);
7017
7017
  } else {
7018
- main.appendChild(this.renderToolbox());
7019
- main.appendChild(this.renderCanvas(state));
7020
- main.appendChild(this.renderConfigPanel(state));
7018
+ const toolboxWrapper = createElement("div", { className: "form-builder-toolbox-wrapper w-full md:w-80 bg-white dark:bg-gray-900 border-r md:border-r border-b md:border-b-0 border-gray-200 dark:border-gray-800" });
7019
+ toolboxWrapper.appendChild(this.renderToolbox());
7020
+ main.appendChild(toolboxWrapper);
7021
+ const canvasWrapper = createElement("div", { className: "form-builder-canvas flex-1 overflow-y-auto" });
7022
+ canvasWrapper.appendChild(this.renderCanvas(state));
7023
+ main.appendChild(canvasWrapper);
7024
+ const configWrapper = createElement("div", { className: "form-builder-config-wrapper w-full md:w-80 bg-white dark:bg-gray-900 border-l md:border-l border-t md:border-t-0 border-gray-200 dark:border-gray-800" });
7025
+ configWrapper.appendChild(this.renderConfigPanel(state));
7026
+ main.appendChild(configWrapper);
7021
7027
  }
7022
7028
  wrapper.appendChild(main);
7023
7029
  this.container.appendChild(wrapper);
@@ -7115,7 +7121,7 @@ var FormBuilder = class {
7115
7121
  return toolbar;
7116
7122
  }
7117
7123
  renderToolbox() {
7118
- const toolbox = createElement("div", { className: "w-80 bg-white dark:bg-gray-900 border-r border-gray-200 dark:border-gray-800 flex flex-col h-full" });
7124
+ const toolbox = createElement("div", { className: "bg-white dark:bg-gray-900 flex flex-col h-full" });
7119
7125
  const tabs = createElement("div", { className: "flex border-b border-gray-200 dark:border-gray-800" });
7120
7126
  const createTab = (id, label) => {
7121
7127
  const isActive = this.activeTab === id;
@@ -7214,7 +7220,7 @@ var FormBuilder = class {
7214
7220
  }
7215
7221
  renderCanvas(state) {
7216
7222
  const canvas = createElement("div", {
7217
- className: "flex-1 bg-white dark:bg-gray-950 p-8 overflow-y-auto h-full",
7223
+ className: "flex-1 bg-white dark:bg-gray-950 p-4 md:p-8 overflow-y-auto",
7218
7224
  onclick: (e) => {
7219
7225
  if (e.target === canvas || e.target === canvas.firstElementChild) {
7220
7226
  formStore.getState().selectField(null);
@@ -7331,7 +7337,7 @@ var FormBuilder = class {
7331
7337
  return canvas;
7332
7338
  }
7333
7339
  renderConfigPanel(state) {
7334
- const panel = createElement("div", { className: "w-80 bg-white dark:bg-gray-900 border-l border-gray-200 dark:border-gray-800 flex flex-col h-full" });
7340
+ const panel = createElement("div", { className: "bg-white dark:bg-gray-900 flex flex-col h-full" });
7335
7341
  const selectedField = state.schema.sections.flatMap((s) => s.fields).find((f) => f.id === state.selectedFieldId);
7336
7342
  if (!selectedField) {
7337
7343
  panel.appendChild(createElement("div", { className: "p-6 text-center text-gray-500", text: "Select a field to configure" }));