@brightspace-ui/core 3.4.0 → 3.5.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.
@@ -1,4 +1,5 @@
1
1
  import '../table-col-sort-button.js';
2
+ import '../table-col-sort-button-item.js';
2
3
  import '../table-controls.js';
3
4
  import '../../button/button-icon.js';
4
5
  import '../../dropdown/dropdown-button-subtle.js';
@@ -24,13 +25,13 @@ const columns = ['Population', 'Size', 'Elevation'];
24
25
  const thText = ['Additional', 'Placeholder', 'Header', 'Row'];
25
26
 
26
27
  const data = () => [
27
- { name: 'Ottawa, Canada', city: 'Ottawa', country: 'Canada', data: { 'population': 994837, 'size': 2790, 'elevation': 70 }, selected: true },
28
- { name: 'Toronto, Canada', city: 'Toronto', country: 'Canada', data: { 'population': 2930000, 'size': 630, 'elevation': 76 }, selected: true },
29
- { name: 'Sydney, Australia', city: 'Sydney', country: 'Australia', data: { 'population': 5312000, 'size': 12368, 'elevation': 3 }, selected: false },
30
- { name: 'Cairo, Egypt', city: 'Cairo', country: 'Egypt', data: { 'population': 9540000, 'size': 3085, 'elevation': 23 }, selected: false },
31
- { name: 'Moscow, Russia', city: 'Moscow', country: 'Russia', data: { 'population': 12712305, 'size': 2511, 'elevation': 124 }, selected: false },
32
- { name: 'London, England', city: 'London', country: 'England', data: { 'population': 8982000, 'size': 1572, 'elevation': 11 }, selected: false },
33
- { name: 'Tokyo, Japan', city: 'Tokyo', country: 'Japan', data: { 'population': 13960000, 'size': 2194, 'elevation': 40 }, selected: false }
28
+ { name: 'Ottawa, Canada', data: { 'city': 'Ottawa', 'country': 'Canada', 'population': 994837, 'size': 2790, 'elevation': 70 }, selected: true },
29
+ { name: 'Toronto, Canada', data: { 'city': 'Toronto', 'country': 'Canada', 'population': 2930000, 'size': 630, 'elevation': 76 }, selected: true },
30
+ { name: 'Sydney, Australia', data: { 'city': 'Sydney', 'country': 'Australia', 'population': 5312000, 'size': 12368, 'elevation': 3 }, selected: false },
31
+ { name: 'Cairo, Egypt', data: { 'city': 'Cairo', 'country': 'Egypt', 'population': 9540000, 'size': 3085, 'elevation': 23 }, selected: false },
32
+ { name: 'Moscow, Russia', data: { 'city': 'Moscow', 'country': 'Russia', 'population': 12712305, 'size': 2511, 'elevation': 124 }, selected: false },
33
+ { name: 'London, England', data: { 'city': 'London', 'country': 'England', 'population': 8982000, 'size': 1572, 'elevation': 11 }, selected: false },
34
+ { name: 'Tokyo, Japan', data: { 'city': 'Tokyo', 'country': 'Japan', 'population': 13960000, 'size': 2194, 'elevation': 40 }, selected: false }
34
35
  ];
35
36
 
36
37
  const formatter = new Intl.NumberFormat('en-US');
@@ -100,7 +101,7 @@ class TestTable extends RtlMixin(DemoPassthroughMixin(TableWrapper, 'd2l-table-w
100
101
  <thead>
101
102
  <tr>
102
103
  <th scope="col" sticky><d2l-selection-select-all></d2l-selection-select-all></th>
103
- ${this._renderDoubleSortButton('City', 'Country')}
104
+ ${this._renderDoubleSortButton('Location')}
104
105
  ${columns.map(columnHeading => this._renderSortButton(columnHeading))}
105
106
  </tr>
106
107
  </thead>
@@ -145,7 +146,7 @@ class TestTable extends RtlMixin(DemoPassthroughMixin(TableWrapper, 'd2l-table-w
145
146
  _handlePagerLoadMore(e) {
146
147
  const startIndex = this._data.length + 1;
147
148
  for (let i = 0; i < e.target.pageSize; i++) {
148
- this._data.push({ name: `Country ${startIndex + i}`, data: { 'population': 26320000, 'size': 6340, 'elevation': 4 }, selected: false });
149
+ this._data.push({ name: `City ${startIndex + i}, Country ${startIndex + i}`, data: { 'city': `City ${startIndex + i}`, 'country': `Country ${startIndex + i}`, 'population': 26320000, 'size': 6340, 'elevation': 4 }, selected: false });
149
150
  }
150
151
  this.requestUpdate();
151
152
  e.detail.complete();
@@ -156,38 +157,40 @@ class TestTable extends RtlMixin(DemoPassthroughMixin(TableWrapper, 'd2l-table-w
156
157
  const desc = e.target.hasAttribute('desc');
157
158
  this._sortDesc = field === this._sortField ? !desc : false; // if sorting on same field then reverse, otherwise sort ascending
158
159
  this._sortField = field;
160
+ this._handleSortData();
161
+ }
162
+
163
+ _handleSortComplex(e) {
164
+ this._sortField = e.target?.getAttribute('data-field');
165
+ this._sortDesc = e.target?.hasAttribute('data-desc');
166
+ this._handleSortData();
167
+ }
159
168
 
169
+ _handleSortData() {
160
170
  this._data = this._data.sort((a, b) => {
161
- if (this._sortField === 'city' || this._sortField === 'country') {
162
- if (this._sortDesc) {
163
- if (a[this._sortField] > b[this._sortField]) return -1;
164
- if (a[this._sortField] < b[this._sortField]) return 1;
165
- } else {
166
- if (a[this._sortField] < b[this._sortField]) return -1;
167
- if (a[this._sortField] > b[this._sortField]) return 1;
168
- }
171
+ if (this._sortDesc) {
172
+ if (a.data[this._sortField] > b.data[this._sortField]) return -1;
173
+ if (a.data[this._sortField] < b.data[this._sortField]) return 1;
169
174
  } else {
170
- if (this._sortDesc) {
171
- return b.data[this._sortField] - a.data[this._sortField];
172
- }
173
- return a.data[this._sortField] - b.data[this._sortField];
175
+ if (a.data[this._sortField] < b.data[this._sortField]) return -1;
176
+ if (a.data[this._sortField] > b.data[this._sortField]) return 1;
174
177
  }
178
+ return 0;
175
179
  });
176
180
  }
177
181
 
178
- _renderDoubleSortButton(item1, item2) {
182
+ _renderDoubleSortButton(name) {
183
+ const noSort = this._sortField?.toLowerCase() !== 'city' && this._sortField?.toLowerCase() !== 'country';
179
184
  return html`
180
185
  <th scope="col">
181
186
  <d2l-table-col-sort-button
182
- @click="${this._handleSort}"
183
- source-type="words"
184
- ?desc="${this._sortDesc}"
185
- ?nosort="${this._sortField !== item1.toLowerCase()}">${item1}</d2l-table-col-sort-button>
186
- <d2l-table-col-sort-button
187
- @click="${this._handleSort}"
188
- source-type="words"
189
187
  ?desc="${this._sortDesc}"
190
- ?nosort="${this._sortField !== item2.toLowerCase()}">${item2}</d2l-table-col-sort-button>
188
+ ?nosort="${noSort}">${name}
189
+ <d2l-table-col-sort-button-item slot="items" text="City, A to Z" data-field="city" @d2l-table-col-sort-button-item-change="${this._handleSortComplex}" value="1"></d2l-table-col-sort-button-item>
190
+ <d2l-table-col-sort-button-item slot="items" text="City, Z to A" data-field="city" data-desc @d2l-table-col-sort-button-item-change="${this._handleSortComplex}" value="2"></d2l-table-col-sort-button-item>
191
+ <d2l-table-col-sort-button-item slot="items" text="Country, A to Z" data-field="country" @d2l-table-col-sort-button-item-change="${this._handleSortComplex}" value="3"></d2l-table-col-sort-button-item>
192
+ <d2l-table-col-sort-button-item slot="items" text="Country, Z to A" data-field="country" data-desc @d2l-table-col-sort-button-item-change="${this._handleSortComplex}" value="4"></d2l-table-col-sort-button-item>
193
+ </d2l-table-col-sort-button>
191
194
  </th>
192
195
  `;
193
196
  }
@@ -0,0 +1,34 @@
1
+ import '../icons/icon.js';
2
+ import { html, LitElement } from 'lit';
3
+ import { MenuItemRadioMixin } from '../menu/menu-item-radio-mixin.js';
4
+ import { menuItemSelectableStyles } from '../menu/menu-item-selectable-styles.js';
5
+ import { RtlMixin } from '../../mixins/rtl/rtl-mixin.js';
6
+
7
+ /**
8
+ * A radio menu item to be used within the d2l-table-col-sort-button component for a multi-faceted sort.
9
+ */
10
+ class TableColSortButtonItem extends RtlMixin(MenuItemRadioMixin(LitElement)) {
11
+
12
+ static get styles() {
13
+ return menuItemSelectableStyles;
14
+ }
15
+
16
+ firstUpdated() {
17
+ super.firstUpdated();
18
+ this.addEventListener('d2l-menu-item-change', this._onChangeOption);
19
+ }
20
+
21
+ render() {
22
+ return html`
23
+ <d2l-icon icon="tier1:check"></d2l-icon>
24
+ <div class="d2l-menu-item-text">${this.text}</div>
25
+ `;
26
+ }
27
+
28
+ _onChangeOption() {
29
+ /** Dispatched when the selected multi-faceted sort option changes */
30
+ this.dispatchEvent(new CustomEvent('d2l-table-col-sort-button-item-change', { bubbles: true, composed: true }));
31
+ }
32
+ }
33
+
34
+ customElements.define('d2l-table-col-sort-button-item', TableColSortButtonItem);
@@ -1,6 +1,10 @@
1
1
  import '../colors/colors.js';
2
+ import '../dropdown/dropdown.js';
3
+ import '../dropdown/dropdown-menu.js';
2
4
  import '../icons/icon.js';
5
+ import '../menu/menu.js';
3
6
  import { css, html, LitElement, unsafeCSS } from 'lit';
7
+ import { classMap } from 'lit/directives/class-map.js';
4
8
  import { FocusMixin } from '../../mixins/focus/focus-mixin.js';
5
9
  import { getFocusPseudoClass } from '../../helpers/focus.js';
6
10
  import { getUniqueId } from '../../helpers/uniqueId.js';
@@ -46,7 +50,9 @@ export class TableColSortButton extends LocalizeCoreElement(FocusMixin(LitElemen
46
50
  sourceType: {
47
51
  attribute: 'source-type',
48
52
  type: String
49
- }
53
+ },
54
+ _hasDropdownItems: { state: true },
55
+ _selectedMenuItemText: { state: true }
50
56
  };
51
57
  }
52
58
 
@@ -60,6 +66,9 @@ export class TableColSortButton extends LocalizeCoreElement(FocusMixin(LitElemen
60
66
  :host([nosort]) {
61
67
  --d2l-table-col-sort-button-additional-padding-inline-end: calc(0.6rem + 18px);
62
68
  }
69
+ :host > :first-child {
70
+ width: var(--d2l-table-col-sort-button-width);
71
+ }
63
72
  :host([nosort][position="center"]) {
64
73
  --d2l-table-col-sort-button-additional-padding-inline-end: calc(0.5 * (0.6rem + 18px) + var(--d2l-table-cell-col-sort-button-size-offset, 4px));
65
74
  --d2l-table-col-sort-button-additional-padding-inline-start: calc(0.5 * (0.6rem + 18px) - var(--d2l-table-cell-col-sort-button-size-offset, 4px));
@@ -91,8 +100,9 @@ export class TableColSortButton extends LocalizeCoreElement(FocusMixin(LitElemen
91
100
  padding: calc(var(--d2l-table-cell-padding) - var(--d2l-table-cell-col-sort-button-size-offset, 4px));
92
101
  padding-inline-end: calc(var(--d2l-table-cell-padding) - var(--d2l-table-cell-col-sort-button-size-offset, 4px) + var(--d2l-table-col-sort-button-additional-padding-inline-end));
93
102
  padding-inline-start: calc(var(--d2l-table-cell-padding) - var(--d2l-table-cell-col-sort-button-size-offset, 4px) + var(--d2l-table-col-sort-button-additional-padding-inline-start));
103
+ text-align: start;
94
104
  text-decoration: none;
95
- width: var(--d2l-table-col-sort-button-width);
105
+ width: 100%;
96
106
  }
97
107
  button::-moz-focus-inner {
98
108
  border: 0;
@@ -111,6 +121,12 @@ export class TableColSortButton extends LocalizeCoreElement(FocusMixin(LitElemen
111
121
  d2l-icon {
112
122
  margin-inline-start: 0.6rem;
113
123
  }
124
+ ::slotted(*[slot="items"]) {
125
+ display: none;
126
+ }
127
+ ::slotted(d2l-table-col-sort-button-item[slot="items"]) {
128
+ display: flex;
129
+ }
114
130
  `;
115
131
  }
116
132
 
@@ -122,31 +138,75 @@ export class TableColSortButton extends LocalizeCoreElement(FocusMixin(LitElemen
122
138
  this.sourceType = 'unknown';
123
139
 
124
140
  this._describedById = getUniqueId();
141
+ this._hasDropdownItems = false;
125
142
  }
126
143
 
127
144
  static get focusElementSelector() {
128
145
  return 'button';
129
146
  }
130
147
 
148
+ firstUpdated(changedProperties) {
149
+ super.firstUpdated(changedProperties);
150
+
151
+ const selectedItem = this.querySelector('[selected]');
152
+ if (selectedItem && !this.nosort) this._selectedMenuItemText = selectedItem.text;
153
+ }
154
+
131
155
  render() {
132
156
  const buttonDescription = this.nosort ? this.localize('components.table-col-sort-button.addSortOrder') : this.localize('components.table-col-sort-button.changeSortOrder');
133
157
  const buttonTitle = this.nosort
134
158
  ? undefined
135
159
  : this.localize('components.table-col-sort-button.title', {
136
- sourceType: this.sourceType,
137
- direction: this.desc ? 'desc' : undefined
160
+ sourceType: this._hasDropdownItems && this._selectedMenuItemText ? 'value' : this.sourceType,
161
+ direction: this.desc ? 'desc' : undefined,
162
+ selectedMenuItemText: this._selectedMenuItemText
138
163
  });
139
164
 
140
165
  const iconView = !this.nosort ?
141
166
  html`<d2l-icon icon="${this.desc ? 'tier1:arrow-toggle-down' : 'tier1:arrow-toggle-up'}"></d2l-icon>` :
142
167
  null;
143
168
 
144
- return html`<button
169
+ const button = html`<button
145
170
  aria-describedby="${this._describedById}"
171
+ class="${classMap({ 'd2l-dropdown-opener': this._hasDropdownItems })}"
146
172
  title="${ifDefined(buttonTitle)}"
147
173
  type="button">
148
174
  <slot></slot>${iconView}
149
175
  </button><span id="${this._describedById}" hidden>${buttonDescription}</span>`;
176
+ if (this._hasDropdownItems) {
177
+ return html`<d2l-dropdown>
178
+ ${button}
179
+ <d2l-dropdown-menu no-pointer align="start" vertical-offset="0">
180
+ <d2l-menu @d2l-table-col-sort-button-item-change="${this._handleTablColSortButtonItemChange}">
181
+ <slot name="items" @slotchange="${this._handleSlotChange}"></slot>
182
+ </d2l-menu>
183
+ </d2l-dropdown-menu>
184
+ </d2l-dropdown>`;
185
+ } else {
186
+ return html`${button}<slot name="items" @slotchange="${this._handleSlotChange}"></slot>`;
187
+ }
188
+ }
189
+
190
+ updated(changedProperties) {
191
+ super.updated(changedProperties);
192
+
193
+ // de-select any selected dropdown menu item
194
+ if (changedProperties.has('nosort') && this.nosort && this._hasDropdownItems) {
195
+ const selectedItem = this.querySelector('[selected]');
196
+ if (selectedItem) selectedItem.selected = false;
197
+ }
198
+ }
199
+
200
+ _handleSlotChange(e) {
201
+ const items = e.target?.assignedNodes({ flatten: true }).filter((node) => node.nodeType === Node.ELEMENT_NODE);
202
+ const filteredItems = items.filter((item) => {
203
+ return item.tagName === 'D2L-TABLE-COL-SORT-BUTTON-ITEM';
204
+ });
205
+ this._hasDropdownItems = filteredItems.length > 0;
206
+ }
207
+
208
+ _handleTablColSortButtonItemChange(e) {
209
+ this._selectedMenuItemText = e.target?.text;
150
210
  }
151
211
 
152
212
  }
@@ -12020,6 +12020,92 @@
12020
12020
  }
12021
12021
  ]
12022
12022
  },
12023
+ {
12024
+ "name": "d2l-table-col-sort-button-item",
12025
+ "path": "./components/table/table-col-sort-button-item.js",
12026
+ "description": "A radio menu item to be used within the d2l-table-col-sort-button component for a multi-faceted sort.",
12027
+ "attributes": [
12028
+ {
12029
+ "name": "value",
12030
+ "description": "REQUIRED: The selectable item's value",
12031
+ "type": "string"
12032
+ },
12033
+ {
12034
+ "name": "selected",
12035
+ "description": "This will set the item to be selected by default",
12036
+ "type": "boolean",
12037
+ "default": "false"
12038
+ },
12039
+ {
12040
+ "name": "text",
12041
+ "description": "REQUIRED: Text displayed by the menu item",
12042
+ "type": "string"
12043
+ },
12044
+ {
12045
+ "name": "description",
12046
+ "description": "Provide a description for the menu item that will be used by screen readers",
12047
+ "type": "string"
12048
+ },
12049
+ {
12050
+ "name": "disabled",
12051
+ "description": "Disables the menu item",
12052
+ "type": "boolean",
12053
+ "default": "false"
12054
+ }
12055
+ ],
12056
+ "properties": [
12057
+ {
12058
+ "name": "value",
12059
+ "attribute": "value",
12060
+ "description": "REQUIRED: The selectable item's value",
12061
+ "type": "string"
12062
+ },
12063
+ {
12064
+ "name": "selected",
12065
+ "attribute": "selected",
12066
+ "description": "This will set the item to be selected by default",
12067
+ "type": "boolean",
12068
+ "default": "false"
12069
+ },
12070
+ {
12071
+ "name": "text",
12072
+ "attribute": "text",
12073
+ "description": "REQUIRED: Text displayed by the menu item",
12074
+ "type": "string"
12075
+ },
12076
+ {
12077
+ "name": "description",
12078
+ "attribute": "description",
12079
+ "description": "Provide a description for the menu item that will be used by screen readers",
12080
+ "type": "string"
12081
+ },
12082
+ {
12083
+ "name": "disabled",
12084
+ "attribute": "disabled",
12085
+ "description": "Disables the menu item",
12086
+ "type": "boolean",
12087
+ "default": "false"
12088
+ }
12089
+ ],
12090
+ "events": [
12091
+ {
12092
+ "name": "d2l-table-col-sort-button-item-change",
12093
+ "description": "Dispatched when the selected multi-faceted sort option changes"
12094
+ },
12095
+ {
12096
+ "name": "d2l-menu-item-change",
12097
+ "description": "Dispatched when the selected menu item changes"
12098
+ },
12099
+ {
12100
+ "name": "d2l-menu-item-select",
12101
+ "description": "Dispatched when the menu item is selected"
12102
+ },
12103
+ {
12104
+ "name": "d2l-menu-item-visibility-change",
12105
+ "description": "Dispatched when the visibility of the menu item changes"
12106
+ }
12107
+ ]
12108
+ },
12023
12109
  {
12024
12110
  "name": "d2l-table-col-sort-button",
12025
12111
  "path": "./components/table/table-col-sort-button.js",
package/lang/en.js CHANGED
@@ -107,7 +107,7 @@ export default {
107
107
  "components.switch.conditions": "Conditions must be met",
108
108
  "components.table-col-sort-button.addSortOrder": "Select to add sort order",
109
109
  "components.table-col-sort-button.changeSortOrder": "Select to change sort order",
110
- "components.table-col-sort-button.title": "{sourceType, select, dates {{direction, select, desc {Sorted new to old} other {Sorted old to new}}} numbers {{direction, select, desc {Sorted high to low} other {Sorted low to high}}} words {{direction, select, desc {Sorted Z to A} other {Sorted A to Z}}} other {{direction, select, desc {Sorted descending} other {Sorted ascending}}}}",
110
+ "components.table-col-sort-button.title": "{sourceType, select, dates {{direction, select, desc {Sorted new to old} other {Sorted old to new}}} numbers {{direction, select, desc {Sorted high to low} other {Sorted low to high}}} words {{direction, select, desc {Sorted Z to A} other {Sorted A to Z}}} value {Sorted {selectedMenuItemText}} other {{direction, select, desc {Sorted descending} other {Sorted ascending}}}}",
111
111
  "components.table-controls.label": "Actions for table",
112
112
  "components.tabs.next": "Scroll Forward",
113
113
  "components.tabs.previous": "Scroll Backward",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brightspace-ui/core",
3
- "version": "3.4.0",
3
+ "version": "3.5.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",