@broberg/cms-inline-edit 0.6.3 → 0.7.1

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
@@ -40,6 +40,47 @@ __export(index_exports, {
40
40
  });
41
41
  module.exports = __toCommonJS(index_exports);
42
42
 
43
+ // src/page-tools.ts
44
+ function parseTags(input) {
45
+ const set = /* @__PURE__ */ new Set();
46
+ const ud = [];
47
+ for (const raa of input.split(",")) {
48
+ const t = raa.trim();
49
+ if (!t) continue;
50
+ const noegle = t.toLowerCase();
51
+ if (set.has(noegle)) continue;
52
+ set.add(noegle);
53
+ ud.push(t);
54
+ }
55
+ return ud;
56
+ }
57
+ function mergeTags(eksisterende, nye) {
58
+ const basis = Array.isArray(eksisterende) ? eksisterende.filter((t) => typeof t === "string") : [];
59
+ const kendte = new Set(basis.map((t) => t.toLowerCase()));
60
+ const ud = [...basis];
61
+ for (const t of nye) {
62
+ if (kendte.has(t.toLowerCase())) continue;
63
+ kendte.add(t.toLowerCase());
64
+ ud.push(t);
65
+ }
66
+ return ud;
67
+ }
68
+ function primaryDocRef(ankre) {
69
+ const taeller = /* @__PURE__ */ new Map();
70
+ for (const a of ankre) {
71
+ if (!a.collection || !a.slug || a.collection === "globals") continue;
72
+ const noegle = `${a.collection} ${a.slug}`;
73
+ const post = taeller.get(noegle) ?? { collection: a.collection, slug: a.slug, n: 0 };
74
+ post.n++;
75
+ taeller.set(noegle, post);
76
+ }
77
+ let bedst = null;
78
+ for (const post of taeller.values()) {
79
+ if (!bedst || post.n > bedst.n) bedst = post;
80
+ }
81
+ return bedst ? { collection: bedst.collection, slug: bedst.slug } : null;
82
+ }
83
+
43
84
  // src/field-slice.ts
44
85
  function applyFieldSlice(current, original, next) {
45
86
  const first = current.indexOf(original);
@@ -270,6 +311,7 @@ var SQUARE_PEN_SVG = '<svg xmlns="http://www.w3.org/2000/svg" width="15" height=
270
311
  var UL_SVG = '<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="8" x2="21" y1="6" y2="6"/><line x1="8" x2="21" y1="12" y2="12"/><line x1="8" x2="21" y1="18" y2="18"/><line x1="3" x2="3.01" y1="6" y2="6"/><line x1="3" x2="3.01" y1="12" y2="12"/><line x1="3" x2="3.01" y1="18" y2="18"/></svg>';
271
312
  var OL_SVG = '<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="10" x2="21" y1="6" y2="6"/><line x1="10" x2="21" y1="12" y2="12"/><line x1="10" x2="21" y1="18" y2="18"/><path d="M4 6h1v4"/><path d="M4 10h2"/><path d="M6 18H4c0-1 2-2 2-3s-1-1.5-2-1"/></svg>';
272
313
  var LINK_SVG = '<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.5.5l3-3a5 5 0 0 0-7-7l-1.7 1.7"/><path d="M14 11a5 5 0 0 0-7.5-.5l-3 3a5 5 0 0 0 7 7l1.7-1.7"/></svg>';
314
+ var WRENCH_SVG = '<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/></svg>';
273
315
  function makeIcon() {
274
316
  const icon = document.createElement("span");
275
317
  icon.style.cssText = "display:inline-flex;line-height:0;flex:0 0 auto";
@@ -340,6 +382,8 @@ function exitEditMode() {
340
382
  deactivateRich();
341
383
  editingActive = false;
342
384
  document.body.removeAttribute("data-cms-editing");
385
+ document.querySelector("[data-cms-inline-edit-toolsrow]")?.remove();
386
+ document.querySelector("[data-cms-inline-edit-tools-popup]")?.remove();
343
387
  document.querySelector("[data-cms-inline-edit-badge]")?.remove();
344
388
  showIdlePill();
345
389
  }
@@ -1276,7 +1320,172 @@ function showActiveBadge(options) {
1276
1320
  badge.addEventListener("mouseenter", () => badge.style.opacity = "1");
1277
1321
  badge.addEventListener("mouseleave", () => badge.style.opacity = ".9");
1278
1322
  badge.addEventListener("click", exitEditMode);
1279
- document.body.appendChild(badge);
1323
+ badge.style.position = "static";
1324
+ badge.style.borderRadius = "999px 0 0 999px";
1325
+ const raekke = document.createElement("div");
1326
+ raekke.setAttribute("data-cms-inline-edit-toolsrow", "");
1327
+ raekke.style.cssText = "position:fixed;bottom:16px;left:16px;display:inline-flex;align-items:stretch;z-index:2147483647;box-shadow:0 4px 16px rgba(0,0,0,.3);border-radius:999px;overflow:visible;";
1328
+ const tools = document.createElement("button");
1329
+ tools.type = "button";
1330
+ tools.setAttribute("data-cms-inline-edit-tools", "");
1331
+ tools.setAttribute("data-testid", "inline-edit-tools");
1332
+ tools.setAttribute("aria-label", "Tools");
1333
+ tools.title = "Tools";
1334
+ tools.style.cssText = "display:inline-flex;align-items:center;background:#1c2027;color:#8b93a3;padding:8px 12px;border:none;border-left:1px solid rgba(255,255,255,.12);border-radius:0 999px 999px 0;cursor:pointer;font:600 12px system-ui,sans-serif;";
1335
+ tools.innerHTML = WRENCH_SVG;
1336
+ tools.addEventListener("mouseenter", () => tools.style.color = "#fff");
1337
+ tools.addEventListener("mouseleave", () => tools.style.color = "#8b93a3");
1338
+ tools.addEventListener("click", () => togglePageTools(options, raekke));
1339
+ raekke.append(badge, tools);
1340
+ document.body.appendChild(raekke);
1341
+ }
1342
+ async function hentDoc(collection, slug, token, options) {
1343
+ const res = await fetch(`${options.cmsBaseUrl}/api/cms/${collection}/${slug}?site=${options.siteId}`, {
1344
+ headers: { Authorization: `Bearer ${token}` }
1345
+ });
1346
+ if (!res.ok) throw new Error(`GET failed: ${res.status}`);
1347
+ const doc = await res.json();
1348
+ return JSON.parse(JSON.stringify(doc.data ?? {}));
1349
+ }
1350
+ async function gemDoc(collection, slug, data, token, options) {
1351
+ const res = await fetch(`${options.cmsBaseUrl}/api/cms/${collection}/${slug}?site=${options.siteId}`, {
1352
+ method: "PATCH",
1353
+ headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
1354
+ body: JSON.stringify({ data })
1355
+ });
1356
+ if (!res.ok) throw new Error(`PATCH failed: ${res.status}`);
1357
+ }
1358
+ function togglePageTools(options, forankring) {
1359
+ const \u00E5ben = document.querySelector("[data-cms-inline-edit-tools-popup]");
1360
+ if (\u00E5ben) {
1361
+ \u00E5ben.remove();
1362
+ return;
1363
+ }
1364
+ const token = getConnectedToken(options);
1365
+ if (!token) return;
1366
+ const ankre = Array.from(
1367
+ document.querySelectorAll("[data-cms-field]")
1368
+ ).map((el) => {
1369
+ const a = {};
1370
+ if (el.dataset.cmsCollection) a.collection = el.dataset.cmsCollection;
1371
+ if (el.dataset.cmsSlug) a.slug = el.dataset.cmsSlug;
1372
+ return a;
1373
+ });
1374
+ const ref = primaryDocRef(ankre);
1375
+ if (!ref) return;
1376
+ const popup = document.createElement("div");
1377
+ popup.setAttribute("data-cms-inline-edit-tools-popup", "");
1378
+ popup.setAttribute("data-testid", "page-tools-popup");
1379
+ popup.style.cssText = "position:fixed;bottom:56px;left:16px;z-index:2147483647;background:#1c2027;border:1px solid #3a3f4a;border-radius:12px;padding:10px;min-width:230px;box-shadow:0 8px 32px rgba(0,0,0,.5);font-family:system-ui,sans-serif;color:#fff;display:flex;flex-direction:column;gap:8px;";
1380
+ const featRow = document.createElement("button");
1381
+ featRow.type = "button";
1382
+ featRow.setAttribute("data-testid", "page-tools-featured");
1383
+ featRow.style.cssText = "display:flex;align-items:center;gap:8px;background:rgba(255,255,255,.04);border:1px solid #3a3f4a;border-radius:9px;padding:8px 10px;font:600 12px system-ui,sans-serif;color:#fff;cursor:pointer;text-align:left;";
1384
+ featRow.textContent = "\u2605 Featured";
1385
+ featRow.disabled = true;
1386
+ popup.appendChild(featRow);
1387
+ const visFeatured = (til) => {
1388
+ featRow.style.borderColor = til ? "#00b2ff" : "#3a3f4a";
1389
+ featRow.style.color = til ? "#40c8ff" : "#fff";
1390
+ featRow.style.background = til ? "rgba(0,178,255,.13)" : "rgba(255,255,255,.04)";
1391
+ };
1392
+ let featured = false;
1393
+ void hentDoc(ref.collection, ref.slug, token, options).then((data) => {
1394
+ featured = data.featured === true;
1395
+ visFeatured(featured);
1396
+ featRow.disabled = false;
1397
+ }).catch(() => {
1398
+ featRow.textContent = "\u2605 Featured \u2014 kunne ikke l\xE6ses";
1399
+ });
1400
+ featRow.addEventListener("click", async () => {
1401
+ featRow.disabled = true;
1402
+ try {
1403
+ const data = await hentDoc(ref.collection, ref.slug, token, options);
1404
+ data.featured = !(data.featured === true);
1405
+ await gemDoc(ref.collection, ref.slug, data, token, options);
1406
+ const frisk = await hentDoc(ref.collection, ref.slug, token, options);
1407
+ featured = frisk.featured === true;
1408
+ visFeatured(featured);
1409
+ } catch {
1410
+ featRow.style.borderColor = "#f3522c";
1411
+ } finally {
1412
+ featRow.disabled = false;
1413
+ }
1414
+ });
1415
+ const tagRow = document.createElement("div");
1416
+ tagRow.style.cssText = "display:flex;align-items:center;gap:6px;";
1417
+ const plus = document.createElement("button");
1418
+ plus.type = "button";
1419
+ plus.setAttribute("data-testid", "page-tools-tags-plus");
1420
+ 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;";
1421
+ plus.textContent = "# Tags +";
1422
+ const felt = document.createElement("input");
1423
+ felt.type = "text";
1424
+ felt.placeholder = "tag1, tag2, tag3";
1425
+ felt.setAttribute("data-testid", "page-tools-tags-input");
1426
+ felt.style.cssText = "flex:1;min-width:0;display:none;background:#12151a;border:1px solid #3a3f4a;border-radius:8px;padding:7px 9px;color:#fff;font:12px system-ui,sans-serif;outline:none;";
1427
+ const gem = document.createElement("button");
1428
+ gem.type = "button";
1429
+ gem.setAttribute("data-testid", "page-tools-tags-gem");
1430
+ gem.style.cssText = "display:none;background:#00b2ff;border:none;border-radius:8px;padding:7px 11px;color:#04222f;font:700 12px system-ui,sans-serif;cursor:pointer;";
1431
+ gem.textContent = "Gem";
1432
+ plus.addEventListener("click", () => {
1433
+ plus.style.display = "none";
1434
+ felt.style.display = "block";
1435
+ gem.style.display = "inline-block";
1436
+ felt.focus();
1437
+ });
1438
+ const gemTags = async () => {
1439
+ const nye = parseTags(felt.value);
1440
+ if (!nye.length) return;
1441
+ gem.disabled = true;
1442
+ gem.textContent = "\u2026";
1443
+ try {
1444
+ const data = await hentDoc(ref.collection, ref.slug, token, options);
1445
+ data.tags = mergeTags(data.tags, nye);
1446
+ await gemDoc(ref.collection, ref.slug, data, token, options);
1447
+ const frisk = await hentDoc(ref.collection, ref.slug, token, options);
1448
+ const holdt = Array.isArray(frisk.tags) && nye.every((t) => frisk.tags.some((e) => String(e).toLowerCase() === t.toLowerCase()));
1449
+ gem.textContent = holdt ? "Gemt \u2713" : "Fejl";
1450
+ if (holdt) {
1451
+ felt.value = "";
1452
+ setTimeout(() => {
1453
+ gem.textContent = "Gem";
1454
+ gem.disabled = false;
1455
+ }, 1200);
1456
+ } else {
1457
+ gem.disabled = false;
1458
+ }
1459
+ } catch {
1460
+ gem.textContent = "Fejl";
1461
+ gem.disabled = false;
1462
+ }
1463
+ };
1464
+ gem.addEventListener("click", () => void gemTags());
1465
+ felt.addEventListener("keydown", (e) => {
1466
+ if (e.key === "Enter") {
1467
+ e.preventDefault();
1468
+ void gemTags();
1469
+ }
1470
+ });
1471
+ tagRow.append(plus, felt, gem);
1472
+ popup.appendChild(tagRow);
1473
+ const lukP\u00E5Klik = (e) => {
1474
+ if (!popup.contains(e.target) && !forankring.contains(e.target)) luk();
1475
+ };
1476
+ const lukP\u00E5Esc = (e) => {
1477
+ if (e.key === "Escape") luk();
1478
+ };
1479
+ const luk = () => {
1480
+ popup.remove();
1481
+ document.removeEventListener("mousedown", lukP\u00E5Klik);
1482
+ document.removeEventListener("keydown", lukP\u00E5Esc);
1483
+ };
1484
+ setTimeout(() => {
1485
+ document.addEventListener("mousedown", lukP\u00E5Klik);
1486
+ document.addEventListener("keydown", lukP\u00E5Esc);
1487
+ }, 0);
1488
+ document.body.appendChild(popup);
1280
1489
  }
1281
1490
  function decodeJwtPayload(token) {
1282
1491
  try {