@brightspace-ui/core 2.105.0 → 2.107.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.
- package/components/list/demo/demo-list-nested.js +1 -2
- package/components/list/demo/demo-list.js +0 -1
- package/components/list/list.js +14 -35
- package/components/paging/README.md +3 -11
- package/components/paging/demo/pager-load-more.html +2 -2
- package/components/paging/pageable-mixin.js +42 -15
- package/components/paging/pageable-subscriber-mixin.js +33 -0
- package/components/paging/pager-load-more.js +9 -18
- package/components/selection/selection-mixin.js +2 -7
- package/controllers/subscriber/subscriberControllers.js +7 -1
- package/custom-elements.json +64 -88
- package/mixins/collection/README.md +36 -0
- package/mixins/collection/collection-mixin.js +18 -0
- package/mixins/localize/README.md +83 -87
- package/mixins/localize/localize-mixin.js +0 -25
- package/package.json +1 -1
|
@@ -169,7 +169,7 @@ class ListDemoNested extends LitElement {
|
|
|
169
169
|
|
|
170
170
|
_renderList(items, nested, includeControls = false, showLoadMore = false) {
|
|
171
171
|
return html`
|
|
172
|
-
<d2l-list ?grid="${!this.disableListGrid}" drag-multiple slot="${ifDefined(nested ? 'nested' : undefined)}">
|
|
172
|
+
<d2l-list ?grid="${!this.disableListGrid}" drag-multiple slot="${ifDefined(nested ? 'nested' : undefined)}" item-count="${this._items.length}">
|
|
173
173
|
${ includeControls ? this._renderListControls() : nothing }
|
|
174
174
|
${repeat(items, item => item.key, item => html`
|
|
175
175
|
${this._renderListItem(item)}
|
|
@@ -268,7 +268,6 @@ class ListDemoNested extends LitElement {
|
|
|
268
268
|
<d2l-pager-load-more slot="pager"
|
|
269
269
|
@d2l-pager-load-more="${this._handlePagerLoadMore}"
|
|
270
270
|
?has-more="${this._lastItemLoadedIndex < this._items.length - 1}"
|
|
271
|
-
item-count="${this._items.length}"
|
|
272
271
|
page-size="${this._remainingItemCount < this._pageSize ? this._remainingItemCount : this._pageSize}">
|
|
273
272
|
</d2l-pager-load-more>
|
|
274
273
|
`;
|
|
@@ -201,7 +201,6 @@ class DemoList extends LitElement {
|
|
|
201
201
|
<d2l-pager-load-more slot="pager"
|
|
202
202
|
@d2l-pager-load-more="${this._handlePagerLoadMore}"
|
|
203
203
|
?has-more="${this._lastItemLoadedIndex < this.items.length - 1}"
|
|
204
|
-
item-count="${this.items.length}"
|
|
205
204
|
page-size="${remainingItemCount < this._pageSize ? remainingItemCount : this._pageSize}">
|
|
206
205
|
</d2l-pager-load-more>
|
|
207
206
|
</d2l-list>
|
package/components/list/list.js
CHANGED
|
@@ -64,8 +64,6 @@ class List extends PageableMixin(SelectionMixin(LitElement)) {
|
|
|
64
64
|
this.dragMultiple = false;
|
|
65
65
|
this.extendSeparators = false;
|
|
66
66
|
this.grid = false;
|
|
67
|
-
this._itemsShowingCount = 0;
|
|
68
|
-
this._itemsShowingTotalCount = 0;
|
|
69
67
|
this._listItemChanges = [];
|
|
70
68
|
this._childHasExpandCollapseToggle = false;
|
|
71
69
|
|
|
@@ -77,7 +75,7 @@ class List extends PageableMixin(SelectionMixin(LitElement)) {
|
|
|
77
75
|
|
|
78
76
|
connectedCallback() {
|
|
79
77
|
super.connectedCallback();
|
|
80
|
-
this.addEventListener('d2l-list-
|
|
78
|
+
this.addEventListener('d2l-list-item-showing-count-change', this._handleListItemShowingCountChange);
|
|
81
79
|
this.addEventListener('d2l-list-item-nested-change', (e) => this._handleListIemNestedChange(e));
|
|
82
80
|
}
|
|
83
81
|
|
|
@@ -190,13 +188,8 @@ class List extends PageableMixin(SelectionMixin(LitElement)) {
|
|
|
190
188
|
return items[index];
|
|
191
189
|
}
|
|
192
190
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
else return this._getListItemsShowingTotalCount(false);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
_getLastItemIndex() {
|
|
199
|
-
return this._itemsShowingCount - 1;
|
|
191
|
+
_getItemShowingCount() {
|
|
192
|
+
return this.getItems().length;
|
|
200
193
|
}
|
|
201
194
|
|
|
202
195
|
_getLazyLoadItems() {
|
|
@@ -204,13 +197,6 @@ class List extends PageableMixin(SelectionMixin(LitElement)) {
|
|
|
204
197
|
return items.length > 0 ? items[0]._getFlattenedListItems().lazyLoadListItems : new Map();
|
|
205
198
|
}
|
|
206
199
|
|
|
207
|
-
async _getListItemsShowingTotalCount(refresh) {
|
|
208
|
-
if (refresh) {
|
|
209
|
-
this._itemsShowingTotalCount = this.getItems().length;
|
|
210
|
-
}
|
|
211
|
-
return this._itemsShowingTotalCount;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
200
|
_handleKeyDown(e) {
|
|
215
201
|
if (!this.grid || this.slot === 'nested' || e.keyCode !== keyCodes.TAB) return;
|
|
216
202
|
e.preventDefault();
|
|
@@ -236,33 +222,26 @@ class List extends PageableMixin(SelectionMixin(LitElement)) {
|
|
|
236
222
|
this._listChildrenUpdatedSubscribers.updateSubscribers();
|
|
237
223
|
}
|
|
238
224
|
|
|
239
|
-
|
|
225
|
+
_handleListItemShowingCountChange() {
|
|
240
226
|
if (this.slot === 'nested') return;
|
|
241
227
|
|
|
242
228
|
// debounce the updates for first render case
|
|
243
|
-
if (this.
|
|
244
|
-
|
|
245
|
-
this.
|
|
246
|
-
setTimeout(
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
if (oldCount !== newCount) this._updatePagerCount(newCount);
|
|
250
|
-
this._updateItemsShowingTotalCountRequested = false;
|
|
229
|
+
if (this._updateItemShowingCountRequested) return;
|
|
230
|
+
|
|
231
|
+
this._updateItemShowingCountRequested = true;
|
|
232
|
+
setTimeout(() => {
|
|
233
|
+
this._updateItemShowingCount();
|
|
234
|
+
this._updateItemShowingCountRequested = false;
|
|
251
235
|
}, 0);
|
|
252
236
|
}
|
|
253
237
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
if (this._itemsShowingCount === items.length) return;
|
|
257
|
-
this._itemsShowingCount = items.length;
|
|
258
|
-
|
|
259
|
-
this._updatePagerCount(await this._getListItemsShowingTotalCount(true));
|
|
238
|
+
_handleSlotChange() {
|
|
239
|
+
this._updateItemShowingCount();
|
|
260
240
|
|
|
261
241
|
/** @ignore */
|
|
262
|
-
this.dispatchEvent(new CustomEvent('d2l-list-
|
|
242
|
+
this.dispatchEvent(new CustomEvent('d2l-list-item-showing-count-change', {
|
|
263
243
|
bubbles: true,
|
|
264
|
-
composed: true
|
|
265
|
-
detail: { count: this._itemsShowingCount }
|
|
244
|
+
composed: true
|
|
266
245
|
}));
|
|
267
246
|
}
|
|
268
247
|
|
|
@@ -7,16 +7,9 @@ The paging components and mixins can be used to provide consistent paging functi
|
|
|
7
7
|
<script type="module">
|
|
8
8
|
import '@brightspace-ui/core/components/paging/pager-load-more.js';
|
|
9
9
|
</script>
|
|
10
|
-
<d2l-pager-load-more has-more page-size="3"
|
|
10
|
+
<d2l-pager-load-more has-more page-size="3"></d2l-pager-load-more>
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
## Best Practices
|
|
14
|
-
<!-- docs: start best practices -->
|
|
15
|
-
<!-- docs: start dos -->
|
|
16
|
-
* Consider the performance impact of acquiring the optional total `item-count`. The `item-count` provides useful context for the user, but counting large numbers of rows can be detrimental to performance. As a very general guide, when the total number of rows that needs to be counted is < 50,000, it's not a performance concern.
|
|
17
|
-
<!-- docs: end dos -->
|
|
18
|
-
<!-- docs: end best practices -->
|
|
19
|
-
|
|
20
13
|
## Load More Paging [d2l-pager-load-more]
|
|
21
14
|
|
|
22
15
|
The `d2l-pager-load-more` component can be used in conjunction with pageable components such as `d2l-list` to provide load-more paging functionality. The pager will dispatch the `d2l-pager-load-more` when clicked, and then the consumer handles the event by loading more items, updating the pager state, and signalling completion by calling `complete()` on the event detail. Focus will be automatically moved on the first new item once complete.
|
|
@@ -24,10 +17,10 @@ The `d2l-pager-load-more` component can be used in conjunction with pageable com
|
|
|
24
17
|
See [Pageable Lists](../../components/list/#pageable-lists).
|
|
25
18
|
|
|
26
19
|
```html
|
|
27
|
-
<d2l-list>
|
|
20
|
+
<d2l-list item-count="85">
|
|
28
21
|
<d2l-list-item ...></d2l-list-item>
|
|
29
22
|
<d2l-list-item ...></d2l-list-item>
|
|
30
|
-
<d2l-pager-load-more slot="pager" has-more page-size="10"
|
|
23
|
+
<d2l-pager-load-more slot="pager" has-more page-size="10"></d2l-pager-load-more>
|
|
31
24
|
</d2l-list>
|
|
32
25
|
```
|
|
33
26
|
|
|
@@ -44,7 +37,6 @@ pager.addEventListener('d2l-pager-load-more', e => {
|
|
|
44
37
|
| Property | Type | Description |
|
|
45
38
|
|---|---|---|
|
|
46
39
|
| `has-more` | Boolean, default: `false` | Whether there are more items that can be loaded. |
|
|
47
|
-
| `item-count` | Number | Total number of items. If not specified, neither it nor the count of items showing will be displayed. |
|
|
48
40
|
| `page-size` | Number, default: 50 | The number of additional items to load. |
|
|
49
41
|
|
|
50
42
|
### Events
|
|
@@ -27,12 +27,12 @@
|
|
|
27
27
|
|
|
28
28
|
<d2l-demo-snippet>
|
|
29
29
|
<template>
|
|
30
|
-
<d2l-test-pageable>
|
|
30
|
+
<d2l-test-pageable item-count="12">
|
|
31
31
|
<ul>
|
|
32
32
|
<li><a href="https://some-website">item 1</a></li>
|
|
33
33
|
<li><a href="https://some-website">item 2</a></li>
|
|
34
34
|
</ul>
|
|
35
|
-
<d2l-pager-load-more id="pager1" slot="pager" has-more page-size="3"
|
|
35
|
+
<d2l-pager-load-more id="pager1" slot="pager" has-more page-size="3"></d2l-pager-load-more>
|
|
36
36
|
</d2l-test-pageable>
|
|
37
37
|
<script>
|
|
38
38
|
document.querySelector('#pager1').addEventListener('d2l-pager-load-more', window.handleLoadMore);
|
|
@@ -1,35 +1,62 @@
|
|
|
1
|
+
import { CollectionMixin } from '../../mixins/collection/collection-mixin.js';
|
|
1
2
|
import { html } from 'lit';
|
|
3
|
+
import { SubscriberRegistryController } from '../../controllers/subscriber/subscriberControllers.js';
|
|
2
4
|
|
|
3
|
-
export const PageableMixin = superclass => class extends superclass {
|
|
5
|
+
export const PageableMixin = superclass => class extends CollectionMixin(superclass) {
|
|
6
|
+
|
|
7
|
+
static get properties() {
|
|
8
|
+
return {
|
|
9
|
+
_itemShowingCount: { state: true },
|
|
10
|
+
};
|
|
11
|
+
}
|
|
4
12
|
|
|
5
13
|
constructor() {
|
|
6
14
|
super();
|
|
7
|
-
|
|
15
|
+
|
|
16
|
+
this._itemShowingCount = 0;
|
|
17
|
+
this._pageableSubscriberRegistry = new SubscriberRegistryController(this, 'pageable', {
|
|
18
|
+
onSubscribe: this._updatePageableSubscriber.bind(this),
|
|
19
|
+
updateSubscribers: this._updatePageableSubscribers.bind(this)
|
|
20
|
+
});
|
|
8
21
|
}
|
|
9
22
|
|
|
10
|
-
|
|
11
|
-
|
|
23
|
+
firstUpdated(changedProperties) {
|
|
24
|
+
super.firstUpdated(changedProperties);
|
|
25
|
+
this._updateItemShowingCount();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
updated(changedProperties) {
|
|
29
|
+
super.updated(changedProperties);
|
|
30
|
+
|
|
31
|
+
if (changedProperties.has('itemCount') || changedProperties.has('_itemShowingCount')) {
|
|
32
|
+
this._pageableSubscriberRegistry.updateSubscribers();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
12
35
|
|
|
13
36
|
/* must be implemented by consumer */
|
|
14
|
-
|
|
37
|
+
_getItemByIndex(index) { } // eslint-disable-line no-unused-vars
|
|
15
38
|
|
|
16
39
|
/* must be implemented by consumer */
|
|
17
|
-
|
|
40
|
+
_getItemShowingCount() { }
|
|
18
41
|
|
|
19
|
-
|
|
20
|
-
|
|
42
|
+
_getLastItemIndex() {
|
|
43
|
+
return this._itemShowingCount - 1;
|
|
21
44
|
}
|
|
22
45
|
|
|
23
46
|
_renderPagerContainer() {
|
|
24
|
-
return html`<slot name="pager"
|
|
47
|
+
return html`<slot name="pager"></slot>`;
|
|
25
48
|
}
|
|
26
49
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
50
|
+
_updateItemShowingCount() {
|
|
51
|
+
this._itemShowingCount = this._getItemShowingCount();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
_updatePageableSubscriber(subscriber) {
|
|
55
|
+
subscriber._pageableInfo = { itemShowingCount: this._itemShowingCount, itemCount: this.itemCount };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
_updatePageableSubscribers(subscribers) {
|
|
59
|
+
subscribers.forEach(subscriber => this._updatePageableSubscriber(subscriber));
|
|
33
60
|
}
|
|
34
61
|
|
|
35
62
|
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { EventSubscriberController, IdSubscriberController } from '../../controllers/subscriber/subscriberControllers.js';
|
|
2
|
+
|
|
3
|
+
export const PageableSubscriberMixin = superclass => class extends superclass {
|
|
4
|
+
|
|
5
|
+
static get properties() {
|
|
6
|
+
return {
|
|
7
|
+
/**
|
|
8
|
+
* Id of the `PageableMixin` component this component wants to observe (if not located within that component)
|
|
9
|
+
* @type {string}
|
|
10
|
+
*/
|
|
11
|
+
pageableFor: { type: String, reflect: true, attribute: 'pageable-for' },
|
|
12
|
+
_pageableInfo: { state: true }
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
constructor() {
|
|
17
|
+
super();
|
|
18
|
+
|
|
19
|
+
this._pageableInfo = { itemCount: null, itemShowingCount: 0 };
|
|
20
|
+
this._pageableEventSubscriber = new EventSubscriberController(this, 'pageable');
|
|
21
|
+
this._pageableIdSubscriber = new IdSubscriberController(this, 'pageable', { idPropertyName: 'pageableFor' });
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async getUpdateComplete() {
|
|
25
|
+
await super.getUpdateComplete();
|
|
26
|
+
await (this.pageableFor ? this._pageableIdSubscriber._subscriptionComplete : this._pageableEventSubscriber._subscriptionComplete);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
_getPageableRegistries() {
|
|
30
|
+
return this.pageableFor ? this._pageableIdSubscriber.registries : [ this._pageableEventSubscriber.registry ];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
};
|
|
@@ -2,7 +2,6 @@ import '../colors/colors.js';
|
|
|
2
2
|
import '../loading-spinner/loading-spinner.js';
|
|
3
3
|
import { css, html, LitElement, nothing } from 'lit';
|
|
4
4
|
import { buttonStyles } from '../button/button-styles.js';
|
|
5
|
-
import { findComposedAncestor } from '../../helpers/dom.js';
|
|
6
5
|
import { FocusMixin } from '../../mixins/focus/focus-mixin.js';
|
|
7
6
|
import { formatNumber } from '@brightspace-ui/intl/lib/number.js';
|
|
8
7
|
import { getFirstFocusableDescendant } from '../../helpers/focus.js';
|
|
@@ -10,6 +9,7 @@ import { getSeparator } from '@brightspace-ui/intl/lib/list.js';
|
|
|
10
9
|
import { labelStyles } from '../typography/styles.js';
|
|
11
10
|
import { LocalizeCoreElement } from '../../helpers/localize-core-element.js';
|
|
12
11
|
import { offscreenStyles } from '../offscreen/offscreen.js';
|
|
12
|
+
import { PageableSubscriberMixin } from './pageable-subscriber-mixin.js';
|
|
13
13
|
|
|
14
14
|
const nativeFocus = document.createElement('div').focus;
|
|
15
15
|
|
|
@@ -17,7 +17,7 @@ const nativeFocus = document.createElement('div').focus;
|
|
|
17
17
|
* A pager component for load-more paging.
|
|
18
18
|
* @fires d2l-pager-load-more - Dispatched when the user clicks the load-more button. Consumers must call the provided "complete" method once items have been loaded.
|
|
19
19
|
*/
|
|
20
|
-
class LoadMore extends FocusMixin(LocalizeCoreElement(LitElement)) {
|
|
20
|
+
class LoadMore extends PageableSubscriberMixin(FocusMixin(LocalizeCoreElement(LitElement))) {
|
|
21
21
|
|
|
22
22
|
static get properties() {
|
|
23
23
|
return {
|
|
@@ -26,21 +26,11 @@ class LoadMore extends FocusMixin(LocalizeCoreElement(LitElement)) {
|
|
|
26
26
|
* @type {boolean}
|
|
27
27
|
*/
|
|
28
28
|
hasMore: { type: Boolean, attribute: 'has-more', reflect: true },
|
|
29
|
-
/**
|
|
30
|
-
* Total number of items. If not specified, neither it nor the count of items showing will be displayed.
|
|
31
|
-
* @type {number}
|
|
32
|
-
*/
|
|
33
|
-
itemCount: { type: Number, attribute: 'item-count', reflect: true },
|
|
34
29
|
/**
|
|
35
30
|
* The number of additional items to load.
|
|
36
31
|
* @type {number}
|
|
37
32
|
*/
|
|
38
33
|
pageSize: { type: Number, attribute: 'page-size', reflect: true },
|
|
39
|
-
/**
|
|
40
|
-
* The number of items showing. Assigned by PageableMixin.
|
|
41
|
-
* @ignore
|
|
42
|
-
*/
|
|
43
|
-
itemShowingCount: { attribute: false, type: Number },
|
|
44
34
|
_loading: { state: true }
|
|
45
35
|
};
|
|
46
36
|
}
|
|
@@ -85,9 +75,8 @@ class LoadMore extends FocusMixin(LocalizeCoreElement(LitElement)) {
|
|
|
85
75
|
constructor() {
|
|
86
76
|
super();
|
|
87
77
|
this.hasMore = false;
|
|
88
|
-
|
|
78
|
+
|
|
89
79
|
/** @ignore */
|
|
90
|
-
this.itemShowingCount = 0;
|
|
91
80
|
this.pageSize = 50;
|
|
92
81
|
this._loading = false;
|
|
93
82
|
}
|
|
@@ -97,7 +86,9 @@ class LoadMore extends FocusMixin(LocalizeCoreElement(LitElement)) {
|
|
|
97
86
|
}
|
|
98
87
|
|
|
99
88
|
render() {
|
|
100
|
-
if (!this.hasMore) return;
|
|
89
|
+
if (!this.hasMore) return nothing;
|
|
90
|
+
const { itemCount, itemShowingCount } = this._pageableInfo;
|
|
91
|
+
|
|
101
92
|
return html`
|
|
102
93
|
${this._loading ? html`
|
|
103
94
|
<span class="d2l-offscreen" role="alert">${this.localize('components.pager-load-more.status-loading')}</span>
|
|
@@ -107,10 +98,10 @@ class LoadMore extends FocusMixin(LocalizeCoreElement(LitElement)) {
|
|
|
107
98
|
<d2l-loading-spinner size="24"></d2l-loading-spinner>
|
|
108
99
|
` : html`
|
|
109
100
|
<span class="action">${this.localize('components.pager-load-more.action', { count: formatNumber(this.pageSize) })}</span>
|
|
110
|
-
${
|
|
101
|
+
${itemCount !== null ? html`
|
|
111
102
|
<span class="d2l-offscreen">${getSeparator({ nonBreaking: true })}</span>
|
|
112
103
|
<span class="separator"></span>
|
|
113
|
-
<span class="info">${this.localize('components.pager-load-more.info', { showingCount: formatNumber(
|
|
104
|
+
<span class="info">${this.localize('components.pager-load-more.info', { showingCount: formatNumber(itemShowingCount), totalCount: itemCount, totalCountFormatted: formatNumber(itemCount) })}</span>
|
|
114
105
|
` : nothing}
|
|
115
106
|
`}
|
|
116
107
|
</button>
|
|
@@ -119,7 +110,7 @@ class LoadMore extends FocusMixin(LocalizeCoreElement(LitElement)) {
|
|
|
119
110
|
|
|
120
111
|
async _handleClick() {
|
|
121
112
|
if (this._loading) return;
|
|
122
|
-
const pageable =
|
|
113
|
+
const pageable = this._getPageableRegistries()[0];
|
|
123
114
|
if (!pageable) return;
|
|
124
115
|
const lastItemIndex = pageable._getLastItemIndex();
|
|
125
116
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { CollectionMixin } from '../../mixins/collection/collection-mixin.js';
|
|
1
2
|
import { RtlMixin } from '../../mixins/rtl/rtl-mixin.js';
|
|
2
3
|
|
|
3
4
|
const keyCodes = {
|
|
@@ -35,15 +36,10 @@ export class SelectionInfo {
|
|
|
35
36
|
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
export const SelectionMixin = superclass => class extends RtlMixin(superclass) {
|
|
39
|
+
export const SelectionMixin = superclass => class extends RtlMixin(CollectionMixin(superclass)) {
|
|
39
40
|
|
|
40
41
|
static get properties() {
|
|
41
42
|
return {
|
|
42
|
-
/**
|
|
43
|
-
* Total number of items. Required when selecting all pages is allowed.
|
|
44
|
-
* @type {number}
|
|
45
|
-
*/
|
|
46
|
-
itemCount: { type: Number, attribute: 'item-count' },
|
|
47
43
|
/**
|
|
48
44
|
* Whether to render with single selection behaviour. If `selection-single` is specified, the nested `d2l-selection-input` elements will render radios instead of checkboxes, and the selection component will maintain a single selected item.
|
|
49
45
|
* @type {boolean}
|
|
@@ -59,7 +55,6 @@ export const SelectionMixin = superclass => class extends RtlMixin(superclass) {
|
|
|
59
55
|
|
|
60
56
|
constructor() {
|
|
61
57
|
super();
|
|
62
|
-
this.itemCount = 0;
|
|
63
58
|
this.selectionSingle = false;
|
|
64
59
|
this._selectAllPages = false;
|
|
65
60
|
this._selectionObservers = new Map();
|
|
@@ -9,6 +9,7 @@ class BaseController {
|
|
|
9
9
|
this._name = name;
|
|
10
10
|
this._options = options;
|
|
11
11
|
this._eventName = `d2l-subscribe-${this._name}`;
|
|
12
|
+
this._subscriptionComplete = Promise.resolve();
|
|
12
13
|
}
|
|
13
14
|
}
|
|
14
15
|
|
|
@@ -111,7 +112,12 @@ export class EventSubscriberController extends BaseSubscriber {
|
|
|
111
112
|
|
|
112
113
|
hostConnected() {
|
|
113
114
|
// delay subscription otherwise import/upgrade order can cause selection mixin to miss event
|
|
114
|
-
|
|
115
|
+
this._subscriptionComplete = new Promise(resolve => {
|
|
116
|
+
requestAnimationFrame(() => {
|
|
117
|
+
this._subscribe();
|
|
118
|
+
resolve();
|
|
119
|
+
});
|
|
120
|
+
});
|
|
115
121
|
}
|
|
116
122
|
|
|
117
123
|
hostDisconnected() {
|
package/custom-elements.json
CHANGED
|
@@ -8864,15 +8864,14 @@
|
|
|
8864
8864
|
"default": "false"
|
|
8865
8865
|
},
|
|
8866
8866
|
{
|
|
8867
|
-
"name": "
|
|
8868
|
-
"description": "
|
|
8867
|
+
"name": "item-count",
|
|
8868
|
+
"description": "Total number of items. If not specified, features like select-all-pages will be disabled.",
|
|
8869
8869
|
"type": "number"
|
|
8870
8870
|
},
|
|
8871
8871
|
{
|
|
8872
|
-
"name": "
|
|
8873
|
-
"description": "
|
|
8874
|
-
"type": "number"
|
|
8875
|
-
"default": "0"
|
|
8872
|
+
"name": "selection-count-override",
|
|
8873
|
+
"description": "ADVANCED: Temporary optional parameter used to override existing count. Will be removed soon, use with caution.",
|
|
8874
|
+
"type": "number"
|
|
8876
8875
|
},
|
|
8877
8876
|
{
|
|
8878
8877
|
"name": "selection-single",
|
|
@@ -8910,19 +8909,18 @@
|
|
|
8910
8909
|
"type": "boolean",
|
|
8911
8910
|
"default": "false"
|
|
8912
8911
|
},
|
|
8912
|
+
{
|
|
8913
|
+
"name": "itemCount",
|
|
8914
|
+
"attribute": "item-count",
|
|
8915
|
+
"description": "Total number of items. If not specified, features like select-all-pages will be disabled.",
|
|
8916
|
+
"type": "number"
|
|
8917
|
+
},
|
|
8913
8918
|
{
|
|
8914
8919
|
"name": "selectionCountOverride",
|
|
8915
8920
|
"attribute": "selection-count-override",
|
|
8916
8921
|
"description": "ADVANCED: Temporary optional parameter used to override existing count. Will be removed soon, use with caution.",
|
|
8917
8922
|
"type": "number"
|
|
8918
8923
|
},
|
|
8919
|
-
{
|
|
8920
|
-
"name": "itemCount",
|
|
8921
|
-
"attribute": "item-count",
|
|
8922
|
-
"description": "Total number of items. Required when selecting all pages is allowed.",
|
|
8923
|
-
"type": "number",
|
|
8924
|
-
"default": "0"
|
|
8925
|
-
},
|
|
8926
8924
|
{
|
|
8927
8925
|
"name": "selectionSingle",
|
|
8928
8926
|
"attribute": "selection-single",
|
|
@@ -10059,6 +10057,11 @@
|
|
|
10059
10057
|
"path": "./components/paging/pager-load-more.js",
|
|
10060
10058
|
"description": "A pager component for load-more paging.",
|
|
10061
10059
|
"attributes": [
|
|
10060
|
+
{
|
|
10061
|
+
"name": "page-size",
|
|
10062
|
+
"description": "The number of additional items to load.",
|
|
10063
|
+
"type": "number"
|
|
10064
|
+
},
|
|
10062
10065
|
{
|
|
10063
10066
|
"name": "has-more",
|
|
10064
10067
|
"description": "Whether there are more items that can be loaded.",
|
|
@@ -10066,19 +10069,18 @@
|
|
|
10066
10069
|
"default": "false"
|
|
10067
10070
|
},
|
|
10068
10071
|
{
|
|
10069
|
-
"name": "
|
|
10070
|
-
"description": "
|
|
10071
|
-
"type": "
|
|
10072
|
-
"default": "-1"
|
|
10073
|
-
},
|
|
10074
|
-
{
|
|
10075
|
-
"name": "page-size",
|
|
10076
|
-
"description": "The number of additional items to load.",
|
|
10077
|
-
"type": "number",
|
|
10078
|
-
"default": "50"
|
|
10072
|
+
"name": "pageable-for",
|
|
10073
|
+
"description": "Id of the `PageableMixin` component this component wants to observe (if not located within that component)",
|
|
10074
|
+
"type": "string"
|
|
10079
10075
|
}
|
|
10080
10076
|
],
|
|
10081
10077
|
"properties": [
|
|
10078
|
+
{
|
|
10079
|
+
"name": "pageSize",
|
|
10080
|
+
"attribute": "page-size",
|
|
10081
|
+
"description": "The number of additional items to load.",
|
|
10082
|
+
"type": "number"
|
|
10083
|
+
},
|
|
10082
10084
|
{
|
|
10083
10085
|
"name": "hasMore",
|
|
10084
10086
|
"attribute": "has-more",
|
|
@@ -10087,18 +10089,10 @@
|
|
|
10087
10089
|
"default": "false"
|
|
10088
10090
|
},
|
|
10089
10091
|
{
|
|
10090
|
-
"name": "
|
|
10091
|
-
"attribute": "
|
|
10092
|
-
"description": "
|
|
10093
|
-
"type": "
|
|
10094
|
-
"default": "-1"
|
|
10095
|
-
},
|
|
10096
|
-
{
|
|
10097
|
-
"name": "pageSize",
|
|
10098
|
-
"attribute": "page-size",
|
|
10099
|
-
"description": "The number of additional items to load.",
|
|
10100
|
-
"type": "number",
|
|
10101
|
-
"default": "50"
|
|
10092
|
+
"name": "pageableFor",
|
|
10093
|
+
"attribute": "pageable-for",
|
|
10094
|
+
"description": "Id of the `PageableMixin` component this component wants to observe (if not located within that component)",
|
|
10095
|
+
"type": "string"
|
|
10102
10096
|
},
|
|
10103
10097
|
{
|
|
10104
10098
|
"name": "documentLocaleSettings",
|
|
@@ -10114,7 +10108,41 @@
|
|
|
10114
10108
|
},
|
|
10115
10109
|
{
|
|
10116
10110
|
"name": "d2l-test-pageable",
|
|
10117
|
-
"path": "./components/paging/test/pageable-component.js"
|
|
10111
|
+
"path": "./components/paging/test/pageable-component.js",
|
|
10112
|
+
"attributes": [
|
|
10113
|
+
{
|
|
10114
|
+
"name": "item-count",
|
|
10115
|
+
"description": "Total number of items. If not specified, features like select-all-pages will be disabled.",
|
|
10116
|
+
"type": "number"
|
|
10117
|
+
}
|
|
10118
|
+
],
|
|
10119
|
+
"properties": [
|
|
10120
|
+
{
|
|
10121
|
+
"name": "itemCount",
|
|
10122
|
+
"attribute": "item-count",
|
|
10123
|
+
"description": "Total number of items. If not specified, features like select-all-pages will be disabled.",
|
|
10124
|
+
"type": "number"
|
|
10125
|
+
}
|
|
10126
|
+
]
|
|
10127
|
+
},
|
|
10128
|
+
{
|
|
10129
|
+
"name": "d2l-test-pageable-simple",
|
|
10130
|
+
"path": "./components/paging/test/pageable-component.js",
|
|
10131
|
+
"attributes": [
|
|
10132
|
+
{
|
|
10133
|
+
"name": "item-count",
|
|
10134
|
+
"description": "Total number of items. If not specified, features like select-all-pages will be disabled.",
|
|
10135
|
+
"type": "number"
|
|
10136
|
+
}
|
|
10137
|
+
],
|
|
10138
|
+
"properties": [
|
|
10139
|
+
{
|
|
10140
|
+
"name": "itemCount",
|
|
10141
|
+
"attribute": "item-count",
|
|
10142
|
+
"description": "Total number of items. If not specified, features like select-all-pages will be disabled.",
|
|
10143
|
+
"type": "number"
|
|
10144
|
+
}
|
|
10145
|
+
]
|
|
10118
10146
|
},
|
|
10119
10147
|
{
|
|
10120
10148
|
"name": "d2l-test-scroll-wrapper",
|
|
@@ -10194,12 +10222,6 @@
|
|
|
10194
10222
|
"description": "ADVANCED: Temporary optional parameter used to override existing count. Will be removed soon, use with caution.",
|
|
10195
10223
|
"type": "number"
|
|
10196
10224
|
},
|
|
10197
|
-
{
|
|
10198
|
-
"name": "item-count",
|
|
10199
|
-
"description": "Total number of items. Required when selecting all pages is allowed.",
|
|
10200
|
-
"type": "number",
|
|
10201
|
-
"default": "0"
|
|
10202
|
-
},
|
|
10203
10225
|
{
|
|
10204
10226
|
"name": "selection-single",
|
|
10205
10227
|
"description": "Whether to render with single selection behaviour. If `selection-single` is specified, the nested `d2l-selection-input` elements will render radios instead of checkboxes, and the selection component will maintain a single selected item.",
|
|
@@ -10214,13 +10236,6 @@
|
|
|
10214
10236
|
"description": "ADVANCED: Temporary optional parameter used to override existing count. Will be removed soon, use with caution.",
|
|
10215
10237
|
"type": "number"
|
|
10216
10238
|
},
|
|
10217
|
-
{
|
|
10218
|
-
"name": "itemCount",
|
|
10219
|
-
"attribute": "item-count",
|
|
10220
|
-
"description": "Total number of items. Required when selecting all pages is allowed.",
|
|
10221
|
-
"type": "number",
|
|
10222
|
-
"default": "0"
|
|
10223
|
-
},
|
|
10224
10239
|
{
|
|
10225
10240
|
"name": "selectionSingle",
|
|
10226
10241
|
"attribute": "selection-single",
|
|
@@ -10843,12 +10858,6 @@
|
|
|
10843
10858
|
"description": "ADVANCED: Temporary optional parameter used to override existing count. Will be removed soon, use with caution.",
|
|
10844
10859
|
"type": "number"
|
|
10845
10860
|
},
|
|
10846
|
-
{
|
|
10847
|
-
"name": "item-count",
|
|
10848
|
-
"description": "Total number of items. Required when selecting all pages is allowed.",
|
|
10849
|
-
"type": "number",
|
|
10850
|
-
"default": "0"
|
|
10851
|
-
},
|
|
10852
10861
|
{
|
|
10853
10862
|
"name": "selection-single",
|
|
10854
10863
|
"description": "Whether to render with single selection behaviour. If `selection-single` is specified, the nested `d2l-selection-input` elements will render radios instead of checkboxes, and the selection component will maintain a single selected item.",
|
|
@@ -10863,13 +10872,6 @@
|
|
|
10863
10872
|
"description": "ADVANCED: Temporary optional parameter used to override existing count. Will be removed soon, use with caution.",
|
|
10864
10873
|
"type": "number"
|
|
10865
10874
|
},
|
|
10866
|
-
{
|
|
10867
|
-
"name": "itemCount",
|
|
10868
|
-
"attribute": "item-count",
|
|
10869
|
-
"description": "Total number of items. Required when selecting all pages is allowed.",
|
|
10870
|
-
"type": "number",
|
|
10871
|
-
"default": "0"
|
|
10872
|
-
},
|
|
10873
10875
|
{
|
|
10874
10876
|
"name": "selectionSingle",
|
|
10875
10877
|
"attribute": "selection-single",
|
|
@@ -11371,12 +11373,6 @@
|
|
|
11371
11373
|
"description": "ADVANCED: Temporary optional parameter used to override existing count. Will be removed soon, use with caution.",
|
|
11372
11374
|
"type": "number"
|
|
11373
11375
|
},
|
|
11374
|
-
{
|
|
11375
|
-
"name": "item-count",
|
|
11376
|
-
"description": "Total number of items. Required when selecting all pages is allowed.",
|
|
11377
|
-
"type": "number",
|
|
11378
|
-
"default": "0"
|
|
11379
|
-
},
|
|
11380
11376
|
{
|
|
11381
11377
|
"name": "selection-single",
|
|
11382
11378
|
"description": "Whether to render with single selection behaviour. If `selection-single` is specified, the nested `d2l-selection-input` elements will render radios instead of checkboxes, and the selection component will maintain a single selected item.",
|
|
@@ -11422,13 +11418,6 @@
|
|
|
11422
11418
|
"description": "ADVANCED: Temporary optional parameter used to override existing count. Will be removed soon, use with caution.",
|
|
11423
11419
|
"type": "number"
|
|
11424
11420
|
},
|
|
11425
|
-
{
|
|
11426
|
-
"name": "itemCount",
|
|
11427
|
-
"attribute": "item-count",
|
|
11428
|
-
"description": "Total number of items. Required when selecting all pages is allowed.",
|
|
11429
|
-
"type": "number",
|
|
11430
|
-
"default": "0"
|
|
11431
|
-
},
|
|
11432
11421
|
{
|
|
11433
11422
|
"name": "selectionSingle",
|
|
11434
11423
|
"attribute": "selection-single",
|
|
@@ -11594,12 +11583,6 @@
|
|
|
11594
11583
|
"description": "ADVANCED: Temporary optional parameter used to override existing count. Will be removed soon, use with caution.",
|
|
11595
11584
|
"type": "number"
|
|
11596
11585
|
},
|
|
11597
|
-
{
|
|
11598
|
-
"name": "item-count",
|
|
11599
|
-
"description": "Total number of items. Required when selecting all pages is allowed.",
|
|
11600
|
-
"type": "number",
|
|
11601
|
-
"default": "0"
|
|
11602
|
-
},
|
|
11603
11586
|
{
|
|
11604
11587
|
"name": "selection-single",
|
|
11605
11588
|
"description": "Whether to render with single selection behaviour. If `selection-single` is specified, the nested `d2l-selection-input` elements will render radios instead of checkboxes, and the selection component will maintain a single selected item.",
|
|
@@ -11635,13 +11618,6 @@
|
|
|
11635
11618
|
"description": "ADVANCED: Temporary optional parameter used to override existing count. Will be removed soon, use with caution.",
|
|
11636
11619
|
"type": "number"
|
|
11637
11620
|
},
|
|
11638
|
-
{
|
|
11639
|
-
"name": "itemCount",
|
|
11640
|
-
"attribute": "item-count",
|
|
11641
|
-
"description": "Total number of items. Required when selecting all pages is allowed.",
|
|
11642
|
-
"type": "number",
|
|
11643
|
-
"default": "0"
|
|
11644
|
-
},
|
|
11645
11621
|
{
|
|
11646
11622
|
"name": "selectionSingle",
|
|
11647
11623
|
"attribute": "selection-single",
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# CollectionMixin
|
|
2
|
+
|
|
3
|
+
The `CollectionMixin` describes a collection of items like a list or table. It has one attribute, `item-count`, which optionally defines the total number of items in the collection. This may be greater than the number of items currently displayed, and is useful for actions like select-all and paging.
|
|
4
|
+
|
|
5
|
+
## Best Practices
|
|
6
|
+
<!-- docs: start best practices -->
|
|
7
|
+
<!-- docs: start dos -->
|
|
8
|
+
* Consider the performance impact of acquiring the optional total `item-count`. The `item-count` provides useful context for the user, but counting large numbers of rows can be detrimental to performance. As a very general guide, when the total number of rows that needs to be counted is < 50,000, it's not a performance concern.
|
|
9
|
+
<!-- docs: end dos -->
|
|
10
|
+
<!-- docs: end best practices -->
|
|
11
|
+
|
|
12
|
+
## Usage
|
|
13
|
+
|
|
14
|
+
Apply the mixin and access the `itemCount` property as needed. Note that `itemCount` has a default value of `null` to indicate that no count was specified.
|
|
15
|
+
|
|
16
|
+
```js
|
|
17
|
+
import { CollectionMixin } from '@brightspace-ui/core/mixins/collection-mixin.js';
|
|
18
|
+
|
|
19
|
+
class MyComponent extends CollectionMixin(LitElement) {
|
|
20
|
+
render() {
|
|
21
|
+
const itemCountToDisplay = this.itemCount !== null ? this.itemCount : 'Unspecified';
|
|
22
|
+
return html`
|
|
23
|
+
<p>Total number of items: ${itemCountToDisplay}</p>
|
|
24
|
+
<slot></slot>
|
|
25
|
+
`;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
<!-- docs: start hidden content -->
|
|
31
|
+
### Properties
|
|
32
|
+
|
|
33
|
+
| Property | Type | Description |
|
|
34
|
+
|---|---|---|
|
|
35
|
+
| `item-count` | Number | Total number of items. Required when selecting all pages is allowed. |
|
|
36
|
+
<!-- docs: end hidden content -->
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const CollectionMixin = superclass => class extends superclass {
|
|
2
|
+
|
|
3
|
+
static get properties() {
|
|
4
|
+
return {
|
|
5
|
+
/**
|
|
6
|
+
* Total number of items. If not specified, features like select-all-pages will be disabled.
|
|
7
|
+
* @type {number}
|
|
8
|
+
*/
|
|
9
|
+
itemCount: { type: Number, attribute: 'item-count', reflect: true },
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
constructor() {
|
|
14
|
+
super();
|
|
15
|
+
this.itemCount = null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
};
|
|
@@ -1,135 +1,84 @@
|
|
|
1
|
-
#
|
|
1
|
+
# LocalizeMixin
|
|
2
2
|
|
|
3
|
-
The `LocalizeMixin`
|
|
4
|
-
|
|
5
|
-
## Providing Resources
|
|
6
|
-
|
|
7
|
-
Your component must provide resources by either implementing a `resources` getter for local resources, or a `localizeConfig` getter to fetch resources asynchronously. The `importFunc` method of `localizeConfig` will be called with lowercase languages in preferential order.
|
|
3
|
+
The `LocalizeMixin` allows text in components to be displayed in the user's preferred language.
|
|
8
4
|
|
|
9
5
|
## Language Resources
|
|
10
6
|
|
|
11
|
-
Resources
|
|
7
|
+
Resources are stored as key-value JSON objects.
|
|
12
8
|
|
|
13
|
-
|
|
9
|
+
### Keys
|
|
14
10
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
Example:
|
|
11
|
+
The key should succinctly and uniquely describe the text being localized. `camelCase` is recommended, although `snake_case` and `kebab-case` are also supported.
|
|
18
12
|
|
|
19
|
-
|
|
20
|
-
{
|
|
21
|
-
"hello": "Hello {firstName}!",
|
|
22
|
-
"goodbye": "Goodbye."
|
|
23
|
-
}
|
|
24
|
-
```
|
|
13
|
+
For large projects, terms may be grouped using the `:` character. For example: `parentGroup:subGroup:termName`.
|
|
25
14
|
|
|
26
|
-
|
|
15
|
+
### Values
|
|
27
16
|
|
|
28
|
-
|
|
17
|
+
Term values must conform to the [ICU Message Syntax](https://formatjs.io/docs/core-concepts/icu-syntax/) format. It supports features such as: [simple arguments](https://formatjs.io/docs/core-concepts/icu-syntax/#simple-argument), the [`{select}` format](https://formatjs.io/docs/core-concepts/icu-syntax/#select-format) and [pluralization](https://formatjs.io/docs/core-concepts/icu-syntax/#plural-format).
|
|
29
18
|
|
|
30
|
-
|
|
19
|
+
> **Note:** Avoid using the ICU Message Syntax number, date and time formatting functionality. Brightspace allows customization of how these are localized, so use [@brightspace-ui/intl](https://github.com/BrightspaceUI/intl)'s `formatNumber`, `formatDate` and `formatTime` instead.
|
|
31
20
|
|
|
32
|
-
|
|
21
|
+
### Files
|
|
33
22
|
|
|
34
|
-
|
|
23
|
+
Store localization resources in their own directory with nothing else in it. There should be one JavaScript file for each supported locale.
|
|
35
24
|
|
|
36
25
|
```javascript
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
static get resources() {
|
|
42
|
-
return {
|
|
43
|
-
'en': {
|
|
44
|
-
hello: 'Hello {firstName}!'
|
|
45
|
-
},
|
|
46
|
-
'fr': {
|
|
47
|
-
hello: 'Bonjour {firstName}!'
|
|
48
|
-
},
|
|
49
|
-
...
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
}
|
|
26
|
+
// en.js
|
|
27
|
+
export default {
|
|
28
|
+
"hello": "Hello {firstName}!"
|
|
29
|
+
};
|
|
54
30
|
```
|
|
55
|
-
#### Example 2: Dynamically Imported Resources
|
|
56
|
-
|
|
57
|
-
This approach is better for components with many language resources. By importing them dynamically, only the resources for the requested language are actually fetched and downloaded.
|
|
58
|
-
|
|
59
|
-
Store your resources in individual files, one for each language:
|
|
60
31
|
```javascript
|
|
61
|
-
//
|
|
32
|
+
// fr.js
|
|
62
33
|
export default {
|
|
63
|
-
|
|
34
|
+
"hello": "Bonjour {firstName}!"
|
|
64
35
|
};
|
|
65
36
|
```
|
|
66
37
|
|
|
67
|
-
|
|
38
|
+
Always provide language resources for base languages (e.g. `en`, `fr`, `pt`). That way, if the user prefers a regional language (e.g. `pt-br`) that isn't recognized, it can fall back to the base language (`pt`).
|
|
39
|
+
|
|
40
|
+
## Using `LocalizeMixin`
|
|
41
|
+
|
|
42
|
+
The component should import and extend `LocalizeMixin`:
|
|
68
43
|
|
|
69
|
-
Then create your `localizeConfig` getter:
|
|
70
44
|
```javascript
|
|
71
45
|
import { LocalizeMixin } from '@brightspace-ui/core/mixins/localize/localize-mixin.js';
|
|
72
46
|
|
|
73
47
|
class MyComponent extends LocalizeMixin(LitElement) {
|
|
74
48
|
|
|
75
|
-
static get localizeConfig() {
|
|
76
|
-
return {
|
|
77
|
-
// Import path must be relative
|
|
78
|
-
importFunc: async lang => (await import(`../lang/${lang}.js`)).default,
|
|
79
|
-
// Optionally enable OSLO
|
|
80
|
-
osloCollection: '@d2l\\my-project\\myComponent',
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
49
|
}
|
|
84
50
|
```
|
|
85
|
-
Occasionally, it may be desirable to localize based on the user's browser settings. To do this, add `useBrowserLangs: true` to your `localizeConfig` object. This option should only be used if *all* supported *locales* have corresponding files named with their 4-character locale code, and all supported *languages*, in addition, have 2-character files. (e.g. `en-us.js`, `en-ca.js` and `en.js`)
|
|
86
51
|
|
|
87
|
-
|
|
52
|
+
Implement a static getter for `localizeConfig` that defines `importFunc()`. It will be passed a language which can subsequently be dynamically imported from the location of the component's resources.
|
|
53
|
+
|
|
54
|
+
The dynamic import path must be relative.
|
|
88
55
|
|
|
89
56
|
```javascript
|
|
90
57
|
static get localizeConfig() {
|
|
91
58
|
return {
|
|
92
|
-
importFunc: async lang => {
|
|
93
|
-
switch (lang) {
|
|
94
|
-
case 'en':
|
|
95
|
-
return (await import('./locales/en.js')).default;
|
|
96
|
-
case 'fr':
|
|
97
|
-
return (await import('./locales/fr.js')).default;
|
|
98
|
-
...
|
|
99
|
-
}
|
|
100
|
-
}
|
|
59
|
+
importFunc: async lang => (await import(`../lang/${lang}.js`)).default,
|
|
101
60
|
};
|
|
102
61
|
}
|
|
103
62
|
```
|
|
104
63
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
```javascript
|
|
108
|
-
import { LocalizeCoreElement } from '@brightspace-ui/core/helpers/localize-core-element.js';
|
|
109
|
-
import { LocalizeMixin } from '@brightspace-ui/core/mixins/localize/localize-mixin.js';
|
|
110
|
-
|
|
111
|
-
class MyComponent extends LocalizeMixin(LocalizeCoreElement(LitElement)) {
|
|
112
|
-
...
|
|
113
|
-
}
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
## `localize()`
|
|
64
|
+
### `localize()`
|
|
117
65
|
|
|
118
|
-
|
|
66
|
+
The `localize()` method is used to localize a piece of text in the component's `render()` method.
|
|
119
67
|
|
|
120
|
-
If
|
|
68
|
+
If the localized string contains arguments, pass them as a key-value object as the 2nd parameter:
|
|
121
69
|
|
|
122
70
|
```javascript
|
|
123
71
|
render() {
|
|
124
|
-
|
|
72
|
+
const message = this.localize('hello', { firstName: 'Mary' });
|
|
73
|
+
return html`<p>${message}</p>`;
|
|
125
74
|
}
|
|
126
75
|
```
|
|
127
76
|
|
|
128
|
-
|
|
77
|
+
### `localizeHTML()`
|
|
129
78
|
|
|
130
79
|
Rich formatting can be included in localization resources and safely converted to HTML with the `localizeHTML()` method.
|
|
131
80
|
|
|
132
|
-
|
|
81
|
+
#### Basic Formatting
|
|
133
82
|
|
|
134
83
|
The following formatting elements are supported out-of-the-box:
|
|
135
84
|
|
|
@@ -144,7 +93,19 @@ Remember that `<strong>` is for content of greater importance (browsers show thi
|
|
|
144
93
|
|
|
145
94
|
Similarly `<em>` *emphasizes* a particular piece of text (browsers show this visually using italics), whereas `<i>` only italicizes the text visually without emphasis.
|
|
146
95
|
|
|
147
|
-
|
|
96
|
+
Example:
|
|
97
|
+
|
|
98
|
+
```json
|
|
99
|
+
{
|
|
100
|
+
"myTerm": "This is <b>bold</b> but <em>not</em> all that <strong>important</strong>."
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
```javascript
|
|
105
|
+
this.localizeHTML('myTerm');
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
#### Links
|
|
148
109
|
|
|
149
110
|
To wrap text in [a link](../../components/link/), define a unique tag in the localization resource:
|
|
150
111
|
|
|
@@ -164,7 +125,7 @@ this.localizeHTML('myTerm', {
|
|
|
164
125
|
});
|
|
165
126
|
```
|
|
166
127
|
|
|
167
|
-
|
|
128
|
+
#### Help Tooltips
|
|
168
129
|
|
|
169
130
|
To use a [help tooltip](../../components/tooltip/), define a unique tag in the localization resource in addition to the tooltip's text:
|
|
170
131
|
|
|
@@ -175,7 +136,7 @@ To use a [help tooltip](../../components/tooltip/), define a unique tag in the l
|
|
|
175
136
|
}
|
|
176
137
|
```
|
|
177
138
|
|
|
178
|
-
Then import `generateTooltipHelp` and pass it the tooltip
|
|
139
|
+
Then import `generateTooltipHelp` and pass it the tooltip contents value:
|
|
179
140
|
|
|
180
141
|
```javascript
|
|
181
142
|
import { generateTooltipHelp } from '@brightspace-ui/core/mixins/localize/localize-mixin.js';
|
|
@@ -184,3 +145,38 @@ this.localizeHTML('octopus', {
|
|
|
184
145
|
tooltip: generateTooltipHelp({ contents: this.localize('cephalopodTooltip') })
|
|
185
146
|
});
|
|
186
147
|
```
|
|
148
|
+
|
|
149
|
+
## Off-Stack Language Overrides
|
|
150
|
+
|
|
151
|
+
To enable OSLO, map the project's localization resources to a language collection in Brightspace by defining `osloCollection` in `localizeConfig`:
|
|
152
|
+
|
|
153
|
+
```javascript
|
|
154
|
+
static get localizeConfig() {
|
|
155
|
+
return {
|
|
156
|
+
osloCollection: '@d2l\\my-project\\myComponent',
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
> **Important:** When defining language resource keys, avoid using the Full Stop (`.`) character for grouping. OSLO does not support it.
|
|
162
|
+
|
|
163
|
+
## Advanced
|
|
164
|
+
|
|
165
|
+
### Chaining `LocalizeMixin`
|
|
166
|
+
|
|
167
|
+
If combining `LocalizeMixin` with `LocalizeCoreElement` (or a mixin that uses `LocalizeCoreElement`), `LocalizeMixin` **must** appear before `LocalizeCoreElement` in the chain.
|
|
168
|
+
|
|
169
|
+
```javascript
|
|
170
|
+
import { LocalizeCoreElement } from '@brightspace-ui/core/helpers/localize-core-element.js';
|
|
171
|
+
import { LocalizeMixin } from '@brightspace-ui/core/mixins/localize/localize-mixin.js';
|
|
172
|
+
|
|
173
|
+
class MyComponent extends LocalizeMixin(LocalizeCoreElement(LitElement)) {
|
|
174
|
+
...
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Ignoring the Brightspace Language
|
|
179
|
+
|
|
180
|
+
Occasionally, it may be desirable to localize based on the browser's language instead of the language in Brightspace.
|
|
181
|
+
|
|
182
|
+
To do this, define `useBrowserLangs` as `true` in `localizeConfig`. This option should only be used if all supported locales have corresponding files named with their 4-character and 2-character locale codes (e.g. `en-us.js`, `en-ca.js` and `en.js`).
|
|
@@ -230,31 +230,6 @@ export const LocalizeMixin = superclass => class extends _LocalizeMixinBase(supe
|
|
|
230
230
|
|
|
231
231
|
};
|
|
232
232
|
|
|
233
|
-
export const LocalizeStaticMixin = superclass => class extends _LocalizeMixinBase(superclass) {
|
|
234
|
-
|
|
235
|
-
static async getLocalizeResources(langs) {
|
|
236
|
-
let resolvedLang = fallbackLang;
|
|
237
|
-
const resolvedResources = Object.assign({}, this.resources[fallbackLang]);
|
|
238
|
-
|
|
239
|
-
langs.reverse().forEach((lang) => {
|
|
240
|
-
if (this.resources[lang]) {
|
|
241
|
-
resolvedLang = lang;
|
|
242
|
-
Object.assign(resolvedResources, this.resources[lang]);
|
|
243
|
-
}
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
return {
|
|
247
|
-
language: resolvedLang,
|
|
248
|
-
resources: resolvedResources
|
|
249
|
-
};
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
static get resources() {
|
|
253
|
-
return { 'en': {} };
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
};
|
|
257
|
-
|
|
258
233
|
export const allowedTags = Object.freeze(['d2l-link', 'd2l-tooltip-help', 'p', 'br', 'b', 'strong', 'i', 'em']);
|
|
259
234
|
|
|
260
235
|
const markupError = `localizeHTML() rich-text replacements must use localizeMarkup templates with only the following allowed elements: ${allowedTags}. For more information, see: https://github.com/BrightspaceUI/core/blob/main/mixins/localize/`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brightspace-ui/core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.107.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",
|