@nascentdigital/funnel-editor 4.4.20 → 4.5.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.
Files changed (38) hide show
  1. package/dist/core/components/interaction/Animate.d.ts +2 -2
  2. package/dist/core/components/interaction/Animate.js +12 -10
  3. package/dist/core/components/interaction/QuickPicker.d.ts +23 -0
  4. package/dist/core/components/interaction/QuickPicker.js +95 -0
  5. package/dist/core/components/interaction/Tabs.d.ts +16 -0
  6. package/dist/core/components/interaction/Tabs.js +24 -0
  7. package/dist/core/components/interaction/index.d.ts +2 -0
  8. package/dist/core/components/interaction/index.js +2 -0
  9. package/dist/core/components/visuals/Icon.d.ts +1 -1
  10. package/dist/core/components/visuals/Icon.js +3 -0
  11. package/dist/core/components/visuals/SkeletonLayout.js +226 -33
  12. package/dist/core/domain/BlockConfiguration.d.ts +7 -1
  13. package/dist/core/domain/BlockConfiguration.js +27 -1
  14. package/dist/core/domain/Section.js +1 -1
  15. package/dist/page/components/BlockEditor.js +3 -3
  16. package/dist/page/components/{BlockConfig.d.ts → config/BlockConfig.d.ts} +1 -1
  17. package/dist/page/components/config/BlockConfig.js +43 -0
  18. package/dist/page/components/config/BlockConfigEditor.d.ts +13 -0
  19. package/dist/page/components/config/BlockConfigEditor.js +94 -0
  20. package/dist/page/components/config/BlockPreview.d.ts +8 -0
  21. package/dist/page/components/config/BlockPreview.js +13 -0
  22. package/dist/page/components/config/BlockView.d.ts +4 -0
  23. package/dist/page/components/config/BlockView.js +9 -0
  24. package/dist/page/components/config/LegacyBlockConfigEditor.d.ts +13 -0
  25. package/dist/page/components/{BlockConfig.js → config/LegacyBlockConfigEditor.js} +4 -29
  26. package/dist/page/components/config/ListLayoutPicker.d.ts +6 -0
  27. package/dist/page/components/config/ListLayoutPicker.js +13 -0
  28. package/dist/page/components/config/ListPanelEditor.d.ts +6 -0
  29. package/dist/page/components/config/ListPanelEditor.js +13 -0
  30. package/dist/page/components/config/MessagePanelEditor.d.ts +6 -0
  31. package/dist/page/components/config/MessagePanelEditor.js +13 -0
  32. package/dist/page/components/config/VisualPanelEditor.d.ts +6 -0
  33. package/dist/page/components/config/VisualPanelEditor.js +13 -0
  34. package/dist/page/components/config/index.d.ts +1 -0
  35. package/dist/page/components/config/index.js +17 -0
  36. package/dist/page/hooks/PageController.js +1 -1
  37. package/dist/styles/index.css +90 -3
  38. package/package.json +2 -2
@@ -1,7 +1,8 @@
1
- import { Variants } from 'motion/react';
1
+ import { Variants as MotionVariants } from 'motion/react';
2
2
  import { PropsWithChildren } from 'react';
3
3
  export declare function Animate({ className, present, layout, effect, children }: Animate.Props): import("react/jsx-runtime").JSX.Element | undefined;
4
4
  export declare namespace Animate {
5
+ const Variants: Readonly<Record<Animate.Effect, MotionVariants>>;
5
6
  type Props = PropsWithChildren & {
6
7
  className?: string;
7
8
  present?: boolean;
@@ -10,4 +11,3 @@ export declare namespace Animate {
10
11
  };
11
12
  type Effect = 'fade';
12
13
  }
13
- export declare const variants: Readonly<Record<Animate.Effect, Variants>>;
@@ -1,6 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.variants = void 0;
4
3
  exports.Animate = Animate;
5
4
  const jsx_runtime_1 = require("react/jsx-runtime");
6
5
  const react_1 = require("motion/react");
@@ -11,13 +10,16 @@ function Animate({ className, present = false, layout = false, effect = 'fade',
11
10
  return undefined;
12
11
  }
13
12
  // render
14
- return ((0, jsx_runtime_1.jsx)(react_1.motion.div, { className: className, variants: exports.variants[effect], initial: "initial", animate: "animate", exit: "exit", layout: layout, children: children }));
13
+ return ((0, jsx_runtime_1.jsx)(react_1.motion.div, { className: className, variants: Animate.Variants[effect], initial: "initial", animate: "animate", exit: "exit", layout: layout, children: children }));
15
14
  }
16
- // helpers
17
- exports.variants = {
18
- fade: {
19
- initial: { opacity: 0 },
20
- animate: { opacity: 1 },
21
- exit: { opacity: 0 }
22
- }
23
- };
15
+ // extension
16
+ (function (Animate) {
17
+ // variants
18
+ Animate.Variants = {
19
+ fade: {
20
+ initial: { opacity: 0, transition: { when: 'beforeChildren' } },
21
+ animate: { opacity: 1, transition: { ease: "easeIn", when: 'beforeChildren' } },
22
+ exit: { opacity: 0, transition: { ease: "easeOut", when: 'afterChildren' } }
23
+ }
24
+ };
25
+ })(Animate || (exports.Animate = Animate = {}));
@@ -0,0 +1,23 @@
1
+ import { RequiredBy } from '@nascentdigital/funnel-core';
2
+ import { Popup } from './Popup';
3
+ export declare const QuickPicker: import("react").ForwardRefExoticComponent<QuickPicker.Props & import("react").RefAttributes<QuickPicker.Ref<string, QuickPicker.Item<string>>>>;
4
+ export declare namespace QuickPicker {
5
+ type Item<TId extends string = string> = {
6
+ id: TId;
7
+ };
8
+ type Props = {
9
+ title: string;
10
+ description?: string;
11
+ };
12
+ interface Ref<TId extends string, TItem extends Item<TId>> {
13
+ readonly isOpen: boolean;
14
+ open(args: CaptureArgs<TId, TItem>): Promise<TId | undefined>;
15
+ close(): void;
16
+ }
17
+ type CaptureArgs<TId extends string, TItem extends Item<TId>> = PopupProps & {
18
+ items: ReadonlyArray<TItem>;
19
+ selectedItem?: TId;
20
+ };
21
+ }
22
+ type PopupProps = RequiredBy<Omit<Popup.Props, 'className' | 'animation' | 'disableClickDismiss' | 'open' | 'onOpened' | 'onClose' | 'onClosed' | 'children'>, 'anchor' | 'placement'>;
23
+ export {};
@@ -0,0 +1,95 @@
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.QuickPicker = void 0;
18
+ const jsx_runtime_1 = require("react/jsx-runtime");
19
+ const funnel_core_1 = require("@nascentdigital/funnel-core");
20
+ const classnames_1 = __importDefault(require("classnames"));
21
+ const react_1 = require("react");
22
+ const Popup_1 = require("./Popup");
23
+ // component
24
+ exports.QuickPicker = (0, react_1.forwardRef)(function QuickPicker({ title, description }, ref) {
25
+ // bind state
26
+ const [opened, setOpened] = (0, react_1.useState)(false);
27
+ const [deferral, setDeferral] = (0, react_1.useState)();
28
+ const deferralRef = (0, react_1.useRef)(deferral);
29
+ deferralRef.current = deferral;
30
+ const [items, setItems] = (0, react_1.useState)([]);
31
+ const [selectedItem, setSelectedItem] = (0, react_1.useState)();
32
+ const [popupProps, setPopupProps] = (0, react_1.useState)();
33
+ // expose interface
34
+ (0, react_1.useImperativeHandle)(ref, () => ({
35
+ get isOpen() {
36
+ const deferral = deferralRef.current;
37
+ return !!deferral && !deferral.isComplete;
38
+ },
39
+ open: async (_a) => {
40
+ var { items, selectedItem } = _a, popupProps = __rest(_a, ["items", "selectedItem"]);
41
+ // reset
42
+ setItems(items);
43
+ setSelectedItem(selectedItem);
44
+ setPopupProps(popupProps);
45
+ // create/set deferral
46
+ const deferral = new funnel_core_1.Deferral();
47
+ setDeferral(deferral);
48
+ // await result
49
+ const result = await deferral.promise;
50
+ // clear deferral
51
+ setDeferral(undefined);
52
+ // return deferral
53
+ return result;
54
+ },
55
+ close: () => { var _a; return (_a = deferralRef.current) === null || _a === void 0 ? void 0 : _a.resolve(undefined); }
56
+ }), []);
57
+ // helpers
58
+ const onPick = (e) => {
59
+ // stop propagation
60
+ e.preventDefault();
61
+ e.stopPropagation();
62
+ // resolve
63
+ deferral === null || deferral === void 0 ? void 0 : deferral.resolve(selectedItem);
64
+ };
65
+ const onClose = (e) => {
66
+ // stop propagation
67
+ if (e) {
68
+ e.preventDefault();
69
+ e.stopPropagation();
70
+ }
71
+ // ignore if already complete
72
+ if (deferral === null || deferral === void 0 ? void 0 : deferral.isComplete) {
73
+ return;
74
+ }
75
+ // complete
76
+ deferral === null || deferral === void 0 ? void 0 : deferral.resolve(undefined);
77
+ };
78
+ const onKeyDown = (e) => {
79
+ // handle keys
80
+ switch (e.key) {
81
+ case 'Enter':
82
+ onPick(e);
83
+ break;
84
+ case 'Escape':
85
+ onClose(e);
86
+ break;
87
+ }
88
+ };
89
+ // render
90
+ return ((0, jsx_runtime_1.jsxs)(Popup_1.Popup, Object.assign({}, (popupProps !== null && popupProps !== void 0 ? popupProps : {}), { className: (0, classnames_1.default)('glass-overlay w-[372px] flex flex-col gap-l8 p-l8 interpolate', 'border-t border-solid border-glass-stroke-l1 rounded-md', {
91
+ 'opacity-none': !opened,
92
+ 'opacity-full': opened
93
+ }), animation: "out", open: !!deferral, onOpened: () => setOpened(true), onClose: onClose, onClosed: () => setOpened(false), children: [(0, jsx_runtime_1.jsxs)("section", { children: [(0, jsx_runtime_1.jsx)("h3", { className: "heading-sm text-content-primary", children: title }), description &&
94
+ (0, jsx_runtime_1.jsx)("p", { className: "body-sm text-content-secondary", children: description })] }), (0, jsx_runtime_1.jsx)("section", { children: "Items" })] })));
95
+ });
@@ -0,0 +1,16 @@
1
+ import { Callback } from '@nascentdigital/funnel-core';
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;
4
+ export declare namespace Tabs {
5
+ type Item<TId extends string = string> = {
6
+ id: TId;
7
+ label: string;
8
+ icon?: Icon.Variant;
9
+ };
10
+ type Props<TId extends string = string> = {
11
+ className?: string;
12
+ items: ReadonlyArray<Item<TId>>;
13
+ selectedItem: TId;
14
+ onSelect?: Callback<TId>;
15
+ };
16
+ }
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.Tabs = Tabs;
7
+ const jsx_runtime_1 = require("react/jsx-runtime");
8
+ const funnel_core_1 = require("@nascentdigital/funnel-core");
9
+ const classnames_1 = __importDefault(require("classnames"));
10
+ const framer_motion_1 = require("framer-motion");
11
+ const visuals_1 = require("../visuals");
12
+ // component
13
+ function Tabs({ className, items, selectedItem, onSelect = funnel_core_1.Fn.NoOp }) {
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', {
16
+ '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
+ '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)) }) }));
24
+ }
@@ -23,7 +23,9 @@ export * from './Picker';
23
23
  export * from './Popup';
24
24
  export * from './Profile';
25
25
  export * from './QuickInput';
26
+ export * from './QuickPicker';
26
27
  export * from './SegmentedButton';
27
28
  export * from './Sortable';
29
+ export * from './Tabs';
28
30
  export * from './Toggle';
29
31
  export * from './Touchable';
@@ -39,7 +39,9 @@ __exportStar(require("./Picker"), exports);
39
39
  __exportStar(require("./Popup"), exports);
40
40
  __exportStar(require("./Profile"), exports);
41
41
  __exportStar(require("./QuickInput"), exports);
42
+ __exportStar(require("./QuickPicker"), exports);
42
43
  __exportStar(require("./SegmentedButton"), exports);
43
44
  __exportStar(require("./Sortable"), exports);
45
+ __exportStar(require("./Tabs"), exports);
44
46
  __exportStar(require("./Toggle"), exports);
45
47
  __exportStar(require("./Touchable"), exports);
@@ -6,7 +6,7 @@ export declare namespace Icon {
6
6
  size?: Size;
7
7
  };
8
8
  type Size = 'xl' | 'lg' | 'md' | 'sm' | 'xs' | 'nav' | 'card' | Sizing;
9
- type Variant = 'Archive' | 'ArrowLeft' | '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' | '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';
10
10
  namespace Variant {
11
11
  function filter(value: any): value is Variant;
12
12
  }
@@ -20,6 +20,7 @@ function Icon({ className, variant, size: sizing = 'md' }) {
20
20
  const values = [
21
21
  'Archive',
22
22
  'ArrowLeft',
23
+ 'ArrowLeftRight',
23
24
  'Atom',
24
25
  'Blocks',
25
26
  'Bold',
@@ -111,6 +112,8 @@ function getIcon(variant) {
111
112
  return lucide_react_1.Archive;
112
113
  case 'ArrowLeft':
113
114
  return lucide_react_1.ArrowLeft;
115
+ case 'ArrowLeftRight':
116
+ return lucide_react_1.ArrowLeftRight;
114
117
  case 'Atom':
115
118
  return lucide_react_1.Atom;
116
119
  case 'Blocks':
@@ -27,17 +27,20 @@ function SkeletonLayout({ variant, layout }) {
27
27
  // helpers
28
28
  var Skeleton;
29
29
  (function (Skeleton) {
30
- let Headline;
31
- (function (Headline) {
32
- Headline.md = 'h-8 bg-ix-primary rounded-xxs overflow-hidden';
33
- Headline.sm = 'h-6 bg-ix-primary rounded-xxs overflow-hidden';
34
- })(Headline = Skeleton.Headline || (Skeleton.Headline = {}));
35
- let Caption;
36
- (function (Caption) {
37
- Caption.md = 'h-4 bg-draft/tint3 rounded-px overflow-hidden';
38
- Caption.sm = 'h-4 bg-draft/tint3 rounded-px overflow-hidden';
39
- })(Caption = Skeleton.Caption || (Skeleton.Caption = {}));
40
- function Media({ className, aspect }) {
30
+ function HeadlinePart({ className, size }) {
31
+ return ((0, jsx_runtime_1.jsx)("div", { className: (0, classnames_1.default)(className, 'bg-ix-primary rounded-xxs overflow-hidden', {
32
+ 'h-6 ': size === 'sm',
33
+ 'h-8': size === 'md'
34
+ }) }));
35
+ }
36
+ Skeleton.HeadlinePart = HeadlinePart;
37
+ function CaptionPart({ className, size }) {
38
+ return ((0, jsx_runtime_1.jsx)("div", { className: (0, classnames_1.default)(className, 'h-4 bg-draft/tint3 rounded-px overflow-hidden', {
39
+ '': size === 'sm'
40
+ }) }));
41
+ }
42
+ Skeleton.CaptionPart = CaptionPart;
43
+ function MediaPart({ className, aspect }) {
41
44
  return ((0, jsx_runtime_1.jsx)("div", { className: (0, classnames_1.default)(className, 'flex justify-center items-center bg-draft/tint3 rounded-xxs overflow-hidden', {
42
45
  'aspect-square': aspect === 'square' || aspect === 'auto',
43
46
  'aspect-phone': aspect === 'phone',
@@ -50,7 +53,21 @@ var Skeleton;
50
53
  absoluteStrokeWidth: false
51
54
  } }) }));
52
55
  }
53
- Skeleton.Media = Media;
56
+ Skeleton.MediaPart = MediaPart;
57
+ function IconPart({ className, size }) {
58
+ return ((0, jsx_runtime_1.jsx)("div", { className: (0, classnames_1.default)(className, 'bg-draft/tint3 rounded-full overflow-hidden', {
59
+ 'w-10 h-10': size === 'sm',
60
+ 'w-14 h-14': size === 'md'
61
+ }) }));
62
+ }
63
+ Skeleton.IconPart = IconPart;
64
+ function LogoPart({ className, size }) {
65
+ return ((0, jsx_runtime_1.jsx)("div", { className: (0, classnames_1.default)(className, 'bg-draft/tint3 rounded-px overflow-hidden', {
66
+ 'w-14 h-6': size === 'sm',
67
+ 'w-20 h-9': size === 'md'
68
+ }) }));
69
+ }
70
+ Skeleton.LogoPart = LogoPart;
54
71
  })(Skeleton || (Skeleton = {}));
55
72
  function renderLayout({ className, variant, layout }) {
56
73
  // TODO: render empty state if there's no layout
@@ -58,55 +75,57 @@ function renderLayout({ className, variant, layout }) {
58
75
  return null;
59
76
  }
60
77
  // render root layout
61
- const { type, items, } = layout.root;
78
+ const { type, items } = layout.root;
62
79
  return ((0, jsx_runtime_1.jsx)("div", { className: getContainerClasses({ className, variant, siblingCount: 1, item: layout.root }), children: items.map((item, parentOffset) => (0, jsx_runtime_1.jsx)(react_1.Fragment, { children: renderItem({
63
80
  className: (0, classnames_1.default)({
64
81
  'flex-1': type === 'row' && variant !== 'mobile'
65
82
  }),
66
83
  variant,
67
84
  parent: type,
85
+ parentNested: false,
68
86
  siblingCount: items.length,
69
87
  item,
70
88
  itemPosition: new ItemPosition({
71
89
  parent: type,
72
90
  parentOffset,
73
- parentSize: items.length,
91
+ parentSize: items.length
74
92
  })
75
93
  }) }, `${item.type}.${parentOffset}`)) }));
76
94
  }
77
- function renderItem({ className, parent, siblingCount, variant, item, itemPosition }) {
95
+ function renderItem({ className, parent, parentNested, siblingCount, variant, item, itemPosition }) {
78
96
  // render item
79
97
  const { type: itemType } = item;
80
98
  switch (itemType) {
81
99
  case 'row':
82
100
  case 'column':
83
101
  case 'stack':
84
- return ((0, jsx_runtime_1.jsx)("div", { className: getContainerClasses({ className, siblingCount, variant, item }), children: item.items.map((item, parentOffset, items) => (0, jsx_runtime_1.jsx)(react_1.Fragment, { children: renderItem({
102
+ return ((0, jsx_runtime_1.jsx)("div", { className: getContainerClasses({ className, parent, siblingCount, variant, item }), children: item.items.map((item, parentOffset, items) => (0, jsx_runtime_1.jsx)(react_1.Fragment, { children: renderItem({
85
103
  className: (0, classnames_1.default)({
86
104
  'flex-1': itemType === 'row'
87
105
  }),
88
106
  variant,
89
107
  parent: itemType,
108
+ parentNested: true,
90
109
  siblingCount: items.length,
91
110
  item: item,
92
111
  itemPosition: new ItemPosition({
93
112
  parent: itemType,
94
113
  parentOffset,
95
- parentSize: items.length,
114
+ parentSize: items.length
96
115
  })
97
116
  }) }, `${item.type}.${parentOffset}`)) }));
98
117
  case 'message':
99
- return renderMessage({ className, parent, siblingCount, variant, item, itemPosition });
118
+ return renderMessage({ className, parent, parentNested, siblingCount, variant, item, itemPosition });
100
119
  case 'visual':
101
- return renderVisual({ className, parent, siblingCount, variant, item, itemPosition });
120
+ return renderVisual({ className, parent, parentNested, siblingCount, variant, item, itemPosition });
102
121
  case 'list':
103
- return renderList({ className, parent, siblingCount, variant, item, itemPosition });
122
+ return renderList({ className, parent, parentNested, siblingCount, variant, item, itemPosition });
104
123
  default:
105
124
  console.error('[SkeletonLayout] no support for BlockLayout item', item);
106
125
  return null;
107
126
  }
108
127
  }
109
- function getContainerClasses({ className, variant, item }) {
128
+ function getContainerClasses({ className, variant, parent, item }) {
110
129
  const { type, alignY } = item;
111
130
  // row style
112
131
  if (type === 'row' && variant !== 'mobile') {
@@ -120,7 +139,7 @@ function getContainerClasses({ className, variant, item }) {
120
139
  'grid-cols-[2fr,1fr]': item.ratio.desktop === '2/1',
121
140
  'grid-cols-[2fr,3fr]': item.ratio.desktop === '2/3',
122
141
  'grid-cols-[3fr,1fr]': item.ratio.desktop === '3/1',
123
- 'grid-cols-[3fr,2fr]': item.ratio.desktop === '3/2',
142
+ 'grid-cols-[3fr,2fr]': item.ratio.desktop === '3/2'
124
143
  });
125
144
  }
126
145
  // or stack
@@ -147,24 +166,198 @@ function renderMessage(args) {
147
166
  'items-end': textAlign.desktop === 'right'
148
167
  });
149
168
  // render
150
- return ((0, jsx_runtime_1.jsx)("div", { className: getPanelWrapClasses(args), children: (0, jsx_runtime_1.jsxs)("div", { className: (0, classnames_1.default)(messageClasses, getPanelClasses(Object.assign(Object.assign({}, args), { className: 'flex flex-col' }))), children: [(0, jsx_runtime_1.jsx)("div", { className: (0, classnames_1.default)('w-full mb-l2', Skeleton.Headline.md) }), (0, jsx_runtime_1.jsx)("div", { className: (0, classnames_1.default)('w-full mb-px', Skeleton.Caption.md) }), (0, jsx_runtime_1.jsx)("div", { className: (0, classnames_1.default)('w-full mb-px', Skeleton.Caption.md) }), (0, jsx_runtime_1.jsx)("div", { className: (0, classnames_1.default)('w-20', Skeleton.Caption.md) })] }) }));
169
+ return ((0, jsx_runtime_1.jsx)("div", { className: getPanelWrapClasses(args), children: (0, jsx_runtime_1.jsxs)("div", { className: (0, classnames_1.default)(messageClasses, getPanelClasses(Object.assign(Object.assign({}, args), { className: 'flex flex-col' }))), children: [(0, jsx_runtime_1.jsx)(Skeleton.HeadlinePart, { className: "w-full mb-l2", size: "md" }), (0, jsx_runtime_1.jsx)(Skeleton.CaptionPart, { className: "w-full mb-px", size: "md" }), (0, jsx_runtime_1.jsx)(Skeleton.CaptionPart, { className: "w-full mb-px", size: "md" }), (0, jsx_runtime_1.jsx)(Skeleton.CaptionPart, { className: "w-20", size: "md" })] }) }));
151
170
  }
152
171
  function renderVisual(args) {
153
172
  // resolve props
154
173
  const { variant, item: { aspectRatio }, parent } = args;
155
174
  const aspect = variant === 'mobile' ? aspectRatio.mobile : aspectRatio.desktop;
156
175
  // render
157
- return ((0, jsx_runtime_1.jsx)("div", { className: getPanelWrapClasses(args), children: (0, jsx_runtime_1.jsx)("div", { className: (0, classnames_1.default)(getPanelClasses(Object.assign(Object.assign({}, args), { className: '' }))), children: (0, jsx_runtime_1.jsx)(Skeleton.Media, { className: "w-full", aspect: aspect }) }) }));
176
+ return ((0, jsx_runtime_1.jsx)("div", { className: getPanelWrapClasses(args), children: (0, jsx_runtime_1.jsx)("div", { className: (0, classnames_1.default)(getPanelClasses(Object.assign(Object.assign({}, args), { className: '' }))), children: (0, jsx_runtime_1.jsx)(Skeleton.MediaPart, { className: "w-full", aspect: aspect }) }) }));
158
177
  }
159
178
  function renderList(args) {
160
179
  // resolve props
161
- const { item: { itemType } } = args;
162
- const rows = 2;
163
- // render
164
- return ((0, jsx_runtime_1.jsx)("div", { className: getPanelWrapClasses(args), children: (0, jsx_runtime_1.jsx)("div", { className: (0, classnames_1.default)(getPanelClasses(Object.assign(Object.assign({}, args), { className: 'flex flex-col gap-l2' }))), children: funnel_core_1.ArrayUtils.series(rows, 1).map(row => (0, jsx_runtime_1.jsxs)("div", { className: "w-full flex flex-row", children: [itemType === 'message+visual' &&
165
- (0, jsx_runtime_1.jsx)("div", { children: (0, jsx_runtime_1.jsx)(Skeleton.Media, { className: "w-full", aspect: "square" }) }), (0, jsx_runtime_1.jsxs)("div", { className: "flex-1 flex flex-col", children: [(0, jsx_runtime_1.jsx)("div", { className: (0, classnames_1.default)('w-24 mb-l1', Skeleton.Headline.sm) }), (0, jsx_runtime_1.jsx)("div", { className: (0, classnames_1.default)('w-full mb-px', Skeleton.Caption.sm) }), (0, jsx_runtime_1.jsx)("div", { className: (0, classnames_1.default)('w-40', Skeleton.Caption.sm) })] })] }, `list.${row}`)) }) }));
180
+ const { variant, parent, parentNested, item: { layout: layoutRaw, itemType, itemCount: itemCountRaw } } = args;
181
+ const layout = variant === 'mobile'
182
+ ? layoutRaw.mobile
183
+ : layoutRaw.desktop;
184
+ // render a grid
185
+ if (layout === 'grid') {
186
+ return renderGrid(args);
187
+ }
188
+ // or render row/column
189
+ const itemCount = variant === 'mobile'
190
+ ? itemCountRaw.mobile
191
+ : itemCountRaw.desktop;
192
+ return ((0, jsx_runtime_1.jsx)("div", { className: getPanelWrapClasses(args), children: (0, jsx_runtime_1.jsx)("div", { className: getListPanelClasses(args), children: funnel_core_1.ArrayUtils.series(itemCount, 1).map(row => (0, jsx_runtime_1.jsx)(react_1.Fragment, { children: renderListItem({ variant, parent, parentNested, layout, itemType, index: row, count: itemCount }) }, `list.${row}`)) }) }));
193
+ }
194
+ function getListPanelClasses(args) {
195
+ // resolve props
196
+ const { variant, item: { layout: layoutRaw } } = args;
197
+ const layout = variant === 'mobile'
198
+ ? layoutRaw.mobile
199
+ : layoutRaw.desktop;
200
+ // determine list classes
201
+ let listClasses = '';
202
+ switch (layout) {
203
+ case 'row':
204
+ listClasses = 'grid grid-rows-1 auto-cols-auto grid-flow-col gap-l2';
205
+ break;
206
+ case 'column':
207
+ listClasses = 'flex flex-col gap-l2';
208
+ break;
209
+ }
210
+ // return completed classes
211
+ return getPanelClasses(Object.assign(Object.assign({}, args), { className: listClasses }));
212
+ }
213
+ function renderGrid(args) {
214
+ // determine props
215
+ const { variant, parent, parentNested, item: { itemType, itemCount: itemCountRaw } } = args;
216
+ const itemCount = variant === 'mobile'
217
+ ? itemCountRaw.mobile
218
+ : itemCountRaw.desktop;
219
+ const { panelClassName, panelItemClassName } = getGridPanelClasses(args);
220
+ // render grid (really a flexbox layout with controlled item widths)
221
+ return ((0, jsx_runtime_1.jsx)("div", { className: getPanelWrapClasses(args), children: (0, jsx_runtime_1.jsx)("div", { className: panelClassName, children: funnel_core_1.ArrayUtils.series(itemCount, 1).map(row => (0, jsx_runtime_1.jsx)(react_1.Fragment, { children: renderListItem({
222
+ className: panelItemClassName,
223
+ variant,
224
+ parent,
225
+ parentNested,
226
+ layout: 'grid',
227
+ itemType, index: row,
228
+ count: itemCount
229
+ }) }, `list.${row}`)) }) }));
230
+ }
231
+ function getGridPanelClasses(args) {
232
+ // resolve props
233
+ const { variant, parent, parentNested, item: { itemType, itemCount: itemCountRaw, itemAlign: itemAlignRaw } } = args;
234
+ const itemAlign = variant === 'mobile'
235
+ ? itemAlignRaw.mobile
236
+ : itemAlignRaw.desktop;
237
+ // determine list classes
238
+ const panelClasses = (0, classnames_1.default)('flex flex-wrap gap-l2', {
239
+ 'justify-center': itemAlign === 'center',
240
+ 'justify-end': itemAlign === 'end'
241
+ });
242
+ let panelItemClasses = '';
243
+ switch (itemType) {
244
+ case 'message':
245
+ case 'message+visual':
246
+ panelItemClasses = (0, classnames_1.default)({
247
+ 'w-full': variant === 'mobile' || (variant === 'desktop' && (parent === 'row' || parentNested)),
248
+ 'w-[50px]': variant === 'desktop' && parent !== 'row' && !parentNested
249
+ });
250
+ break;
251
+ case 'visual':
252
+ panelItemClasses = (0, classnames_1.default)({
253
+ '': variant === 'mobile' || (variant === 'desktop' && (parent === 'row' || parentNested)),
254
+ 'flex-1': variant === 'desktop' && parent !== 'row' && !parentNested
255
+ });
256
+ break;
257
+ case 'icon':
258
+ panelItemClasses = (0, classnames_1.default)({});
259
+ break;
260
+ case 'logo':
261
+ panelItemClasses = (0, classnames_1.default)({
262
+ '': variant === 'desktop',
263
+ '': variant === 'mobile'
264
+ });
265
+ break;
266
+ }
267
+ // return completed classes
268
+ return {
269
+ panelClassName: getPanelClasses(Object.assign(Object.assign({}, args), { className: panelClasses })),
270
+ panelItemClassName: panelItemClasses
271
+ };
272
+ }
273
+ function renderListItem({ className, variant, parent, parentNested, layout, itemType, index, count }) {
274
+ let itemClassName = '';
275
+ switch (itemType) {
276
+ case 'message':
277
+ case 'message+visual':
278
+ return ((0, jsx_runtime_1.jsxs)("div", { className: (0, classnames_1.default)(className, 'flex flex-row p-3 bg-background rounded-xxs'), children: [itemType === 'message+visual' &&
279
+ (0, jsx_runtime_1.jsx)("div", { className: "flex justify-center items-center", children: (0, jsx_runtime_1.jsx)("div", { className: "bg-content-tertiary rounded-full w-4 h-4" }) }), (0, jsx_runtime_1.jsxs)("div", { className: "flex-1 flex flex-col", children: [(0, jsx_runtime_1.jsx)(Skeleton.HeadlinePart, { className: "w-11/12 mb-l1", size: "sm" }), (0, jsx_runtime_1.jsx)(Skeleton.CaptionPart, { className: "w-full mb-px", size: "sm" }), (0, jsx_runtime_1.jsx)(Skeleton.CaptionPart, { className: "w-8/12", size: "sm" })] })] }));
280
+ case 'visual': {
281
+ // determine aspect
282
+ let aspect;
283
+ switch (layout) {
284
+ case 'column':
285
+ aspect = parent === 'row' || parentNested
286
+ ? 'widescreen'
287
+ : variant === 'mobile'
288
+ ? 'widescreen'
289
+ : 'cinema';
290
+ break;
291
+ case 'row':
292
+ aspect = parent === 'row' || parentNested
293
+ ? 'square'
294
+ : 'widescreen';
295
+ break;
296
+ case 'grid': {
297
+ // aspect for mobile/nested
298
+ if (variant === 'mobile' || parent === 'row' || parentNested) {
299
+ itemClassName = 'w-full';
300
+ aspect = count === 1
301
+ ? 'square'
302
+ : count <= 3
303
+ ? 'tv'
304
+ : 'widescreen';
305
+ }
306
+ // adjust aspect based on layout (if it's a grid)
307
+ else if (layout === 'grid') {
308
+ // set base class
309
+ itemClassName = 'min-w-[50px]';
310
+ // override base + pick aspect
311
+ switch (count) {
312
+ case 1:
313
+ aspect = 'widescreen';
314
+ break;
315
+ case 2:
316
+ aspect = 'widescreen';
317
+ break;
318
+ case 3:
319
+ itemClassName = index === 1
320
+ ? 'min-w-full'
321
+ : 'min-w-[50px]';
322
+ aspect = index === 1
323
+ ? 'cinema'
324
+ : 'tv';
325
+ break;
326
+ case 4:
327
+ aspect = 'tv';
328
+ break;
329
+ case 5:
330
+ itemClassName = index === 3
331
+ ? 'min-w-full'
332
+ : 'min-w-[50px]';
333
+ aspect = index === 3
334
+ ? 'cinema'
335
+ : 'tv';
336
+ break;
337
+ default:
338
+ aspect = 'widescreen';
339
+ break;
340
+ }
341
+ }
342
+ // otherwise standard
343
+ else {
344
+ aspect = 'widescreen';
345
+ }
346
+ break;
347
+ }
348
+ }
349
+ // create visual
350
+ return ((0, jsx_runtime_1.jsx)("div", { className: (0, classnames_1.default)(className, itemClassName), children: (0, jsx_runtime_1.jsx)(Skeleton.MediaPart, { aspect: aspect }) }));
351
+ }
352
+ case 'icon':
353
+ return ((0, jsx_runtime_1.jsx)(Skeleton.IconPart, { className: (0, classnames_1.default)(className, itemClassName), size: variant === 'mobile' ? 'md' : 'sm' }));
354
+ case 'logo':
355
+ return ((0, jsx_runtime_1.jsx)(Skeleton.LogoPart, { className: (0, classnames_1.default)(className, itemClassName), size: variant === 'mobile' ? 'md' : 'sm' }));
356
+ default:
357
+ return null;
358
+ }
166
359
  }
167
- function getPanelWrapClasses({ className, variant, parent, item: { mobileOrder, alignY, fullBleed }, itemPosition }) {
360
+ function getPanelWrapClasses({ className, variant, parent, parentNested, item: { mobileOrder, alignY, fullBleed }, itemPosition }) {
168
361
  return variant === 'mobile'
169
362
  ? (0, classnames_1.default)(className, 'w-full flex flex-col', {
170
363
  'justify-start': alignY === 'start',
@@ -178,18 +371,18 @@ function getPanelWrapClasses({ className, variant, parent, item: { mobileOrder,
178
371
  'items-start': alignY === 'start',
179
372
  'items-center': alignY === 'center',
180
373
  'items-end': alignY === 'end',
181
- 'px-l4': parent !== 'row' && !fullBleed.desktop,
374
+ 'px-l4': parent !== 'row' && !fullBleed.desktop && !parentNested,
182
375
  '-ml-l4': itemPosition.left && fullBleed.desktop,
183
376
  '-mr-l4': itemPosition.right && fullBleed.desktop
184
377
  });
185
378
  }
186
- function getPanelClasses({ className, variant, parent, item: { type, alignX, alignY, size } }) {
379
+ function getPanelClasses({ className, variant, parent, parentNested, item: { alignX, size } }) {
187
380
  // force full width for mobile
188
381
  if (variant === 'mobile') {
189
382
  return (0, classnames_1.default)(className);
190
383
  }
191
384
  // or size for desktop
192
- switch (size) {
385
+ switch (parent === 'row' || parentNested ? 'full' : size) {
193
386
  case 'xs':
194
387
  return (0, classnames_1.default)(className, 'col-span-4', {
195
388
  'col-start-1': alignX === 'start',