@squaredr/fieldcraft-pro 0.0.5 → 1.0.0
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/LICENSE.txt +48 -10
- package/README.md +37 -6
- package/dist/{chunk-APHGEYG4.mjs → chunk-DDQDYTMB.mjs} +155 -129
- package/dist/chunk-LOFGBNJV.mjs +976 -0
- package/dist/chunk-QQ4JZGTD.mjs +9 -0
- package/dist/form-builder/index.d.mts +2 -2
- package/dist/form-builder/index.d.ts +2 -2
- package/dist/form-builder/index.js +150 -119
- package/dist/form-builder/index.mjs +2 -1
- package/dist/index.js +983 -443
- package/dist/index.mjs +4 -3
- package/dist/response-viewer/index.d.mts +4 -1
- package/dist/response-viewer/index.d.ts +4 -1
- package/dist/response-viewer/index.js +839 -324
- package/dist/response-viewer/index.mjs +2 -1
- package/dist/styles.css +1 -1
- package/package.json +13 -12
- package/dist/chunk-NIKQEWPG.mjs +0 -467
- /package/{dist/theme-editor-styles.css → theme-editor/styles.css} +0 -0
package/LICENSE.txt
CHANGED
|
@@ -1,13 +1,51 @@
|
|
|
1
|
-
|
|
1
|
+
SquaredR Commercial License
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
and confidential. Unauthorized copying, modification, distribution, or use of this
|
|
5
|
-
Software, via any medium, is strictly prohibited.
|
|
3
|
+
Copyright (c) 2024-2026 SquaredR. All rights reserved.
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
https://squaredr.tech/legal/fieldcraft-pro-license for full terms.
|
|
5
|
+
This software and associated documentation files (the "Software") are the
|
|
6
|
+
proprietary property of SquaredR.
|
|
10
7
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
GRANT OF LICENSE
|
|
9
|
+
|
|
10
|
+
Subject to the terms of this license and payment of applicable fees, SquaredR
|
|
11
|
+
grants you a non-exclusive, non-transferable license to:
|
|
12
|
+
|
|
13
|
+
1. Use the Software in a single production application per license key.
|
|
14
|
+
2. Modify the Software for your own internal use within the licensed
|
|
15
|
+
application.
|
|
16
|
+
3. Use the Software in unlimited development and staging environments
|
|
17
|
+
without a license key (development mode).
|
|
18
|
+
|
|
19
|
+
RESTRICTIONS
|
|
20
|
+
|
|
21
|
+
You may NOT:
|
|
22
|
+
|
|
23
|
+
1. Redistribute, sublicense, sell, or otherwise transfer the Software or
|
|
24
|
+
any derivative works to third parties.
|
|
25
|
+
2. Remove or alter any copyright notices, license keys, or proprietary
|
|
26
|
+
markings in the Software.
|
|
27
|
+
3. Use the Software to build a competing product or service.
|
|
28
|
+
4. Share license keys across organizations or unrelated projects.
|
|
29
|
+
|
|
30
|
+
DEVELOPMENT MODE
|
|
31
|
+
|
|
32
|
+
The Software may be used without a license key for development and evaluation
|
|
33
|
+
purposes. Development mode includes visual watermarks and console notices to
|
|
34
|
+
indicate unlicensed usage. Production deployment requires a valid license key.
|
|
35
|
+
|
|
36
|
+
DISCLAIMER OF WARRANTIES
|
|
37
|
+
|
|
38
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
39
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
40
|
+
FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
41
|
+
SQUAREDR BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY ARISING FROM
|
|
42
|
+
THE USE OF THE SOFTWARE.
|
|
43
|
+
|
|
44
|
+
TERMINATION
|
|
45
|
+
|
|
46
|
+
This license is effective until terminated. It will terminate automatically
|
|
47
|
+
if you fail to comply with any term. Upon termination, you must destroy all
|
|
48
|
+
copies of the Software in your possession.
|
|
49
|
+
|
|
50
|
+
For licensing inquiries: support@squaredr.tech
|
|
51
|
+
Purchase a license: https://squaredr.tech/products/fieldcraft/admin-pro#pricing
|
package/README.md
CHANGED
|
@@ -4,9 +4,9 @@ Pro components for [FieldCraft](https://squaredr.tech/products/fieldcraft) — a
|
|
|
4
4
|
|
|
5
5
|
**Includes:**
|
|
6
6
|
|
|
7
|
-
- **Form Builder** — Drag-and-drop visual form designer with
|
|
8
|
-
- **Response Viewer** — Read-only viewer for submitted form data with table and detail views
|
|
9
|
-
- **Theme Editor** — Visual theme editor for form styling and branding with CSS custom properties
|
|
7
|
+
- **Form Builder** — Drag-and-drop visual form designer with 32 field types, undo/redo, import/export, and live preview
|
|
8
|
+
- **Response Viewer** — Read-only viewer for submitted form data with table and detail views, CSV/JSON export
|
|
9
|
+
- **Theme Editor** — Visual theme editor for form styling and branding with CSS custom properties and live preview
|
|
10
10
|
|
|
11
11
|
## Install
|
|
12
12
|
|
|
@@ -48,7 +48,38 @@ import "@squaredr/fieldcraft-pro/styles.css"; // Form Builder style
|
|
|
48
48
|
import "@squaredr/fieldcraft-pro/theme-editor/styles.css"; // Theme Editor styles
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
-
##
|
|
51
|
+
## Response Viewer
|
|
52
|
+
|
|
53
|
+
Display submitted form responses in table or card/detail views:
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
import { ResponseViewer } from "@squaredr/fieldcraft-pro";
|
|
57
|
+
|
|
58
|
+
<ResponseViewer
|
|
59
|
+
schema={schema}
|
|
60
|
+
responses={responses}
|
|
61
|
+
onExport={(format) => console.log(`Export as ${format}`)}
|
|
62
|
+
/>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Supports clinical field rendering (scoring instruments, body diagrams, pain scales, BMI, vitals) when paired with `@squaredr/fieldcraft-pro-telehealth`.
|
|
66
|
+
|
|
67
|
+
## Theme Editor
|
|
68
|
+
|
|
69
|
+
Live-edit form themes with color pickers, typography controls, and shape/spacing sliders:
|
|
70
|
+
|
|
71
|
+
```tsx
|
|
72
|
+
import { ThemeEditor } from "@squaredr/fieldcraft-pro";
|
|
73
|
+
|
|
74
|
+
<ThemeEditor
|
|
75
|
+
onChange={(theme) => console.log(theme)}
|
|
76
|
+
onSave={(theme) => saveToServer(theme)}
|
|
77
|
+
/>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Includes presets: Clean, Dark, Modern, High Contrast, Clinical, Playful.
|
|
81
|
+
|
|
82
|
+
## Field Types (32)
|
|
52
83
|
|
|
53
84
|
| Category | Fields |
|
|
54
85
|
|----------|--------|
|
|
@@ -79,8 +110,8 @@ import { FormBuilder, QUESTION_TYPE_INFO, DEFAULT_PALETTE } from "@squaredr/fiel
|
|
|
79
110
|
## Requirements
|
|
80
111
|
|
|
81
112
|
- React 18 or 19
|
|
82
|
-
- `@squaredr/fieldcraft-core` ^1.
|
|
83
|
-
- `@squaredr/fieldcraft-react` ^1.2.
|
|
113
|
+
- `@squaredr/fieldcraft-core` ^1.3.3
|
|
114
|
+
- `@squaredr/fieldcraft-react` ^1.2.8
|
|
84
115
|
|
|
85
116
|
## License
|
|
86
117
|
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
+
import { cn } from './chunk-QQ4JZGTD.mjs';
|
|
1
2
|
import { requireLicense } from '@squaredr/fieldcraft-pro-license';
|
|
2
|
-
import { createContext, Component, useRef, useEffect, useCallback, useState, useMemo, useContext } from 'react';
|
|
3
|
+
import { forwardRef, createContext, Component, useRef, useEffect, useCallback, useState, useMemo, useContext } from 'react';
|
|
3
4
|
import { DndContext, DragOverlay, useSensors, useSensor, MouseSensor, TouchSensor, useDraggable, useDroppable } from '@dnd-kit/core';
|
|
4
|
-
import { Undo2, Redo2, Upload, Download, Save, Search, ChevronRight, Plus, Settings, Copy, Trash2, X, HelpCircle, MoveVertical, Minus, Video, Image, FileText, PartyPopper, Hand, ShieldCheck, SeparatorHorizontal, Info, Heading, Trophy, EyeOff, Calculator, CreditCard, MapPin, Repeat, Grid3X3, Camera, PenTool, Paperclip, CalendarCheck, CalendarRange, Clock, Calendar, ArrowUpDown, Globe, ToggleLeft,
|
|
5
|
+
import { ChevronDown, Undo2, Redo2, Upload, Download, Save, Search, ChevronRight, Plus, Settings, Copy, Trash2, X, HelpCircle, MoveVertical, Minus, Video, Image, FileText, PartyPopper, Hand, ShieldCheck, SeparatorHorizontal, Info, Heading, Trophy, EyeOff, Calculator, CreditCard, MapPin, Repeat, Grid3X3, Camera, PenTool, Paperclip, CalendarCheck, CalendarRange, Clock, Calendar, ArrowUpDown, Globe, ToggleLeft, CheckSquare, CircleDot, ListOrdered, TrendingUp, BarChart3, Star, SlidersHorizontal, Hash, UserCheck, Link, PhoneCall, Phone, Mail, AlignLeft, Type, GripVertical } from 'lucide-react';
|
|
5
6
|
import { Button, Separator, Input, Badge, Textarea, Label, Switch } from '@squaredr/fieldcraft-react';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
7
|
+
import { validateSchema, FormEngineSchemaError } from '@squaredr/fieldcraft-core';
|
|
8
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
9
9
|
|
|
10
|
-
// src/form-builder/components/FormBuilderGated.tsx
|
|
11
10
|
var MAX_HISTORY = 50;
|
|
12
11
|
function useUndoRedo(currentSchema, setSchema) {
|
|
13
12
|
const historyRef = useRef([currentSchema]);
|
|
@@ -78,6 +77,39 @@ function generateOptionId() {
|
|
|
78
77
|
function deepClone(obj) {
|
|
79
78
|
return structuredClone(obj);
|
|
80
79
|
}
|
|
80
|
+
function cleanCondition(expr, deletedFieldId) {
|
|
81
|
+
if (expr.field === deletedFieldId) return void 0;
|
|
82
|
+
if (expr.field && expr.field !== deletedFieldId) return expr;
|
|
83
|
+
if (expr.conditions) {
|
|
84
|
+
const cleaned = expr.conditions.map((c) => cleanCondition(c, deletedFieldId)).filter((c) => c !== void 0);
|
|
85
|
+
if (cleaned.length === 0) return void 0;
|
|
86
|
+
if (cleaned.length === 1) return cleaned[0];
|
|
87
|
+
return { ...expr, conditions: cleaned };
|
|
88
|
+
}
|
|
89
|
+
return expr;
|
|
90
|
+
}
|
|
91
|
+
function cleanupOrphanConditions(schema, deletedFieldId) {
|
|
92
|
+
for (const section of schema.sections) {
|
|
93
|
+
if (section.showIf) {
|
|
94
|
+
const cleaned = cleanCondition(section.showIf, deletedFieldId);
|
|
95
|
+
if (cleaned) {
|
|
96
|
+
section.showIf = cleaned;
|
|
97
|
+
} else {
|
|
98
|
+
delete section.showIf;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
for (const question of section.questions) {
|
|
102
|
+
if (question.showIf) {
|
|
103
|
+
const cleaned = cleanCondition(question.showIf, deletedFieldId);
|
|
104
|
+
if (cleaned) {
|
|
105
|
+
question.showIf = cleaned;
|
|
106
|
+
} else {
|
|
107
|
+
delete question.showIf;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
81
113
|
function addSection(schema, section, index) {
|
|
82
114
|
const newSchema = deepClone(schema);
|
|
83
115
|
newSchema.sections.splice(index, 0, section);
|
|
@@ -85,7 +117,12 @@ function addSection(schema, section, index) {
|
|
|
85
117
|
}
|
|
86
118
|
function removeSection(schema, sectionId) {
|
|
87
119
|
const newSchema = deepClone(schema);
|
|
120
|
+
const removedSection = newSchema.sections.find((s) => s.id === sectionId);
|
|
121
|
+
const removedFieldIds = removedSection ? removedSection.questions.map((q) => q.id) : [];
|
|
88
122
|
newSchema.sections = newSchema.sections.filter((s) => s.id !== sectionId);
|
|
123
|
+
for (const fieldId of removedFieldIds) {
|
|
124
|
+
cleanupOrphanConditions(newSchema, fieldId);
|
|
125
|
+
}
|
|
89
126
|
return newSchema;
|
|
90
127
|
}
|
|
91
128
|
function updateSection(schema, sectionId, updates) {
|
|
@@ -135,6 +172,7 @@ function removeQuestion(schema, sectionId, questionId) {
|
|
|
135
172
|
const section = newSchema.sections.find((s) => s.id === sectionId);
|
|
136
173
|
if (!section) return schema;
|
|
137
174
|
section.questions = section.questions.filter((q) => q.id !== questionId);
|
|
175
|
+
cleanupOrphanConditions(newSchema, questionId);
|
|
138
176
|
return newSchema;
|
|
139
177
|
}
|
|
140
178
|
function updateQuestion(schema, sectionId, questionId, updates) {
|
|
@@ -904,9 +942,6 @@ var ICON_MAP = {
|
|
|
904
942
|
function getIcon(name) {
|
|
905
943
|
return ICON_MAP[name] ?? HelpCircle;
|
|
906
944
|
}
|
|
907
|
-
function cn(...inputs) {
|
|
908
|
-
return twMerge(clsx(inputs));
|
|
909
|
-
}
|
|
910
945
|
function PaletteItem({ questionType, typeInfo }) {
|
|
911
946
|
const { attributes, listeners, setNodeRef, isDragging } = useDraggable({
|
|
912
947
|
id: `palette-${questionType}`,
|
|
@@ -973,11 +1008,13 @@ function QuestionPalette({ questionTypes, palette }) {
|
|
|
973
1008
|
const isCollapsed = collapsed[category.category] && !search;
|
|
974
1009
|
return /* @__PURE__ */ jsxs("div", { className: "mb-3", children: [
|
|
975
1010
|
/* @__PURE__ */ jsxs(
|
|
976
|
-
|
|
1011
|
+
Button,
|
|
977
1012
|
{
|
|
978
1013
|
type: "button",
|
|
1014
|
+
variant: "ghost",
|
|
1015
|
+
size: "xs",
|
|
979
1016
|
onClick: () => toggleCategory(category.category),
|
|
980
|
-
className: "
|
|
1017
|
+
className: "w-full justify-start px-1 text-[11px] font-semibold uppercase tracking-widest text-muted-foreground",
|
|
981
1018
|
"aria-expanded": !isCollapsed,
|
|
982
1019
|
children: [
|
|
983
1020
|
/* @__PURE__ */ jsx(
|
|
@@ -1341,11 +1378,12 @@ function FormCanvas({ builderState }) {
|
|
|
1341
1378
|
section.id
|
|
1342
1379
|
)),
|
|
1343
1380
|
/* @__PURE__ */ jsxs(
|
|
1344
|
-
|
|
1381
|
+
Button,
|
|
1345
1382
|
{
|
|
1346
1383
|
type: "button",
|
|
1384
|
+
variant: "outline",
|
|
1347
1385
|
onClick: handleAddSection,
|
|
1348
|
-
className: "w-full py-4 text-sm font-medium
|
|
1386
|
+
className: "w-full py-4 h-auto text-sm font-medium border-dashed border-fcb-border-strong text-muted-foreground hover:border-primary hover:text-primary hover:bg-primary/5",
|
|
1349
1387
|
children: [
|
|
1350
1388
|
/* @__PURE__ */ jsx(Plus, { size: 14, strokeWidth: 2 }),
|
|
1351
1389
|
"Add Section"
|
|
@@ -1355,6 +1393,26 @@ function FormCanvas({ builderState }) {
|
|
|
1355
1393
|
] })
|
|
1356
1394
|
] }) });
|
|
1357
1395
|
}
|
|
1396
|
+
var NativeSelect = forwardRef(
|
|
1397
|
+
({ className, wrapperClassName, children, ...props }, ref) => {
|
|
1398
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("relative", wrapperClassName), children: [
|
|
1399
|
+
/* @__PURE__ */ jsx(
|
|
1400
|
+
"select",
|
|
1401
|
+
{
|
|
1402
|
+
ref,
|
|
1403
|
+
className: cn(
|
|
1404
|
+
"flex h-9 w-full appearance-none rounded-md border border-input bg-card px-3 py-1 pr-8 text-sm shadow-xs transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring text-foreground cursor-pointer",
|
|
1405
|
+
className
|
|
1406
|
+
),
|
|
1407
|
+
...props,
|
|
1408
|
+
children
|
|
1409
|
+
}
|
|
1410
|
+
),
|
|
1411
|
+
/* @__PURE__ */ jsx(ChevronDown, { className: "pointer-events-none absolute right-2.5 top-1/2 -translate-y-1/2 size-4 text-muted-foreground" })
|
|
1412
|
+
] });
|
|
1413
|
+
}
|
|
1414
|
+
);
|
|
1415
|
+
NativeSelect.displayName = "NativeSelect";
|
|
1358
1416
|
function useConfigUpdater(question, onUpdate) {
|
|
1359
1417
|
return (field, value) => {
|
|
1360
1418
|
const current = question.config ?? {};
|
|
@@ -1749,18 +1807,7 @@ function SelectField({
|
|
|
1749
1807
|
}) {
|
|
1750
1808
|
return /* @__PURE__ */ jsxs("div", { children: [
|
|
1751
1809
|
/* @__PURE__ */ jsx(Label, { className: "text-xs text-muted-foreground mb-1", children: label }),
|
|
1752
|
-
/* @__PURE__ */
|
|
1753
|
-
/* @__PURE__ */ jsx(
|
|
1754
|
-
"select",
|
|
1755
|
-
{
|
|
1756
|
-
value,
|
|
1757
|
-
onChange: (e) => onChange(e.target.value),
|
|
1758
|
-
className: "flex h-9 w-full appearance-none rounded-md border border-input bg-card px-3 py-1 pr-8 text-sm shadow-xs transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring text-foreground cursor-pointer",
|
|
1759
|
-
children: options.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
|
|
1760
|
-
}
|
|
1761
|
-
),
|
|
1762
|
-
/* @__PURE__ */ jsx("svg", { className: "pointer-events-none absolute right-2.5 top-1/2 -translate-y-1/2 size-4 text-muted-foreground", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" }) })
|
|
1763
|
-
] })
|
|
1810
|
+
/* @__PURE__ */ jsx(NativeSelect, { value, onChange: (e) => onChange(e.target.value), children: options.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value)) })
|
|
1764
1811
|
] });
|
|
1765
1812
|
}
|
|
1766
1813
|
function LikertLabelsEditor({
|
|
@@ -1781,15 +1828,7 @@ function LikertLabelsEditor({
|
|
|
1781
1828
|
return /* @__PURE__ */ jsxs("div", { children: [
|
|
1782
1829
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-2", children: [
|
|
1783
1830
|
/* @__PURE__ */ jsx(Label, { className: "text-xs text-muted-foreground", children: "Scale Labels" }),
|
|
1784
|
-
/* @__PURE__ */ jsx(
|
|
1785
|
-
"button",
|
|
1786
|
-
{
|
|
1787
|
-
type: "button",
|
|
1788
|
-
onClick: handleAdd,
|
|
1789
|
-
className: "text-xs text-primary hover:underline",
|
|
1790
|
-
children: "+ Add"
|
|
1791
|
-
}
|
|
1792
|
-
)
|
|
1831
|
+
/* @__PURE__ */ jsx(Button, { type: "button", variant: "link", size: "xs", onClick: handleAdd, className: "px-0", children: "+ Add" })
|
|
1793
1832
|
] }),
|
|
1794
1833
|
/* @__PURE__ */ jsx("div", { className: "space-y-1.5", children: labels.map((label, index) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 group", children: [
|
|
1795
1834
|
/* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground w-4 text-right shrink-0", children: index + 1 }),
|
|
@@ -1802,11 +1841,13 @@ function LikertLabelsEditor({
|
|
|
1802
1841
|
}
|
|
1803
1842
|
),
|
|
1804
1843
|
labels.length > 2 && /* @__PURE__ */ jsx(
|
|
1805
|
-
|
|
1844
|
+
Button,
|
|
1806
1845
|
{
|
|
1807
1846
|
type: "button",
|
|
1847
|
+
variant: "ghost",
|
|
1848
|
+
size: "icon-xs",
|
|
1808
1849
|
onClick: () => handleRemove(index),
|
|
1809
|
-
className: "text-
|
|
1850
|
+
className: "text-destructive opacity-0 group-hover:opacity-100 transition-opacity",
|
|
1810
1851
|
children: "x"
|
|
1811
1852
|
}
|
|
1812
1853
|
)
|
|
@@ -1835,15 +1876,7 @@ function MatrixItemsEditor({
|
|
|
1835
1876
|
return /* @__PURE__ */ jsxs("div", { children: [
|
|
1836
1877
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-2", children: [
|
|
1837
1878
|
/* @__PURE__ */ jsx(Label, { className: "text-xs text-muted-foreground", children: label }),
|
|
1838
|
-
/* @__PURE__ */ jsx(
|
|
1839
|
-
"button",
|
|
1840
|
-
{
|
|
1841
|
-
type: "button",
|
|
1842
|
-
onClick: handleAdd,
|
|
1843
|
-
className: "text-xs text-primary hover:underline",
|
|
1844
|
-
children: "+ Add"
|
|
1845
|
-
}
|
|
1846
|
-
)
|
|
1879
|
+
/* @__PURE__ */ jsx(Button, { type: "button", variant: "link", size: "xs", onClick: handleAdd, className: "px-0", children: "+ Add" })
|
|
1847
1880
|
] }),
|
|
1848
1881
|
/* @__PURE__ */ jsx("div", { className: "space-y-1.5", children: items.map((item, index) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 group", children: [
|
|
1849
1882
|
/* @__PURE__ */ jsx(
|
|
@@ -1855,11 +1888,13 @@ function MatrixItemsEditor({
|
|
|
1855
1888
|
}
|
|
1856
1889
|
),
|
|
1857
1890
|
items.length > 1 && /* @__PURE__ */ jsx(
|
|
1858
|
-
|
|
1891
|
+
Button,
|
|
1859
1892
|
{
|
|
1860
1893
|
type: "button",
|
|
1894
|
+
variant: "ghost",
|
|
1895
|
+
size: "icon-xs",
|
|
1861
1896
|
onClick: () => handleRemove(index),
|
|
1862
|
-
className: "text-
|
|
1897
|
+
className: "text-destructive opacity-0 group-hover:opacity-100 transition-opacity",
|
|
1863
1898
|
children: "x"
|
|
1864
1899
|
}
|
|
1865
1900
|
)
|
|
@@ -1892,7 +1927,7 @@ function ScoringOptionsEditor({
|
|
|
1892
1927
|
return /* @__PURE__ */ jsxs("div", { children: [
|
|
1893
1928
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-2", children: [
|
|
1894
1929
|
/* @__PURE__ */ jsx(Label, { className: "text-xs text-muted-foreground", children: "Score Options" }),
|
|
1895
|
-
/* @__PURE__ */ jsx(
|
|
1930
|
+
/* @__PURE__ */ jsx(Button, { type: "button", variant: "link", size: "xs", onClick: handleAdd, className: "px-0", children: "+ Add" })
|
|
1896
1931
|
] }),
|
|
1897
1932
|
/* @__PURE__ */ jsx("div", { className: "space-y-1.5", children: options.map((opt, index) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 group", children: [
|
|
1898
1933
|
/* @__PURE__ */ jsx(
|
|
@@ -1914,7 +1949,7 @@ function ScoringOptionsEditor({
|
|
|
1914
1949
|
placeholder: "Score"
|
|
1915
1950
|
}
|
|
1916
1951
|
),
|
|
1917
|
-
options.length > 1 && /* @__PURE__ */ jsx(
|
|
1952
|
+
options.length > 1 && /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "icon-xs", onClick: () => handleRemove(index), className: "text-destructive opacity-0 group-hover:opacity-100 transition-opacity", children: "x" })
|
|
1918
1953
|
] }, index)) })
|
|
1919
1954
|
] });
|
|
1920
1955
|
}
|
|
@@ -1936,7 +1971,7 @@ function ScoreRangesEditor({
|
|
|
1936
1971
|
return /* @__PURE__ */ jsxs("div", { children: [
|
|
1937
1972
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-2", children: [
|
|
1938
1973
|
/* @__PURE__ */ jsx(Label, { className: "text-xs text-muted-foreground", children: "Score Ranges" }),
|
|
1939
|
-
/* @__PURE__ */ jsx(
|
|
1974
|
+
/* @__PURE__ */ jsx(Button, { type: "button", variant: "link", size: "xs", onClick: handleAdd, className: "px-0", children: "+ Add" })
|
|
1940
1975
|
] }),
|
|
1941
1976
|
/* @__PURE__ */ jsx("div", { className: "space-y-2", children: ranges.map((range, index) => /* @__PURE__ */ jsxs("div", { className: "p-2 rounded-md border border-border space-y-1.5 group", children: [
|
|
1942
1977
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
@@ -1944,7 +1979,7 @@ function ScoreRangesEditor({
|
|
|
1944
1979
|
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "to" }),
|
|
1945
1980
|
/* @__PURE__ */ jsx(Input, { type: "number", value: range.max, onChange: (e) => handleUpdate(index, { max: Number(e.target.value) }), className: "h-7 text-xs w-16", placeholder: "Max" }),
|
|
1946
1981
|
/* @__PURE__ */ jsx(Input, { value: range.label, onChange: (e) => handleUpdate(index, { label: e.target.value }), className: "h-7 text-xs flex-1", placeholder: "Label" }),
|
|
1947
|
-
/* @__PURE__ */ jsx(
|
|
1982
|
+
/* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "icon-xs", onClick: () => handleRemove(index), className: "text-destructive opacity-0 group-hover:opacity-100 transition-opacity", children: "x" })
|
|
1948
1983
|
] }),
|
|
1949
1984
|
/* @__PURE__ */ jsx(Input, { value: range.description ?? "", onChange: (e) => handleUpdate(index, { description: e.target.value || void 0 }), className: "h-7 text-xs", placeholder: "Description (optional)" })
|
|
1950
1985
|
] }, index)) })
|
|
@@ -1994,26 +2029,20 @@ function ValidationRulesEditor({ question, onUpdate }) {
|
|
|
1994
2029
|
},
|
|
1995
2030
|
`${rule.type}-${index}`
|
|
1996
2031
|
)) }),
|
|
1997
|
-
availableTypes.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-3", children: /* @__PURE__ */ jsxs(
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
children:
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
availableTypes.map((t) => /* @__PURE__ */ jsx("option", { value: t.value, children: t.label }, t.value))
|
|
2012
|
-
]
|
|
2013
|
-
}
|
|
2014
|
-
),
|
|
2015
|
-
/* @__PURE__ */ jsx("svg", { className: "pointer-events-none absolute right-2.5 top-1/2 -translate-y-1/2 size-3.5 text-muted-foreground", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" }) })
|
|
2016
|
-
] }) })
|
|
2032
|
+
availableTypes.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-3", children: /* @__PURE__ */ jsxs(
|
|
2033
|
+
NativeSelect,
|
|
2034
|
+
{
|
|
2035
|
+
value: "",
|
|
2036
|
+
onChange: (e) => {
|
|
2037
|
+
if (e.target.value) addRule(e.target.value);
|
|
2038
|
+
},
|
|
2039
|
+
className: "h-8 text-xs text-muted-foreground",
|
|
2040
|
+
children: [
|
|
2041
|
+
/* @__PURE__ */ jsx("option", { value: "", children: "Add validation rule..." }),
|
|
2042
|
+
availableTypes.map((t) => /* @__PURE__ */ jsx("option", { value: t.value, children: t.label }, t.value))
|
|
2043
|
+
]
|
|
2044
|
+
}
|
|
2045
|
+
) })
|
|
2017
2046
|
] });
|
|
2018
2047
|
}
|
|
2019
2048
|
function RuleRow({
|
|
@@ -2027,13 +2056,15 @@ function RuleRow({
|
|
|
2027
2056
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
2028
2057
|
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-foreground", children: label }),
|
|
2029
2058
|
/* @__PURE__ */ jsx(
|
|
2030
|
-
|
|
2059
|
+
Button,
|
|
2031
2060
|
{
|
|
2032
2061
|
type: "button",
|
|
2062
|
+
variant: "ghost",
|
|
2063
|
+
size: "icon-xs",
|
|
2033
2064
|
onClick: onRemove,
|
|
2034
|
-
className: "opacity-0 group-hover:opacity-100 transition-opacity",
|
|
2065
|
+
className: "opacity-0 group-hover:opacity-100 transition-opacity hover:bg-destructive/10 hover:text-destructive",
|
|
2035
2066
|
title: "Remove rule",
|
|
2036
|
-
children: /* @__PURE__ */ jsx(Trash2, { size: 12,
|
|
2067
|
+
children: /* @__PURE__ */ jsx(Trash2, { size: 12, strokeWidth: 1.75 })
|
|
2037
2068
|
}
|
|
2038
2069
|
)
|
|
2039
2070
|
] }),
|
|
@@ -2268,11 +2299,13 @@ function ConditionEditor({ question, schema, onUpdate }) {
|
|
|
2268
2299
|
!hasConditions && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mb-3", children: "Always visible. Add a rule to show this field conditionally." }),
|
|
2269
2300
|
hasConditions && /* @__PURE__ */ jsx("div", { className: "space-y-2 mb-3", children: showIf.conditions.map((cond, index) => /* @__PURE__ */ jsxs("div", { children: [
|
|
2270
2301
|
index > 0 && /* @__PURE__ */ jsx(
|
|
2271
|
-
|
|
2302
|
+
Button,
|
|
2272
2303
|
{
|
|
2273
2304
|
type: "button",
|
|
2305
|
+
variant: "link",
|
|
2306
|
+
size: "xs",
|
|
2274
2307
|
onClick: toggleCombine,
|
|
2275
|
-
className: "text-[10px] font-semibold uppercase tracking-wider
|
|
2308
|
+
className: "text-[10px] font-semibold uppercase tracking-wider px-0 mb-1.5",
|
|
2276
2309
|
children: showIf.combine ?? "AND"
|
|
2277
2310
|
}
|
|
2278
2311
|
),
|
|
@@ -2280,43 +2313,39 @@ function ConditionEditor({ question, schema, onUpdate }) {
|
|
|
2280
2313
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
2281
2314
|
/* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground uppercase tracking-wider", children: "When" }),
|
|
2282
2315
|
/* @__PURE__ */ jsx(
|
|
2283
|
-
|
|
2316
|
+
Button,
|
|
2284
2317
|
{
|
|
2285
2318
|
type: "button",
|
|
2319
|
+
variant: "ghost",
|
|
2320
|
+
size: "icon-xs",
|
|
2286
2321
|
onClick: () => removeCondition(index),
|
|
2287
|
-
className: "opacity-0 group-hover:opacity-100 transition-opacity",
|
|
2322
|
+
className: "opacity-0 group-hover:opacity-100 transition-opacity hover:bg-destructive/10 hover:text-destructive",
|
|
2288
2323
|
title: "Remove condition",
|
|
2289
|
-
children: /* @__PURE__ */ jsx(Trash2, { size: 12,
|
|
2324
|
+
children: /* @__PURE__ */ jsx(Trash2, { size: 12, strokeWidth: 1.75 })
|
|
2290
2325
|
}
|
|
2291
2326
|
)
|
|
2292
2327
|
] }),
|
|
2293
|
-
/* @__PURE__ */ jsxs(
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
children:
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
"
|
|
2311
|
-
{
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
className: "flex h-7 w-full appearance-none rounded-md border border-input bg-card px-2 py-1 pr-7 text-xs shadow-xs transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring text-foreground cursor-pointer",
|
|
2315
|
-
children: OPERATORS.map((op) => /* @__PURE__ */ jsx("option", { value: op.value, children: op.label }, op.value))
|
|
2316
|
-
}
|
|
2317
|
-
),
|
|
2318
|
-
/* @__PURE__ */ jsx("svg", { className: "pointer-events-none absolute right-2 top-1/2 -translate-y-1/2 size-3 text-muted-foreground", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" }) })
|
|
2319
|
-
] }),
|
|
2328
|
+
/* @__PURE__ */ jsxs(
|
|
2329
|
+
NativeSelect,
|
|
2330
|
+
{
|
|
2331
|
+
value: cond.field ?? "",
|
|
2332
|
+
onChange: (e) => updateCondition(index, { field: e.target.value }),
|
|
2333
|
+
className: "h-7 text-xs",
|
|
2334
|
+
children: [
|
|
2335
|
+
/* @__PURE__ */ jsx("option", { value: "", children: "Select field..." }),
|
|
2336
|
+
fieldOptions.map((f) => /* @__PURE__ */ jsx("option", { value: f.id, children: f.label }, f.id))
|
|
2337
|
+
]
|
|
2338
|
+
}
|
|
2339
|
+
),
|
|
2340
|
+
/* @__PURE__ */ jsx(
|
|
2341
|
+
NativeSelect,
|
|
2342
|
+
{
|
|
2343
|
+
value: cond.operator ?? "eq",
|
|
2344
|
+
onChange: (e) => updateCondition(index, { operator: e.target.value }),
|
|
2345
|
+
className: "h-7 text-xs",
|
|
2346
|
+
children: OPERATORS.map((op) => /* @__PURE__ */ jsx("option", { value: op.value, children: op.label }, op.value))
|
|
2347
|
+
}
|
|
2348
|
+
),
|
|
2320
2349
|
cond.operator !== "exists" && cond.operator !== "notExists" && /* @__PURE__ */ jsx(
|
|
2321
2350
|
Input,
|
|
2322
2351
|
{
|
|
@@ -2329,11 +2358,13 @@ function ConditionEditor({ question, schema, onUpdate }) {
|
|
|
2329
2358
|
] })
|
|
2330
2359
|
] }, index)) }),
|
|
2331
2360
|
fieldOptions.length > 0 ? /* @__PURE__ */ jsxs(
|
|
2332
|
-
|
|
2361
|
+
Button,
|
|
2333
2362
|
{
|
|
2334
2363
|
type: "button",
|
|
2364
|
+
variant: "link",
|
|
2365
|
+
size: "xs",
|
|
2335
2366
|
onClick: addCondition,
|
|
2336
|
-
className: "
|
|
2367
|
+
className: "px-0 gap-1.5",
|
|
2337
2368
|
children: [
|
|
2338
2369
|
/* @__PURE__ */ jsx(Plus, { size: 12, strokeWidth: 2 }),
|
|
2339
2370
|
"Add condition"
|
|
@@ -2495,18 +2526,7 @@ function SettingsSelect({
|
|
|
2495
2526
|
}) {
|
|
2496
2527
|
return /* @__PURE__ */ jsxs("div", { children: [
|
|
2497
2528
|
/* @__PURE__ */ jsx(Label, { className: "text-xs text-muted-foreground mb-1", children: label }),
|
|
2498
|
-
/* @__PURE__ */
|
|
2499
|
-
/* @__PURE__ */ jsx(
|
|
2500
|
-
"select",
|
|
2501
|
-
{
|
|
2502
|
-
value,
|
|
2503
|
-
onChange: (e) => onChange(e.target.value),
|
|
2504
|
-
className: "flex h-9 w-full appearance-none rounded-md border border-input bg-card px-3 py-1 pr-8 text-sm shadow-xs transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring text-foreground cursor-pointer",
|
|
2505
|
-
children: options.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
|
|
2506
|
-
}
|
|
2507
|
-
),
|
|
2508
|
-
/* @__PURE__ */ jsx("svg", { className: "pointer-events-none absolute right-2.5 top-1/2 -translate-y-1/2 size-4 text-muted-foreground", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" }) })
|
|
2509
|
-
] })
|
|
2529
|
+
/* @__PURE__ */ jsx(NativeSelect, { value, onChange: (e) => onChange(e.target.value), children: options.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value)) })
|
|
2510
2530
|
] });
|
|
2511
2531
|
}
|
|
2512
2532
|
function PropertiesPanel({ builderState }) {
|
|
@@ -2608,13 +2628,14 @@ function QuestionProperties({ question, sectionId, builderState, onClose, onOpen
|
|
|
2608
2628
|
typeInfo?.label
|
|
2609
2629
|
] }) }),
|
|
2610
2630
|
/* @__PURE__ */ jsx("div", { className: "shrink-0 px-4 pt-3 flex gap-0.5", children: tabs.map((tab) => /* @__PURE__ */ jsx(
|
|
2611
|
-
|
|
2631
|
+
Button,
|
|
2612
2632
|
{
|
|
2613
2633
|
type: "button",
|
|
2634
|
+
variant: activeTab === tab.key ? "default" : "ghost",
|
|
2635
|
+
size: "xs",
|
|
2614
2636
|
onClick: () => setActiveTab(tab.key),
|
|
2615
2637
|
className: cn(
|
|
2616
|
-
|
|
2617
|
-
activeTab === tab.key ? "bg-primary text-primary-foreground" : "bg-transparent text-muted-foreground hover:bg-accent hover:text-foreground"
|
|
2638
|
+
activeTab !== tab.key && "text-muted-foreground"
|
|
2618
2639
|
),
|
|
2619
2640
|
children: tab.label
|
|
2620
2641
|
},
|
|
@@ -2873,13 +2894,18 @@ function FormBuilderCore(props) {
|
|
|
2873
2894
|
reader.onload = (event) => {
|
|
2874
2895
|
try {
|
|
2875
2896
|
const parsed = JSON.parse(event.target?.result);
|
|
2876
|
-
|
|
2877
|
-
|
|
2897
|
+
const validated = validateSchema(parsed);
|
|
2898
|
+
builderState.resetSchema(validated);
|
|
2899
|
+
} catch (err) {
|
|
2900
|
+
if (err instanceof FormEngineSchemaError) {
|
|
2901
|
+
const details = err.issues.slice(0, 3).map((i) => `\u2022 ${i.path.join(".")}: ${i.message}`).join("\n");
|
|
2902
|
+
alert(`Invalid schema:
|
|
2903
|
+
${details}`);
|
|
2904
|
+
} else if (err instanceof SyntaxError) {
|
|
2905
|
+
alert("Failed to parse JSON file.");
|
|
2878
2906
|
} else {
|
|
2879
|
-
alert("Invalid schema
|
|
2907
|
+
alert("Invalid schema file.");
|
|
2880
2908
|
}
|
|
2881
|
-
} catch {
|
|
2882
|
-
alert("Failed to parse JSON file.");
|
|
2883
2909
|
}
|
|
2884
2910
|
};
|
|
2885
2911
|
reader.readAsText(file);
|
|
@@ -3165,4 +3191,4 @@ var cleanPreset = {
|
|
|
3165
3191
|
textDim: "#9ca3af"
|
|
3166
3192
|
};
|
|
3167
3193
|
|
|
3168
|
-
export { DEFAULT_PALETTE, DEFAULT_SCHEMA, FormBuilder, FormBuilderThemeProvider, QUESTION_TYPE_INFO, addOption, addQuestion, addSection, cleanPreset,
|
|
3194
|
+
export { DEFAULT_PALETTE, DEFAULT_SCHEMA, FormBuilder, FormBuilderThemeProvider, QUESTION_TYPE_INFO, addOption, addQuestion, addSection, cleanPreset, duplicateQuestion, duplicateSection, findQuestion, findSection, generateId, generateOptionId, generateQuestionId, generateSectionId, moveOption, moveQuestion, moveSection, removeOption, removeQuestion, removeSection, squaredrDarkPreset, updateOption, updateQuestion, updateSection, useBuilderState, useBuilderTheme, useDragDrop, useUndoRedo };
|