cms-renderer 0.5.0 → 0.5.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.
@@ -245,13 +245,27 @@ import { notFound } from "next/navigation";
245
245
  import React from "react";
246
246
  import { BlockToolbar } from "./block-toolbar.js";
247
247
  import { ClientEditableBlock } from "./client-editable-block.js";
248
- import { jsx, jsxs } from "react/jsx-runtime";
248
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
249
+ var SKIP_SPAN_PARENTS = /* @__PURE__ */ new Set([
250
+ // SVG text-bearing elements — spans are not valid SVG children
251
+ "text",
252
+ "tspan",
253
+ "textPath",
254
+ "desc",
255
+ // Form elements whose text content must not be interrupted
256
+ "option",
257
+ "optgroup",
258
+ // Non-rendered elements
259
+ "script",
260
+ "style",
261
+ "noscript"
262
+ ]);
249
263
  function walkReactNode(node, visitors, ctx = {}) {
250
264
  const path = ctx.path ?? [];
251
265
  if (node == null || typeof node === "boolean") return node;
252
266
  if (typeof node === "string" || typeof node === "number") {
253
267
  const value = String(node);
254
- return visitors.onText ? visitors.onText({ value, path, parentType: ctx.parentType, key: ctx.key }) : node;
268
+ return visitors.onText ? visitors.onText({ value, path, parentType: ctx.parentType, key: ctx.key, inSvg: ctx.inSvg }) : node;
255
269
  }
256
270
  if (Array.isArray(node)) {
257
271
  return node.map((child, i) => {
@@ -259,7 +273,8 @@ function walkReactNode(node, visitors, ctx = {}) {
259
273
  const result = walkReactNode(child, visitors, {
260
274
  path: [...path, i],
261
275
  parentType: ctx.parentType,
262
- key: childKey
276
+ key: childKey,
277
+ inSvg: ctx.inSvg
263
278
  });
264
279
  if (React.isValidElement(result) && result.key == null) {
265
280
  return React.cloneElement(result, { key: childKey ?? `arr-${path.join("-")}-${i}` });
@@ -270,13 +285,15 @@ function walkReactNode(node, visitors, ctx = {}) {
270
285
  if (React.isValidElement(node)) {
271
286
  const el = node;
272
287
  const elProps = el.props;
288
+ const nextInSvg = ctx.inSvg || el.type === "svg";
273
289
  const hasChildren = elProps && "children" in elProps;
274
290
  const nextChildren = hasChildren ? React.Children.map(elProps.children, (child, i) => {
275
291
  const childKey = child?.key ?? null;
276
292
  const result = walkReactNode(child, visitors, {
277
293
  path: [...path, "children", i],
278
294
  parentType: el.type,
279
- key: childKey
295
+ key: childKey,
296
+ inSvg: nextInSvg
280
297
  });
281
298
  if (React.isValidElement(result) && result.key == null) {
282
299
  return React.cloneElement(result, { key: childKey ?? `child-${path.join("-")}-${i}` });
@@ -309,6 +326,140 @@ function extractContentValues(content, basePath = []) {
309
326
  walk(content, basePath);
310
327
  return map;
311
328
  }
329
+ var CMS_EDITABLE_STYLES = `
330
+ .cms-block-toolbar {
331
+ position: fixed;
332
+ transform: translateX(-50%);
333
+ display: flex;
334
+ gap: 4px;
335
+ background: #1f2937;
336
+ border-radius: 6px;
337
+ padding: 4px;
338
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
339
+ opacity: 0;
340
+ pointer-events: none;
341
+ transition: opacity 0.15s ease;
342
+ z-index: 9999;
343
+ }
344
+ .cms-block-toolbar button {
345
+ display: flex;
346
+ align-items: center;
347
+ justify-content: center;
348
+ width: 28px;
349
+ height: 28px;
350
+ border: none;
351
+ background: transparent;
352
+ color: #9ca3af;
353
+ border-radius: 4px;
354
+ cursor: pointer;
355
+ transition: background 0.15s ease, color 0.15s ease;
356
+ }
357
+ .cms-block-toolbar button:hover {
358
+ background: #374151;
359
+ color: #fff;
360
+ }
361
+ .cms-block-toolbar button.delete:hover {
362
+ background: #dc2626;
363
+ color: #fff;
364
+ }
365
+ .cms-block-toolbar button:disabled {
366
+ opacity: 0.4;
367
+ cursor: not-allowed;
368
+ }
369
+ .cms-block-toolbar button:disabled:hover {
370
+ background: transparent;
371
+ color: #9ca3af;
372
+ }
373
+ .cms-block-toolbar svg {
374
+ width: 16px;
375
+ height: 16px;
376
+ }
377
+ `;
378
+ var CMS_EDITABLE_SCRIPT = `
379
+ (function() {
380
+ if (!window.__cmsEditableInitialized) {
381
+ window.__cmsEditableInitialized = true;
382
+ var _ab = null;
383
+
384
+ function _tb(bid) {
385
+ return document.querySelector('.cms-block-toolbar[data-block-id="' + bid + '"]');
386
+ }
387
+
388
+ function _show(bid, target) {
389
+ var tb = _tb(bid);
390
+ if (tb && target) {
391
+ var r = target.getBoundingClientRect();
392
+ tb.style.left = Math.round(r.left + r.width / 2) + 'px';
393
+ tb.style.top = Math.round(r.bottom + 8) + 'px';
394
+ tb.style.opacity = '1';
395
+ tb.style.pointerEvents = 'auto';
396
+ }
397
+ }
398
+
399
+ function _hide(bid) {
400
+ var tb = _tb(bid);
401
+ if (tb) { tb.style.opacity = '0'; tb.style.pointerEvents = 'none'; }
402
+ }
403
+
404
+ document.addEventListener('mouseover', function(e) {
405
+ if (e.target.closest('.cms-block-toolbar')) return;
406
+ var bel = e.target.closest('[data-cms-block]');
407
+ var bid = bel ? bel.getAttribute('data-block-id') : null;
408
+ if (bid === _ab) return;
409
+ if (_ab) _hide(_ab);
410
+ _ab = bid;
411
+ if (bid) _show(bid, e.target);
412
+ });
413
+
414
+ document.addEventListener('click', function(e) {
415
+ if (e.target.closest('.cms-block-toolbar')) return;
416
+
417
+ var path = e.composedPath ? e.composedPath() : [e.target];
418
+ var et = null;
419
+ for (var i = 0; i < path.length; i++) {
420
+ var n = path[i];
421
+ if (n.nodeType === 1 && n.hasAttribute && n.hasAttribute('data-cms-editable')) { et = n; break; }
422
+ }
423
+ if (!et) et = e.target.closest && e.target.closest('[data-cms-editable]');
424
+
425
+ if (et) {
426
+ if (window.parent && window.parent !== window) {
427
+ window.parent.postMessage({
428
+ type: 'cms-editable-click',
429
+ blockId: et.getAttribute('data-block-id'),
430
+ blockType: et.getAttribute('data-block-type'),
431
+ contentPath: et.getAttribute('data-content-path')
432
+ }, '*');
433
+ }
434
+ return;
435
+ }
436
+
437
+ var bt = e.target.closest && e.target.closest('[data-cms-block]');
438
+ if (bt) {
439
+ if (window.parent && window.parent !== window) {
440
+ window.parent.postMessage({
441
+ type: 'cms-editable-click',
442
+ blockId: bt.getAttribute('data-block-id'),
443
+ blockType: bt.getAttribute('data-block-type'),
444
+ contentPath: null
445
+ }, '*');
446
+ }
447
+ }
448
+ });
449
+ }
450
+ })();
451
+ `;
452
+ function CmsEditableInit() {
453
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
454
+ /* @__PURE__ */ jsx("style", { children: CMS_EDITABLE_STYLES }),
455
+ /* @__PURE__ */ jsx(
456
+ "script",
457
+ {
458
+ dangerouslySetInnerHTML: { __html: CMS_EDITABLE_SCRIPT }
459
+ }
460
+ )
461
+ ] });
462
+ }
312
463
  function pathMatchesPattern(path, pattern) {
313
464
  const pathSegs = path.split("/").filter(Boolean);
314
465
  const patternSegs = pattern.split("/").filter(Boolean);
@@ -398,7 +549,11 @@ function BlockRenderer({
398
549
  if (isWalkable) {
399
550
  const usedPaths = /* @__PURE__ */ new Set();
400
551
  renderedComponent = walkReactNode(renderedTree, {
401
- onText: ({ value, key, path: path2 }) => {
552
+ onText: ({ value, key, path: path2, inSvg, parentType }) => {
553
+ if (inSvg) return value;
554
+ if (parentType && typeof parentType === "string" && SKIP_SPAN_PARENTS.has(parentType)) {
555
+ return value;
556
+ }
402
557
  const matches = contentValueMap.get(value);
403
558
  if (!matches || matches.length === 0) return value;
404
559
  const match = matches.find((m) => !usedPaths.has(m.contentPath)) ?? matches[0];
@@ -412,6 +567,7 @@ function BlockRenderer({
412
567
  "data-block-id": block.id,
413
568
  "data-block-type": block.type,
414
569
  "data-content-path": match.contentPath,
570
+ style: { display: "contents" },
415
571
  children: value
416
572
  },
417
573
  spanKey
@@ -431,143 +587,6 @@ function BlockRenderer({
431
587
  "data-block-type": block.type,
432
588
  style: { display: "contents" },
433
589
  children: [
434
- /* @__PURE__ */ jsx("style", { children: `
435
- [data-cms-block] {
436
- display: contents;
437
- }
438
- [data-cms-editable] {
439
- display: contents;
440
- cursor: pointer;
441
- }
442
- .cms-block-toolbar {
443
- position: fixed;
444
- transform: translateX(-50%);
445
- display: flex;
446
- gap: 4px;
447
- background: #1f2937;
448
- border-radius: 6px;
449
- padding: 4px;
450
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
451
- opacity: 0;
452
- pointer-events: none;
453
- transition: opacity 0.15s ease;
454
- z-index: 9999;
455
- }
456
- .cms-block-toolbar button {
457
- display: flex;
458
- align-items: center;
459
- justify-content: center;
460
- width: 28px;
461
- height: 28px;
462
- border: none;
463
- background: transparent;
464
- color: #9ca3af;
465
- border-radius: 4px;
466
- cursor: pointer;
467
- transition: background 0.15s ease, color 0.15s ease;
468
- }
469
- .cms-block-toolbar button:hover {
470
- background: #374151;
471
- color: #fff;
472
- }
473
- .cms-block-toolbar button.delete:hover {
474
- background: #dc2626;
475
- color: #fff;
476
- }
477
- .cms-block-toolbar button:disabled {
478
- opacity: 0.4;
479
- cursor: not-allowed;
480
- }
481
- .cms-block-toolbar button:disabled:hover {
482
- background: transparent;
483
- color: #9ca3af;
484
- }
485
- .cms-block-toolbar svg {
486
- width: 16px;
487
- height: 16px;
488
- }
489
- ` }),
490
- /* @__PURE__ */ jsx(
491
- "script",
492
- {
493
- dangerouslySetInnerHTML: {
494
- __html: `
495
- (function() {
496
- if (!window.__cmsEditableInitialized) {
497
- window.__cmsEditableInitialized = true;
498
- var _ab = null;
499
-
500
- function _tb(bid) {
501
- return document.querySelector('.cms-block-toolbar[data-block-id="' + bid + '"]');
502
- }
503
-
504
- function _show(bid, target) {
505
- var tb = _tb(bid);
506
- if (tb && target) {
507
- var r = target.getBoundingClientRect();
508
- tb.style.left = Math.round(r.left + r.width / 2) + 'px';
509
- tb.style.top = Math.round(r.bottom + 8) + 'px';
510
- tb.style.opacity = '1';
511
- tb.style.pointerEvents = 'auto';
512
- }
513
- }
514
-
515
- function _hide(bid) {
516
- var tb = _tb(bid);
517
- if (tb) { tb.style.opacity = '0'; tb.style.pointerEvents = 'none'; }
518
- }
519
-
520
- document.addEventListener('mouseover', function(e) {
521
- if (e.target.closest('.cms-block-toolbar')) return;
522
- var bel = e.target.closest('[data-cms-block]');
523
- var bid = bel ? bel.getAttribute('data-block-id') : null;
524
- if (bid === _ab) return;
525
- if (_ab) _hide(_ab);
526
- _ab = bid;
527
- if (bid) _show(bid, e.target);
528
- });
529
-
530
- document.addEventListener('click', function(e) {
531
- if (e.target.closest('.cms-block-toolbar')) return;
532
-
533
- var path = e.composedPath ? e.composedPath() : [e.target];
534
- var et = null;
535
- for (var i = 0; i < path.length; i++) {
536
- var n = path[i];
537
- if (n.nodeType === 1 && n.hasAttribute && n.hasAttribute('data-cms-editable')) { et = n; break; }
538
- }
539
- if (!et) et = e.target.closest && e.target.closest('[data-cms-editable]');
540
-
541
- if (et) {
542
- if (window.parent && window.parent !== window) {
543
- window.parent.postMessage({
544
- type: 'cms-editable-click',
545
- blockId: et.getAttribute('data-block-id'),
546
- blockType: et.getAttribute('data-block-type'),
547
- contentPath: et.getAttribute('data-content-path')
548
- }, '*');
549
- }
550
- return;
551
- }
552
-
553
- var bt = e.target.closest && e.target.closest('[data-cms-block]');
554
- if (bt) {
555
- if (window.parent && window.parent !== window) {
556
- window.parent.postMessage({
557
- type: 'cms-editable-click',
558
- blockId: bt.getAttribute('data-block-id'),
559
- blockType: bt.getAttribute('data-block-type'),
560
- contentPath: null
561
- }, '*');
562
- }
563
- }
564
- });
565
- }
566
- })();
567
- `
568
- }
569
- }
570
- ),
571
590
  needsClientSideSpans && contentEntries.length > 0 ? /* @__PURE__ */ jsx(
572
591
  ClientEditableBlock,
573
592
  {
@@ -624,7 +643,7 @@ function getCmsClient(options) {
624
643
  }
625
644
 
626
645
  // lib/renderer.tsx
627
- import { jsx as jsx2 } from "react/jsx-runtime";
646
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
628
647
  function getWebsiteId(providedWebsiteId) {
629
648
  const websiteId = providedWebsiteId ?? process.env.NEXT_PUBLIC_WEBSITE_ID ?? process.env.WEBSITE_ID ?? process.env.CMS_WEBSITE_ID;
630
649
  if (!websiteId) {
@@ -743,17 +762,20 @@ async function ParametricRoutePage({
743
762
  }
744
763
  ])
745
764
  ) : void 0;
746
- return /* @__PURE__ */ jsx2("main", { children: blocks.map((block) => /* @__PURE__ */ jsx2(
747
- BlockRenderer,
748
- {
749
- block,
750
- registry: registry ?? {},
751
- disableEditable: !editMode,
752
- routeParams,
753
- path
754
- },
755
- block.id
756
- )) });
765
+ return /* @__PURE__ */ jsxs2("main", { children: [
766
+ editMode && /* @__PURE__ */ jsx2(CmsEditableInit, {}),
767
+ blocks.map((block) => /* @__PURE__ */ jsx2(
768
+ BlockRenderer,
769
+ {
770
+ block,
771
+ registry: registry ?? {},
772
+ disableEditable: !editMode,
773
+ routeParams,
774
+ path
775
+ },
776
+ block.id
777
+ ))
778
+ ] });
757
779
  } catch (error) {
758
780
  console.error(`Route fetch error for path: ${path}`, error);
759
781
  const errorCode = error instanceof Error && "data" in error ? error.data?.code : error instanceof Error && "code" in error ? error.code : void 0;