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

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
@@ -15236,8 +15259,8 @@ function OhhwellsBridge() {
15236
15259
  if (!wrapper || !form) return;
15237
15260
  commitPlaceholderEdit(wrapper);
15238
15261
  run(form, wrapper);
15239
- beginPlaceholderEdit(wrapper);
15240
15262
  persistFields(form);
15263
+ beginPlaceholderEdit(wrapper);
15241
15264
  setFormPickRect(form.getBoundingClientRect());
15242
15265
  },
15243
15266
  [persistFields]
@@ -15595,6 +15618,12 @@ function OhhwellsBridge() {
15595
15618
  }
15596
15619
  el.removeAttribute("contenteditable");
15597
15620
  el.removeAttribute("data-ohw-editing");
15621
+ const labelField = getFieldWrapper(el);
15622
+ if (labelField) {
15623
+ syncRequiredMark(labelField);
15624
+ const owner = labelField.closest('[data-ohw-editable="form"]');
15625
+ if (owner) persistFieldsRef.current(owner);
15626
+ }
15598
15627
  activeElRef.current = null;
15599
15628
  setNavGroupForceOpen(null, false);
15600
15629
  setReorderHrefKey(null);
@@ -15750,6 +15779,12 @@ function OhhwellsBridge() {
15750
15779
  }
15751
15780
  el.removeAttribute("contenteditable");
15752
15781
  el.removeAttribute("data-ohw-editing");
15782
+ const labelField = getFieldWrapper(el);
15783
+ if (labelField) {
15784
+ syncRequiredMark(labelField);
15785
+ const owner = labelField.closest('[data-ohw-editable="form"]');
15786
+ if (owner) persistFieldsRef.current(owner);
15787
+ }
15753
15788
  activeElRef.current = null;
15754
15789
  setMaxBadge(null);
15755
15790
  setActiveCommands(/* @__PURE__ */ new Set());
@@ -16876,6 +16911,9 @@ function OhhwellsBridge() {
16876
16911
  reconcileFooterOrderFromContent(content);
16877
16912
  reconcileSocialsFromContent(content);
16878
16913
  applySocialsDisplayFromContent(content);
16914
+ document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
16915
+ listFieldWrappers(form).forEach(syncRequiredMark);
16916
+ });
16879
16917
  } finally {
16880
16918
  observer?.observe(document.body, { childList: true, subtree: true });
16881
16919
  }
@@ -19003,6 +19041,13 @@ function OhhwellsBridge() {
19003
19041
  if (brandKitRef.current) {
19004
19042
  nodes.push({ key: BRAND_KIT_KEY, type: "brand", text: brandKitRef.current });
19005
19043
  }
19044
+ document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
19045
+ const formKey = formKeyOf(form);
19046
+ if (!formKey) return;
19047
+ const specs = readFieldsFromDom(form);
19048
+ const text = specs.length ? JSON.stringify(specs) : editContentRef.current[fieldsKey(formKey)];
19049
+ if (text) nodes.push({ key: fieldsKey(formKey), type: "text", text });
19050
+ });
19006
19051
  postToParentRef.current({ type: "ow:save-result", nodes });
19007
19052
  };
19008
19053
  const handleInsertSection = (e) => {