@nascentdigital/funnel-editor 4.5.1 → 4.5.2

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,26 @@
1
+ import { Callback, Provider, Transform } from '@nascentdigital/funnel-core';
2
+ import { HTMLMotionProps, Variants } from 'framer-motion';
3
+ import { PropsWithChildren, ReactNode } from 'react';
4
+ export declare function SideSheet({ className, title, wide, backdrop, disabled, disableDismiss, disableOverlay, disableSave, canSave, renderSaveLabel, onSave: onSaveRaw, open, onOpened: onOpenedRaw, onClose: onCloseRaw, onClosed: onClosedRaw, onKeyDown: onKeyDownRaw, customActions, children, ...props }: SideSheet.Props): import("react").ReactPortal | null;
5
+ export declare namespace SideSheet {
6
+ type Props = Omit<HTMLMotionProps<'div'>, 'children'> & PropsWithChildren & {
7
+ title: ReactNode;
8
+ wide?: boolean;
9
+ backdrop?: Backdrop;
10
+ disabled?: boolean;
11
+ disableDismiss?: boolean;
12
+ disableOverlay?: boolean;
13
+ disableSave?: boolean;
14
+ canSave?: Provider<boolean> | Provider.Async<boolean>;
15
+ renderSaveLabel?: Transform<State, string>;
16
+ onSave?: Callback.Void | Callback.AsyncVoid;
17
+ customActions?: ReactNode;
18
+ open?: boolean;
19
+ onOpened?: Callback.Void;
20
+ onClose?: Callback.Void;
21
+ onClosed?: Callback.Void;
22
+ };
23
+ type State = 'editing' | 'validating' | 'saving' | 'error' | 'saved';
24
+ type Backdrop = 'none' | 'glass';
25
+ }
26
+ export declare const Animations: Record<'background' | 'slideRight', Variants>;
@@ -0,0 +1,200 @@
1
+ "use strict";
2
+ var __rest = (this && this.__rest) || function (s, e) {
3
+ var t = {};
4
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
5
+ t[p] = s[p];
6
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
7
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
8
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
9
+ t[p[i]] = s[p[i]];
10
+ }
11
+ return t;
12
+ };
13
+ var __importDefault = (this && this.__importDefault) || function (mod) {
14
+ return (mod && mod.__esModule) ? mod : { "default": mod };
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.Animations = void 0;
18
+ exports.SideSheet = SideSheet;
19
+ const jsx_runtime_1 = require("react/jsx-runtime");
20
+ const funnel_core_1 = require("@nascentdigital/funnel-core");
21
+ const classnames_1 = __importDefault(require("classnames"));
22
+ const framer_motion_1 = require("framer-motion");
23
+ const react_1 = require("motion/react");
24
+ const react_2 = require("react");
25
+ const react_dom_1 = require("react-dom");
26
+ const Button_1 = require("./Button");
27
+ // component
28
+ function SideSheet(_a) {
29
+ var { className, title, wide, backdrop = 'none', disabled = false, disableDismiss = false, disableOverlay = false, disableSave = false, canSave = funnel_core_1.Fn.True, renderSaveLabel = onRenderSaveLabel, onSave: onSaveRaw = funnel_core_1.Fn.NoOp, open = false, onOpened: onOpenedRaw = funnel_core_1.Fn.NoOp, onClose: onCloseRaw = funnel_core_1.Fn.NoOp, onClosed: onClosedRaw = funnel_core_1.Fn.NoOp, onKeyDown: onKeyDownRaw, customActions, children } = _a, props = __rest(_a, ["className", "title", "wide", "backdrop", "disabled", "disableDismiss", "disableOverlay", "disableSave", "canSave", "renderSaveLabel", "onSave", "open", "onOpened", "onClose", "onClosed", "onKeyDown", "customActions", "children"]);
30
+ // bind state + hooksb= + refs
31
+ const [state, setState] = (0, react_2.useState)('editing');
32
+ const locked = disabled || state === 'validating' || state === 'saving' || state === 'saved';
33
+ const id = (0, react_2.useId)();
34
+ const openRef = (0, react_2.useRef)(open);
35
+ openRef.current = open;
36
+ const onOpenedRef = (0, react_2.useRef)(onOpenedRaw);
37
+ onOpenedRef.current = onOpenedRaw;
38
+ const onClosedRef = (0, react_2.useRef)(onClosedRaw);
39
+ onClosedRef.current = onClosedRaw;
40
+ // reset state on open
41
+ (0, react_2.useEffect)(() => {
42
+ if (open) {
43
+ setState('editing');
44
+ }
45
+ }, [open]);
46
+ // lock tab focus
47
+ (0, react_2.useEffect)(() => {
48
+ // lock when opened
49
+ let unlock = undefined;
50
+ if (open) {
51
+ unlock = funnel_core_1.Browser.lockFocus(`#${CSS.escape(id)}`);
52
+ }
53
+ // unlock on unmount
54
+ return () => unlock && unlock();
55
+ }, [open, id]);
56
+ // skip for SSR
57
+ if (!funnel_core_1.Browser.available) {
58
+ return null;
59
+ }
60
+ // helpers
61
+ const onSave = async (e) => {
62
+ console.debug('[SideSheet/onSave]', { state, disabled, open });
63
+ // consume event
64
+ if (e) {
65
+ e.preventDefault();
66
+ e.stopPropagation();
67
+ }
68
+ // ignore if state is invalid
69
+ if (state !== 'editing' && state !== 'error') {
70
+ return;
71
+ }
72
+ // try to save
73
+ try {
74
+ // update state
75
+ setState('validating');
76
+ // skip if not valid
77
+ const valid = await Promise.resolve(canSave());
78
+ if (!valid) {
79
+ setState('error');
80
+ return;
81
+ }
82
+ // update state
83
+ setState('saving');
84
+ // try to save
85
+ await Promise.resolve(onSaveRaw());
86
+ // mark as saved
87
+ setState('saved');
88
+ }
89
+ // handle error
90
+ catch (e) {
91
+ console.warn('[SideSheet/onSave] error', e);
92
+ // update state
93
+ setState('error');
94
+ }
95
+ };
96
+ const onClose = (e) => {
97
+ // consume event
98
+ if (e) {
99
+ e.preventDefault();
100
+ e.stopPropagation();
101
+ }
102
+ // close
103
+ onCloseRaw();
104
+ };
105
+ const onKeyDown = async (e) => {
106
+ // ignore if disabled OR state is invalid
107
+ if (disabled || (state !== 'editing' && state !== 'error')) {
108
+ return;
109
+ }
110
+ // cancel on escape
111
+ switch (e.key) {
112
+ case 'Escape':
113
+ if (!disableDismiss) {
114
+ onClose(e);
115
+ }
116
+ break;
117
+ case 'Enter':
118
+ await onSave(e);
119
+ break;
120
+ }
121
+ };
122
+ const onAnimationComplete = (definition) => {
123
+ if (definition === 'visible') {
124
+ onOpenedRef.current();
125
+ }
126
+ else if (definition === 'exit') {
127
+ onClosedRef.current();
128
+ }
129
+ };
130
+ const onBackdropClick = (e) => {
131
+ // close only if the backdrop is directly clicked
132
+ const target = e.target;
133
+ if ((target === null || target === void 0 ? void 0 : target.id) === id) {
134
+ // stop bubbling
135
+ e.stopPropagation();
136
+ // close
137
+ onClose();
138
+ }
139
+ };
140
+ // render
141
+ return (0, react_dom_1.createPortal)((0, jsx_runtime_1.jsx)(react_1.AnimatePresence, { children: open &&
142
+ (0, jsx_runtime_1.jsxs)(framer_motion_1.motion.div, { id: id, className: (0, classnames_1.default)('fixed inset-0 z-popup', {
143
+ 'pointer-events-none': disableOverlay
144
+ }), variants: exports.Animations.background, initial: "initial", animate: "animate", exit: "exit", onAnimationComplete: definition => onAnimationComplete(definition), onClick: disableDismiss || locked ? undefined : onBackdropClick, layout: true, children: [backdrop !== 'none' &&
145
+ (0, jsx_runtime_1.jsx)(framer_motion_1.motion.div, { className: (0, classnames_1.default)('fixed inset-0 z-backdrop pointer-events-none', {
146
+ 'backdrop-blur-[9px] bg-glass/backdrop will-change-[backdrop-filter]': backdrop === 'glass'
147
+ }) }, "backdrop"), (0, jsx_runtime_1.jsx)(framer_motion_1.motion.div, Object.assign({}, props, { className: (0, classnames_1.default)(className, 'fixed right-0 inset-y-0 pointer-events-none', {
148
+ 'w-full sm:w-[380px]': !wide,
149
+ 'w-full md:w-screen-sm': wide,
150
+ }), variants: exports.Animations.slideRight, initial: "initial", animate: "animate", exit: "exit", onKeyDown: onKeyDown, tabIndex: -1, children: (0, jsx_runtime_1.jsxs)("div", { className: (0, classnames_1.default)(className, 'w-full h-full max-h-full pointer-events-auto', 'flex flex-col', 'bg-background border-l border-solid border-border-l1 rounded-tl-md rounded-bl-md'), children: [(0, jsx_runtime_1.jsx)("h2", { className: "heading-md text-content-primary p-l8", children: title }), (0, jsx_runtime_1.jsx)("div", { className: "flex-1 overflow-y-scroll", children: children }), (0, jsx_runtime_1.jsxs)("div", { className: "flex justify-end items-center gap-l4 p-l8", children: [(0, jsx_runtime_1.jsx)(Button_1.Button, { variant: "text", size: "sm", disabled: locked, onClick: onClose, children: "Cancel" }), customActions &&
151
+ (0, jsx_runtime_1.jsx)("div", { className: "flex-1 flex justify-end items-center gap-l4", children: customActions }), (0, jsx_runtime_1.jsx)(Button_1.Button, { variant: "primary", size: "sm", loading: state === 'saving', disabled: disableSave || locked, onClick: onSave, children: renderSaveLabel(state) })] })] }) }))] }, id) }), document.body);
152
+ }
153
+ // helpers
154
+ exports.Animations = {
155
+ background: {
156
+ hidden: {
157
+ opacity: 0
158
+ },
159
+ visible: {
160
+ opacity: 1,
161
+ transition: {
162
+ ease: 'easeIn',
163
+ duration: 0.3,
164
+ when: 'beforeChildren'
165
+ }
166
+ },
167
+ exit: {
168
+ opacity: 0,
169
+ transition: {
170
+ ease: 'easeIn',
171
+ duration: 0.15,
172
+ bounce: 0,
173
+ when: 'afterChildren'
174
+ }
175
+ }
176
+ },
177
+ slideRight: {
178
+ initial: {
179
+ x: '100%'
180
+ },
181
+ animate: {
182
+ x: '0'
183
+ },
184
+ exit: {
185
+ x: '100%'
186
+ }
187
+ }
188
+ };
189
+ function onRenderSaveLabel(state) {
190
+ switch (state) {
191
+ case 'editing':
192
+ case 'validating':
193
+ case 'error':
194
+ return 'Save';
195
+ case 'saving':
196
+ return 'Saving...';
197
+ case 'saved':
198
+ return 'Saved';
199
+ }
200
+ }
@@ -25,6 +25,7 @@ export * from './Profile';
25
25
  export * from './QuickInput';
26
26
  export * from './QuickPicker';
27
27
  export * from './SegmentedButton';
28
+ export * from './SideSheet';
28
29
  export * from './Sortable';
29
30
  export * from './Tabs';
30
31
  export * from './Toggle';
@@ -41,6 +41,7 @@ __exportStar(require("./Profile"), exports);
41
41
  __exportStar(require("./QuickInput"), exports);
42
42
  __exportStar(require("./QuickPicker"), exports);
43
43
  __exportStar(require("./SegmentedButton"), exports);
44
+ __exportStar(require("./SideSheet"), exports);
44
45
  __exportStar(require("./Sortable"), exports);
45
46
  __exportStar(require("./Tabs"), exports);
46
47
  __exportStar(require("./Toggle"), exports);
@@ -9,8 +9,7 @@ const LegacyBlockConfigEditor_1 = require("./LegacyBlockConfigEditor");
9
9
  // component
10
10
  function BlockConfig({ className, block: blockOrSectionBlock }) {
11
11
  // bind ref
12
- const editorRef = (0, react_1.useRef)(null);
13
- const legacyEditorRef = (0, react_1.useRef)(null);
12
+ const [configuration, setConfiguration] = (0, react_1.useState)();
14
13
  // skip if there's no config
15
14
  const block = blockOrSectionBlock instanceof core_1.SectionBlock
16
15
  ? blockOrSectionBlock.block
@@ -19,25 +18,11 @@ function BlockConfig({ className, block: blockOrSectionBlock }) {
19
18
  if (config.length === 0) {
20
19
  return undefined;
21
20
  }
22
- // helpers
23
- const onEdit = async (event) => {
24
- var _a, _b;
25
- // use new configurator (if enabled)
26
- if (process.env.NEXT_PUBLIC_EDITOR_BLOCK_CONFIG === 'experimental') {
27
- await ((_a = editorRef.current) === null || _a === void 0 ? void 0 : _a.edit({
28
- block: blockOrSectionBlock,
29
- anchor: event.currentTarget
30
- }));
31
- }
32
- // or use legacy version
33
- else {
34
- await ((_b = legacyEditorRef.current) === null || _b === void 0 ? void 0 : _b.edit({
35
- block: blockOrSectionBlock,
36
- anchor: event.currentTarget
37
- }));
38
- }
39
- };
40
21
  // render
41
22
  const valid = config.every(field => field.valid);
42
- return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(core_1.StatusChip, { className: className, variant: valid ? 'success' : 'error', onClick: onEdit, children: "Configure" }), (0, jsx_runtime_1.jsx)(BlockConfigEditor_1.BlockConfigEditor, { ref: editorRef }), (0, jsx_runtime_1.jsx)(LegacyBlockConfigEditor_1.LegacyBlockConfigEditor, { ref: legacyEditorRef })] }));
23
+ return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(core_1.StatusChip, { className: className, variant: valid ? 'success' : 'error', onClick: () => setConfiguration(block.getConfiguration()), children: "Configure" }), process.env.NEXT_PUBLIC_EDITOR_BLOCK_CONFIG === 'experimental'
24
+ ?
25
+ (0, jsx_runtime_1.jsx)(BlockConfigEditor_1.BlockConfigEditor, { configuration: configuration, onClose: () => setConfiguration(undefined) })
26
+ :
27
+ (0, jsx_runtime_1.jsx)(LegacyBlockConfigEditor_1.LegacyBlockConfigEditor, { configuration: configuration, onClose: () => setConfiguration(undefined) })] }));
43
28
  }
@@ -1,13 +1,9 @@
1
- import { Rect } from '@nascentdigital/funnel-core';
2
- import { Block, SectionBlock } from '../../../core';
3
- export declare const BlockConfigEditor: import("react").ForwardRefExoticComponent<import("react").RefAttributes<BlockConfigEditor.Ref>>;
1
+ import { Callback } from '@nascentdigital/funnel-core';
2
+ import { BlockConfiguration } from '../../../core';
3
+ export declare function BlockConfigEditor({ configuration, onClose }: BlockConfigEditor.Props): import("react/jsx-runtime").JSX.Element | null;
4
4
  export declare namespace BlockConfigEditor {
5
- type Props = {};
6
- interface Ref {
7
- edit(options: Options): Promise<void>;
8
- }
9
- type Options = {
10
- block: Block | SectionBlock;
11
- anchor: HTMLElement | Rect;
5
+ type Props = {
6
+ configuration?: BlockConfiguration;
7
+ onClose: Callback.Void;
12
8
  };
13
9
  }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.BlockConfigEditor = void 0;
3
+ exports.BlockConfigEditor = BlockConfigEditor;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const funnel_core_1 = require("@nascentdigital/funnel-core");
6
6
  const client_1 = require("@nascentdigital/funnel-ui/client");
@@ -11,11 +11,8 @@ const core_1 = require("../../../core");
11
11
  const hooks_1 = require("../../hooks");
12
12
  const BlockPreview_1 = require("./BlockPreview");
13
13
  // editor
14
- exports.BlockConfigEditor = (0, react_2.forwardRef)(function BlockConfigEditor(_, ref) {
14
+ function BlockConfigEditor({ configuration, onClose }) {
15
15
  // bind state + hooks
16
- const [deferral, setDeferral] = (0, react_2.useState)(undefined);
17
- const [configuration, setConfiguration] = (0, react_2.useState)();
18
- const [anchor, setAnchor] = (0, react_2.useState)();
19
16
  const [blockView, setBlockView] = (0, react_2.useState)('desktop');
20
17
  const pageController = (0, hooks_1.usePageController)();
21
18
  const touch = (0, client_1.useTouch)();
@@ -24,40 +21,20 @@ exports.BlockConfigEditor = (0, react_2.forwardRef)(function BlockConfigEditor(_
24
21
  const settingsFields = blockView === 'desktop' ? desktopFields : mobileFields;
25
22
  // sync w/ configuration
26
23
  (0, core_1.useBlockConfiguration)(configuration);
27
- // bind ref
28
- (0, react_2.useImperativeHandle)(ref, () => ({
29
- edit: async ({ block, anchor }) => {
30
- // reset state
31
- setConfiguration(block.getConfiguration());
32
- setAnchor(anchor);
24
+ // reset to desktop when open
25
+ (0, react_2.useEffect)(() => {
26
+ if (configuration) {
33
27
  setBlockView('desktop');
34
- // create deferral
35
- const deferral = new funnel_core_1.Deferral();
36
- setDeferral(deferral);
37
- // wait until deferral is resolved
38
- return deferral.promise;
39
28
  }
40
- }), []);
29
+ }, [configuration]);
41
30
  // helpers
42
31
  const onSave = async () => {
43
32
  // save config
44
33
  configuration === null || configuration === void 0 ? void 0 : configuration.save();
45
34
  // wait for page to save
46
35
  await (0, rxjs_1.firstValueFrom)(pageController.saved$);
47
- // complete (we're done)
48
- deferral === null || deferral === void 0 ? void 0 : deferral.resolve();
49
- // refresh
50
- touch();
51
- };
52
- const onCancel = () => {
53
- // skip if missing or already completed
54
- if (!deferral || deferral.isComplete) {
55
- return;
56
- }
57
- // cancel
58
- deferral.resolve(undefined);
59
- // touch
60
- touch();
36
+ // complete
37
+ onClose();
61
38
  };
62
39
  const onChangePanelLayout = async (e) => {
63
40
  var _a;
@@ -88,10 +65,10 @@ exports.BlockConfigEditor = (0, react_2.forwardRef)(function BlockConfigEditor(_
88
65
  return null;
89
66
  }
90
67
  // render
91
- return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)(core_1.MiniEditor, { className: "w-[550px] max-w-full", title: "Block Configuration", open: !!deferral && !deferral.isComplete, anchor: anchor, anchorOffset: 4, onSave: onSave, onClose: onCancel, disableSave: !modified || !valid, customActions: (0, jsx_runtime_1.jsx)(core_1.Button, { variant: "secondary", size: "sm", disabled: configuration.isConfigInDefault, onClick: () => configuration.reset(), children: "Reset to Default" }), wide: true, children: [(0, jsx_runtime_1.jsxs)("section", { className: "flex items-center gap-l4", children: [(0, jsx_runtime_1.jsx)("span", { className: "label-sm text-content-primary flex-1", children: "Layout" }), (0, jsx_runtime_1.jsx)(core_1.Button, { variant: "secondary", size: "sm", iconLeft: "ArrowLeftRight", children: "Rearrange" }), (0, jsx_runtime_1.jsx)(core_1.Button, { variant: "secondary", size: "sm", iconLeft: "LayoutTemplate", onClick: onChangePanelLayout, children: "Change" })] }), blocksFields.length > 0 &&
68
+ return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)(core_1.SideSheet, { title: "Block Configuration", open: !!configuration, onSave: onSave, onClose: onClose, disableSave: !modified || !valid, customActions: (0, jsx_runtime_1.jsx)(core_1.Button, { variant: "secondary", size: "sm", disabled: configuration.isConfigInDefault, onClick: () => configuration.reset(), children: "Reset to Default" }), wide: true, children: [(0, jsx_runtime_1.jsxs)("section", { className: "flex items-center gap-l4", children: [(0, jsx_runtime_1.jsx)("span", { className: "label-sm text-content-primary flex-1", children: "Layout" }), (0, jsx_runtime_1.jsx)(core_1.Button, { variant: "secondary", size: "sm", iconLeft: "ArrowLeftRight", children: "Rearrange" }), (0, jsx_runtime_1.jsx)(core_1.Button, { variant: "secondary", size: "sm", iconLeft: "LayoutTemplate", onClick: onChangePanelLayout, children: "Change" })] }), blocksFields.length > 0 &&
92
69
  (0, jsx_runtime_1.jsx)("section", { className: "grid grid-cols-2 auto-rows-auto items-start gap-l6", children: (0, jsx_runtime_1.jsx)(core_1.FieldErrorController, { children: blocksFields.map(field => (0, jsx_runtime_1.jsx)(core_1.FieldEditor, { className: "col-span-2", field: field, variant: "wide" }, field.id)) }) }), (0, jsx_runtime_1.jsx)(core_1.Tabs, { items: blockViews, selectedItem: blockView, onSelect: setBlockView }), (0, jsx_runtime_1.jsx)("section", { className: "stacked p-l6 bg-surface1 rounded-md", children: (0, jsx_runtime_1.jsx)(react_1.AnimatePresence, { children: (0, jsx_runtime_1.jsx)(core_1.Animate, { effect: "fade", present: true, children: (0, jsx_runtime_1.jsx)(BlockPreview_1.BlockPreview, { variant: blockView, configuration: configuration }) }, blockView) }) }), settingsFields.length > 0 &&
93
70
  (0, jsx_runtime_1.jsx)("section", { className: "grid grid-cols-2 auto-rows-auto items-start gap-l6", children: (0, jsx_runtime_1.jsx)(core_1.FieldErrorController, { children: settingsFields.map(field => (0, jsx_runtime_1.jsx)(core_1.FieldEditor, { className: "col-span-2", field: field, variant: "wide" }, field.id)) }) }), (0, jsx_runtime_1.jsx)("hr", { className: "divider border-border-l1" })] }), (0, jsx_runtime_1.jsx)(core_1.QuickPicker, { ref: panelLayoutPickerRef, variant: "card", title: "Pick Block Layout", description: "Select an available layout for this block.", renderItem: (item) => renderPanelLayoutItem(item) })] }));
94
- });
71
+ }
95
72
  function renderPanelLayoutItem(item) {
96
73
  switch (item.id) {
97
74
  case 'stack.h':
@@ -1,13 +1,9 @@
1
- import { Rect } from '@nascentdigital/funnel-core';
2
- import { Block, SectionBlock } from '../../../core';
3
- export declare const LegacyBlockConfigEditor: import("react").ForwardRefExoticComponent<import("react").RefAttributes<LegacyBlockConfigEditor.Ref>>;
1
+ import { Callback } from '@nascentdigital/funnel-core';
2
+ import { BlockConfiguration } from '../../../core';
3
+ export declare function LegacyBlockConfigEditor({ configuration, onClose }: LegacyBlockConfigEditor.Props): import("react/jsx-runtime").JSX.Element;
4
4
  export declare namespace LegacyBlockConfigEditor {
5
- type Props = {};
6
- interface Ref {
7
- edit(options: Options): Promise<void>;
8
- }
9
- type Options = {
10
- block: Block | SectionBlock;
11
- anchor: HTMLElement | Rect;
5
+ type Props = {
6
+ configuration?: BlockConfiguration;
7
+ onClose: Callback.Void;
12
8
  };
13
9
  }
@@ -1,63 +1,35 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.LegacyBlockConfigEditor = void 0;
3
+ exports.LegacyBlockConfigEditor = LegacyBlockConfigEditor;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
- const funnel_core_1 = require("@nascentdigital/funnel-core");
6
- const client_1 = require("@nascentdigital/funnel-ui/client");
7
5
  const react_1 = require("react");
8
6
  const rxjs_1 = require("rxjs");
9
7
  const core_1 = require("../../../core");
10
8
  const hooks_1 = require("../../hooks");
11
9
  // editor
12
- exports.LegacyBlockConfigEditor = (0, react_1.forwardRef)(function LegacyBlockConfigEditor(_, ref) {
10
+ function LegacyBlockConfigEditor({ configuration, onClose }) {
13
11
  // bind state + hooks
14
- const [deferral, setDeferral] = (0, react_1.useState)(undefined);
15
- const [configuration, setConfiguration] = (0, react_1.useState)();
16
- const [anchor, setAnchor] = (0, react_1.useState)();
12
+ const [closing, setClosing] = (0, react_1.useState)(false);
17
13
  const pageController = (0, hooks_1.usePageController)();
18
- const touch = (0, client_1.useTouch)();
19
14
  const { modified = false, valid = false } = configuration !== null && configuration !== void 0 ? configuration : {};
20
15
  // sync w/ configuration
21
16
  (0, core_1.useBlockConfiguration)(configuration);
22
- // bind ref
23
- (0, react_1.useImperativeHandle)(ref, () => ({
24
- edit: async ({ block, anchor }) => {
25
- // reset state
26
- setConfiguration(block.getConfiguration());
27
- setAnchor(anchor);
28
- // create deferral
29
- const deferral = new funnel_core_1.Deferral();
30
- setDeferral(deferral);
31
- // wait until deferral is resolved
32
- return deferral.promise;
17
+ // reset closing on new open
18
+ (0, react_1.useEffect)(() => {
19
+ if (configuration) {
20
+ setClosing(false);
33
21
  }
34
- }), []);
22
+ }, [configuration]);
35
23
  // helpers
36
24
  const onSave = async () => {
37
25
  // save config
38
26
  configuration === null || configuration === void 0 ? void 0 : configuration.save();
39
27
  // wait for page to save
40
28
  await (0, rxjs_1.firstValueFrom)(pageController.saved$);
41
- // complete (we're done)
42
- deferral === null || deferral === void 0 ? void 0 : deferral.resolve();
43
- // refresh
44
- touch();
29
+ // complete
30
+ onClose();
45
31
  };
46
- const onCancel = () => {
47
- // skip if missing or already completed
48
- if (!deferral || deferral.isComplete) {
49
- return;
50
- }
51
- // cancel
52
- deferral.resolve(undefined);
53
- // touch
54
- touch();
55
- };
56
- // skip if there's no config
57
- if (!configuration) {
58
- return null;
59
- }
60
32
  // render
61
- const { layout } = configuration;
62
- return ((0, jsx_runtime_1.jsx)(core_1.MiniEditor, { title: "Block Configuration", open: !!deferral && !deferral.isComplete, anchor: anchor, anchorOffset: 4, onSave: onSave, onClose: onCancel, disableSave: !modified || !valid, customActions: (0, jsx_runtime_1.jsx)(core_1.Button, { variant: "secondary", size: "sm", disabled: configuration.isConfigInDefault, onClick: () => configuration.reset(), children: "Reset to Default" }), wide: true, children: (0, jsx_runtime_1.jsxs)("div", { className: "flex justify-start items-start gap-l9", children: [(0, jsx_runtime_1.jsxs)("section", { className: "flex flex-col items-start gap-l8", children: [(0, jsx_runtime_1.jsx)(core_1.SkeletonLayout, { variant: "desktop", layout: layout }), (0, jsx_runtime_1.jsx)(core_1.SkeletonLayout, { variant: "mobile", layout: layout })] }), (0, jsx_runtime_1.jsx)("div", { className: "grid grid-cols-[max-content_1fr] auto-rows-auto items-start gap-l6", children: (0, jsx_runtime_1.jsx)(core_1.FieldErrorController, { children: configuration.editableFields.map(field => (0, jsx_runtime_1.jsx)(core_1.FieldEditor, { className: "col-span-2", field: field, variant: "wide" }, field.id)) }) })] }) }));
63
- });
33
+ const { layout } = configuration !== null && configuration !== void 0 ? configuration : {};
34
+ return ((0, jsx_runtime_1.jsx)(core_1.SideSheet, { title: "Block Configuration", backdrop: "none", open: !!configuration || closing, onSave: onSave, onClose: onClose, disableSave: !modified || !valid, customActions: (0, jsx_runtime_1.jsx)(core_1.Button, { variant: "secondary", size: "sm", disabled: configuration === null || configuration === void 0 ? void 0 : configuration.isConfigInDefault, onClick: () => configuration === null || configuration === void 0 ? void 0 : configuration.reset(), children: "Reset to Default" }), wide: true, children: (0, jsx_runtime_1.jsxs)("div", { className: "h-full flex flex-col gap-l9 p-l8", children: [(0, jsx_runtime_1.jsxs)("section", { className: "flex flex-wrap justify-center items-start gap-l8", children: [(0, jsx_runtime_1.jsx)(core_1.SkeletonLayout, { variant: "desktop", layout: layout }), (0, jsx_runtime_1.jsx)(core_1.SkeletonLayout, { variant: "mobile", layout: layout })] }), (0, jsx_runtime_1.jsx)("div", { className: "grid grid-cols-[max-content_1fr] auto-rows-auto items-start gap-l6", children: (0, jsx_runtime_1.jsx)(core_1.FieldErrorController, { children: configuration === null || configuration === void 0 ? void 0 : configuration.editableFields.map(field => (0, jsx_runtime_1.jsx)(core_1.FieldEditor, { className: "col-span-2", field: field, variant: "wide" }, field.id)) }) })] }) }));
35
+ }
@@ -4711,9 +4711,6 @@ video {
4711
4711
  .w-\[50px\]{
4712
4712
  width: 50px;
4713
4713
  }
4714
- .w-\[550px\]{
4715
- width: 550px;
4716
- }
4717
4714
  .w-\[64px\]{
4718
4715
  width: 64px;
4719
4716
  }
@@ -4723,6 +4720,9 @@ video {
4723
4720
  .w-full{
4724
4721
  width: 100%;
4725
4722
  }
4723
+ .w-screen-sm{
4724
+ width: 600px;
4725
+ }
4726
4726
  .min-w-18{
4727
4727
  min-width: 18px;
4728
4728
  }
@@ -5035,6 +5035,12 @@ video {
5035
5035
  border-top-left-radius: 18px;
5036
5036
  border-top-right-radius: 18px;
5037
5037
  }
5038
+ .rounded-bl-md{
5039
+ border-bottom-left-radius: 12px;
5040
+ }
5041
+ .rounded-tl-md{
5042
+ border-top-left-radius: 12px;
5043
+ }
5038
5044
  .border{
5039
5045
  border-width: 1px;
5040
5046
  }
@@ -6201,6 +6207,10 @@ video {
6201
6207
  width: 80%;
6202
6208
  }
6203
6209
 
6210
+ .sm\:w-\[380px\]{
6211
+ width: 380px;
6212
+ }
6213
+
6204
6214
  .sm\:max-w-screen-sm{
6205
6215
  max-width: 480px;
6206
6216
  }
@@ -6231,6 +6241,10 @@ video {
6231
6241
  width: 280px;
6232
6242
  }
6233
6243
 
6244
+ .md\:w-screen-sm{
6245
+ width: 600px;
6246
+ }
6247
+
6234
6248
  .md\:grid-cols-3{
6235
6249
  grid-template-columns: repeat(3, minmax(0, 1fr));
6236
6250
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nascentdigital/funnel-editor",
3
- "version": "4.5.1",
3
+ "version": "4.5.2",
4
4
  "publishConfig": {
5
5
  "registry": "https://registry.npmjs.org/",
6
6
  "access": "restricted"
@@ -89,5 +89,5 @@
89
89
  "ts-jest": "^29",
90
90
  "type-fest": "^4"
91
91
  },
92
- "gitHead": "3348c90ff56f3dd931ab564f8a6ad13d454a4ca1"
92
+ "gitHead": "5b63f9cd8843cc111b4458c71aa031a974455241"
93
93
  }