@cmssy/react 9.1.1 → 9.3.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.
@@ -0,0 +1,90 @@
1
+ "use client";
2
+ 'use strict';
3
+
4
+ var react = require('react');
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+
7
+ // src/components/block-error.ts
8
+ function blockErrorMessage(err) {
9
+ return err instanceof Error ? err.message : String(err);
10
+ }
11
+ var SOURCE_LABELS = {
12
+ loader: "loader failed",
13
+ render: "render failed",
14
+ unregistered: "type not registered"
15
+ };
16
+ var cardStyle = {
17
+ boxSizing: "border-box",
18
+ margin: "8px 0",
19
+ padding: "12px 16px",
20
+ border: "1px dashed #ef4444",
21
+ borderRadius: "8px",
22
+ background: "rgba(239, 68, 68, 0.08)",
23
+ color: "#ef4444",
24
+ fontFamily: "ui-monospace, SFMono-Regular, Menlo, Consolas, 'Liberation Mono', monospace",
25
+ fontSize: "13px",
26
+ lineHeight: 1.5,
27
+ textAlign: "left"
28
+ };
29
+ var headingStyle = {
30
+ fontWeight: 700,
31
+ marginBottom: "4px"
32
+ };
33
+ var metaStyle = {
34
+ opacity: 0.8,
35
+ marginBottom: "4px"
36
+ };
37
+ var messageStyle = {
38
+ whiteSpace: "pre-wrap",
39
+ overflowWrap: "anywhere"
40
+ };
41
+ function BlockErrorCard({
42
+ blockType,
43
+ blockId,
44
+ error
45
+ }) {
46
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "alert", "data-cmssy-block-error": error.source, style: cardStyle, children: [
47
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: headingStyle, children: [
48
+ 'Block "',
49
+ blockType,
50
+ '" - ',
51
+ SOURCE_LABELS[error.source]
52
+ ] }),
53
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: metaStyle, children: [
54
+ "id: ",
55
+ blockId
56
+ ] }),
57
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: messageStyle, children: error.message })
58
+ ] });
59
+ }
60
+ var BlockErrorBoundary = class extends react.Component {
61
+ state = { failed: false, error: void 0 };
62
+ static getDerivedStateFromError(error) {
63
+ return { failed: true, error };
64
+ }
65
+ componentDidCatch(error) {
66
+ if (typeof console !== "undefined") {
67
+ console.error(
68
+ `[cmssy] block "${this.props.blockType}" (${this.props.blockId}) failed to render`,
69
+ error
70
+ );
71
+ }
72
+ }
73
+ render() {
74
+ if (!this.state.failed) return this.props.children;
75
+ if (!this.props.editMode) return null;
76
+ return /* @__PURE__ */ jsxRuntime.jsx(
77
+ BlockErrorCard,
78
+ {
79
+ blockType: this.props.blockType,
80
+ blockId: this.props.blockId,
81
+ error: {
82
+ source: "render",
83
+ message: blockErrorMessage(this.state.error)
84
+ }
85
+ }
86
+ );
87
+ }
88
+ };
89
+
90
+ exports.BlockErrorBoundary = BlockErrorBoundary;
@@ -0,0 +1,22 @@
1
+ import * as react from 'react';
2
+ import { Component, ReactNode } from 'react';
3
+ import * as react_jsx_runtime from 'react/jsx-runtime';
4
+
5
+ interface BlockErrorBoundaryProps {
6
+ blockType: string;
7
+ blockId: string;
8
+ editMode?: boolean;
9
+ children?: ReactNode;
10
+ }
11
+ interface BlockErrorBoundaryState {
12
+ failed: boolean;
13
+ error: unknown;
14
+ }
15
+ declare class BlockErrorBoundary extends Component<BlockErrorBoundaryProps, BlockErrorBoundaryState> {
16
+ state: BlockErrorBoundaryState;
17
+ static getDerivedStateFromError(error: unknown): BlockErrorBoundaryState;
18
+ componentDidCatch(error: unknown): void;
19
+ render(): string | number | bigint | boolean | react_jsx_runtime.JSX.Element | Iterable<ReactNode> | Promise<string | number | bigint | boolean | react.ReactPortal | react.ReactElement<unknown, string | react.JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | null | undefined;
20
+ }
21
+
22
+ export { BlockErrorBoundary, type BlockErrorBoundaryProps };
@@ -0,0 +1,22 @@
1
+ import * as react from 'react';
2
+ import { Component, ReactNode } from 'react';
3
+ import * as react_jsx_runtime from 'react/jsx-runtime';
4
+
5
+ interface BlockErrorBoundaryProps {
6
+ blockType: string;
7
+ blockId: string;
8
+ editMode?: boolean;
9
+ children?: ReactNode;
10
+ }
11
+ interface BlockErrorBoundaryState {
12
+ failed: boolean;
13
+ error: unknown;
14
+ }
15
+ declare class BlockErrorBoundary extends Component<BlockErrorBoundaryProps, BlockErrorBoundaryState> {
16
+ state: BlockErrorBoundaryState;
17
+ static getDerivedStateFromError(error: unknown): BlockErrorBoundaryState;
18
+ componentDidCatch(error: unknown): void;
19
+ render(): string | number | bigint | boolean | react_jsx_runtime.JSX.Element | Iterable<ReactNode> | Promise<string | number | bigint | boolean | react.ReactPortal | react.ReactElement<unknown, string | react.JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | null | undefined;
20
+ }
21
+
22
+ export { BlockErrorBoundary, type BlockErrorBoundaryProps };
@@ -0,0 +1,88 @@
1
+ "use client";
2
+ import { Component } from 'react';
3
+ import { jsx, jsxs } from 'react/jsx-runtime';
4
+
5
+ // src/components/block-error.ts
6
+ function blockErrorMessage(err) {
7
+ return err instanceof Error ? err.message : String(err);
8
+ }
9
+ var SOURCE_LABELS = {
10
+ loader: "loader failed",
11
+ render: "render failed",
12
+ unregistered: "type not registered"
13
+ };
14
+ var cardStyle = {
15
+ boxSizing: "border-box",
16
+ margin: "8px 0",
17
+ padding: "12px 16px",
18
+ border: "1px dashed #ef4444",
19
+ borderRadius: "8px",
20
+ background: "rgba(239, 68, 68, 0.08)",
21
+ color: "#ef4444",
22
+ fontFamily: "ui-monospace, SFMono-Regular, Menlo, Consolas, 'Liberation Mono', monospace",
23
+ fontSize: "13px",
24
+ lineHeight: 1.5,
25
+ textAlign: "left"
26
+ };
27
+ var headingStyle = {
28
+ fontWeight: 700,
29
+ marginBottom: "4px"
30
+ };
31
+ var metaStyle = {
32
+ opacity: 0.8,
33
+ marginBottom: "4px"
34
+ };
35
+ var messageStyle = {
36
+ whiteSpace: "pre-wrap",
37
+ overflowWrap: "anywhere"
38
+ };
39
+ function BlockErrorCard({
40
+ blockType,
41
+ blockId,
42
+ error
43
+ }) {
44
+ return /* @__PURE__ */ jsxs("div", { role: "alert", "data-cmssy-block-error": error.source, style: cardStyle, children: [
45
+ /* @__PURE__ */ jsxs("div", { style: headingStyle, children: [
46
+ 'Block "',
47
+ blockType,
48
+ '" - ',
49
+ SOURCE_LABELS[error.source]
50
+ ] }),
51
+ /* @__PURE__ */ jsxs("div", { style: metaStyle, children: [
52
+ "id: ",
53
+ blockId
54
+ ] }),
55
+ /* @__PURE__ */ jsx("div", { style: messageStyle, children: error.message })
56
+ ] });
57
+ }
58
+ var BlockErrorBoundary = class extends Component {
59
+ state = { failed: false, error: void 0 };
60
+ static getDerivedStateFromError(error) {
61
+ return { failed: true, error };
62
+ }
63
+ componentDidCatch(error) {
64
+ if (typeof console !== "undefined") {
65
+ console.error(
66
+ `[cmssy] block "${this.props.blockType}" (${this.props.blockId}) failed to render`,
67
+ error
68
+ );
69
+ }
70
+ }
71
+ render() {
72
+ if (!this.state.failed) return this.props.children;
73
+ if (!this.props.editMode) return null;
74
+ return /* @__PURE__ */ jsx(
75
+ BlockErrorCard,
76
+ {
77
+ blockType: this.props.blockType,
78
+ blockId: this.props.blockId,
79
+ error: {
80
+ source: "render",
81
+ message: blockErrorMessage(this.state.error)
82
+ }
83
+ }
84
+ );
85
+ }
86
+ };
87
+
88
+ export { BlockErrorBoundary };
package/dist/client.cjs CHANGED
@@ -3,6 +3,7 @@
3
3
 
4
4
  var react = require('react');
5
5
  var core = require('@cmssy/core');
6
+ var blockErrorBoundary = require('@cmssy/react/block-error-boundary');
6
7
  var jsxRuntime = require('react/jsx-runtime');
7
8
 
8
9
  // src/registry.ts
@@ -421,13 +422,85 @@ function useDragAgent(config) {
421
422
  }, [config.editorOrigin]);
422
423
  return { dropY };
423
424
  }
425
+
426
+ // src/components/block-error.ts
427
+ var BLOCK_ERROR_KEY = "__cmssyBlockError";
428
+ var SOURCES = [
429
+ "loader",
430
+ "render",
431
+ "unregistered"
432
+ ];
433
+ function unregisteredBlockError(type) {
434
+ return {
435
+ source: "unregistered",
436
+ message: `Block type "${type}" is not registered in this site's blocks array.`
437
+ };
438
+ }
439
+ function readBlockError(value) {
440
+ if (typeof value !== "object" || value === null) return void 0;
441
+ const marked = value[BLOCK_ERROR_KEY];
442
+ if (typeof marked !== "object" || marked === null) return void 0;
443
+ const { message, source } = marked;
444
+ if (typeof message !== "string") return void 0;
445
+ if (!SOURCES.includes(source)) return void 0;
446
+ return { message, source };
447
+ }
448
+ var SOURCE_LABELS = {
449
+ loader: "loader failed",
450
+ render: "render failed",
451
+ unregistered: "type not registered"
452
+ };
453
+ var cardStyle = {
454
+ boxSizing: "border-box",
455
+ margin: "8px 0",
456
+ padding: "12px 16px",
457
+ border: "1px dashed #ef4444",
458
+ borderRadius: "8px",
459
+ background: "rgba(239, 68, 68, 0.08)",
460
+ color: "#ef4444",
461
+ fontFamily: "ui-monospace, SFMono-Regular, Menlo, Consolas, 'Liberation Mono', monospace",
462
+ fontSize: "13px",
463
+ lineHeight: 1.5,
464
+ textAlign: "left"
465
+ };
466
+ var headingStyle = {
467
+ fontWeight: 700,
468
+ marginBottom: "4px"
469
+ };
470
+ var metaStyle = {
471
+ opacity: 0.8,
472
+ marginBottom: "4px"
473
+ };
474
+ var messageStyle = {
475
+ whiteSpace: "pre-wrap",
476
+ overflowWrap: "anywhere"
477
+ };
478
+ function BlockErrorCard({
479
+ blockType,
480
+ blockId,
481
+ error
482
+ }) {
483
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "alert", "data-cmssy-block-error": error.source, style: cardStyle, children: [
484
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: headingStyle, children: [
485
+ 'Block "',
486
+ blockType,
487
+ '" - ',
488
+ SOURCE_LABELS[error.source]
489
+ ] }),
490
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: metaStyle, children: [
491
+ "id: ",
492
+ blockId
493
+ ] }),
494
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: messageStyle, children: error.message })
495
+ ] });
496
+ }
424
497
  var WARN_CAP = 256;
425
498
  var warned = /* @__PURE__ */ new Set();
426
499
  function UnknownBlock({ type }) {
427
500
  if (typeof window !== "undefined" && !warned.has(type)) {
428
501
  if (warned.size >= WARN_CAP) warned.clear();
429
502
  warned.add(type);
430
- console.warn(`[cmssy] no component registered for block type "${type}"`);
503
+ console.error(`[cmssy] no component registered for block type "${type}"`);
431
504
  }
432
505
  return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-cmssy-unknown-block": type });
433
506
  }
@@ -440,32 +513,71 @@ function CmssyBlock({
440
513
  patchedStyle,
441
514
  patchedAdvanced,
442
515
  editable,
516
+ editMode,
443
517
  layoutPosition,
444
518
  context,
445
519
  data
446
520
  }) {
521
+ const inEditMode = editMode ?? editable ?? false;
447
522
  const Component = Object.hasOwn(blockMap, block.type) ? blockMap[block.type] : void 0;
448
- const base = core.getBlockContentForLanguage(block.content, locale, defaultLocale);
449
- const content = patchedContent ? { ...base, ...patchedContent } : base;
450
- const style = patchedStyle ? { ...core.asBucket(block.style), ...patchedStyle } : core.asBucket(block.style);
451
- const advanced = patchedAdvanced ? { ...core.asBucket(block.advanced), ...patchedAdvanced } : core.asBucket(block.advanced);
452
- return /* @__PURE__ */ jsxRuntime.jsx(
523
+ const blockError = readBlockError(data);
524
+ const wrap = (children, hidden = false) => /* @__PURE__ */ jsxRuntime.jsx(
453
525
  "div",
454
526
  {
455
527
  "data-block-id": block.id,
456
528
  "data-block-type": block.type,
457
529
  "data-layout-position": layoutPosition,
458
530
  draggable: editable || void 0,
459
- style: Component ? void 0 : { display: "none" },
460
- children: Component ? react.createElement(Component, {
461
- content,
462
- style,
463
- advanced,
464
- context,
465
- data
466
- }) : /* @__PURE__ */ jsxRuntime.jsx(UnknownBlock, { type: block.type })
531
+ style: hidden ? { display: "none" } : void 0,
532
+ children
467
533
  }
468
534
  );
535
+ if (!Component) {
536
+ return inEditMode ? wrap(
537
+ /* @__PURE__ */ jsxRuntime.jsx(
538
+ BlockErrorCard,
539
+ {
540
+ blockType: block.type,
541
+ blockId: block.id,
542
+ error: unregisteredBlockError(block.type)
543
+ }
544
+ )
545
+ ) : wrap(/* @__PURE__ */ jsxRuntime.jsx(UnknownBlock, { type: block.type }), true);
546
+ }
547
+ if (blockError) {
548
+ if (!inEditMode) return null;
549
+ return wrap(
550
+ /* @__PURE__ */ jsxRuntime.jsx(
551
+ BlockErrorCard,
552
+ {
553
+ blockType: block.type,
554
+ blockId: block.id,
555
+ error: blockError
556
+ }
557
+ )
558
+ );
559
+ }
560
+ const base = core.getBlockContentForLanguage(block.content, locale, defaultLocale);
561
+ const content = patchedContent ? { ...base, ...patchedContent } : base;
562
+ const style = patchedStyle ? { ...core.asBucket(block.style), ...patchedStyle } : core.asBucket(block.style);
563
+ const advanced = patchedAdvanced ? { ...core.asBucket(block.advanced), ...patchedAdvanced } : core.asBucket(block.advanced);
564
+ return wrap(
565
+ /* @__PURE__ */ jsxRuntime.jsx(
566
+ blockErrorBoundary.BlockErrorBoundary,
567
+ {
568
+ blockType: block.type,
569
+ blockId: block.id,
570
+ editMode: inEditMode,
571
+ children: react.createElement(Component, {
572
+ content,
573
+ style,
574
+ advanced,
575
+ context,
576
+ data
577
+ })
578
+ }
579
+ )
580
+ );
469
581
  }
470
582
  function CmssyEditablePage({
471
583
  page,
@@ -674,6 +786,7 @@ function CmssyEditableLayout({
674
786
  defaultLocale,
675
787
  blockMap,
676
788
  patchedContent: patches[block.id],
789
+ editMode: true,
677
790
  layoutPosition: position,
678
791
  context,
679
792
  data: data?.[block.id]
package/dist/client.js CHANGED
@@ -2,6 +2,7 @@
2
2
  import { createContext, useState, useRef, useEffect, useMemo, useContext, useCallback, createElement } from 'react';
3
3
  import { fields, resolveInitialTarget, buildBlockContext, postToEditor, PROTOCOL_VERSION, parseEditorMessage, getBlockContentForLanguage, asBucket, toMinorUnits, formatPrice } from '@cmssy/core';
4
4
  export { formatPrice, fractionDigits, fromMinorUnits, toMinorUnits } from '@cmssy/core';
5
+ import { BlockErrorBoundary } from '@cmssy/react/block-error-boundary';
5
6
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
6
7
 
7
8
  // src/registry.ts
@@ -420,13 +421,85 @@ function useDragAgent(config) {
420
421
  }, [config.editorOrigin]);
421
422
  return { dropY };
422
423
  }
424
+
425
+ // src/components/block-error.ts
426
+ var BLOCK_ERROR_KEY = "__cmssyBlockError";
427
+ var SOURCES = [
428
+ "loader",
429
+ "render",
430
+ "unregistered"
431
+ ];
432
+ function unregisteredBlockError(type) {
433
+ return {
434
+ source: "unregistered",
435
+ message: `Block type "${type}" is not registered in this site's blocks array.`
436
+ };
437
+ }
438
+ function readBlockError(value) {
439
+ if (typeof value !== "object" || value === null) return void 0;
440
+ const marked = value[BLOCK_ERROR_KEY];
441
+ if (typeof marked !== "object" || marked === null) return void 0;
442
+ const { message, source } = marked;
443
+ if (typeof message !== "string") return void 0;
444
+ if (!SOURCES.includes(source)) return void 0;
445
+ return { message, source };
446
+ }
447
+ var SOURCE_LABELS = {
448
+ loader: "loader failed",
449
+ render: "render failed",
450
+ unregistered: "type not registered"
451
+ };
452
+ var cardStyle = {
453
+ boxSizing: "border-box",
454
+ margin: "8px 0",
455
+ padding: "12px 16px",
456
+ border: "1px dashed #ef4444",
457
+ borderRadius: "8px",
458
+ background: "rgba(239, 68, 68, 0.08)",
459
+ color: "#ef4444",
460
+ fontFamily: "ui-monospace, SFMono-Regular, Menlo, Consolas, 'Liberation Mono', monospace",
461
+ fontSize: "13px",
462
+ lineHeight: 1.5,
463
+ textAlign: "left"
464
+ };
465
+ var headingStyle = {
466
+ fontWeight: 700,
467
+ marginBottom: "4px"
468
+ };
469
+ var metaStyle = {
470
+ opacity: 0.8,
471
+ marginBottom: "4px"
472
+ };
473
+ var messageStyle = {
474
+ whiteSpace: "pre-wrap",
475
+ overflowWrap: "anywhere"
476
+ };
477
+ function BlockErrorCard({
478
+ blockType,
479
+ blockId,
480
+ error
481
+ }) {
482
+ return /* @__PURE__ */ jsxs("div", { role: "alert", "data-cmssy-block-error": error.source, style: cardStyle, children: [
483
+ /* @__PURE__ */ jsxs("div", { style: headingStyle, children: [
484
+ 'Block "',
485
+ blockType,
486
+ '" - ',
487
+ SOURCE_LABELS[error.source]
488
+ ] }),
489
+ /* @__PURE__ */ jsxs("div", { style: metaStyle, children: [
490
+ "id: ",
491
+ blockId
492
+ ] }),
493
+ /* @__PURE__ */ jsx("div", { style: messageStyle, children: error.message })
494
+ ] });
495
+ }
423
496
  var WARN_CAP = 256;
424
497
  var warned = /* @__PURE__ */ new Set();
425
498
  function UnknownBlock({ type }) {
426
499
  if (typeof window !== "undefined" && !warned.has(type)) {
427
500
  if (warned.size >= WARN_CAP) warned.clear();
428
501
  warned.add(type);
429
- console.warn(`[cmssy] no component registered for block type "${type}"`);
502
+ console.error(`[cmssy] no component registered for block type "${type}"`);
430
503
  }
431
504
  return /* @__PURE__ */ jsx("div", { "data-cmssy-unknown-block": type });
432
505
  }
@@ -439,32 +512,71 @@ function CmssyBlock({
439
512
  patchedStyle,
440
513
  patchedAdvanced,
441
514
  editable,
515
+ editMode,
442
516
  layoutPosition,
443
517
  context,
444
518
  data
445
519
  }) {
520
+ const inEditMode = editMode ?? editable ?? false;
446
521
  const Component = Object.hasOwn(blockMap, block.type) ? blockMap[block.type] : void 0;
447
- const base = getBlockContentForLanguage(block.content, locale, defaultLocale);
448
- const content = patchedContent ? { ...base, ...patchedContent } : base;
449
- const style = patchedStyle ? { ...asBucket(block.style), ...patchedStyle } : asBucket(block.style);
450
- const advanced = patchedAdvanced ? { ...asBucket(block.advanced), ...patchedAdvanced } : asBucket(block.advanced);
451
- return /* @__PURE__ */ jsx(
522
+ const blockError = readBlockError(data);
523
+ const wrap = (children, hidden = false) => /* @__PURE__ */ jsx(
452
524
  "div",
453
525
  {
454
526
  "data-block-id": block.id,
455
527
  "data-block-type": block.type,
456
528
  "data-layout-position": layoutPosition,
457
529
  draggable: editable || void 0,
458
- style: Component ? void 0 : { display: "none" },
459
- children: Component ? createElement(Component, {
460
- content,
461
- style,
462
- advanced,
463
- context,
464
- data
465
- }) : /* @__PURE__ */ jsx(UnknownBlock, { type: block.type })
530
+ style: hidden ? { display: "none" } : void 0,
531
+ children
466
532
  }
467
533
  );
534
+ if (!Component) {
535
+ return inEditMode ? wrap(
536
+ /* @__PURE__ */ jsx(
537
+ BlockErrorCard,
538
+ {
539
+ blockType: block.type,
540
+ blockId: block.id,
541
+ error: unregisteredBlockError(block.type)
542
+ }
543
+ )
544
+ ) : wrap(/* @__PURE__ */ jsx(UnknownBlock, { type: block.type }), true);
545
+ }
546
+ if (blockError) {
547
+ if (!inEditMode) return null;
548
+ return wrap(
549
+ /* @__PURE__ */ jsx(
550
+ BlockErrorCard,
551
+ {
552
+ blockType: block.type,
553
+ blockId: block.id,
554
+ error: blockError
555
+ }
556
+ )
557
+ );
558
+ }
559
+ const base = getBlockContentForLanguage(block.content, locale, defaultLocale);
560
+ const content = patchedContent ? { ...base, ...patchedContent } : base;
561
+ const style = patchedStyle ? { ...asBucket(block.style), ...patchedStyle } : asBucket(block.style);
562
+ const advanced = patchedAdvanced ? { ...asBucket(block.advanced), ...patchedAdvanced } : asBucket(block.advanced);
563
+ return wrap(
564
+ /* @__PURE__ */ jsx(
565
+ BlockErrorBoundary,
566
+ {
567
+ blockType: block.type,
568
+ blockId: block.id,
569
+ editMode: inEditMode,
570
+ children: createElement(Component, {
571
+ content,
572
+ style,
573
+ advanced,
574
+ context,
575
+ data
576
+ })
577
+ }
578
+ )
579
+ );
468
580
  }
469
581
  function CmssyEditablePage({
470
582
  page,
@@ -673,6 +785,7 @@ function CmssyEditableLayout({
673
785
  defaultLocale,
674
786
  blockMap,
675
787
  patchedContent: patches[block.id],
788
+ editMode: true,
676
789
  layoutPosition: position,
677
790
  context,
678
791
  data: data?.[block.id]