@nascentdigital/funnel-ui 4.5.3 → 4.5.6

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 (74) hide show
  1. package/dist/Tailwind.d.ts +1 -1
  2. package/dist/Tailwind.js +70 -24
  3. package/dist/client/interaction/Carousel.d.ts +1 -1
  4. package/dist/client/interaction/Carousel.js +28 -30
  5. package/dist/server/Page.d.ts +15 -0
  6. package/dist/server/Page.js +70 -0
  7. package/dist/server/blocks/Block.d.ts +4 -2
  8. package/dist/server/blocks/Block.js +9 -2
  9. package/dist/server/blocks/Editorial.d.ts +2 -2
  10. package/dist/server/blocks/Editorial.js +4 -4
  11. package/dist/server/blocks/Form.d.ts +2 -2
  12. package/dist/server/blocks/Form.js +3 -3
  13. package/dist/server/blocks/Projects.d.ts +29 -37
  14. package/dist/server/blocks/Projects.js +13 -84
  15. package/dist/server/blocks/RelatedPages.d.ts +58 -0
  16. package/dist/server/blocks/RelatedPages.js +99 -0
  17. package/dist/server/blocks/Stats.d.ts +19 -31
  18. package/dist/server/blocks/Stats.js +13 -57
  19. package/dist/server/blocks/Testimonial.d.ts +2 -2
  20. package/dist/server/blocks/Testimonial.js +4 -4
  21. package/dist/server/blocks/index.d.ts +1 -0
  22. package/dist/server/blocks/index.js +1 -0
  23. package/dist/server/entities/previews/MessagePreview.d.ts +2 -2
  24. package/dist/server/entities/previews/MessagePreview.js +1 -1
  25. package/dist/server/fields/Caption.js +3 -2
  26. package/dist/server/fields/Description.js +1 -1
  27. package/dist/server/fields/FreeFormContent.js +1 -1
  28. package/dist/server/fields/Headline.js +1 -1
  29. package/dist/server/fields/Quote.js +1 -1
  30. package/dist/server/fields/RichText.js +1 -0
  31. package/dist/server/index.d.ts +2 -0
  32. package/dist/server/index.js +2 -0
  33. package/dist/server/layouts/BentoLayout.d.ts +289 -0
  34. package/dist/server/layouts/BentoLayout.js +25 -0
  35. package/dist/server/layouts/index.d.ts +1 -0
  36. package/dist/server/layouts/index.js +1 -0
  37. package/dist/server/panels/BlockPanel.js +2 -3
  38. package/dist/server/panels/ListPanel.d.ts +4 -4
  39. package/dist/server/panels/ListPanel.js +13 -2
  40. package/dist/server/panels/MessagePanel.js +0 -3
  41. package/dist/server/panels/VisualPanel.js +0 -2
  42. package/dist/server/schema/Anchor.d.ts +1 -1
  43. package/dist/server/schema/Anchor.js +2 -2
  44. package/dist/server/schema/BlockConfig.js +1 -1
  45. package/dist/server/schema/ListPanelLayout.d.ts +6 -0
  46. package/dist/server/schema/ListPanelLayout.js +3 -0
  47. package/dist/server/utils/ReactUtils.d.ts +6 -0
  48. package/dist/server/utils/ReactUtils.js +25 -0
  49. package/dist/server/utils/index.d.ts +1 -0
  50. package/dist/server/utils/index.js +17 -0
  51. package/dist/styles/Page.css +5 -0
  52. package/dist/styles/Section.css +52 -0
  53. package/dist/styles/blocks/Block-spacing.css +31 -38
  54. package/dist/styles/blocks/Block-theming.css +2 -4
  55. package/dist/styles/blocks/MediaLayout.css +16 -0
  56. package/dist/styles/blocks/Projects.css +0 -9
  57. package/dist/styles/blocks/RelatedPages.css +13 -0
  58. package/dist/styles/blocks/Stats.css +1 -30
  59. package/dist/styles/components/Carousel.css +128 -0
  60. package/dist/styles/components/_index.css +2 -1
  61. package/dist/styles/entities/Person.css +4 -4
  62. package/dist/styles/entities/Project.css +8 -2
  63. package/dist/styles/entities/UniqueSellingPoint.css +3 -3
  64. package/dist/styles/fields/Caption.css +2 -2
  65. package/dist/styles/fields/Description.css +2 -2
  66. package/dist/styles/fields/Icon.css +1 -1
  67. package/dist/styles/fields/Link.css +1 -1
  68. package/dist/styles/fields/Quote.css +2 -2
  69. package/dist/styles/fields/Title.css +1 -1
  70. package/dist/styles/index.css +2 -0
  71. package/dist/styles/layouts/BentoLayout.css +41 -0
  72. package/dist/styles/layouts/_index.css +1 -0
  73. package/dist/styles/panels/TestimonialPanel.css +4 -0
  74. package/package.json +2 -3
@@ -193,7 +193,7 @@ export declare namespace Tailwind {
193
193
  handler: () => void;
194
194
  } | {
195
195
  handler: import("tailwindcss/types/config").PluginCreator;
196
- config?: Partial<import("tailwindcss/types/config").Config> | undefined;
196
+ config?: Partial<import("tailwindcss/types/config").Config>;
197
197
  })[];
198
198
  };
199
199
  function toPixel(value: number): PixelSize;
package/dist/Tailwind.js CHANGED
@@ -6,7 +6,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.Tailwind = void 0;
7
7
  const funnel_core_1 = require("@nascentdigital/funnel-core");
8
8
  const aspect_ratio_1 = __importDefault(require("@tailwindcss/aspect-ratio"));
9
- const container_queries_1 = __importDefault(require("@tailwindcss/container-queries"));
10
9
  const color_1 = __importDefault(require("color"));
11
10
  const fs_1 = __importDefault(require("fs"));
12
11
  const path_1 = __importDefault(require("path"));
@@ -168,28 +167,21 @@ var Tailwind;
168
167
  'panel-md': '739px',
169
168
  // 1280px with 102px gutters
170
169
  'panel-lg': '1075px'
171
- }
170
+ },
172
171
  }
173
172
  },
174
173
  // plugins
175
174
  plugins: [
176
175
  // aspect ratios
177
176
  aspect_ratio_1.default,
178
- // container queries
179
- container_queries_1.default,
180
- // custom selectors
177
+ // custom variants
181
178
  (0, plugin_1.default)(function ({ addVariant }) {
182
179
  // not first/last child
183
180
  addVariant('not-first', '&:not(:first-child)');
184
181
  addVariant('not-last', '&:not(:last-child)');
185
182
  }),
186
- // block theming
187
- (0, plugin_1.default)(function ({ addComponents, matchUtilities, matchVariant, theme }) {
188
- // blow up if there's no themes
189
- if (!themes) {
190
- throw new funnel_core_1.ArgumentError('Tailwind.createConfig() is missing "themes" arg');
191
- }
192
- // counter: provides custom counting control via tailwind
183
+ // counter utils
184
+ (0, plugin_1.default)(function ({ matchUtilities, theme }) {
193
185
  matchUtilities({
194
186
  'counter': (value) => ({
195
187
  counterReset: value
@@ -203,21 +195,75 @@ var Tailwind;
203
195
  }, {
204
196
  values: theme('counterName')
205
197
  });
206
- // block theming: provides custom counting control via tailwind
207
- Object.entries(themes).forEach(([themeId, { group = 'default' }]) => addComponents({
208
- // first block
209
- [`*:not(.nd-Block.nd-themeGroup-${group}) + .nd-Block.nd-themeGroup-${group}`]: {
210
- paddingTop: 'var(--nd-section-pt-value)'
198
+ }),
199
+ // max-width container queries
200
+ (0, plugin_1.default)(function ({ matchUtilities, matchVariant, theme }) {
201
+ var _a;
202
+ // helpers
203
+ function parseValue(value) {
204
+ var _a, _b;
205
+ const numericValue = (_b = (_a = value.match(/^(\d+\.\d+|\d+|\.\d+)\D+/)) === null || _a === void 0 ? void 0 : _a[1]) !== null && _b !== void 0 ? _b : null;
206
+ if (numericValue === null) {
207
+ return null;
208
+ }
209
+ return parseFloat(value);
210
+ }
211
+ // container query generator
212
+ const values = (_a = theme('containers')) !== null && _a !== void 0 ? _a : {};
213
+ matchUtilities({
214
+ '@container': (value, { modifier }) => {
215
+ return {
216
+ 'container-type': value,
217
+ 'container-name': modifier,
218
+ };
211
219
  },
212
- // continued block
213
- [`.nd-Block.nd-themeGroup-${group} + .nd-Block.nd-themeGroup-${group}`]: {
214
- paddingTop: 'var(--nd-section-gap-value)'
220
+ }, {
221
+ values: {
222
+ DEFAULT: 'inline-size',
223
+ normal: 'normal',
215
224
  },
216
- // last block
217
- [`.nd-Block.nd-themeGroup-${group}:has(+ :not(.nd-Block.nd-themeGroup-${group}))`]: {
218
- paddingBottom: 'var(--nd-section-pb-value)'
225
+ modifiers: 'any',
226
+ });
227
+ const variantOptions = {
228
+ values,
229
+ sort: (aVariant, zVariant) => {
230
+ var _a, _b;
231
+ const a = parseFloat(aVariant.value);
232
+ const z = parseFloat(zVariant.value);
233
+ if (a === null || z === null)
234
+ return 0;
235
+ // Sort values themselves regardless of unit
236
+ if (a - z !== 0)
237
+ return a - z;
238
+ const aLabel = (_a = aVariant.modifier) !== null && _a !== void 0 ? _a : '';
239
+ const zLabel = (_b = zVariant.modifier) !== null && _b !== void 0 ? _b : '';
240
+ // Explicitly move empty labels to the end
241
+ if (aLabel === '' && zLabel !== '') {
242
+ return 1;
243
+ }
244
+ else if (aLabel !== '' && zLabel === '') {
245
+ return -1;
246
+ }
247
+ // Sort labels alphabetically in the English locale
248
+ // We are intentionally overriding the locale because we do not want the sort to
249
+ // be affected by the machine's locale (be it a developer or CI environment)
250
+ return aLabel.localeCompare(zLabel, 'en', { numeric: true });
219
251
  }
220
- }));
252
+ };
253
+ matchVariant('@', (value = '', { modifier }) => {
254
+ const parsed = parseValue(value);
255
+ return parsed !== null ? `@container ${modifier !== null && modifier !== void 0 ? modifier : ''} (min-width: ${value})` : [];
256
+ }, variantOptions);
257
+ matchVariant('@max', (value = '', { modifier }) => {
258
+ const parsed = parseValue(value);
259
+ return parsed !== null ? `@container ${modifier !== null && modifier !== void 0 ? modifier : ''} (max-width: ${value})` : [];
260
+ }, variantOptions);
261
+ },
262
+ // configuration
263
+ {
264
+ theme: {
265
+ containers: {},
266
+ },
221
267
  })
222
268
  ]
223
269
  };
@@ -9,7 +9,7 @@ export declare namespace Carousel {
9
9
  infinite?: boolean;
10
10
  autoplay?: boolean;
11
11
  autoplayInterval?: Duration;
12
- disableClipping?: boolean;
12
+ disableClipping?: boolean | 'left' | 'right';
13
13
  renderIndicator?: Transform<RenderIndicatorArgs, ReactNode>;
14
14
  renderNavButton?: Transform<RenderNavButtonArgs, ReactNode>;
15
15
  renderPlayButton?: Transform<RenderPlayButtonArgs, ReactNode>;
@@ -204,11 +204,11 @@ function Carousel({ className, spacing = 0, infinite = false, autoplay = false,
204
204
  hotkeysRef.current = container;
205
205
  // render nothing if there are no children
206
206
  if (childCount === 0 || !children) {
207
- return ((0, jsx_runtime_1.jsx)("div", { className: (0, classnames_1.default)(className, 'Carousel-empty'), children: "No content" }));
207
+ return ((0, jsx_runtime_1.jsx)("div", { className: (0, classnames_1.default)(className, 'nd-Carousel-empty'), children: "No content" }));
208
208
  }
209
209
  // or render single child if there's nothing else
210
210
  else if (childCount === 1) {
211
- return ((0, jsx_runtime_1.jsx)("div", { className: (0, classnames_1.default)(className, 'Carousel-single'), children: children }));
211
+ return ((0, jsx_runtime_1.jsx)("div", { className: (0, classnames_1.default)(className, 'nd-Carousel-single'), children: children }));
212
212
  }
213
213
  const onDragStart = () => {
214
214
  // stop autoplay (if running)
@@ -305,29 +305,29 @@ function Carousel({ className, spacing = 0, infinite = false, autoplay = false,
305
305
  setPlay(!play);
306
306
  };
307
307
  // otherwise, render carousel
308
- return ((0, jsx_runtime_1.jsxs)("div", { ref: containerRef, className: (0, classnames_1.default)(className, 'Carousel-root outline-none', {
309
- 'Carousel-dragging': state === 'dragging',
310
- 'Carousel-animating': state === 'animating'
311
- }), tabIndex: -1, "aria-roledescription": "carousel", children: [(0, jsx_runtime_1.jsxs)("div", { ref: pagerRef, className: (0, classnames_1.default)('Carousel-pager', {
308
+ return ((0, jsx_runtime_1.jsxs)("div", { ref: containerRef, className: (0, classnames_1.default)(className, 'nd-Carousel-root outline-none', {
309
+ 'nd-Carousel-dragging': state === 'dragging',
310
+ 'nd-Carousel-animating': state === 'animating'
311
+ }), tabIndex: -1, "aria-roledescription": "carousel", children: [(0, jsx_runtime_1.jsxs)("div", { ref: pagerRef, className: (0, classnames_1.default)('nd-Carousel-pager', {
312
312
  'invisible': !pager,
313
- 'overflow-visible': disableClipping,
314
- 'overflow-hidden': !disableClipping,
313
+ 'nd-Carousel-disableClipping': disableClipping === true,
314
+ 'nd-Carousel-disableClipping-left': disableClipping === 'left',
315
+ 'nd-Carousel-disableClipping-right': disableClipping === 'right',
315
316
  'pointer-events-none': state !== 'ready' && state !== 'dragging'
316
- }), onPointerEnter: () => setInteracting(true), onPointerLeave: () => setInteracting(false), onPointerDown: () => setPlay(false), children: [(0, jsx_runtime_1.jsx)(react_1.motion.ol, { className: "Carousel-pages", style: { x: pagerTranslate }, drag: "x", dragMomentum: false, onDragStart: onDragStart, onDragEnd: onDragEnd, onDrag: onDrag, "aria-atomic": "false", "aria-live": canAutoplay && play && !interacting ? 'off' : 'polite', children: pages.map(({ id, order, page, index, active, virtual }) => (0, jsx_runtime_1.jsx)("li", Object.assign({ id: `${carouselId}-${id}`, className: (0, classnames_1.default)('Carousel-page', {
317
- 'Carousel-pageActive': active,
318
- 'Carousel-pagePrev': index === prevIndex,
319
- 'Carousel-pageNext': index === nextIndex,
320
- 'Carousel-pageDragActive': dragIndex !== undefined && dragIndex === index,
321
- 'Carousel-pageDragPrev': dragIndex !== undefined && (dragIndex - 1) === index,
322
- 'Carousel-pageDragNext': dragIndex !== undefined && (dragIndex + 1) === index,
323
- 'Carousel-pageHidden': state !== 'animating' &&
324
- ((!active && index !== prevIndex && index !== nextIndex && dragIndex === undefined)
325
- || (dragIndex !== undefined && dragIndex !== index && (dragIndex - 1) !== index && (dragIndex + 1) !== index))
317
+ }), onPointerEnter: () => setInteracting(true), onPointerLeave: () => setInteracting(false), onPointerDown: () => setPlay(false), children: [(0, jsx_runtime_1.jsx)(react_1.motion.ol, { className: "nd-Carousel-pages", style: { x: pagerTranslate }, drag: "x", dragMomentum: false, onDragStart: onDragStart, onDragEnd: onDragEnd, onDrag: onDrag, "aria-atomic": "false", "aria-live": canAutoplay && play && !interacting ? 'off' : 'polite', children: pages.map(({ id, order, page, index, active, virtual }) => (0, jsx_runtime_1.jsx)("li", Object.assign({ id: `${carouselId}-${id}`, className: (0, classnames_1.default)('nd-Carousel-page', {
318
+ 'nd-Carousel-pageActive': active,
319
+ 'nd-Carousel-pagePrev': index === prevIndex,
320
+ 'nd-Carousel-pageNext': index === nextIndex,
321
+ 'nd-Carousel-pageDragActive': dragIndex !== undefined && dragIndex === index,
322
+ 'nd-Carousel-pageDragPrev': dragIndex !== undefined && (dragIndex - 1) === index,
323
+ 'nd-Carousel-pageDragNext': dragIndex !== undefined && (dragIndex + 1) === index,
324
+ 'nd-Carousel-pageHidden': ((!active && index !== prevIndex && index !== nextIndex && dragIndex === undefined)
325
+ || (dragIndex !== undefined && dragIndex !== index && (dragIndex - 1) !== index && (dragIndex + 1) !== index))
326
326
  }), style: { margin: `0 ${spacing}px`, order } }, (!virtual && {
327
327
  role: 'tabpanel',
328
328
  'aria-label': `${index + 1} of ${childCount}`,
329
329
  'aria-labelledby': `${carouselId}_${index}_ref`
330
- }), { children: page }), id)) }), (0, jsx_runtime_1.jsx)("div", { className: "Carousel-pagesAlt", children: children.map((page, index) => (0, jsx_runtime_1.jsx)("div", { className: "Carousel-page", children: page }, index)) })] }), (0, jsx_runtime_1.jsxs)("nav", { className: "Carousel-controls", children: [renderNavButton
330
+ }), { children: page }), id)) }), (0, jsx_runtime_1.jsx)("div", { className: "nd-Carousel-pagesAlt", children: children.map((page, index) => (0, jsx_runtime_1.jsx)("div", { className: "nd-Carousel-page", children: page }, index)) })] }), (0, jsx_runtime_1.jsxs)("nav", { className: "nd-Carousel-controls", children: [renderNavButton
331
331
  ?
332
332
  renderNavButton({
333
333
  type: 'prev',
@@ -336,7 +336,7 @@ function Carousel({ className, spacing = 0, infinite = false, autoplay = false,
336
336
  : infinite || dragIndex > 0,
337
337
  enabled: prevIndex !== undefined,
338
338
  props: {
339
- className: 'Carousel-prev',
339
+ className: 'nd-Carousel-prev',
340
340
  onClick: onPrev,
341
341
  onPointerEnter: () => setInteracting(true),
342
342
  onPointerLeave: () => setInteracting(false),
@@ -345,7 +345,7 @@ function Carousel({ className, spacing = 0, infinite = false, autoplay = false,
345
345
  }
346
346
  })
347
347
  :
348
- (0, jsx_runtime_1.jsx)("button", { className: "Carousel-prev", disabled: prevIndex === undefined, onClick: onPrev, onPointerEnter: () => setInteracting(true), onPointerLeave: () => setInteracting(false), "aria-label": prevIndex === undefined ? 'No previous slide' : `Move to slide ${prevIndex + 1} of ${childCount}`, "aria-disabled": prevIndex === undefined, children: "Prev" }), renderNavButton
348
+ (0, jsx_runtime_1.jsx)("button", { className: "nd-Carousel-prev", disabled: prevIndex === undefined, onClick: onPrev, onPointerEnter: () => setInteracting(true), onPointerLeave: () => setInteracting(false), "aria-label": prevIndex === undefined ? 'No previous slide' : `Move to slide ${prevIndex + 1} of ${childCount}`, "aria-disabled": prevIndex === undefined }), renderNavButton
349
349
  ?
350
350
  renderNavButton({
351
351
  type: 'next',
@@ -354,7 +354,7 @@ function Carousel({ className, spacing = 0, infinite = false, autoplay = false,
354
354
  : infinite || dragIndex < childCount - 1,
355
355
  enabled: nextIndex !== undefined,
356
356
  props: {
357
- className: 'Carousel-next',
357
+ className: 'nd-Carousel-next',
358
358
  onClick: onNext,
359
359
  onPointerEnter: () => setInteracting(true),
360
360
  onPointerLeave: () => setInteracting(false),
@@ -363,20 +363,20 @@ function Carousel({ className, spacing = 0, infinite = false, autoplay = false,
363
363
  }
364
364
  })
365
365
  :
366
- (0, jsx_runtime_1.jsx)("button", { className: "Carousel-next", disabled: nextIndex === undefined, onClick: onNext, onPointerEnter: () => setInteracting(true), onPointerLeave: () => setInteracting(false), "aria-label": nextIndex === undefined ? 'No next slide' : `Move to slide ${nextIndex + 1} of ${childCount}`, "aria-disabled": nextIndex === undefined, children: "Next" }), canAutoplay && infinite &&
366
+ (0, jsx_runtime_1.jsx)("button", { className: "nd-Carousel-next", disabled: nextIndex === undefined, onClick: onNext, onPointerEnter: () => setInteracting(true), onPointerLeave: () => setInteracting(false), "aria-label": nextIndex === undefined ? 'No next slide' : `Move to slide ${nextIndex + 1} of ${childCount}`, "aria-disabled": nextIndex === undefined }), canAutoplay && infinite &&
367
367
  (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: renderPlayButton
368
368
  ?
369
369
  renderPlayButton({
370
370
  props: {
371
371
  ref: playButtonRef,
372
- className: 'Carousel-play',
372
+ className: 'nd-Carousel-play',
373
373
  onClick: onTogglePlay,
374
374
  'aria-label': play ? 'Stop slide rotation' : 'Start slide rotation'
375
375
  }
376
376
  })
377
377
  :
378
- (0, jsx_runtime_1.jsx)("button", { ref: playButtonRef, className: "Carousel-play", onClick: onTogglePlay, "aria-label": play ? 'Stop slide rotation' : 'Start slide rotation', children: play ? 'Pause' : 'Play' }) })] }), (0, jsx_runtime_1.jsx)("nav", { className: "Carousel-indicatorsWrap", children: (0, jsx_runtime_1.jsx)("ol", { ref: indicatorsRef, className: "Carousel-indicators", onPointerEnter: () => setInteracting(true), onPointerLeave: () => setInteracting(false), role: "tablist", "aria-label": "Choose slide to display", children: children.map((_, index) => (0, jsx_runtime_1.jsx)("li", { className: (0, classnames_1.default)('Carousel-indicator', {
379
- 'Carousel-indicatorActive': index === activeIndex
378
+ (0, jsx_runtime_1.jsx)("button", { ref: playButtonRef, className: "nd-Carousel-play", onClick: onTogglePlay, "aria-label": play ? 'Stop slide rotation' : 'Start slide rotation', children: play ? 'Pause' : 'Play' }) })] }), (0, jsx_runtime_1.jsx)("nav", { className: "nd-Carousel-indicatorsWrap", children: (0, jsx_runtime_1.jsx)("ol", { ref: indicatorsRef, className: "Carousel-indicators", onPointerEnter: () => setInteracting(true), onPointerLeave: () => setInteracting(false), role: "tablist", "aria-label": "Choose slide to display", children: children.map((_, index) => (0, jsx_runtime_1.jsx)("li", { className: (0, classnames_1.default)('nd-Carousel-indicator', {
379
+ 'nd-Carousel-indicatorActive': index === activeIndex
380
380
  }), children: renderIndicator
381
381
  ?
382
382
  renderIndicator({
@@ -387,7 +387,7 @@ function Carousel({ className, spacing = 0, infinite = false, autoplay = false,
387
387
  : index === dragIndex,
388
388
  props: {
389
389
  id: `${carouselId}_${index}_ref`,
390
- className: 'Carousel-indicatorBtn',
390
+ className: 'nd-Carousel-indicatorBtn',
391
391
  onClick: e => onJump(e, index),
392
392
  role: 'tab',
393
393
  'aria-label': play ? 'Stop slide rotation' : 'Start slide rotation',
@@ -396,7 +396,7 @@ function Carousel({ className, spacing = 0, infinite = false, autoplay = false,
396
396
  }
397
397
  })
398
398
  :
399
- (0, jsx_runtime_1.jsx)("button", { id: `${carouselId}_${index}_ref`, className: "Carousel-indicatorBtn", disabled: index === activeIndex, onClick: e => onJump(e, index), role: "tab", "aria-label": `Slide ${index + 1}`, "aria-controls": `${carouselId}_${activeIndex}`, "aria-selected": index === activeIndex, children: index + 1 }) }, index)) }) })] }));
399
+ (0, jsx_runtime_1.jsx)("button", { id: `${carouselId}_${index}_ref`, className: "nd-Carousel-indicatorBtn", disabled: index === activeIndex, onClick: e => onJump(e, index), role: "tab", "aria-label": `Slide ${index + 1}`, "aria-controls": `${carouselId}_${activeIndex}`, "aria-selected": index === activeIndex, children: index + 1 }) }, index)) }) })] }));
400
400
  }
401
401
  function getCarouselSpecs({ spacing, infinite, children, activeIndex, pagerWidth }) {
402
402
  // setup for pager with boundaries (bookended)
@@ -490,8 +490,6 @@ async function onAnimateTo({ target, childCount, offset, spacing, infinite, page
490
490
  })
491
491
  // mark ready after animation
492
492
  .finally(() => {
493
- // reset drag
494
- setDragIndex(undefined);
495
493
  // reset state
496
494
  setState('ready');
497
495
  });
@@ -0,0 +1,15 @@
1
+ import { PropsWithChildren, ReactElement } from 'react';
2
+ import { BlockConfig } from './schema';
3
+ export declare function Page({ className, children }: Page.Props): import("react/jsx-runtime").JSX.Element;
4
+ export declare namespace Page {
5
+ type Props = PropsWithChildren & {
6
+ className?: string;
7
+ };
8
+ type Item = ReactElement<BlockProps>;
9
+ }
10
+ type BlockProps = {
11
+ model: {
12
+ config: BlockConfig.Presets.ThemeOnly;
13
+ };
14
+ };
15
+ export {};
@@ -0,0 +1,70 @@
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.Page = Page;
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 react_1 = require("react");
11
+ // component
12
+ function Page({ className, children }) {
13
+ // group children by themes
14
+ const blocks = react_1.Children.toArray(children)
15
+ .flatMap(element => element);
16
+ const groupedBlocks = groupBlocks(blocks);
17
+ console.debug('[Page] children', { groupedBlocks, blocks, children });
18
+ // render
19
+ return ((0, jsx_runtime_1.jsx)("main", { className: (0, classnames_1.default)(className, 'nd-Page'), children: groupedBlocks.map((blockGroup, index) => {
20
+ var _a, _b, _c;
21
+ return (0, jsx_runtime_1.jsx)("section", { className: (0, classnames_1.default)(`nd-Section nd-themeGroup-${(_c = (_b = funnel_core_1.BlockThemes.current()[(_a = blockGroup.theme) !== null && _a !== void 0 ? _a : 'default']) === null || _b === void 0 ? void 0 : _b.group) !== null && _c !== void 0 ? _c : 'default'}`), children: blockGroup.items }, index);
22
+ }) }));
23
+ }
24
+ function filterBlockProps(node) {
25
+ return (0, react_1.isValidElement)(node)
26
+ && !!node.props && typeof node.props === 'object' && !Array.isArray(node.props)
27
+ && 'model' in node.props && !!node.props.model && typeof node.props.model === 'object' && !Array.isArray(node.props.model);
28
+ }
29
+ function groupBlocks(maybeBlocks) {
30
+ // group blocks
31
+ let deferredItems = [];
32
+ const groupedBlocks = maybeBlocks.reduce((groups, maybeBlock) => {
33
+ var _a, _b, _c;
34
+ // handle block
35
+ let group = funnel_core_1.ArrayUtils.last(groups);
36
+ if (filterBlockProps(maybeBlock)) {
37
+ // create new group if theme differs
38
+ const theme = (_c = (_b = (_a = maybeBlock.props.model.config) === null || _a === void 0 ? void 0 : _a.theme) === null || _b === void 0 ? void 0 : _b.value) !== null && _c !== void 0 ? _c : 'default';
39
+ if (!group || group.theme !== theme) {
40
+ // create group
41
+ group = { theme, items: [] };
42
+ // flush deferred items (if any)
43
+ if (deferredItems.length > 0) {
44
+ group.items.push(...deferredItems);
45
+ deferredItems = [];
46
+ }
47
+ // add group
48
+ groups.push(group);
49
+ }
50
+ // add block to group
51
+ group.items.push(maybeBlock);
52
+ }
53
+ // add item to current group
54
+ else if (group) {
55
+ group.items.push(maybeBlock);
56
+ }
57
+ // or defer item
58
+ else {
59
+ deferredItems.push(maybeBlock);
60
+ }
61
+ // keep reducing
62
+ return groups;
63
+ }, []);
64
+ // add any deferred items
65
+ if (deferredItems.length > 0) {
66
+ groupedBlocks.push({ theme: 'default', items: deferredItems });
67
+ }
68
+ // return
69
+ return groupedBlocks;
70
+ }
@@ -1,5 +1,5 @@
1
1
  import { BlockLayout, Schema, Transform } from '@nascentdigital/funnel-core';
2
- import { FunctionComponent, ReactNode } from 'react';
2
+ import { FunctionComponent, ReactElement, ReactNode } from 'react';
3
3
  import { HTMLBlockElement } from '../../types';
4
4
  import { BlockPanel, ListPanel, MessagePanel, VisualPanel } from '../panels';
5
5
  import { BlockConfig } from '../schema';
@@ -20,6 +20,7 @@ export declare namespace Block {
20
20
  id: TBlockId;
21
21
  name: string;
22
22
  version: number;
23
+ hint?: string;
23
24
  className?: string;
24
25
  message?: TMessage;
25
26
  visual?: TVisual;
@@ -50,7 +51,8 @@ export declare namespace Block {
50
51
  listPanelProps?: ListPanel.Props.Configuration & {
51
52
  itemProps: TItemProps;
52
53
  };
53
- } : {}));
54
+ } : {})) & HTMLBlockElement<'div'>;
54
55
  }
55
56
  }
57
+ function filterNode(node: ReactNode): node is ReactElement<Props>;
56
58
  }
@@ -50,7 +50,7 @@ function Block(_a) {
50
50
  }
51
51
  // extension
52
52
  (function (Block) {
53
- function createSchema({ id, name, version, message, visual, list, extendConfigSchema }) {
53
+ function createSchema({ id, name, version, hint, message, visual, list, extendConfigSchema }) {
54
54
  // build fields schema and panel key mapping
55
55
  let fieldsSchema = {};
56
56
  const panelKeyMapping = {};
@@ -73,6 +73,7 @@ function Block(_a) {
73
73
  id,
74
74
  name,
75
75
  version,
76
+ hint,
76
77
  config: configSchema,
77
78
  fields: fieldsSchema,
78
79
  getConfig: args => panels_1.BlockPanel.getConfig(Object.assign({ args }, panelKeyMapping)),
@@ -125,7 +126,7 @@ function Block(_a) {
125
126
  }
126
127
  };
127
128
  // render block
128
- return ((0, jsx_runtime_1.jsx)(Block, { className: (0, classnames_1.default)(className, args.className), config: config, layout: layout, renderPanel: getPanel }));
129
+ return ((0, jsx_runtime_1.jsx)(Block, { id: props.id, className: (0, classnames_1.default)(className, args.className), config: config, layout: layout, renderPanel: getPanel }));
129
130
  };
130
131
  // return
131
132
  return {
@@ -134,6 +135,12 @@ function Block(_a) {
134
135
  };
135
136
  }
136
137
  Block.create = create;
138
+ function filterNode(node) {
139
+ return (0, react_1.isValidElement)(node)
140
+ && !!node.props && typeof node.props === 'object' && !Array.isArray(node.props)
141
+ && 'model' in node.props && !!node.props.model && typeof node.props.model === 'object' && !Array.isArray(node.props.model);
142
+ }
143
+ Block.filterNode = filterNode;
137
144
  })(Block || (exports.Block = Block = {}));
138
145
  function renderLayout({ renderPanel, layout }) {
139
146
  // render root container (row/column/stack)
@@ -3,7 +3,7 @@ import { ReactNode } from 'react';
3
3
  import { HTMLBlockElement } from '../../types';
4
4
  import { Caption, FreeFormContent, Headline, Title, Visual } from '../fields';
5
5
  import { BlockConfig } from '../schema';
6
- export declare function Editorial({ className, editorial, titleTag, headlineTag, captionTag, freeFormContentTag, headerDecorator, visualDecorator, visualLoading, ...props }: Editorial.Props): import("react/jsx-runtime").JSX.Element | null;
6
+ export declare function Editorial({ className, model, titleTag, headlineTag, captionTag, freeFormContentTag, headerDecorator, visualDecorator, visualLoading, ...props }: Editorial.Props): import("react/jsx-runtime").JSX.Element | null;
7
7
  export declare namespace Editorial {
8
8
  const schema: Schema.Block<"_nd-editorial", {
9
9
  visual: Schema.Optional<Schema.AssetField<Schema.AssetField.LinkedValue.Visual>>;
@@ -20,7 +20,7 @@ export declare namespace Editorial {
20
20
  visualLayout: Schema.EnumField<Schema.EnumField.Options<BlockConfig.VisualLayout>>;
21
21
  }>;
22
22
  type Props = HTMLBlockElement<'div'> & {
23
- editorial: Model;
23
+ model: Model;
24
24
  titleTag?: Title.Tag;
25
25
  headlineTag?: Headline.Tag;
26
26
  captionTag?: Caption.Tag;
@@ -23,14 +23,14 @@ const schema_1 = require("../schema");
23
23
  // block
24
24
  function Editorial(_a) {
25
25
  var _b, _c, _d, _e, _f;
26
- var { className, editorial, titleTag = 'span', headlineTag = 'div', captionTag = 'div', freeFormContentTag = 'div', headerDecorator, visualDecorator, visualLoading = 'eager' } = _a, props = __rest(_a, ["className", "editorial", "titleTag", "headlineTag", "captionTag", "freeFormContentTag", "headerDecorator", "visualDecorator", "visualLoading"]);
26
+ var { className, model, titleTag = 'span', headlineTag = 'div', captionTag = 'div', freeFormContentTag = 'div', headerDecorator, visualDecorator, visualLoading = 'eager' } = _a, props = __rest(_a, ["className", "model", "titleTag", "headlineTag", "captionTag", "freeFormContentTag", "headerDecorator", "visualDecorator", "visualLoading"]);
27
27
  // dont render if missing model
28
- if (!editorial) {
28
+ if (!model) {
29
29
  console.warn('[Editorial] attempted to render without editorial model');
30
30
  return null;
31
31
  }
32
32
  // render
33
- const { title: titleRaw, headline: headlineRaw, caption: captionRaw, ctas: ctasRaw, visual: visualRaw, content: contentRaw, extraContent: extraContentRaw } = editorial.fields;
33
+ const { title: titleRaw, headline: headlineRaw, caption: captionRaw, ctas: ctasRaw, visual: visualRaw, content: contentRaw, extraContent: extraContentRaw } = model.fields;
34
34
  const visual = visualRaw === null || visualRaw === void 0 ? void 0 : visualRaw.value;
35
35
  const title = titleRaw === null || titleRaw === void 0 ? void 0 : titleRaw.value;
36
36
  const headline = headlineRaw === null || headlineRaw === void 0 ? void 0 : headlineRaw.value;
@@ -38,7 +38,7 @@ function Editorial(_a) {
38
38
  const ctas = (_b = ctasRaw === null || ctasRaw === void 0 ? void 0 : ctasRaw.value) !== null && _b !== void 0 ? _b : [];
39
39
  const content = contentRaw === null || contentRaw === void 0 ? void 0 : contentRaw.value;
40
40
  const extraContent = extraContentRaw === null || extraContentRaw === void 0 ? void 0 : extraContentRaw.value;
41
- return ((0, jsx_runtime_1.jsx)("div", Object.assign({ className: (0, classnames_1.default)(className, 'nd-Block nd-Editorial', schema_1.BlockConfig.Theme.getClassName((_d = (_c = editorial === null || editorial === void 0 ? void 0 : editorial.config) === null || _c === void 0 ? void 0 : _c.theme) === null || _d === void 0 ? void 0 : _d.value), schema_1.BlockConfig.VisualLayout.getClassName((_f = (_e = editorial === null || editorial === void 0 ? void 0 : editorial.config) === null || _e === void 0 ? void 0 : _e.visualLayout) === null || _f === void 0 ? void 0 : _f.value)) }, props, { children: (0, jsx_runtime_1.jsxs)("div", { className: "nd-Editorial-content", children: [(title || headline || caption || ctas.length > 0) &&
41
+ return ((0, jsx_runtime_1.jsx)("div", Object.assign({ className: (0, classnames_1.default)(className, 'nd-Block nd-Editorial', schema_1.BlockConfig.Theme.getClassName((_d = (_c = model === null || model === void 0 ? void 0 : model.config) === null || _c === void 0 ? void 0 : _c.theme) === null || _d === void 0 ? void 0 : _d.value), schema_1.BlockConfig.VisualLayout.getClassName((_f = (_e = model === null || model === void 0 ? void 0 : model.config) === null || _e === void 0 ? void 0 : _e.visualLayout) === null || _f === void 0 ? void 0 : _f.value)) }, props, { children: (0, jsx_runtime_1.jsxs)("div", { className: "nd-Editorial-content", children: [(title || headline || caption || ctas.length > 0) &&
42
42
  (0, jsx_runtime_1.jsxs)("div", { className: "nd-Editorial-headerWrap", children: [(0, jsx_runtime_1.jsxs)("div", { className: "nd-Editorial-header", children: [title &&
43
43
  (0, jsx_runtime_1.jsx)(fields_1.Title, { className: "nd-Editorial-title", as: titleTag, title: title }), headline &&
44
44
  (0, jsx_runtime_1.jsx)(fields_1.Headline, { className: "nd-Editorial-headline", as: headlineTag, headline: headline }), caption &&
@@ -3,7 +3,7 @@ import { JSXElementConstructor } from 'react';
3
3
  import { HTMLBlockElement, HTMLElements } from '../../types';
4
4
  import { Caption, Headline, Title } from '../fields';
5
5
  import { FormMessage } from './FormMessage';
6
- export declare function Form({ className, titleTag, headlineTag, captionTag, privacyStatementTag, form, component: ClientForm, ...props }: Form.Props): import("react/jsx-runtime").JSX.Element | null;
6
+ export declare function Form({ className, titleTag, headlineTag, captionTag, privacyStatementTag, model, component: ClientForm, ...props }: Form.Props): import("react/jsx-runtime").JSX.Element | null;
7
7
  export declare namespace Form {
8
8
  const schema: (formId: string) => Schema.Block<"_nd-form", {
9
9
  title: Schema.Optional<Schema.TextField>;
@@ -30,7 +30,7 @@ export declare namespace Form {
30
30
  headlineTag?: Headline.Tag;
31
31
  captionTag?: Caption.Tag;
32
32
  privacyStatementTag?: Caption.Tag;
33
- form: Model;
33
+ model: Model;
34
34
  component: Component;
35
35
  };
36
36
  type Tag = HTMLElements.Standard.Container;
@@ -23,10 +23,10 @@ const schema_1 = require("../schema");
23
23
  const FormMessage_1 = require("./FormMessage");
24
24
  // component
25
25
  function Form(_a) {
26
- var { className, titleTag = 'span', headlineTag = 'h2', captionTag = 'div', privacyStatementTag = 'div', form, component: ClientForm } = _a, props = __rest(_a, ["className", "titleTag", "headlineTag", "captionTag", "privacyStatementTag", "form", "component"]);
26
+ var { className, titleTag = 'span', headlineTag = 'h2', captionTag = 'div', privacyStatementTag = 'div', model, component: ClientForm } = _a, props = __rest(_a, ["className", "titleTag", "headlineTag", "captionTag", "privacyStatementTag", "model", "component"]);
27
27
  // dont render if missing model
28
- const { config, fields } = form !== null && form !== void 0 ? form : {};
29
- if (!form) {
28
+ const { config, fields } = model !== null && model !== void 0 ? model : {};
29
+ if (!model) {
30
30
  console.warn('[Form] attempted to render without form model');
31
31
  return null;
32
32
  }
@@ -1,43 +1,35 @@
1
1
  import { Schema } from '@nascentdigital/funnel-core';
2
- import { HTMLBlockElement, HTMLElements } from '../../types';
2
+ import { ComponentProps } from 'react';
3
3
  import { Project } from '../entities';
4
- import { Caption, Headline } from '../fields';
5
- export declare function Projects({ className, headlineTag, captionTag, projectProps, projects: projectData, ...props }: Projects.Props): import("react/jsx-runtime").JSX.Element | null;
4
+ import { Block } from './Block';
5
+ export declare function Projects(props: ComponentProps<typeof Projects.Component>): import("react/jsx-runtime").JSX.Element;
6
6
  export declare namespace Projects {
7
- type Layout = 'masonry';
8
- namespace Layout {
9
- const Enum: Schema.EnumField.Options<Layout>;
10
- const schema: Schema.EnumField<Schema.EnumField.Options<"masonry">>;
11
- function getClassName(layout?: Layout): string;
12
- }
13
- const schema: Schema.Block<"_nd-projects", {
14
- headline: Schema.Optional<Schema.RichTextField>;
15
- caption: Schema.Optional<Schema.RichTextField>;
16
- items: Schema.ListField<{
17
- item: Schema.CollectionField<Schema.Collection<{
18
- client: Schema.CollectionField<Schema.Collection<{
19
- division: Schema.Optional<Schema.TextField>;
20
- location: Schema.Optional<Schema.TextField>;
21
- logo: Schema.Optional<Schema.AssetField<Schema.AssetField.LinkedValue.Visual>>;
22
- badge: Schema.Optional<Schema.AssetField<Schema.AssetField.LinkedValue.Image>>;
23
- badgeText: Schema.Optional<Schema.TextField>;
24
- }>>;
25
- headline: Schema.RichTextField;
26
- visual: Schema.AssetField<Schema.AssetField.LinkedValue.Visual>;
27
- link: Schema.Optional<Schema.LinkField>;
7
+ const Component: import("react").FunctionComponent<Block.Create.Result.Component<"_nd-projects", true, undefined, Schema.ListField<{
8
+ item: Schema.CollectionField<Schema.Collection<{
9
+ client: Schema.CollectionField<Schema.Collection<{
10
+ division: Schema.Optional<Schema.TextField>;
11
+ location: Schema.Optional<Schema.TextField>;
12
+ logo: Schema.Optional<Schema.AssetField<Schema.AssetField.LinkedValue.Visual>>;
13
+ badge: Schema.Optional<Schema.AssetField<Schema.AssetField.LinkedValue.Image>>;
14
+ badgeText: Schema.Optional<Schema.TextField>;
28
15
  }>>;
29
- }>;
30
- }, {
31
- layout: Schema.EnumField<Schema.EnumField.Options<"masonry">>;
32
- theme: Schema.EnumField<Readonly<import("@nascentdigital/funnel-core").BlockThemes.Options>>;
33
- }>;
34
- type Props = HTMLBlockElement<'div'> & {
35
- headlineTag?: Headline.Tag;
36
- captionTag?: Caption.Tag;
37
- projectProps?: Project.Props.Configuration;
38
- projects: Model;
39
- };
40
- type Tag = HTMLElements.Standard.Container;
16
+ headline: Schema.RichTextField;
17
+ visual: Schema.AssetField<Schema.AssetField.LinkedValue.Visual>;
18
+ link: Schema.Optional<Schema.LinkField>;
19
+ }>>;
20
+ }>, Schema.Block.Config, Project.Props.Configuration>>, schema: Block.Create.Result.Schema<"_nd-projects", true, undefined, Schema.ListField<{
21
+ item: Schema.CollectionField<Schema.Collection<{
22
+ client: Schema.CollectionField<Schema.Collection<{
23
+ division: Schema.Optional<Schema.TextField>;
24
+ location: Schema.Optional<Schema.TextField>;
25
+ logo: Schema.Optional<Schema.AssetField<Schema.AssetField.LinkedValue.Visual>>;
26
+ badge: Schema.Optional<Schema.AssetField<Schema.AssetField.LinkedValue.Image>>;
27
+ badgeText: Schema.Optional<Schema.TextField>;
28
+ }>>;
29
+ headline: Schema.RichTextField;
30
+ visual: Schema.AssetField<Schema.AssetField.LinkedValue.Visual>;
31
+ link: Schema.Optional<Schema.LinkField>;
32
+ }>>;
33
+ }>, Schema.Block.Config>;
41
34
  type Model = Schema.Block.LinkedData<typeof schema>;
42
- type ItemModel = Model['fields']['items']['value'][number];
43
35
  }