@broberg/cms-inline-edit 0.7.1 → 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.cjs +89 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -1
- package/dist/index.d.ts +14 -1
- package/dist/index.js +88 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
|
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
|
|
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
|
|
419
|
-
|
|
420
|
-
|
|
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) {
|
|
@@ -1377,11 +1400,48 @@ function togglePageTools(options, forankring) {
|
|
|
1377
1400
|
plus.setAttribute("data-testid", "page-tools-tags-plus");
|
|
1378
1401
|
plus.style.cssText = "background:rgba(255,255,255,.04);border:1px solid #3a3f4a;border-radius:9px;padding:8px 12px;color:#fff;font:600 12px system-ui,sans-serif;cursor:pointer;";
|
|
1379
1402
|
plus.textContent = "# Tags +";
|
|
1403
|
+
const boks = document.createElement("div");
|
|
1404
|
+
boks.setAttribute("data-testid", "page-tools-tags-boks");
|
|
1405
|
+
boks.style.cssText = "flex:1;min-width:0;display:none;flex-wrap:wrap;align-items:center;gap:5px;background:#12151a;border:1px solid #3a3f4a;border-radius:8px;padding:5px 7px;cursor:text;";
|
|
1406
|
+
const chips = [];
|
|
1380
1407
|
const felt = document.createElement("input");
|
|
1381
1408
|
felt.type = "text";
|
|
1382
1409
|
felt.placeholder = "tag1, tag2, tag3";
|
|
1383
1410
|
felt.setAttribute("data-testid", "page-tools-tags-input");
|
|
1384
|
-
felt.style.cssText = "flex:1;min-width:
|
|
1411
|
+
felt.style.cssText = "flex:1;min-width:70px;background:transparent;border:none;padding:2px;color:#fff;font:12px system-ui,sans-serif;outline:none;";
|
|
1412
|
+
const tegnChips = () => {
|
|
1413
|
+
Array.prototype.forEach.call(boks.querySelectorAll("[data-chip]"), (c) => c.remove());
|
|
1414
|
+
chips.forEach((t, i) => {
|
|
1415
|
+
const chip = document.createElement("span");
|
|
1416
|
+
chip.setAttribute("data-chip", "");
|
|
1417
|
+
chip.setAttribute("data-testid", "page-tools-tag-chip");
|
|
1418
|
+
chip.style.cssText = "display:inline-flex;align-items:center;gap:4px;background:rgba(0,178,255,.15);border:1px solid rgba(0,178,255,.45);border-radius:999px;padding:2px 4px 2px 8px;color:#7fd8ff;font:600 11px system-ui,sans-serif;";
|
|
1419
|
+
chip.textContent = t;
|
|
1420
|
+
const x = document.createElement("button");
|
|
1421
|
+
x.type = "button";
|
|
1422
|
+
x.setAttribute("data-testid", "page-tools-tag-fjern");
|
|
1423
|
+
x.setAttribute("aria-label", `Fjern ${t}`);
|
|
1424
|
+
x.style.cssText = "background:none;border:none;color:#7fd8ff;font:700 13px system-ui,sans-serif;cursor:pointer;line-height:1;padding:0 3px;";
|
|
1425
|
+
x.textContent = "\xD7";
|
|
1426
|
+
x.addEventListener("click", (e) => {
|
|
1427
|
+
e.stopPropagation();
|
|
1428
|
+
chips.splice(i, 1);
|
|
1429
|
+
tegnChips();
|
|
1430
|
+
felt.focus();
|
|
1431
|
+
});
|
|
1432
|
+
chip.appendChild(x);
|
|
1433
|
+
boks.insertBefore(chip, felt);
|
|
1434
|
+
});
|
|
1435
|
+
};
|
|
1436
|
+
const tagify = () => {
|
|
1437
|
+
const nye = parseTags(felt.value);
|
|
1438
|
+
for (const t of nye) {
|
|
1439
|
+
if (!chips.some((c) => c.toLowerCase() === t.toLowerCase())) chips.push(t);
|
|
1440
|
+
}
|
|
1441
|
+
felt.value = "";
|
|
1442
|
+
tegnChips();
|
|
1443
|
+
};
|
|
1444
|
+
boks.addEventListener("click", () => felt.focus());
|
|
1385
1445
|
const gem = document.createElement("button");
|
|
1386
1446
|
gem.type = "button";
|
|
1387
1447
|
gem.setAttribute("data-testid", "page-tools-tags-gem");
|
|
@@ -1389,12 +1449,13 @@ function togglePageTools(options, forankring) {
|
|
|
1389
1449
|
gem.textContent = "Gem";
|
|
1390
1450
|
plus.addEventListener("click", () => {
|
|
1391
1451
|
plus.style.display = "none";
|
|
1392
|
-
|
|
1452
|
+
boks.style.display = "flex";
|
|
1393
1453
|
gem.style.display = "inline-block";
|
|
1394
1454
|
felt.focus();
|
|
1395
1455
|
});
|
|
1396
1456
|
const gemTags = async () => {
|
|
1397
|
-
|
|
1457
|
+
tagify();
|
|
1458
|
+
const nye = [...chips];
|
|
1398
1459
|
if (!nye.length) return;
|
|
1399
1460
|
gem.disabled = true;
|
|
1400
1461
|
gem.textContent = "\u2026";
|
|
@@ -1407,6 +1468,8 @@ function togglePageTools(options, forankring) {
|
|
|
1407
1468
|
gem.textContent = holdt ? "Gemt \u2713" : "Fejl";
|
|
1408
1469
|
if (holdt) {
|
|
1409
1470
|
felt.value = "";
|
|
1471
|
+
chips.length = 0;
|
|
1472
|
+
tegnChips();
|
|
1410
1473
|
setTimeout(() => {
|
|
1411
1474
|
gem.textContent = "Gem";
|
|
1412
1475
|
gem.disabled = false;
|
|
@@ -1421,12 +1484,27 @@ function togglePageTools(options, forankring) {
|
|
|
1421
1484
|
};
|
|
1422
1485
|
gem.addEventListener("click", () => void gemTags());
|
|
1423
1486
|
felt.addEventListener("keydown", (e) => {
|
|
1487
|
+
if (e.key === ",") {
|
|
1488
|
+
e.preventDefault();
|
|
1489
|
+
tagify();
|
|
1490
|
+
return;
|
|
1491
|
+
}
|
|
1424
1492
|
if (e.key === "Enter") {
|
|
1425
1493
|
e.preventDefault();
|
|
1426
1494
|
void gemTags();
|
|
1495
|
+
return;
|
|
1427
1496
|
}
|
|
1497
|
+
if (e.key === "Backspace" && felt.value === "" && chips.length) {
|
|
1498
|
+
e.preventDefault();
|
|
1499
|
+
chips.pop();
|
|
1500
|
+
tegnChips();
|
|
1501
|
+
}
|
|
1502
|
+
});
|
|
1503
|
+
felt.addEventListener("input", () => {
|
|
1504
|
+
if (felt.value.includes(",")) tagify();
|
|
1428
1505
|
});
|
|
1429
|
-
|
|
1506
|
+
boks.appendChild(felt);
|
|
1507
|
+
tagRow.append(plus, boks, gem);
|
|
1430
1508
|
popup.appendChild(tagRow);
|
|
1431
1509
|
const lukP\u00E5Klik = (e) => {
|
|
1432
1510
|
if (!popup.contains(e.target) && !forankring.contains(e.target)) luk();
|
|
@@ -1477,6 +1555,7 @@ export {
|
|
|
1477
1555
|
isDangerousUrl,
|
|
1478
1556
|
isExternalHost,
|
|
1479
1557
|
isSchemeless,
|
|
1558
|
+
plainTextWithBreaks,
|
|
1480
1559
|
positionPopover,
|
|
1481
1560
|
renderCurrentRefLine,
|
|
1482
1561
|
rescanFields,
|