@waypointjs/builder 0.1.5 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -4,6 +4,9 @@ var core = require('@waypointjs/core');
4
4
  var react = require('react');
5
5
  var zustand = require('zustand');
6
6
  var jsxRuntime = require('react/jsx-runtime');
7
+ var core$1 = require('@dnd-kit/core');
8
+ var sortable = require('@dnd-kit/sortable');
9
+ var utilities = require('@dnd-kit/utilities');
7
10
  var react$1 = require('@waypointjs/react');
8
11
  var devtools = require('@waypointjs/devtools');
9
12
 
@@ -1181,8 +1184,19 @@ var VALIDATION_RULES = [
1181
1184
  { type: "notInEnum", label: "not in enum", hasValue: true, isEnum: true },
1182
1185
  { type: "custom", label: "Custom validator", hasValue: false }
1183
1186
  ];
1187
+ var COMPARATOR_TYPES = /* @__PURE__ */ new Set([
1188
+ "equals",
1189
+ "notEquals",
1190
+ "greaterThan",
1191
+ "greaterThanOrEqual",
1192
+ "lessThan",
1193
+ "lessThanOrEqual",
1194
+ "contains",
1195
+ "notContains"
1196
+ ]);
1184
1197
  function ValidationBuilder({ value, onChange }) {
1185
1198
  const externalEnums = useBuilderExternalEnums();
1199
+ const allFieldPaths = useAllFieldPaths();
1186
1200
  const updateRule = (index, updates) => {
1187
1201
  onChange(value.map((r, i) => i === index ? { ...r, ...updates } : r));
1188
1202
  };
@@ -1218,35 +1232,68 @@ function ValidationBuilder({ value, onChange }) {
1218
1232
  ]
1219
1233
  }
1220
1234
  ) : /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles2.valueGroup, children: [
1221
- /* @__PURE__ */ jsxRuntime.jsx(
1222
- "input",
1235
+ COMPARATOR_TYPES.has(rule.type) && /* @__PURE__ */ jsxRuntime.jsx(
1236
+ "button",
1223
1237
  {
1224
- style: styles2.valueInput,
1225
- placeholder: "value",
1226
- value: rule.value != null ? String(rule.value) : "",
1227
- onChange: (e) => updateRule(index, { value: e.target.value })
1238
+ type: "button",
1239
+ style: {
1240
+ ...styles2.refToggle,
1241
+ background: rule.refField !== void 0 ? "var(--wp-primary)" : "var(--wp-surface-muted)",
1242
+ color: rule.refField !== void 0 ? "#fff" : "var(--wp-text-secondary)"
1243
+ },
1244
+ title: rule.refField !== void 0 ? "Comparing to field \u2014 click for static value" : "Static value \u2014 click to compare to another field",
1245
+ onClick: () => {
1246
+ if (rule.refField !== void 0) {
1247
+ updateRule(index, { refField: void 0, value: "" });
1248
+ } else {
1249
+ updateRule(index, { refField: "", value: void 0 });
1250
+ }
1251
+ },
1252
+ children: "\u21C4"
1228
1253
  }
1229
1254
  ),
1230
- externalEnums.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
1255
+ rule.refField !== void 0 ? /* @__PURE__ */ jsxRuntime.jsxs(
1231
1256
  "select",
1232
1257
  {
1233
- style: styles2.enumPicker,
1234
- title: "Pick a value from an enum",
1235
- value: "",
1236
- onChange: (e) => {
1237
- if (e.target.value) updateRule(index, { value: e.target.value });
1238
- },
1258
+ style: { ...styles2.typeSelect, flex: "1 1 auto" },
1259
+ value: rule.refField,
1260
+ onChange: (e) => updateRule(index, { refField: e.target.value }),
1239
1261
  children: [
1240
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "\u229E" }),
1241
- externalEnums.map((en) => /* @__PURE__ */ jsxRuntime.jsx("optgroup", { label: en.label, children: en.values.map((v) => /* @__PURE__ */ jsxRuntime.jsxs("option", { value: String(v.value), children: [
1242
- v.label,
1243
- " (",
1244
- v.value,
1245
- ")"
1246
- ] }, String(v.value))) }, en.id))
1262
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "\u2014 pick field \u2014" }),
1263
+ allFieldPaths.filter((fp) => !fp.isExternal).map((fp) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: fp.path, children: fp.label }, fp.path))
1247
1264
  ]
1248
1265
  }
1249
- )
1266
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1267
+ /* @__PURE__ */ jsxRuntime.jsx(
1268
+ "input",
1269
+ {
1270
+ style: styles2.valueInput,
1271
+ placeholder: "value",
1272
+ value: rule.value != null ? String(rule.value) : "",
1273
+ onChange: (e) => updateRule(index, { value: e.target.value })
1274
+ }
1275
+ ),
1276
+ externalEnums.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
1277
+ "select",
1278
+ {
1279
+ style: styles2.enumPicker,
1280
+ title: "Pick a value from an enum",
1281
+ value: "",
1282
+ onChange: (e) => {
1283
+ if (e.target.value) updateRule(index, { value: e.target.value });
1284
+ },
1285
+ children: [
1286
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "\u229E" }),
1287
+ externalEnums.map((en) => /* @__PURE__ */ jsxRuntime.jsx("optgroup", { label: en.label, children: en.values.map((v) => /* @__PURE__ */ jsxRuntime.jsxs("option", { value: String(v.value), children: [
1288
+ v.label,
1289
+ " (",
1290
+ v.value,
1291
+ ")"
1292
+ ] }, String(v.value))) }, en.id))
1293
+ ]
1294
+ }
1295
+ )
1296
+ ] })
1250
1297
  ] })),
1251
1298
  rule.type === "custom" && /* @__PURE__ */ jsxRuntime.jsx(
1252
1299
  "input",
@@ -1293,7 +1340,17 @@ var styles2 = {
1293
1340
  background: "var(--wp-canvas)",
1294
1341
  color: "var(--wp-text)"
1295
1342
  },
1296
- valueGroup: { display: "flex", alignItems: "center", gap: 4 },
1343
+ valueGroup: { display: "flex", alignItems: "center", gap: 4, flex: "1 1 auto" },
1344
+ refToggle: {
1345
+ border: "1px solid var(--wp-border-muted)",
1346
+ borderRadius: "var(--wp-radius)",
1347
+ cursor: "pointer",
1348
+ fontSize: 11,
1349
+ padding: "4px 6px",
1350
+ flexShrink: 0,
1351
+ fontWeight: 600,
1352
+ lineHeight: 1
1353
+ },
1297
1354
  valueInput: {
1298
1355
  width: 90,
1299
1356
  fontSize: 12,
@@ -1557,6 +1614,8 @@ function FieldEditor() {
1557
1614
  const externalEnums = useBuilderExternalEnums();
1558
1615
  const [conditionModalOpen, setConditionModalOpen] = react.useState(false);
1559
1616
  const [validationModalOpen, setValidationModalOpen] = react.useState(false);
1617
+ const [dynDefaultModalOpen, setDynDefaultModalOpen] = react.useState(false);
1618
+ const [editingDynIdx, setEditingDynIdx] = react.useState(null);
1560
1619
  const step = schema.steps.find((s) => s.id === selectedStepId);
1561
1620
  const field = step?.fields.find((f) => f.id === selectedFieldId);
1562
1621
  if (!field || !step) {
@@ -1620,6 +1679,58 @@ function FieldEditor() {
1620
1679
  }
1621
1680
  )
1622
1681
  ] }),
1682
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.conditionRow, children: [
1683
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.conditionInfo, children: [
1684
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles5.label, children: "Dynamic defaults" }),
1685
+ (field.dynamicDefault?.length ?? 0) > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexDirection: "column", gap: 4 }, children: field.dynamicDefault.map((rule, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
1686
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: styles5.conditionBadge, children: [
1687
+ rule.when.rules.length,
1688
+ " rule",
1689
+ rule.when.rules.length !== 1 ? "s" : ""
1690
+ ] }),
1691
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { fontSize: 11, color: "var(--wp-text-subtle)" }, children: [
1692
+ "\u2192 ",
1693
+ JSON.stringify(rule.value)
1694
+ ] }),
1695
+ !readOnly && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1696
+ /* @__PURE__ */ jsxRuntime.jsx(
1697
+ "button",
1698
+ {
1699
+ style: { ...styles5.editConditionBtn, fontSize: 10, padding: "2px 6px" },
1700
+ onClick: () => {
1701
+ setEditingDynIdx(i);
1702
+ setDynDefaultModalOpen(true);
1703
+ },
1704
+ children: "Edit"
1705
+ }
1706
+ ),
1707
+ /* @__PURE__ */ jsxRuntime.jsx(
1708
+ "button",
1709
+ {
1710
+ style: { ...styles5.clearConditionBtn, fontSize: 10, padding: "2px 6px" },
1711
+ onClick: () => {
1712
+ const updated = field.dynamicDefault.filter((_, j) => j !== i);
1713
+ updateField(step.id, field.id, { dynamicDefault: updated.length ? updated : void 0 });
1714
+ },
1715
+ children: "\xD7"
1716
+ }
1717
+ )
1718
+ ] })
1719
+ ] }, i)) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles5.conditionNone, children: "No dynamic defaults" })
1720
+ ] }),
1721
+ !readOnly && /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles5.conditionActions, children: /* @__PURE__ */ jsxRuntime.jsx(
1722
+ "button",
1723
+ {
1724
+ style: styles5.editConditionBtn,
1725
+ onClick: () => {
1726
+ setEditingDynIdx(null);
1727
+ setDynDefaultModalOpen(true);
1728
+ },
1729
+ children: "Add"
1730
+ }
1731
+ ) })
1732
+ ] }),
1733
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles5.divider }),
1623
1734
  ENUM_FIELD_TYPES.includes(field.type) && externalEnums.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.group, children: [
1624
1735
  /* @__PURE__ */ jsxRuntime.jsx("label", { style: styles5.label, children: "Options source" }),
1625
1736
  /* @__PURE__ */ jsxRuntime.jsxs(
@@ -1747,6 +1858,28 @@ function FieldEditor() {
1747
1858
  ]
1748
1859
  }
1749
1860
  ),
1861
+ dynDefaultModalOpen && /* @__PURE__ */ jsxRuntime.jsx(
1862
+ DynDefaultModal,
1863
+ {
1864
+ rule: editingDynIdx !== null ? field.dynamicDefault?.[editingDynIdx] : void 0,
1865
+ onSave: (rule) => {
1866
+ const current = field.dynamicDefault ?? [];
1867
+ let updated;
1868
+ if (editingDynIdx !== null) {
1869
+ updated = current.map((r, i) => i === editingDynIdx ? rule : r);
1870
+ } else {
1871
+ updated = [...current, rule];
1872
+ }
1873
+ updateField(step.id, field.id, { dynamicDefault: updated });
1874
+ setDynDefaultModalOpen(false);
1875
+ setEditingDynIdx(null);
1876
+ },
1877
+ onClose: () => {
1878
+ setDynDefaultModalOpen(false);
1879
+ setEditingDynIdx(null);
1880
+ }
1881
+ }
1882
+ ),
1750
1883
  conditionModalOpen && /* @__PURE__ */ jsxRuntime.jsxs(
1751
1884
  Modal,
1752
1885
  {
@@ -1770,6 +1903,96 @@ function FieldEditor() {
1770
1903
  )
1771
1904
  ] });
1772
1905
  }
1906
+ function DynDefaultModal({
1907
+ rule,
1908
+ onSave,
1909
+ onClose
1910
+ }) {
1911
+ const [condition, setCondition] = react.useState(rule?.when);
1912
+ const [value, setValue] = react.useState(rule?.value != null ? String(rule.value) : "");
1913
+ const canSave = condition && condition.rules.length > 0 && value !== "";
1914
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1915
+ Modal,
1916
+ {
1917
+ title: rule ? "Edit dynamic default" : "Add dynamic default",
1918
+ onClose,
1919
+ width: 620,
1920
+ children: [
1921
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: { fontSize: 13, color: "var(--wp-text-muted)", margin: "0 0 16px" }, children: "When the condition matches, this value will be used as the field default." }),
1922
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: 16 }, children: [
1923
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 11, fontWeight: 600, color: "var(--wp-text-muted)", textTransform: "uppercase", letterSpacing: "0.05em", marginBottom: 6 }, children: "Condition" }),
1924
+ /* @__PURE__ */ jsxRuntime.jsx(ConditionBuilder, { value: condition, onChange: setCondition })
1925
+ ] }),
1926
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: 20 }, children: [
1927
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 11, fontWeight: 600, color: "var(--wp-text-muted)", textTransform: "uppercase", letterSpacing: "0.05em", marginBottom: 6 }, children: "Default value when condition matches" }),
1928
+ /* @__PURE__ */ jsxRuntime.jsx(
1929
+ "input",
1930
+ {
1931
+ style: {
1932
+ fontSize: 13,
1933
+ padding: "6px 8px",
1934
+ border: "1px solid var(--wp-border-muted)",
1935
+ borderRadius: "var(--wp-radius)",
1936
+ outline: "none",
1937
+ width: "100%",
1938
+ boxSizing: "border-box",
1939
+ background: "var(--wp-canvas)",
1940
+ color: "var(--wp-text)"
1941
+ },
1942
+ value,
1943
+ placeholder: "Value to set as default",
1944
+ onChange: (e) => setValue(e.target.value)
1945
+ }
1946
+ )
1947
+ ] }),
1948
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "flex-end", gap: 8 }, children: [
1949
+ /* @__PURE__ */ jsxRuntime.jsx(
1950
+ "button",
1951
+ {
1952
+ style: {
1953
+ fontSize: 13,
1954
+ padding: "7px 16px",
1955
+ background: "transparent",
1956
+ color: "var(--wp-text-muted)",
1957
+ border: "1px solid var(--wp-border)",
1958
+ borderRadius: "var(--wp-radius-lg)",
1959
+ cursor: "pointer"
1960
+ },
1961
+ onClick: onClose,
1962
+ children: "Cancel"
1963
+ }
1964
+ ),
1965
+ /* @__PURE__ */ jsxRuntime.jsx(
1966
+ "button",
1967
+ {
1968
+ disabled: !canSave,
1969
+ style: {
1970
+ fontSize: 13,
1971
+ padding: "7px 20px",
1972
+ background: canSave ? "var(--wp-primary)" : "var(--wp-border)",
1973
+ color: "var(--wp-canvas)",
1974
+ border: "none",
1975
+ borderRadius: "var(--wp-radius-lg)",
1976
+ cursor: canSave ? "pointer" : "not-allowed",
1977
+ fontWeight: 600,
1978
+ opacity: canSave ? 1 : 0.5
1979
+ },
1980
+ onClick: () => {
1981
+ if (!canSave || !condition) return;
1982
+ let parsed = value;
1983
+ if (value === "true") parsed = true;
1984
+ else if (value === "false") parsed = false;
1985
+ else if (!isNaN(Number(value)) && value.trim() !== "") parsed = Number(value);
1986
+ onSave({ when: condition, value: parsed });
1987
+ },
1988
+ children: rule ? "Update" : "Add"
1989
+ }
1990
+ )
1991
+ ] })
1992
+ ]
1993
+ }
1994
+ );
1995
+ }
1773
1996
  var styles5 = {
1774
1997
  container: { display: "flex", flexDirection: "column", height: "100%", overflow: "hidden" },
1775
1998
  empty: {
@@ -1989,6 +2212,107 @@ function getStepDependencyLabels(stepId, deps, schema) {
1989
2212
  const required = deps.get(stepId) ?? /* @__PURE__ */ new Set();
1990
2213
  return [...required].map((id) => schema.steps.find((s) => s.id === id)?.title ?? id).filter(Boolean);
1991
2214
  }
2215
+ function SortableItem({ id, disabled, children }) {
2216
+ const {
2217
+ attributes,
2218
+ listeners,
2219
+ setNodeRef,
2220
+ transform,
2221
+ transition,
2222
+ isDragging
2223
+ } = sortable.useSortable({ id, disabled });
2224
+ const style = {
2225
+ transform: utilities.CSS.Transform.toString(transform),
2226
+ transition,
2227
+ opacity: isDragging ? 0.4 : 1,
2228
+ position: "relative"
2229
+ };
2230
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { ref: setNodeRef, style, children: children({
2231
+ handleProps: { ...attributes, ...listeners },
2232
+ isDragging
2233
+ }) });
2234
+ }
2235
+ function DragHandle({ handleProps, disabled }) {
2236
+ return /* @__PURE__ */ jsxRuntime.jsx(
2237
+ "button",
2238
+ {
2239
+ type: "button",
2240
+ ...handleProps,
2241
+ style: {
2242
+ ...dragHandleStyle,
2243
+ ...disabled ? dragHandleDisabledStyle : {}
2244
+ },
2245
+ title: "Drag to reorder",
2246
+ "aria-label": "Drag handle",
2247
+ children: "\u283F"
2248
+ }
2249
+ );
2250
+ }
2251
+ var dragHandleStyle = {
2252
+ cursor: "grab",
2253
+ touchAction: "none",
2254
+ border: "none",
2255
+ background: "transparent",
2256
+ color: "var(--wp-text-muted)",
2257
+ fontSize: 16,
2258
+ padding: "2px 4px",
2259
+ borderRadius: 4,
2260
+ display: "flex",
2261
+ alignItems: "center",
2262
+ justifyContent: "center",
2263
+ flexShrink: 0,
2264
+ lineHeight: 1
2265
+ };
2266
+ var dragHandleDisabledStyle = {
2267
+ cursor: "not-allowed",
2268
+ opacity: 0.3
2269
+ };
2270
+ function SortableList({
2271
+ items,
2272
+ disabled,
2273
+ onReorder,
2274
+ renderItem,
2275
+ renderOverlay
2276
+ }) {
2277
+ const [activeId, setActiveId] = react.useState(null);
2278
+ const sensors = core$1.useSensors(
2279
+ core$1.useSensor(core$1.PointerSensor, { activationConstraint: { distance: 8 } }),
2280
+ core$1.useSensor(core$1.TouchSensor, { activationConstraint: { delay: 150, tolerance: 5 } }),
2281
+ core$1.useSensor(core$1.KeyboardSensor)
2282
+ );
2283
+ const handleDragStart = react.useCallback((event) => {
2284
+ setActiveId(String(event.active.id));
2285
+ }, []);
2286
+ const handleDragEnd = react.useCallback(
2287
+ (event) => {
2288
+ setActiveId(null);
2289
+ const { active, over } = event;
2290
+ if (!over || active.id === over.id) return;
2291
+ const fromIndex = items.indexOf(String(active.id));
2292
+ const toIndex = items.indexOf(String(over.id));
2293
+ if (fromIndex === -1 || toIndex === -1) return;
2294
+ onReorder(fromIndex, toIndex);
2295
+ },
2296
+ [items, onReorder]
2297
+ );
2298
+ const handleDragCancel = react.useCallback(() => {
2299
+ setActiveId(null);
2300
+ }, []);
2301
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2302
+ core$1.DndContext,
2303
+ {
2304
+ sensors,
2305
+ collisionDetection: core$1.closestCenter,
2306
+ onDragStart: handleDragStart,
2307
+ onDragEnd: handleDragEnd,
2308
+ onDragCancel: handleDragCancel,
2309
+ children: [
2310
+ /* @__PURE__ */ jsxRuntime.jsx(sortable.SortableContext, { items, strategy: sortable.verticalListSortingStrategy, disabled, children: items.map((id, index) => renderItem(id, index)) }),
2311
+ /* @__PURE__ */ jsxRuntime.jsx(core$1.DragOverlay, { dropAnimation: null, children: activeId && renderOverlay ? renderOverlay(activeId) : null })
2312
+ ]
2313
+ }
2314
+ );
2315
+ }
1992
2316
  var FIELD_TYPES = [
1993
2317
  "text",
1994
2318
  "number",
@@ -2042,6 +2366,16 @@ function FieldList() {
2042
2366
  setMoveError(null);
2043
2367
  reorderFields(step.id, fromIndex, toIndex);
2044
2368
  };
2369
+ const fieldIds = step.fields.map((f) => f.id);
2370
+ const renderOverlay = (id) => {
2371
+ const field = step.fields.find((f) => f.id === id);
2372
+ if (!field) return null;
2373
+ const ct = appCustomTypes.find((c) => c.id === field.type);
2374
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { ...styles6.card, ...styles6.cardDragOverlay }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles6.cardTop, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.cardLeft, children: [
2375
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { ...styles6.typeBadge, ...ct ? styles6.typeBadgeCustom : {} }, children: ct ? `${ct.icon ? ct.icon + " " : ""}${ct.label}` : field.type }),
2376
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles6.fieldLabel, children: field.label })
2377
+ ] }) }) });
2378
+ };
2045
2379
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.container, children: [
2046
2380
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.header, children: [
2047
2381
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
@@ -2061,137 +2395,147 @@ function FieldList() {
2061
2395
  ] }),
2062
2396
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.list, children: [
2063
2397
  step.fields.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles6.emptyFields, children: 'No fields yet. Click "Add field" to start.' }),
2064
- step.fields.map((field, index) => {
2065
- const isSelected = field.id === selectedFieldId;
2066
- const isRequired = field.validation?.some((v) => v.type === "required") ?? false;
2067
- const hasCondition = !!field.visibleWhen;
2068
- const hasDeps = (field.dependsOn?.length ?? 0) > 0;
2069
- const enumDef = field.externalEnumId ? externalEnums.find((e) => e.id === field.externalEnumId) : void 0;
2070
- const isUsedAsDep = allDependencyTargets.has(`${step.id}.${field.id}`);
2071
- const canMoveUp = index > 0 && isFieldMoveValid(step.fields, step.id, index, index - 1).valid;
2072
- const canMoveDown = index < step.fields.length - 1 && isFieldMoveValid(step.fields, step.id, index, index + 1).valid;
2073
- const intraStepDeps = (field.dependsOn ?? []).filter((p) => p.startsWith(`${step.id}.`)).map((p) => {
2074
- const fieldId = p.slice(step.id.length + 1);
2075
- return step.fields.find((f) => f.id === fieldId)?.label ?? fieldId;
2076
- });
2077
- const intraStepDependents = step.fields.filter(
2078
- (f) => f.id !== field.id && (f.dependsOn ?? []).includes(`${step.id}.${field.id}`)
2079
- );
2080
- return /* @__PURE__ */ jsxRuntime.jsxs(
2081
- "div",
2082
- {
2083
- style: { ...styles6.card, ...isSelected ? styles6.cardSelected : {} },
2084
- onClick: () => selectField(field.id),
2085
- children: [
2086
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.cardTop, children: [
2087
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.cardLeft, children: [
2088
- (() => {
2089
- const ct = appCustomTypes.find((c) => c.id === field.type);
2090
- return /* @__PURE__ */ jsxRuntime.jsx("span", { style: { ...styles6.typeBadge, ...ct ? styles6.typeBadgeCustom : {} }, children: ct ? `${ct.icon ? ct.icon + " " : ""}${ct.label}` : field.type });
2091
- })(),
2092
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles6.fieldLabel, children: field.label })
2093
- ] }),
2094
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.cardRight, children: [
2095
- !readOnly && /* @__PURE__ */ jsxRuntime.jsxs(
2096
- "select",
2097
- {
2098
- style: styles6.typeSelect,
2099
- value: field.type,
2100
- onClick: (e) => e.stopPropagation(),
2101
- onChange: (e) => {
2102
- const newType = e.target.value;
2103
- const customType = appCustomTypes.find((ct) => ct.id === newType);
2104
- updateField(step.id, field.id, {
2105
- type: newType,
2106
- ...customType?.defaultValidation ? { validation: customType.defaultValidation } : {}
2107
- });
2108
- },
2109
- children: [
2110
- FIELD_TYPES.map((t) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: t, children: t }, t)),
2111
- appCustomTypes.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("optgroup", { label: "Custom", children: appCustomTypes.map((ct) => /* @__PURE__ */ jsxRuntime.jsxs("option", { value: ct.id, children: [
2112
- ct.icon ? `${ct.icon} ` : "",
2113
- ct.label
2114
- ] }, ct.id)) })
2115
- ]
2116
- }
2117
- ),
2118
- !readOnly && index > 0 && /* @__PURE__ */ jsxRuntime.jsx(
2119
- "button",
2120
- {
2121
- style: { ...styles6.iconBtn, ...canMoveUp ? {} : styles6.iconBtnBlocked },
2122
- title: canMoveUp ? "Move up" : "Can't move \u2014 dependency order required",
2123
- onClick: (e) => {
2124
- e.stopPropagation();
2125
- tryMove(index, index - 1);
2126
- },
2127
- children: "\u2191"
2128
- }
2129
- ),
2130
- !readOnly && index < step.fields.length - 1 && /* @__PURE__ */ jsxRuntime.jsx(
2131
- "button",
2132
- {
2133
- style: { ...styles6.iconBtn, ...canMoveDown ? {} : styles6.iconBtnBlocked },
2134
- title: canMoveDown ? "Move down" : "Can't move \u2014 dependency order required",
2135
- onClick: (e) => {
2136
- e.stopPropagation();
2137
- tryMove(index, index + 1);
2138
- },
2139
- children: "\u2193"
2140
- }
2141
- ),
2142
- !readOnly && /* @__PURE__ */ jsxRuntime.jsx(
2143
- "button",
2144
- {
2145
- style: styles6.iconBtn,
2146
- title: "Duplicate field",
2147
- onClick: (e) => {
2148
- e.stopPropagation();
2149
- duplicateField(step.id, field.id);
2150
- },
2151
- children: "\u29C9"
2152
- }
2153
- ),
2154
- !readOnly && /* @__PURE__ */ jsxRuntime.jsx(
2155
- "button",
2156
- {
2157
- style: { ...styles6.iconBtn, color: "var(--wp-danger)" },
2158
- title: "Remove field",
2159
- onClick: (e) => {
2160
- e.stopPropagation();
2161
- removeField(step.id, field.id);
2162
- },
2163
- children: "\u2715"
2164
- }
2165
- )
2166
- ] })
2167
- ] }),
2168
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.badges, children: [
2169
- !isRequired && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles6.badgeOptional, children: "optional" }),
2170
- isRequired && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles6.badgeRequired, children: "required" }),
2171
- hasCondition && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles6.badgeCondition, children: "conditional" }),
2172
- hasDeps && /* @__PURE__ */ jsxRuntime.jsxs("span", { style: styles6.badgeDep, children: [
2173
- "depends on ",
2174
- field.dependsOn.length
2175
- ] }),
2176
- isUsedAsDep && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles6.badgeUsed, children: "\u2190 dependency" }),
2177
- field.externalEnumId && /* @__PURE__ */ jsxRuntime.jsxs("span", { style: styles6.badgeEnum, children: [
2178
- "\u229E ",
2179
- enumDef ? enumDef.label : field.externalEnumId
2180
- ] })
2181
- ] }),
2182
- intraStepDeps.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.depRow, children: [
2183
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles6.depLabel, children: "needs:" }),
2184
- intraStepDeps.map((label) => /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles6.depBadge, children: label }, label))
2185
- ] }),
2186
- intraStepDependents.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.depRow, children: [
2187
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles6.depLabelUsed, children: "used by:" }),
2188
- intraStepDependents.map((f) => /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles6.depBadgeUsed, children: f.label }, f.id))
2189
- ] })
2190
- ]
2191
- },
2192
- field.id
2193
- );
2194
- })
2398
+ /* @__PURE__ */ jsxRuntime.jsx(
2399
+ SortableList,
2400
+ {
2401
+ items: fieldIds,
2402
+ disabled: readOnly,
2403
+ onReorder: tryMove,
2404
+ renderOverlay,
2405
+ renderItem: (id, index) => {
2406
+ const field = step.fields[index];
2407
+ const isSelected = field.id === selectedFieldId;
2408
+ const isRequired = field.validation?.some((v) => v.type === "required") ?? false;
2409
+ const hasCondition = !!field.visibleWhen;
2410
+ const hasDeps = (field.dependsOn?.length ?? 0) > 0;
2411
+ const enumDef = field.externalEnumId ? externalEnums.find((e) => e.id === field.externalEnumId) : void 0;
2412
+ const isUsedAsDep = allDependencyTargets.has(`${step.id}.${field.id}`);
2413
+ const canMoveUp = index > 0 && isFieldMoveValid(step.fields, step.id, index, index - 1).valid;
2414
+ const canMoveDown = index < step.fields.length - 1 && isFieldMoveValid(step.fields, step.id, index, index + 1).valid;
2415
+ const intraStepDeps = (field.dependsOn ?? []).filter((p) => p.startsWith(`${step.id}.`)).map((p) => {
2416
+ const fieldId = p.slice(step.id.length + 1);
2417
+ return step.fields.find((f) => f.id === fieldId)?.label ?? fieldId;
2418
+ });
2419
+ const intraStepDependents = step.fields.filter(
2420
+ (f) => f.id !== field.id && (f.dependsOn ?? []).includes(`${step.id}.${field.id}`)
2421
+ );
2422
+ return /* @__PURE__ */ jsxRuntime.jsx(SortableItem, { id: field.id, disabled: readOnly, children: ({ handleProps }) => /* @__PURE__ */ jsxRuntime.jsxs(
2423
+ "div",
2424
+ {
2425
+ style: { ...styles6.card, ...isSelected ? styles6.cardSelected : {} },
2426
+ onClick: () => selectField(field.id),
2427
+ children: [
2428
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.cardTop, children: [
2429
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.cardLeft, children: [
2430
+ !readOnly && /* @__PURE__ */ jsxRuntime.jsx(DragHandle, { handleProps }),
2431
+ (() => {
2432
+ const ct = appCustomTypes.find((c) => c.id === field.type);
2433
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { style: { ...styles6.typeBadge, ...ct ? styles6.typeBadgeCustom : {} }, children: ct ? `${ct.icon ? ct.icon + " " : ""}${ct.label}` : field.type });
2434
+ })(),
2435
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles6.fieldLabel, children: field.label })
2436
+ ] }),
2437
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.cardRight, children: [
2438
+ !readOnly && /* @__PURE__ */ jsxRuntime.jsxs(
2439
+ "select",
2440
+ {
2441
+ style: styles6.typeSelect,
2442
+ value: field.type,
2443
+ onClick: (e) => e.stopPropagation(),
2444
+ onChange: (e) => {
2445
+ const newType = e.target.value;
2446
+ const customType = appCustomTypes.find((ct) => ct.id === newType);
2447
+ updateField(step.id, field.id, {
2448
+ type: newType,
2449
+ ...customType?.defaultValidation ? { validation: customType.defaultValidation } : {}
2450
+ });
2451
+ },
2452
+ children: [
2453
+ FIELD_TYPES.map((t) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: t, children: t }, t)),
2454
+ appCustomTypes.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("optgroup", { label: "Custom", children: appCustomTypes.map((ct) => /* @__PURE__ */ jsxRuntime.jsxs("option", { value: ct.id, children: [
2455
+ ct.icon ? `${ct.icon} ` : "",
2456
+ ct.label
2457
+ ] }, ct.id)) })
2458
+ ]
2459
+ }
2460
+ ),
2461
+ !readOnly && index > 0 && /* @__PURE__ */ jsxRuntime.jsx(
2462
+ "button",
2463
+ {
2464
+ style: { ...styles6.iconBtn, ...canMoveUp ? {} : styles6.iconBtnBlocked },
2465
+ title: canMoveUp ? "Move up" : "Can't move \u2014 dependency order required",
2466
+ onClick: (e) => {
2467
+ e.stopPropagation();
2468
+ tryMove(index, index - 1);
2469
+ },
2470
+ children: "\u2191"
2471
+ }
2472
+ ),
2473
+ !readOnly && index < step.fields.length - 1 && /* @__PURE__ */ jsxRuntime.jsx(
2474
+ "button",
2475
+ {
2476
+ style: { ...styles6.iconBtn, ...canMoveDown ? {} : styles6.iconBtnBlocked },
2477
+ title: canMoveDown ? "Move down" : "Can't move \u2014 dependency order required",
2478
+ onClick: (e) => {
2479
+ e.stopPropagation();
2480
+ tryMove(index, index + 1);
2481
+ },
2482
+ children: "\u2193"
2483
+ }
2484
+ ),
2485
+ !readOnly && /* @__PURE__ */ jsxRuntime.jsx(
2486
+ "button",
2487
+ {
2488
+ style: styles6.iconBtn,
2489
+ title: "Duplicate field",
2490
+ onClick: (e) => {
2491
+ e.stopPropagation();
2492
+ duplicateField(step.id, field.id);
2493
+ },
2494
+ children: "\u29C9"
2495
+ }
2496
+ ),
2497
+ !readOnly && /* @__PURE__ */ jsxRuntime.jsx(
2498
+ "button",
2499
+ {
2500
+ style: { ...styles6.iconBtn, color: "var(--wp-danger)" },
2501
+ title: "Remove field",
2502
+ onClick: (e) => {
2503
+ e.stopPropagation();
2504
+ removeField(step.id, field.id);
2505
+ },
2506
+ children: "\u2715"
2507
+ }
2508
+ )
2509
+ ] })
2510
+ ] }),
2511
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.badges, children: [
2512
+ !isRequired && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles6.badgeOptional, children: "optional" }),
2513
+ isRequired && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles6.badgeRequired, children: "required" }),
2514
+ hasCondition && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles6.badgeCondition, children: "conditional" }),
2515
+ hasDeps && /* @__PURE__ */ jsxRuntime.jsxs("span", { style: styles6.badgeDep, children: [
2516
+ "depends on ",
2517
+ field.dependsOn.length
2518
+ ] }),
2519
+ isUsedAsDep && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles6.badgeUsed, children: "\u2190 dependency" }),
2520
+ field.externalEnumId && /* @__PURE__ */ jsxRuntime.jsxs("span", { style: styles6.badgeEnum, children: [
2521
+ "\u229E ",
2522
+ enumDef ? enumDef.label : field.externalEnumId
2523
+ ] })
2524
+ ] }),
2525
+ intraStepDeps.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.depRow, children: [
2526
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles6.depLabel, children: "needs:" }),
2527
+ intraStepDeps.map((label) => /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles6.depBadge, children: label }, label))
2528
+ ] }),
2529
+ intraStepDependents.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.depRow, children: [
2530
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles6.depLabelUsed, children: "used by:" }),
2531
+ intraStepDependents.map((f) => /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles6.depBadgeUsed, children: f.label }, f.id))
2532
+ ] })
2533
+ ]
2534
+ }
2535
+ ) }, field.id);
2536
+ }
2537
+ }
2538
+ )
2195
2539
  ] })
2196
2540
  ] });
2197
2541
  }
@@ -2252,6 +2596,12 @@ var styles6 = {
2252
2596
  gap: 6
2253
2597
  },
2254
2598
  cardSelected: { background: "var(--wp-primary-muted)", border: "1px solid var(--wp-primary-border)" },
2599
+ cardDragOverlay: {
2600
+ boxShadow: "0 4px 16px rgba(0,0,0,0.25)",
2601
+ border: "1px solid var(--wp-primary-border)",
2602
+ background: "var(--wp-surface)",
2603
+ opacity: 0.95
2604
+ },
2255
2605
  cardTop: { display: "flex", alignItems: "center", justifyContent: "space-between" },
2256
2606
  cardLeft: { display: "flex", alignItems: "center", gap: 8, minWidth: 0 },
2257
2607
  cardRight: { display: "flex", alignItems: "center", gap: 4, flexShrink: 0 },
@@ -2411,6 +2761,9 @@ function PreviewPanel({ store, schema, externalEnums }) {
2411
2761
  setErrors({});
2412
2762
  const oldIds = tree.steps.map((s) => s.definition.id).join(",");
2413
2763
  store.getState().setStepData(stepId, stepData);
2764
+ if ((store.getState().skippedSteps ?? []).includes(stepId)) {
2765
+ store.getState().unskipStep(stepId);
2766
+ }
2414
2767
  const newData = store.getState().data;
2415
2768
  const newTree = core.resolveTree(schema, newData, store.getState().externalVars, externalEnums);
2416
2769
  const newIds = newTree.steps.map((s) => s.definition.id).join(",");
@@ -2447,7 +2800,8 @@ function PreviewPanel({ store, schema, externalEnums }) {
2447
2800
  {
2448
2801
  tree,
2449
2802
  currentIdx,
2450
- onSelect: (id) => store.getState().setCurrentStep(id)
2803
+ onSelect: (id) => store.getState().setCurrentStep(id),
2804
+ skippedStepIds: store.getState().skippedSteps
2451
2805
  }
2452
2806
  ),
2453
2807
  extVarDefs.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
@@ -2492,33 +2846,57 @@ function PreviewPanel({ store, schema, externalEnums }) {
2492
2846
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles7.rightCol, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.stepRenderer, children: [
2493
2847
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles7.progressTrack, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { ...styles7.progressFill, width: `${progress}%` } }) }),
2494
2848
  /* @__PURE__ */ jsxRuntime.jsx("h2", { style: styles7.stepTitle, children: currentStep?.definition.title ?? "" }),
2495
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles7.fieldsContainer, children: fields.map((field) => /* @__PURE__ */ jsxRuntime.jsx(
2496
- FieldRenderer,
2497
- {
2498
- field,
2499
- value: stepData[field.definition.id],
2500
- error: errors[field.definition.id],
2501
- onChange: (val) => {
2502
- setFieldValue(field.definition.id, val);
2503
- if (errors[field.definition.id]) {
2504
- setErrors((prev) => {
2505
- const next = { ...prev };
2506
- delete next[field.definition.id];
2507
- return next;
2508
- });
2849
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles7.fieldsContainer, children: fields.map((field) => {
2850
+ const storedVal = stepData[field.definition.id];
2851
+ const displayVal = storedVal !== void 0 && storedVal !== null && storedVal !== "" ? storedVal : field.resolvedDefaultValue ?? field.definition.defaultValue ?? void 0;
2852
+ return /* @__PURE__ */ jsxRuntime.jsx(
2853
+ FieldRenderer,
2854
+ {
2855
+ field,
2856
+ value: displayVal,
2857
+ error: errors[field.definition.id],
2858
+ onChange: (val) => {
2859
+ setFieldValue(field.definition.id, val);
2860
+ if (errors[field.definition.id]) {
2861
+ setErrors((prev) => {
2862
+ const next = { ...prev };
2863
+ delete next[field.definition.id];
2864
+ return next;
2865
+ });
2866
+ }
2509
2867
  }
2510
- }
2511
- },
2512
- field.definition.id
2513
- )) }),
2868
+ },
2869
+ field.definition.id
2870
+ );
2871
+ }) }),
2514
2872
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.navRow, children: [
2515
2873
  currentIdx > 0 && /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles7.secondaryBtn, onClick: handlePrev, children: "\u2190 Pr\xE9c\xE9dent" }),
2516
- /* @__PURE__ */ jsxRuntime.jsx("button", { style: { ...styles7.primaryBtn, marginLeft: "auto" }, onClick: handleNext, children: core.getNextStep(tree.steps, stepId) ? "Continuer \u2192" : "Terminer \u2713" })
2874
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: 8, marginLeft: "auto" }, children: [
2875
+ currentStep?.definition.skippable && /* @__PURE__ */ jsxRuntime.jsx(
2876
+ "button",
2877
+ {
2878
+ style: styles7.secondaryBtn,
2879
+ onClick: () => {
2880
+ setErrors({});
2881
+ store.getState().skipStep(stepId);
2882
+ const next = core.getNextStep(tree.steps, stepId);
2883
+ if (next) {
2884
+ store.getState().setCurrentStep(next.definition.id);
2885
+ } else {
2886
+ setDone(true);
2887
+ }
2888
+ },
2889
+ children: "Passer"
2890
+ }
2891
+ ),
2892
+ /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles7.primaryBtn, onClick: handleNext, children: core.getNextStep(tree.steps, stepId) ? "Continuer \u2192" : "Terminer \u2713" })
2893
+ ] })
2517
2894
  ] })
2518
2895
  ] }) })
2519
2896
  ] });
2520
2897
  }
2521
- function StepList({ tree, currentIdx, onSelect }) {
2898
+ function StepList({ tree, currentIdx, onSelect, skippedStepIds }) {
2899
+ const skipped = new Set(skippedStepIds ?? []);
2522
2900
  const allSteps = [
2523
2901
  ...tree.steps.map((s) => ({ ...s, hidden: false })),
2524
2902
  ...tree.hiddenSteps.map((s) => ({ ...s, hidden: true }))
@@ -2526,11 +2904,13 @@ function StepList({ tree, currentIdx, onSelect }) {
2526
2904
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.stepList, children: [
2527
2905
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles7.stepListTitle, children: "\xC9tapes" }),
2528
2906
  allSteps.map((step) => {
2529
- const isVisible = !step.hidden;
2907
+ const isStepVisible = !step.hidden;
2530
2908
  const visIdx = tree.steps.findIndex((s) => s.definition.id === step.definition.id);
2909
+ const isSkipped = skipped.has(step.definition.id);
2531
2910
  let status = "hidden";
2532
- if (isVisible) {
2533
- if (visIdx < currentIdx) status = "done";
2911
+ if (isStepVisible) {
2912
+ if (isSkipped && visIdx < currentIdx) status = "skipped";
2913
+ else if (visIdx < currentIdx) status = "done";
2534
2914
  else if (visIdx === currentIdx) status = "current";
2535
2915
  else status = "upcoming";
2536
2916
  }
@@ -2541,20 +2921,23 @@ function StepList({ tree, currentIdx, onSelect }) {
2541
2921
  ...styles7.stepItem,
2542
2922
  ...status === "current" ? styles7.stepItemCurrent : {},
2543
2923
  ...status === "hidden" ? styles7.stepItemHidden : {},
2544
- cursor: status === "done" ? "pointer" : "default"
2924
+ ...status === "skipped" ? { opacity: 0.6 } : {},
2925
+ cursor: status === "done" || status === "skipped" ? "pointer" : "default"
2545
2926
  },
2546
2927
  onClick: () => {
2547
- if (status === "done") onSelect(step.definition.id);
2928
+ if (status === "done" || status === "skipped") onSelect(step.definition.id);
2548
2929
  },
2549
2930
  children: [
2550
2931
  /* @__PURE__ */ jsxRuntime.jsxs("span", { style: styles7.stepStatus, children: [
2551
2932
  status === "done" && "\u2713",
2933
+ status === "skipped" && "\u23ED",
2552
2934
  status === "current" && "\u2192",
2553
2935
  status === "upcoming" && "\u25CB",
2554
2936
  status === "hidden" && "\u2013"
2555
2937
  ] }),
2556
2938
  /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles7.stepName, children: step.definition.title }),
2557
- status === "hidden" && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles7.hiddenBadge, children: "hidden" })
2939
+ status === "hidden" && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles7.hiddenBadge, children: "hidden" }),
2940
+ status === "skipped" && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { ...styles7.hiddenBadge, color: "var(--wp-warning)", background: "var(--wp-warning-bg)" }, children: "skipped" })
2558
2941
  ]
2559
2942
  },
2560
2943
  step.definition.id
@@ -3019,6 +3402,19 @@ function StepEditor() {
3019
3402
  ),
3020
3403
  /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: `resume-${step.id}`, style: styles8.checkLabel, children: "Resume from this step" })
3021
3404
  ] }),
3405
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.checkRow, children: [
3406
+ /* @__PURE__ */ jsxRuntime.jsx(
3407
+ "input",
3408
+ {
3409
+ type: "checkbox",
3410
+ id: `skippable-${step.id}`,
3411
+ checked: !!step.skippable,
3412
+ disabled: readOnly,
3413
+ onChange: readOnly ? void 0 : (e) => updateStep(step.id, { skippable: e.target.checked || void 0 })
3414
+ }
3415
+ ),
3416
+ /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: `skippable-${step.id}`, style: styles8.checkLabel, children: "Skippable (user can bypass this step)" })
3417
+ ] }),
3022
3418
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles8.divider }),
3023
3419
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.conditionRow, children: [
3024
3420
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.conditionInfo, children: [
@@ -3176,15 +3572,28 @@ function StepList2() {
3176
3572
  const [moveError, setMoveError] = react.useState(null);
3177
3573
  const steps = schema.steps;
3178
3574
  const deps = computeStepDependencies(schema);
3179
- const tryMove = (fromIndex, toIndex) => {
3180
- const check = isMoveValid(steps, deps, fromIndex, toIndex);
3181
- if (!check.valid) {
3182
- setMoveError(check.reason ?? "Invalid move");
3183
- setTimeout(() => setMoveError(null), 3e3);
3184
- return;
3185
- }
3186
- setMoveError(null);
3187
- reorderSteps(fromIndex, toIndex);
3575
+ const tryMove = react.useCallback(
3576
+ (fromIndex, toIndex) => {
3577
+ const check = isMoveValid(steps, deps, fromIndex, toIndex);
3578
+ if (!check.valid) {
3579
+ setMoveError(check.reason ?? "Invalid move");
3580
+ setTimeout(() => setMoveError(null), 3e3);
3581
+ return;
3582
+ }
3583
+ setMoveError(null);
3584
+ reorderSteps(fromIndex, toIndex);
3585
+ },
3586
+ [steps, deps, reorderSteps]
3587
+ );
3588
+ const stepIds = steps.map((s) => s.id);
3589
+ const renderOverlay = (id) => {
3590
+ const step = steps.find((s) => s.id === id);
3591
+ if (!step) return null;
3592
+ const index = steps.indexOf(step);
3593
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { ...styles9.card, ...styles9.cardDragOverlay }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles9.cardMain, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.cardLeft, children: [
3594
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles9.cardIndex, children: index + 1 }),
3595
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles9.cardTitle, children: step.title })
3596
+ ] }) }) });
3188
3597
  };
3189
3598
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.container, children: [
3190
3599
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.header, children: [
@@ -3201,107 +3610,117 @@ function StepList2() {
3201
3610
  ] }),
3202
3611
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.list, children: [
3203
3612
  steps.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles9.empty, children: 'No steps yet. Click "Add step" to start.' }),
3204
- steps.map((step, index) => {
3205
- const isSelected = step.id === selectedStepId;
3206
- const hasCondition = !!step.visibleWhen;
3207
- const depLabels = getStepDependencyLabels(step.id, deps, schema);
3208
- const canMoveUp = index > 0 && isMoveValid(steps, deps, index, index - 1).valid;
3209
- const canMoveDown = index < steps.length - 1 && isMoveValid(steps, deps, index, index + 1).valid;
3210
- const dependents = steps.filter(
3211
- (s) => (deps.get(s.id) ?? /* @__PURE__ */ new Set()).has(step.id)
3212
- );
3213
- return /* @__PURE__ */ jsxRuntime.jsxs(
3214
- "div",
3215
- {
3216
- style: {
3217
- ...styles9.card,
3218
- ...isSelected ? styles9.cardSelected : {}
3219
- },
3220
- onClick: () => selectStep(step.id),
3221
- children: [
3222
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.cardMain, children: [
3223
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.cardLeft, children: [
3224
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles9.cardIndex, children: index + 1 }),
3225
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { minWidth: 0 }, children: [
3226
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles9.cardTitle, children: step.title }),
3227
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.cardMeta, children: [
3228
- step.fields.length,
3229
- " field",
3230
- step.fields.length !== 1 ? "s" : "",
3231
- hasCondition && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles9.conditionBadge, children: "conditional" })
3613
+ /* @__PURE__ */ jsxRuntime.jsx(
3614
+ SortableList,
3615
+ {
3616
+ items: stepIds,
3617
+ disabled: readOnly,
3618
+ onReorder: tryMove,
3619
+ renderOverlay,
3620
+ renderItem: (id, index) => {
3621
+ const step = steps[index];
3622
+ const isSelected = step.id === selectedStepId;
3623
+ const hasCondition = !!step.visibleWhen;
3624
+ const depLabels = getStepDependencyLabels(step.id, deps, schema);
3625
+ const canMoveUp = index > 0 && isMoveValid(steps, deps, index, index - 1).valid;
3626
+ const canMoveDown = index < steps.length - 1 && isMoveValid(steps, deps, index, index + 1).valid;
3627
+ const dependents = steps.filter(
3628
+ (s) => (deps.get(s.id) ?? /* @__PURE__ */ new Set()).has(step.id)
3629
+ );
3630
+ return /* @__PURE__ */ jsxRuntime.jsx(SortableItem, { id: step.id, disabled: readOnly, children: ({ handleProps }) => /* @__PURE__ */ jsxRuntime.jsxs(
3631
+ "div",
3632
+ {
3633
+ style: {
3634
+ ...styles9.card,
3635
+ ...isSelected ? styles9.cardSelected : {}
3636
+ },
3637
+ onClick: () => selectStep(step.id),
3638
+ children: [
3639
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.cardMain, children: [
3640
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.cardLeft, children: [
3641
+ !readOnly && /* @__PURE__ */ jsxRuntime.jsx(DragHandle, { handleProps }),
3642
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles9.cardIndex, children: index + 1 }),
3643
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { minWidth: 0 }, children: [
3644
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles9.cardTitle, children: step.title }),
3645
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.cardMeta, children: [
3646
+ step.fields.length,
3647
+ " field",
3648
+ step.fields.length !== 1 ? "s" : "",
3649
+ hasCondition && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles9.conditionBadge, children: "conditional" })
3650
+ ] })
3651
+ ] })
3652
+ ] }),
3653
+ !readOnly && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.cardActions, children: [
3654
+ index > 0 && /* @__PURE__ */ jsxRuntime.jsx(
3655
+ "button",
3656
+ {
3657
+ style: {
3658
+ ...styles9.iconBtn,
3659
+ ...canMoveUp ? {} : styles9.iconBtnBlocked
3660
+ },
3661
+ title: canMoveUp ? "Move up" : `Can't move up \u2014 dependency order required`,
3662
+ onClick: (e) => {
3663
+ e.stopPropagation();
3664
+ tryMove(index, index - 1);
3665
+ },
3666
+ children: "\u2191"
3667
+ }
3668
+ ),
3669
+ index < steps.length - 1 && /* @__PURE__ */ jsxRuntime.jsx(
3670
+ "button",
3671
+ {
3672
+ style: {
3673
+ ...styles9.iconBtn,
3674
+ ...canMoveDown ? {} : styles9.iconBtnBlocked
3675
+ },
3676
+ title: canMoveDown ? "Move down" : `Can't move down \u2014 dependency order required`,
3677
+ onClick: (e) => {
3678
+ e.stopPropagation();
3679
+ tryMove(index, index + 1);
3680
+ },
3681
+ children: "\u2193"
3682
+ }
3683
+ ),
3684
+ /* @__PURE__ */ jsxRuntime.jsx(
3685
+ "button",
3686
+ {
3687
+ style: styles9.iconBtn,
3688
+ title: "Duplicate step",
3689
+ onClick: (e) => {
3690
+ e.stopPropagation();
3691
+ duplicateStep(step.id);
3692
+ },
3693
+ children: "\u29C9"
3694
+ }
3695
+ ),
3696
+ /* @__PURE__ */ jsxRuntime.jsx(
3697
+ "button",
3698
+ {
3699
+ style: { ...styles9.iconBtn, ...styles9.deleteBtn },
3700
+ title: "Remove step",
3701
+ onClick: (e) => {
3702
+ e.stopPropagation();
3703
+ removeStep(step.id);
3704
+ },
3705
+ children: "\u2715"
3706
+ }
3707
+ )
3232
3708
  ] })
3709
+ ] }),
3710
+ depLabels.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.depRow, children: [
3711
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles9.depLabel, children: "needs:" }),
3712
+ depLabels.map((label) => /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles9.depBadge, children: label }, label))
3713
+ ] }),
3714
+ dependents.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.depRow, children: [
3715
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles9.depLabelUsed, children: "used by:" }),
3716
+ dependents.map((s) => /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles9.depBadgeUsed, children: s.title }, s.id))
3233
3717
  ] })
3234
- ] }),
3235
- !readOnly && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.cardActions, children: [
3236
- index > 0 && /* @__PURE__ */ jsxRuntime.jsx(
3237
- "button",
3238
- {
3239
- style: {
3240
- ...styles9.iconBtn,
3241
- ...canMoveUp ? {} : styles9.iconBtnBlocked
3242
- },
3243
- title: canMoveUp ? "Move up" : `Can't move up \u2014 dependency order required`,
3244
- onClick: (e) => {
3245
- e.stopPropagation();
3246
- tryMove(index, index - 1);
3247
- },
3248
- children: "\u2191"
3249
- }
3250
- ),
3251
- index < steps.length - 1 && /* @__PURE__ */ jsxRuntime.jsx(
3252
- "button",
3253
- {
3254
- style: {
3255
- ...styles9.iconBtn,
3256
- ...canMoveDown ? {} : styles9.iconBtnBlocked
3257
- },
3258
- title: canMoveDown ? "Move down" : `Can't move down \u2014 dependency order required`,
3259
- onClick: (e) => {
3260
- e.stopPropagation();
3261
- tryMove(index, index + 1);
3262
- },
3263
- children: "\u2193"
3264
- }
3265
- ),
3266
- /* @__PURE__ */ jsxRuntime.jsx(
3267
- "button",
3268
- {
3269
- style: styles9.iconBtn,
3270
- title: "Duplicate step",
3271
- onClick: (e) => {
3272
- e.stopPropagation();
3273
- duplicateStep(step.id);
3274
- },
3275
- children: "\u29C9"
3276
- }
3277
- ),
3278
- /* @__PURE__ */ jsxRuntime.jsx(
3279
- "button",
3280
- {
3281
- style: { ...styles9.iconBtn, ...styles9.deleteBtn },
3282
- title: "Remove step",
3283
- onClick: (e) => {
3284
- e.stopPropagation();
3285
- removeStep(step.id);
3286
- },
3287
- children: "\u2715"
3288
- }
3289
- )
3290
- ] })
3291
- ] }),
3292
- depLabels.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.depRow, children: [
3293
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles9.depLabel, children: "needs:" }),
3294
- depLabels.map((label) => /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles9.depBadge, children: label }, label))
3295
- ] }),
3296
- dependents.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.depRow, children: [
3297
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles9.depLabelUsed, children: "used by:" }),
3298
- dependents.map((s) => /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles9.depBadgeUsed, children: s.title }, s.id))
3299
- ] })
3300
- ]
3301
- },
3302
- step.id
3303
- );
3304
- })
3718
+ ]
3719
+ }
3720
+ ) }, step.id);
3721
+ }
3722
+ }
3723
+ )
3305
3724
  ] })
3306
3725
  ] });
3307
3726
  }
@@ -3353,6 +3772,12 @@ var styles9 = {
3353
3772
  gap: 6
3354
3773
  },
3355
3774
  cardSelected: { background: "var(--wp-primary-muted)", border: "1px solid var(--wp-primary-border)" },
3775
+ cardDragOverlay: {
3776
+ boxShadow: "0 4px 16px rgba(0,0,0,0.25)",
3777
+ border: "1px solid var(--wp-primary-border)",
3778
+ background: "var(--wp-surface)",
3779
+ opacity: 0.95
3780
+ },
3356
3781
  cardMain: { display: "flex", alignItems: "center", justifyContent: "space-between" },
3357
3782
  cardLeft: { display: "flex", alignItems: "center", gap: 10, minWidth: 0, flex: 1 },
3358
3783
  cardIndex: {