@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.js CHANGED
@@ -1,3 +1,44 @@
1
+ // src/page-tools.ts
2
+ function parseTags(input) {
3
+ const set = /* @__PURE__ */ new Set();
4
+ const ud = [];
5
+ for (const raa of input.split(",")) {
6
+ const t = raa.trim();
7
+ if (!t) continue;
8
+ const noegle = t.toLowerCase();
9
+ if (set.has(noegle)) continue;
10
+ set.add(noegle);
11
+ ud.push(t);
12
+ }
13
+ return ud;
14
+ }
15
+ function mergeTags(eksisterende, nye) {
16
+ const basis = Array.isArray(eksisterende) ? eksisterende.filter((t) => typeof t === "string") : [];
17
+ const kendte = new Set(basis.map((t) => t.toLowerCase()));
18
+ const ud = [...basis];
19
+ for (const t of nye) {
20
+ if (kendte.has(t.toLowerCase())) continue;
21
+ kendte.add(t.toLowerCase());
22
+ ud.push(t);
23
+ }
24
+ return ud;
25
+ }
26
+ function primaryDocRef(ankre) {
27
+ const taeller = /* @__PURE__ */ new Map();
28
+ for (const a of ankre) {
29
+ if (!a.collection || !a.slug || a.collection === "globals") continue;
30
+ const noegle = `${a.collection} ${a.slug}`;
31
+ const post = taeller.get(noegle) ?? { collection: a.collection, slug: a.slug, n: 0 };
32
+ post.n++;
33
+ taeller.set(noegle, post);
34
+ }
35
+ let bedst = null;
36
+ for (const post of taeller.values()) {
37
+ if (!bedst || post.n > bedst.n) bedst = post;
38
+ }
39
+ return bedst ? { collection: bedst.collection, slug: bedst.slug } : null;
40
+ }
41
+
1
42
  // src/field-slice.ts
2
43
  function applyFieldSlice(current, original, next) {
3
44
  const first = current.indexOf(original);
@@ -228,6 +269,7 @@ var SQUARE_PEN_SVG = '<svg xmlns="http://www.w3.org/2000/svg" width="15" height=
228
269
  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>';
229
270
  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>';
230
271
  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>';
272
+ 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>';
231
273
  function makeIcon() {
232
274
  const icon = document.createElement("span");
233
275
  icon.style.cssText = "display:inline-flex;line-height:0;flex:0 0 auto";
@@ -298,6 +340,8 @@ function exitEditMode() {
298
340
  deactivateRich();
299
341
  editingActive = false;
300
342
  document.body.removeAttribute("data-cms-editing");
343
+ document.querySelector("[data-cms-inline-edit-toolsrow]")?.remove();
344
+ document.querySelector("[data-cms-inline-edit-tools-popup]")?.remove();
301
345
  document.querySelector("[data-cms-inline-edit-badge]")?.remove();
302
346
  showIdlePill();
303
347
  }
@@ -1234,7 +1278,172 @@ function showActiveBadge(options) {
1234
1278
  badge.addEventListener("mouseenter", () => badge.style.opacity = "1");
1235
1279
  badge.addEventListener("mouseleave", () => badge.style.opacity = ".9");
1236
1280
  badge.addEventListener("click", exitEditMode);
1237
- document.body.appendChild(badge);
1281
+ badge.style.position = "static";
1282
+ badge.style.borderRadius = "999px 0 0 999px";
1283
+ const raekke = document.createElement("div");
1284
+ raekke.setAttribute("data-cms-inline-edit-toolsrow", "");
1285
+ 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;";
1286
+ const tools = document.createElement("button");
1287
+ tools.type = "button";
1288
+ tools.setAttribute("data-cms-inline-edit-tools", "");
1289
+ tools.setAttribute("data-testid", "inline-edit-tools");
1290
+ tools.setAttribute("aria-label", "Tools");
1291
+ tools.title = "Tools";
1292
+ 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;";
1293
+ tools.innerHTML = WRENCH_SVG;
1294
+ tools.addEventListener("mouseenter", () => tools.style.color = "#fff");
1295
+ tools.addEventListener("mouseleave", () => tools.style.color = "#8b93a3");
1296
+ tools.addEventListener("click", () => togglePageTools(options, raekke));
1297
+ raekke.append(badge, tools);
1298
+ document.body.appendChild(raekke);
1299
+ }
1300
+ async function hentDoc(collection, slug, token, options) {
1301
+ const res = await fetch(`${options.cmsBaseUrl}/api/cms/${collection}/${slug}?site=${options.siteId}`, {
1302
+ headers: { Authorization: `Bearer ${token}` }
1303
+ });
1304
+ if (!res.ok) throw new Error(`GET failed: ${res.status}`);
1305
+ const doc = await res.json();
1306
+ return JSON.parse(JSON.stringify(doc.data ?? {}));
1307
+ }
1308
+ async function gemDoc(collection, slug, data, token, options) {
1309
+ const res = await fetch(`${options.cmsBaseUrl}/api/cms/${collection}/${slug}?site=${options.siteId}`, {
1310
+ method: "PATCH",
1311
+ headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
1312
+ body: JSON.stringify({ data })
1313
+ });
1314
+ if (!res.ok) throw new Error(`PATCH failed: ${res.status}`);
1315
+ }
1316
+ function togglePageTools(options, forankring) {
1317
+ const \u00E5ben = document.querySelector("[data-cms-inline-edit-tools-popup]");
1318
+ if (\u00E5ben) {
1319
+ \u00E5ben.remove();
1320
+ return;
1321
+ }
1322
+ const token = getConnectedToken(options);
1323
+ if (!token) return;
1324
+ const ankre = Array.from(
1325
+ document.querySelectorAll("[data-cms-field]")
1326
+ ).map((el) => {
1327
+ const a = {};
1328
+ if (el.dataset.cmsCollection) a.collection = el.dataset.cmsCollection;
1329
+ if (el.dataset.cmsSlug) a.slug = el.dataset.cmsSlug;
1330
+ return a;
1331
+ });
1332
+ const ref = primaryDocRef(ankre);
1333
+ if (!ref) return;
1334
+ const popup = document.createElement("div");
1335
+ popup.setAttribute("data-cms-inline-edit-tools-popup", "");
1336
+ popup.setAttribute("data-testid", "page-tools-popup");
1337
+ 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;";
1338
+ const featRow = document.createElement("button");
1339
+ featRow.type = "button";
1340
+ featRow.setAttribute("data-testid", "page-tools-featured");
1341
+ 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;";
1342
+ featRow.textContent = "\u2605 Featured";
1343
+ featRow.disabled = true;
1344
+ popup.appendChild(featRow);
1345
+ const visFeatured = (til) => {
1346
+ featRow.style.borderColor = til ? "#00b2ff" : "#3a3f4a";
1347
+ featRow.style.color = til ? "#40c8ff" : "#fff";
1348
+ featRow.style.background = til ? "rgba(0,178,255,.13)" : "rgba(255,255,255,.04)";
1349
+ };
1350
+ let featured = false;
1351
+ void hentDoc(ref.collection, ref.slug, token, options).then((data) => {
1352
+ featured = data.featured === true;
1353
+ visFeatured(featured);
1354
+ featRow.disabled = false;
1355
+ }).catch(() => {
1356
+ featRow.textContent = "\u2605 Featured \u2014 kunne ikke l\xE6ses";
1357
+ });
1358
+ featRow.addEventListener("click", async () => {
1359
+ featRow.disabled = true;
1360
+ try {
1361
+ const data = await hentDoc(ref.collection, ref.slug, token, options);
1362
+ data.featured = !(data.featured === true);
1363
+ await gemDoc(ref.collection, ref.slug, data, token, options);
1364
+ const frisk = await hentDoc(ref.collection, ref.slug, token, options);
1365
+ featured = frisk.featured === true;
1366
+ visFeatured(featured);
1367
+ } catch {
1368
+ featRow.style.borderColor = "#f3522c";
1369
+ } finally {
1370
+ featRow.disabled = false;
1371
+ }
1372
+ });
1373
+ const tagRow = document.createElement("div");
1374
+ tagRow.style.cssText = "display:flex;align-items:center;gap:6px;";
1375
+ const plus = document.createElement("button");
1376
+ plus.type = "button";
1377
+ plus.setAttribute("data-testid", "page-tools-tags-plus");
1378
+ 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
+ plus.textContent = "# Tags +";
1380
+ const felt = document.createElement("input");
1381
+ felt.type = "text";
1382
+ felt.placeholder = "tag1, tag2, tag3";
1383
+ felt.setAttribute("data-testid", "page-tools-tags-input");
1384
+ 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;";
1385
+ const gem = document.createElement("button");
1386
+ gem.type = "button";
1387
+ gem.setAttribute("data-testid", "page-tools-tags-gem");
1388
+ 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;";
1389
+ gem.textContent = "Gem";
1390
+ plus.addEventListener("click", () => {
1391
+ plus.style.display = "none";
1392
+ felt.style.display = "block";
1393
+ gem.style.display = "inline-block";
1394
+ felt.focus();
1395
+ });
1396
+ const gemTags = async () => {
1397
+ const nye = parseTags(felt.value);
1398
+ if (!nye.length) return;
1399
+ gem.disabled = true;
1400
+ gem.textContent = "\u2026";
1401
+ try {
1402
+ const data = await hentDoc(ref.collection, ref.slug, token, options);
1403
+ data.tags = mergeTags(data.tags, nye);
1404
+ await gemDoc(ref.collection, ref.slug, data, token, options);
1405
+ const frisk = await hentDoc(ref.collection, ref.slug, token, options);
1406
+ const holdt = Array.isArray(frisk.tags) && nye.every((t) => frisk.tags.some((e) => String(e).toLowerCase() === t.toLowerCase()));
1407
+ gem.textContent = holdt ? "Gemt \u2713" : "Fejl";
1408
+ if (holdt) {
1409
+ felt.value = "";
1410
+ setTimeout(() => {
1411
+ gem.textContent = "Gem";
1412
+ gem.disabled = false;
1413
+ }, 1200);
1414
+ } else {
1415
+ gem.disabled = false;
1416
+ }
1417
+ } catch {
1418
+ gem.textContent = "Fejl";
1419
+ gem.disabled = false;
1420
+ }
1421
+ };
1422
+ gem.addEventListener("click", () => void gemTags());
1423
+ felt.addEventListener("keydown", (e) => {
1424
+ if (e.key === "Enter") {
1425
+ e.preventDefault();
1426
+ void gemTags();
1427
+ }
1428
+ });
1429
+ tagRow.append(plus, felt, gem);
1430
+ popup.appendChild(tagRow);
1431
+ const lukP\u00E5Klik = (e) => {
1432
+ if (!popup.contains(e.target) && !forankring.contains(e.target)) luk();
1433
+ };
1434
+ const lukP\u00E5Esc = (e) => {
1435
+ if (e.key === "Escape") luk();
1436
+ };
1437
+ const luk = () => {
1438
+ popup.remove();
1439
+ document.removeEventListener("mousedown", lukP\u00E5Klik);
1440
+ document.removeEventListener("keydown", lukP\u00E5Esc);
1441
+ };
1442
+ setTimeout(() => {
1443
+ document.addEventListener("mousedown", lukP\u00E5Klik);
1444
+ document.addEventListener("keydown", lukP\u00E5Esc);
1445
+ }, 0);
1446
+ document.body.appendChild(popup);
1238
1447
  }
1239
1448
  function decodeJwtPayload(token) {
1240
1449
  try {