@brightspace-ui/core 3.147.4 → 3.148.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.
@@ -446,6 +446,111 @@ If an item is draggable, the `drag-handle-text` attribute should be used to prov
446
446
  <d2l-my-drag-drop-elem></d2l-my-drag-drop-elem>
447
447
  ```
448
448
 
449
+ #### Draggable lists with interactive content
450
+
451
+ When a list item contains interactive content and the list item is not interactive in any way other than being `draggable` (i.e., not a link, button, `selectable`, or `expandable`), in order for the interactive content to have mouse events work as expected, one of the following should be done:
452
+ - use the `drag-target-handle-only` on the list item; this causes the drag target to be the handle only rather than the entire cell
453
+ - put the interactive content in the `actions` slot
454
+
455
+ These scenarios can be seen in the demo below.
456
+
457
+ <!-- docs: demo code display:block sandboxTitle:'List - Drag & Drop with interactive content'-->
458
+ ```html
459
+ <script type="module">
460
+ import '@brightspace-ui/core/components/list/list.js';
461
+ import '@brightspace-ui/core/components/list/list-item.js';
462
+ import '@brightspace-ui/core/components/list/list-item-content.js';
463
+ import '@brightspace-ui/core/components/switch/switch.js';
464
+ import '@brightspace-ui/core/components/tooltip/tooltip-help.js';
465
+ import { css, html, LitElement } from 'lit';
466
+ import { labelStyles } from '@brightspace-ui/core/components/typography/styles.js';
467
+
468
+ class ListDemoDragAndDropInteractiveUsage extends LitElement {
469
+ static get properties() {
470
+ return {
471
+ list: { type: Array }
472
+ };
473
+ }
474
+
475
+ static get styles() {
476
+ return labelStyles;
477
+ }
478
+
479
+ constructor() {
480
+ super();
481
+ this.list = [
482
+ { key: '1', content: 'Initially first list item' },
483
+ { key: '2', content: 'Initially second list item' }
484
+ ];
485
+ }
486
+
487
+ render() {
488
+ return html`
489
+ <div style="display: flex; gap: 2rem; flex-wrap: wrap;">
490
+ <div style="flex-grow: 1; min-width: 100px;">
491
+ <div class="d2l-label-text" style="padding-bottom: 1rem;">Using "drag-target-handle-only":</div>
492
+ ${this._renderList1()}
493
+ </div>
494
+
495
+ <div style="flex-grow: 1; min-width: 100px;">
496
+ <div class="d2l-label-text" style="padding-bottom: 1rem;">Using "actions" slot:</div>
497
+ ${this._renderList2()}
498
+ </div>
499
+ </div>
500
+ `;
501
+ }
502
+
503
+ _renderList1() {
504
+ const listItems = this.list.map((item) => {
505
+ return html`
506
+ <d2l-list-item draggable key="${item.key}" label="Draggable List Item" drag-target-handle-only>
507
+ <d2l-list-item-content>
508
+ ${item.content}
509
+ <div slot="secondary"><d2l-tooltip-help text="Hover for more info">Secondary information</d2l-tooltip-help></div>
510
+ </d2l-list-item-content>
511
+ </d2l-list-item>
512
+ `;
513
+ });
514
+
515
+ return html`
516
+ <d2l-list @d2l-list-item-position-change="${this._moveItems}">
517
+ ${listItems}
518
+ </d2l-list>
519
+ `;
520
+ }
521
+
522
+ _renderList2() {
523
+ const listItems = this.list.map((item) => {
524
+ return html`
525
+ <d2l-list-item draggable key="${item.key}" label="Draggable List Item">
526
+ <d2l-list-item-content>
527
+ ${item.content}
528
+ <div slot="secondary">Secondary information</div>
529
+ </d2l-list-item-content>
530
+ <div slot="actions">
531
+ <d2l-switch text="Action switch" text-position="hidden"></d2l-switch>
532
+ </div>
533
+ </d2l-list-item>
534
+ `;
535
+ });
536
+
537
+ return html`
538
+ <d2l-list @d2l-list-item-position-change="${this._moveItems}" >
539
+ ${listItems}
540
+ </d2l-list>
541
+ `;
542
+ }
543
+
544
+ _moveItems(e) {
545
+ e.detail.reorder(this.list, { keyFn: (item) => item.key });
546
+ this.requestUpdate('list', []);
547
+ }
548
+ }
549
+ customElements.define('d2l-my-drag-drop-elem-interactive', ListDemoDragAndDropInteractiveUsage);
550
+ </script>
551
+ <d2l-my-drag-drop-elem-interactive></d2l-my-drag-drop-elem-interactive>
552
+ ```
553
+
449
554
  ## List Controls [d2l-list-controls]
450
555
 
451
556
  The `d2l-list-controls` component can be placed in the `d2l-list`'s `controls` slot to provide a select-all checkbox, summary, a slot for `d2l-selection-action`s, and overflow-group behaviour.
@@ -14,6 +14,7 @@ import { RtlMixin } from '../../mixins/rtl/rtl-mixin.js';
14
14
  import { styleMap } from 'lit/directives/style-map.js';
15
15
 
16
16
  const usePopoverMixin = getFlag('GAUD-7355-tooltip-popover', false);
17
+ const useMutationObserver = getFlag('GAUD-8203-tooltip-mutation-observer', true);
17
18
 
18
19
  const contentBorderSize = 1;
19
20
  const contentHorizontalPadding = 15;
@@ -192,6 +193,7 @@ if (usePopoverMixin) {
192
193
  this.#handleTargetFocusBound = this.#handleTargetFocus.bind(this);
193
194
  this.#handleTargetMouseEnterBound = this.#handleTargetMouseEnter.bind(this);
194
195
  this.#handleTargetMouseLeaveBound = this.#handleTargetMouseLeave.bind(this);
196
+ this.#handleTargetMutationBound = this.#handleTargetMutation.bind(this);
195
197
  this.#handleTargetResizeBound = this.#handleTargetResize.bind(this);
196
198
  this.#handleTargetTouchEndBound = this.#handleTargetTouchEnd.bind(this);
197
199
  this.#handleTargetTouchStartBound = this.#handleTargetTouchStart.bind(this);
@@ -284,6 +286,7 @@ if (usePopoverMixin) {
284
286
  #handleTargetFocusBound;
285
287
  #handleTargetMouseEnterBound;
286
288
  #handleTargetMouseLeaveBound;
289
+ #handleTargetMutationBound;
287
290
  #handleTargetResizeBound;
288
291
  #handleTargetTouchEndBound;
289
292
  #handleTargetTouchStartBound;
@@ -299,6 +302,7 @@ if (usePopoverMixin) {
299
302
  #showing;
300
303
  #target;
301
304
  #targetSizeObserver;
305
+ #targetMutationObserver;
302
306
 
303
307
  #adaptPositionLocation(val) {
304
308
  switch (val) {
@@ -332,6 +336,12 @@ if (usePopoverMixin) {
332
336
 
333
337
  this.#targetSizeObserver = new ResizeObserver(this.#handleTargetResizeBound);
334
338
  this.#targetSizeObserver.observe(this.#target);
339
+
340
+ if (useMutationObserver) {
341
+ this.#targetMutationObserver = new MutationObserver(this.#handleTargetMutationBound);
342
+ this.#targetMutationObserver.observe(this.#target, { attributes: true, attributeFilter: ['id'] });
343
+ this.#targetMutationObserver.observe(this.#target.parentNode, { childList: true });
344
+ }
335
345
  }
336
346
 
337
347
  #findTarget() {
@@ -341,7 +351,7 @@ if (usePopoverMixin) {
341
351
  if (this.for) {
342
352
  const targetSelector = `#${cssEscape(this.for)}`;
343
353
  target = ownerRoot.querySelector(targetSelector);
344
- target = target || ownerRoot?.host?.querySelector(targetSelector);
354
+ target = (target || ownerRoot?.host?.querySelector(targetSelector)) ?? null;
345
355
  } else {
346
356
  const parentNode = this.parentNode;
347
357
  target = parentNode.nodeType === Node.DOCUMENT_FRAGMENT_NODE ? ownerRoot.host : parentNode;
@@ -402,6 +412,13 @@ if (usePopoverMixin) {
402
412
  setTimeout(() => this.#updateShowing(), 100); // delay to allow for mouseenter to fire if hovering on tooltip
403
413
  }
404
414
 
415
+ #handleTargetMutation([m]) {
416
+ if (!this.#target.isConnected || (m.target === this.#target && m.attributeName === 'id')) {
417
+ this.#targetMutationObserver.disconnect();
418
+ this._updateTarget();
419
+ }
420
+ }
421
+
405
422
  #handleTargetResize() {
406
423
  this.#resizeRunSinceTruncationCheck = true;
407
424
  if (!this.showing) return;
@@ -453,6 +470,11 @@ if (usePopoverMixin) {
453
470
  this.#targetSizeObserver.disconnect();
454
471
  this.#targetSizeObserver = null;
455
472
  }
473
+
474
+ if (this.#targetMutationObserver) {
475
+ this.#targetMutationObserver.disconnect();
476
+ this.#targetMutationObserver = null;
477
+ }
456
478
  }
457
479
 
458
480
  async #showingChanged(newValue, dispatch) {
@@ -954,6 +976,7 @@ if (usePopoverMixin) {
954
976
  this._onTargetMouseEnter = this._onTargetMouseEnter.bind(this);
955
977
  this._onTargetMouseLeave = this._onTargetMouseLeave.bind(this);
956
978
  this._onTargetResize = this._onTargetResize.bind(this);
979
+ this._onTargetMutation = this._onTargetMutation.bind(this);
957
980
  this._onTargetClick = this._onTargetClick.bind(this);
958
981
  this._onTargetTouchStart = this._onTargetTouchStart.bind(this);
959
982
  this._onTargetTouchEnd = this._onTargetTouchEnd.bind(this);
@@ -1069,13 +1092,12 @@ if (usePopoverMixin) {
1069
1092
  willUpdate(changedProperties) {
1070
1093
  super.willUpdate(changedProperties);
1071
1094
 
1072
- changedProperties.forEach((_, prop) => {
1073
- if (prop === 'for') {
1074
- this._updateTarget();
1075
- } else if (prop === 'forceShow') {
1076
- this._updateShowing();
1077
- }
1078
- });
1095
+ if (changedProperties.has('for')) {
1096
+ this._updateTarget();
1097
+ }
1098
+ if (changedProperties.has('forceShow')) {
1099
+ this._updateShowing();
1100
+ }
1079
1101
  }
1080
1102
 
1081
1103
  hide() {
@@ -1192,6 +1214,12 @@ if (usePopoverMixin) {
1192
1214
 
1193
1215
  this._targetSizeObserver = new ResizeObserver(this._onTargetResize);
1194
1216
  this._targetSizeObserver.observe(this._target);
1217
+
1218
+ if (useMutationObserver) {
1219
+ this._targetMutationObserver = new MutationObserver(this._onTargetMutation);
1220
+ this._targetMutationObserver.observe(this._target, { attributes: true, attributeFilter: ['id'] });
1221
+ this._targetMutationObserver.observe(this._target.parentNode, { childList: true });
1222
+ }
1195
1223
  }
1196
1224
 
1197
1225
  _computeAvailableSpaces(targetRect, spaceAround) {
@@ -1260,7 +1288,7 @@ if (usePopoverMixin) {
1260
1288
  if (this.for) {
1261
1289
  const targetSelector = `#${cssEscape(this.for)}`;
1262
1290
  target = ownerRoot.querySelector(targetSelector);
1263
- target = target || ownerRoot?.host?.querySelector(targetSelector);
1291
+ target = (target || ownerRoot?.host?.querySelector(targetSelector)) ?? null;
1264
1292
  } else {
1265
1293
  const parentNode = this.parentNode;
1266
1294
  target = parentNode.nodeType === Node.DOCUMENT_FRAGMENT_NODE ? ownerRoot.host : parentNode;
@@ -1391,6 +1419,13 @@ if (usePopoverMixin) {
1391
1419
  setTimeout(() => this._updateShowing(), 100); // delay to allow for mouseenter to fire if hovering on tooltip
1392
1420
  }
1393
1421
 
1422
+ _onTargetMutation([m]) {
1423
+ if (!this._target?.isConnected || (m.target === this._target && m.attributeName === 'id')) {
1424
+ this._targetMutationObserver?.disconnect();
1425
+ this._updateTarget();
1426
+ }
1427
+ }
1428
+
1394
1429
  _onTargetResize() {
1395
1430
  this._resizeRunSinceTruncationCheck = true;
1396
1431
  if (!this.showing) {
@@ -1426,6 +1461,11 @@ if (usePopoverMixin) {
1426
1461
  this._targetSizeObserver.disconnect();
1427
1462
  this._targetSizeObserver = null;
1428
1463
  }
1464
+
1465
+ if (this._targetMutationObserver) {
1466
+ this._targetMutationObserver.disconnect();
1467
+ this._targetMutationObserver = null;
1468
+ }
1429
1469
  }
1430
1470
 
1431
1471
  async _showingChanged(newValue, dispatch) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brightspace-ui/core",
3
- "version": "3.147.4",
3
+ "version": "3.148.0",
4
4
  "description": "A collection of accessible, free, open-source web components for building Brightspace applications",
5
5
  "type": "module",
6
6
  "repository": "https://github.com/BrightspaceUI/core.git",