@brightspace-ui/core 2.41.0 → 2.42.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.
@@ -254,7 +254,7 @@ A tag-list allowing the user to see (and remove) the currently applied filters.
254
254
  display: block;
255
255
  max-width: 100%;
256
256
  }
257
- }
257
+ }
258
258
  </style>
259
259
  <div class="filter-wrapper">
260
260
  <d2l-filter-tags filter-ids="core-filter core-filter-2"></d2l-filter-tags>
@@ -300,7 +300,78 @@ A tag-list allowing the user to see (and remove) the currently applied filters.
300
300
 
301
301
  ## Filter Overflow Group [d2l-filter-overflow-group]
302
302
 
303
- **Coming Soon!**
303
+ The `d2l-filter-overflow-group` is a container for multiple filters that handles overflow on smaller screens. Overflowing filters are displayed in a single filter.
304
+
305
+ <!-- docs: demo live name:d2l-filter-overflow-group align:start display:block autoSize:false size:medium -->
306
+ ```html
307
+ <script type="module">
308
+ import '@brightspace-ui/core/components/filter/filter.js';
309
+ import '@brightspace-ui/core/components/filter/filter-dimension-set.js';
310
+ import '@brightspace-ui/core/components/filter/filter-dimension-set-value.js';
311
+ import '@brightspace-ui/core/components/filter/filter-overflow-group.js';
312
+ </script>
313
+ <d2l-filter-overflow-group>
314
+ <d2l-filter>
315
+ <d2l-filter-dimension-set key="skill" text="Skill">
316
+ <d2l-filter-dimension-set-value key="communication" text="Fall"></d2l-filter-dimension-set-value>
317
+ <d2l-filter-dimension-set-value key="leadership" text="Winter"></d2l-filter-dimension-set-value>
318
+ <d2l-filter-dimension-set-value key="management" text="Spring"></d2l-filter-dimension-set-value>
319
+ <d2l-filter-dimension-set-value key="planning" text="Summer"></d2l-filter-dimension-set-value>
320
+ </d2l-filter-dimension-set>
321
+ </d2l-filter>
322
+ <d2l-filter>
323
+ <d2l-filter-dimension-set key="type" text="Type" selection-single>
324
+ <d2l-filter-dimension-set-value key="certificate" text="Certificate"></d2l-filter-dimension-set-value>
325
+ <d2l-filter-dimension-set-value key="degree" text="Degree"></d2l-filter-dimension-set-value>
326
+ <d2l-filter-dimension-set-value key="diploma" text="Diploma"></d2l-filter-dimension-set-value>
327
+ <d2l-filter-dimension-set-value key="course" text="Course"></d2l-filter-dimension-set-value>
328
+ </d2l-filter-dimension-set>
329
+ </d2l-filter>
330
+ <d2l-filter>
331
+ <d2l-filter-dimension-set key="course" text="Course" select-all>
332
+ <d2l-filter-dimension-set-value key="art" text="Art"></d2l-filter-dimension-set-value>
333
+ <d2l-filter-dimension-set-value key="astronomy" text="Astronomy" selected></d2l-filter-dimension-set-value>
334
+ <d2l-filter-dimension-set-value key="biology" text="Biology"></d2l-filter-dimension-set-value>
335
+ <d2l-filter-dimension-set-value key="chemistry" text="Chemistry"></d2l-filter-dimension-set-value>
336
+ </d2l-filter-dimension-set>
337
+ <d2l-filter-dimension-set key="duration" text="Duration">
338
+ <d2l-filter-dimension-set-value key="lessthanthree" text="< 3 months"></d2l-filter-dimension-set-value>
339
+ <d2l-filter-dimension-set-value key="threetosix" text="3-6 months"></d2l-filter-dimension-set-value>
340
+ <d2l-filter-dimension-set-value key="sixtotwelve" text="6-12 months"></d2l-filter-dimension-set-value>
341
+ </d2l-filter-dimension-set>
342
+ </d2l-filter>
343
+ <d2l-filter>
344
+ <d2l-filter-dimension-set key="provider" text="Semester3">
345
+ <d2l-filter-dimension-set-value key="mcmaster" text="McMaster"></d2l-filter-dimension-set-value>
346
+ <d2l-filter-dimension-set-value key="powered" text="PowerED"></d2l-filter-dimension-set-value>
347
+ <d2l-filter-dimension-set-value key="guelph" text="University of Guelph"></d2l-filter-dimension-set-value>
348
+ <d2l-filter-dimension-set-value key="manitoba" text="University of Manitoba"></d2l-filter-dimension-set-value>
349
+ </d2l-filter-dimension-set>
350
+ </d2l-filter>
351
+ <d2l-filter>
352
+ <d2l-filter-dimension-set key="format" text="Format">
353
+ <d2l-filter-dimension-set-value key="selfpaced" text="Self-Paced"></d2l-filter-dimension-set-value>
354
+ <d2l-filter-dimension-set-value key="instructor" text="Instructor Lead" selected></d2l-filter-dimension-set-value>
355
+ </d2l-filter-dimension-set>
356
+ </d2l-filter>
357
+ <d2l-filter>
358
+ <d2l-filter-dimension-set key="language" text="Language" selection-single>
359
+ <d2l-filter-dimension-set-value key="english" text="English"></d2l-filter-dimension-set-value>
360
+ <d2l-filter-dimension-set-value key="french" text="French"></d2l-filter-dimension-set-value>
361
+ <d2l-filter-dimension-set-value key="spanish" text="Spanish"></d2l-filter-dimension-set-value>
362
+ </d2l-filter-dimension-set>
363
+ </d2l-filter>
364
+ </d2l-filter-overflow-group>
365
+ ```
366
+
367
+ <!-- docs: start hidden content -->
368
+ ### Properties
369
+
370
+ | Property | Type | Description |
371
+ |---|---|---|
372
+ | `min-to-show` | Number | The minimum number of elements to always show. Please consult the design team when using this attribute. |
373
+ | `max-to-show` | Number | The maximum number of elements to show |
374
+ <!-- docs: end hidden content -->
304
375
 
305
376
  ## Filter Dimension: Date [d2l-filter-dimension-date]
306
377
 
@@ -1,26 +1,63 @@
1
- import { css, LitElement } from 'lit';
2
- import { OverflowGroupMixin } from '../overflow-group/overflow-group-mixin.js';
1
+ import './filter.js';
2
+ import { css, html, LitElement } from 'lit';
3
+ import { OVERFLOW_CLASS, OverflowGroupMixin } from '../overflow-group/overflow-group-mixin.js';
4
+ import { RtlMixin } from '../../mixins/rtl-mixin.js';
5
+
6
+ function createFilterItem(node) {
7
+ const dimensionSets = node.querySelectorAll('d2l-filter-dimension-set');
8
+ const clones = Array.from(dimensionSets).map((set) => set.cloneNode(true));
9
+ return clones;
10
+ }
3
11
 
4
12
  /**
5
13
  * A component that can be used to display a group of filters that will be put into an overflow filter when they no longer fit on the first line of their container
6
14
  * @slot - d2l-filters to be added to the container
7
15
  */
8
- class FilterOverflowGroup extends OverflowGroupMixin(LitElement) {
16
+ class FilterOverflowGroup extends OverflowGroupMixin(RtlMixin(LitElement)) {
9
17
 
10
18
  static get styles() {
11
19
  return [super.styles, css`
12
20
  ::slotted(d2l-filter) {
13
21
  margin-right: 0.3rem;
14
22
  }
23
+ :host([dir="rtl"]) ::slotted(d2l-filter) {
24
+ margin-left: 0.3rem;
25
+ margin-right: 0;
26
+ }
15
27
  `];
16
28
  }
17
29
 
30
+ firstUpdated(changedProperties) {
31
+ super.firstUpdated(changedProperties);
32
+
33
+ this.addEventListener('d2l-filter-change', this._handleFilterChange);
34
+ }
35
+
18
36
  convertToOverflowItem(node) {
19
37
  const tagName = node.tagName.toLowerCase();
20
38
  if (tagName !== 'd2l-filter') console.warn(`d2l-filter-overflow-group: ${tagName} is invalid in this group. This group should only contain d2l-filter direct child elements.`);
39
+ else return createFilterItem(node);
40
+ }
41
+
42
+ getOverflowContainer(overflowItems) {
43
+ return html`
44
+ <d2l-filter class="${OVERFLOW_CLASS}" @d2l-filter-change="${this._handleFilterChange}">
45
+ ${overflowItems}
46
+ </d2l-filter>
47
+ `;
21
48
  }
22
49
 
23
- getOverflowContainer() {
50
+ _handleFilterChange(e) {
51
+ const target = (e.target.classList && e.target.classList.contains(OVERFLOW_CLASS)) ? this : e.target;
52
+ e.detail.dimensions.forEach((dimension) => {
53
+ const filterSet = target.querySelector(`d2l-filter-dimension-set[key=${dimension.dimensionKey}`);
54
+ if (!filterSet) return;
55
+ dimension.changes.forEach((change) => {
56
+ const filterSetValue = filterSet.querySelector(`d2l-filter-dimension-set-value[key=${change.valueKey}]`);
57
+ if (!filterSetValue) return;
58
+ filterSetValue.selected = change.selected;
59
+ });
60
+ });
24
61
  }
25
62
 
26
63
  }
@@ -2,6 +2,7 @@ import { css, html, nothing } from 'lit';
2
2
  import { LocalizeCoreElement } from '../../helpers/localize-core-element.js';
3
3
  import { offscreenStyles } from '../offscreen/offscreen.js';
4
4
  import ResizeObserver from 'resize-observer-polyfill/dist/ResizeObserver.es.js';
5
+ import { styleMap } from 'lit/directives/style-map.js';
5
6
 
6
7
  export const OVERFLOW_CLASS = 'd2l-overflow-container';
7
8
  export const OVERFLOW_MINI_CLASS = 'd2l-overflow-container-mini';
@@ -28,8 +29,7 @@ export const OverflowGroupMixin = superclass => class extends LocalizeCoreElemen
28
29
  static get properties() {
29
30
  return {
30
31
  /**
31
- * Use predefined classes on slot elements to set min and max slotted items to show
32
- * @type {boolean}
32
+ * @ignore
33
33
  */
34
34
  autoShow: {
35
35
  type: Boolean,
@@ -95,7 +95,9 @@ export const OverflowGroupMixin = superclass => class extends LocalizeCoreElemen
95
95
  this._handleResize = this._handleResize.bind(this);
96
96
  this._resizeObserver = new ResizeObserver((entries) => requestAnimationFrame(() => this._handleResize(entries)));
97
97
 
98
+ this._hasResized = false;
98
99
  this._isObserving = false;
100
+ this._itemHeight = 0;
99
101
  this._mini = this.openerType === OPENER_TYPE.ICON;
100
102
  this._overflowContainerHidden = false;
101
103
  this._slotItems = [];
@@ -129,8 +131,13 @@ export const OverflowGroupMixin = superclass => class extends LocalizeCoreElemen
129
131
  }
130
132
  });
131
133
 
134
+ const containerStyles = {
135
+ minHeight: this.autoShow ? 'none' : `${this._itemHeight}px`,
136
+ maxHeight: this.autoShow ? 'none' : `${this._itemHeight}px`
137
+ };
138
+
132
139
  return html`
133
- <div class="d2l-overflow-group-container">
140
+ <div class="d2l-overflow-group-container" style="${styleMap(containerStyles)}">
134
141
  <slot @slotchange="${this._handleSlotChange}"></slot>
135
142
  ${overflowContainer}
136
143
  </div>
@@ -277,12 +284,15 @@ export const OverflowGroupMixin = superclass => class extends LocalizeCoreElemen
277
284
 
278
285
  _getItemLayouts(filteredNodes) {
279
286
  const items = filteredNodes.map((node) => {
287
+ node.removeAttribute('data-is-chomped');
280
288
  const computedStyles = window.getComputedStyle(node);
289
+ const itemHidden = computedStyles.display === 'none';
290
+ this._itemHeight = !itemHidden ? Math.max(this._itemHeight, Math.ceil(parseFloat(computedStyles.height))) : this._itemHeight;
281
291
 
282
292
  return {
283
293
  type: node.tagName.toLowerCase(),
284
294
  isChomped: false,
285
- isHidden: computedStyles.display === 'none',
295
+ isHidden: itemHidden,
286
296
  width: Math.ceil(parseFloat(computedStyles.width) || 0)
287
297
  + parseInt(computedStyles.marginRight) || 0
288
298
  + parseInt(computedStyles.marginLeft) || 0,
@@ -316,20 +326,44 @@ export const OverflowGroupMixin = superclass => class extends LocalizeCoreElemen
316
326
  if (!mutations || mutations.length === 0) return;
317
327
  if (this._updateOverflowItemsRequested) return;
318
328
 
329
+ let isWidthModifyingMutation = false;
330
+ for (const mutation of mutations) {
331
+ if (mutation.attributeName
332
+ && (mutation.attributeName === 'selected' || mutation.attributeName === 'text')
333
+ ) {
334
+ isWidthModifyingMutation = true;
335
+ break;
336
+ }
337
+ }
338
+
319
339
  this._updateOverflowItemsRequested = true;
320
340
  setTimeout(() => {
321
- this._overflowItems = this._slotItems.map(node => this.convertToOverflowItem(node));
341
+ this._overflowItems = this._slotItems.map((node) => this.convertToOverflowItem(node));
342
+
343
+ // when certain attributes change the corresponding item width can also change and so we need to re-get the layouts and chomp
344
+ if (isWidthModifyingMutation) {
345
+ this._itemLayouts = this._getItemLayouts(this._slotItems);
346
+ this._chomp();
347
+ }
322
348
  this._updateOverflowItemsRequested = false;
323
349
  this.requestUpdate();
324
350
  }, 0);
325
351
  }
326
352
 
327
- _handleResize(entries) {
353
+ async _handleResize(entries) {
354
+ await (document.fonts ? document.fonts.ready : Promise.resolve()); // computed widths can be incorrect if we don't wait for fonts to load
328
355
  this._availableWidth = Math.ceil(entries[0].contentRect.width);
329
- this._chomp();
356
+
357
+ if (!this._hasResized) {
358
+ this._hasResized = true;
359
+ await this._handleSlotChange();
360
+ } else {
361
+ this._chomp();
362
+ }
330
363
  }
331
364
 
332
365
  _handleSlotChange() {
366
+ if (!this._hasResized) return;
333
367
  requestAnimationFrame(async() => {
334
368
  await this._getItems();
335
369
 
@@ -56,6 +56,7 @@ function createMenuItemSeparator() {
56
56
  * A component that can be used to display a set of buttons, links or menus that will be put into a dropdown menu when they no longer fit on the first line of their container
57
57
  * @slot - Buttons, dropdown buttons, links or other items to be added to the container
58
58
  * @attr {'default'|'icon'} [opener-type="default"] - Set the opener type to 'icon' for a `...` menu icon instead of `More actions` text
59
+ * @attr {boolean} auto-show - Use predefined classes on slot elements to set min and max buttons to show
59
60
  */
60
61
  class OverflowGroup extends OverflowGroupMixin(RtlMixin(LitElement)) {
61
62
 
@@ -3364,12 +3364,6 @@
3364
3364
  "path": "./components/filter/filter-overflow-group.js",
3365
3365
  "description": "A component that can be used to display a group of filters that will be put into an overflow filter when they no longer fit on the first line of their container",
3366
3366
  "attributes": [
3367
- {
3368
- "name": "auto-show",
3369
- "description": "Use predefined classes on slot elements to set min and max slotted items to show",
3370
- "type": "boolean",
3371
- "default": "false"
3372
- },
3373
3367
  {
3374
3368
  "name": "max-to-show",
3375
3369
  "description": "maximum amount of slotted items to show",
@@ -3386,8 +3380,6 @@
3386
3380
  "properties": [
3387
3381
  {
3388
3382
  "name": "autoShow",
3389
- "attribute": "auto-show",
3390
- "description": "Use predefined classes on slot elements to set min and max slotted items to show",
3391
3383
  "type": "boolean",
3392
3384
  "default": "false"
3393
3385
  },
@@ -8831,12 +8823,6 @@
8831
8823
  "type": "'default'|'subtle'",
8832
8824
  "default": "\"\\\"default\\\"\""
8833
8825
  },
8834
- {
8835
- "name": "auto-show",
8836
- "description": "Use predefined classes on slot elements to set min and max slotted items to show",
8837
- "type": "boolean",
8838
- "default": "false"
8839
- },
8840
8826
  {
8841
8827
  "name": "max-to-show",
8842
8828
  "description": "maximum amount of slotted items to show",
@@ -8854,6 +8840,11 @@
8854
8840
  "description": "Set the opener type to 'icon' for a `...` menu icon instead of `More actions` text",
8855
8841
  "type": "'default'|'icon'",
8856
8842
  "default": "\"default\""
8843
+ },
8844
+ {
8845
+ "name": "auto-show",
8846
+ "description": "Use predefined classes on slot elements to set min and max buttons to show",
8847
+ "type": "boolean"
8857
8848
  }
8858
8849
  ],
8859
8850
  "properties": [
@@ -8866,8 +8857,6 @@
8866
8857
  },
8867
8858
  {
8868
8859
  "name": "autoShow",
8869
- "attribute": "auto-show",
8870
- "description": "Use predefined classes on slot elements to set min and max slotted items to show",
8871
8860
  "type": "boolean",
8872
8861
  "default": "false"
8873
8862
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brightspace-ui/core",
3
- "version": "2.41.0",
3
+ "version": "2.42.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",