@brightspace-ui/core 3.320.2 → 3.321.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.
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { html, LitElement } from 'lit';
|
|
2
|
+
import { FocusMixin } from '../../mixins/focus/focus-mixin.js';
|
|
3
|
+
import { PropertyRequiredMixin } from '../../mixins/property-required/property-required-mixin.js';
|
|
4
|
+
import { SlottedIconMixin } from '../icons/slotted-icon-mixin.js';
|
|
5
|
+
import { ThemeMixin } from '../../mixins/theme/theme-mixin.js';
|
|
6
|
+
import { toolbarButtonStyles } from './toolbar-item-styles.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A toolbar button that performs an action
|
|
10
|
+
*/
|
|
11
|
+
class ToolbarButton extends SlottedIconMixin(FocusMixin(ThemeMixin(PropertyRequiredMixin(LitElement)))) {
|
|
12
|
+
|
|
13
|
+
static properties = {
|
|
14
|
+
/**
|
|
15
|
+
* Disables the toolbar button
|
|
16
|
+
* @type {boolean}
|
|
17
|
+
*/
|
|
18
|
+
disabled: { type: Boolean },
|
|
19
|
+
/**
|
|
20
|
+
* ACCESSIBILITY: REQUIRED: Accessible text for the button
|
|
21
|
+
* @type {string}
|
|
22
|
+
*/
|
|
23
|
+
text: { type: String, required: true },
|
|
24
|
+
_activeFocusable: { state: true }
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
static styles = [super.styles, toolbarButtonStyles];
|
|
28
|
+
|
|
29
|
+
constructor() {
|
|
30
|
+
super();
|
|
31
|
+
this.disabled = false;
|
|
32
|
+
this.text = '';
|
|
33
|
+
this._activeFocusable = false;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
static get focusElementSelector() {
|
|
37
|
+
return 'button';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
render() {
|
|
41
|
+
return html`
|
|
42
|
+
<button
|
|
43
|
+
aria-disabled="${this.disabled ? 'true' : 'false'}"
|
|
44
|
+
aria-label="${this.text}"
|
|
45
|
+
@click="${this.#handleClick}"
|
|
46
|
+
tabindex="${this._activeFocusable ? 0 : -1}"
|
|
47
|
+
title="${this.text}"
|
|
48
|
+
type="button">
|
|
49
|
+
<div class="background"></div>
|
|
50
|
+
${this._renderIcon()}
|
|
51
|
+
</button>
|
|
52
|
+
`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
#handleClick(e) {
|
|
56
|
+
if (this.disabled) e.stopPropagation();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
customElements.define('d2l-toolbar-button', ToolbarButton);
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import '../colors/colors.js';
|
|
2
|
+
import { css } from 'lit';
|
|
3
|
+
import { getFocusRingStyles } from '../../helpers/focus.js';
|
|
4
|
+
|
|
5
|
+
export const toolbarButtonStyles = css`
|
|
6
|
+
:host {
|
|
7
|
+
display: inline-block;
|
|
8
|
+
}
|
|
9
|
+
:host([hidden]) {
|
|
10
|
+
display: none;
|
|
11
|
+
}
|
|
12
|
+
button {
|
|
13
|
+
--d2l-focus-ring-offset: 0;
|
|
14
|
+
align-items: center;
|
|
15
|
+
background-color: transparent;
|
|
16
|
+
border: none;
|
|
17
|
+
border-radius: 0.2rem;
|
|
18
|
+
box-sizing: border-box;
|
|
19
|
+
color: var(--d2l-theme-text-color-static-standard);
|
|
20
|
+
cursor: pointer;
|
|
21
|
+
display: flex;
|
|
22
|
+
fill: var(--d2l-theme-icon-color-standard);
|
|
23
|
+
font-family: inherit;
|
|
24
|
+
justify-content: center;
|
|
25
|
+
margin: 0;
|
|
26
|
+
min-height: 34px;
|
|
27
|
+
min-width: 34px;
|
|
28
|
+
outline: none;
|
|
29
|
+
padding: 0;
|
|
30
|
+
position: relative;
|
|
31
|
+
text-align: center;
|
|
32
|
+
-webkit-user-select: none;
|
|
33
|
+
-moz-user-select: none;
|
|
34
|
+
-ms-user-select: none;
|
|
35
|
+
user-select: none;
|
|
36
|
+
vertical-align: middle;
|
|
37
|
+
white-space: nowrap;
|
|
38
|
+
width: auto;
|
|
39
|
+
z-index: 0; /* create stacking context for the background so it can be animated */
|
|
40
|
+
}
|
|
41
|
+
button::-moz-focus-inner {
|
|
42
|
+
border: 0;
|
|
43
|
+
}
|
|
44
|
+
.background {
|
|
45
|
+
background-color: transparent;
|
|
46
|
+
border-radius: 4px;
|
|
47
|
+
height: 100%;
|
|
48
|
+
position: absolute;
|
|
49
|
+
transform: scale(0.1, 0.1);
|
|
50
|
+
transition: background-color 150ms linear, transform 150ms linear;
|
|
51
|
+
width: 100%;
|
|
52
|
+
z-index: -1;
|
|
53
|
+
}
|
|
54
|
+
${getFocusRingStyles('button')}
|
|
55
|
+
@supports not selector(:focus-visible) {
|
|
56
|
+
button:hover > .background,
|
|
57
|
+
button:focus > .background {
|
|
58
|
+
background-color: var(--d2l-theme-background-color-interactive-secondary-default);
|
|
59
|
+
transform: scale(1, 1);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
@supports selector(:focus-visible) {
|
|
63
|
+
button:hover > .background,
|
|
64
|
+
button:focus-visible > .background {
|
|
65
|
+
background-color: var(--d2l-theme-background-color-interactive-secondary-default);
|
|
66
|
+
transform: scale(1, 1);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
button[disabled],
|
|
70
|
+
button[aria-disabled="true"] {
|
|
71
|
+
cursor: default;
|
|
72
|
+
opacity: var(--d2l-theme-opacity-disabled-control);
|
|
73
|
+
}
|
|
74
|
+
button[disabled]:hover,
|
|
75
|
+
button[disabled]:focus {
|
|
76
|
+
background-color: transparent;
|
|
77
|
+
fill: var(--d2l-theme-text-color-static-standard);
|
|
78
|
+
}
|
|
79
|
+
@media (prefers-reduced-motion: reduce) {
|
|
80
|
+
.background {
|
|
81
|
+
transition: none;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
@supports not selector(:focus-visible) {
|
|
86
|
+
:host([theme="dark"]) button:hover > .background,
|
|
87
|
+
:host([theme="dark"]) button:focus > .background {
|
|
88
|
+
background-color: var(--d2l-color-tungsten);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
@supports selector(:focus-visible) {
|
|
92
|
+
:host([theme="dark"]) button:hover > .background,
|
|
93
|
+
:host([theme="dark"]) button:focus-visible > .background {
|
|
94
|
+
background-color: var(--d2l-color-tungsten);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
:host([theme="dark"]) button,
|
|
98
|
+
:host([theme="dark"]) ::slotted(d2l-icon-custom),
|
|
99
|
+
:host([theme="dark"]) d2l-icon {
|
|
100
|
+
color: var(--d2l-color-regolith);
|
|
101
|
+
fill: var(--d2l-color-regolith);
|
|
102
|
+
}
|
|
103
|
+
`;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import '../colors/colors.js';
|
|
2
|
+
import { createRef, ref } from 'lit/directives/ref.js';
|
|
3
|
+
import { css, html, LitElement } from 'lit';
|
|
4
|
+
import { PropertyRequiredMixin } from '../../mixins/property-required/property-required-mixin.js';
|
|
5
|
+
|
|
6
|
+
const keyCodes = Object.freeze({
|
|
7
|
+
END: 35,
|
|
8
|
+
HOME: 36,
|
|
9
|
+
LEFT: 37,
|
|
10
|
+
RIGHT: 39
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* A toolbar that provides a slot for toolbar items
|
|
15
|
+
* @slot - Slot for toolbar items (ex. ``)
|
|
16
|
+
*/
|
|
17
|
+
class Toolbar extends PropertyRequiredMixin(LitElement) {
|
|
18
|
+
|
|
19
|
+
static properties = {
|
|
20
|
+
/**
|
|
21
|
+
* ACCESSIBILITY: REQUIRED: Accessible label for the toolbar
|
|
22
|
+
* @type {string}
|
|
23
|
+
*/
|
|
24
|
+
label: { type: String, required: true }
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
static styles = css`
|
|
28
|
+
:host {
|
|
29
|
+
display: inline-block;
|
|
30
|
+
}
|
|
31
|
+
:host([hidden]) {
|
|
32
|
+
display: none;
|
|
33
|
+
}
|
|
34
|
+
.container {
|
|
35
|
+
border-block-end: 1px solid var(--d2l-theme-border-color-standard);
|
|
36
|
+
display: flex;
|
|
37
|
+
flex-wrap: wrap;
|
|
38
|
+
gap: 4px;
|
|
39
|
+
padding-block-end: 4px;
|
|
40
|
+
}
|
|
41
|
+
`;
|
|
42
|
+
|
|
43
|
+
constructor() {
|
|
44
|
+
super();
|
|
45
|
+
this.label = '';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
render() {
|
|
49
|
+
return html`
|
|
50
|
+
<div
|
|
51
|
+
aria-label="${this.label}"
|
|
52
|
+
class="container"
|
|
53
|
+
@keydown="${this.#handleKeyDown}"
|
|
54
|
+
role="toolbar">
|
|
55
|
+
<slot ${ref(this.#slotRef)} @slotchange="${this.#handleSlotChange}"></slot>
|
|
56
|
+
</div>
|
|
57
|
+
`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
focus() {
|
|
61
|
+
const focusables = this.#getFocusables();
|
|
62
|
+
const item = focusables.find(item => item._activeFocusable);
|
|
63
|
+
if (!item) return;
|
|
64
|
+
item.focus();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
setActiveFocusable(focusable) {
|
|
68
|
+
const focusables = this.#getFocusables();
|
|
69
|
+
if (focusables.length === 0) return;
|
|
70
|
+
|
|
71
|
+
const focusableExists = !!focusables.find(item => item === focusable);
|
|
72
|
+
if (!focusableExists) return;
|
|
73
|
+
|
|
74
|
+
const currentFocusable = focusables.find(item => item._activeFocusable);
|
|
75
|
+
if (currentFocusable) currentFocusable._activeFocusable = false;
|
|
76
|
+
|
|
77
|
+
focusable._activeFocusable = true;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
#slotRef = createRef();
|
|
81
|
+
|
|
82
|
+
#getFocusables() {
|
|
83
|
+
return this.#slotRef.value?.assignedElements({ flatten: true })
|
|
84
|
+
.filter(elem => elem.tagName === 'D2L-TOOLBAR-BUTTON') ?? [];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async #handleKeyDown(e) {
|
|
88
|
+
|
|
89
|
+
// todo: don't steal focus if arrow key originated from inside a dropdown
|
|
90
|
+
|
|
91
|
+
if (e.keyCode !== keyCodes.LEFT && e.keyCode !== keyCodes.RIGHT && e.keyCode !== keyCodes.HOME && e.keyCode !== keyCodes.END) return;
|
|
92
|
+
|
|
93
|
+
const isRtl = document.documentElement.getAttribute('dir') === 'rtl';
|
|
94
|
+
const focusables = this.#getFocusables();
|
|
95
|
+
if (focusables.length === 0) return;
|
|
96
|
+
|
|
97
|
+
const index = focusables.findIndex(item => item._activeFocusable);
|
|
98
|
+
focusables[index]._activeFocusable = false;
|
|
99
|
+
|
|
100
|
+
let focusable;
|
|
101
|
+
if (isRtl && e.keyCode === keyCodes.LEFT) {
|
|
102
|
+
if (index === focusables.length - 1) focusable = focusables[0];
|
|
103
|
+
else focusable = focusables[index + 1];
|
|
104
|
+
} else if (isRtl && e.keyCode === keyCodes.RIGHT) {
|
|
105
|
+
if (index === 0) focusable = focusables[focusables.length - 1];
|
|
106
|
+
else focusable = focusables[index - 1];
|
|
107
|
+
} else if (e.keyCode === keyCodes.LEFT) {
|
|
108
|
+
if (index === 0) focusable = focusables[focusables.length - 1];
|
|
109
|
+
else focusable = focusables[index - 1];
|
|
110
|
+
} else if (e.keyCode === keyCodes.RIGHT) {
|
|
111
|
+
if (index === focusables.length - 1) focusable = focusables[0];
|
|
112
|
+
else focusable = focusables[index + 1];
|
|
113
|
+
} else if (e.keyCode === keyCodes.HOME) {
|
|
114
|
+
focusable = focusables[0];
|
|
115
|
+
} else if (e.keyCode === keyCodes.END) {
|
|
116
|
+
focusable = focusables[focusables.length - 1];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// prevent default so page doesn't scroll when hitting HOME/END
|
|
120
|
+
e.preventDefault();
|
|
121
|
+
|
|
122
|
+
focusable._activeFocusable = true;
|
|
123
|
+
await focusable.updateComplete;
|
|
124
|
+
requestAnimationFrame(() => focusable.focus());
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
#handleSlotChange() {
|
|
128
|
+
const focusables = this.#getFocusables();
|
|
129
|
+
this.#resetActiveFocusable(focusables);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
#resetActiveFocusable(focusables) {
|
|
133
|
+
if (focusables.length === 0) return;
|
|
134
|
+
|
|
135
|
+
const item = focusables.find(item => item._activeFocusable);
|
|
136
|
+
if (item) item._activeFocusable = false;
|
|
137
|
+
|
|
138
|
+
focusables[0]._activeFocusable = true;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
customElements.define('d2l-toolbar', Toolbar);
|
package/custom-elements.json
CHANGED
|
@@ -17181,6 +17181,89 @@
|
|
|
17181
17181
|
}
|
|
17182
17182
|
]
|
|
17183
17183
|
},
|
|
17184
|
+
{
|
|
17185
|
+
"name": "d2l-toolbar-button",
|
|
17186
|
+
"path": "./components/toolbar/toolbar-button.js",
|
|
17187
|
+
"description": "A toolbar button that performs an action",
|
|
17188
|
+
"attributes": [
|
|
17189
|
+
{
|
|
17190
|
+
"name": "disabled",
|
|
17191
|
+
"description": "Disables the toolbar button",
|
|
17192
|
+
"type": "boolean",
|
|
17193
|
+
"default": "false"
|
|
17194
|
+
},
|
|
17195
|
+
{
|
|
17196
|
+
"name": "text",
|
|
17197
|
+
"description": "ACCESSIBILITY: REQUIRED: Accessible text for the button",
|
|
17198
|
+
"type": "string",
|
|
17199
|
+
"default": "\"\""
|
|
17200
|
+
},
|
|
17201
|
+
{
|
|
17202
|
+
"name": "icon",
|
|
17203
|
+
"description": "Preset icon key (e.g. \"tier1:gear\")",
|
|
17204
|
+
"type": "string"
|
|
17205
|
+
}
|
|
17206
|
+
],
|
|
17207
|
+
"properties": [
|
|
17208
|
+
{
|
|
17209
|
+
"name": "styles",
|
|
17210
|
+
"type": "array",
|
|
17211
|
+
"default": "[[null],\"toolbarButtonStyles\"]"
|
|
17212
|
+
},
|
|
17213
|
+
{
|
|
17214
|
+
"name": "disabled",
|
|
17215
|
+
"attribute": "disabled",
|
|
17216
|
+
"description": "Disables the toolbar button",
|
|
17217
|
+
"type": "boolean",
|
|
17218
|
+
"default": "false"
|
|
17219
|
+
},
|
|
17220
|
+
{
|
|
17221
|
+
"name": "text",
|
|
17222
|
+
"attribute": "text",
|
|
17223
|
+
"description": "ACCESSIBILITY: REQUIRED: Accessible text for the button",
|
|
17224
|
+
"type": "string",
|
|
17225
|
+
"default": "\"\""
|
|
17226
|
+
},
|
|
17227
|
+
{
|
|
17228
|
+
"name": "icon",
|
|
17229
|
+
"attribute": "icon",
|
|
17230
|
+
"description": "Preset icon key (e.g. \"tier1:gear\")",
|
|
17231
|
+
"type": "string"
|
|
17232
|
+
}
|
|
17233
|
+
]
|
|
17234
|
+
},
|
|
17235
|
+
{
|
|
17236
|
+
"name": "d2l-toolbar",
|
|
17237
|
+
"path": "./components/toolbar/toolbar.js",
|
|
17238
|
+
"description": "A toolbar that provides a slot for toolbar items",
|
|
17239
|
+
"attributes": [
|
|
17240
|
+
{
|
|
17241
|
+
"name": "label",
|
|
17242
|
+
"description": "ACCESSIBILITY: REQUIRED: Accessible label for the toolbar",
|
|
17243
|
+
"type": "string",
|
|
17244
|
+
"default": "\"\""
|
|
17245
|
+
}
|
|
17246
|
+
],
|
|
17247
|
+
"properties": [
|
|
17248
|
+
{
|
|
17249
|
+
"name": "styles",
|
|
17250
|
+
"default": "\"css`\\n\\t\\t:host {\\n\\t\\t\\tdisplay: inline-block;\\n\\t\\t}\\n\\t\\t:host([hidden]) {\\n\\t\\t\\tdisplay: none;\\n\\t\\t}\\n\\t\\t.container {\\n\\t\\t\\tborder-block-end: 1px solid var(--d2l-theme-border-color-standard);\\n\\t\\t\\tdisplay: flex;\\n\\t\\t\\tflex-wrap: wrap;\\n\\t\\t\\tgap: 4px;\\n\\t\\t\\tpadding-block-end: 4px;\\n\\t\\t}\\n\\t`\""
|
|
17251
|
+
},
|
|
17252
|
+
{
|
|
17253
|
+
"name": "label",
|
|
17254
|
+
"attribute": "label",
|
|
17255
|
+
"description": "ACCESSIBILITY: REQUIRED: Accessible label for the toolbar",
|
|
17256
|
+
"type": "string",
|
|
17257
|
+
"default": "\"\""
|
|
17258
|
+
}
|
|
17259
|
+
],
|
|
17260
|
+
"slots": [
|
|
17261
|
+
{
|
|
17262
|
+
"name": "",
|
|
17263
|
+
"description": "Slot for toolbar items (ex. ``)"
|
|
17264
|
+
}
|
|
17265
|
+
]
|
|
17266
|
+
},
|
|
17184
17267
|
{
|
|
17185
17268
|
"name": "d2l-demo-tooltip-drag-and-drop-list",
|
|
17186
17269
|
"path": "./components/tooltip/demo/tooltip-drag-and-drop-list.js",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brightspace-ui/core",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.321.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",
|