@ohhwells/bridge 0.1.65-next.182 → 0.1.65-next.184

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
@@ -7018,6 +7018,7 @@ var FIELD_TYPES = [
7018
7018
  ];
7019
7019
  var FIELD_ATTR = "data-ohw-form-field";
7020
7020
  var FIELD_TYPE_ATTR = "data-ohw-field-type";
7021
+ var PLACEHOLDER_EDIT_ATTR = "data-ohw-placeholder-edit";
7021
7022
  function fieldsKey(formKey) {
7022
7023
  return `${formKey}-fields`;
7023
7024
  }
@@ -7055,7 +7056,7 @@ function ensureFieldLabel(wrapper, key) {
7055
7056
  label.style.lineHeight = "1.3";
7056
7057
  const fromPlaceholder = input?.getAttribute("placeholder")?.trim();
7057
7058
  const text = fromPlaceholder && fromPlaceholder.length < 40 ? fromPlaceholder : key.replace(/[-_]/g, " ");
7058
- label.textContent = input?.hasAttribute("required") ? `${text} *` : text;
7059
+ label.textContent = text;
7059
7060
  if (input && input.parentElement === wrapper) wrapper.insertBefore(label, input);
7060
7061
  else wrapper.insertBefore(label, wrapper.firstChild);
7061
7062
  return label;
@@ -7111,8 +7112,11 @@ function readFieldsFromDom(form) {
7111
7112
  return {
7112
7113
  key: fieldKeyOf(wrapper),
7113
7114
  type: fieldTypeOf(wrapper),
7114
- label: fieldLabelOf(wrapper)?.textContent?.trim() ?? "",
7115
- placeholder: input?.getAttribute("placeholder") ?? "",
7115
+ label: fieldLabelText(fieldLabelOf(wrapper)).trim(),
7116
+ // While a field is selected its placeholder text lives in the value (see
7117
+ // beginPlaceholderEdit), so reading the attribute would store an empty one — whoever
7118
+ // happens to save at that moment must still record what the owner typed.
7119
+ placeholder: input ? input.hasAttribute(PLACEHOLDER_EDIT_ATTR) ? input.value : input.getAttribute("placeholder") ?? "" : "",
7116
7120
  required: Boolean(input?.hasAttribute("required"))
7117
7121
  };
7118
7122
  });
@@ -7140,7 +7144,7 @@ function applyFieldType(wrapper, type) {
7140
7144
  const followsDefaults = {
7141
7145
  // A label the owner wrote stays; one that still reads as a type name (or as the old
7142
7146
  // placeholder the template shipped) follows the new type.
7143
- label: isDefaultText(label?.textContent ?? "", (d) => d.label) || (label?.textContent ?? "").trim().replace(/\s*\*$/, "") === placeholder.trim()
7147
+ label: isDefaultText(label?.textContent ?? "", (d) => d.label) || fieldLabelText(label).trim() === placeholder.trim()
7144
7148
  };
7145
7149
  const { tag, inputType } = inputTagFor(type);
7146
7150
  wrapper.setAttribute(FIELD_TYPE_ATTR, type);
@@ -7154,8 +7158,7 @@ function applyFieldType(wrapper, type) {
7154
7158
  const applyDefaults = (el) => {
7155
7159
  el.setAttribute("placeholder", defaults.placeholder);
7156
7160
  if (label && followsDefaults.label) {
7157
- const required = (label.textContent ?? "").trim().endsWith("*");
7158
- label.textContent = required ? `${defaults.label} *` : defaults.label;
7161
+ label.textContent = defaults.label;
7159
7162
  }
7160
7163
  };
7161
7164
  if (input.tagName.toLowerCase() === tag) {
@@ -7175,13 +7178,19 @@ function applyFieldType(wrapper, type) {
7175
7178
  input.replaceWith(next);
7176
7179
  applyDefaults(next);
7177
7180
  }
7181
+ var REQUIRED_MARK_ATTR = "data-ohw-field-required";
7182
+ function fieldLabelText(label) {
7183
+ if (!label) return "";
7184
+ return (label.textContent ?? "").replace(/\s*\*\s*$/, "").trimEnd();
7185
+ }
7178
7186
  function syncRequiredMark(wrapper) {
7179
7187
  const label = fieldLabelOf(wrapper);
7180
7188
  if (!label) return;
7181
- const required = isFieldRequired(wrapper);
7182
- const base = (label.textContent ?? "").replace(/\s*\*\s*$/, "").trimEnd();
7183
- const next = required ? `${base} *` : base;
7184
- if (label.textContent !== next) label.textContent = next;
7189
+ label.querySelectorAll("[data-ohw-required-mark]").forEach((el) => el.remove());
7190
+ const words = fieldLabelText(label);
7191
+ if ((label.textContent ?? "") !== words) label.textContent = words;
7192
+ if (isFieldRequired(wrapper)) label.setAttribute(REQUIRED_MARK_ATTR, "");
7193
+ else label.removeAttribute(REQUIRED_MARK_ATTR);
7185
7194
  }
7186
7195
  function setFieldRequired(wrapper, required) {
7187
7196
  const input = fieldInputOf(wrapper);
@@ -7191,7 +7200,6 @@ function setFieldRequired(wrapper, required) {
7191
7200
  else input.removeAttribute("required");
7192
7201
  if (label) syncRequiredMark(wrapper);
7193
7202
  }
7194
- var PLACEHOLDER_EDIT_ATTR = "data-ohw-placeholder-edit";
7195
7203
  function beginPlaceholderEdit(wrapper) {
7196
7204
  const input = fieldInputOf(wrapper);
7197
7205
  if (!input || input.hasAttribute(PLACEHOLDER_EDIT_ATTR)) return;
@@ -7270,14 +7278,25 @@ function duplicateField(form, wrapper) {
7270
7278
  function removeField(wrapper) {
7271
7279
  wrapper.remove();
7272
7280
  }
7273
- function moveField(form, key, toIndex) {
7281
+ function applyFieldOrder(form, keys) {
7274
7282
  const wrappers = listFieldWrappers(form);
7275
- const moving = wrappers.find((wrapper) => fieldKeyOf(wrapper) === key);
7276
- if (!moving) return;
7277
- const rest = wrappers.filter((wrapper) => wrapper !== moving);
7278
- const target = rest[Math.max(0, Math.min(rest.length, toIndex))];
7279
- if (target) target.before(moving);
7280
- else rest[rest.length - 1]?.after(moving);
7283
+ const byKey = new Map(wrappers.map((wrapper) => [fieldKeyOf(wrapper), wrapper]));
7284
+ const ordered = keys.map((key) => byKey.get(key)).filter((w) => Boolean(w));
7285
+ if (ordered.length !== wrappers.length) return;
7286
+ const slots = wrappers.map((wrapper) => {
7287
+ const slot = document.createComment("ohw-field-slot");
7288
+ wrapper.replaceWith(slot);
7289
+ return slot;
7290
+ });
7291
+ ordered.forEach((wrapper, index) => slots[index].replaceWith(wrapper));
7292
+ }
7293
+ function moveField(form, key, toIndex) {
7294
+ const keys = listFieldWrappers(form).map(fieldKeyOf);
7295
+ const from = keys.indexOf(key);
7296
+ if (from === -1) return;
7297
+ const rest = keys.filter((k) => k !== key);
7298
+ rest.splice(Math.max(0, Math.min(rest.length, toIndex)), 0, key);
7299
+ applyFieldOrder(form, rest);
7281
7300
  }
7282
7301
  function reconcileFieldsFromContent(form, content) {
7283
7302
  const formKey = form.getAttribute("data-ohw-key");
@@ -7303,14 +7322,18 @@ function reconcileFieldsFromContent(form, content) {
7303
7322
  setFieldRequired(wrapper, spec.required);
7304
7323
  setFieldPlaceholder(wrapper, spec.placeholder);
7305
7324
  const label = fieldLabelOf(wrapper);
7306
- if (label && spec.label) label.textContent = spec.label;
7325
+ if (label && spec.label) label.textContent = spec.label.replace(/\s*\*\s*$/, "").trimEnd();
7307
7326
  syncRequiredMark(wrapper);
7308
7327
  });
7309
7328
  const wanted = new Set(stored.map((spec) => spec.key));
7310
7329
  listFieldWrappers(form).forEach((wrapper) => {
7311
7330
  if (!wanted.has(fieldKeyOf(wrapper))) wrapper.remove();
7312
7331
  });
7313
- stored.forEach((spec, index) => moveField(form, spec.key, index));
7332
+ const currentOrder = listFieldWrappers(form).map(fieldKeyOf).join("\0");
7333
+ const storedOrder = stored.map((spec) => spec.key).join("\0");
7334
+ if (currentOrder !== storedOrder) {
7335
+ applyFieldOrder(form, stored.map((spec) => spec.key));
7336
+ }
7314
7337
  }
7315
7338
 
7316
7339
  // src/ui/form-field-toolbar.tsx
@@ -13387,8 +13410,10 @@ function getLinkHref3(el) {
13387
13410
  }
13388
13411
  function collectEditableNodes(extraContent, root = document) {
13389
13412
  const isScoped = root !== document;
13390
- const editableEls = Array.from(root.querySelectorAll("[data-ohw-editable]"));
13391
- if (isScoped && root instanceof HTMLElement && root.matches("[data-ohw-editable]")) {
13413
+ const editableEls = Array.from(
13414
+ root.querySelectorAll('[data-ohw-editable]:not([data-ohw-editable="form"])')
13415
+ );
13416
+ if (isScoped && root instanceof HTMLElement && root.matches('[data-ohw-editable]:not([data-ohw-editable="form"])')) {
13392
13417
  editableEls.unshift(root);
13393
13418
  }
13394
13419
  const nodes = editableEls.map((el) => {
@@ -15236,8 +15261,8 @@ function OhhwellsBridge() {
15236
15261
  if (!wrapper || !form) return;
15237
15262
  commitPlaceholderEdit(wrapper);
15238
15263
  run(form, wrapper);
15239
- beginPlaceholderEdit(wrapper);
15240
15264
  persistFields(form);
15265
+ beginPlaceholderEdit(wrapper);
15241
15266
  setFormPickRect(form.getBoundingClientRect());
15242
15267
  },
15243
15268
  [persistFields]
@@ -15595,6 +15620,12 @@ function OhhwellsBridge() {
15595
15620
  }
15596
15621
  el.removeAttribute("contenteditable");
15597
15622
  el.removeAttribute("data-ohw-editing");
15623
+ const labelField = getFieldWrapper(el);
15624
+ if (labelField) {
15625
+ syncRequiredMark(labelField);
15626
+ const owner = labelField.closest('[data-ohw-editable="form"]');
15627
+ if (owner) persistFieldsRef.current(owner);
15628
+ }
15598
15629
  activeElRef.current = null;
15599
15630
  setNavGroupForceOpen(null, false);
15600
15631
  setReorderHrefKey(null);
@@ -15750,6 +15781,12 @@ function OhhwellsBridge() {
15750
15781
  }
15751
15782
  el.removeAttribute("contenteditable");
15752
15783
  el.removeAttribute("data-ohw-editing");
15784
+ const labelField = getFieldWrapper(el);
15785
+ if (labelField) {
15786
+ syncRequiredMark(labelField);
15787
+ const owner = labelField.closest('[data-ohw-editable="form"]');
15788
+ if (owner) persistFieldsRef.current(owner);
15789
+ }
15753
15790
  activeElRef.current = null;
15754
15791
  setMaxBadge(null);
15755
15792
  setActiveCommands(/* @__PURE__ */ new Set());
@@ -16876,6 +16913,9 @@ function OhhwellsBridge() {
16876
16913
  reconcileFooterOrderFromContent(content);
16877
16914
  reconcileSocialsFromContent(content);
16878
16915
  applySocialsDisplayFromContent(content);
16916
+ document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
16917
+ listFieldWrappers(form).forEach(syncRequiredMark);
16918
+ });
16879
16919
  } finally {
16880
16920
  observer?.observe(document.body, { childList: true, subtree: true });
16881
16921
  }
@@ -19003,6 +19043,13 @@ function OhhwellsBridge() {
19003
19043
  if (brandKitRef.current) {
19004
19044
  nodes.push({ key: BRAND_KIT_KEY, type: "brand", text: brandKitRef.current });
19005
19045
  }
19046
+ document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
19047
+ const formKey = formKeyOf(form);
19048
+ if (!formKey) return;
19049
+ const specs = readFieldsFromDom(form);
19050
+ const text = specs.length ? JSON.stringify(specs) : editContentRef.current[fieldsKey(formKey)];
19051
+ if (text) nodes.push({ key: fieldsKey(formKey), type: "text", text });
19052
+ });
19006
19053
  postToParentRef.current({ type: "ow:save-result", nodes });
19007
19054
  };
19008
19055
  const handleInsertSection = (e) => {