@broberg/cms-inline-edit 0.7.2 → 0.8.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/index.d.cts CHANGED
@@ -183,6 +183,19 @@ declare function disconnect(options: InlineEditOptions): void;
183
183
  * MPA consumers (broberg: full reload per navigation re-runs init) never need it.
184
184
  */
185
185
  declare function rescanFields(): void;
186
+ /**
187
+ * A plain field's value, with its line breaks intact.
188
+ *
189
+ * `textContent` drops <br> entirely — a two-paragraph value read through it
190
+ * comes back as one run of text, and saving then DESTROYS the break that was
191
+ * already stored. So this is not only how a new break gets in; it is what stops
192
+ * an existing one being silently flattened the next time anyone edits the field.
193
+ *
194
+ * Deliberately narrow: <br> becomes "\n" and text nodes come through verbatim.
195
+ * A plain field is a string, not a document — anything richer belongs in a
196
+ * richtext field, which has its own serializer.
197
+ */
198
+ declare function plainTextWithBreaks(el: HTMLElement): string;
186
199
  interface LinkablePage {
187
200
  collection: string;
188
201
  slug: string;
@@ -253,4 +266,4 @@ declare function setLinkTarget(el: HTMLElement, newTab: boolean): void;
253
266
  */
254
267
  declare function htmlToMarkdown(html: string): string;
255
268
 
256
- export { type InlineEditLabels, type InlineEditOptions, applyFieldSlice, buildConnectUrl, disconnect, extractLinkTargets, getConnectedToken, htmlToMarkdown, initInlineEdit, isBareEmail, isDangerousUrl, isExternalHost, isSchemeless, positionPopover, renderCurrentRefLine, rescanFields, resolveExistingRef, setLinkTarget, withHttps };
269
+ export { type InlineEditLabels, type InlineEditOptions, applyFieldSlice, buildConnectUrl, disconnect, extractLinkTargets, getConnectedToken, htmlToMarkdown, initInlineEdit, isBareEmail, isDangerousUrl, isExternalHost, isSchemeless, plainTextWithBreaks, positionPopover, renderCurrentRefLine, rescanFields, resolveExistingRef, setLinkTarget, withHttps };
package/dist/index.d.ts CHANGED
@@ -183,6 +183,19 @@ declare function disconnect(options: InlineEditOptions): void;
183
183
  * MPA consumers (broberg: full reload per navigation re-runs init) never need it.
184
184
  */
185
185
  declare function rescanFields(): void;
186
+ /**
187
+ * A plain field's value, with its line breaks intact.
188
+ *
189
+ * `textContent` drops <br> entirely — a two-paragraph value read through it
190
+ * comes back as one run of text, and saving then DESTROYS the break that was
191
+ * already stored. So this is not only how a new break gets in; it is what stops
192
+ * an existing one being silently flattened the next time anyone edits the field.
193
+ *
194
+ * Deliberately narrow: <br> becomes "\n" and text nodes come through verbatim.
195
+ * A plain field is a string, not a document — anything richer belongs in a
196
+ * richtext field, which has its own serializer.
197
+ */
198
+ declare function plainTextWithBreaks(el: HTMLElement): string;
186
199
  interface LinkablePage {
187
200
  collection: string;
188
201
  slug: string;
@@ -253,4 +266,4 @@ declare function setLinkTarget(el: HTMLElement, newTab: boolean): void;
253
266
  */
254
267
  declare function htmlToMarkdown(html: string): string;
255
268
 
256
- export { type InlineEditLabels, type InlineEditOptions, applyFieldSlice, buildConnectUrl, disconnect, extractLinkTargets, getConnectedToken, htmlToMarkdown, initInlineEdit, isBareEmail, isDangerousUrl, isExternalHost, isSchemeless, positionPopover, renderCurrentRefLine, rescanFields, resolveExistingRef, setLinkTarget, withHttps };
269
+ export { type InlineEditLabels, type InlineEditOptions, applyFieldSlice, buildConnectUrl, disconnect, extractLinkTargets, getConnectedToken, htmlToMarkdown, initInlineEdit, isBareEmail, isDangerousUrl, isExternalHost, isSchemeless, plainTextWithBreaks, positionPopover, renderCurrentRefLine, rescanFields, resolveExistingRef, setLinkTarget, withHttps };
package/dist/index.js CHANGED
@@ -397,7 +397,7 @@ function wireField(el, token, options) {
397
397
  e.preventDefault();
398
398
  e.stopPropagation();
399
399
  const tokenSafe = hasTokenChips(el);
400
- el.dataset.cmsOriginalValue = tokenSafe ? serializeTokenSafe(el) : el.textContent ?? "";
400
+ el.dataset.cmsOriginalValue = tokenSafe ? serializeTokenSafe(el) : plainTextWithBreaks(el);
401
401
  el.setAttribute("contenteditable", "true");
402
402
  if (tokenSafe) lockTokenChips(el);
403
403
  el.focus();
@@ -405,7 +405,7 @@ function wireField(el, token, options) {
405
405
  el.addEventListener("blur", () => {
406
406
  el.removeAttribute("contenteditable");
407
407
  const original = el.dataset.cmsOriginalValue ?? "";
408
- const current = hasTokenChips(el) ? serializeTokenSafe(el) : el.textContent ?? "";
408
+ const current = hasTokenChips(el) ? serializeTokenSafe(el) : plainTextWithBreaks(el);
409
409
  if (current.trim() === original.trim()) return;
410
410
  void saveField(el, current.trim(), token, options);
411
411
  });
@@ -415,12 +415,35 @@ function wireField(el, token, options) {
415
415
  document.execCommand("insertText", false, text);
416
416
  });
417
417
  el.addEventListener("keydown", (e) => {
418
- if (e.key === "Enter") {
419
- e.preventDefault();
420
- el.blur();
418
+ if (e.key !== "Enter") return;
419
+ e.preventDefault();
420
+ if (e.shiftKey) {
421
+ document.execCommand("insertLineBreak");
422
+ return;
421
423
  }
424
+ el.blur();
422
425
  });
423
426
  }
427
+ function plainTextWithBreaks(el) {
428
+ let out = "";
429
+ const walk = (node) => {
430
+ if (node.nodeType === Node.TEXT_NODE) {
431
+ out += node.textContent ?? "";
432
+ return;
433
+ }
434
+ if (node.nodeType !== Node.ELEMENT_NODE) return;
435
+ const tag = node.tagName.toLowerCase();
436
+ if (tag === "br") {
437
+ out += "\n";
438
+ return;
439
+ }
440
+ const blocky = tag === "div" || tag === "p";
441
+ if (blocky && out !== "" && !out.endsWith("\n")) out += "\n";
442
+ node.childNodes.forEach(walk);
443
+ };
444
+ el.childNodes.forEach(walk);
445
+ return out;
446
+ }
424
447
  var richCtx = null;
425
448
  var richToolbar = null;
426
449
  function wireRichField(el, token, options, mode) {
@@ -1532,6 +1555,7 @@ export {
1532
1555
  isDangerousUrl,
1533
1556
  isExternalHost,
1534
1557
  isSchemeless,
1558
+ plainTextWithBreaks,
1535
1559
  positionPopover,
1536
1560
  renderCurrentRefLine,
1537
1561
  rescanFields,