@shortfuse/materialdesignweb 0.9.3 → 0.10.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 (53) hide show
  1. package/README.md +51 -37
  2. package/api/README.md +5 -0
  3. package/api/css.css-data.json +23 -0
  4. package/api/custom-elements.json +116343 -0
  5. package/api/html.html-data.json +2994 -0
  6. package/bin/mdw-css.js +8 -8
  7. package/components/BottomSheet.js +2 -2
  8. package/components/Box.js +2 -2
  9. package/components/NavItem.js +4 -1
  10. package/components/Page.js +3 -0
  11. package/components/Pane.js +0 -2
  12. package/components/SideSheet.js +2 -2
  13. package/components/Surface.js +2 -2
  14. package/core/Composition.js +57 -16
  15. package/core/CompositionAdapter.js +55 -12
  16. package/core/CustomElement.js +177 -34
  17. package/core/customTypes.js +66 -0
  18. package/core/jsonMergePatch.js +203 -20
  19. package/core/observe.js +256 -10
  20. package/dist/CustomElement.min.js +1 -1
  21. package/dist/CustomElement.min.js.map +3 -3
  22. package/dist/index.min.js +171 -158
  23. package/dist/index.min.js.map +4 -4
  24. package/dist/meta.json +1 -1
  25. package/loaders/theme.js +4 -1
  26. package/mixins/{FlexableMixin.js → FlexboxMixin.js} +5 -3
  27. package/package.json +32 -10
  28. package/services/theme.js +115 -97
  29. package/types/components/NavBarItem.d.ts +1 -0
  30. package/types/components/NavDrawerItem.d.ts +1 -0
  31. package/types/components/NavItem.d.ts +1 -0
  32. package/types/components/NavItem.d.ts.map +1 -1
  33. package/types/components/NavRailItem.d.ts +1 -0
  34. package/types/components/Page.d.ts.map +1 -1
  35. package/types/core/Composition.d.ts +5 -1
  36. package/types/core/Composition.d.ts.map +1 -1
  37. package/types/core/CompositionAdapter.d.ts +0 -25
  38. package/types/core/CompositionAdapter.d.ts.map +1 -1
  39. package/types/core/CustomElement.d.ts +122 -19
  40. package/types/core/CustomElement.d.ts.map +1 -1
  41. package/types/core/customTypes.d.ts +8 -0
  42. package/types/core/customTypes.d.ts.map +1 -1
  43. package/types/core/jsonMergePatch.d.ts +58 -4
  44. package/types/core/jsonMergePatch.d.ts.map +1 -1
  45. package/types/core/observe.d.ts +21 -2
  46. package/types/core/observe.d.ts.map +1 -1
  47. package/types/index.d.ts +1 -1
  48. package/types/mixins/FlexboxMixin.d.ts +14 -0
  49. package/types/mixins/FlexboxMixin.d.ts.map +1 -0
  50. package/types/services/theme.d.ts +6 -0
  51. package/types/services/theme.d.ts.map +1 -1
  52. package/types/mixins/FlexableMixin.d.ts +0 -14
  53. package/types/mixins/FlexableMixin.d.ts.map +0 -1
package/bin/mdw-css.js CHANGED
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { generateThemeCSS, generateTypographyGlobalCSS, themeOptionsFromSearchParams } from '../services/theme.js';
3
+ import { generateResetCSS, generateThemeCSS, generateTypographyGlobalCSS, themeOptionsFromSearchParams } from '../services/theme.js';
4
4
  import { getSearchParams } from '../utils/cli.js';
5
5
 
6
- process.stdout.write(
7
- generateThemeCSS(
8
- themeOptionsFromSearchParams(
9
- getSearchParams(),
10
- ),
11
- ),
12
- );
6
+ const options = themeOptionsFromSearchParams(getSearchParams());
7
+ process.stdout.write(generateThemeCSS(options));
8
+
13
9
  process.stdout.write(generateTypographyGlobalCSS());
10
+
11
+ if (options.resetCSS) {
12
+ process.stdout.write(generateResetCSS());
13
+ }
@@ -2,7 +2,7 @@ import CustomElement from '../core/CustomElement.js';
2
2
  import { ELEMENT_ANIMATION_TYPE, EVENT_HANDLER_TYPE } from '../core/customTypes.js';
3
3
  import AriaReflectorMixin from '../mixins/AriaReflectorMixin.js';
4
4
  import DelegatesFocusMixin from '../mixins/DelegatesFocusMixin.js';
5
- import FlexableMixin from '../mixins/FlexableMixin.js';
5
+ import FlexboxMixin from '../mixins/FlexboxMixin.js';
6
6
  import ResizeObserverMixin from '../mixins/ResizeObserverMixin.js';
7
7
  import ShapeMixin from '../mixins/ShapeMixin.js';
8
8
  import StateMixin from '../mixins/StateMixin.js';
@@ -98,7 +98,7 @@ CustomElement
98
98
  export default CustomElement
99
99
  .extend()
100
100
  .mixin(ThemableMixin)
101
- .mixin(FlexableMixin)
101
+ .mixin(FlexboxMixin)
102
102
  .mixin(ShapeMixin)
103
103
  .mixin(AriaReflectorMixin)
104
104
  .mixin(DelegatesFocusMixin)
package/components/Box.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import CustomElement from '../core/CustomElement.js';
2
- import FlexableMixin from '../mixins/FlexableMixin.js';
2
+ import FlexboxMixin from '../mixins/FlexboxMixin.js';
3
3
  import ThemableMixin from '../mixins/ThemableMixin.js';
4
4
 
5
5
  /**
@@ -9,7 +9,7 @@ import ThemableMixin from '../mixins/ThemableMixin.js';
9
9
  export default CustomElement
10
10
  .extend()
11
11
  .mixin(ThemableMixin)
12
- .mixin(FlexableMixin)
12
+ .mixin(FlexboxMixin)
13
13
  .html`<slot id=slot></slot>`
14
14
  .css`
15
15
  :host(:where([color])) {
@@ -62,6 +62,9 @@ export default CustomElement
62
62
  iconVariation({ active }) {
63
63
  return active ? 'filled' : null;
64
64
  },
65
+ _customRippleState({ active }) {
66
+ return active ? null : 'complete';
67
+ },
65
68
  })
66
69
  .html`
67
70
  <mdw-icon id=icon aria-hidden=true src={src} active={active} icon={icon} variation={iconVariation}></mdw-icon>
@@ -76,7 +79,7 @@ export default CustomElement
76
79
  anchor.setAttribute('href', '{_anchorHref}');
77
80
  anchor.before(html`
78
81
  <mdw-shape id=shape active={active} shape-style=full>
79
- <mdw-ripple id=ripple ripple-origin=center keep-alive hold-ripple ripple-state=${({ active }) => ((active) ? null : 'complete')}></mdw-ripple>
82
+ <mdw-ripple id=ripple ripple-origin=center keep-alive hold-ripple ripple-state={_customRippleState}></mdw-ripple>
80
83
  ${state}
81
84
  ${rippleContainer}
82
85
  </mdw-shape>
@@ -66,6 +66,8 @@ export default CustomElement
66
66
  --mdw-pane__max-width: 1040px;
67
67
  --mdw-pane__margin-inline: 16px;
68
68
  --mdw-pane__padding-inline: max(var(--mdw-pane__margin-inline), calc((100% - var(--mdw-pane__max-width)) / 2));
69
+ --mdw-pane__padding-block: 16px;
70
+ --mdw-pane__padding: var(--mdw-pane__padding-block) var(--mdw-pane__padding-inline);
69
71
  --mdw-pane__shape__size: 0;
70
72
  display: grid;
71
73
  column-gap: var(--mdw-page__margin);
@@ -83,6 +85,7 @@ export default CustomElement
83
85
  @media (min-width: 648px) {
84
86
  :host {
85
87
  --mdw-pane__margin-inline: 24px;
88
+ --mdw-pane__padding-block: 24px;
86
89
  }
87
90
  }
88
91
 
@@ -19,8 +19,6 @@ export default Box
19
19
  --mdw-ink: var(--mdw-color__on-surface);
20
20
  background-color: rgb(var(--mdw-bg));
21
21
  color: rgb(var(--mdw-ink));
22
-
23
- overflow: auto;
24
22
  }
25
23
  `
26
24
  .autoRegister('mdw-pane');
@@ -4,7 +4,7 @@ import { isRtl } from '../core/dom.js';
4
4
  import AriaReflectorMixin from '../mixins/AriaReflectorMixin.js';
5
5
  import DelegatesFocusMixin from '../mixins/DelegatesFocusMixin.js';
6
6
  import ElevationMixin from '../mixins/ElevationMixin.js';
7
- import FlexableMixin from '../mixins/FlexableMixin.js';
7
+ import FlexboxMixin from '../mixins/FlexboxMixin.js';
8
8
  import ResizeObserverMixin from '../mixins/ResizeObserverMixin.js';
9
9
  import ShapeMixin from '../mixins/ShapeMixin.js';
10
10
  import ThemableMixin from '../mixins/ThemableMixin.js';
@@ -36,7 +36,7 @@ const SUPPORTS_SCROLLEND = 'onscrollend' in window;
36
36
  export default CustomElement
37
37
  .extend()
38
38
  .mixin(ThemableMixin)
39
- .mixin(FlexableMixin)
39
+ .mixin(FlexboxMixin)
40
40
  .mixin(ElevationMixin)
41
41
  .mixin(ShapeMixin)
42
42
  .mixin(AriaReflectorMixin)
@@ -1,6 +1,6 @@
1
1
  import CustomElement from '../core/CustomElement.js';
2
2
  import ElevationMixin from '../mixins/ElevationMixin.js';
3
- import FlexableMixin from '../mixins/FlexableMixin.js';
3
+ import FlexboxMixin from '../mixins/FlexboxMixin.js';
4
4
  import ShapeMixin from '../mixins/ShapeMixin.js';
5
5
  import ThemableMixin from '../mixins/ThemableMixin.js';
6
6
 
@@ -12,7 +12,7 @@ import ThemableMixin from '../mixins/ThemableMixin.js';
12
12
  export default CustomElement
13
13
  .extend()
14
14
  .mixin(ThemableMixin)
15
- .mixin(FlexableMixin)
15
+ .mixin(FlexboxMixin)
16
16
  .mixin(ElevationMixin)
17
17
  .mixin(ShapeMixin)
18
18
  .html`<slot id=slot></slot>`
@@ -28,6 +28,7 @@ import { generateUID } from './uid.js';
28
28
  * @prop {ShadowRoot} [shadowRoot] where
29
29
  * @prop {any} [context] `this` on callbacks/events
30
30
  * @prop {any} [injections]
31
+ * @prop {number} [iterationIndex]
31
32
  */
32
33
 
33
34
  /**
@@ -162,6 +163,7 @@ import { generateUID } from './uid.js';
162
163
  * @prop {any} [defaultValue]
163
164
  * @prop {RenderGraphSearch} search
164
165
  * @prop {InterpolateOptions['injections']} [injections]
166
+ * @prop {number} [adapterIndex]
165
167
  */
166
168
 
167
169
  /**
@@ -379,7 +381,7 @@ function searchWithDeepProp(state, changes, data) {
379
381
  * @typedef InitializationState
380
382
  * @prop {Element} lastElement
381
383
  * @prop {ChildNode} lastChildNode
382
- * @prop {(Element|Text)[]} nodes
384
+ * @prop {(ChildNode)[]} nodes
383
385
  * @prop {any[]} caches
384
386
  * @prop {Comment[]} comments
385
387
  * @prop {Uint8Array} nodeStates
@@ -387,6 +389,7 @@ function searchWithDeepProp(state, changes, data) {
387
389
  * @prop {HTMLElement[]} refs
388
390
  * @prop {number} lastChildNodeIndex
389
391
  * @prop {RenderOptions<?>} options
392
+ * @prop {CompositionAdapter<any>[][]} adapters
390
393
  */
391
394
 
392
395
  /** Splits: `{template}text{template}` as `['', 'template', 'text', 'template', '']` */
@@ -480,6 +483,7 @@ export default class Composition {
480
483
  searchIndex: 0,
481
484
  cacheIndex: 0,
482
485
  commentIndex: 0,
486
+ adapterIndex: 0,
483
487
  /** @type {this['nodesToBind'][0]} */
484
488
  nodeEntry: null,
485
489
  };
@@ -698,6 +702,7 @@ export default class Composition {
698
702
  nodes: [],
699
703
  caches: this.initCache.slice(),
700
704
  refs: [],
705
+ adapters: [],
701
706
  options,
702
707
  };
703
708
 
@@ -743,6 +748,7 @@ export default class Composition {
743
748
  * @param {Partial<T>} changes
744
749
  * @param {T} data
745
750
  */
751
+ // eslint-disable-next-line no-shadow
746
752
  const draw = (changes, data) => {
747
753
  if (!changes) return;
748
754
  let ranSearch = false;
@@ -796,6 +802,7 @@ export default class Composition {
796
802
  * @param {any} value
797
803
  * @param {Partial<T>} [data]
798
804
  */
805
+ // eslint-disable-next-line no-shadow
799
806
  draw.byProp = (prop, value, data) => {
800
807
  if (!this.actionsByPropsUsed?.has(prop)) return;
801
808
  let ranSearch = false;
@@ -813,6 +820,7 @@ export default class Composition {
813
820
  }
814
821
 
815
822
  /** @type {Partial<T>} */
823
+ // eslint-disable-next-line no-shadow
816
824
  let changes;
817
825
  const actions = this.actionsByPropsUsed.get(prop);
818
826
  for (const action of actions) {
@@ -1256,7 +1264,7 @@ export default class Composition {
1256
1264
  return null;
1257
1265
  }
1258
1266
  const parsedValue = trimmed.slice(1, -1);
1259
- const [valueName, iterableName] = parsedValue.split(/\s+of\s+/);
1267
+ const [valueName, iterableClause] = parsedValue.split(/\s+of\s+/);
1260
1268
  element.removeAttribute('mdw-for');
1261
1269
  // Create a new composition targetting element as root
1262
1270
 
@@ -1286,6 +1294,9 @@ export default class Composition {
1286
1294
  index: null,
1287
1295
  };
1288
1296
 
1297
+ const iterableParts = iterableClause.split('.');
1298
+ const iterableDeepProp = iterableParts.length > 1 ? iterableParts : null;
1299
+ const iterableName = iterableDeepProp ? iterableParts[0] : iterableClause;
1289
1300
  const propsUsed = [iterableName];
1290
1301
  /** @type {RenderGraphSearch} */
1291
1302
  const search = {
@@ -1293,9 +1304,9 @@ export default class Composition {
1293
1304
  searchIndex: this._interpolationState.searchIndex++,
1294
1305
  query: null,
1295
1306
  prop: null,
1296
- deepProp: null,
1307
+ deepProp: iterableDeepProp,
1297
1308
  propsUsed,
1298
- deepPropsUsed: [[iterableName]],
1309
+ deepPropsUsed: iterableDeepProp ? [iterableDeepProp] : [[iterableName]],
1299
1310
  defaultValue: {},
1300
1311
  invocation: null,
1301
1312
  };
@@ -1306,16 +1317,27 @@ export default class Composition {
1306
1317
  nodeIndex: this._interpolationState.nodeIndex,
1307
1318
  search,
1308
1319
  commentIndex: this._interpolationState.commentIndex++,
1320
+ adapterIndex: this._interpolationState.adapterIndex++,
1309
1321
  injections,
1310
1322
  invocation(state, value, changes, data) {
1311
- if (!newComposition.adapter) {
1312
- // console.log({ state.options });
1323
+ // Optimistic get
1324
+ let adaptersArray = state.adapters[this.adapterIndex];
1325
+ const iterationIndex = state.options.iterationIndex ?? 0;
1326
+ let adapter;
1327
+ if (!adaptersArray || (!(adapter = adaptersArray[iterationIndex]))) {
1313
1328
  const instanceAnchorElement = state.nodes[this.nodeIndex];
1314
1329
  const anchorNode = createEmptyComment();
1330
+
1331
+ if (state.comments[this.commentIndex]) {
1332
+ throw new Error('Comment already exists at index');
1333
+ }
1334
+
1315
1335
  // Avoid leak
1316
1336
  state.comments[this.commentIndex] = anchorNode;
1337
+
1338
+ // @ts-ignore DocumentFragment in documents can be replaced (bad typings)
1317
1339
  instanceAnchorElement.replaceWith(anchorNode);
1318
- newComposition.adapter = new CompositionAdapter({
1340
+ adapter = new CompositionAdapter({
1319
1341
  anchorNode,
1320
1342
  composition: newComposition,
1321
1343
  renderOptions: {
@@ -1325,15 +1347,32 @@ export default class Composition {
1325
1347
  injections: this.injections,
1326
1348
  },
1327
1349
  });
1350
+
1351
+ // eslint-disable-next-line no-multi-assign
1352
+ adaptersArray = ((state.adapters[this.adapterIndex] ??= []));
1353
+ adaptersArray[iterationIndex] = adapter;
1354
+ }
1355
+
1356
+ const currentInjections = state.options.injections ?? this.injections;
1357
+ const source = data ?? state.options.store;
1358
+ let iterable = iterableDeepProp
1359
+ ? deepPropFromObject(iterableDeepProp, source)
1360
+ : source[iterableName];
1361
+ if (iterable === undefined && iterableDeepProp && currentInjections) {
1362
+ iterable = deepPropFromObject(iterableDeepProp, currentInjections);
1328
1363
  }
1329
- const { adapter } = newComposition;
1330
- const iterable = (data ?? state.options.store)[iterableName];
1331
1364
  // Remove oversized
1332
1365
  if (!iterable || iterable.length === 0) {
1333
1366
  adapter.removeEntries();
1334
1367
  return;
1335
1368
  }
1336
- const changeList = changes[iterableName];
1369
+
1370
+ const changeList = iterableDeepProp
1371
+ ? deepPropFromObject(iterableDeepProp, changes)
1372
+ : changes[iterableName];
1373
+
1374
+ if (changeList === undefined) return;
1375
+
1337
1376
  const innerChanges = { ...changes };
1338
1377
  const needTargetAll = newComposition.props.some((prop) => prop !== iterableName && prop in changes);
1339
1378
 
@@ -1351,9 +1390,10 @@ export default class Composition {
1351
1390
  const index = (+key);
1352
1391
  const resource = iterable[index];
1353
1392
  innerChanges[valueName] = change;
1354
- this.injections[valueName] = resource;
1355
- this.injections.index = index;
1356
-
1393
+ currentInjections[valueName] = resource;
1394
+ currentInjections.index = index;
1395
+ adapter.renderOptions.injections = currentInjections;
1396
+ adapter.renderOptions.iterationIndex = index;
1357
1397
  adapter.renderData(index, innerChanges, data, resource, change);
1358
1398
  }
1359
1399
  } else {
@@ -1376,9 +1416,10 @@ export default class Composition {
1376
1416
  }
1377
1417
  innerChanges[valueName] = change;
1378
1418
  }
1379
- this.injections[valueName] = resource;
1380
- this.injections.index = index;
1381
-
1419
+ currentInjections[valueName] = resource;
1420
+ currentInjections.index = index;
1421
+ adapter.renderOptions.injections = currentInjections;
1422
+ adapter.renderOptions.iterationIndex = index;
1382
1423
  adapter.renderData(index, innerChanges, data, resource, change);
1383
1424
  // adapter.renderIndex(index, innerChanges, data, resource);
1384
1425
  }
@@ -29,6 +29,38 @@ import { createEmptyComment } from './optimizations.js';
29
29
  * @prop {Comment} [comment]
30
30
  */
31
31
 
32
+ /**
33
+ * @param {any} node
34
+ * @return {HTMLElement}
35
+ */
36
+ function captureFocus(node) {
37
+ return node?.matches?.(':focus') ? node : node?.querySelector?.(':focus');
38
+ }
39
+
40
+ /** @param {HTMLElement} focused */
41
+ function restoreFocus(focused) {
42
+ if (focused && focused.isConnected && !focused.matches(':focus')) {
43
+ focused.focus();
44
+ }
45
+ }
46
+
47
+ /**
48
+ * @param {Element|Comment} node
49
+ * @param {ChildNode} beforeNode
50
+ */
51
+ function moveNode(node, beforeNode) {
52
+ const focused = captureFocus(node);
53
+ if (beforeNode) {
54
+ beforeNode.before(node);
55
+ } else {
56
+ node.parentNode?.append(node);
57
+ }
58
+ restoreFocus(focused);
59
+ }
60
+
61
+ // @ts-expect-error Bad typings
62
+ const hasMoveBefore = typeof Element.prototype.moveBefore === 'function';
63
+
32
64
  /** @template T */
33
65
  export default class CompositionAdapter {
34
66
  /** @param {DomAdapterCreateOptions<T>} options */
@@ -216,28 +248,37 @@ export default class CompositionAdapter {
216
248
 
217
249
  const correctMetadata = this.metadata[oldIndex];
218
250
 
219
- // Move back <=
251
+ // Swap metadata entries instead of removing/recreating.
220
252
  this.metadata[newIndex] = correctMetadata;
221
- this.metadata.splice(oldIndex, 1);
253
+ this.metadata[oldIndex] = metadataAtIndex;
222
254
 
223
255
  const { domNode: domNodeToRemove } = metadataAtIndex;
224
- domNodeToRemove.replaceWith(correctMetadata.domNode);
256
+
257
+ const removalContainer = domNodeToRemove.parentNode;
258
+ const correctNext = correctMetadata.domNode.nextSibling;
259
+ if (hasMoveBefore) {
260
+ // @ts-expect-error Bad typings
261
+ removalContainer.moveBefore(correctMetadata.domNode, domNodeToRemove);
262
+ if (correctNext) {
263
+ // @ts-expect-error Bad typings
264
+ removalContainer.moveBefore(domNodeToRemove, correctNext);
265
+ }
266
+ } else {
267
+ moveNode(correctMetadata.domNode, domNodeToRemove);
268
+ if (correctNext) {
269
+ moveNode(domNodeToRemove, correctNext);
270
+ }
271
+ }
225
272
 
226
273
  if (!skipOnMatch) {
227
- console.warn('no skip on match on swap', newIndex);
274
+ // console.warn('no skip on match on swap', newIndex);
228
275
  correctMetadata.render(changes, data);
229
276
  }
230
277
 
231
278
  // Remove posterior
232
279
 
233
280
  this.keys[newIndex] = key;
234
- this.keys.splice(oldIndex, 1);
235
-
236
- domNodeToRemove.remove();
237
-
238
- // Don't release in case we may need it later
239
- // console.debug('Caching key', key);
240
- metadataCache.set(currentKey, metadataAtIndex);
281
+ this.keys[oldIndex] = currentKey;
241
282
 
242
283
  return;
243
284
  }
@@ -266,7 +307,9 @@ export default class CompositionAdapter {
266
307
  removeEntries(startIndex = 0) {
267
308
  const { length } = this.metadata;
268
309
  for (let index = length - 1; index >= startIndex; index--) {
269
- this.metadata[index].domNode.remove();
310
+ const entry = this.metadata[index];
311
+ if (!entry || !entry.domNode) continue;
312
+ entry.domNode.remove();
270
313
  }
271
314
  this.metadata.length = startIndex;
272
315
  this.keys.length = startIndex;