@brightspace-ui/core 2.59.3 → 2.60.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.
@@ -155,7 +155,7 @@ export const FormElementMixin = superclass => class extends LocalizeCoreElement(
155
155
  return this.localize('components.form-element.defaultError', { label });
156
156
  }
157
157
 
158
- /** @ignore */
158
+ /** @ignore Note: this may be our custom FormElementValidityState or native ValidityState depending on the source. */
159
159
  get validity() {
160
160
  return this._validity;
161
161
  }
@@ -306,6 +306,15 @@ class InputNumber extends FocusMixin(LabelledMixin(SkeletonMixin(FormElementMixi
306
306
  return super.validationMessage;
307
307
  }
308
308
 
309
+ /** @ignore */
310
+ get validity() {
311
+ const elem = this.shadowRoot && this.shadowRoot.querySelector('d2l-input-text');
312
+ if (elem && !elem.validity.valid) {
313
+ return elem.validity;
314
+ }
315
+ return super.validity;
316
+ }
317
+
309
318
  firstUpdated(changedProperties) {
310
319
  super.firstUpdated(changedProperties);
311
320
  this.addEventListener('d2l-localize-resources-change', () => {
@@ -331,6 +340,7 @@ class InputNumber extends FocusMixin(LabelledMixin(SkeletonMixin(FormElementMixi
331
340
  ?hide-invalid-icon="${this.hideInvalidIcon}"
332
341
  id="${this._inputId}"
333
342
  input-width="${this.inputWidth}"
343
+ @invalid-change="${this._handleInvalidChange}"
334
344
  label="${ifDefined(this.label)}"
335
345
  ?label-hidden="${this.labelHidden || this.labelledBy}"
336
346
  .labelRequired="${false}"
@@ -464,6 +474,10 @@ class InputNumber extends FocusMixin(LabelledMixin(SkeletonMixin(FormElementMixi
464
474
  }
465
475
  }
466
476
 
477
+ _handleInvalidChange() {
478
+ this.requestValidate(true);
479
+ }
480
+
467
481
  _handleKeyPress(e) {
468
482
  // NOTE: keypress event is deprecated, but keydown fires for ALL keys (including Shift/Tab/etc.)
469
483
  // which makes it hard to selectively supress "not numbers". The "beforeinput" event is
@@ -307,7 +307,7 @@ class InputText extends FocusMixin(LabelledMixin(FormElementMixin(SkeletonMixin(
307
307
  /** @ignore */
308
308
  get validity() {
309
309
  const elem = this.shadowRoot && this.shadowRoot.querySelector('.d2l-input');
310
- if (!elem.validity.valid) {
310
+ if (elem && !elem.validity.valid) {
311
311
  return elem.validity;
312
312
  }
313
313
  return super.validity;
@@ -1,119 +1,35 @@
1
- import '../overflow-group/overflow-group.js';
2
- import '../selection/selection-select-all.js';
3
- import '../selection/selection-select-all-pages.js';
4
- import '../selection/selection-summary.js';
5
- import { css, html, LitElement } from 'lit';
6
- import { classMap } from 'lit/directives/class-map.js';
1
+ import { css } from 'lit';
7
2
  import { findComposedAncestor } from '../../helpers/dom.js';
8
- import { LocalizeCoreElement } from '../../helpers/localize-core-element.js';
9
- import { RtlMixin } from '../../mixins/rtl-mixin.js';
3
+ import { SelectionHeader } from '../selection/selection-header.js';
10
4
 
11
5
  /**
12
6
  * A header for list components containing select-all, etc.
13
7
  * @slot - Responsive container using `d2l-overflow-group` for `d2l-selection-action` elements
14
8
  */
15
- class ListHeader extends RtlMixin(LocalizeCoreElement(LitElement)) {
16
-
9
+ class ListHeader extends SelectionHeader {
17
10
  static get properties() {
18
11
  return {
19
- /**
20
- * Whether to render select-all and selection summary
21
- * @type {boolean}
22
- */
23
- noSelection: { type: Boolean, attribute: 'no-selection' },
24
- /**
25
- * Disables sticky positioning for the header
26
- * @type {boolean}
27
- */
28
- noSticky: { type: Boolean, attribute: 'no-sticky' },
29
- /**
30
- * Whether all pages can be selected
31
- * @type {boolean}
32
- */
33
- selectAllPagesAllowed: { type: Boolean, attribute: 'select-all-pages-allowed' },
34
- _hasActions: { state: true },
35
- _scrolled: { type: Boolean, reflect: true }
12
+ _extendSeparator: { state: true }
36
13
  };
37
14
  }
38
15
 
39
16
  static get styles() {
40
- return css`
17
+ return [super.styles, css`
41
18
  :host {
42
- background-color: var(--d2l-list-header-background-color, white);
43
- display: block;
44
- position: sticky;
45
- top: 0;
19
+ --d2l-selection-header-background-color: var(--d2l-list-header-background-color);
46
20
  z-index: 6; /* must be greater than d2l-list-item-active-border */
47
21
  }
48
22
  :host([no-sticky]) {
49
- background-color: transparent;
50
- position: static;
51
23
  z-index: auto;
52
24
  }
53
- .d2l-list-header-shadow {
54
- transition: box-shadow 200ms ease-out;
55
- }
56
- :host([_scrolled]) .d2l-list-header-shadow {
57
- background-color: var(--d2l-list-header-background-color, white);
58
- bottom: -4px;
59
- box-shadow: 0 8px 12px -9px rgba(0, 0, 0, 0.3);
60
- clip: rect(30px, auto, 200px, auto);
61
- height: 40px;
62
- position: absolute;
63
- width: 100%;
64
- z-index: -1;
65
- }
66
- :host([hidden]) {
67
- display: none;
68
- }
69
- .d2l-list-header-container {
70
- align-items: center;
71
- display: flex;
72
- margin-bottom: 6px;
73
- margin-top: 6px;
74
- min-height: 54px;
75
- }
76
- .d2l-list-header-container-slim {
77
- min-height: 36px;
78
- }
79
25
  .d2l-list-header-extend-separator {
80
26
  padding: 0 0.9rem;
81
27
  }
82
- d2l-selection-select-all {
83
- flex: none;
84
- }
85
- d2l-selection-summary {
86
- flex: none;
87
- margin-left: 0.9rem;
88
- }
89
- :host([dir="rtl"]) d2l-selection-summary {
90
- margin-left: 0;
91
- margin-right: 0.9rem;
92
- }
93
- d2l-selection-select-all-pages {
94
- flex: none;
95
- margin-left: 0.45rem;
96
- }
97
- :host([dir="rtl"]) d2l-selection-select-all-pages {
98
- margin-left: 0;
99
- margin-right: 0.45rem;
100
- }
101
- .d2l-list-header-actions {
102
- --d2l-overflow-group-justify-content: flex-end;
103
- flex: auto;
104
- text-align: right;
105
- }
106
- :host([dir="rtl"]) .d2l-list-header-actions {
107
- text-align: left;
108
- }
109
- `;
28
+ `];
110
29
  }
111
30
 
112
31
  constructor() {
113
32
  super();
114
- this.noSelection = false;
115
- this.noSticky = false;
116
- this.selectAllPagesAllowed = false;
117
33
  this._extendSeparator = false;
118
34
  }
119
35
 
@@ -121,40 +37,15 @@ class ListHeader extends RtlMixin(LocalizeCoreElement(LitElement)) {
121
37
  super.connectedCallback();
122
38
 
123
39
  const parent = findComposedAncestor(this.parentNode, node => node && node.tagName === 'D2L-LIST');
124
- if (!parent) return;
125
-
126
- this._extendSeparator = parent.hasAttribute('extend-separators');
40
+ if (parent) this._extendSeparator = parent.hasAttribute('extend-separators');
127
41
  }
128
42
 
129
- render() {
130
- const classes = {
131
- 'd2l-list-header-container': true,
132
- 'd2l-list-header-container-slim': (!this._hasActions && !this.selectAllPagesAllowed),
43
+ _getSelectionHeaderContainerClasses() {
44
+ return {
45
+ ...super._getSelectionHeaderContainerClasses(),
133
46
  'd2l-list-header-extend-separator': this._extendSeparator
134
47
  };
135
- return html`
136
- <div class="${classMap(classes)}">
137
- ${this.noSelection ? null : html`
138
- <d2l-selection-select-all></d2l-selection-select-all>
139
- <d2l-selection-summary
140
- aria-hidden="true"
141
- class="d2l-list-header-summary"
142
- no-selection-text="${this.localize('components.selection.select-all')}">
143
- </d2l-selection-summary>
144
- ${this.selectAllPagesAllowed ? html`<d2l-selection-select-all-pages></d2l-selection-select-all-pages>` : null}
145
- `}
146
- <div class="d2l-list-header-actions">
147
- <d2l-overflow-group opener-type="icon"><slot @slotchange="${this._handleSlotChange}"></slot></d2l-overflow-group>
148
- </div>
149
- </div>
150
- ${!this.noSticky ? html`<div class="d2l-list-header-shadow"></div>` : null}
151
- `;
152
- }
153
-
154
- _handleSlotChange(e) {
155
- this._hasActions = (e.target.assignedNodes({ flatten: true }).filter(node => node.nodeType === Node.ELEMENT_NODE).length > 0);
156
48
  }
157
-
158
49
  }
159
50
 
160
51
  customElements.define('d2l-list-header', ListHeader);
@@ -106,25 +106,11 @@ class List extends PageableMixin(SelectionMixin(LitElement)) {
106
106
 
107
107
  });
108
108
 
109
- if (typeof(IntersectionObserver) === 'function') {
110
- this._intersectionObserver = new IntersectionObserver(entries => {
111
- const slot = this.shadowRoot.querySelector('slot[name="header"]');
112
- const header = slot.assignedNodes({ flatten: true }).find(node => {
113
- return node.nodeType === Node.ELEMENT_NODE && node.tagName === 'D2L-LIST-HEADER';
114
- });
115
- if (!header) return;
116
- const entry = entries[0];
117
- header._scrolled = !entry.isIntersecting;
118
- });
119
- this._intersectionObserver.observe(this.shadowRoot.querySelector('.d2l-list-top-sentinel'));
120
- }
121
-
122
109
  }
123
110
 
124
111
  render() {
125
112
  const role = !this.grid ? 'list' : 'application';
126
113
  return html`
127
- <div class="d2l-list-top-sentinel"></div>
128
114
  <div role="${role}" class="d2l-list-container">
129
115
  <slot name="header"></slot>
130
116
  <slot @keydown="${this._handleKeyDown}" @slotchange="${this._handleSlotChange}"></slot>
@@ -36,6 +36,7 @@ The `SelectionMixin` defines the `selection-single` attribute that consumers can
36
36
  </script>
37
37
  <script type="module">
38
38
  import '@brightspace-ui/core/components/selection/selection-action.js';
39
+ import '@brightspace-ui/core/components/selection/selection-header.js';
39
40
  import '@brightspace-ui/core/components/selection/selection-input.js';
40
41
  import '@brightspace-ui/core/components/selection/selection-select-all.js';
41
42
  import '@brightspace-ui/core/components/selection/selection-summary.js';
@@ -50,8 +51,6 @@ The `SelectionMixin` defines the `selection-single` attribute that consumers can
50
51
  }
51
52
  li {
52
53
  list-style-type: none;
53
- }
54
- li, .d2l-selection-header, .d2l-selection-header-wrapper {
55
54
  align-items: center;
56
55
  display: flex;
57
56
  }
@@ -62,26 +61,13 @@ The `SelectionMixin` defines the `selection-single` attribute that consumers can
62
61
  margin-left: 10px;
63
62
  margin-right: 0;
64
63
  }
65
- .d2l-selection-header-wrapper {
66
- flex-wrap: wrap;
67
- }
68
- .d2l-selection-header {
69
- flex: auto;
70
- }
71
- d2l-selection-summary {
72
- flex: none;
73
- }
74
64
  </style>
75
65
  <!-- docs: end hidden content -->
76
66
  <d2l-demo-selection>
77
- <div class="d2l-selection-header-wrapper">
78
- <div class="d2l-selection-header">
79
- <d2l-selection-select-all></d2l-selection-select-all>
80
- <d2l-selection-action text="Bookmark" icon="tier1:bookmark-hollow" requires-selection></d2l-selection-action>
81
- <d2l-selection-action text="Settings" icon="tier1:gear"></d2l-selection-action>
82
- </div>
83
- <d2l-selection-summary></d2l-selection-summary>
84
- </div>
67
+ <d2l-selection-header>
68
+ <d2l-selection-action text="Bookmark" icon="tier1:bookmark-hollow" requires-selection></d2l-selection-action>
69
+ <d2l-selection-action text="Settings" icon="tier1:gear"></d2l-selection-action>
70
+ </d2l-selection-header>
85
71
  <ul>
86
72
  <li><d2l-selection-input key="geo" label="Geography" selected></d2l-selection-input>Geography</li>
87
73
  <li><d2l-selection-input key="sci" label="Science"></d2l-selection-input>Science</li>
@@ -441,3 +427,58 @@ The `d2l-selection-summary` component may be placed inside the selection control
441
427
  | `no-selection-text` | String | Text to display if no items are selected. By default, the "0 selected" message is displayed. |
442
428
  | `selection-for` | String | Id of the corresponding `SelectionMixin` component, if not placed within it. |
443
429
  <!-- docs: end hidden content -->
430
+
431
+ ## Selection Header [d2l-selection-header]
432
+
433
+ The `d2l-selection-header` provides a standardized wrapper to display selection information and actions. It includes a select-all checkbox, summary, a slot for `d2l-selection-action`s, and overflow-group behaviour.
434
+
435
+ When using lists, use the list-specific `d2l-list-header` instead, which extends this component's behaviour.
436
+
437
+ <!-- docs: demo live name:d2l-selection-header autoSize:false size:medium -->
438
+ ```html
439
+ <script type="module">
440
+ import '@brightspace-ui/core/components/selection/selection-action.js';
441
+ import '@brightspace-ui/core/components/selection/selection-header.js';
442
+ import '@brightspace-ui/core/components/selection/demo/demo-selection.js';
443
+ </script>
444
+ <!-- docs: start hidden content -->
445
+ <style>
446
+ ul {
447
+ padding: 0;
448
+ }
449
+ li {
450
+ list-style-type: none;
451
+ align-items: center;
452
+ display: flex;
453
+ }
454
+ d2l-selection-input {
455
+ margin-right: 10px;
456
+ }
457
+ [dir="rtl"] d2l-selection-input {
458
+ margin-left: 10px;
459
+ margin-right: 0;
460
+ }
461
+ </style>
462
+ <!-- docs: end hidden content -->
463
+ <d2l-demo-selection>
464
+ <d2l-selection-header>
465
+ <d2l-selection-action text="Bookmark" icon="tier1:bookmark-hollow" requires-selection></d2l-selection-action>
466
+ <d2l-selection-action text="Settings" icon="tier1:gear"></d2l-selection-action>
467
+ </d2l-selection-header>
468
+ <ul>
469
+ <li><d2l-selection-input key="geo" label="Geography" selected></d2l-selection-input>Geography</li>
470
+ <li><d2l-selection-input key="sci" label="Science"></d2l-selection-input>Science</li>
471
+ <li><d2l-selection-input key="mth" label="Math"></d2l-selection-input>Math</li>
472
+ </ul>
473
+ </d2l-demo-selection>
474
+ ```
475
+
476
+ <!-- docs: start hidden content -->
477
+ ### Properties
478
+
479
+ | Property | Type | Description |
480
+ |---|---|---|
481
+ | `no-selection` | Boolean | Whether to render select-all and selection summary |
482
+ | `no-sticky` | Boolean | Disables sticky positioning for the header |
483
+ | `select-all-pages-allowed` | Boolean | Whether all pages can be selected |
484
+ <!-- docs: end hidden content -->
@@ -11,6 +11,7 @@
11
11
  import '../selection-action.js';
12
12
  import '../selection-action-dropdown.js';
13
13
  import '../selection-action-menu-item.js';
14
+ import '../selection-header.js';
14
15
  import '../selection-input.js';
15
16
  import '../selection-select-all.js';
16
17
  import '../selection-summary.js';
@@ -56,22 +57,19 @@
56
57
  <d2l-demo-snippet>
57
58
  <template>
58
59
  <d2l-demo-selection>
59
- <div class="d2l-selection-header">
60
- <div style="flex: auto;">
61
- <d2l-selection-select-all></d2l-selection-select-all>
62
- <d2l-selection-action text="Bookmark" icon="tier1:bookmark-hollow" requires-selection></d2l-selection-action>
63
- <d2l-selection-action text="Settings" icon="tier1:gear"></d2l-selection-action>
64
- <d2l-selection-action-dropdown text="Actions 1" requires-selection>
65
- <d2l-dropdown-menu>
66
- <d2l-menu label="Actions">
67
- <d2l-selection-action-menu-item text="Action 1"></d2l-selection-action-menu-item>
68
- <d2l-selection-action-menu-item text="Action 2"></d2l-selection-action-menu-item>
69
- </d2l-menu>
70
- </d2l-dropdown-menu>
71
- </d2l-selection-action-dropdown>
72
- </div>
73
- <d2l-selection-summary style="flex: none;"></d2l-selection-summary>
74
- </div>
60
+ <d2l-selection-header>
61
+ <d2l-selection-action text="Bookmark" icon="tier1:bookmark-hollow" requires-selection></d2l-selection-action>
62
+ <d2l-selection-action text="Settings" icon="tier1:gear"></d2l-selection-action>
63
+ <d2l-selection-action-dropdown text="Actions 1" requires-selection>
64
+ <d2l-dropdown-menu>
65
+ <d2l-menu label="Actions">
66
+ <d2l-selection-action-menu-item text="Action 1"></d2l-selection-action-menu-item>
67
+ <d2l-selection-action-menu-item text="Action 2"></d2l-selection-action-menu-item>
68
+ </d2l-menu>
69
+ </d2l-dropdown-menu>
70
+ </d2l-selection-action-dropdown>
71
+ </d2l-selection-header>
72
+
75
73
  <ul>
76
74
  <li><d2l-selection-input key="geo" label="Geography" selected></d2l-selection-input>Geography</li>
77
75
  <li><d2l-selection-input key="sci" label="Science"></d2l-selection-input>Science</li>
@@ -86,13 +84,31 @@
86
84
  <d2l-demo-snippet>
87
85
  <template>
88
86
  <d2l-demo-selection selection-single>
89
- <div class="d2l-selection-header">
90
- <div style="flex: auto;">
91
- <d2l-selection-action text="Bookmark" icon="tier1:bookmark-hollow" requires-selection></d2l-selection-action>
92
- <d2l-selection-action text="Settings" icon="tier1:gear"></d2l-selection-action>
93
- </div>
94
- <d2l-selection-summary style="flex: none;"></d2l-selection-summary>
95
- </div>
87
+ <d2l-selection-header>
88
+ <d2l-selection-action text="Bookmark" icon="tier1:bookmark-hollow" requires-selection></d2l-selection-action>
89
+ <d2l-selection-action text="Settings" icon="tier1:gear"></d2l-selection-action>
90
+ </d2l-selection-header>
91
+
92
+ <ul>
93
+ <li><d2l-selection-input key="geo" label="Geography"></d2l-selection-input>Geography</li>
94
+ <li><d2l-selection-input key="sci" label="Science"></d2l-selection-input>Science</li>
95
+ <li><d2l-selection-input key="mth" label="Math"></d2l-selection-input>Math</li>
96
+ </ul>
97
+ </d2l-demo-selection>
98
+ </template>
99
+ </d2l-demo-snippet>
100
+
101
+
102
+ <h2>None-sticky selection header</h2>
103
+
104
+ <d2l-demo-snippet>
105
+ <template>
106
+ <d2l-demo-selection>
107
+ <d2l-selection-header no-sticky>
108
+ <d2l-selection-action text="Bookmark" icon="tier1:bookmark-hollow" requires-selection></d2l-selection-action>
109
+ <d2l-selection-action text="Settings" icon="tier1:gear"></d2l-selection-action>
110
+ </d2l-selection-header>
111
+
96
112
  <ul>
97
113
  <li><d2l-selection-input key="geo" label="Geography"></d2l-selection-input>Geography</li>
98
114
  <li><d2l-selection-input key="sci" label="Science"></d2l-selection-input>Science</li>
@@ -109,11 +125,10 @@
109
125
  <div class="d2l-selection-collections">
110
126
  <div class="d2l-selection-collection">
111
127
  Pick Your Toppings
112
- <div class="d2l-selection-header">
113
- <d2l-selection-select-all selection-for="collection-1"></d2l-selection-select-all>
114
- <d2l-selection-action selection-for="collection-1" text="Add Note" icon="tier1:add-message" style="flex: 1; padding: 0 0.5rem;"></d2l-selection-action>
115
- <d2l-selection-summary selection-for="collection-1"></d2l-selection-summary>
116
- </div>
128
+ <d2l-selection-header selection-for="collection-1">
129
+ <d2l-selection-action selection-for="collection-1" text="Add Note" icon="tier1:add-message"></d2l-selection-action>
130
+ </d2l-selection-header>
131
+
117
132
  <d2l-demo-selection id="collection-1">
118
133
  <ul>
119
134
  <li><d2l-selection-input key="let" label="Lettuce" selected></d2l-selection-input>Lettuce</li>
@@ -126,11 +141,9 @@
126
141
 
127
142
  <div class="d2l-selection-collection">
128
143
  Pick Your Bread
129
- <div class="d2l-selection-header">
130
- <d2l-selection-select-all selection-for="collection-2"></d2l-selection-select-all>
144
+ <d2l-selection-header selection-for="collection-2">
131
145
  <d2l-selection-action selection-for="collection-2" text="Add Note" icon="tier1:add-message"></d2l-selection-action>
132
- <d2l-selection-summary selection-for="collection-2"></d2l-selection-summary>
133
- </div>
146
+ </d2l-selection-header>
134
147
  <d2l-demo-selection id="collection-2" selection-single>
135
148
  <ul>
136
149
  <li><d2l-selection-input key="it" label="Italian"></d2l-selection-input>Italian</li>
@@ -0,0 +1,192 @@
1
+ import '../overflow-group/overflow-group.js';
2
+ import './selection-select-all.js';
3
+ import './selection-select-all-pages.js';
4
+ import './selection-summary.js';
5
+ import { css, html, LitElement } from 'lit';
6
+ import { classMap } from 'lit/directives/class-map.js';
7
+ import { LocalizeCoreElement } from '../../helpers/localize-core-element.js';
8
+ import { RtlMixin } from '../../mixins/rtl-mixin.js';
9
+ import { SelectionObserverMixin } from './selection-observer-mixin.js';
10
+
11
+ /**
12
+ * A header for selection components (e.g. list, table-wrapper) containing select-all, etc.
13
+ * @slot - Responsive container using `d2l-overflow-group` for `d2l-selection-action` elements
14
+ */
15
+ export class SelectionHeader extends SelectionObserverMixin(RtlMixin(LocalizeCoreElement(LitElement))) {
16
+
17
+ static get properties() {
18
+ return {
19
+ /**
20
+ * Whether to render select-all and selection summary
21
+ * @type {boolean}
22
+ */
23
+ noSelection: { type: Boolean, attribute: 'no-selection' },
24
+ /**
25
+ * Disables sticky positioning for the header
26
+ * @type {boolean}
27
+ */
28
+ noSticky: { type: Boolean, attribute: 'no-sticky' },
29
+ /**
30
+ * Whether all pages can be selected
31
+ * @type {boolean}
32
+ */
33
+ selectAllPagesAllowed: { type: Boolean, attribute: 'select-all-pages-allowed' },
34
+ _hasActions: { state: true },
35
+ _scrolled: { type: Boolean, reflect: true }
36
+ };
37
+ }
38
+
39
+ static get styles() {
40
+ return css`
41
+ :host {
42
+ background-color: var(--d2l-selection-header-background-color, white);
43
+ display: block;
44
+ position: sticky;
45
+ top: 0;
46
+ }
47
+ :host([no-sticky]) {
48
+ background-color: transparent;
49
+ position: static;
50
+ }
51
+ @media (prefers-reduced-motion: no-preference) {
52
+ .d2l-selection-header-shadow {
53
+ transition: box-shadow 200ms ease-out;
54
+ }
55
+ }
56
+ :host([_scrolled]) .d2l-selection-header-shadow {
57
+ background-color: var(--d2l-selection-header-background-color, white);
58
+ bottom: -4px;
59
+ box-shadow: 0 8px 12px -9px rgba(0, 0, 0, 0.3);
60
+ clip: rect(30px, auto, 200px, auto);
61
+ height: 40px;
62
+ position: absolute;
63
+ width: 100%;
64
+ z-index: -1;
65
+ }
66
+ :host([hidden]) {
67
+ display: none;
68
+ }
69
+ .d2l-selection-header-container {
70
+ align-items: center;
71
+ display: flex;
72
+ margin-bottom: 6px;
73
+ margin-top: 6px;
74
+ min-height: 54px;
75
+ }
76
+ .d2l-selection-header-container-slim {
77
+ min-height: 36px;
78
+ }
79
+ d2l-selection-select-all {
80
+ flex: none;
81
+ }
82
+ d2l-selection-summary {
83
+ flex: none;
84
+ margin-left: 0.9rem;
85
+ }
86
+ :host([dir="rtl"]) d2l-selection-summary {
87
+ margin-left: 0;
88
+ margin-right: 0.9rem;
89
+ }
90
+ d2l-selection-select-all-pages {
91
+ flex: none;
92
+ margin-left: 0.45rem;
93
+ }
94
+ :host([dir="rtl"]) d2l-selection-select-all-pages {
95
+ margin-left: 0;
96
+ margin-right: 0.45rem;
97
+ }
98
+ .d2l-selection-header-actions {
99
+ --d2l-overflow-group-justify-content: flex-end;
100
+ flex: auto;
101
+ text-align: right;
102
+ }
103
+ :host([dir="rtl"]) .d2l-selection-header-actions {
104
+ text-align: left;
105
+ }
106
+ .d2l-sticky-edge {
107
+ left: 0;
108
+ position: absolute;
109
+ right: 0;
110
+ top: -1px;
111
+ }
112
+ `;
113
+ }
114
+
115
+ constructor() {
116
+ super();
117
+ this.noSelection = false;
118
+ this.noSticky = false;
119
+ this.selectAllPagesAllowed = false;
120
+ this._scrolled = false;
121
+ }
122
+
123
+ disconnectedCallback() {
124
+ super.disconnectedCallback();
125
+ this._stickyObserverDisconnect();
126
+ }
127
+
128
+ render() {
129
+ const classes = this._getSelectionHeaderContainerClasses();
130
+ return html`
131
+ <div class="d2l-sticky-edge"></div>
132
+ <div class="${classMap(classes)}">
133
+ ${this.noSelection ? null : html`
134
+ <d2l-selection-select-all></d2l-selection-select-all>
135
+ <d2l-selection-summary
136
+ aria-hidden="true"
137
+ no-selection-text="${this.localize('components.selection.select-all')}"
138
+ >
139
+ </d2l-selection-summary>
140
+ ${this.selectAllPagesAllowed ? html`<d2l-selection-select-all-pages></d2l-selection-select-all-pages>` : null}
141
+ `}
142
+ <div class="d2l-selection-header-actions">
143
+ <d2l-overflow-group opener-type="icon"><slot @slotchange="${this._handleSlotChange}"></slot></d2l-overflow-group>
144
+ </div>
145
+ </div>
146
+ ${!this.noSticky ? html`<div class="d2l-selection-header-shadow"></div>` : null}
147
+ `;
148
+ }
149
+
150
+ updated(changedProperties) {
151
+ super.updated(changedProperties);
152
+
153
+ if (changedProperties.has('noSticky')) {
154
+ this._stickyObserverUpdate();
155
+ }
156
+ }
157
+
158
+ _getSelectionHeaderContainerClasses() {
159
+ return {
160
+ 'd2l-selection-header-container': true,
161
+ 'd2l-selection-header-container-slim': (!this._hasActions && !this.selectAllPagesAllowed)
162
+ };
163
+ }
164
+
165
+ _handleSlotChange(e) {
166
+ this._hasActions = (e.target.assignedNodes({ flatten: true }).filter(node => node.nodeType === Node.ELEMENT_NODE).length > 0);
167
+ }
168
+
169
+ _stickyObserverDisconnect() {
170
+ if (this._stickyIntersectionObserver) {
171
+ this._stickyIntersectionObserver.disconnect();
172
+ this._stickyIntersectionObserver = undefined;
173
+ this._scrolled = false;
174
+ }
175
+ }
176
+
177
+ _stickyObserverUpdate() {
178
+ this._stickyObserverDisconnect();
179
+
180
+ if (!this.noSticky && typeof(IntersectionObserver) === 'function') {
181
+ this._stickyIntersectionObserver = new IntersectionObserver(([e]) => {
182
+ this._scrolled = !e.isIntersecting;
183
+ });
184
+
185
+ const target = this.shadowRoot.querySelector('.d2l-sticky-edge');
186
+ this._stickyIntersectionObserver.observe(target);
187
+ }
188
+ }
189
+
190
+ }
191
+
192
+ customElements.define('d2l-selection-header', SelectionHeader);
@@ -18,6 +18,7 @@ Tables are used to display tabular data in rows and columns. They can allow user
18
18
  * There are more than just a few dimensions
19
19
  * The dimensions need to be sortable
20
20
  * The dimensions need to be easily compared across rows (ie- scannable)
21
+ * Specify [column and row headings](https://www.w3.org/WAI/tutorials/tables/) so data is meaningful to screen reader users
21
22
  <!-- docs: end dos -->
22
23
 
23
24
  <!-- docs: start donts -->
@@ -7337,6 +7337,11 @@
7337
7337
  "description": "Whether all pages can be selected",
7338
7338
  "type": "boolean",
7339
7339
  "default": "false"
7340
+ },
7341
+ {
7342
+ "name": "selection-for",
7343
+ "description": "Id of the `SelectionMixin` component this component wants to observe (if not located within that component)",
7344
+ "type": "string"
7340
7345
  }
7341
7346
  ],
7342
7347
  "properties": [
@@ -7361,11 +7366,25 @@
7361
7366
  "type": "boolean",
7362
7367
  "default": "false"
7363
7368
  },
7369
+ {
7370
+ "name": "selectionFor",
7371
+ "attribute": "selection-for",
7372
+ "description": "Id of the `SelectionMixin` component this component wants to observe (if not located within that component)",
7373
+ "type": "string"
7374
+ },
7375
+ {
7376
+ "name": "selectionInfo"
7377
+ },
7364
7378
  {
7365
7379
  "name": "documentLocaleSettings",
7366
7380
  "default": "\"getDocumentLocaleSettings()\""
7367
7381
  }
7368
7382
  ],
7383
+ "events": [
7384
+ {
7385
+ "name": "d2l-selection-observer-subscribe"
7386
+ }
7387
+ ],
7369
7388
  "slots": [
7370
7389
  {
7371
7390
  "name": "",
@@ -9739,6 +9758,83 @@
9739
9758
  }
9740
9759
  ]
9741
9760
  },
9761
+ {
9762
+ "name": "d2l-selection-header",
9763
+ "path": "./components/selection/selection-header.js",
9764
+ "description": "A header for selection components (e.g. list, table-wrapper) containing select-all, etc.",
9765
+ "attributes": [
9766
+ {
9767
+ "name": "no-selection",
9768
+ "description": "Whether to render select-all and selection summary",
9769
+ "type": "boolean",
9770
+ "default": "false"
9771
+ },
9772
+ {
9773
+ "name": "no-sticky",
9774
+ "description": "Disables sticky positioning for the header",
9775
+ "type": "boolean",
9776
+ "default": "false"
9777
+ },
9778
+ {
9779
+ "name": "select-all-pages-allowed",
9780
+ "description": "Whether all pages can be selected",
9781
+ "type": "boolean",
9782
+ "default": "false"
9783
+ },
9784
+ {
9785
+ "name": "selection-for",
9786
+ "description": "Id of the `SelectionMixin` component this component wants to observe (if not located within that component)",
9787
+ "type": "string"
9788
+ }
9789
+ ],
9790
+ "properties": [
9791
+ {
9792
+ "name": "noSelection",
9793
+ "attribute": "no-selection",
9794
+ "description": "Whether to render select-all and selection summary",
9795
+ "type": "boolean",
9796
+ "default": "false"
9797
+ },
9798
+ {
9799
+ "name": "noSticky",
9800
+ "attribute": "no-sticky",
9801
+ "description": "Disables sticky positioning for the header",
9802
+ "type": "boolean",
9803
+ "default": "false"
9804
+ },
9805
+ {
9806
+ "name": "selectAllPagesAllowed",
9807
+ "attribute": "select-all-pages-allowed",
9808
+ "description": "Whether all pages can be selected",
9809
+ "type": "boolean",
9810
+ "default": "false"
9811
+ },
9812
+ {
9813
+ "name": "selectionFor",
9814
+ "attribute": "selection-for",
9815
+ "description": "Id of the `SelectionMixin` component this component wants to observe (if not located within that component)",
9816
+ "type": "string"
9817
+ },
9818
+ {
9819
+ "name": "selectionInfo"
9820
+ },
9821
+ {
9822
+ "name": "documentLocaleSettings",
9823
+ "default": "\"getDocumentLocaleSettings()\""
9824
+ }
9825
+ ],
9826
+ "events": [
9827
+ {
9828
+ "name": "d2l-selection-observer-subscribe"
9829
+ }
9830
+ ],
9831
+ "slots": [
9832
+ {
9833
+ "name": "",
9834
+ "description": "Responsive container using `d2l-overflow-group` for `d2l-selection-action` elements"
9835
+ }
9836
+ ]
9837
+ },
9742
9838
  {
9743
9839
  "name": "d2l-selection-input",
9744
9840
  "path": "./components/selection/selection-input.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brightspace-ui/core",
3
- "version": "2.59.3",
3
+ "version": "2.60.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",