@ssa-ui-kit/utils 3.21.3 → 3.22.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.
@@ -0,0 +1,41 @@
1
+ # @ssa-ui-kit/utils — AI source map
2
+
3
+ Generated from the TypeScript program at build time — do not edit by hand.
4
+
5
+ **Everything below is imported from the package root:** `import { X } from '@ssa-ui-kit/utils'`
6
+
7
+ 13 functions · 1 class · 4 exported types
8
+
9
+ ## Reading this file
10
+
11
+ - This is an index, not a reference. For full documentation and usage examples, read the matching `.d.ts` in `dist/`.
12
+
13
+ ## Utility namespaces
14
+
15
+ - `dateFormatters` — formatTime, formatDayOfWeek, formatDate, printDayOfTheWeek, getTimeAgo
16
+
17
+ ## Functions
18
+
19
+ - `assocPath` — Returns a copy of an object with a nested value replaced.
20
+ - `callAll` — Combines several handlers into one that calls each with the same arguments.
21
+ - `debounce` — Delays a call until the input stops arriving.
22
+ - `dissocPath` — Returns a copy of an object with the key at a nested path removed.
23
+ - `generateRange` — Builds the list of page numbers a paginator should display.
24
+ - `isNill` — Checks whether a value is `null` or `undefined`.
25
+ - `mapObjIndexed` — Maps over an object's values, keeping its keys.
26
+ - `path` — Reads a nested value by key path.
27
+ - `pathOr` — Reads a nested value by key path, falling back to a default when missing.
28
+ - `prop` — Reads one own property from an object.
29
+ - `propOr` — Reads one own property, falling back to a default when it is missing.
30
+ - `seededRandom` — A deterministic stand-in for `Math.random`, for generating fixture data.
31
+ - `throttle` — Rate-limits a function to at most one call per interval.
32
+
33
+ ## Classes
34
+
35
+ - `ClassnameArray` — A `string[]` with chainable helpers for building a class list.
36
+
37
+ ## Exported types
38
+
39
+ _Names only — read the `.d.ts` for their shape._
40
+
41
+ `Has` · `MustInclude` · `PathObject` · `PathValue`
package/dist/index.js CHANGED
@@ -79,6 +79,21 @@ __webpack_require__.d(dateFormatters_namespaceObject, {
79
79
 
80
80
  ;// ./src/utils/CallAll.ts
81
81
  /* eslint-disable @typescript-eslint/no-explicit-any */
82
+ /**
83
+ * Combines several handlers into one that calls each with the same arguments.
84
+ *
85
+ * Nullish entries are skipped, so an optional prop can be passed straight
86
+ * through without a guard — the usual case is merging a component's own handler
87
+ * with one a consumer supplied.
88
+ *
89
+ * Handlers run in order and their return values are discarded. Because they all
90
+ * receive the same event object, one calling `stopPropagation` affects the
91
+ * others' view of it, but it does not stop later handlers from running.
92
+ *
93
+ * @example
94
+ * // `props.onClick` may be undefined — callAll skips it
95
+ * <button onClick={callAll(closeMenu, props.onClick)}>Close</button>
96
+ */
82
97
  const callAll = (...fns) => (...args) => fns.forEach(fn => fn?.(...args));
83
98
  /* eslint-enable @typescript-eslint/no-explicit-any */
84
99
  ;// ./src/utils/isNill.ts
@@ -96,6 +111,33 @@ const callAll = (...fns) => (...args) => fns.forEach(fn => fn?.(...args));
96
111
  */
97
112
  const isNill = value => value === null || value === undefined;
98
113
  ;// ./src/utils/throttle/throttle.ts
114
+ /**
115
+ * Rate-limits a function to at most one call per interval.
116
+ *
117
+ * ### Returns a `[throttled, cancel]` tuple
118
+ * Not a callable function with a `.cancel()` method as in lodash — destructure
119
+ * it: `const [onScroll, cancelScroll] = throttle(fn, 100)`.
120
+ *
121
+ * Leading **and** trailing: the first call runs immediately, calls during the
122
+ * cooling-off window are dropped except the last, and that one runs when the
123
+ * window closes. So a burst produces a call at the start and a call at the end,
124
+ * never a silent gap — unlike `debounce`, which only fires at the end.
125
+ *
126
+ * Call `cancel` on unmount to clear a pending trailing invocation.
127
+ *
128
+ * @param delayMs - Minimum gap between invocations, in ms.
129
+ *
130
+ * @example
131
+ * const [onScroll, cancel] = throttle(() => setOffset(window.scrollY), 100);
132
+ *
133
+ * useEffect(() => {
134
+ * window.addEventListener('scroll', onScroll);
135
+ * return () => {
136
+ * window.removeEventListener('scroll', onScroll);
137
+ * cancel();
138
+ * };
139
+ * }, [onScroll, cancel]);
140
+ */
99
141
  const throttle = (fn, delayMs) => {
100
142
  let isThrottled = false;
101
143
  let savedArgs = null;
@@ -124,6 +166,29 @@ const throttle = (fn, delayMs) => {
124
166
  }];
125
167
  };
126
168
  ;// ./src/utils/debounce/debounce.ts
169
+ /**
170
+ * Delays a call until the input stops arriving.
171
+ *
172
+ * ### Returns a `[debounced, cancel]` tuple
173
+ * Not a callable function with a `.cancel()` method as in lodash — destructure
174
+ * it: `const [search, cancelSearch] = debounce(fn, 300)`. Calling the returned
175
+ * value directly is a type error, because it is an array.
176
+ *
177
+ * Trailing edge only: nothing runs until `wait` ms have passed with no further
178
+ * calls, and only the most recent arguments are used. For a call that fires
179
+ * immediately and then rate-limits, use `throttle`.
180
+ *
181
+ * Call `cancel` on unmount so a pending invocation cannot fire against a
182
+ * component that is gone.
183
+ *
184
+ * @param wait - Quiet period in ms before the call goes through. @default 200
185
+ *
186
+ * @example
187
+ * const [search, cancel] = debounce((term: string) => void fetchResults(term), 300);
188
+ *
189
+ * useEffect(() => cancel, [cancel]);
190
+ * <Input onChange={(e) => search(e.target.value)} />
191
+ */
127
192
  const debounce = (func, wait = 200) => {
128
193
  let timeoutId = null;
129
194
  const executedFunction = (...args) => {
@@ -157,15 +222,64 @@ const periodValues = {
157
222
  hour: 3600,
158
223
  min: 60
159
224
  };
225
+
226
+ /**
227
+ * Formats a timestamp as a zero-padded 12-hour clock time, e.g. `"09:05 AM"`.
228
+ *
229
+ * Locale is hard-coded to `en-US`, so output does not follow the user's locale.
230
+ * The **time zone is not** — it uses the runtime's, so a server and a browser
231
+ * can render the same timestamp differently.
232
+ *
233
+ * @param timestampMs - Milliseconds since the epoch, as `Date.now()` returns.
234
+ *
235
+ * @example
236
+ * dateFormatters.formatTime(1735732800000); // '09:20 AM'
237
+ */
160
238
  const formatTime = timestampMs => new Date(timestampMs).toLocaleTimeString('en-US', {
161
239
  hour: '2-digit',
162
240
  minute: '2-digit'
163
241
  });
242
+
243
+ /**
244
+ * Returns the three-letter English weekday for a timestamp, e.g. `"Mon"`.
245
+ *
246
+ * Uses the runtime's time zone, so a timestamp near midnight can resolve to a
247
+ * different day than the user expects.
248
+ *
249
+ * @param timestampMs - Milliseconds since the epoch.
250
+ *
251
+ * @example
252
+ * dateFormatters.formatDayOfWeek(Date.now()); // 'Thu'
253
+ */
164
254
  const formatDayOfWeek = timestampMs => dayOfWeekNames[new Date(timestampMs).getDay()];
255
+
256
+ /**
257
+ * Formats a timestamp as day and abbreviated month, e.g. `"05 Jan"` — no year.
258
+ *
259
+ * Locale is hard-coded to `en-US`. Since the year is omitted, do not use it
260
+ * where dates span more than one year without adding the year yourself.
261
+ *
262
+ * @param timestampMs - Milliseconds since the epoch.
263
+ *
264
+ * @example
265
+ * dateFormatters.formatDate(1735732800000); // '01 Jan'
266
+ */
165
267
  const formatDate = timestampMs => new Date(timestampMs).toLocaleDateString('en-US', {
166
268
  day: '2-digit',
167
269
  month: 'short'
168
270
  });
271
+
272
+ /**
273
+ * Maps a weekday index to its three-letter English name.
274
+ *
275
+ * Takes a **day index** (0 = Sunday … 6 = Saturday), not a timestamp — for a
276
+ * timestamp use `formatDayOfWeek`. Anything outside 0–6 returns `undefined`,
277
+ * so the return type is `string | undefined`.
278
+ *
279
+ * @example
280
+ * dateFormatters.printDayOfTheWeek(0); // 'Sun'
281
+ * dateFormatters.printDayOfTheWeek(9); // undefined
282
+ */
169
283
  const printDayOfTheWeek = day => {
170
284
  switch (day) {
171
285
  case 0:
@@ -184,6 +298,25 @@ const printDayOfTheWeek = day => {
184
298
  return 'Sat';
185
299
  }
186
300
  };
301
+
302
+ /**
303
+ * Renders how long ago a date was, e.g. `"3 days ago"`, `"1 hour ago"`.
304
+ *
305
+ * Falls back to `"Just Now"` for anything under a minute. Granularity stops at
306
+ * years, and months are approximated as 30 days, so long gaps drift from the
307
+ * calendar.
308
+ *
309
+ * Throws `Error('Invalid date')` when the value cannot be parsed — including
310
+ * date strings the runtime does not recognise, so prefer passing a timestamp.
311
+ * The result is computed against `Date.now()` at call time and does not update
312
+ * on its own; re-render on a timer if it needs to stay current.
313
+ *
314
+ * @param timeValue - Timestamp in ms, or any string `new Date()` accepts.
315
+ *
316
+ * @example
317
+ * dateFormatters.getTimeAgo(Date.now() - 3 * 86400 * 1000); // '3 days ago'
318
+ * dateFormatters.getTimeAgo(Date.now() - 5000); // 'Just Now'
319
+ */
187
320
  const getTimeAgo = timeValue => {
188
321
  const date = new Date(timeValue).getTime();
189
322
  if (Number.isNaN(date)) {
@@ -201,6 +334,25 @@ const getTimeAgo = timeValue => {
201
334
  return 'Just Now';
202
335
  };
203
336
  ;// ./src/utils/objects/mapObjIndexed.ts
337
+ /**
338
+ * Maps over an object's values, keeping its keys.
339
+ *
340
+ * ### Not curried, unlike the accessors in this package
341
+ * `prop`, `path`, `propOr` and `assocPath` all take their arguments one group
342
+ * at a time; this one takes both at once — `mapObjIndexed(fn, obj)`. Ramda's
343
+ * version is curried, so a habit carried over from there will not compile here.
344
+ *
345
+ * The callback receives `(value, key, obj)`, mirroring `Array.prototype.map`.
346
+ * Only own enumerable keys are visited, and a new object is returned.
347
+ *
348
+ * @example
349
+ * mapObjIndexed((value) => value * 2, { a: 1, b: 2 }); // { a: 2, b: 4 }
350
+ *
351
+ * @example
352
+ * // The key is available as the second argument
353
+ * mapObjIndexed((value, key) => `${key}=${value}`, { page: 2, size: 20 });
354
+ * // { page: 'page=2', size: 'size=20' }
355
+ */
204
356
  const mapObjIndexed = (fn, obj) => {
205
357
  return Object.keys(obj).reduce((result, key) => {
206
358
  result[key] = fn(obj[key], key, obj);
@@ -209,16 +361,6 @@ const mapObjIndexed = (fn, obj) => {
209
361
  };
210
362
  ;// ./src/utils/pagination/generateRange.ts
211
363
 
212
- /**
213
- * The function that returns an array of page numbers to show in the pagination
214
- * component.
215
- *
216
- * Rules:
217
- * - To always show the 1st and the last page.
218
- * - To show one item before and one item after the selected page.
219
- * - To return "-1" for the skipped items. This is to be able to display "..." in
220
- * the pagination component.
221
- * */
222
364
  const SKIPPED_ITEMS_DELTA = 2;
223
365
  const getSelectedRange = (pagesCount, selectedPage) => {
224
366
  const range = [];
@@ -238,6 +380,34 @@ const fill = (range, minValue, maxValue) => {
238
380
  range.push(i);
239
381
  }
240
382
  };
383
+
384
+ /**
385
+ * Builds the list of page numbers a paginator should display.
386
+ *
387
+ * Always includes the first and last page, plus one page either side of the
388
+ * selection, and collapses the rest.
389
+ *
390
+ * ### `-1` marks a gap, it is not a page
391
+ * Skipped stretches are represented by `-1`, which the caller renders as an
392
+ * ellipsis. Render the array directly and a `-1` button appears — check for it:
393
+ * `range.map((page) => (page === -1 ? <Ellipsis /> : <PageButton page={page} />))`.
394
+ *
395
+ * ### It throws on bad input rather than clamping
396
+ * A non-integer `pagesCount` or `selectedPage`, or a selection outside
397
+ * `1…pagesCount`, raises an `Error`. Only `pagesCount <= 0` is tolerated,
398
+ * returning `[]`. Note pages are **1-based**, so `selectedPage: 0` throws.
399
+ *
400
+ * @param pagesCount - Total pages. `<= 0` yields an empty array.
401
+ * @param selectedPage - Current page, 1-based. Omit for the unselected layout.
402
+ *
403
+ * @example
404
+ * generateRange(5); // [1, 2, 3, 4, 5] — short enough to show in full
405
+ * generateRange(10); // [1, 2, 3, -1, 10]
406
+ *
407
+ * @example
408
+ * generateRange(10, 5); // [1, -1, 4, 5, 6, -1, 10] — gaps on both sides
409
+ * generateRange(10, 10); // [1, -1, 9, 10]
410
+ */
241
411
  const generateRange = (pagesCount, selectedPage) => {
242
412
  if (isNill(pagesCount) || !Number.isInteger(pagesCount)) {
243
413
  throw new Error('Pages count should be an integer');
@@ -279,11 +449,65 @@ const generateRange = (pagesCount, selectedPage) => {
279
449
  };
280
450
  /* harmony default export */ const pagination_generateRange = (generateRange);
281
451
  ;// ./src/utils/objects/assocPath.ts
452
+ /**
453
+ * Returns a copy of an object with a nested value replaced.
454
+ *
455
+ * **Curried, and only curried**: `assocPath(keys, value)(obj)`. The path is an
456
+ * array of keys; intermediate objects are created as needed. The input is never
457
+ * mutated.
458
+ *
459
+ * ### It deep-clones through JSON, which is lossy
460
+ * The copy is produced with `JSON.parse(JSON.stringify(...))`, so any value the
461
+ * JSON round-trip cannot represent is altered — silently, and with no type
462
+ * error, because the signature still claims `T`:
463
+ *
464
+ * - `Date` becomes an ISO **string**
465
+ * - `Map`, `Set` and class instances become `{}` (prototypes are lost)
466
+ * - keys whose value is `undefined` are **dropped**
467
+ * - `NaN` and `Infinity` become `null`
468
+ * - functions are dropped
469
+ *
470
+ * Safe for plain JSON-shaped state; for anything richer, spread the update by
471
+ * hand or use a structural-sharing helper.
472
+ *
473
+ * @example
474
+ * assocPath(['user', 'name'], 'Ada')({ user: { name: 'Grace', id: 1 } });
475
+ * // { user: { name: 'Ada', id: 1 } }
476
+ *
477
+ * @example
478
+ * // Lossy: `when` comes back as a string, not a Date
479
+ * assocPath(['id'], 2)({ id: 1, when: new Date() });
480
+ * // { id: 2, when: '2020-01-01T00:00:00.000Z' }
481
+ */
282
482
  const assocPath = ([first, ...rest], value) => sourceObject => JSON.parse(JSON.stringify({
283
483
  ...sourceObject,
284
484
  [first]: rest.length ? assocPath(rest, value)(sourceObject[first]) : value
285
485
  }));
286
486
  ;// ./src/utils/objects/dissocPath.ts
487
+ /**
488
+ * Returns a copy of an object with the key at a nested path removed.
489
+ *
490
+ * **Curried, and only curried**: `dissocPath(keys)(obj)`. The path is an array
491
+ * of keys. The input is never mutated.
492
+ *
493
+ * ### Missing intermediate keys throw
494
+ * Only the final key is optional. If a key *along the way* is absent the walk
495
+ * dereferences `undefined` and raises a `TypeError`, so guard paths that may
496
+ * not exist. Removing an already-absent leaf is a harmless no-op.
497
+ *
498
+ * ### It deep-clones through JSON, which is lossy
499
+ * Same caveat as `assocPath`: `Date` becomes a string, `Map`/`Set`/class
500
+ * instances become `{}`, `undefined` values are dropped, and `NaN`/`Infinity`
501
+ * become `null`. Use it on plain JSON-shaped state.
502
+ *
503
+ * @example
504
+ * dissocPath(['user', 'token'])({ user: { id: 1, token: 'x' } });
505
+ * // { user: { id: 1 } }
506
+ *
507
+ * @example
508
+ * dissocPath(['missing'])({ x: 1 }); // { x: 1 } — no-op
509
+ * dissocPath(['a', 'b'])({ x: 1 }); // TypeError: `a` does not exist
510
+ */
287
511
  const dissocPath = path => sourceObject => {
288
512
  const resultObject = JSON.parse(JSON.stringify(sourceObject));
289
513
  path.reduce((acc, key, index) => {
@@ -295,12 +519,48 @@ const dissocPath = path => sourceObject => {
295
519
  return resultObject;
296
520
  };
297
521
  ;// ./src/utils/objects/path.ts
522
+ /**
523
+ * Reads a nested value by key path.
524
+ *
525
+ * **Curried, and only curried**: `path(keys)(obj)`. The path is an **array** of
526
+ * keys, not a dotted string — `path(['user', 'name'])`, never
527
+ * `path('user.name')`. Numeric keys index into arrays.
528
+ *
529
+ * Every step is optional-chained, so a missing key anywhere along the way
530
+ * yields `undefined` instead of throwing. The return type defaults to `unknown`,
531
+ * so annotate it when you need a narrower type.
532
+ *
533
+ * @example
534
+ * path(['user', 'name'])({ user: { name: 'Ada' } }); // 'Ada'
535
+ * path(['user', 'name'])({}); // undefined
536
+ *
537
+ * @example
538
+ * // Numeric keys walk into arrays
539
+ * path<Response, string>(['items', 0, 'label'])(response);
540
+ */
298
541
  const path_path = path => obj => path.reduce((prev, curr) => prev?.[curr], obj);
299
542
  ;// ./src/utils/objects/pathOr.ts
300
543
 
301
544
 
302
545
 
303
546
  /* eslint-disable @typescript-eslint/no-explicit-any */
547
+ /**
548
+ * Reads a nested value by key path, falling back to a default when missing.
549
+ *
550
+ * **Curried, and only curried**: `pathOr(fallback, keys)(obj)`. Default first,
551
+ * then an **array** of keys — not a dotted string, and not lodash's
552
+ * `get(obj, path, fallback)` order.
553
+ *
554
+ * ### Only `null` and `undefined` trigger the default
555
+ * As with `propOr`, the fallback applies to nullish values only. A nested `0`
556
+ * or `''` is returned unchanged. Because every step is optional-chained, a
557
+ * break anywhere along the path resolves to the default rather than throwing.
558
+ *
559
+ * @example
560
+ * pathOr('—', ['user', 'name'])({ user: { name: 'Ada' } }); // 'Ada'
561
+ * pathOr('—', ['user', 'name'])({}); // '—'
562
+ * pathOr('—', ['stats', 'total'])({ stats: { total: 0 } }); // 0, not '—'
563
+ */
304
564
  const pathOr = (defaultValue, path) => obj => {
305
565
  const result = path_path(path)(obj);
306
566
  return isNill(result) ? defaultValue : result;
@@ -308,12 +568,52 @@ const pathOr = (defaultValue, path) => obj => {
308
568
  /* eslint-enable @typescript-eslint/no-explicit-any */
309
569
  ;// ./src/utils/objects/prop.ts
310
570
  /* eslint-disable @typescript-eslint/no-explicit-any */
571
+ /**
572
+ * Reads one own property from an object.
573
+ *
574
+ * **Curried, and only curried** — unlike Ramda's `prop`, this takes the key
575
+ * first and returns a reader. `prop('name', user)` is a type error; write
576
+ * `prop('name')(user)`. That shape is what makes it usable point-free, e.g. as
577
+ * `items.map(prop('id'))`.
578
+ *
579
+ * Reads a single key, not a dotted path: `prop('a.b')` looks for a literal
580
+ * `"a.b"` key. Use `path` for nested access. A nullish object yields
581
+ * `undefined` rather than throwing.
582
+ *
583
+ * @example
584
+ * prop('name')({ name: 'Ada' }); // 'Ada'
585
+ * prop('name')(undefined); // undefined
586
+ *
587
+ * @example
588
+ * // Point-free over a collection
589
+ * users.map(prop<User, string>('id'));
590
+ */
311
591
  const prop = propName => obj => obj?.[propName];
312
592
  ;// ./src/utils/objects/propOr.ts
313
593
 
314
594
 
315
595
 
316
596
  /* eslint-disable @typescript-eslint/no-explicit-any */
597
+ /**
598
+ * Reads one own property, falling back to a default when it is missing.
599
+ *
600
+ * **Curried, and only curried**: `propOr(fallback, key)(obj)`. The default
601
+ * comes first, matching Ramda's argument order — not lodash's
602
+ * `get(obj, key, fallback)`.
603
+ *
604
+ * ### Only `null` and `undefined` trigger the default
605
+ * The fallback applies when the value is nullish, **not** when it is merely
606
+ * falsy. `0`, `''` and `false` are returned as-is, so `propOr('—', 'count')`
607
+ * on `{ count: 0 }` gives `0`, not `'—'`. That is usually what you want for
608
+ * numbers; if you need empty strings to fall back too, check explicitly.
609
+ *
610
+ * Reads a single key, not a dotted path — use `pathOr` for nested access.
611
+ *
612
+ * @example
613
+ * propOr('—', 'name')({ name: 'Ada' }); // 'Ada'
614
+ * propOr('—', 'name')({}); // '—'
615
+ * propOr('—', 'count')({ count: 0 }); // 0, not '—'
616
+ */
317
617
  const propOr = (defaultValue, propName) => obj => {
318
618
  const result = prop(propName)(obj);
319
619
  return isNill(result) ? defaultValue : result;
@@ -328,7 +628,22 @@ const propOr = (defaultValue, propName) => obj => {
328
628
 
329
629
 
330
630
  ;// ./src/utils/story.ts
331
- // Useful for generating test datasets for storybook stories
631
+ /**
632
+ * A deterministic stand-in for `Math.random`, for generating fixture data.
633
+ *
634
+ * Returns a generator function; calling it yields the next value in `[0, 1)`.
635
+ * The same seed always produces the same sequence, which is the point — chart
636
+ * and table stories keep identical data between renders, so visual diffs show
637
+ * real changes rather than new noise.
638
+ *
639
+ * Linear congruential, so it is fine for demo data and unfit for anything
640
+ * needing statistical quality or unpredictability.
641
+ *
642
+ * @example
643
+ * const random = seededRandom(42);
644
+ * const series = Array.from({ length: 30 }, () => Math.round(random() * 100));
645
+ * // identical on every run
646
+ */
332
647
  const seededRandom = seed => {
333
648
  return () => {
334
649
  // Linear congruential generator
@@ -337,6 +652,32 @@ const seededRandom = seed => {
337
652
  };
338
653
  };
339
654
  ;// ./src/utils/ClassnameArray/ClassnameArray.ts
655
+ /**
656
+ * A `string[]` with chainable helpers for building a class list.
657
+ *
658
+ * ### It mutates, and it is a real Array
659
+ * `toggle` and `clear` change the instance in place and return `this`, so calls
660
+ * chain but the original is modified — do not share one instance across
661
+ * renders. Because it extends `Array`, `join(' ')` produces the final
662
+ * `className`, and every array method still works.
663
+ *
664
+ * Start from `new ClassnameArray()` and toggle onto it, or seed one with
665
+ * `ClassnameArray.from([...])`. Avoid `new ClassnameArray(n)` with a single
666
+ * number — the inherited `Array` constructor reads that as a length, giving you
667
+ * `n` empty slots.
668
+ *
669
+ * @example
670
+ * const classNames = new ClassnameArray();
671
+ * classNames
672
+ * .toggle('day--selected', isSelected)
673
+ * .toggle('day--disabled', isDisabled);
674
+ *
675
+ * <td className={classNames.join(' ')} />
676
+ *
677
+ * @example
678
+ * // Seeded with a base class
679
+ * ClassnameArray.from(['button']).toggle('button--active', isActive);
680
+ */
340
681
  class ClassnameArray extends Array {
341
682
  toggle(className, condition) {
342
683
  const index = this.indexOf(className);