@brightspace-ui/core 3.320.2 → 3.321.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.
@@ -907,7 +907,6 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
907
907
  scrollToPromise
908
908
  ]);
909
909
  p.then(async() => {
910
- await new Promise(resolve => requestAnimationFrame(resolve));
911
910
  await new Promise(resolve => requestAnimationFrame(resolve));
912
911
  if (this.#loadingCompleteResolve) {
913
912
  this.#loadingCompleteResolve();
@@ -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);
@@ -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.320.2",
3
+ "version": "3.321.1",
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",