@brightspace-ui/core 2.103.0 → 2.104.1
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/README.md +7 -7
- package/components/alert/alert.js +1 -1
- package/components/breadcrumbs/breadcrumb.js +2 -2
- package/components/breadcrumbs/breadcrumbs.js +1 -1
- package/components/button/button-icon.js +3 -3
- package/components/button/button-mixin.js +1 -1
- package/components/button/button-move.js +2 -2
- package/components/button/button-subtle.js +1 -1
- package/components/button/floating-buttons.js +1 -1
- package/components/calendar/calendar.js +1 -1
- package/components/card/card-footer-link.js +2 -2
- package/components/card/card.js +2 -2
- package/components/collapsible-panel/collapsible-panel.js +2 -2
- package/components/count-badge/count-badge-icon.js +1 -1
- package/components/count-badge/count-badge-mixin.js +1 -1
- package/components/count-badge/count-badge.js +1 -1
- package/components/dialog/dialog-mixin.js +1 -1
- package/components/dropdown/dropdown-button.js +1 -1
- package/components/dropdown/dropdown-content-mixin.js +1 -1
- package/components/dropdown/dropdown-context-menu.js +1 -1
- package/components/dropdown/dropdown-menu.js +1 -1
- package/components/dropdown/dropdown-more.js +1 -1
- package/components/empty-state/empty-state-simple.js +1 -1
- package/components/filter/filter-overflow-group.js +1 -1
- package/components/filter/filter.js +2 -2
- package/components/focus-trap/focus-trap.js +1 -1
- package/components/form/form-errory-summary.js +1 -1
- package/components/html-block/demo/html-block.html +1 -1
- package/components/html-block/html-block.js +1 -1
- package/components/icons/icon-custom.js +1 -1
- package/components/icons/icon.js +1 -1
- package/components/inputs/README.md +1 -1
- package/components/inputs/demo/input-radio-label-simple-test.js +1 -1
- package/components/inputs/demo/input-radio-label-test.js +1 -1
- package/components/inputs/demo/input-select-test.js +1 -1
- package/components/inputs/docs/input-radio.md +1 -1
- package/components/inputs/docs/input-select-styles.md +1 -1
- package/components/inputs/input-checkbox-spacer.js +1 -1
- package/components/inputs/input-checkbox.js +2 -2
- package/components/inputs/input-color.js +1 -1
- package/components/inputs/input-date-range.js +2 -2
- package/components/inputs/input-date-time-range.js +2 -2
- package/components/inputs/input-date-time.js +3 -3
- package/components/inputs/input-date.js +2 -2
- package/components/inputs/input-fieldset.js +1 -1
- package/components/inputs/input-number.js +2 -2
- package/components/inputs/input-percent.js +3 -3
- package/components/inputs/input-radio-spacer.js +1 -1
- package/components/inputs/input-search.js +2 -2
- package/components/inputs/input-text.js +3 -3
- package/components/inputs/input-textarea.js +3 -3
- package/components/inputs/input-time-range.js +2 -2
- package/components/inputs/input-time.js +2 -2
- package/components/link/link.js +1 -1
- package/components/list/list-item-drag-handle.js +2 -2
- package/components/list/list-item-drag-image.js +1 -1
- package/components/list/list-item-generic-layout.js +2 -3
- package/components/list/list-item-mixin.js +2 -2
- package/components/list/list-item-placement-marker.js +1 -1
- package/components/menu/menu-item-checkbox.js +1 -1
- package/components/menu/menu-item-radio.js +1 -1
- package/components/menu/menu-item-return.js +1 -1
- package/components/menu/menu.js +1 -1
- package/components/meter/meter-circle.js +1 -1
- package/components/meter/meter-linear.js +1 -1
- package/components/meter/meter-radial.js +1 -1
- package/components/object-property-list/object-property-list-item-link.js +1 -1
- package/components/offscreen/offscreen.js +1 -1
- package/components/overflow-group/overflow-group.js +1 -1
- package/components/paging/pager-load-more.js +1 -1
- package/components/scroll-wrapper/demo/scroll-wrapper-test.js +1 -1
- package/components/scroll-wrapper/scroll-wrapper.js +1 -1
- package/components/selection/selection-action-dropdown.js +1 -1
- package/components/selection/selection-action.js +1 -1
- package/components/selection/selection-controls.js +1 -1
- package/components/selection/selection-input.js +1 -1
- package/components/selection/selection-mixin.js +1 -1
- package/components/selection/selection-select-all-pages.js +1 -1
- package/components/selection/selection-select-all.js +1 -1
- package/components/skeleton/skeleton-mixin.js +1 -1
- package/components/switch/switch-mixin.js +2 -2
- package/components/table/demo/table-test.js +1 -1
- package/components/table/table-col-sort-button.js +1 -1
- package/components/table/table-wrapper.js +1 -1
- package/components/tabs/tab-internal.js +1 -1
- package/components/tabs/tabs.js +2 -2
- package/components/tag-list/tag-list-item-mixin.js +1 -1
- package/components/tag-list/tag-list.js +2 -2
- package/components/tooltip/tooltip-help.js +1 -1
- package/components/tooltip/tooltip.js +1 -1
- package/helpers/demo/prism.html +3 -3
- package/mixins/{arrow-keys-mixin.md → arrow-keys/README.md} +2 -2
- package/mixins/arrow-keys/arrow-keys-mixin.js +125 -0
- package/mixins/{demo → arrow-keys/demo}/arrow-keys-mixin.html +2 -2
- package/mixins/{demo → arrow-keys/demo}/arrow-keys-test.js +1 -1
- package/mixins/arrow-keys-mixin.js +1 -125
- package/mixins/{focus-mixin.md → focus/README.md} +1 -1
- package/mixins/focus/focus-mixin.js +43 -0
- package/mixins/focus-mixin.js +1 -43
- package/mixins/{interactive-mixin.md → interactive/README.md} +1 -1
- package/mixins/{interactive-mixin.js → interactive/interactive-mixin.js} +4 -4
- package/mixins/{labelled-mixin.md → labelled/README.md} +2 -2
- package/mixins/{demo → labelled/demo}/labelled-mixin.html +3 -3
- package/mixins/labelled/labelled-mixin.js +215 -0
- package/mixins/labelled-mixin.js +1 -215
- package/mixins/localize/localize-mixin.js +1 -1
- package/mixins/{provider-mixin.md → provider/README.md} +3 -3
- package/mixins/provider/provider-mixin.js +35 -0
- package/mixins/provider-mixin.js +1 -35
- package/mixins/{rtl-mixin.md → rtl/README.md} +1 -1
- package/mixins/rtl/rtl-mixin.js +40 -0
- package/mixins/rtl-mixin.js +1 -40
- package/mixins/theme/theme-mixin.js +19 -0
- package/mixins/theme-mixin.js +1 -19
- package/mixins/{visible-on-ancestor-mixin.md → visible-on-ancestor/README.md} +1 -1
- package/mixins/visible-on-ancestor/visible-on-ancestor-mixin.js +160 -0
- package/mixins/visible-on-ancestor-mixin.js +1 -160
- package/package.json +1 -1
- package/templates/primary-secondary/primary-secondary.js +1 -1
package/mixins/labelled-mixin.js
CHANGED
|
@@ -1,215 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { cssEscape } from '../helpers/dom.js';
|
|
3
|
-
|
|
4
|
-
const getCommonAncestor = (elem1, elem2) => {
|
|
5
|
-
|
|
6
|
-
const labelledPath = new WeakMap();
|
|
7
|
-
let elem = elem1;
|
|
8
|
-
while (elem) {
|
|
9
|
-
labelledPath.set(elem, elem);
|
|
10
|
-
elem = elem.parentNode;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
let ancestorElem = elem2.parentNode;
|
|
14
|
-
while (ancestorElem) {
|
|
15
|
-
if (labelledPath.has(ancestorElem)) return ancestorElem;
|
|
16
|
-
ancestorElem = ancestorElem.parentNode;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
const isCustomElement = elem => {
|
|
22
|
-
return elem.tagName.includes('-');
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
const getLabel = labelElem => {
|
|
26
|
-
if (isCustomElement(labelElem)) return labelElem._label;
|
|
27
|
-
else return labelElem.textContent;
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
const waitForElement = async(contextElement, selector, timeout) => {
|
|
31
|
-
let elem = contextElement.querySelector(selector);
|
|
32
|
-
if (elem) return elem;
|
|
33
|
-
|
|
34
|
-
return new Promise(resolve => {
|
|
35
|
-
let elapsedTime = 0;
|
|
36
|
-
const intervalId = setInterval(() => {
|
|
37
|
-
elem = contextElement.querySelector(selector);
|
|
38
|
-
if (!elem) elapsedTime += 100;
|
|
39
|
-
if (elem || elapsedTime > timeout) {
|
|
40
|
-
clearInterval(intervalId);
|
|
41
|
-
resolve(elem);
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
}, 100);
|
|
45
|
-
});
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
export const LabelMixin = superclass => class extends superclass {
|
|
49
|
-
|
|
50
|
-
static get properties() {
|
|
51
|
-
return {
|
|
52
|
-
_label: { type: String, reflect: true }
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
connectedCallback() {
|
|
57
|
-
super.connectedCallback();
|
|
58
|
-
this.addEventListener('d2l-label-change', this._handleLabelChange);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
disconnectedCallback() {
|
|
62
|
-
super.disconnectedCallback();
|
|
63
|
-
this.removeEventListener('d2l-label-change', this._handleLabelChange);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
updateLabel(text) {
|
|
67
|
-
this._label = text;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
_handleLabelChange(e) {
|
|
71
|
-
e.stopPropagation();
|
|
72
|
-
this.updateLabel(e.detail);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
export const LabelledMixin = superclass => class extends superclass {
|
|
78
|
-
|
|
79
|
-
static get properties() {
|
|
80
|
-
return {
|
|
81
|
-
/**
|
|
82
|
-
* The id of element that provides the label for this element
|
|
83
|
-
* @type {string}
|
|
84
|
-
*/
|
|
85
|
-
labelledBy: { type: String, reflect: true, attribute: 'labelled-by' },
|
|
86
|
-
/**
|
|
87
|
-
* REQUIRED: Explicitly defined label for the element
|
|
88
|
-
* @type {string}
|
|
89
|
-
*/
|
|
90
|
-
label: { type: String }
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
constructor() {
|
|
95
|
-
super();
|
|
96
|
-
this.labelRequired = true;
|
|
97
|
-
this._labelElem = null;
|
|
98
|
-
this._missingLabelErrorHasBeenThrown = false;
|
|
99
|
-
this._validatingLabelTimeout = null;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
firstUpdated(changedProperties) {
|
|
103
|
-
super.firstUpdated(changedProperties);
|
|
104
|
-
this._validateLabel(); // need to check this even if "label" isn't updated in case it's never set
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
async updated(changedProperties) {
|
|
108
|
-
|
|
109
|
-
super.updated(changedProperties);
|
|
110
|
-
|
|
111
|
-
if (changedProperties.has('label')) this._validateLabel();
|
|
112
|
-
|
|
113
|
-
if (!changedProperties.has('labelledBy')) return;
|
|
114
|
-
|
|
115
|
-
if (!this.labelledBy) {
|
|
116
|
-
this._updateLabelElem(null);
|
|
117
|
-
} else {
|
|
118
|
-
const labelElem = await waitForElement(this.getRootNode(), `#${cssEscape(this.labelledBy)}`, 3000);
|
|
119
|
-
if (!labelElem) {
|
|
120
|
-
this._throwError(
|
|
121
|
-
new Error(`LabelledMixin: "${this.tagName.toLowerCase()}" is labelled-by="${this.labelledBy}", but no such element exists`)
|
|
122
|
-
);
|
|
123
|
-
}
|
|
124
|
-
this._updateLabelElem(labelElem);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
_throwError(err) {
|
|
130
|
-
if (!this.labelRequired || this._missingLabelErrorHasBeenThrown) return;
|
|
131
|
-
this._missingLabelErrorHasBeenThrown = true;
|
|
132
|
-
setTimeout(() => { throw err; }); // we don't want to prevent rendering
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
_updateLabelElem(labelElem) {
|
|
136
|
-
|
|
137
|
-
// setting textContent doesn't change labelElem but we do need to refetch the label
|
|
138
|
-
if (labelElem === this._labelElem && this._labelElem) {
|
|
139
|
-
this.label = getLabel(this._labelElem);
|
|
140
|
-
return;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
this._labelElem = labelElem;
|
|
144
|
-
|
|
145
|
-
if (this._labelObserver) this._labelObserver.disconnect();
|
|
146
|
-
if (!this._labelElem) {
|
|
147
|
-
this.label = undefined;
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
this._labelObserver = new MutationObserver(() => {
|
|
152
|
-
const newElem = this.getRootNode().querySelector(`#${cssEscape(this.labelledBy)}`);
|
|
153
|
-
if (isCustomElement(newElem)) {
|
|
154
|
-
requestAnimationFrame(() => {
|
|
155
|
-
// element often sets its label in its own updated(), so we need to wait
|
|
156
|
-
this._updateLabelElem(newElem);
|
|
157
|
-
});
|
|
158
|
-
} else {
|
|
159
|
-
this._updateLabelElem(newElem);
|
|
160
|
-
}
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
const ancestor = getCommonAncestor(this, this._labelElem);
|
|
164
|
-
|
|
165
|
-
// assumption: the labelling element will not change from a native to a custom element
|
|
166
|
-
// or vice versa, which allows the use of a more optimal observer configuration
|
|
167
|
-
if (isCustomElement(this._labelElem)) {
|
|
168
|
-
this._labelObserver.observe(ancestor, {
|
|
169
|
-
attributes: true, // required for legacy-Edge, otherwise attributeFilter throws a syntax error
|
|
170
|
-
attributeFilter: ['_label'],
|
|
171
|
-
childList: true,
|
|
172
|
-
subtree: true
|
|
173
|
-
});
|
|
174
|
-
} else {
|
|
175
|
-
this._labelObserver.observe(ancestor, {
|
|
176
|
-
characterData: true,
|
|
177
|
-
childList: true,
|
|
178
|
-
subtree: true
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
this.label = getLabel(this._labelElem);
|
|
183
|
-
/** @ignore */
|
|
184
|
-
this.dispatchEvent(new CustomEvent(
|
|
185
|
-
'd2l-labelled-mixin-label-elem-change', {
|
|
186
|
-
bubbles: false,
|
|
187
|
-
composed: false
|
|
188
|
-
}
|
|
189
|
-
));
|
|
190
|
-
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
_validateLabel() {
|
|
194
|
-
clearTimeout(this._validatingLabelTimeout);
|
|
195
|
-
// don't error immediately in case it doesn't get set immediately
|
|
196
|
-
this._validatingLabelTimeout = setTimeout(() => {
|
|
197
|
-
this._validatingLabelTimeout = null;
|
|
198
|
-
const hasLabel = (typeof this.label === 'string') && this.label.length > 0;
|
|
199
|
-
if (!hasLabel) {
|
|
200
|
-
if (this.labelledBy) {
|
|
201
|
-
if (this._labelElem) {
|
|
202
|
-
this._throwError(
|
|
203
|
-
new Error(`LabelledMixin: "${this.tagName.toLowerCase()}" is labelled-by="${this.labelledBy}", but its label is empty`)
|
|
204
|
-
);
|
|
205
|
-
}
|
|
206
|
-
} else {
|
|
207
|
-
this._throwError(
|
|
208
|
-
new Error(`LabelledMixin: "${this.tagName.toLowerCase()}" is missing a required "label" attribute`)
|
|
209
|
-
);
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
}, 3000);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
};
|
|
1
|
+
export { LabelMixin, LabelledMixin } from './labelled/labelled-mixin.js';
|
|
@@ -106,7 +106,7 @@ export const _LocalizeMixinBase = dedupeMixin(superclass => class LocalizeMixinC
|
|
|
106
106
|
if (!value) return '';
|
|
107
107
|
|
|
108
108
|
let params = {};
|
|
109
|
-
if (arguments.length > 1 && arguments[1]
|
|
109
|
+
if (arguments.length > 1 && arguments[1]?.constructor === Object) {
|
|
110
110
|
// support for key-value replacements as a single arg
|
|
111
111
|
params = arguments[1];
|
|
112
112
|
} else {
|
|
@@ -7,7 +7,7 @@ The `ProviderMixin` and `RequesterMixin` can be used to create a DI-like system
|
|
|
7
7
|
Apply the `ProviderMixin` to the component that will be responsible for providing some data to components that request it:
|
|
8
8
|
|
|
9
9
|
```js
|
|
10
|
-
import { ProviderMixin } from '@brightspace-ui/core/mixins/provider-mixin.js';
|
|
10
|
+
import { ProviderMixin } from '@brightspace-ui/core/mixins/provider/provider-mixin.js';
|
|
11
11
|
|
|
12
12
|
class InterestingFactProvider extends ProviderMixin(LitElement) {
|
|
13
13
|
constructor() {
|
|
@@ -24,7 +24,7 @@ Once this has been set up, child components can request your provider's data via
|
|
|
24
24
|
NB: due to its reliance on DOM events, `requestInstance()` needs to be called after the element has been attached to the DOM, such as in `connectedCallback()`.
|
|
25
25
|
|
|
26
26
|
```js
|
|
27
|
-
import { RequesterMixin } from '@brightspace-ui/core/mixins/provider-mixin.js'
|
|
27
|
+
import { RequesterMixin } from '@brightspace-ui/core/mixins/provider/provider-mixin.js'
|
|
28
28
|
|
|
29
29
|
class InterestingFactUI extends RequesterMixin(LitElement) {
|
|
30
30
|
static get properties() {
|
|
@@ -55,7 +55,7 @@ class InterestingFactUI extends RequesterMixin(LitElement) {
|
|
|
55
55
|
In the absence of a component context, the `requestInstance` helper may be used by providing the `node` context and the `key` for the instance.
|
|
56
56
|
|
|
57
57
|
```js
|
|
58
|
-
import { requestInstance } from '@brightspace-ui/core/mixins/provider-mixin.js'
|
|
58
|
+
import { requestInstance } from '@brightspace-ui/core/mixins/provider/provider-mixin.js'
|
|
59
59
|
|
|
60
60
|
const factString = requestInstance(node, 'd2l-interesting-fact-string');
|
|
61
61
|
const factObjectString = requestInstance(node, 'd2l-interesting-fact-object').fact;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export function provideInstance(node, key, obj) {
|
|
2
|
+
if (!node._providerInstances) {
|
|
3
|
+
node._providerInstances = new Map();
|
|
4
|
+
node.addEventListener('d2l-request-instance', e => {
|
|
5
|
+
if (node._providerInstances.has(e.detail.key)) {
|
|
6
|
+
e.detail.instance = node._providerInstances.get(e.detail.key);
|
|
7
|
+
e.stopPropagation();
|
|
8
|
+
}
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
node._providerInstances.set(key, obj);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const ProviderMixin = superclass => class extends superclass {
|
|
15
|
+
provideInstance(key, obj) {
|
|
16
|
+
provideInstance(this, key, obj);
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export function requestInstance(node, key) {
|
|
21
|
+
const event = new CustomEvent('d2l-request-instance', {
|
|
22
|
+
detail: { key },
|
|
23
|
+
bubbles: true,
|
|
24
|
+
composed: true,
|
|
25
|
+
cancelable: true
|
|
26
|
+
});
|
|
27
|
+
node.dispatchEvent(event);
|
|
28
|
+
return event.detail.instance;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const RequesterMixin = superclass => class extends superclass {
|
|
32
|
+
requestInstance(key) {
|
|
33
|
+
return requestInstance(this, key);
|
|
34
|
+
}
|
|
35
|
+
};
|
package/mixins/provider-mixin.js
CHANGED
|
@@ -1,35 +1 @@
|
|
|
1
|
-
export
|
|
2
|
-
if (!node._providerInstances) {
|
|
3
|
-
node._providerInstances = new Map();
|
|
4
|
-
node.addEventListener('d2l-request-instance', e => {
|
|
5
|
-
if (node._providerInstances.has(e.detail.key)) {
|
|
6
|
-
e.detail.instance = node._providerInstances.get(e.detail.key);
|
|
7
|
-
e.stopPropagation();
|
|
8
|
-
}
|
|
9
|
-
});
|
|
10
|
-
}
|
|
11
|
-
node._providerInstances.set(key, obj);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export const ProviderMixin = superclass => class extends superclass {
|
|
15
|
-
provideInstance(key, obj) {
|
|
16
|
-
provideInstance(this, key, obj);
|
|
17
|
-
}
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
export function requestInstance(node, key) {
|
|
21
|
-
const event = new CustomEvent('d2l-request-instance', {
|
|
22
|
-
detail: { key },
|
|
23
|
-
bubbles: true,
|
|
24
|
-
composed: true,
|
|
25
|
-
cancelable: true
|
|
26
|
-
});
|
|
27
|
-
node.dispatchEvent(event);
|
|
28
|
-
return event.detail.instance;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export const RequesterMixin = superclass => class extends superclass {
|
|
32
|
-
requestInstance(key) {
|
|
33
|
-
return requestInstance(this, key);
|
|
34
|
-
}
|
|
35
|
-
};
|
|
1
|
+
export { provideInstance, ProviderMixin, requestInstance, RequesterMixin } from './provider/provider-mixin.js';
|
|
@@ -7,7 +7,7 @@ The `RtlMixin` creates `dir` attributes on host elements based on the document's
|
|
|
7
7
|
Apply the mixin and define RTL styles.
|
|
8
8
|
|
|
9
9
|
```js
|
|
10
|
-
import { RtlMixin } from '@brightspace-ui/core/mixins/rtl-mixin.js';
|
|
10
|
+
import { RtlMixin } from '@brightspace-ui/core/mixins/rtl/rtl-mixin.js';
|
|
11
11
|
class MyComponent extends RtlMixin(LitElement) {
|
|
12
12
|
static get styles() {
|
|
13
13
|
return css`
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { dedupeMixin } from '@open-wc/dedupe-mixin';
|
|
2
|
+
import { getDocumentLocaleSettings } from '@brightspace-ui/intl/lib/common.js';
|
|
3
|
+
|
|
4
|
+
export const RtlMixin = dedupeMixin(superclass => class extends superclass {
|
|
5
|
+
|
|
6
|
+
static get properties() {
|
|
7
|
+
return {
|
|
8
|
+
/**
|
|
9
|
+
* @ignore
|
|
10
|
+
*/
|
|
11
|
+
dir: { type: String, reflect: true }
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
constructor() {
|
|
16
|
+
super();
|
|
17
|
+
this._localeSettings = getDocumentLocaleSettings();
|
|
18
|
+
this._handleLanguageChange = this._handleLanguageChange.bind(this);
|
|
19
|
+
this._handleLanguageChange();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
connectedCallback() {
|
|
23
|
+
super.connectedCallback();
|
|
24
|
+
this._localeSettings.addChangeListener(this._handleLanguageChange);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
disconnectedCallback() {
|
|
28
|
+
super.disconnectedCallback();
|
|
29
|
+
this._localeSettings.removeChangeListener(this._handleLanguageChange);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
_handleLanguageChange() {
|
|
33
|
+
const dir = document.documentElement.getAttribute('dir');
|
|
34
|
+
// avoid reflecting "ltr" for better performance
|
|
35
|
+
if (dir && (dir !== 'ltr' || this.dir === 'rtl')) {
|
|
36
|
+
this.dir = dir;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
});
|
package/mixins/rtl-mixin.js
CHANGED
|
@@ -1,40 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { getDocumentLocaleSettings } from '@brightspace-ui/intl/lib/common.js';
|
|
3
|
-
|
|
4
|
-
export const RtlMixin = dedupeMixin(superclass => class extends superclass {
|
|
5
|
-
|
|
6
|
-
static get properties() {
|
|
7
|
-
return {
|
|
8
|
-
/**
|
|
9
|
-
* @ignore
|
|
10
|
-
*/
|
|
11
|
-
dir: { type: String, reflect: true }
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
constructor() {
|
|
16
|
-
super();
|
|
17
|
-
this._localeSettings = getDocumentLocaleSettings();
|
|
18
|
-
this._handleLanguageChange = this._handleLanguageChange.bind(this);
|
|
19
|
-
this._handleLanguageChange();
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
connectedCallback() {
|
|
23
|
-
super.connectedCallback();
|
|
24
|
-
this._localeSettings.addChangeListener(this._handleLanguageChange);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
disconnectedCallback() {
|
|
28
|
-
super.disconnectedCallback();
|
|
29
|
-
this._localeSettings.removeChangeListener(this._handleLanguageChange);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
_handleLanguageChange() {
|
|
33
|
-
const dir = document.documentElement.getAttribute('dir');
|
|
34
|
-
// avoid reflecting "ltr" for better performance
|
|
35
|
-
if (dir && (dir !== 'ltr' || this.dir === 'rtl')) {
|
|
36
|
-
this.dir = dir;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
});
|
|
1
|
+
export { RtlMixin } from './rtl/rtl-mixin.js';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This is a draft mixin that may eventually be extended to support
|
|
3
|
+
* themed components, including "dark mode". At that point, the
|
|
4
|
+
* "theme" attribute could resolve automatically based on the user's
|
|
5
|
+
* OS preference. For now, it's only used in menus/dropdowns by
|
|
6
|
+
* the media player.
|
|
7
|
+
*/
|
|
8
|
+
export const ThemeMixin = superclass => class extends superclass {
|
|
9
|
+
|
|
10
|
+
static get properties() {
|
|
11
|
+
return {
|
|
12
|
+
/**
|
|
13
|
+
* @ignore
|
|
14
|
+
*/
|
|
15
|
+
theme: { reflect: true, type: String }
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
};
|
package/mixins/theme-mixin.js
CHANGED
|
@@ -1,19 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* This is a draft mixin that may eventually be extended to support
|
|
3
|
-
* themed components, including "dark mode". At that point, the
|
|
4
|
-
* "theme" attribute could resolve automatically based on the user's
|
|
5
|
-
* OS preference. For now, it's only used in menus/dropdowns by
|
|
6
|
-
* the media player.
|
|
7
|
-
*/
|
|
8
|
-
export const ThemeMixin = superclass => class extends superclass {
|
|
9
|
-
|
|
10
|
-
static get properties() {
|
|
11
|
-
return {
|
|
12
|
-
/**
|
|
13
|
-
* @ignore
|
|
14
|
-
*/
|
|
15
|
-
theme: { reflect: true, type: String }
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
};
|
|
1
|
+
export { ThemeMixin } from './theme/theme-mixin.js';
|
|
@@ -7,7 +7,7 @@ The `VisibleOnAncestorMixin` adds a behavior to a component so that it is initia
|
|
|
7
7
|
Apply the mixin and include the required `visibleOnAncestorStyles`.
|
|
8
8
|
|
|
9
9
|
```js
|
|
10
|
-
import { VisibleOnAncestorMixin, visibleOnAncestorStyles } from '@brightspace-ui/core/mixins/visible-on-ancestor-mixin.js';
|
|
10
|
+
import { VisibleOnAncestorMixin, visibleOnAncestorStyles } from '@brightspace-ui/core/mixins/visible-on-ancestor/visible-on-ancestor-mixin.js';
|
|
11
11
|
class MyComponent extends VisibleOnAncestorMixin(LitElement) {
|
|
12
12
|
static get styles() {
|
|
13
13
|
return [ visibleOnAncestorStyles, css`/* MyComponent styles */` ];
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { findComposedAncestor, isComposedAncestor } from '../../helpers/dom.js';
|
|
2
|
+
import { css } from 'lit';
|
|
3
|
+
|
|
4
|
+
const reduceMotion = matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
5
|
+
|
|
6
|
+
export const visibleOnAncestorStyles = css`
|
|
7
|
+
|
|
8
|
+
:host([__voa-state="hidden"]),
|
|
9
|
+
:host([__voa-state="hiding"]) {
|
|
10
|
+
opacity: 0 !important;
|
|
11
|
+
transform: translateY(-10px) !important;
|
|
12
|
+
}
|
|
13
|
+
:host([__voa-state="showing"]),
|
|
14
|
+
:host([__voa-state="hiding"]) {
|
|
15
|
+
transition: transform 200ms ease-out, opacity 200ms ease-out !important;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@media only screen and (hover: none), only screen and (-moz-touch-enabled: 1) {
|
|
19
|
+
:host([__voa-state="hidden"]),
|
|
20
|
+
:host([__voa-state="hiding"]) {
|
|
21
|
+
opacity: 1 !important;
|
|
22
|
+
transform: translateY(0) !important;
|
|
23
|
+
}
|
|
24
|
+
:host([__voa-state="hidden"][d2l-visible-on-ancestor-no-hover-hide]),
|
|
25
|
+
:host([__voa-state="hiding"][d2l-visible-on-ancestor-no-hover-hide]) {
|
|
26
|
+
opacity: 0 !important;
|
|
27
|
+
transform: translateY(-10px) !important;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
`;
|
|
32
|
+
|
|
33
|
+
export const VisibleOnAncestorMixin = superclass => class extends superclass {
|
|
34
|
+
|
|
35
|
+
static get properties() {
|
|
36
|
+
return {
|
|
37
|
+
/**
|
|
38
|
+
* @ignore
|
|
39
|
+
*/
|
|
40
|
+
visibleOnAncestor: { type: Boolean, reflect: true, attribute: 'visible-on-ancestor' },
|
|
41
|
+
__voaState: { type: String, reflect: true, attribute: '__voa-state' }
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
constructor() {
|
|
46
|
+
super();
|
|
47
|
+
|
|
48
|
+
this.visibleOnAncestor = false;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
attributeChangedCallback(name, oldval, newval) {
|
|
52
|
+
if (name === 'visible-on-ancestor' && this.__voaAttached) {
|
|
53
|
+
if (newval) this.__voaInit();
|
|
54
|
+
else this.__voaUninit();
|
|
55
|
+
}
|
|
56
|
+
super.attributeChangedCallback(name, oldval, newval);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
connectedCallback() {
|
|
60
|
+
super.connectedCallback();
|
|
61
|
+
this.__voaAttached = true;
|
|
62
|
+
if (this.visibleOnAncestor) {
|
|
63
|
+
requestAnimationFrame(() => this.__voaInit());
|
|
64
|
+
} else this.__voaState = null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
disconnectedCallback() {
|
|
68
|
+
this.__voaAttached = false;
|
|
69
|
+
this.__voaUninit();
|
|
70
|
+
super.disconnectedCallback();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
__voaHandleBlur(e) {
|
|
74
|
+
if (isComposedAncestor(this.__voaTarget, e.relatedTarget)) return;
|
|
75
|
+
this.__voaFocusIn = false;
|
|
76
|
+
this.__voaHide();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
__voaHandleFocus() {
|
|
80
|
+
this.__voaFocusIn = true;
|
|
81
|
+
this.__voaShow();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
__voaHandleMouseEnter() {
|
|
85
|
+
this.__voaMouseOver = true;
|
|
86
|
+
this.__voaShow();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
__voaHandleMouseLeave() {
|
|
90
|
+
this.__voaMouseOver = false;
|
|
91
|
+
this.__voaHide();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
__voaHide() {
|
|
95
|
+
if (this.__voaFocusIn || this.__voaMouseOver) return;
|
|
96
|
+
if (reduceMotion) {
|
|
97
|
+
this.__voaState = 'hidden';
|
|
98
|
+
} else {
|
|
99
|
+
const handleTransitionEnd = (e) => {
|
|
100
|
+
if (e.propertyName !== 'transform') return;
|
|
101
|
+
this.removeEventListener('transitionend', handleTransitionEnd);
|
|
102
|
+
this.__voaState = 'hidden';
|
|
103
|
+
};
|
|
104
|
+
this.addEventListener('transitionend', handleTransitionEnd);
|
|
105
|
+
this.__voaState = 'hiding';
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
__voaInit() {
|
|
110
|
+
|
|
111
|
+
if (!this.visibleOnAncestor) return;
|
|
112
|
+
|
|
113
|
+
this.__voaTarget = findComposedAncestor(this, (node) => {
|
|
114
|
+
if (!node || node.nodeType !== 1) return false;
|
|
115
|
+
return (node.classList.contains('d2l-visible-on-ancestor-target'));
|
|
116
|
+
});
|
|
117
|
+
if (!this.__voaTarget) {
|
|
118
|
+
this.__voaState = null;
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
this.__voaHandleBlur = this.__voaHandleBlur.bind(this);
|
|
123
|
+
this.__voaHandleFocus = this.__voaHandleFocus.bind(this);
|
|
124
|
+
this.__voaHandleMouseEnter = this.__voaHandleMouseEnter.bind(this);
|
|
125
|
+
this.__voaHandleMouseLeave = this.__voaHandleMouseLeave.bind(this);
|
|
126
|
+
|
|
127
|
+
this.__voaTarget.addEventListener('focus', this.__voaHandleFocus, true);
|
|
128
|
+
this.__voaTarget.addEventListener('blur', this.__voaHandleBlur, true);
|
|
129
|
+
this.__voaTarget.addEventListener('mouseenter', this.__voaHandleMouseEnter);
|
|
130
|
+
this.__voaTarget.addEventListener('mouseleave', this.__voaHandleMouseLeave);
|
|
131
|
+
|
|
132
|
+
this.__voaState = 'hidden';
|
|
133
|
+
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
__voaShow() {
|
|
137
|
+
if (reduceMotion) {
|
|
138
|
+
this.__voaState = 'shown';
|
|
139
|
+
} else {
|
|
140
|
+
const handleTransitionEnd = (e) => {
|
|
141
|
+
if (e.propertyName !== 'transform') return;
|
|
142
|
+
this.removeEventListener('transitionend', handleTransitionEnd);
|
|
143
|
+
this.__voaState = 'shown';
|
|
144
|
+
};
|
|
145
|
+
this.addEventListener('transitionend', handleTransitionEnd);
|
|
146
|
+
this.__voaState = 'showing';
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
__voaUninit() {
|
|
151
|
+
this.__voaState = null;
|
|
152
|
+
if (!this.__voaTarget) return;
|
|
153
|
+
this.__voaTarget.removeEventListener('focus', this.__voaHandleFocus, true);
|
|
154
|
+
this.__voaTarget.removeEventListener('blur', this.__voaHandleBlur, true);
|
|
155
|
+
this.__voaTarget.removeEventListener('mouseenter', this.__voaHandleMouseEnter);
|
|
156
|
+
this.__voaTarget.removeEventListener('mouseleave', this.__voaHandleMouseLeave);
|
|
157
|
+
this.__voaTarget = null;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
};
|