@nascentdigital/funnel-editor 4.5.7 → 4.6.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.
Files changed (135) hide show
  1. package/dist/collection/hooks/CollectionController.d.ts +1 -1
  2. package/dist/core/components/ColorModeToggle.js +6 -4
  3. package/dist/core/components/Header.js +3 -2
  4. package/dist/core/components/UserEditor.d.ts +2 -2
  5. package/dist/core/components/feedback/Badge.d.ts +2 -1
  6. package/dist/core/components/feedback/Badge.js +4 -3
  7. package/dist/core/components/fields/DynamicPagesEditor.js +7 -6
  8. package/dist/core/components/fields/EmptyFieldCard.js +4 -4
  9. package/dist/core/components/fields/FieldCardLayout.js +5 -5
  10. package/dist/core/components/fields/FieldHeader.js +3 -2
  11. package/dist/core/components/fields/FieldsPanel.js +4 -4
  12. package/dist/core/components/fields/FormEditor.js +5 -4
  13. package/dist/core/components/fields/asset/AssetEditor.d.ts +3 -2
  14. package/dist/core/components/fields/asset/AssetEditor.js +1 -1
  15. package/dist/core/components/fields/asset/AssetUploader.d.ts +3 -2
  16. package/dist/core/components/fields/asset/AssetUploader.js +1 -1
  17. package/dist/core/components/fields/collection/CollectionCard.js +1 -11
  18. package/dist/core/components/fields/collection/CollectionPicker.js +3 -2
  19. package/dist/core/components/fields/collection/previews/index.d.ts +0 -1
  20. package/dist/core/components/fields/collection/previews/index.js +0 -1
  21. package/dist/core/components/fields/embed/EmbeddedVideoEditor.d.ts +2 -2
  22. package/dist/core/components/fields/factories/AssetFactory.d.ts +1 -1
  23. package/dist/core/components/fields/factories/CollectionFactory.d.ts +1 -1
  24. package/dist/core/components/fields/factories/EmbeddedVideoFactory.d.ts +2 -2
  25. package/dist/core/components/fields/factories/FieldValueFactory.d.ts +1 -1
  26. package/dist/core/components/fields/factories/FieldValueFactory.js +1 -1
  27. package/dist/core/components/fields/factories/LinkFactory.d.ts +2 -2
  28. package/dist/core/components/fields/factories/TextFactory.d.ts +2 -2
  29. package/dist/core/components/fields/form/FormFieldEditor.d.ts +3 -2
  30. package/dist/core/components/fields/index.d.ts +1 -0
  31. package/dist/core/components/fields/index.js +1 -0
  32. package/dist/core/components/fields/link/LinkEditor.d.ts +2 -2
  33. package/dist/core/components/fields/link/LinkEditor.js +4 -0
  34. package/dist/core/components/fields/list/CreateFieldButton.d.ts +1 -1
  35. package/dist/core/components/fields/list/CreateFieldButton.js +4 -5
  36. package/dist/core/components/fields/list/CreateFieldCard.js +4 -4
  37. package/dist/core/components/fields/page/PagePicker.js +4 -2
  38. package/dist/core/components/fields/richtext/FloatingToolbarPlugin.js +3 -2
  39. package/dist/core/components/fields/richtext/LinkEditor.js +17 -4
  40. package/dist/core/components/fields/richtext/LinkPopup.js +11 -7
  41. package/dist/core/components/interaction/AvatarButton.d.ts +2 -1
  42. package/dist/core/components/interaction/AvatarButton.js +3 -2
  43. package/dist/core/components/interaction/Button.d.ts +2 -0
  44. package/dist/core/components/interaction/Button.js +3 -2
  45. package/dist/core/components/interaction/Combobox.js +2 -2
  46. package/dist/core/components/interaction/IconButton.d.ts +2 -0
  47. package/dist/core/components/interaction/IconButton.js +3 -2
  48. package/dist/core/components/interaction/IconLink.d.ts +1 -1
  49. package/dist/core/components/interaction/MiniEditor.d.ts +3 -2
  50. package/dist/core/components/interaction/MiniEditor.js +7 -5
  51. package/dist/core/components/interaction/Popup.d.ts +20 -15
  52. package/dist/core/components/interaction/Popup.js +63 -66
  53. package/dist/core/components/interaction/QuickInput.js +2 -0
  54. package/dist/core/components/interaction/QuickPicker.js +4 -4
  55. package/dist/core/components/interaction/Tabs.d.ts +5 -1
  56. package/dist/core/components/interaction/Tabs.js +18 -8
  57. package/dist/core/components/interaction/menu/useMenuController.d.ts +2 -2
  58. package/dist/core/components/interaction/table/TableItem.d.ts +2 -1
  59. package/dist/core/components/interaction/table/TableItem.js +5 -3
  60. package/dist/core/components/layout/InfoPanel.js +4 -2
  61. package/dist/core/components/visuals/Icon.d.ts +1 -1
  62. package/dist/core/components/visuals/Icon.js +6 -0
  63. package/dist/core/components/visuals/index.d.ts +0 -1
  64. package/dist/core/components/visuals/index.js +0 -1
  65. package/dist/core/domain/Block.d.ts +4 -0
  66. package/dist/core/domain/Block.js +7 -0
  67. package/dist/core/domain/BlockConfiguration.d.ts +9 -2
  68. package/dist/core/domain/BlockConfiguration.js +49 -12
  69. package/dist/core/domain/CollectionBrowser.d.ts +2 -2
  70. package/dist/core/domain/CollectionFieldProxy.d.ts +7 -2
  71. package/dist/core/domain/CollectionFieldProxy.js +55 -66
  72. package/dist/core/domain/SectionBlock.d.ts +5 -0
  73. package/dist/core/domain/SectionBlock.js +15 -0
  74. package/dist/core/hooks/useBlockConfiguration.d.ts +7 -1
  75. package/dist/core/hooks/useBlockConfiguration.js +8 -1
  76. package/dist/core/hooks/usePage.d.ts +2 -2
  77. package/dist/core/hooks/usePage.js +5 -4
  78. package/dist/core/repository/CollectionRepository.js +1 -1
  79. package/dist/core/repository/CollectionSerializer.d.ts +3 -2
  80. package/dist/core/repository/CollectionSerializer.js +12 -7
  81. package/dist/core/repository/PageRepository.js +2 -2
  82. package/dist/core/repository/PageSerializer.d.ts +5 -3
  83. package/dist/core/repository/PageSerializer.js +10 -11
  84. package/dist/explore/components/CreateCollectionDialog.d.ts +2 -3
  85. package/dist/explore/components/CreateCollectionDialog.js +1 -1
  86. package/dist/explore/components/CreatePageDialog.d.ts +3 -3
  87. package/dist/explore/components/CreatePageDialog.js +2 -2
  88. package/dist/explore/screens/CollectionExplorer.js +4 -4
  89. package/dist/explore/screens/PageExplorer.js +4 -4
  90. package/dist/page/components/BlockEditor.js +10 -2
  91. package/dist/page/components/config/BlockConfigButton.d.ts +8 -0
  92. package/dist/page/components/config/BlockConfigButton.js +320 -0
  93. package/dist/page/components/config/BlockConfigurationProvider.d.ts +8 -0
  94. package/dist/page/components/config/BlockConfigurationProvider.js +17 -0
  95. package/dist/page/components/config/LegacyBlockPreview.d.ts +8 -0
  96. package/dist/page/components/config/{BlockPreview.js → LegacyBlockPreview.js} +30 -40
  97. package/dist/page/components/config/index.d.ts +3 -1
  98. package/dist/page/components/config/index.js +3 -1
  99. package/dist/page/components/index.d.ts +1 -1
  100. package/dist/page/components/index.js +1 -1
  101. package/dist/page/components/nav/NavSection.js +4 -3
  102. package/dist/page/components/panels/BlockConfigPanel.d.ts +1 -0
  103. package/dist/page/components/panels/BlockConfigPanel.js +25 -0
  104. package/dist/page/components/{PageStatus.d.ts → panels/PageStatus.d.ts} +3 -3
  105. package/dist/page/components/{PageStatus.js → panels/PageStatus.js} +10 -14
  106. package/dist/page/components/panels/index.d.ts +2 -0
  107. package/dist/page/components/{status → panels}/index.js +2 -1
  108. package/dist/page/hooks/PageController.d.ts +2 -2
  109. package/dist/page/screens/PageEditor.js +8 -1
  110. package/dist/styles/index.css +157 -46
  111. package/dist/styles/tailwind.js +7 -3
  112. package/dist/users/screens/UserExplorer.js +7 -6
  113. package/package.json +3 -2
  114. package/dist/core/components/fields/collection/previews/ClientPreview.d.ts +0 -10
  115. package/dist/core/components/fields/collection/previews/ClientPreview.js +0 -29
  116. package/dist/core/components/visuals/SkeletonLayout.d.ts +0 -9
  117. package/dist/core/components/visuals/SkeletonLayout.js +0 -429
  118. package/dist/page/components/config/BlockConfig.d.ts +0 -8
  119. package/dist/page/components/config/BlockConfig.js +0 -28
  120. package/dist/page/components/config/BlockConfigEditor.d.ts +0 -9
  121. package/dist/page/components/config/BlockConfigEditor.js +0 -129
  122. package/dist/page/components/config/BlockPreview.d.ts +0 -10
  123. package/dist/page/components/config/BlockView.d.ts +0 -4
  124. package/dist/page/components/config/BlockView.js +0 -9
  125. package/dist/page/components/config/LegacyBlockConfigEditor.d.ts +0 -9
  126. package/dist/page/components/config/LegacyBlockConfigEditor.js +0 -27
  127. package/dist/page/components/config/ListPanelEditor.d.ts +0 -8
  128. package/dist/page/components/config/ListPanelEditor.js +0 -102
  129. package/dist/page/components/config/MessagePanelEditor.d.ts +0 -8
  130. package/dist/page/components/config/MessagePanelEditor.js +0 -17
  131. package/dist/page/components/config/VisualPanelEditor.d.ts +0 -8
  132. package/dist/page/components/config/VisualPanelEditor.js +0 -76
  133. package/dist/page/components/status/index.d.ts +0 -1
  134. /package/dist/page/components/{status → panels}/AutoSave.d.ts +0 -0
  135. /package/dist/page/components/{status → panels}/AutoSave.js +0 -0
@@ -14,22 +14,24 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
14
14
  return (mod && mod.__esModule) ? mod : { "default": mod };
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.animations = void 0;
18
17
  exports.Popup = Popup;
19
- exports.getPopupPlacement = getPopupPlacement;
20
18
  const jsx_runtime_1 = require("react/jsx-runtime");
21
19
  const funnel_core_1 = require("@nascentdigital/funnel-core");
20
+ const funnel_ui_1 = require("@nascentdigital/funnel-ui");
22
21
  const client_1 = require("@nascentdigital/funnel-ui/client");
23
22
  const classnames_1 = __importDefault(require("classnames"));
24
23
  const framer_motion_1 = require("framer-motion");
25
24
  const react_1 = require("react");
26
25
  const react_dom_1 = require("react-dom");
26
+ const Animate_1 = require("./Animate");
27
27
  // component
28
28
  function Popup(_a) {
29
- var _b;
30
- var { className, style = {}, anchor, placement, anchorOffset, viewportPadding, animation = 'in-out', backdrop = 'none', disabled = false, disableDismiss = false, disableOverlay = false, open = false, onOpened: onOpenedRaw = funnel_core_1.Fn.NoOp, onClose = funnel_core_1.Fn.NoOp, onClosed: onClosedRaw = funnel_core_1.Fn.NoOp, onKeyDown: onKeyDownRaw, children } = _a, props = __rest(_a, ["className", "style", "anchor", "placement", "anchorOffset", "viewportPadding", "animation", "backdrop", "disabled", "disableDismiss", "disableOverlay", "open", "onOpened", "onClose", "onClosed", "onKeyDown", "children"]);
29
+ var { className, style = {}, anchor, anchorOffset, placement = 'bottom', viewportPadding, animation = 'in-out', backdrop = 'none', disabled = false, disableDismiss = false, disableOverlay = false, open = false, onOpened: onOpenedRaw = funnel_core_1.Fn.NoOp, onClose = funnel_core_1.Fn.NoOp, onClosed: onClosedRaw = funnel_core_1.Fn.NoOp, onKeyDown: onKeyDownRaw, children } = _a, props = __rest(_a, ["className", "style", "anchor", "anchorOffset", "placement", "viewportPadding", "animation", "backdrop", "disabled", "disableDismiss", "disableOverlay", "open", "onOpened", "onClose", "onClosed", "onKeyDown", "children"]);
31
30
  // bind context
32
31
  const id = (0, react_1.useId)();
32
+ const anchorId = typeof anchor === 'string'
33
+ ? anchor
34
+ : id;
33
35
  const [contentRef, contentElement] = (0, client_1.useRefElement)();
34
36
  const openRef = (0, react_1.useRef)(open);
35
37
  openRef.current = open;
@@ -37,8 +39,6 @@ function Popup(_a) {
37
39
  onOpenedRef.current = onOpenedRaw;
38
40
  const onClosedRef = (0, react_1.useRef)(onClosedRaw);
39
41
  onClosedRef.current = onClosedRaw;
40
- // re-render on window size change
41
- (0, client_1.useViewportSize)();
42
42
  // lock tab focus
43
43
  (0, react_1.useEffect)(() => {
44
44
  // lock when opened
@@ -71,7 +71,7 @@ function Popup(_a) {
71
71
  }
72
72
  };
73
73
  const onAnimationComplete = (definition) => {
74
- if (definition === 'visible') {
74
+ if (definition === 'animate') {
75
75
  onOpenedRef.current();
76
76
  }
77
77
  else if (definition === 'exit') {
@@ -89,72 +89,69 @@ function Popup(_a) {
89
89
  }
90
90
  };
91
91
  // render
92
- const popupPlacement = getPopupPlacement({
93
- contentElement,
94
- anchor,
95
- anchorOffset,
96
- placement,
97
- viewportPadding
98
- });
99
- return ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: (0, react_dom_1.createPortal)((0, jsx_runtime_1.jsx)(framer_motion_1.AnimatePresence, { children: open &&
100
- (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: (0, jsx_runtime_1.jsxs)(framer_motion_1.motion.div, { id: id, className: (0, classnames_1.default)(popupPlacement.containerClassName, 'fixed inset-0 z-popup', {
101
- 'pointer-events-none': disableOverlay
102
- }), variants: exports.animations, initial: animation !== 'out' ? 'hidden' : 'visible', animate: "visible", exit: animation !== 'in' ? 'exit' : 'visible', onAnimationComplete: definition => onAnimationComplete(definition), onClick: disableDismiss ? undefined : onBackdropClick, children: [backdrop !== 'none' &&
103
- (0, jsx_runtime_1.jsx)(framer_motion_1.motion.div, { className: (0, classnames_1.default)('fixed inset-0 z-backdrop', {
104
- 'backdrop-blur-[9px] bg-glass/backdrop will-change-[backdrop-filter]': backdrop === 'glass'
105
- }) }, "backdrop"), (0, jsx_runtime_1.jsx)("div", Object.assign({}, props, { ref: contentRef, style: Object.assign(Object.assign({}, style), ((_b = popupPlacement.style) !== null && _b !== void 0 ? _b : {})), className: (0, classnames_1.default)(className, popupPlacement.className, 'pointer-events-auto'), onKeyDown: onKeyDown, children: children }))] }, id) }) }), document.body) }));
92
+ const { popupContainerClassName, popupClassName } = getPopupClassNames({ anchor, anchorId, anchorOffset, placement });
93
+ return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [anchor && typeof anchor !== 'string' &&
94
+ (0, jsx_runtime_1.jsx)(Popup.AnchorElement, { tag: "div", anchorId: anchorId, children: anchor }), (0, react_dom_1.createPortal)((0, jsx_runtime_1.jsx)(framer_motion_1.AnimatePresence, { mode: "wait", children: open &&
95
+ (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: (0, jsx_runtime_1.jsxs)(framer_motion_1.motion.div, { id: id, className: (0, classnames_1.default)(popupContainerClassName, 'fixed inset-0 z-popup', {
96
+ 'pointer-events-none': disableOverlay
97
+ }), variants: Animate_1.Animate.Variants.fade, initial: animation !== 'out' ? 'initial' : 'animate', animate: "animate", exit: animation !== 'in' ? 'exit' : 'animate', onAnimationComplete: definition => onAnimationComplete(definition), onClick: disableDismiss ? undefined : onBackdropClick, children: [backdrop !== 'none' &&
98
+ (0, jsx_runtime_1.jsx)(framer_motion_1.motion.div, { className: (0, classnames_1.default)('fixed inset-0 z-backdrop', {
99
+ 'backdrop-blur-[9px] bg-glass/backdrop will-change-[backdrop-filter]': backdrop === 'glass'
100
+ }) }, "backdrop"), (0, jsx_runtime_1.jsx)("div", Object.assign({}, props, { ref: contentRef, className: (0, classnames_1.default)(className, popupClassName, 'pointer-events-auto'), style: anchor
101
+ ? Popup.getAnchorStyle(anchorId, { margin: `${anchorOffset !== null && anchorOffset !== void 0 ? anchorOffset : 0}px` })
102
+ : undefined, onKeyDown: onKeyDown, children: children }))] }, id) }) }), document.body)] }));
106
103
  }
107
- // helpers
108
- exports.animations = {
109
- hidden: {
110
- opacity: 0
111
- },
112
- visible: {
113
- opacity: 1,
114
- transition: {
115
- ease: 'easeIn',
116
- duration: 0.3,
117
- when: 'beforeChildren'
118
- }
119
- },
120
- exit: {
121
- opacity: 0,
122
- transition: {
123
- ease: 'easeIn',
124
- duration: 0.15,
125
- bounce: 0,
126
- when: 'afterChildren'
127
- }
104
+ // extension
105
+ (function (Popup) {
106
+ // anchor helpers
107
+ Popup.anchorClassName = 'anchor/[--nd-popup-anchor]';
108
+ function getAnchorStyle(anchorId, styles = {}) {
109
+ return Object.assign(Object.assign({}, styles), { '--nd-popup-anchor': normalizeAnchorId(anchorId) });
128
110
  }
129
- };
130
- function getPopupPlacement({ anchor, anchorOffset = 0, placement = 'bottom', viewportPadding = 16, contentElement }) {
131
- // center if there's no anchor
132
- const anchorRect = funnel_core_1.Browser.getElementRect(anchor);
133
- if (!anchorRect || !contentElement) {
111
+ Popup.getAnchorStyle = getAnchorStyle;
112
+ function getAnchorFrame(frame) {
113
+ return frame && {
114
+ left: `${frame.left}px`,
115
+ top: `${frame.top}px`,
116
+ width: `${frame.width}px`,
117
+ height: `${frame.height}px`
118
+ };
119
+ }
120
+ Popup.getAnchorFrame = getAnchorFrame;
121
+ // container
122
+ function AnchorElement(_a) {
123
+ var { anchorId, tag, className, style } = _a, props = __rest(_a, ["anchorId", "tag", "className", "style"]);
124
+ // render
125
+ return ((0, jsx_runtime_1.jsx)(funnel_ui_1.HTMLContainer, Object.assign({ tag: tag, className: (0, classnames_1.default)(className, Popup.anchorClassName), style: getAnchorStyle(normalizeAnchorId(anchorId), style) }, props)));
126
+ }
127
+ Popup.AnchorElement = AnchorElement;
128
+ })(Popup || (exports.Popup = Popup = {}));
129
+ // helpers
130
+ function normalizeAnchorId(anchorId) {
131
+ return anchorId.startsWith('--')
132
+ ? anchorId
133
+ : `--${anchorId}`;
134
+ }
135
+ function getPopupClassNames({ anchor, placement }) {
136
+ // return anchor classes (via tailwind anchor plugin)
137
+ if (anchor) {
134
138
  return {
135
- containerClassName: 'flex flex-col justify-end items-center sm:justify-center',
136
- className: 'z-dialog'
139
+ popupContainerClassName: '',
140
+ popupClassName: (0, classnames_1.default)('!fixed anchored/[--nd-popup-anchor] z-dialog', {
141
+ 'anchored-top': placement === 'top',
142
+ 'anchored-top-span-right': placement === 'top-start',
143
+ 'anchored-bottom': placement === 'bottom',
144
+ 'anchored-left': placement === 'left',
145
+ 'anchored-right': placement === 'right',
146
+ 'anchored-bottom-span-left': placement === 'bottom-end'
147
+ })
137
148
  };
138
149
  }
139
- // or place absolutely
150
+ // or treat as modal if there's no anchor
140
151
  else {
141
- // resolve placement args
142
- const containerRect = new funnel_core_1.Rect(0, 0, window.innerWidth, window.innerHeight);
143
- const popupFrame = funnel_core_1.Placement.forContent({
144
- containerRect,
145
- containerPadding: viewportPadding,
146
- anchorRect,
147
- anchorOffset,
148
- placement,
149
- contentSize: {
150
- width: contentElement.offsetWidth,
151
- height: contentElement.offsetHeight
152
- }
153
- });
154
- // set style to frame
155
152
  return {
156
- className: 'absolute z-dialog',
157
- style: popupFrame
153
+ popupContainerClassName: 'flex flex-col justify-end items-center sm:justify-center',
154
+ popupClassName: 'z-dialog'
158
155
  };
159
156
  }
160
157
  }
@@ -54,6 +54,7 @@ exports.QuickInput = (0, react_1.forwardRef)(function QuickInput(_, ref) {
54
54
  setSchema(schema);
55
55
  setInitialValue(initialValue);
56
56
  setPopupProps(popupProps);
57
+ console.debug('[QuickInput] opening editor with', popupProps);
57
58
  // create/set deferral
58
59
  const deferral = new funnel_core_1.Deferral();
59
60
  setDeferral(deferral);
@@ -61,6 +62,7 @@ exports.QuickInput = (0, react_1.forwardRef)(function QuickInput(_, ref) {
61
62
  const result = await deferral.promise;
62
63
  // clear deferral
63
64
  setDeferral(undefined);
65
+ console.debug('[QuickInput] closing editor with', popupProps);
64
66
  // return deferral
65
67
  return result;
66
68
  },
@@ -92,8 +92,8 @@ exports.QuickPicker = (0, react_1.forwardRef)(function QuickPicker({ variant, ti
92
92
  'opacity-none': !opened,
93
93
  'opacity-full': opened
94
94
  }), animation: "out", open: !!deferral, onOpened: () => setOpened(true), onClose: onClose, onClosed: () => setOpened(false), children: [(0, jsx_runtime_1.jsxs)("section", { className: "px-l8", children: [(0, jsx_runtime_1.jsx)("h3", { className: "heading-sm text-content-primary", children: title }), description &&
95
- (0, jsx_runtime_1.jsx)("p", { className: "body-sm text-content-secondary", children: description })] }), (0, jsx_runtime_1.jsx)("section", { className: (0, classnames_1.default)({
96
- 'flex flex-wrap gap-l5 items-center px-l8 pb-l8 overflow-y-scroll': variant === 'card',
95
+ (0, jsx_runtime_1.jsx)("p", { className: "body-sm text-content-secondary", children: description })] }), (0, jsx_runtime_1.jsx)("section", { className: (0, classnames_1.default)('px-l8 pb-l8 overflow-y-scroll scrollbar-thin scroller-edge', {
96
+ 'flex flex-wrap gap-l5 items-center ': variant === 'card',
97
97
  'w-[288px]': variant === 'card' && items.length < 4,
98
98
  'w-[372px] max-h-[213px]': variant === 'card' && items.length >= 4
99
99
  }), children: items.map(item => renderCard({
@@ -104,7 +104,7 @@ exports.QuickPicker = (0, react_1.forwardRef)(function QuickPicker({ variant, ti
104
104
  })) })] })));
105
105
  });
106
106
  function renderCard({ item, content, selected, onSelect }) {
107
- return ((0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col items-center gap-l2 shrink-0", children: [(0, jsx_runtime_1.jsx)("div", { className: (0, classnames_1.default)('FieldCard w-[72px] h-[55px]', {
107
+ return ((0, jsx_runtime_1.jsxs)("div", { className: "group flex flex-col items-center gap-l2 shrink-0 cursor-pointer", onClick: () => onSelect(item.id), children: [(0, jsx_runtime_1.jsx)("div", { className: (0, classnames_1.default)('FieldCard w-[72px] h-[55px] group-hover:FieldCard-hover group-active:FieldCard-press', {
108
108
  'FieldCard-selected': selected
109
- }), onClick: () => onSelect(item.id), children: content }), (0, jsx_runtime_1.jsx)("span", { className: "label-xxs text-content-tertiary cursor-default", children: item.label })] }, item.id));
109
+ }), children: content }), (0, jsx_runtime_1.jsx)("span", { className: (0, classnames_1.default)('label-xxs text-content-tertiary interpolate-fast pointer-events-none', 'group-hover:text-content-secondary group-active:text-content-primary'), children: item.label })] }, item.id));
110
110
  }
@@ -1,7 +1,9 @@
1
1
  import { Callback } from '@nascentdigital/funnel-core';
2
2
  import { Icon } from '../visuals';
3
- export declare function Tabs<TId extends string>({ className, items, selectedItem, onSelect }: Tabs.Props<TId>): import("react/jsx-runtime").JSX.Element;
3
+ export declare function Tabs<TId extends string>({ className, variant, size, items, selectedItem, onSelect }: Tabs.Props<TId>): import("react/jsx-runtime").JSX.Element;
4
4
  export declare namespace Tabs {
5
+ type Variant = 'underlined' | 'highlighted';
6
+ type Size = 'md' | 'sm';
5
7
  type Item<TId extends string = string> = {
6
8
  id: TId;
7
9
  label: string;
@@ -9,6 +11,8 @@ export declare namespace Tabs {
9
11
  };
10
12
  type Props<TId extends string = string> = {
11
13
  className?: string;
14
+ variant?: Variant;
15
+ size?: Size;
12
16
  items: ReadonlyArray<Item<TId>>;
13
17
  selectedItem: TId;
14
18
  onSelect?: Callback<TId>;
@@ -10,15 +10,25 @@ const classnames_1 = __importDefault(require("classnames"));
10
10
  const framer_motion_1 = require("framer-motion");
11
11
  const visuals_1 = require("../visuals");
12
12
  // component
13
- function Tabs({ className, items, selectedItem, onSelect = funnel_core_1.Fn.NoOp }) {
13
+ function Tabs({ className, variant = 'underlined', size = 'md', items, selectedItem, onSelect = funnel_core_1.Fn.NoOp }) {
14
14
  // render
15
- return ((0, jsx_runtime_1.jsx)("nav", { className: (0, classnames_1.default)(className), children: (0, jsx_runtime_1.jsx)("ul", { className: "grid auto-cols-fr grid-flow-col items-center", children: items.map(item => (0, jsx_runtime_1.jsxs)("li", { className: (0, classnames_1.default)('group relative border-b border-solid border-content-tertiary cursor-pointer interpolate', {
15
+ return ((0, jsx_runtime_1.jsx)("nav", { className: (0, classnames_1.default)(className), children: (0, jsx_runtime_1.jsx)("ul", { className: (0, classnames_1.default)('grid auto-cols-fr grid-flow-col items-center cursor-pointer', {
16
+ 'rounded-lg bg-surface-placeholder-raised p-l1': variant === 'highlighted',
17
+ }), children: items.map(item => (0, jsx_runtime_1.jsxs)("li", { className: (0, classnames_1.default)('group relative interpolate', {
18
+ 'border-b border-solid border-content-tertiary': variant === 'underlined',
16
19
  'pointer-events-none': item.id === selectedItem,
17
- 'hover:border-content-secondary active:border-content-tertiary': item.id !== selectedItem
18
- }), onClick: () => onSelect(item.id), children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex justify-center items-center gap-l6", children: [item.icon &&
19
- (0, jsx_runtime_1.jsx)(visuals_1.Icon, { variant: item.icon, size: "nav" }), (0, jsx_runtime_1.jsx)("span", { className: (0, classnames_1.default)('inline-block label-sm py-l6 interpolate', {
20
+ 'hover:border-content-secondary active:border-content-tertiary': item.id !== selectedItem,
21
+ }), onClick: () => onSelect(item.id), children: [(0, jsx_runtime_1.jsxs)("div", { className: (0, classnames_1.default)('flex justify-center items-center gap-l6', {
22
+ 'relative z-supersurface': variant === 'highlighted'
23
+ }), children: [item.icon &&
24
+ (0, jsx_runtime_1.jsx)(visuals_1.Icon, { variant: item.icon, size: "nav" }), (0, jsx_runtime_1.jsx)("span", { className: (0, classnames_1.default)('inline-block interpolate', {
20
25
  'text-content-primary': item.id === selectedItem,
21
- 'text-content-secondary group-hover:text-content-primary group-active:text-content-tertiary': item.id !== selectedItem
22
- }), children: item.label })] }), item.id === selectedItem &&
23
- (0, jsx_runtime_1.jsx)(framer_motion_1.motion.span, { id: "underline", layoutId: "underline", className: "absolute inset-x-0 -bottom-px block divider border-b-2 border-ix-primary -mb-px z-supersurface" })] }, item.id)) }) }));
26
+ 'text-content-secondary group-hover:text-content-primary group-active:text-content-tertiary': item.id !== selectedItem,
27
+ 'py-l6 label-md': variant === 'underlined' && size === 'md',
28
+ 'py-l3 label-xs': variant === 'underlined' && size === 'sm',
29
+ 'py-l3 label-md': variant === 'highlighted' && size === 'md',
30
+ 'py-l2 label-xs': variant === 'highlighted' && size === 'sm'
31
+ }), children: item.label })] }), variant === 'underlined' && item.id === selectedItem &&
32
+ (0, jsx_runtime_1.jsx)(framer_motion_1.motion.span, { id: "underline", layoutId: "underline", className: "absolute inset-x-0 -bottom-px block divider border-b-2 border-ix-primary -mb-px z-supersurface" }), variant === 'highlighted' && item.id === selectedItem &&
33
+ (0, jsx_runtime_1.jsx)(framer_motion_1.motion.span, { layoutId: "active-tab", className: "absolute z-surface inset-0 bg-surface-placeholder rounded-lg" })] }, item.id)) }) }));
24
34
  }
@@ -11,12 +11,12 @@ export declare class MenuController<TItemId extends string, TContext = any> {
11
11
  set items(items: ReadonlyArray<MenuItem<TItemId>> | undefined);
12
12
  get selectedItem(): TItemId | undefined;
13
13
  set selectedItem(item: TItemId | undefined);
14
- get anchor(): HTMLElement | undefined;
14
+ get anchor(): string | undefined;
15
15
  private _context?;
16
16
  get context(): TContext | undefined;
17
17
  get changed$(): Observable<Date>;
18
18
  get isOpen(): boolean;
19
- open(anchor: HTMLElement, context?: TContext): void;
19
+ open(anchor: string, context?: TContext): void;
20
20
  close(): void;
21
21
  private raiseChange;
22
22
  }
@@ -1,10 +1,11 @@
1
1
  import { Callback } from '@nascentdigital/funnel-core';
2
2
  import { MouseEvent, PropsWithChildren, ReactNode } from 'react';
3
3
  import { Icon } from '../../visuals';
4
- export declare function TableItem({ className, nested, details, iconLeft, iconRight, disabled, onClick, children }: TableItem.Props): import("react/jsx-runtime").JSX.Element;
4
+ export declare function TableItem({ className, anchorId, nested, details, iconLeft, iconRight, disabled, onClick, children }: TableItem.Props): import("react/jsx-runtime").JSX.Element;
5
5
  export declare namespace TableItem {
6
6
  type Props = PropsWithChildren & {
7
7
  className?: string;
8
+ anchorId?: string;
8
9
  nested?: boolean;
9
10
  details?: ReactNode;
10
11
  iconLeft?: Icon.Variant | ReactNode;
@@ -8,8 +8,9 @@ const jsx_runtime_1 = require("react/jsx-runtime");
8
8
  const funnel_core_1 = require("@nascentdigital/funnel-core");
9
9
  const classnames_1 = __importDefault(require("classnames"));
10
10
  const visuals_1 = require("../../visuals");
11
+ const interaction_1 = require("../../interaction");
11
12
  // component
12
- function TableItem({ className, nested = false, details, iconLeft, iconRight, disabled = false, onClick, children }) {
13
+ function TableItem({ className, anchorId, nested = false, details, iconLeft, iconRight, disabled = false, onClick, children }) {
13
14
  // render
14
15
  const labelOnly = !details && !iconRight;
15
16
  const interactive = !!onClick;
@@ -17,8 +18,9 @@ function TableItem({ className, nested = false, details, iconLeft, iconRight, di
17
18
  'bg-surface1': !nested,
18
19
  'bg-transparent': nested,
19
20
  'interactive-disabled': !interactive && !disabled,
20
- 'bg-ix-disabled/tint1 pointer-events-none': disabled
21
- }), tabIndex: 0, onClick: onClick, children: [(0, jsx_runtime_1.jsx)("div", { className: "interactive-tint" }), (0, jsx_runtime_1.jsxs)("div", { className: "flex-1 flex items-center", children: [(0, jsx_runtime_1.jsxs)("div", { className: (0, classnames_1.default)('flex-1 flex items-center gap-l3 py-l5'), children: [visuals_1.Icon.Variant.filter(iconLeft)
21
+ 'bg-ix-disabled/tint1 pointer-events-none': disabled,
22
+ [interaction_1.Popup.anchorClassName]: !!anchorId
23
+ }), style: anchorId ? interaction_1.Popup.getAnchorStyle(anchorId) : undefined, tabIndex: 0, onClick: onClick, children: [(0, jsx_runtime_1.jsx)("div", { className: "interactive-tint" }), (0, jsx_runtime_1.jsxs)("div", { className: "flex-1 flex items-center", children: [(0, jsx_runtime_1.jsxs)("div", { className: (0, classnames_1.default)('flex-1 flex items-center gap-l3 py-l5'), children: [visuals_1.Icon.Variant.filter(iconLeft)
22
24
  ?
23
25
  (0, jsx_runtime_1.jsx)(visuals_1.Icon, { className: (0, classnames_1.default)({
24
26
  'text-content-secondary': !disabled,
@@ -19,6 +19,7 @@ const jsx_runtime_1 = require("react/jsx-runtime");
19
19
  const funnel_core_1 = require("@nascentdigital/funnel-core");
20
20
  const funnel_ui_1 = require("@nascentdigital/funnel-ui");
21
21
  const classnames_1 = __importDefault(require("classnames"));
22
+ const react_1 = require("react");
22
23
  const feedback_1 = require("../feedback");
23
24
  const interaction_1 = require("../interaction");
24
25
  // component
@@ -26,10 +27,11 @@ function InfoPanel(_a) {
26
27
  var { className, contentClassName, title, caption, status, menu: menuItems, onMenuSelect = funnel_core_1.Fn.NoOp, children } = _a, props = __rest(_a, ["className", "contentClassName", "title", "caption", "status", "menu", "onMenuSelect", "children"]);
27
28
  // bind refs
28
29
  const menu = (0, interaction_1.useMenuController)(menuItems);
30
+ const anchorId = (0, react_1.useId)();
29
31
  // helpers
30
32
  const onMenuOpen = (e) => {
31
33
  // open menu
32
- menu.open(e.currentTarget);
34
+ menu.open(anchorId);
33
35
  };
34
36
  const onMenuClick = (menuId) => {
35
37
  // close menu
@@ -40,7 +42,7 @@ function InfoPanel(_a) {
40
42
  // render
41
43
  return ((0, jsx_runtime_1.jsxs)("section", { className: (0, classnames_1.default)(className, 'flex flex-col p-l7', 'bg-surface1 rounded-xxl'), children: [(0, jsx_runtime_1.jsxs)("div", { className: "shrink-0 flex items-center gap-l4", children: [status &&
42
44
  (0, jsx_runtime_1.jsx)(feedback_1.StatusIcon, { variant: status, size: "md" }), (0, jsx_runtime_1.jsx)("h3", { className: "flex-1 heading-sm text-content-primary ellipsis", children: title }), menu &&
43
- (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(interaction_1.IconButton, { className: "shrink-0", icon: "EllipsisVertical", onClick: onMenuOpen }), (0, jsx_runtime_1.jsx)(interaction_1.Menu, { controller: menu, onItemClick: onMenuClick })] })] }), caption &&
45
+ (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(interaction_1.IconButton, { anchorId: anchorId, className: "shrink-0", icon: "EllipsisVertical", onClick: onMenuOpen }), (0, jsx_runtime_1.jsx)(interaction_1.Menu, { controller: menu, onItemClick: onMenuClick })] })] }), caption &&
44
46
  (0, jsx_runtime_1.jsx)(funnel_ui_1.Markup, { className: (0, classnames_1.default)('markup-caption mt-l6', {
45
47
  'text-destructive-content bg-destructive/tint0 p-l5 rounded-md': status === 'error'
46
48
  }), markdown: caption }), (0, jsx_runtime_1.jsx)("div", { className: (0, classnames_1.default)(contentClassName), children: children })] }));
@@ -6,7 +6,7 @@ export declare namespace Icon {
6
6
  size?: Size;
7
7
  };
8
8
  type Size = 'xl' | 'lg' | 'md' | 'sm' | 'xs' | 'xxs' | 'nav' | 'card' | Sizing;
9
- type Variant = 'Archive' | 'ArrowLeft' | 'ArrowRight' | 'ArrowLeftRight' | 'Atom' | 'Blocks' | 'Bold' | 'Check' | 'ChevronDown' | 'ChevronRight' | 'ChevronUp' | 'Circle' | 'CircleAlert' | 'CircleCheck' | 'CircleCheckBig' | 'CirclePlus' | 'CircleQuestionMark' | 'CloudCheck' | 'CloudUpload' | 'CornerDownRight' | 'EllipsisVertical' | 'Eye' | 'EyeOff' | 'File' | 'FileDown' | 'FilePlusCorner' | 'FileSearchCorner' | 'Goal' | 'Globe' | 'GripVertical' | 'HatGlasses' | 'History' | 'Highlighter' | 'Image' | 'ImageDown' | 'Info' | 'Italic' | 'Layers' | 'LayoutTemplate' | 'Link' | 'Link2' | 'List' | 'ListChecks' | 'ListIndentDecrease' | 'ListIndentIncrease' | 'ListOrdered' | 'Lock' | 'LockOpen' | 'Mail' | 'MessageSquareQuote' | 'Minus' | 'MonitorCog' | 'Moon' | 'NotebookPen' | 'Pencil' | 'Pin' | 'Plus' | 'Redo' | 'RefreshCw' | 'RotateCcw' | 'RotateCw' | 'SearchCode' | 'Settings' | 'SquarePlay' | 'Star' | 'Strikethrough' | 'Subscript' | 'Sun' | 'Superscript' | 'TextAlignStart' | 'Trash' | 'TriangleAlert' | 'Trophy' | 'Underline' | 'Undo' | 'Upload' | 'UserRound' | 'X';
9
+ type Variant = 'Archive' | 'ArrowLeft' | 'ArrowRight' | 'ArrowLeftRight' | 'Atom' | 'Blocks' | 'Bold' | 'Check' | 'ChevronDown' | 'ChevronRight' | 'ChevronUp' | 'Circle' | 'CircleAlert' | 'CircleCheck' | 'CircleCheckBig' | 'CirclePlus' | 'CircleQuestionMark' | 'CloudCheck' | 'CloudUpload' | 'CornerDownRight' | 'EllipsisVertical' | 'Eye' | 'EyeOff' | 'File' | 'FileDown' | 'FilePlusCorner' | 'FileSearchCorner' | 'Goal' | 'Globe' | 'GripVertical' | 'HatGlasses' | 'History' | 'Highlighter' | 'Image' | 'ImageDown' | 'Info' | 'Italic' | 'Layers' | 'LayoutTemplate' | 'Link' | 'Link2' | 'List' | 'ListChecks' | 'ListIndentDecrease' | 'ListIndentIncrease' | 'ListOrdered' | 'Lock' | 'LockOpen' | 'Mail' | 'MessageSquareQuote' | 'Minus' | 'Monitor' | 'MonitorCog' | 'Moon' | 'NotebookPen' | 'Pencil' | 'Pin' | 'Plus' | 'Redo' | 'RefreshCw' | 'RotateCcw' | 'RotateCw' | 'SearchCode' | 'Settings' | 'Smartphone' | 'SquarePlay' | 'Star' | 'Strikethrough' | 'Subscript' | 'Sun' | 'Superscript' | 'TextAlignStart' | 'Trash' | 'TriangleAlert' | 'Trophy' | 'Underline' | 'Undo' | 'Upload' | 'UserRound' | 'X';
10
10
  namespace Variant {
11
11
  function filter(value: any): value is Variant;
12
12
  }
@@ -69,6 +69,7 @@ function Icon({ className, variant, size: sizing = 'md' }) {
69
69
  'Mail',
70
70
  'MessageSquareQuote',
71
71
  'Minus',
72
+ 'Monitor',
72
73
  'MonitorCog',
73
74
  'Moon',
74
75
  'NotebookPen',
@@ -81,6 +82,7 @@ function Icon({ className, variant, size: sizing = 'md' }) {
81
82
  'RotateCw',
82
83
  'SearchCode',
83
84
  'Settings',
85
+ 'Smartphone',
84
86
  'SquarePlay',
85
87
  'Star',
86
88
  'Strikethrough',
@@ -211,6 +213,8 @@ function getIcon(variant) {
211
213
  return lucide_react_1.MessageSquareQuote;
212
214
  case 'Minus':
213
215
  return lucide_react_1.Minus;
216
+ case 'Monitor':
217
+ return lucide_react_1.Monitor;
214
218
  case 'MonitorCog':
215
219
  return lucide_react_1.MonitorCog;
216
220
  case 'Moon':
@@ -235,6 +239,8 @@ function getIcon(variant) {
235
239
  return lucide_react_1.SearchCode;
236
240
  case 'Settings':
237
241
  return lucide_react_1.Settings;
242
+ case 'Smartphone':
243
+ return lucide_react_1.Smartphone;
238
244
  case 'SquarePlay':
239
245
  return lucide_react_1.SquarePlay;
240
246
  case 'Star':
@@ -2,4 +2,3 @@ export * from './BlockIcon';
2
2
  export * from './Icon';
3
3
  export * from './Illustration';
4
4
  export * from './Logo';
5
- export * from './SkeletonLayout';
@@ -18,4 +18,3 @@ __exportStar(require("./BlockIcon"), exports);
18
18
  __exportStar(require("./Icon"), exports);
19
19
  __exportStar(require("./Illustration"), exports);
20
20
  __exportStar(require("./Logo"), exports);
21
- __exportStar(require("./SkeletonLayout"), exports);
@@ -8,6 +8,10 @@ export declare class Block<TBlock extends Schema.Block = Schema.Block> extends I
8
8
  readonly config: ReadonlyArray<FieldModel.Config>;
9
9
  readonly fields: ReadonlyArray<FieldModel>;
10
10
  readonly nameField: Field<Schema.TextField>;
11
+ readonly messageFields: ReadonlyArray<FieldModel>;
12
+ readonly visualFields: ReadonlyArray<FieldModel>;
13
+ readonly listsFields: ReadonlyArray<FieldModel>;
14
+ readonly extendedFields: ReadonlyArray<FieldModel>;
11
15
  private _modifiedOn?;
12
16
  private _savingOn?;
13
17
  constructor(args: Block.ConstructorArgs<TBlock>);
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Block = void 0;
4
4
  const funnel_core_1 = require("@nascentdigital/funnel-core");
5
+ const funnel_ui_1 = require("@nascentdigital/funnel-ui");
5
6
  const rxjs_1 = require("rxjs");
6
7
  const BlockConfiguration_1 = require("./BlockConfiguration");
7
8
  const ItemBase_1 = require("./ItemBase");
@@ -31,6 +32,12 @@ class Block extends ItemBase_1.ItemBase {
31
32
  publishedValue: undefined,
32
33
  required: true
33
34
  });
35
+ // group fields
36
+ const messageKeys = Object.keys(funnel_ui_1.MessagePanel.Fields.getSchema());
37
+ this.messageFields = this.fields.filter(field => messageKeys.includes(field.id));
38
+ this.visualFields = this.fields.filter(field => field.id === 'visual');
39
+ this.listsFields = this.fields.filter(field => field.id === 'list');
40
+ this.extendedFields = this.fields.filter(field => !messageKeys.includes(field.id) && field.id !== 'visual' && field.id !== 'list');
34
41
  // listen to field changes
35
42
  this.eventBus.events$
36
43
  .pipe((0, rxjs_1.filter)(funnel_core_1.Field.Event.filter), (0, rxjs_1.filter)(event => (event.type === 'changed' || event.type === 'reset')
@@ -8,15 +8,19 @@ export declare class BlockConfiguration {
8
8
  readonly assetStore: IAssetStore;
9
9
  readonly sectionBlock?: SectionBlock;
10
10
  readonly block: Block;
11
- readonly fields: ReadonlyArray<FieldModel.Config>;
11
+ private _fields;
12
12
  private _editableFields;
13
+ private _scope;
13
14
  private _layout?;
14
15
  private _slotState;
15
16
  private _defaultConfig?;
16
17
  private _isConfigInDefault?;
17
18
  constructor({ block: blockOrSectionBlock, assetStore }: BlockConfiguration.ConstructorArgs);
19
+ get fields(): ReadonlyArray<FieldModel.Config>;
18
20
  get editableFields(): ReadonlyArray<FieldModel.Config>;
19
21
  set editableFields(fields: ReadonlyArray<FieldModel.Config>);
22
+ get scope(): BlockConfiguration.Scope;
23
+ set scope(scope: BlockConfiguration.Scope);
20
24
  get blocksFields(): FieldModel.Config[];
21
25
  get blockFieldData(): Schema.Block.Data.ForFields<Schema.Fields>;
22
26
  get desktopFields(): FieldModel.Config[];
@@ -27,6 +31,7 @@ export declare class BlockConfiguration {
27
31
  get slotState(): Readonly<BlockConfiguration.SlotState>;
28
32
  gotToNextSlot(): void;
29
33
  gotToPreviousSlot(): void;
34
+ get hasPanels(): boolean;
30
35
  get messageFields(): Record<keyof MessagePanel.Config.Keys<'message'>, FieldModel.Config | undefined>;
31
36
  get listFields(): Record<keyof ListPanel.Config.Keys<'list'>, FieldModel.Config | undefined>;
32
37
  get visualFields(): Record<keyof VisualPanel.Config.Keys<'visual'>, FieldModel.Config | undefined>;
@@ -39,12 +44,14 @@ export declare class BlockConfiguration {
39
44
  getFieldData(fieldId: string): Schema.Field.LinkedData | undefined;
40
45
  getEditableField(fieldId: string): FieldModel.Config | undefined;
41
46
  save(): void;
42
- reset(): void;
47
+ resetToDefault(): void;
43
48
  get isConfigInDefault(): boolean | undefined;
44
49
  sync(): void;
50
+ reset(): void;
45
51
  private raiseEvent;
46
52
  }
47
53
  export declare namespace BlockConfiguration {
54
+ type Scope = 'desktop' | 'mobile';
48
55
  type SlotState = {
49
56
  field?: Field<Schema.EnumField<Schema.EnumField.Options<SlotVariation>>>;
50
57
  options: ReadonlyArray<SlotVariation>;
@@ -9,37 +9,34 @@ const SectionBlock_1 = require("./SectionBlock");
9
9
  class BlockConfiguration {
10
10
  constructor({ block: blockOrSectionBlock, assetStore }) {
11
11
  // initialize core fields
12
- const eventBus = new funnel_core_1.EventBus();
13
- this.eventBus = eventBus;
12
+ this.eventBus = new funnel_core_1.EventBus();
14
13
  this.assetStore = assetStore;
15
14
  const fields = [];
16
- this.fields = fields;
15
+ this._fields = fields;
17
16
  this._editableFields = fields;
17
+ this._scope = 'desktop';
18
18
  this._isConfigInDefault = false;
19
19
  this._slotState = { options: [] };
20
- // initialize section block
20
+ // handle section blocks
21
21
  if (blockOrSectionBlock instanceof SectionBlock_1.SectionBlock) {
22
22
  // capture parents
23
23
  this.sectionBlock = blockOrSectionBlock;
24
24
  this.block = blockOrSectionBlock.block;
25
- // add name
26
- const nameField = blockOrSectionBlock.block.nameField.clone({ eventBus });
27
- nameField.reset(funnel_core_1.Schema.TextField.Value.toData(blockOrSectionBlock.block.name));
28
- fields.push(nameField);
29
25
  }
30
26
  // or regular block
31
27
  else {
32
28
  this.block = blockOrSectionBlock;
33
29
  }
34
- // add fields
35
- this.block.config.forEach(field => fields.push(field.clone({ eventBus })));
36
30
  // sync
37
- this.sync();
31
+ this.reset();
38
32
  // force sync if a field is modified
39
33
  this.eventBus.events$
40
34
  .pipe((0, rxjs_1.filter)(event => funnel_core_1.Field.Event.filter(event) && event.type === 'changed'))
41
35
  .subscribe(() => this.sync());
42
36
  }
37
+ get fields() {
38
+ return this._fields;
39
+ }
43
40
  get editableFields() {
44
41
  return this._editableFields;
45
42
  }
@@ -47,6 +44,18 @@ class BlockConfiguration {
47
44
  this._editableFields = fields;
48
45
  this.raiseEvent('changed');
49
46
  }
47
+ get scope() {
48
+ return this._scope;
49
+ }
50
+ set scope(scope) {
51
+ // ignore if unchanged
52
+ if (this._scope === scope) {
53
+ return;
54
+ }
55
+ // update + raise event
56
+ this._scope = scope;
57
+ this.raiseEvent('changed');
58
+ }
50
59
  get blocksFields() {
51
60
  return [
52
61
  this.nameField,
@@ -130,6 +139,16 @@ class BlockConfiguration {
130
139
  };
131
140
  field.value = funnel_core_1.Schema.EnumField.Value.toData(newOption);
132
141
  }
142
+ get hasPanels() {
143
+ // build list of panel keys
144
+ const panelKeys = [
145
+ ...Object.values(funnel_ui_1.MessagePanel.Config.getKeys('message')),
146
+ ...Object.values(funnel_ui_1.ListPanel.Config.getKeys('list')),
147
+ ...Object.values(funnel_ui_1.VisualPanel.Config.getKeys('visual'))
148
+ ];
149
+ // return true if any keys exist
150
+ return this._editableFields.some(field => panelKeys.includes(field.id));
151
+ }
133
152
  get messageFields() {
134
153
  const keys = funnel_ui_1.MessagePanel.Config.getKeys('message');
135
154
  return {
@@ -291,7 +310,7 @@ class BlockConfiguration {
291
310
  }
292
311
  });
293
312
  }
294
- reset() {
313
+ resetToDefault() {
295
314
  // set the editable fields to their default values
296
315
  this.editableFields.forEach(field => {
297
316
  // throw if default config is not set
@@ -378,6 +397,24 @@ class BlockConfiguration {
378
397
  // determine if the values of the editable fields are same as default
379
398
  this._isConfigInDefault = funnel_core_1.Value.equals(Object.values(this._defaultConfig), this.editableFields.map(field => field.value));
380
399
  }
400
+ reset() {
401
+ // create new fields
402
+ const fields = [];
403
+ // add name field
404
+ const { eventBus, sectionBlock, block } = this;
405
+ if (sectionBlock) {
406
+ const nameField = block.nameField.clone({ eventBus });
407
+ nameField.reset(funnel_core_1.Schema.TextField.Value.toData(block.name));
408
+ fields.push(nameField);
409
+ }
410
+ // add fields
411
+ block.config.forEach(field => fields.push(field.clone({ eventBus })));
412
+ // bind fields
413
+ this._fields = fields;
414
+ this._editableFields = fields;
415
+ // force sync
416
+ this.sync();
417
+ }
381
418
  raiseEvent(type) {
382
419
  this.eventBus.publish(BlockConfiguration.Event.create({ type, configuration: this }));
383
420
  }