@nascentdigital/funnel-core 4.4.15 → 4.4.16

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.
@@ -1,63 +1,44 @@
1
+ import { JsonObject } from 'type-fest';
2
+ import { PartialBy } from '../../types';
1
3
  export type BlockLayout = {
2
4
  name: string;
3
5
  root: BlockLayout.Root;
4
- tight?: BlockLayout.Tight;
5
- tightMobile?: BlockLayout.Tight;
6
- tightDesktop?: BlockLayout.Tight;
6
+ tight: BlockLayout.ResponsiveValue<BlockLayout.Tight>;
7
7
  };
8
8
  export declare namespace BlockLayout {
9
- type Tight = 'above' | 'below' | 'both' | 'none';
10
- namespace Tight {
11
- const values: ReadonlyArray<Tight>;
12
- function parse(value: string, fallback?: Tight): Tight;
13
- }
14
- type Ratio = '1/1' | '1/2' | '1/3' | '2/1' | '3/1' | '2/3' | '3/2';
15
- namespace Ratio {
16
- const values: ReadonlyArray<Ratio>;
17
- function parse(value: string, fallback?: Ratio): Ratio;
18
- }
19
- namespace FullBleed {
20
- function parse(value: string, fallback?: boolean): boolean;
21
- }
22
9
  type Root = Row | Column | Stack;
23
10
  type Container = Row | Row.Nested | Column | Column.Nested | Stack | Stack.Nested;
24
- type Row = {
11
+ namespace Container {
12
+ type Base = {
13
+ alignX?: Alignment;
14
+ alignY?: Alignment;
15
+ };
16
+ type Nested = {
17
+ items: ReadonlyArray<Panel>;
18
+ mobileOrder?: MobileOrder;
19
+ };
20
+ }
21
+ type Row = Container.Base & {
25
22
  type: 'row';
26
23
  items: ReadonlyArray<Column.Nested | Stack.Nested | Panel>;
27
- alignX?: Alignment;
28
- alignY?: Alignment;
29
- ratio?: Ratio;
30
- ratioDesktop?: Ratio;
24
+ ratio: ResponsiveValue<Ratio>;
31
25
  };
32
26
  namespace Row {
33
- type Nested = Omit<Row, 'items'> & {
34
- items: ReadonlyArray<Panel>;
35
- mobileOrder?: MobileOrder;
36
- };
27
+ type Nested = Omit<Row, 'items'> & Container.Nested;
37
28
  }
38
- type Column = {
29
+ type Column = Container.Base & {
39
30
  type: 'column';
40
31
  items: ReadonlyArray<Row.Nested | Stack.Nested | Panel>;
41
- alignX?: Alignment;
42
- alignY?: Alignment;
43
32
  };
44
33
  namespace Column {
45
- type Nested = Omit<Column, 'items'> & {
46
- items: ReadonlyArray<Panel>;
47
- mobileOrder?: MobileOrder;
48
- };
34
+ type Nested = Omit<Column, 'items'> & Container.Nested;
49
35
  }
50
- type Stack = {
36
+ type Stack = Container.Base & {
51
37
  type: 'stack';
52
38
  items: ReadonlyArray<Row.Nested | Column.Nested | Panel>;
53
- alignX?: Alignment;
54
- alignY?: Alignment;
55
39
  };
56
40
  namespace Stack {
57
- type Nested = Omit<Stack, 'items'> & {
58
- items: ReadonlyArray<Panel>;
59
- mobileOrder?: MobileOrder;
60
- };
41
+ type Nested = Omit<Stack, 'items'> & Container.Nested;
61
42
  }
62
43
  type NestedItem = Row.Nested | Column.Nested | Stack.Nested | Panel;
63
44
  type Panel = Message | Visual | List;
@@ -69,22 +50,16 @@ export declare namespace BlockLayout {
69
50
  alignX?: Alignment;
70
51
  alignY?: Alignment;
71
52
  size?: Size;
72
- fullBleed?: boolean;
73
- fullBleedMobile?: boolean;
74
- fullBleedDesktop?: boolean;
53
+ fullBleed: ResponsiveValue<boolean>;
75
54
  };
76
55
  }
77
56
  type Message = Panel.Base & {
78
57
  type: 'message';
79
- textAlign?: TextAlignment;
80
- textAlignMobile?: TextAlignment;
81
- textAlignDesktop?: TextAlignment;
58
+ textAlign: ResponsiveValue<TextAlignment>;
82
59
  };
83
60
  type Visual = Panel.Base & {
84
61
  type: 'visual';
85
- aspectRatio: AspectRatio;
86
- aspectRatioMobile?: AspectRatio;
87
- aspectRatioDesktop?: AspectRatio;
62
+ aspectRatio: ResponsiveValue<AspectRatio>;
88
63
  };
89
64
  type List = Panel.Base & {
90
65
  type: 'list';
@@ -97,6 +72,19 @@ export declare namespace BlockLayout {
97
72
  function parse(value: string, fallback?: ItemType): ItemType;
98
73
  }
99
74
  }
75
+ type Tight = 'above' | 'below' | 'both' | 'none';
76
+ namespace Tight {
77
+ const values: ReadonlyArray<Tight>;
78
+ function parse(value: string, fallback?: Tight): Tight;
79
+ }
80
+ type Ratio = '1/1' | '1/2' | '1/3' | '2/1' | '3/1' | '2/3' | '3/2';
81
+ namespace Ratio {
82
+ const values: ReadonlyArray<Ratio>;
83
+ function parse(value: string, fallback?: Ratio): Ratio;
84
+ }
85
+ namespace FullBleed {
86
+ function parse(value: string, fallback?: boolean): boolean;
87
+ }
100
88
  type MobileOrder = 'first' | 'last';
101
89
  namespace MobileOrder {
102
90
  const values: ReadonlyArray<MobileOrder>;
@@ -122,5 +110,48 @@ export declare namespace BlockLayout {
122
110
  const values: ReadonlyArray<AspectRatio>;
123
111
  function parse(value: string, fallback?: AspectRatio): AspectRatio;
124
112
  }
113
+ type ResponsiveValue<T extends string | boolean = string | boolean> = {
114
+ base: T;
115
+ mobile: T;
116
+ desktop: T;
117
+ };
118
+ namespace ResponsiveValue {
119
+ type ParseArgs<T extends string | boolean> = {
120
+ element: ParsedElement;
121
+ property: string;
122
+ parser: (value: string, fallback: T) => T;
123
+ fallback: T | PartialBy<ResponsiveValue<T>, 'desktop' | 'mobile'>;
124
+ };
125
+ function parse<T extends string | boolean>({ element, property, parser, fallback: fallbackRaw }: ParseArgs<T>): ResponsiveValue<T>;
126
+ }
127
+ type ResponsiveClassNames<T extends ResponsiveValue> = T['base'] extends string ? ResponsiveClassNames.ForString<T, T['base']> : T['base'] extends boolean ? ResponsiveClassNames.ForBoolean<T, T['base']> : never;
128
+ namespace ResponsiveClassNames {
129
+ type ForString<T extends ResponsiveValue<V>, V extends string = string> = Record<keyof T, Record<V, string>> & {
130
+ value: T;
131
+ };
132
+ type ForBoolean<T extends ResponsiveValue<V>, V extends boolean = boolean> = Record<keyof T, Record<ForBoolean.Key, string>> & {
133
+ value: T;
134
+ };
135
+ namespace ForBoolean {
136
+ type Key = 'true' | 'false';
137
+ }
138
+ function parse<T extends ResponsiveValue>(args: ResponsiveClassNames<T>): string;
139
+ }
125
140
  function parse(xml: string): BlockLayout;
126
141
  }
142
+ type ParsedElement<TExpected extends ParsedElement.Tag = ParsedElement.Tag> = {
143
+ tag: TExpected;
144
+ attributes: Record<string, string>;
145
+ children: JsonObject[];
146
+ };
147
+ declare namespace ParsedElement {
148
+ const tags: readonly ["block", "row", "col", "stack", "message", "visual", "list"];
149
+ type Tag = typeof tags[number];
150
+ type ParseArgs<TExpected extends Tag = Tag> = {
151
+ data: JsonObject;
152
+ expectedTags?: ReadonlyArray<TExpected>;
153
+ expectedChildren?: number | false;
154
+ };
155
+ function parse<TExpected extends Tag = Tag>({ data, expectedTags: expectedTagsRaw, expectedChildren }: ParseArgs<TExpected>): ParsedElement<TExpected>;
156
+ }
157
+ export {};
@@ -10,13 +10,41 @@ var __rest = (this && this.__rest) || function (s, e) {
10
10
  }
11
11
  return t;
12
12
  };
13
+ var __importDefault = (this && this.__importDefault) || function (mod) {
14
+ return (mod && mod.__esModule) ? mod : { "default": mod };
15
+ };
13
16
  Object.defineProperty(exports, "__esModule", { value: true });
14
17
  exports.BlockLayout = void 0;
18
+ const classnames_1 = __importDefault(require("classnames"));
15
19
  const fast_xml_parser_1 = require("fast-xml-parser");
16
20
  const errors_1 = require("../../errors");
17
21
  // extension
18
22
  var BlockLayout;
19
23
  (function (BlockLayout) {
24
+ let List;
25
+ (function (List) {
26
+ let ItemType;
27
+ (function (ItemType) {
28
+ // values
29
+ ItemType.values = ['message', 'message+visual'];
30
+ // helper
31
+ function parse(value, fallback) {
32
+ // return if it's a match
33
+ if (ItemType.values.includes(value)) {
34
+ return value;
35
+ }
36
+ // or fallback if we have one
37
+ else if (fallback) {
38
+ return fallback;
39
+ }
40
+ // or throw
41
+ else {
42
+ throw new errors_1.ArgumentError(`Invalid BlockLayout.List.ItemType "${value}"`, 'value');
43
+ }
44
+ }
45
+ ItemType.parse = parse;
46
+ })(ItemType = List.ItemType || (List.ItemType = {}));
47
+ })(List = BlockLayout.List || (BlockLayout.List = {}));
20
48
  let Tight;
21
49
  (function (Tight) {
22
50
  // values
@@ -59,6 +87,7 @@ var BlockLayout;
59
87
  }
60
88
  Ratio.parse = parse;
61
89
  })(Ratio = BlockLayout.Ratio || (BlockLayout.Ratio = {}));
90
+ // fullbleed (for panels)
62
91
  let FullBleed;
63
92
  (function (FullBleed) {
64
93
  // helper
@@ -82,30 +111,6 @@ var BlockLayout;
82
111
  }
83
112
  FullBleed.parse = parse;
84
113
  })(FullBleed = BlockLayout.FullBleed || (BlockLayout.FullBleed = {}));
85
- let List;
86
- (function (List) {
87
- let ItemType;
88
- (function (ItemType) {
89
- // values
90
- ItemType.values = ['message', 'message+visual'];
91
- // helper
92
- function parse(value, fallback) {
93
- // return if it's a match
94
- if (ItemType.values.includes(value)) {
95
- return value;
96
- }
97
- // or fallback if we have one
98
- else if (fallback) {
99
- return fallback;
100
- }
101
- // or throw
102
- else {
103
- throw new errors_1.ArgumentError(`Invalid BlockLayout.List.ItemType "${value}"`, 'value');
104
- }
105
- }
106
- ItemType.parse = parse;
107
- })(ItemType = List.ItemType || (List.ItemType = {}));
108
- })(List = BlockLayout.List || (BlockLayout.List = {}));
109
114
  let MobileOrder;
110
115
  (function (MobileOrder) {
111
116
  // values
@@ -207,6 +212,45 @@ var BlockLayout;
207
212
  }
208
213
  AspectRatio.parse = parse;
209
214
  })(AspectRatio = BlockLayout.AspectRatio || (BlockLayout.AspectRatio = {}));
215
+ let ResponsiveValue;
216
+ (function (ResponsiveValue) {
217
+ function parse({ element, property, parser, fallback: fallbackRaw }) {
218
+ var _a, _b;
219
+ // normalize the fallback
220
+ const fallback = typeof fallbackRaw === 'string' || typeof fallbackRaw === 'boolean'
221
+ ? { base: fallbackRaw, mobile: fallbackRaw, desktop: fallbackRaw }
222
+ : { base: fallbackRaw.base, mobile: (_a = fallbackRaw.mobile) !== null && _a !== void 0 ? _a : fallbackRaw.base, desktop: (_b = fallbackRaw.desktop) !== null && _b !== void 0 ? _b : fallbackRaw.base };
223
+ // fetch the values from the element
224
+ const { attributes } = element;
225
+ return {
226
+ base: parser(attributes[property], fallback.base),
227
+ mobile: parser(attributes[`${property}Mobile`], fallback.mobile),
228
+ desktop: parser(attributes[`${property}Desktop`], fallback.desktop)
229
+ };
230
+ }
231
+ ResponsiveValue.parse = parse;
232
+ })(ResponsiveValue = BlockLayout.ResponsiveValue || (BlockLayout.ResponsiveValue = {}));
233
+ let ResponsiveClassNames;
234
+ (function (ResponsiveClassNames) {
235
+ // helpers
236
+ function parse(args) {
237
+ // handle string types
238
+ if (typeof args.value.base === 'string') {
239
+ // destructure args
240
+ const { value: { mobile, base, desktop }, mobile: mobileClasses, base: baseClasses, desktop: desktopClasses } = args;
241
+ // return classes
242
+ return (0, classnames_1.default)(mobileClasses[mobile], baseClasses[base], desktopClasses[desktop]);
243
+ }
244
+ // or boolean
245
+ else {
246
+ // destructure args
247
+ const { value: { mobile, base, desktop }, mobile: mobileClasses, base: baseClasses, desktop: desktopClasses } = args;
248
+ // return classes
249
+ return (0, classnames_1.default)(mobileClasses[mobile ? 'true' : 'false'], baseClasses[base ? 'true' : 'false'], desktopClasses[desktop ? 'true' : 'false']);
250
+ }
251
+ }
252
+ ResponsiveClassNames.parse = parse;
253
+ })(ResponsiveClassNames = BlockLayout.ResponsiveClassNames || (BlockLayout.ResponsiveClassNames = {}));
210
254
  // parser
211
255
  const xmlParser = new fast_xml_parser_1.XMLParser({
212
256
  ignoreDeclaration: true,
@@ -224,26 +268,34 @@ var BlockLayout;
224
268
  })(BlockLayout || (exports.BlockLayout = BlockLayout = {}));
225
269
  // helpers
226
270
  function parseBlock(json) {
227
- // parse block
228
- const block = ParsedElement.parse({ data: json, expectedTags: ['block'], expectedChildren: 1 });
229
- const tight = BlockLayout.Tight.parse(block.attributes['tight'], 'none');
271
+ // parse
272
+ const element = ParsedElement.parse({ data: json, expectedTags: ['block'], expectedChildren: 1 });
273
+ const tight = BlockLayout.ResponsiveValue.parse({
274
+ element,
275
+ property: 'tight',
276
+ parser: BlockLayout.Tight.parse,
277
+ fallback: 'none'
278
+ });
230
279
  // return block
231
280
  return {
232
- name: block.attributes['name'],
281
+ name: element.attributes['name'],
233
282
  tight,
234
- tightMobile: BlockLayout.Tight.parse(block.attributes['tightMobile'], tight),
235
- tightDesktop: BlockLayout.Tight.parse(block.attributes['tightDesktop'], tight),
236
- root: parseRoot(ParsedElement.parse({ data: block.children[0], expectedTags: ['row', 'col', 'stack'] })),
283
+ root: parseRoot(ParsedElement.parse({ data: element.children[0], expectedTags: ['row', 'col', 'stack'] })),
237
284
  };
238
285
  }
239
- function parseRoot({ tag, attributes, children }) {
240
- const ratio = BlockLayout.Ratio.parse(attributes['ratio'], '1/1');
241
- // parse attributes
286
+ function parseRoot(element) {
287
+ // parse
288
+ const { tag, attributes, children } = element;
242
289
  const _a = parseItemAttributes(attributes), { size } = _a, itemAttributes = __rest(_a, ["size"]);
243
290
  // create root
244
291
  switch (tag) {
245
292
  case 'row':
246
- return Object.assign(Object.assign({ type: 'row' }, itemAttributes), { ratio, ratioDesktop: BlockLayout.Ratio.parse(attributes['ratioDesktop'], ratio), items: children.map(data => parseNestedItem(ParsedElement.parse({
293
+ return Object.assign(Object.assign({ type: 'row' }, itemAttributes), { ratio: BlockLayout.ResponsiveValue.parse({
294
+ element,
295
+ property: 'ratio',
296
+ parser: BlockLayout.Ratio.parse,
297
+ fallback: '1/1'
298
+ }), items: children.map(data => parseNestedItem(ParsedElement.parse({
247
299
  data,
248
300
  expectedTags: ParsedElement.tags.filter(t => t !== tag)
249
301
  }))) });
@@ -261,14 +313,19 @@ function parseRoot({ tag, attributes, children }) {
261
313
  throw new errors_1.ArgumentError(`Invalid XML BlockLayout root encountered "${tag}" where instead of "row" or "col"`);
262
314
  }
263
315
  }
264
- function parseNestedItem({ tag, attributes, children }) {
265
- // parse attributes
316
+ function parseNestedItem(element) {
317
+ // parse
318
+ const { tag, attributes, children } = element;
266
319
  const _a = parseItemAttributes(attributes), { size } = _a, itemAttributes = __rest(_a, ["size"]);
267
- const ratio = BlockLayout.Ratio.parse(attributes['ratio'], '1/1');
268
320
  // create container
269
321
  switch (tag) {
270
322
  case 'row':
271
- return Object.assign(Object.assign({ type: 'row' }, itemAttributes), { ratio, ratioDesktop: BlockLayout.Ratio.parse(attributes['ratioDesktop'], ratio), items: children.map(data => parsePanel(Object.assign({}, ParsedElement.parse({
323
+ return Object.assign(Object.assign({ type: 'row' }, itemAttributes), { ratio: BlockLayout.ResponsiveValue.parse({
324
+ element,
325
+ property: 'ratio',
326
+ parser: BlockLayout.Ratio.parse,
327
+ fallback: '1/1'
328
+ }), items: children.map(data => parsePanel(Object.assign({}, ParsedElement.parse({
272
329
  data,
273
330
  expectedTags: ParsedElement.tags.filter(t => t !== 'row' && t !== 'col')
274
331
  })))) });
@@ -282,28 +339,36 @@ function parseNestedItem({ tag, attributes, children }) {
282
339
  return parsePanel({ tag, attributes, children });
283
340
  }
284
341
  }
285
- function parsePanel({ tag, attributes }) {
342
+ function parsePanel(element) {
286
343
  var _a;
344
+ // parse
345
+ const { tag, attributes } = element;
287
346
  const key = (_a = attributes['key']) !== null && _a !== void 0 ? _a : tag;
288
347
  const itemAttributes = parseItemAttributes(attributes);
289
- const textAlign = BlockLayout.TextAlignment.parse(attributes['textAlign'], 'left');
290
- const aspectRatio = BlockLayout.AspectRatio.parse(attributes['aspect'], 'auto');
291
- const fullBleed = BlockLayout.FullBleed.parse(attributes['fullBleed'], false);
292
- const fullBleedMobile = BlockLayout.FullBleed.parse(attributes['fullBleedMobile'], fullBleed);
293
- const fullBleedDesktop = BlockLayout.FullBleed.parse(attributes['fullBleedDesktop'], fullBleed);
348
+ const fullBleed = BlockLayout.ResponsiveValue.parse({
349
+ element,
350
+ property: 'fullBleed',
351
+ parser: BlockLayout.FullBleed.parse,
352
+ fallback: false
353
+ });
354
+ // create panel
294
355
  switch (tag) {
295
356
  case 'message':
296
- return Object.assign(Object.assign({ type: 'message', key }, itemAttributes), { textAlign, textAlignMobile: BlockLayout.TextAlignment.parse(attributes['textAlignMobile'], textAlign), textAlignDesktop: BlockLayout.TextAlignment.parse(attributes['textAlignDesktop'], textAlign), fullBleed,
297
- fullBleedMobile,
298
- fullBleedDesktop });
357
+ return Object.assign({ type: 'message', key, textAlign: BlockLayout.ResponsiveValue.parse({
358
+ element,
359
+ property: 'textAlign',
360
+ parser: BlockLayout.TextAlignment.parse,
361
+ fallback: 'left'
362
+ }), fullBleed }, itemAttributes);
299
363
  case 'visual':
300
- return Object.assign(Object.assign({ type: 'visual', key }, itemAttributes), { aspectRatio, aspectRatioMobile: BlockLayout.AspectRatio.parse(attributes['aspectMobile'], aspectRatio), aspectRatioDesktop: BlockLayout.AspectRatio.parse(attributes['aspectDesktop'], aspectRatio), fullBleed,
301
- fullBleedMobile,
302
- fullBleedDesktop });
364
+ return Object.assign({ type: 'visual', key, aspectRatio: BlockLayout.ResponsiveValue.parse({
365
+ element,
366
+ property: 'aspect',
367
+ parser: BlockLayout.AspectRatio.parse,
368
+ fallback: 'auto'
369
+ }), fullBleed }, itemAttributes);
303
370
  case 'list':
304
- return Object.assign(Object.assign({ type: 'list', key }, itemAttributes), { fullBleed,
305
- fullBleedMobile,
306
- fullBleedDesktop, itemType: BlockLayout.List.ItemType.parse(attributes['itemType'], 'message+visual') });
371
+ return Object.assign({ type: 'list', key, itemType: BlockLayout.List.ItemType.parse(attributes['itemType'], 'message+visual'), fullBleed }, itemAttributes);
307
372
  default:
308
373
  throw new errors_1.ArgumentError(`Invalid XML BlockLayout - encountered "${tag}" where instead of BlockLayout.Panel`);
309
374
  }
@@ -313,7 +378,7 @@ function parseItemAttributes(attributes) {
313
378
  mobileOrder: BlockLayout.MobileOrder.parse(attributes['mobileOrder']),
314
379
  alignX: BlockLayout.Alignment.parse(attributes['alignX'], 'start'),
315
380
  alignY: BlockLayout.Alignment.parse(attributes['alignY'], 'start'),
316
- size: BlockLayout.Size.parse(attributes['size'], 'full'),
381
+ size: BlockLayout.Size.parse(attributes['size'], 'full')
317
382
  };
318
383
  }
319
384
  var ParsedElement;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nascentdigital/funnel-core",
3
- "version": "4.4.15",
3
+ "version": "4.4.16",
4
4
  "repository": {
5
5
  "url": "git+https://github.com/nascentdigital/funnel.git",
6
6
  "directory": "libs/core"
@@ -37,6 +37,7 @@
37
37
  "@nodable/flexible-xml-parser": "^1",
38
38
  "blurhash": "^2",
39
39
  "byte-size": "^9",
40
+ "classnames": "^2",
40
41
  "clone-deep": "^4",
41
42
  "css.escape": "^1",
42
43
  "date-fns": "^4",
@@ -63,5 +64,5 @@
63
64
  "jest-tsd": "^0.2.2",
64
65
  "ts-jest": "^29.3.2"
65
66
  },
66
- "gitHead": "242a9e0979c03d4037eb2462c5b01b2a095e8e8f"
67
- }
67
+ "gitHead": "d1bfd4468426b7326bd9662cd169242867025f5e"
68
+ }