@squaredr/fieldcraft-pro 1.3.0 → 1.4.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/dist/chunk-22T5PY6L.mjs +1081 -0
- package/dist/{chunk-4BX7FCXH.mjs → chunk-BK4SMEW4.mjs} +840 -21
- package/dist/{chunk-GNBXIG63.mjs → chunk-BYT2P3I6.mjs} +1264 -88
- package/dist/form-builder/index.d.mts +7 -367
- package/dist/form-builder/index.d.ts +7 -367
- package/dist/form-builder/index.js +1266 -90
- package/dist/form-builder/index.mjs +1 -1
- package/dist/index-nvIvXlmc.d.mts +398 -0
- package/dist/index-nvIvXlmc.d.ts +398 -0
- package/dist/index.d.mts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2926 -335
- package/dist/index.mjs +4 -4
- package/dist/preview-schema-DFvV4y6J.d.mts +66 -0
- package/dist/preview-schema-DFvV4y6J.d.ts +66 -0
- package/dist/response-viewer/index.d.mts +9 -1
- package/dist/response-viewer/index.d.ts +9 -1
- package/dist/response-viewer/index.js +838 -19
- package/dist/response-viewer/index.mjs +1 -1
- package/dist/styles.css +1 -1
- package/dist/theme-editor/index.d.mts +30 -26
- package/dist/theme-editor/index.d.ts +30 -26
- package/dist/theme-editor/index.js +702 -22
- package/dist/theme-editor/index.mjs +1 -1
- package/package.json +8 -6
- package/theme-editor/styles.css +266 -62
- package/dist/chunk-7LQW5QYT.mjs +0 -407
|
@@ -8,6 +8,8 @@ var fieldcraftReact = require('@squaredr/fieldcraft-react');
|
|
|
8
8
|
var fieldcraftCore = require('@squaredr/fieldcraft-core');
|
|
9
9
|
var clsx = require('clsx');
|
|
10
10
|
var tailwindMerge = require('tailwind-merge');
|
|
11
|
+
var react$1 = require('@xyflow/react');
|
|
12
|
+
require('@xyflow/react/dist/style.css');
|
|
11
13
|
|
|
12
14
|
// ../license/dist/index.mjs
|
|
13
15
|
var PRO_FEATURES = [
|
|
@@ -300,19 +302,19 @@ function UnlicensedOverlay({ featureName, reason, children }) {
|
|
|
300
302
|
)
|
|
301
303
|
] });
|
|
302
304
|
}
|
|
303
|
-
function requireLicense(
|
|
305
|
+
function requireLicense(Component3, featureName) {
|
|
304
306
|
function LicenseGated(props) {
|
|
305
307
|
const { status, tier } = useLicense();
|
|
306
308
|
const isProduction = isProductionEnvironment();
|
|
307
309
|
if (!isProduction) {
|
|
308
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
310
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Component3, { ...props });
|
|
309
311
|
}
|
|
310
312
|
if (status === "validating") {
|
|
311
313
|
return null;
|
|
312
314
|
}
|
|
313
315
|
const isLicensed = status === "valid" && tier != null && tierHasFeature(tier, featureName);
|
|
314
316
|
if (isLicensed) {
|
|
315
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
317
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Component3, { ...props });
|
|
316
318
|
}
|
|
317
319
|
let reason = "A valid license is required for production use.";
|
|
318
320
|
if (status === "invalid") {
|
|
@@ -326,9 +328,9 @@ function requireLicense(Component2, featureName) {
|
|
|
326
328
|
} else if (!tier || !tierHasFeature(tier, featureName)) {
|
|
327
329
|
reason = "Your license does not include this feature. Please upgrade your plan.";
|
|
328
330
|
}
|
|
329
|
-
return /* @__PURE__ */ jsxRuntime.jsx(UnlicensedOverlay, { featureName, reason, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
331
|
+
return /* @__PURE__ */ jsxRuntime.jsx(UnlicensedOverlay, { featureName, reason, children: /* @__PURE__ */ jsxRuntime.jsx(Component3, { ...props }) });
|
|
330
332
|
}
|
|
331
|
-
LicenseGated.displayName = `requireLicense(${
|
|
333
|
+
LicenseGated.displayName = `requireLicense(${Component3.displayName || Component3.name || "Component"})`;
|
|
332
334
|
return LicenseGated;
|
|
333
335
|
}
|
|
334
336
|
var MAX_HISTORY = 50;
|
|
@@ -894,7 +896,81 @@ var QUESTION_TYPE_INFO = {
|
|
|
894
896
|
description: "Upload files",
|
|
895
897
|
defaultConfig: { type: "file_upload", maxFiles: 1, maxSizeMb: 10 }
|
|
896
898
|
},
|
|
899
|
+
// ── Compound ──
|
|
900
|
+
legal_name: {
|
|
901
|
+
type: "legal_name",
|
|
902
|
+
label: "Legal Name",
|
|
903
|
+
category: "text",
|
|
904
|
+
icon: "UserCheck",
|
|
905
|
+
description: "First, middle, and last name",
|
|
906
|
+
defaultConfig: { type: "legal_name" }
|
|
907
|
+
},
|
|
908
|
+
address: {
|
|
909
|
+
type: "address",
|
|
910
|
+
label: "Address",
|
|
911
|
+
category: "text",
|
|
912
|
+
icon: "MapPin",
|
|
913
|
+
description: "Street, city, state, ZIP",
|
|
914
|
+
defaultConfig: { type: "address", includeCountry: false }
|
|
915
|
+
},
|
|
916
|
+
signature: {
|
|
917
|
+
type: "signature",
|
|
918
|
+
label: "Signature",
|
|
919
|
+
category: "media",
|
|
920
|
+
icon: "PenTool",
|
|
921
|
+
description: "Canvas signature pad",
|
|
922
|
+
defaultConfig: { type: "signature" }
|
|
923
|
+
},
|
|
924
|
+
date_range: {
|
|
925
|
+
type: "date_range",
|
|
926
|
+
label: "Date Range",
|
|
927
|
+
category: "datetime",
|
|
928
|
+
icon: "CalendarRange",
|
|
929
|
+
description: "Start and end date picker",
|
|
930
|
+
defaultConfig: { type: "date_range" }
|
|
931
|
+
},
|
|
897
932
|
// ── Advanced ──
|
|
933
|
+
repeater: {
|
|
934
|
+
type: "repeater",
|
|
935
|
+
label: "Repeater",
|
|
936
|
+
category: "advanced",
|
|
937
|
+
icon: "ListPlus",
|
|
938
|
+
description: "Repeatable group of fields",
|
|
939
|
+
defaultConfig: { type: "repeater", fields: [], minItems: 1, maxItems: 10 }
|
|
940
|
+
},
|
|
941
|
+
likert: {
|
|
942
|
+
type: "likert",
|
|
943
|
+
label: "Likert Scale",
|
|
944
|
+
category: "advanced",
|
|
945
|
+
icon: "AlignHorizontalSpaceAround",
|
|
946
|
+
description: "Agreement scale (e.g. Strongly Disagree to Strongly Agree)",
|
|
947
|
+
requiresOptions: true,
|
|
948
|
+
defaultConfig: {
|
|
949
|
+
type: "likert",
|
|
950
|
+
scale: [
|
|
951
|
+
{ label: "Strongly Disagree", value: "1" },
|
|
952
|
+
{ label: "Disagree", value: "2" },
|
|
953
|
+
{ label: "Neutral", value: "3" },
|
|
954
|
+
{ label: "Agree", value: "4" },
|
|
955
|
+
{ label: "Strongly Agree", value: "5" }
|
|
956
|
+
]
|
|
957
|
+
}
|
|
958
|
+
},
|
|
959
|
+
scoring: {
|
|
960
|
+
type: "scoring",
|
|
961
|
+
label: "Scoring",
|
|
962
|
+
category: "advanced",
|
|
963
|
+
icon: "Award",
|
|
964
|
+
description: "Scored question with point values per option",
|
|
965
|
+
requiresOptions: true,
|
|
966
|
+
defaultConfig: {
|
|
967
|
+
type: "scoring",
|
|
968
|
+
options: [
|
|
969
|
+
{ label: "Option 1", value: "1", score: 1 },
|
|
970
|
+
{ label: "Option 2", value: "2", score: 2 }
|
|
971
|
+
]
|
|
972
|
+
}
|
|
973
|
+
},
|
|
898
974
|
matrix: {
|
|
899
975
|
type: "matrix",
|
|
900
976
|
label: "Matrix",
|
|
@@ -1028,7 +1104,7 @@ var DEFAULT_PALETTE = [
|
|
|
1028
1104
|
{
|
|
1029
1105
|
category: "text",
|
|
1030
1106
|
label: "Text Input",
|
|
1031
|
-
types: ["short_text", "long_text", "email", "phone", "url"]
|
|
1107
|
+
types: ["short_text", "long_text", "email", "phone", "url", "legal_name", "address"]
|
|
1032
1108
|
},
|
|
1033
1109
|
{
|
|
1034
1110
|
category: "numeric",
|
|
@@ -1043,12 +1119,12 @@ var DEFAULT_PALETTE = [
|
|
|
1043
1119
|
{
|
|
1044
1120
|
category: "datetime",
|
|
1045
1121
|
label: "Date & Time",
|
|
1046
|
-
types: ["date", "time"]
|
|
1122
|
+
types: ["date", "time", "date_range"]
|
|
1047
1123
|
},
|
|
1048
1124
|
{
|
|
1049
1125
|
category: "media",
|
|
1050
1126
|
label: "Media",
|
|
1051
|
-
types: ["file_upload"]
|
|
1127
|
+
types: ["file_upload", "signature"]
|
|
1052
1128
|
},
|
|
1053
1129
|
{
|
|
1054
1130
|
category: "content",
|
|
@@ -1063,7 +1139,7 @@ var DEFAULT_PALETTE = [
|
|
|
1063
1139
|
{
|
|
1064
1140
|
category: "advanced",
|
|
1065
1141
|
label: "Advanced",
|
|
1066
|
-
types: ["matrix", "calculated", "hidden"]
|
|
1142
|
+
types: ["repeater", "likert", "scoring", "matrix", "calculated", "hidden"]
|
|
1067
1143
|
}
|
|
1068
1144
|
];
|
|
1069
1145
|
|
|
@@ -2580,7 +2656,8 @@ function getFieldOptions(schema, excludeId) {
|
|
|
2580
2656
|
return fields;
|
|
2581
2657
|
}
|
|
2582
2658
|
function ConditionEditor({ question, schema, onUpdate }) {
|
|
2583
|
-
const
|
|
2659
|
+
const rawShowIf = question.showIf;
|
|
2660
|
+
const showIf = rawShowIf && rawShowIf.field && !rawShowIf.conditions ? { combine: "AND", conditions: [rawShowIf] } : rawShowIf;
|
|
2584
2661
|
const fieldOptions = getFieldOptions(schema, question.id);
|
|
2585
2662
|
const updateShowIf = (next) => {
|
|
2586
2663
|
onUpdate({ showIf: next });
|
|
@@ -2702,6 +2779,24 @@ function ConditionEditor({ question, schema, onUpdate }) {
|
|
|
2702
2779
|
}
|
|
2703
2780
|
function FormSettingsPanel({ schema, onUpdate }) {
|
|
2704
2781
|
const settings = schema.settings ?? {};
|
|
2782
|
+
const hasConditions = schema.sections.some(
|
|
2783
|
+
(s) => s.showIf || s.questions.some((q) => q.showIf)
|
|
2784
|
+
);
|
|
2785
|
+
const displayModeOptions = hasConditions ? [
|
|
2786
|
+
{ label: "Stepped", value: "stepped" },
|
|
2787
|
+
{ label: "Conversational", value: "conversational" }
|
|
2788
|
+
] : [
|
|
2789
|
+
{ label: "Stepped", value: "stepped" },
|
|
2790
|
+
{ label: "Classic", value: "classic" },
|
|
2791
|
+
{ label: "Conversational", value: "conversational" }
|
|
2792
|
+
];
|
|
2793
|
+
const displayMode = settings.displayMode ?? "stepped";
|
|
2794
|
+
const effectiveDisplayMode = hasConditions && displayMode === "classic" ? "stepped" : displayMode;
|
|
2795
|
+
react.useEffect(() => {
|
|
2796
|
+
if (effectiveDisplayMode !== displayMode) {
|
|
2797
|
+
onUpdate({ ...schema, settings: { ...settings, displayMode: effectiveDisplayMode } });
|
|
2798
|
+
}
|
|
2799
|
+
}, [effectiveDisplayMode, displayMode]);
|
|
2705
2800
|
const updateSettings = (updates) => {
|
|
2706
2801
|
onUpdate({ ...schema, settings: { ...settings, ...updates } });
|
|
2707
2802
|
};
|
|
@@ -2729,12 +2824,8 @@ function FormSettingsPanel({ schema, onUpdate }) {
|
|
|
2729
2824
|
SettingsSelect,
|
|
2730
2825
|
{
|
|
2731
2826
|
label: "Mode",
|
|
2732
|
-
value:
|
|
2733
|
-
options:
|
|
2734
|
-
{ label: "Classic", value: "classic" },
|
|
2735
|
-
{ label: "Stepped", value: "stepped" },
|
|
2736
|
-
{ label: "Conversational", value: "conversational" }
|
|
2737
|
-
],
|
|
2827
|
+
value: effectiveDisplayMode,
|
|
2828
|
+
options: displayModeOptions,
|
|
2738
2829
|
onChange: (v) => updateSettings({ displayMode: v })
|
|
2739
2830
|
}
|
|
2740
2831
|
),
|
|
@@ -3112,6 +3203,873 @@ function FieldGroup({ label, children }) {
|
|
|
3112
3203
|
children
|
|
3113
3204
|
] });
|
|
3114
3205
|
}
|
|
3206
|
+
var PreviewErrorBoundary = class extends react.Component {
|
|
3207
|
+
state = { error: null };
|
|
3208
|
+
static getDerivedStateFromError(error) {
|
|
3209
|
+
return { error };
|
|
3210
|
+
}
|
|
3211
|
+
componentDidCatch(error, info) {
|
|
3212
|
+
console.error("[FormBuilder Preview]", error, info);
|
|
3213
|
+
}
|
|
3214
|
+
componentDidUpdate(prevProps) {
|
|
3215
|
+
if (prevProps.resetKey !== this.props.resetKey && this.state.error) {
|
|
3216
|
+
this.setState({ error: null });
|
|
3217
|
+
}
|
|
3218
|
+
}
|
|
3219
|
+
render() {
|
|
3220
|
+
if (this.state.error) {
|
|
3221
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4 text-destructive text-sm font-mono", children: [
|
|
3222
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: "Render Error:" }),
|
|
3223
|
+
" ",
|
|
3224
|
+
this.state.error.message
|
|
3225
|
+
] });
|
|
3226
|
+
}
|
|
3227
|
+
return this.props.children;
|
|
3228
|
+
}
|
|
3229
|
+
};
|
|
3230
|
+
function FormPreviewPanel({ schema, preview }) {
|
|
3231
|
+
const hasSections = schema.sections && schema.sections.length > 0;
|
|
3232
|
+
const hasFields = hasSections && schema.sections.some((s) => s.questions.length > 0);
|
|
3233
|
+
if (!hasFields) {
|
|
3234
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 flex items-center justify-center text-muted-foreground text-sm", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center space-y-2", children: [
|
|
3235
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-medium", children: "No fields to preview" }),
|
|
3236
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs", children: "Add fields in the Design view to see a live preview here." })
|
|
3237
|
+
] }) });
|
|
3238
|
+
}
|
|
3239
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 overflow-y-auto", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-2xl mx-auto py-8 px-4", children: /* @__PURE__ */ jsxRuntime.jsx(PreviewErrorBoundary, { resetKey: schema.id + (schema.version ?? ""), children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3240
|
+
fieldcraftReact.FormEngineRenderer,
|
|
3241
|
+
{
|
|
3242
|
+
schema,
|
|
3243
|
+
theme: preview?.theme,
|
|
3244
|
+
components: preview?.components,
|
|
3245
|
+
onReady: preview?.onReady,
|
|
3246
|
+
onFieldChange: preview?.onFieldChange,
|
|
3247
|
+
onSubmit: preview?.onSubmit ?? (() => {
|
|
3248
|
+
})
|
|
3249
|
+
}
|
|
3250
|
+
) }) }) });
|
|
3251
|
+
}
|
|
3252
|
+
|
|
3253
|
+
// src/form-builder/utils/validation-markers.ts
|
|
3254
|
+
var MarkerSeverity = {
|
|
3255
|
+
Error: 8};
|
|
3256
|
+
function errorsToMarkers(errors) {
|
|
3257
|
+
return errors.map((error) => ({
|
|
3258
|
+
severity: MarkerSeverity.Error,
|
|
3259
|
+
message: error.message,
|
|
3260
|
+
startLineNumber: error.line ?? 1,
|
|
3261
|
+
startColumn: error.column ?? 1,
|
|
3262
|
+
endLineNumber: error.endLine ?? error.line ?? 1,
|
|
3263
|
+
endColumn: error.endColumn ?? error.column ?? 1,
|
|
3264
|
+
source: "FieldCraft Schema Validator"
|
|
3265
|
+
}));
|
|
3266
|
+
}
|
|
3267
|
+
var Editor = react.lazy(() => import('@monaco-editor/react').then((m) => ({ default: m.default })));
|
|
3268
|
+
function MonacoWrapper({ value, onChange, errors }) {
|
|
3269
|
+
const editorRef = react.useRef(null);
|
|
3270
|
+
const monacoRef = react.useRef(null);
|
|
3271
|
+
const handleMount = (editor, monaco) => {
|
|
3272
|
+
editorRef.current = editor;
|
|
3273
|
+
monacoRef.current = monaco;
|
|
3274
|
+
};
|
|
3275
|
+
react.useEffect(() => {
|
|
3276
|
+
const editor = editorRef.current;
|
|
3277
|
+
const monaco = monacoRef.current;
|
|
3278
|
+
if (!editor || !monaco) return;
|
|
3279
|
+
const model = editor.getModel();
|
|
3280
|
+
if (!model) return;
|
|
3281
|
+
const markers = errorsToMarkers(errors);
|
|
3282
|
+
monaco.editor.setModelMarkers(model, "fieldcraft-schema", markers);
|
|
3283
|
+
}, [errors]);
|
|
3284
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3285
|
+
Editor,
|
|
3286
|
+
{
|
|
3287
|
+
height: "100%",
|
|
3288
|
+
language: "json",
|
|
3289
|
+
theme: "vs-dark",
|
|
3290
|
+
value,
|
|
3291
|
+
onChange: (val) => onChange(val ?? ""),
|
|
3292
|
+
onMount: handleMount,
|
|
3293
|
+
options: {
|
|
3294
|
+
minimap: { enabled: false },
|
|
3295
|
+
fontSize: 13,
|
|
3296
|
+
wordWrap: "on",
|
|
3297
|
+
formatOnPaste: true,
|
|
3298
|
+
automaticLayout: true,
|
|
3299
|
+
tabSize: 2,
|
|
3300
|
+
scrollBeyondLastLine: false,
|
|
3301
|
+
padding: { top: 12 }
|
|
3302
|
+
}
|
|
3303
|
+
}
|
|
3304
|
+
);
|
|
3305
|
+
}
|
|
3306
|
+
function JsonEditorPanel({ value, onChange, errors, onValidate }) {
|
|
3307
|
+
const handleChange = react.useCallback(
|
|
3308
|
+
(newValue) => {
|
|
3309
|
+
onChange(newValue);
|
|
3310
|
+
onValidate(newValue);
|
|
3311
|
+
},
|
|
3312
|
+
[onChange, onValidate]
|
|
3313
|
+
);
|
|
3314
|
+
const handleFormat = react.useCallback(() => {
|
|
3315
|
+
try {
|
|
3316
|
+
const formatted = JSON.stringify(JSON.parse(value), null, 2);
|
|
3317
|
+
onChange(formatted);
|
|
3318
|
+
} catch {
|
|
3319
|
+
}
|
|
3320
|
+
}, [value, onChange]);
|
|
3321
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 flex flex-col overflow-hidden", children: [
|
|
3322
|
+
errors.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "shrink-0 px-3 py-1.5 bg-destructive/10 border-b border-destructive/20 text-destructive text-xs font-mono overflow-x-auto", children: [
|
|
3323
|
+
errors.slice(0, 3).map((e, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
3324
|
+
e.line ? `Line ${e.line}: ` : "",
|
|
3325
|
+
e.path ? `${e.path}: ` : "",
|
|
3326
|
+
e.message
|
|
3327
|
+
] }, i)),
|
|
3328
|
+
errors.length > 3 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-muted-foreground", children: [
|
|
3329
|
+
"...and ",
|
|
3330
|
+
errors.length - 3,
|
|
3331
|
+
" more"
|
|
3332
|
+
] })
|
|
3333
|
+
] }),
|
|
3334
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "shrink-0 flex items-center justify-between px-3 py-1.5 bg-card border-b border-border text-xs", children: [
|
|
3335
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: errors.length === 0 ? "Valid schema" : `${errors.length} error${errors.length !== 1 ? "s" : ""}` }),
|
|
3336
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3337
|
+
"button",
|
|
3338
|
+
{
|
|
3339
|
+
type: "button",
|
|
3340
|
+
onClick: handleFormat,
|
|
3341
|
+
className: "text-primary hover:underline",
|
|
3342
|
+
children: "Format JSON"
|
|
3343
|
+
}
|
|
3344
|
+
)
|
|
3345
|
+
] }),
|
|
3346
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3347
|
+
react.Suspense,
|
|
3348
|
+
{
|
|
3349
|
+
fallback: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center h-full text-sm text-muted-foreground", children: "Loading editor..." }),
|
|
3350
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(MonacoWrapper, { value, onChange: handleChange, errors })
|
|
3351
|
+
}
|
|
3352
|
+
) })
|
|
3353
|
+
] });
|
|
3354
|
+
}
|
|
3355
|
+
|
|
3356
|
+
// src/form-builder/utils/logic-graph.ts
|
|
3357
|
+
function extractFieldRefs(expr) {
|
|
3358
|
+
const refs = [];
|
|
3359
|
+
if (expr.field) {
|
|
3360
|
+
refs.push({ field: expr.field, operator: expr.operator, value: expr.value });
|
|
3361
|
+
}
|
|
3362
|
+
if (expr.conditions) {
|
|
3363
|
+
for (const child of expr.conditions) {
|
|
3364
|
+
refs.push(...extractFieldRefs(child));
|
|
3365
|
+
}
|
|
3366
|
+
}
|
|
3367
|
+
return refs;
|
|
3368
|
+
}
|
|
3369
|
+
function buildLogicGraph(schema) {
|
|
3370
|
+
const nodes = [];
|
|
3371
|
+
const edges = [];
|
|
3372
|
+
const nodeIds = /* @__PURE__ */ new Set();
|
|
3373
|
+
for (const section of schema.sections) {
|
|
3374
|
+
nodes.push({
|
|
3375
|
+
id: section.id,
|
|
3376
|
+
label: section.title || "Untitled Section",
|
|
3377
|
+
type: "section",
|
|
3378
|
+
sectionId: section.id,
|
|
3379
|
+
sectionTitle: section.title || "Untitled Section",
|
|
3380
|
+
hasCondition: !!section.showIf
|
|
3381
|
+
});
|
|
3382
|
+
nodeIds.add(section.id);
|
|
3383
|
+
if (section.showIf) {
|
|
3384
|
+
const refs = extractFieldRefs(section.showIf);
|
|
3385
|
+
for (const ref of refs) {
|
|
3386
|
+
edges.push({ from: ref.field, to: section.id, edgeType: "showIf", operator: ref.operator, value: ref.value });
|
|
3387
|
+
}
|
|
3388
|
+
}
|
|
3389
|
+
if (section.onExit) {
|
|
3390
|
+
for (const rule of section.onExit.rules) {
|
|
3391
|
+
if (rule.jumpTo) {
|
|
3392
|
+
edges.push({ from: section.id, to: rule.jumpTo, edgeType: "onExit", jumpTo: rule.jumpTo });
|
|
3393
|
+
}
|
|
3394
|
+
}
|
|
3395
|
+
if (section.onExit.default) {
|
|
3396
|
+
edges.push({ from: section.id, to: section.onExit.default, edgeType: "onExit", jumpTo: section.onExit.default });
|
|
3397
|
+
}
|
|
3398
|
+
}
|
|
3399
|
+
for (const question of section.questions) {
|
|
3400
|
+
nodes.push({
|
|
3401
|
+
id: question.id,
|
|
3402
|
+
label: question.label || question.id,
|
|
3403
|
+
type: "field",
|
|
3404
|
+
fieldType: question.type,
|
|
3405
|
+
sectionId: section.id,
|
|
3406
|
+
sectionTitle: section.title || "Untitled Section",
|
|
3407
|
+
hasCondition: !!question.showIf
|
|
3408
|
+
});
|
|
3409
|
+
nodeIds.add(question.id);
|
|
3410
|
+
if (question.showIf) {
|
|
3411
|
+
const refs = extractFieldRefs(question.showIf);
|
|
3412
|
+
for (const ref of refs) {
|
|
3413
|
+
edges.push({ from: ref.field, to: question.id, edgeType: "showIf", operator: ref.operator, value: ref.value });
|
|
3414
|
+
}
|
|
3415
|
+
}
|
|
3416
|
+
}
|
|
3417
|
+
}
|
|
3418
|
+
const validEdges = edges.filter((e) => nodeIds.has(e.from) && nodeIds.has(e.to));
|
|
3419
|
+
return { nodes, edges: validEdges };
|
|
3420
|
+
}
|
|
3421
|
+
var SECTION_COLORS = [
|
|
3422
|
+
"#3b82f6",
|
|
3423
|
+
// blue
|
|
3424
|
+
"#8b5cf6",
|
|
3425
|
+
// violet
|
|
3426
|
+
"#06b6d4",
|
|
3427
|
+
// cyan
|
|
3428
|
+
"#f59e0b",
|
|
3429
|
+
// amber
|
|
3430
|
+
"#10b981",
|
|
3431
|
+
// emerald
|
|
3432
|
+
"#ef4444",
|
|
3433
|
+
// red
|
|
3434
|
+
"#ec4899",
|
|
3435
|
+
// pink
|
|
3436
|
+
"#6366f1"
|
|
3437
|
+
// indigo
|
|
3438
|
+
];
|
|
3439
|
+
var FIELD_NODE_WIDTH = 220;
|
|
3440
|
+
var FIELD_NODE_HEIGHT = 52;
|
|
3441
|
+
var SECTION_HEADER_HEIGHT = 40;
|
|
3442
|
+
var SECTION_PADDING_X = 16;
|
|
3443
|
+
var SECTION_PADDING_TOP = 12;
|
|
3444
|
+
var SECTION_PADDING_BOTTOM = 16;
|
|
3445
|
+
var FIELD_GAP = 10;
|
|
3446
|
+
var SECTION_GAP = 80;
|
|
3447
|
+
function formatEdgeLabel(edge) {
|
|
3448
|
+
if (edge.edgeType === "onExit") return "jump";
|
|
3449
|
+
if (!edge.operator) return "";
|
|
3450
|
+
const op = edge.operator;
|
|
3451
|
+
const val = edge.value;
|
|
3452
|
+
if (op === "exists") return "has value";
|
|
3453
|
+
if (op === "notExists") return "is empty";
|
|
3454
|
+
if (val === void 0 || val === null || val === "") return op;
|
|
3455
|
+
const short = String(val).length > 12 ? String(val).slice(0, 12) + "\u2026" : String(val);
|
|
3456
|
+
return `${op} ${short}`;
|
|
3457
|
+
}
|
|
3458
|
+
function buildReactFlowGraph(schema) {
|
|
3459
|
+
const graph = buildLogicGraph(schema);
|
|
3460
|
+
const nodes = [];
|
|
3461
|
+
const edges = [];
|
|
3462
|
+
let sectionX = 0;
|
|
3463
|
+
let maxSectionHeight = 0;
|
|
3464
|
+
for (const section of schema.sections) {
|
|
3465
|
+
const fieldCount = section.questions.length;
|
|
3466
|
+
const contentHeight = SECTION_HEADER_HEIGHT + SECTION_PADDING_TOP + fieldCount * FIELD_NODE_HEIGHT + Math.max(0, fieldCount - 1) * FIELD_GAP + SECTION_PADDING_BOTTOM;
|
|
3467
|
+
if (contentHeight > maxSectionHeight) maxSectionHeight = contentHeight;
|
|
3468
|
+
}
|
|
3469
|
+
if (maxSectionHeight < SECTION_HEADER_HEIGHT + SECTION_PADDING_TOP + SECTION_PADDING_BOTTOM + FIELD_NODE_HEIGHT) {
|
|
3470
|
+
maxSectionHeight = SECTION_HEADER_HEIGHT + SECTION_PADDING_TOP + SECTION_PADDING_BOTTOM + FIELD_NODE_HEIGHT;
|
|
3471
|
+
}
|
|
3472
|
+
const sectionWidth = FIELD_NODE_WIDTH + SECTION_PADDING_X * 2;
|
|
3473
|
+
for (let sIdx = 0; sIdx < schema.sections.length; sIdx++) {
|
|
3474
|
+
const section = schema.sections[sIdx];
|
|
3475
|
+
const colorIndex = sIdx;
|
|
3476
|
+
nodes.push({
|
|
3477
|
+
id: section.id,
|
|
3478
|
+
type: "sectionNode",
|
|
3479
|
+
position: { x: sectionX, y: 0 },
|
|
3480
|
+
data: {
|
|
3481
|
+
label: section.title || "Untitled Section",
|
|
3482
|
+
sectionId: section.id,
|
|
3483
|
+
fieldCount: section.questions.length,
|
|
3484
|
+
hasCondition: !!section.showIf,
|
|
3485
|
+
hasExitLogic: !!section.onExit,
|
|
3486
|
+
colorIndex
|
|
3487
|
+
},
|
|
3488
|
+
style: { width: sectionWidth, height: maxSectionHeight }
|
|
3489
|
+
});
|
|
3490
|
+
for (let fIdx = 0; fIdx < section.questions.length; fIdx++) {
|
|
3491
|
+
const question = section.questions[fIdx];
|
|
3492
|
+
const fieldY = SECTION_HEADER_HEIGHT + SECTION_PADDING_TOP + fIdx * (FIELD_NODE_HEIGHT + FIELD_GAP);
|
|
3493
|
+
nodes.push({
|
|
3494
|
+
id: question.id,
|
|
3495
|
+
type: "fieldNode",
|
|
3496
|
+
position: { x: SECTION_PADDING_X, y: fieldY },
|
|
3497
|
+
parentId: section.id,
|
|
3498
|
+
extent: "parent",
|
|
3499
|
+
data: {
|
|
3500
|
+
label: question.label || question.id,
|
|
3501
|
+
fieldType: question.type,
|
|
3502
|
+
sectionId: section.id,
|
|
3503
|
+
hasCondition: !!question.showIf,
|
|
3504
|
+
colorIndex
|
|
3505
|
+
},
|
|
3506
|
+
style: { width: FIELD_NODE_WIDTH }
|
|
3507
|
+
});
|
|
3508
|
+
}
|
|
3509
|
+
if (sIdx < schema.sections.length - 1) {
|
|
3510
|
+
const nextSection = schema.sections[sIdx + 1];
|
|
3511
|
+
edges.push({
|
|
3512
|
+
id: `pipeline-${section.id}-${nextSection.id}`,
|
|
3513
|
+
source: section.id,
|
|
3514
|
+
target: nextSection.id,
|
|
3515
|
+
type: "pipelineEdge",
|
|
3516
|
+
data: { edgeType: "pipeline" }
|
|
3517
|
+
});
|
|
3518
|
+
}
|
|
3519
|
+
sectionX += sectionWidth + SECTION_GAP;
|
|
3520
|
+
}
|
|
3521
|
+
for (let i = 0; i < graph.edges.length; i++) {
|
|
3522
|
+
const edge = graph.edges[i];
|
|
3523
|
+
edges.push({
|
|
3524
|
+
id: `e-${edge.from}-${edge.to}-${i}`,
|
|
3525
|
+
source: edge.from,
|
|
3526
|
+
target: edge.to,
|
|
3527
|
+
type: "conditionEdge",
|
|
3528
|
+
animated: edge.edgeType === "showIf",
|
|
3529
|
+
style: edge.edgeType === "onExit" ? { strokeDasharray: "6 3", stroke: "#f59e0b" } : void 0,
|
|
3530
|
+
label: formatEdgeLabel(edge),
|
|
3531
|
+
data: {
|
|
3532
|
+
edgeType: edge.edgeType,
|
|
3533
|
+
operator: edge.operator,
|
|
3534
|
+
value: edge.value,
|
|
3535
|
+
sourceId: edge.from,
|
|
3536
|
+
targetId: edge.to
|
|
3537
|
+
}
|
|
3538
|
+
});
|
|
3539
|
+
}
|
|
3540
|
+
return { nodes, edges };
|
|
3541
|
+
}
|
|
3542
|
+
function SectionNodeInner({ data }) {
|
|
3543
|
+
const d = data;
|
|
3544
|
+
const color = SECTION_COLORS[d.colorIndex % SECTION_COLORS.length];
|
|
3545
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fc-flow-section-group", style: { borderColor: color }, children: [
|
|
3546
|
+
/* @__PURE__ */ jsxRuntime.jsx(react$1.Handle, { type: "target", position: react$1.Position.Left, className: "fc-flow-handle fc-flow-handle--section" }),
|
|
3547
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fc-flow-section-group__header", style: { background: color }, children: [
|
|
3548
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "fc-flow-section-group__label", children: d.label.length > 30 ? d.label.slice(0, 30) + "\u2026" : d.label }),
|
|
3549
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "fc-flow-section-group__meta", children: [
|
|
3550
|
+
d.fieldCount,
|
|
3551
|
+
" field",
|
|
3552
|
+
d.fieldCount !== 1 ? "s" : "",
|
|
3553
|
+
d.hasCondition && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "fc-flow-section-group__badge", title: "Has showIf condition", children: "?" }),
|
|
3554
|
+
d.hasExitLogic && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "fc-flow-section-group__badge fc-flow-section-group__badge--jump", title: "Has onExit jump", children: "\u2934" })
|
|
3555
|
+
] })
|
|
3556
|
+
] }),
|
|
3557
|
+
/* @__PURE__ */ jsxRuntime.jsx(react$1.Handle, { type: "source", position: react$1.Position.Right, className: "fc-flow-handle fc-flow-handle--section" })
|
|
3558
|
+
] });
|
|
3559
|
+
}
|
|
3560
|
+
var SectionNode = react.memo(SectionNodeInner);
|
|
3561
|
+
function FieldNodeInner({ data }) {
|
|
3562
|
+
const d = data;
|
|
3563
|
+
const color = SECTION_COLORS[d.colorIndex % SECTION_COLORS.length];
|
|
3564
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3565
|
+
"div",
|
|
3566
|
+
{
|
|
3567
|
+
className: "fc-flow-field",
|
|
3568
|
+
style: {
|
|
3569
|
+
borderColor: d.hasCondition ? color : void 0,
|
|
3570
|
+
borderWidth: d.hasCondition ? 2 : 1
|
|
3571
|
+
},
|
|
3572
|
+
children: [
|
|
3573
|
+
/* @__PURE__ */ jsxRuntime.jsx(react$1.Handle, { type: "target", position: react$1.Position.Left, className: "fc-flow-handle" }),
|
|
3574
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fc-flow-field__content", children: [
|
|
3575
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "fc-flow-field__label", children: d.label.length > 26 ? d.label.slice(0, 26) + "\u2026" : d.label }),
|
|
3576
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "fc-flow-field__type", children: d.fieldType })
|
|
3577
|
+
] }),
|
|
3578
|
+
d.hasCondition && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fc-flow-badge", style: { background: color }, title: "Has showIf condition", children: "?" }),
|
|
3579
|
+
/* @__PURE__ */ jsxRuntime.jsx(react$1.Handle, { type: "source", position: react$1.Position.Right, className: "fc-flow-handle" })
|
|
3580
|
+
]
|
|
3581
|
+
}
|
|
3582
|
+
);
|
|
3583
|
+
}
|
|
3584
|
+
var FieldNode = react.memo(FieldNodeInner);
|
|
3585
|
+
var logicFlowNodeTypes = {
|
|
3586
|
+
sectionNode: SectionNode,
|
|
3587
|
+
fieldNode: FieldNode
|
|
3588
|
+
};
|
|
3589
|
+
function ConditionEdgeInner({
|
|
3590
|
+
id,
|
|
3591
|
+
sourceX,
|
|
3592
|
+
sourceY,
|
|
3593
|
+
targetX,
|
|
3594
|
+
targetY,
|
|
3595
|
+
sourcePosition,
|
|
3596
|
+
targetPosition,
|
|
3597
|
+
label,
|
|
3598
|
+
style,
|
|
3599
|
+
data,
|
|
3600
|
+
markerEnd
|
|
3601
|
+
}) {
|
|
3602
|
+
const [edgePath, labelX, labelY] = react$1.getBezierPath({
|
|
3603
|
+
sourceX,
|
|
3604
|
+
sourceY,
|
|
3605
|
+
sourcePosition,
|
|
3606
|
+
targetX,
|
|
3607
|
+
targetY,
|
|
3608
|
+
targetPosition
|
|
3609
|
+
});
|
|
3610
|
+
const isJump = data?.edgeType === "onExit";
|
|
3611
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3612
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3613
|
+
react$1.BaseEdge,
|
|
3614
|
+
{
|
|
3615
|
+
id,
|
|
3616
|
+
path: edgePath,
|
|
3617
|
+
markerEnd,
|
|
3618
|
+
style: {
|
|
3619
|
+
stroke: isJump ? "#f59e0b" : "var(--primary, #3b82f6)",
|
|
3620
|
+
strokeWidth: 1.5,
|
|
3621
|
+
strokeOpacity: 0.7,
|
|
3622
|
+
...style
|
|
3623
|
+
}
|
|
3624
|
+
}
|
|
3625
|
+
),
|
|
3626
|
+
label && /* @__PURE__ */ jsxRuntime.jsx(react$1.EdgeLabelRenderer, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3627
|
+
"div",
|
|
3628
|
+
{
|
|
3629
|
+
className: "fc-flow-edge-label",
|
|
3630
|
+
style: {
|
|
3631
|
+
transform: `translate(-50%, -50%) translate(${labelX}px, ${labelY}px)`,
|
|
3632
|
+
background: isJump ? "#f59e0b" : "var(--primary, #3b82f6)"
|
|
3633
|
+
},
|
|
3634
|
+
children: label
|
|
3635
|
+
}
|
|
3636
|
+
) })
|
|
3637
|
+
] });
|
|
3638
|
+
}
|
|
3639
|
+
var ConditionEdge = react.memo(ConditionEdgeInner);
|
|
3640
|
+
function PipelineEdgeInner({
|
|
3641
|
+
id,
|
|
3642
|
+
sourceX,
|
|
3643
|
+
sourceY,
|
|
3644
|
+
targetX,
|
|
3645
|
+
targetY,
|
|
3646
|
+
sourcePosition,
|
|
3647
|
+
targetPosition,
|
|
3648
|
+
markerEnd
|
|
3649
|
+
}) {
|
|
3650
|
+
const [edgePath] = react$1.getBezierPath({
|
|
3651
|
+
sourceX,
|
|
3652
|
+
sourceY,
|
|
3653
|
+
sourcePosition,
|
|
3654
|
+
targetX,
|
|
3655
|
+
targetY,
|
|
3656
|
+
targetPosition
|
|
3657
|
+
});
|
|
3658
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3659
|
+
react$1.BaseEdge,
|
|
3660
|
+
{
|
|
3661
|
+
id,
|
|
3662
|
+
path: edgePath,
|
|
3663
|
+
markerEnd,
|
|
3664
|
+
style: {
|
|
3665
|
+
stroke: "var(--muted-foreground, #71717a)",
|
|
3666
|
+
strokeWidth: 2,
|
|
3667
|
+
strokeOpacity: 0.4
|
|
3668
|
+
}
|
|
3669
|
+
}
|
|
3670
|
+
);
|
|
3671
|
+
}
|
|
3672
|
+
var PipelineEdge = react.memo(PipelineEdgeInner);
|
|
3673
|
+
var logicFlowEdgeTypes = {
|
|
3674
|
+
conditionEdge: ConditionEdge,
|
|
3675
|
+
pipelineEdge: PipelineEdge
|
|
3676
|
+
};
|
|
3677
|
+
function findQuestion2(schema, fieldId) {
|
|
3678
|
+
for (const section of schema.sections) {
|
|
3679
|
+
for (const q of section.questions) {
|
|
3680
|
+
if (q.id === fieldId) return q;
|
|
3681
|
+
}
|
|
3682
|
+
}
|
|
3683
|
+
return void 0;
|
|
3684
|
+
}
|
|
3685
|
+
function updateQuestionInSchema(schema, fieldId, updates) {
|
|
3686
|
+
return {
|
|
3687
|
+
...schema,
|
|
3688
|
+
sections: schema.sections.map((section) => ({
|
|
3689
|
+
...section,
|
|
3690
|
+
questions: section.questions.map(
|
|
3691
|
+
(q) => q.id === fieldId ? { ...q, ...updates } : q
|
|
3692
|
+
)
|
|
3693
|
+
}))
|
|
3694
|
+
};
|
|
3695
|
+
}
|
|
3696
|
+
function removeConditionForSource(showIf, sourceFieldId) {
|
|
3697
|
+
if (!showIf) return void 0;
|
|
3698
|
+
if (showIf.field === sourceFieldId) return void 0;
|
|
3699
|
+
if (showIf.field && showIf.field !== sourceFieldId) return showIf;
|
|
3700
|
+
if (showIf.conditions) {
|
|
3701
|
+
const filtered = showIf.conditions.map((c) => removeConditionForSource(c, sourceFieldId)).filter((c) => c !== void 0);
|
|
3702
|
+
if (filtered.length === 0) return void 0;
|
|
3703
|
+
if (filtered.length === 1) return filtered[0];
|
|
3704
|
+
return { ...showIf, conditions: filtered };
|
|
3705
|
+
}
|
|
3706
|
+
return showIf;
|
|
3707
|
+
}
|
|
3708
|
+
var defaultEdgeOptions = {
|
|
3709
|
+
markerEnd: {
|
|
3710
|
+
type: react$1.MarkerType.ArrowClosed,
|
|
3711
|
+
width: 16,
|
|
3712
|
+
height: 12
|
|
3713
|
+
}
|
|
3714
|
+
};
|
|
3715
|
+
function LogicFlowEditor({ schema, onChange, onClose }) {
|
|
3716
|
+
const noSections = schema.sections.length === 0;
|
|
3717
|
+
const flowGraph = react.useMemo(() => buildReactFlowGraph(schema), [schema]);
|
|
3718
|
+
const [nodes, setNodes, onNodesChange] = react$1.useNodesState(flowGraph.nodes);
|
|
3719
|
+
const [edges, setEdges, onEdgesChange] = react$1.useEdgesState(flowGraph.edges);
|
|
3720
|
+
const [selectedFieldId, setSelectedFieldId] = react.useState(null);
|
|
3721
|
+
const selectedQuestion = selectedFieldId ? findQuestion2(schema, selectedFieldId) : void 0;
|
|
3722
|
+
react.useEffect(() => {
|
|
3723
|
+
setNodes(flowGraph.nodes);
|
|
3724
|
+
setEdges(flowGraph.edges);
|
|
3725
|
+
}, [flowGraph, setNodes, setEdges]);
|
|
3726
|
+
const sectionIds = react.useMemo(() => {
|
|
3727
|
+
const ids = [];
|
|
3728
|
+
for (const section of schema.sections) {
|
|
3729
|
+
ids.push(section.id);
|
|
3730
|
+
}
|
|
3731
|
+
return ids;
|
|
3732
|
+
}, [schema]);
|
|
3733
|
+
const onNodeClick = react.useCallback(
|
|
3734
|
+
(_event, node) => {
|
|
3735
|
+
if (node.type === "fieldNode") {
|
|
3736
|
+
setSelectedFieldId(node.id);
|
|
3737
|
+
}
|
|
3738
|
+
},
|
|
3739
|
+
[]
|
|
3740
|
+
);
|
|
3741
|
+
const onConnect = react.useCallback(
|
|
3742
|
+
(connection) => {
|
|
3743
|
+
if (!connection.source || !connection.target) return;
|
|
3744
|
+
const targetQuestion = findQuestion2(schema, connection.target);
|
|
3745
|
+
if (!targetQuestion) return;
|
|
3746
|
+
const newCondition = {
|
|
3747
|
+
field: connection.source,
|
|
3748
|
+
operator: "exists"
|
|
3749
|
+
};
|
|
3750
|
+
let updatedShowIf;
|
|
3751
|
+
const existing = targetQuestion.showIf;
|
|
3752
|
+
if (!existing) {
|
|
3753
|
+
updatedShowIf = { combine: "AND", conditions: [newCondition] };
|
|
3754
|
+
} else {
|
|
3755
|
+
updatedShowIf = {
|
|
3756
|
+
...existing,
|
|
3757
|
+
conditions: [...existing.conditions ?? [], newCondition]
|
|
3758
|
+
};
|
|
3759
|
+
}
|
|
3760
|
+
const updatedSchema = updateQuestionInSchema(schema, connection.target, {
|
|
3761
|
+
showIf: updatedShowIf
|
|
3762
|
+
});
|
|
3763
|
+
onChange(updatedSchema);
|
|
3764
|
+
setSelectedFieldId(connection.target);
|
|
3765
|
+
},
|
|
3766
|
+
[schema, onChange]
|
|
3767
|
+
);
|
|
3768
|
+
const onEdgesDelete = react.useCallback(
|
|
3769
|
+
(deletedEdges) => {
|
|
3770
|
+
let updatedSchema = schema;
|
|
3771
|
+
for (const edge of deletedEdges) {
|
|
3772
|
+
const edgeData = edge.data;
|
|
3773
|
+
if (!edgeData) continue;
|
|
3774
|
+
const targetId = edge.target;
|
|
3775
|
+
const sourceId = edge.source;
|
|
3776
|
+
const edgeType = edgeData.edgeType;
|
|
3777
|
+
if (edgeType === "showIf") {
|
|
3778
|
+
const question = findQuestion2(updatedSchema, targetId);
|
|
3779
|
+
if (!question) continue;
|
|
3780
|
+
const updatedShowIf = removeConditionForSource(
|
|
3781
|
+
question.showIf,
|
|
3782
|
+
sourceId
|
|
3783
|
+
);
|
|
3784
|
+
updatedSchema = updateQuestionInSchema(updatedSchema, targetId, {
|
|
3785
|
+
showIf: updatedShowIf
|
|
3786
|
+
});
|
|
3787
|
+
}
|
|
3788
|
+
}
|
|
3789
|
+
onChange(updatedSchema);
|
|
3790
|
+
},
|
|
3791
|
+
[schema, onChange]
|
|
3792
|
+
);
|
|
3793
|
+
const handleConditionUpdate = react.useCallback(
|
|
3794
|
+
(updates) => {
|
|
3795
|
+
if (!selectedFieldId) return;
|
|
3796
|
+
const updatedSchema = updateQuestionInSchema(schema, selectedFieldId, updates);
|
|
3797
|
+
onChange(updatedSchema);
|
|
3798
|
+
},
|
|
3799
|
+
[schema, selectedFieldId, onChange]
|
|
3800
|
+
);
|
|
3801
|
+
const showIfCount = flowGraph.edges.filter(
|
|
3802
|
+
(e) => e.data?.edgeType === "showIf"
|
|
3803
|
+
).length;
|
|
3804
|
+
const jumpCount = flowGraph.edges.filter(
|
|
3805
|
+
(e) => e.data?.edgeType === "onExit"
|
|
3806
|
+
).length;
|
|
3807
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fixed inset-0 z-50 flex flex-col bg-background/95 backdrop-blur-sm", children: [
|
|
3808
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "h-12 shrink-0 flex items-center justify-between px-4 border-b border-border bg-card", children: [
|
|
3809
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
3810
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.GitBranch, { size: 16, className: "text-primary" }),
|
|
3811
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-sm font-semibold", children: "Logic Flow" }),
|
|
3812
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-muted-foreground", children: [
|
|
3813
|
+
showIfCount,
|
|
3814
|
+
" condition",
|
|
3815
|
+
showIfCount !== 1 ? "s" : "",
|
|
3816
|
+
jumpCount > 0 && ` \xB7 ${jumpCount} jump${jumpCount !== 1 ? "s" : ""}`,
|
|
3817
|
+
" \xB7 ",
|
|
3818
|
+
sectionIds.length,
|
|
3819
|
+
" section",
|
|
3820
|
+
sectionIds.length !== 1 ? "s" : ""
|
|
3821
|
+
] })
|
|
3822
|
+
] }),
|
|
3823
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
|
|
3824
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-muted-foreground mr-2", children: "Drag between nodes to create conditions \xB7 Select edges and press Delete to remove" }),
|
|
3825
|
+
/* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Button, { variant: "ghost", size: "icon-sm", onClick: onClose, title: "Close logic flow", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 16, strokeWidth: 1.75 }) })
|
|
3826
|
+
] })
|
|
3827
|
+
] }),
|
|
3828
|
+
noSections ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 flex items-center justify-center text-muted-foreground text-sm", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center space-y-2", children: [
|
|
3829
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.GitBranch, { size: 32, className: "mx-auto opacity-40" }),
|
|
3830
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-medium", children: "No sections found" }),
|
|
3831
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs max-w-xs", children: "Add sections and fields to your form to see the logic flow pipeline." })
|
|
3832
|
+
] }) }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 flex", children: [
|
|
3833
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3834
|
+
react$1.ReactFlow,
|
|
3835
|
+
{
|
|
3836
|
+
nodes,
|
|
3837
|
+
edges,
|
|
3838
|
+
onNodesChange,
|
|
3839
|
+
onEdgesChange,
|
|
3840
|
+
onNodeClick,
|
|
3841
|
+
onConnect,
|
|
3842
|
+
onEdgesDelete,
|
|
3843
|
+
nodeTypes: logicFlowNodeTypes,
|
|
3844
|
+
edgeTypes: logicFlowEdgeTypes,
|
|
3845
|
+
defaultEdgeOptions,
|
|
3846
|
+
fitView: true,
|
|
3847
|
+
fitViewOptions: { padding: 0.2 },
|
|
3848
|
+
deleteKeyCode: ["Backspace", "Delete"],
|
|
3849
|
+
proOptions: { hideAttribution: true },
|
|
3850
|
+
children: [
|
|
3851
|
+
/* @__PURE__ */ jsxRuntime.jsx(react$1.Background, { gap: 20, size: 1 }),
|
|
3852
|
+
/* @__PURE__ */ jsxRuntime.jsx(react$1.Controls, { showInteractive: false }),
|
|
3853
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3854
|
+
react$1.MiniMap,
|
|
3855
|
+
{
|
|
3856
|
+
nodeColor: (node) => {
|
|
3857
|
+
const d = node.data;
|
|
3858
|
+
const idx = d.colorIndex ?? 0;
|
|
3859
|
+
return SECTION_COLORS[idx % SECTION_COLORS.length];
|
|
3860
|
+
},
|
|
3861
|
+
maskColor: "rgba(0, 0, 0, 0.3)"
|
|
3862
|
+
}
|
|
3863
|
+
)
|
|
3864
|
+
]
|
|
3865
|
+
}
|
|
3866
|
+
) }),
|
|
3867
|
+
selectedFieldId && selectedQuestion && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-72 border-l border-border bg-card overflow-auto", children: [
|
|
3868
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-3 py-3 border-b border-border flex items-center justify-between", children: [
|
|
3869
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
3870
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs font-semibold truncate", children: selectedQuestion.label || selectedQuestion.id }),
|
|
3871
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[10px] text-muted-foreground", children: selectedQuestion.type })
|
|
3872
|
+
] }),
|
|
3873
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3874
|
+
fieldcraftReact.Button,
|
|
3875
|
+
{
|
|
3876
|
+
variant: "ghost",
|
|
3877
|
+
size: "icon-xs",
|
|
3878
|
+
onClick: () => setSelectedFieldId(null),
|
|
3879
|
+
title: "Close panel",
|
|
3880
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 14, strokeWidth: 1.75 })
|
|
3881
|
+
}
|
|
3882
|
+
)
|
|
3883
|
+
] }),
|
|
3884
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-3", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3885
|
+
ConditionEditor,
|
|
3886
|
+
{
|
|
3887
|
+
question: selectedQuestion,
|
|
3888
|
+
schema,
|
|
3889
|
+
onUpdate: handleConditionUpdate
|
|
3890
|
+
}
|
|
3891
|
+
) })
|
|
3892
|
+
] })
|
|
3893
|
+
] }),
|
|
3894
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "shrink-0 px-4 py-2 border-t border-border bg-card flex items-center gap-4 text-xs text-muted-foreground flex-wrap", children: [
|
|
3895
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
3896
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-5 h-0.5 rounded", style: { background: "var(--muted-foreground)", opacity: 0.4 } }),
|
|
3897
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "flow" })
|
|
3898
|
+
] }),
|
|
3899
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
3900
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-5 h-0.5 bg-primary rounded" }),
|
|
3901
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "showIf" })
|
|
3902
|
+
] }),
|
|
3903
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
3904
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-5 h-0.5 rounded", style: { background: "#f59e0b" } }),
|
|
3905
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "jump" })
|
|
3906
|
+
] }),
|
|
3907
|
+
sectionIds.map((id, i) => {
|
|
3908
|
+
const section = schema.sections.find((s) => s.id === id);
|
|
3909
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
3910
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-3 h-3 rounded-sm", style: { background: SECTION_COLORS[i % SECTION_COLORS.length] } }),
|
|
3911
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: section?.title || id })
|
|
3912
|
+
] }, id);
|
|
3913
|
+
})
|
|
3914
|
+
] })
|
|
3915
|
+
] });
|
|
3916
|
+
}
|
|
3917
|
+
function TemplateGallery({ templates, onSelect, onClose }) {
|
|
3918
|
+
const [search, setSearch] = react.useState("");
|
|
3919
|
+
const [activeCategory, setActiveCategory] = react.useState("all");
|
|
3920
|
+
const [confirmTemplate, setConfirmTemplate] = react.useState(null);
|
|
3921
|
+
const categories = react.useMemo(() => {
|
|
3922
|
+
const cats = /* @__PURE__ */ new Set();
|
|
3923
|
+
for (const t of templates) cats.add(t.meta.category);
|
|
3924
|
+
return ["all", ...Array.from(cats)];
|
|
3925
|
+
}, [templates]);
|
|
3926
|
+
const filtered = react.useMemo(() => {
|
|
3927
|
+
return templates.filter((t) => {
|
|
3928
|
+
const matchesCategory = activeCategory === "all" || t.meta.category === activeCategory;
|
|
3929
|
+
const matchesSearch = !search || t.meta.name.toLowerCase().includes(search.toLowerCase()) || t.meta.description.toLowerCase().includes(search.toLowerCase()) || t.meta.tags?.some((tag) => tag.toLowerCase().includes(search.toLowerCase()));
|
|
3930
|
+
return matchesCategory && matchesSearch;
|
|
3931
|
+
});
|
|
3932
|
+
}, [templates, activeCategory, search]);
|
|
3933
|
+
const handleConfirm = () => {
|
|
3934
|
+
if (confirmTemplate) {
|
|
3935
|
+
onSelect(confirmTemplate);
|
|
3936
|
+
setConfirmTemplate(null);
|
|
3937
|
+
onClose();
|
|
3938
|
+
}
|
|
3939
|
+
};
|
|
3940
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full max-w-3xl max-h-[85vh] mx-4 bg-card border border-border rounded-lg shadow-xl flex flex-col overflow-hidden", children: [
|
|
3941
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "shrink-0 flex items-center justify-between px-4 py-3 border-b border-border", children: [
|
|
3942
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
3943
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.LayoutTemplate, { size: 18, className: "text-primary", strokeWidth: 1.75 }),
|
|
3944
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-sm font-semibold", children: "Template Gallery" }),
|
|
3945
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-muted-foreground", children: [
|
|
3946
|
+
"(",
|
|
3947
|
+
templates.length,
|
|
3948
|
+
" templates)"
|
|
3949
|
+
] })
|
|
3950
|
+
] }),
|
|
3951
|
+
/* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Button, { variant: "ghost", size: "icon-sm", onClick: onClose, title: "Close", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 16, strokeWidth: 1.75 }) })
|
|
3952
|
+
] }),
|
|
3953
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "shrink-0 px-4 py-2 space-y-2 border-b border-border", children: [
|
|
3954
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
|
|
3955
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Search, { size: 14, className: "absolute left-2.5 top-1/2 -translate-y-1/2 text-muted-foreground", strokeWidth: 1.75 }),
|
|
3956
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3957
|
+
"input",
|
|
3958
|
+
{
|
|
3959
|
+
type: "text",
|
|
3960
|
+
placeholder: "Search templates...",
|
|
3961
|
+
value: search,
|
|
3962
|
+
onChange: (e) => setSearch(e.target.value),
|
|
3963
|
+
className: "w-full pl-8 pr-3 py-1.5 text-sm bg-background border border-border rounded-md text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-primary"
|
|
3964
|
+
}
|
|
3965
|
+
)
|
|
3966
|
+
] }),
|
|
3967
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-1 overflow-x-auto pb-0.5", children: categories.map((cat) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
3968
|
+
"button",
|
|
3969
|
+
{
|
|
3970
|
+
type: "button",
|
|
3971
|
+
onClick: () => setActiveCategory(cat),
|
|
3972
|
+
className: `shrink-0 px-2.5 py-1 rounded-md text-xs font-medium transition-colors ${activeCategory === cat ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground hover:text-foreground"}`,
|
|
3973
|
+
children: cat === "all" ? "All" : cat.charAt(0).toUpperCase() + cat.slice(1)
|
|
3974
|
+
},
|
|
3975
|
+
cat
|
|
3976
|
+
)) })
|
|
3977
|
+
] }),
|
|
3978
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 overflow-y-auto p-4", children: filtered.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col items-center justify-center h-40 text-muted-foreground text-sm", children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: "No templates match your search." }) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-3", children: filtered.map((template) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3979
|
+
"button",
|
|
3980
|
+
{
|
|
3981
|
+
type: "button",
|
|
3982
|
+
onClick: () => setConfirmTemplate(template),
|
|
3983
|
+
className: "text-left p-3 rounded-lg border border-border bg-background hover:border-primary/50 hover:bg-primary/5 transition-colors group",
|
|
3984
|
+
children: [
|
|
3985
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-2 mb-1.5", children: [
|
|
3986
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-medium text-foreground group-hover:text-primary transition-colors", children: template.meta.name }),
|
|
3987
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 px-1.5 py-0.5 rounded text-[10px] font-medium bg-muted text-muted-foreground", children: template.meta.category })
|
|
3988
|
+
] }),
|
|
3989
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground mb-2 line-clamp-2", children: template.meta.description }),
|
|
3990
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 text-xs text-muted-foreground", children: [
|
|
3991
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-1", children: [
|
|
3992
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileText, { size: 11, strokeWidth: 1.75 }),
|
|
3993
|
+
template.meta.fieldCount,
|
|
3994
|
+
" field",
|
|
3995
|
+
template.meta.fieldCount !== 1 ? "s" : ""
|
|
3996
|
+
] }),
|
|
3997
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-1", children: [
|
|
3998
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Layers, { size: 11, strokeWidth: 1.75 }),
|
|
3999
|
+
template.meta.sectionCount,
|
|
4000
|
+
" section",
|
|
4001
|
+
template.meta.sectionCount !== 1 ? "s" : ""
|
|
4002
|
+
] })
|
|
4003
|
+
] })
|
|
4004
|
+
]
|
|
4005
|
+
},
|
|
4006
|
+
template.meta.id
|
|
4007
|
+
)) }) }),
|
|
4008
|
+
confirmTemplate && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "shrink-0 px-4 py-3 border-t border-border bg-muted/50 flex items-center justify-between gap-3", children: [
|
|
4009
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-sm", children: [
|
|
4010
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Use " }),
|
|
4011
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-foreground", children: confirmTemplate.meta.name }),
|
|
4012
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "? This will replace the current form." })
|
|
4013
|
+
] }),
|
|
4014
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 shrink-0", children: [
|
|
4015
|
+
/* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Button, { variant: "ghost", size: "sm", onClick: () => setConfirmTemplate(null), children: "Cancel" }),
|
|
4016
|
+
/* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Button, { size: "sm", onClick: handleConfirm, children: "Use Template" })
|
|
4017
|
+
] })
|
|
4018
|
+
] })
|
|
4019
|
+
] }) });
|
|
4020
|
+
}
|
|
4021
|
+
function useDebouncedValidation(delayMs = 500) {
|
|
4022
|
+
const [errors, setErrors] = react.useState([]);
|
|
4023
|
+
const timerRef = react.useRef(null);
|
|
4024
|
+
const validate = react.useCallback(
|
|
4025
|
+
(text) => {
|
|
4026
|
+
if (timerRef.current) {
|
|
4027
|
+
clearTimeout(timerRef.current);
|
|
4028
|
+
}
|
|
4029
|
+
timerRef.current = setTimeout(() => {
|
|
4030
|
+
const result = [];
|
|
4031
|
+
let parsed;
|
|
4032
|
+
try {
|
|
4033
|
+
parsed = JSON.parse(text);
|
|
4034
|
+
} catch (err) {
|
|
4035
|
+
if (err instanceof SyntaxError) {
|
|
4036
|
+
const posMatch = err.message.match(/position\s+(\d+)/i);
|
|
4037
|
+
let line = 1;
|
|
4038
|
+
let column = 1;
|
|
4039
|
+
if (posMatch) {
|
|
4040
|
+
const pos = parseInt(posMatch[1], 10);
|
|
4041
|
+
const upToPos = text.slice(0, pos);
|
|
4042
|
+
line = (upToPos.match(/\n/g) || []).length + 1;
|
|
4043
|
+
column = pos - upToPos.lastIndexOf("\n");
|
|
4044
|
+
}
|
|
4045
|
+
result.push({ message: err.message, line, column, endLine: line, endColumn: column + 1 });
|
|
4046
|
+
} else {
|
|
4047
|
+
result.push({ message: "Invalid JSON" });
|
|
4048
|
+
}
|
|
4049
|
+
setErrors(result);
|
|
4050
|
+
return;
|
|
4051
|
+
}
|
|
4052
|
+
try {
|
|
4053
|
+
fieldcraftCore.validateSchema(parsed);
|
|
4054
|
+
} catch (err) {
|
|
4055
|
+
if (err instanceof fieldcraftCore.FormEngineSchemaError) {
|
|
4056
|
+
for (const issue of err.issues) {
|
|
4057
|
+
result.push({
|
|
4058
|
+
message: issue.message,
|
|
4059
|
+
path: issue.path?.join(".")
|
|
4060
|
+
});
|
|
4061
|
+
}
|
|
4062
|
+
} else if (err instanceof Error) {
|
|
4063
|
+
result.push({ message: err.message });
|
|
4064
|
+
}
|
|
4065
|
+
}
|
|
4066
|
+
setErrors(result);
|
|
4067
|
+
}, delayMs);
|
|
4068
|
+
},
|
|
4069
|
+
[delayMs]
|
|
4070
|
+
);
|
|
4071
|
+
return { errors, validate };
|
|
4072
|
+
}
|
|
3115
4073
|
var ThemeCtx = react.createContext({});
|
|
3116
4074
|
function useBuilderTheme() {
|
|
3117
4075
|
return react.useContext(ThemeCtx);
|
|
@@ -3184,7 +4142,16 @@ var FormBuilderErrorBoundary = class extends react.Component {
|
|
|
3184
4142
|
}
|
|
3185
4143
|
};
|
|
3186
4144
|
function FormBuilderCore(props) {
|
|
3187
|
-
const { initialSchema = DEFAULT_SCHEMA, onChange, onSave, height = "100vh", theme, className, toolbarExtra, questionTypes, palette } = props;
|
|
4145
|
+
const { initialSchema = DEFAULT_SCHEMA, onChange, onSave, height = "100vh", theme, className, toolbarExtra, questionTypes, palette, preview, templates, schemaUrl } = props;
|
|
4146
|
+
const [viewMode, setViewMode] = react.useState("design");
|
|
4147
|
+
const [jsonText, setJsonText] = react.useState("");
|
|
4148
|
+
const [jsonSwitchError, setJsonSwitchError] = react.useState(null);
|
|
4149
|
+
const [showLogicMap, setShowLogicMap] = react.useState(false);
|
|
4150
|
+
const [showTemplateGallery, setShowTemplateGallery] = react.useState(false);
|
|
4151
|
+
const [saveValidationErrors, setSaveValidationErrors] = react.useState(null);
|
|
4152
|
+
const [schemaUrlLoading, setSchemaUrlLoading] = react.useState(!!schemaUrl);
|
|
4153
|
+
const [schemaUrlError, setSchemaUrlError] = react.useState(null);
|
|
4154
|
+
const { errors: jsonErrors, validate: validateJson } = useDebouncedValidation(400);
|
|
3188
4155
|
const mergedQuestionTypes = questionTypes ? { ...QUESTION_TYPE_INFO, ...questionTypes } : QUESTION_TYPE_INFO;
|
|
3189
4156
|
const builderState = useBuilderState(initialSchema);
|
|
3190
4157
|
const dragDrop = useDragDrop(builderState);
|
|
@@ -3200,15 +4167,54 @@ function FormBuilderCore(props) {
|
|
|
3200
4167
|
setMobilePanel("none");
|
|
3201
4168
|
}
|
|
3202
4169
|
}, [dragDrop.activeDragItem]);
|
|
4170
|
+
react.useEffect(() => {
|
|
4171
|
+
if (!schemaUrl) return;
|
|
4172
|
+
let cancelled = false;
|
|
4173
|
+
setSchemaUrlLoading(true);
|
|
4174
|
+
setSchemaUrlError(null);
|
|
4175
|
+
fetch(schemaUrl).then((res) => {
|
|
4176
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}: ${res.statusText}`);
|
|
4177
|
+
return res.json();
|
|
4178
|
+
}).then((json) => {
|
|
4179
|
+
if (cancelled) return;
|
|
4180
|
+
try {
|
|
4181
|
+
const validated = fieldcraftCore.validateSchema(json);
|
|
4182
|
+
builderState.resetSchema(validated);
|
|
4183
|
+
} catch (err) {
|
|
4184
|
+
if (err instanceof fieldcraftCore.FormEngineSchemaError) {
|
|
4185
|
+
setSchemaUrlError(`Invalid schema: ${err.issues[0]?.message ?? "validation failed"}`);
|
|
4186
|
+
} else {
|
|
4187
|
+
setSchemaUrlError(err.message);
|
|
4188
|
+
}
|
|
4189
|
+
}
|
|
4190
|
+
setSchemaUrlLoading(false);
|
|
4191
|
+
}).catch((err) => {
|
|
4192
|
+
if (cancelled) return;
|
|
4193
|
+
setSchemaUrlError(err.message ?? "Failed to fetch schema");
|
|
4194
|
+
setSchemaUrlLoading(false);
|
|
4195
|
+
});
|
|
4196
|
+
return () => {
|
|
4197
|
+
cancelled = true;
|
|
4198
|
+
};
|
|
4199
|
+
}, [schemaUrl]);
|
|
3203
4200
|
react.useEffect(() => {
|
|
3204
4201
|
if (onChange && builderState.isDirty) {
|
|
3205
4202
|
onChange(builderState.schema);
|
|
3206
4203
|
}
|
|
3207
4204
|
}, [builderState.schema, builderState.isDirty, onChange]);
|
|
3208
4205
|
const handleSave = react.useCallback(() => {
|
|
3209
|
-
if (onSave)
|
|
4206
|
+
if (!onSave) return;
|
|
4207
|
+
try {
|
|
4208
|
+
fieldcraftCore.validateSchema(builderState.schema);
|
|
4209
|
+
setSaveValidationErrors(null);
|
|
3210
4210
|
onSave(builderState.schema);
|
|
3211
4211
|
builderState.markClean();
|
|
4212
|
+
} catch (err) {
|
|
4213
|
+
if (err instanceof fieldcraftCore.FormEngineSchemaError) {
|
|
4214
|
+
setSaveValidationErrors(err.issues.map((issue) => issue.message));
|
|
4215
|
+
} else {
|
|
4216
|
+
setSaveValidationErrors([err.message ?? "Unknown validation error"]);
|
|
4217
|
+
}
|
|
3212
4218
|
}
|
|
3213
4219
|
}, [onSave, builderState]);
|
|
3214
4220
|
const handleExport = react.useCallback(() => {
|
|
@@ -3251,6 +4257,32 @@ ${details}`);
|
|
|
3251
4257
|
},
|
|
3252
4258
|
[builderState]
|
|
3253
4259
|
);
|
|
4260
|
+
const handleViewModeChange = react.useCallback(
|
|
4261
|
+
(newMode) => {
|
|
4262
|
+
setJsonSwitchError(null);
|
|
4263
|
+
if (viewMode === "json" && newMode !== "json") {
|
|
4264
|
+
try {
|
|
4265
|
+
const parsed = JSON.parse(jsonText);
|
|
4266
|
+
const validated = fieldcraftCore.validateSchema(parsed);
|
|
4267
|
+
builderState.resetSchema(validated);
|
|
4268
|
+
} catch (err) {
|
|
4269
|
+
if (err instanceof SyntaxError) {
|
|
4270
|
+
setJsonSwitchError("Cannot switch: JSON has syntax errors");
|
|
4271
|
+
} else if (err instanceof fieldcraftCore.FormEngineSchemaError) {
|
|
4272
|
+
setJsonSwitchError(`Cannot switch: ${err.issues[0]?.message ?? "Invalid schema"}`);
|
|
4273
|
+
} else {
|
|
4274
|
+
setJsonSwitchError("Cannot switch: Invalid schema");
|
|
4275
|
+
}
|
|
4276
|
+
return;
|
|
4277
|
+
}
|
|
4278
|
+
}
|
|
4279
|
+
if (newMode === "json") {
|
|
4280
|
+
setJsonText(JSON.stringify(builderState.schema, null, 2));
|
|
4281
|
+
}
|
|
4282
|
+
setViewMode(newMode);
|
|
4283
|
+
},
|
|
4284
|
+
[viewMode, jsonText, builderState]
|
|
4285
|
+
);
|
|
3254
4286
|
const handleKeyDown = react.useCallback(
|
|
3255
4287
|
(e) => {
|
|
3256
4288
|
const mod = e.metaKey || e.ctrlKey;
|
|
@@ -3352,8 +4384,74 @@ ${details}`);
|
|
|
3352
4384
|
/* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Separator, { orientation: "vertical", className: "mx-2 h-5" }),
|
|
3353
4385
|
builderState.isDirty && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-primary", role: "status", children: "Unsaved changes" })
|
|
3354
4386
|
] }),
|
|
3355
|
-
/* @__PURE__ */ jsxRuntime.
|
|
4387
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-center gap-2", children: [
|
|
4388
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-0.5 bg-muted rounded-md p-0.5", children: [
|
|
4389
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
4390
|
+
fieldcraftReact.Button,
|
|
4391
|
+
{
|
|
4392
|
+
variant: viewMode === "design" ? "secondary" : "ghost",
|
|
4393
|
+
size: "sm",
|
|
4394
|
+
onClick: () => handleViewModeChange("design"),
|
|
4395
|
+
className: "gap-1.5 h-7 px-2.5 text-xs",
|
|
4396
|
+
children: [
|
|
4397
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.PencilRuler, { size: 13, strokeWidth: 1.75 }),
|
|
4398
|
+
"Design"
|
|
4399
|
+
]
|
|
4400
|
+
}
|
|
4401
|
+
),
|
|
4402
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
4403
|
+
fieldcraftReact.Button,
|
|
4404
|
+
{
|
|
4405
|
+
variant: viewMode === "preview" ? "secondary" : "ghost",
|
|
4406
|
+
size: "sm",
|
|
4407
|
+
onClick: () => handleViewModeChange("preview"),
|
|
4408
|
+
className: "gap-1.5 h-7 px-2.5 text-xs",
|
|
4409
|
+
children: [
|
|
4410
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Eye, { size: 13, strokeWidth: 1.75 }),
|
|
4411
|
+
"Preview"
|
|
4412
|
+
]
|
|
4413
|
+
}
|
|
4414
|
+
),
|
|
4415
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
4416
|
+
fieldcraftReact.Button,
|
|
4417
|
+
{
|
|
4418
|
+
variant: viewMode === "json" ? "secondary" : "ghost",
|
|
4419
|
+
size: "sm",
|
|
4420
|
+
onClick: () => handleViewModeChange("json"),
|
|
4421
|
+
className: "gap-1.5 h-7 px-2.5 text-xs",
|
|
4422
|
+
children: [
|
|
4423
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Code, { size: 13, strokeWidth: 1.75 }),
|
|
4424
|
+
"JSON"
|
|
4425
|
+
]
|
|
4426
|
+
}
|
|
4427
|
+
)
|
|
4428
|
+
] }),
|
|
4429
|
+
toolbarExtra
|
|
4430
|
+
] }),
|
|
3356
4431
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-1.5", children: [
|
|
4432
|
+
templates && templates.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
4433
|
+
fieldcraftReact.Button,
|
|
4434
|
+
{
|
|
4435
|
+
variant: "ghost",
|
|
4436
|
+
size: "icon-sm",
|
|
4437
|
+
onClick: () => setShowTemplateGallery(true),
|
|
4438
|
+
title: "Browse templates",
|
|
4439
|
+
"aria-label": "Template gallery",
|
|
4440
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.LayoutTemplate, { size: 15, strokeWidth: 1.75 })
|
|
4441
|
+
}
|
|
4442
|
+
),
|
|
4443
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4444
|
+
fieldcraftReact.Button,
|
|
4445
|
+
{
|
|
4446
|
+
variant: "ghost",
|
|
4447
|
+
size: "icon-sm",
|
|
4448
|
+
onClick: () => setShowLogicMap(true),
|
|
4449
|
+
title: "View branching logic map",
|
|
4450
|
+
"aria-label": "Logic map",
|
|
4451
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.GitBranch, { size: 15, strokeWidth: 1.75 })
|
|
4452
|
+
}
|
|
4453
|
+
),
|
|
4454
|
+
/* @__PURE__ */ jsxRuntime.jsx(fieldcraftReact.Separator, { orientation: "vertical", className: "mx-1 h-5" }),
|
|
3357
4455
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3358
4456
|
fieldcraftReact.Button,
|
|
3359
4457
|
{
|
|
@@ -3397,6 +4495,47 @@ ${details}`);
|
|
|
3397
4495
|
}
|
|
3398
4496
|
) }),
|
|
3399
4497
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-center gap-1", children: [
|
|
4498
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-0.5 bg-muted rounded-md p-0.5", children: [
|
|
4499
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
4500
|
+
fieldcraftReact.Button,
|
|
4501
|
+
{
|
|
4502
|
+
variant: viewMode === "design" ? "secondary" : "ghost",
|
|
4503
|
+
size: "sm",
|
|
4504
|
+
onClick: () => handleViewModeChange("design"),
|
|
4505
|
+
className: "gap-1 h-7 px-2 text-xs",
|
|
4506
|
+
children: [
|
|
4507
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.PencilRuler, { size: 12, strokeWidth: 1.75 }),
|
|
4508
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden sm:inline", children: "Design" })
|
|
4509
|
+
]
|
|
4510
|
+
}
|
|
4511
|
+
),
|
|
4512
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
4513
|
+
fieldcraftReact.Button,
|
|
4514
|
+
{
|
|
4515
|
+
variant: viewMode === "preview" ? "secondary" : "ghost",
|
|
4516
|
+
size: "sm",
|
|
4517
|
+
onClick: () => handleViewModeChange("preview"),
|
|
4518
|
+
className: "gap-1 h-7 px-2 text-xs",
|
|
4519
|
+
children: [
|
|
4520
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Eye, { size: 12, strokeWidth: 1.75 }),
|
|
4521
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden sm:inline", children: "Preview" })
|
|
4522
|
+
]
|
|
4523
|
+
}
|
|
4524
|
+
),
|
|
4525
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
4526
|
+
fieldcraftReact.Button,
|
|
4527
|
+
{
|
|
4528
|
+
variant: viewMode === "json" ? "secondary" : "ghost",
|
|
4529
|
+
size: "sm",
|
|
4530
|
+
onClick: () => handleViewModeChange("json"),
|
|
4531
|
+
className: "gap-1 h-7 px-2 text-xs",
|
|
4532
|
+
children: [
|
|
4533
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Code, { size: 12, strokeWidth: 1.75 }),
|
|
4534
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden sm:inline", children: "JSON" })
|
|
4535
|
+
]
|
|
4536
|
+
}
|
|
4537
|
+
)
|
|
4538
|
+
] }),
|
|
3400
4539
|
toolbarExtra,
|
|
3401
4540
|
/* @__PURE__ */ jsxRuntime.jsxs(fieldcraftReact.Button, { onClick: handleSave, size: "sm", className: "border-0 shadow-sm fcb-glow ml-1", "aria-label": "Save form", children: [
|
|
3402
4541
|
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Save, { size: 14, strokeWidth: 2 }),
|
|
@@ -3485,7 +4624,50 @@ ${details}`);
|
|
|
3485
4624
|
"aria-hidden": "true"
|
|
3486
4625
|
}
|
|
3487
4626
|
),
|
|
3488
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "
|
|
4627
|
+
jsonSwitchError && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "shrink-0 px-4 py-2 bg-destructive/10 border-b border-destructive/20 text-destructive text-xs flex items-center justify-between", children: [
|
|
4628
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: jsonSwitchError }),
|
|
4629
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: () => setJsonSwitchError(null), className: "ml-2 hover:underline", children: "Dismiss" })
|
|
4630
|
+
] }),
|
|
4631
|
+
saveValidationErrors && saveValidationErrors.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "shrink-0 px-4 py-2 bg-destructive/10 border-b border-destructive/20 text-xs", children: [
|
|
4632
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-1", children: [
|
|
4633
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-semibold text-destructive", children: [
|
|
4634
|
+
"Schema validation failed (",
|
|
4635
|
+
saveValidationErrors.length,
|
|
4636
|
+
" issue",
|
|
4637
|
+
saveValidationErrors.length !== 1 ? "s" : "",
|
|
4638
|
+
")"
|
|
4639
|
+
] }),
|
|
4640
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: () => setSaveValidationErrors(null), className: "text-destructive hover:underline", children: "Dismiss" })
|
|
4641
|
+
] }),
|
|
4642
|
+
/* @__PURE__ */ jsxRuntime.jsxs("ul", { className: "list-disc pl-4 text-destructive/80 space-y-0.5", children: [
|
|
4643
|
+
saveValidationErrors.slice(0, 10).map((msg, i) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: msg }, i)),
|
|
4644
|
+
saveValidationErrors.length > 10 && /* @__PURE__ */ jsxRuntime.jsxs("li", { children: [
|
|
4645
|
+
"...and ",
|
|
4646
|
+
saveValidationErrors.length - 10,
|
|
4647
|
+
" more"
|
|
4648
|
+
] })
|
|
4649
|
+
] })
|
|
4650
|
+
] }),
|
|
4651
|
+
schemaUrlLoading && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "shrink-0 px-4 py-2 border-b border-border bg-muted/50 text-xs text-muted-foreground flex items-center gap-2", children: [
|
|
4652
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block w-3 h-3 border-2 border-primary border-t-transparent rounded-full animate-spin" }),
|
|
4653
|
+
"Loading schema from URL..."
|
|
4654
|
+
] }),
|
|
4655
|
+
schemaUrlError && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "shrink-0 px-4 py-2 bg-destructive/10 border-b border-destructive/20 text-destructive text-xs flex items-center justify-between", children: [
|
|
4656
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
4657
|
+
"Failed to load schema: ",
|
|
4658
|
+
schemaUrlError
|
|
4659
|
+
] }),
|
|
4660
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: () => setSchemaUrlError(null), className: "ml-2 hover:underline", children: "Dismiss" })
|
|
4661
|
+
] }),
|
|
4662
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 flex overflow-hidden relative", children: viewMode === "json" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
4663
|
+
JsonEditorPanel,
|
|
4664
|
+
{
|
|
4665
|
+
value: jsonText,
|
|
4666
|
+
onChange: setJsonText,
|
|
4667
|
+
errors: jsonErrors,
|
|
4668
|
+
onValidate: validateJson
|
|
4669
|
+
}
|
|
4670
|
+
) : viewMode === "preview" ? /* @__PURE__ */ jsxRuntime.jsx(FormPreviewPanel, { schema: builderState.schema, preview }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3489
4671
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden md:flex", children: /* @__PURE__ */ jsxRuntime.jsx(QuestionPalette, { questionTypes: mergedQuestionTypes, palette }) }),
|
|
3490
4672
|
/* @__PURE__ */ jsxRuntime.jsx(FormCanvas, { builderState }),
|
|
3491
4673
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden md:flex", children: /* @__PURE__ */ jsxRuntime.jsx(PropertiesPanel, { builderState }) }),
|
|
@@ -3499,11 +4681,27 @@ ${details}`);
|
|
|
3499
4681
|
"aria-hidden": "true"
|
|
3500
4682
|
}
|
|
3501
4683
|
)
|
|
3502
|
-
] })
|
|
4684
|
+
] }) })
|
|
3503
4685
|
]
|
|
3504
4686
|
}
|
|
3505
4687
|
),
|
|
3506
|
-
/* @__PURE__ */ jsxRuntime.jsx(core.DragOverlay, { dropAnimation: null, children: dragDrop.activeDragItem && /* @__PURE__ */ jsxRuntime.jsx(DragOverlayContent, { item: dragDrop.activeDragItem, schema: builderState.schema, questionTypes: mergedQuestionTypes }) })
|
|
4688
|
+
/* @__PURE__ */ jsxRuntime.jsx(core.DragOverlay, { dropAnimation: null, children: dragDrop.activeDragItem && /* @__PURE__ */ jsxRuntime.jsx(DragOverlayContent, { item: dragDrop.activeDragItem, schema: builderState.schema, questionTypes: mergedQuestionTypes }) }),
|
|
4689
|
+
showLogicMap && /* @__PURE__ */ jsxRuntime.jsx(
|
|
4690
|
+
LogicFlowEditor,
|
|
4691
|
+
{
|
|
4692
|
+
schema: builderState.schema,
|
|
4693
|
+
onChange: (updated) => builderState.resetSchema(updated),
|
|
4694
|
+
onClose: () => setShowLogicMap(false)
|
|
4695
|
+
}
|
|
4696
|
+
),
|
|
4697
|
+
showTemplateGallery && templates && /* @__PURE__ */ jsxRuntime.jsx(
|
|
4698
|
+
TemplateGallery,
|
|
4699
|
+
{
|
|
4700
|
+
templates,
|
|
4701
|
+
onSelect: (template) => builderState.resetSchema(template.schema),
|
|
4702
|
+
onClose: () => setShowTemplateGallery(false)
|
|
4703
|
+
}
|
|
4704
|
+
)
|
|
3507
4705
|
]
|
|
3508
4706
|
}
|
|
3509
4707
|
) });
|
|
@@ -3558,76 +4756,54 @@ var FormBuilder = requireLicense(FormBuilderInner, "FormBuilder");
|
|
|
3558
4756
|
|
|
3559
4757
|
// src/form-builder/theme/presets.ts
|
|
3560
4758
|
var squaredrDarkPreset = {
|
|
3561
|
-
background: "#
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
// sr-error
|
|
3585
|
-
destructiveForeground: "#e8e8ea",
|
|
3586
|
-
// ink-100
|
|
3587
|
-
border: "#1c1c20",
|
|
3588
|
-
// ink-800
|
|
3589
|
-
input: "#1c1c20",
|
|
3590
|
-
// ink-800
|
|
3591
|
-
ring: "oklch(0.82 0.14 210)",
|
|
3592
|
-
// sr-accent-cyan
|
|
3593
|
-
radius: "6px",
|
|
3594
|
-
surface: "#111113",
|
|
3595
|
-
// ink-900
|
|
3596
|
-
surfaceHover: "#17171a",
|
|
3597
|
-
// ink-850
|
|
3598
|
-
canvas: "#0a0a0b",
|
|
3599
|
-
// ink-950
|
|
3600
|
-
panel: "#111113",
|
|
3601
|
-
// ink-900
|
|
3602
|
-
borderStrong: "#26262c",
|
|
3603
|
-
// ink-700
|
|
3604
|
-
textDim: "#5a5a66"
|
|
3605
|
-
// ink-500
|
|
4759
|
+
background: "#0F1A1F",
|
|
4760
|
+
foreground: "#E8EFF1",
|
|
4761
|
+
card: "#16242A",
|
|
4762
|
+
primary: "#63BDB4",
|
|
4763
|
+
primaryForeground: "#0F1A1F",
|
|
4764
|
+
secondary: "#182F31",
|
|
4765
|
+
secondaryForeground: "#E8EFF1",
|
|
4766
|
+
muted: "#182F31",
|
|
4767
|
+
mutedForeground: "#8CA1A9",
|
|
4768
|
+
accent: "#182F31",
|
|
4769
|
+
accentForeground: "#E8EFF1",
|
|
4770
|
+
destructive: "#E08072",
|
|
4771
|
+
destructiveForeground: "#0F1A1F",
|
|
4772
|
+
border: "#2A3B42",
|
|
4773
|
+
input: "#2A3B42",
|
|
4774
|
+
ring: "#63BDB4",
|
|
4775
|
+
radius: "0px",
|
|
4776
|
+
surface: "#16242A",
|
|
4777
|
+
surfaceHover: "#182F31",
|
|
4778
|
+
canvas: "#0F1A1F",
|
|
4779
|
+
panel: "#16242A",
|
|
4780
|
+
borderStrong: "#2F4F4C",
|
|
4781
|
+
textDim: "#5E7680"
|
|
3606
4782
|
};
|
|
3607
4783
|
var cleanPreset = {
|
|
3608
|
-
background: "#
|
|
3609
|
-
foreground: "#
|
|
3610
|
-
card: "#
|
|
3611
|
-
primary: "#
|
|
3612
|
-
primaryForeground: "#
|
|
3613
|
-
secondary: "#
|
|
3614
|
-
secondaryForeground: "#
|
|
3615
|
-
muted: "#
|
|
3616
|
-
mutedForeground: "#
|
|
3617
|
-
accent: "#
|
|
3618
|
-
accentForeground: "#
|
|
3619
|
-
destructive: "#
|
|
3620
|
-
destructiveForeground: "#
|
|
3621
|
-
border: "#
|
|
3622
|
-
input: "#
|
|
3623
|
-
ring: "#
|
|
3624
|
-
radius: "
|
|
3625
|
-
surface: "#
|
|
3626
|
-
surfaceHover: "#
|
|
3627
|
-
canvas: "#
|
|
3628
|
-
panel: "#
|
|
3629
|
-
borderStrong: "#
|
|
3630
|
-
textDim: "#
|
|
4784
|
+
background: "#F4F7F8",
|
|
4785
|
+
foreground: "#12222A",
|
|
4786
|
+
card: "#FFFFFF",
|
|
4787
|
+
primary: "#1F6B6E",
|
|
4788
|
+
primaryForeground: "#FFFFFF",
|
|
4789
|
+
secondary: "#EDF3F2",
|
|
4790
|
+
secondaryForeground: "#12222A",
|
|
4791
|
+
muted: "#EDF3F2",
|
|
4792
|
+
mutedForeground: "#6A7B85",
|
|
4793
|
+
accent: "#EDF3F2",
|
|
4794
|
+
accentForeground: "#12222A",
|
|
4795
|
+
destructive: "#B04A3C",
|
|
4796
|
+
destructiveForeground: "#FFFFFF",
|
|
4797
|
+
border: "#DCE4E8",
|
|
4798
|
+
input: "#DCE4E8",
|
|
4799
|
+
ring: "#1F6B6E",
|
|
4800
|
+
radius: "0px",
|
|
4801
|
+
surface: "#FAFCFC",
|
|
4802
|
+
surfaceHover: "#EDF3F2",
|
|
4803
|
+
canvas: "#F4F7F8",
|
|
4804
|
+
panel: "#FFFFFF",
|
|
4805
|
+
borderStrong: "#B9D1CF",
|
|
4806
|
+
textDim: "#6A7B85"
|
|
3631
4807
|
};
|
|
3632
4808
|
|
|
3633
4809
|
exports.DEFAULT_PALETTE = DEFAULT_PALETTE;
|