@brightspace-ui/core 3.140.0 → 3.141.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.
@@ -3,6 +3,7 @@ import '../tooltip/tooltip.js';
3
3
  import { css, html, LitElement, nothing, unsafeCSS } from 'lit';
4
4
  import { VisibleOnAncestorMixin, visibleOnAncestorStyles } from '../../mixins/visible-on-ancestor/visible-on-ancestor-mixin.js';
5
5
  import { FocusMixin } from '../../mixins/focus/focus-mixin.js';
6
+ import { getFlag } from '../../helpers/flags.js';
6
7
  import { getFocusPseudoClass } from '../../helpers/focus.js';
7
8
  import { getUniqueId } from '../../helpers/uniqueId.js';
8
9
  import { ifDefined } from 'lit/directives/if-defined.js';
@@ -15,6 +16,7 @@ const MODE = {
15
16
  icon_and_text: 'icon-and-text',
16
17
  icon_when_interacted: 'icon-when-interacted'
17
18
  };
19
+ const whiteAddButtonBackgroundFlag = getFlag('GAUD-7495-add-button-white-background', true);
18
20
 
19
21
  /**
20
22
  * A component for quickly adding items to a specific locaiton.
@@ -31,7 +33,8 @@ class ButtonAdd extends RtlMixin(PropertyRequiredMixin(FocusMixin(LocalizeCoreEl
31
33
  * ACCESSIBILITY: The text associated with the button. When mode is `icon-and-text` this text is displayed next to the icon, otherwise this text is in a tooltip.
32
34
  * @type {string}
33
35
  */
34
- text: { type: String, required: true }
36
+ text: { type: String, required: true },
37
+ _whiteBackgroundAddButton: { type: Boolean, attribute: '_white-background-add-button', reflect: true }
35
38
  };
36
39
  }
37
40
 
@@ -102,9 +105,14 @@ class ButtonAdd extends RtlMixin(PropertyRequiredMixin(FocusMixin(LocalizeCoreEl
102
105
  }
103
106
 
104
107
  button:${unsafeCSS(getFocusPseudoClass())} d2l-button-add-icon-text {
108
+ background-color: white;
105
109
  border-radius: 0.3rem;
106
110
  box-shadow: 0 0 0 2px var(--d2l-button-add-hover-focus-color);
107
111
  }
112
+ /* remove when GAUD-7495-add-button-white-background is cleaned up */
113
+ :host(:not([_white-background-add-button])) button:${unsafeCSS(getFocusPseudoClass())} d2l-button-add-icon-text {
114
+ background-color: transparent;
115
+ }
108
116
  :host([mode="icon-when-interacted"]) button:${unsafeCSS(getFocusPseudoClass())} d2l-button-add-icon-text,
109
117
  :host([mode="icon"]) button:${unsafeCSS(getFocusPseudoClass())} d2l-button-add-icon-text {
110
118
  border-radius: 0.2rem;
@@ -159,6 +167,7 @@ class ButtonAdd extends RtlMixin(PropertyRequiredMixin(FocusMixin(LocalizeCoreEl
159
167
  this.mode = MODE.icon;
160
168
 
161
169
  this._buttonId = getUniqueId();
170
+ this._whiteBackgroundAddButton = whiteAddButtonBackgroundFlag;
162
171
  }
163
172
 
164
173
  static get focusElementSelector() {
@@ -237,13 +246,25 @@ class ButtonAddIconText extends VisibleOnAncestorMixin(LitElement) {
237
246
  }
238
247
 
239
248
  render() {
240
- return html`
249
+ const whiteBackgroundAddButton = html`
250
+ <svg width="18" height="18" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
251
+ <g>
252
+ <circle cx="9" cy="9" r="9" fill="#fff"/>
253
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M9 2a7 7 0 1 1-7 7 7.008 7.008 0 0 1 7-7Zm0-2a9 9 0 1 0 0 18A9 9 0 0 0 9 0Z"/>
254
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M10 5a1 1 0 0 0-2 0v3H5a1 1 0 0 0 0 2h3v3a1 1 0 1 0 2 0v-3h3a1 1 0 1 0 0-2h-3V5Z"/>
255
+ </g>
256
+ </svg>
257
+ `;
258
+ const translucentBackgroundAddButton = html`
241
259
  <svg width="18" height="18" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
242
260
  <g>
243
261
  <path fill-rule="evenodd" clip-rule="evenodd" d="M9 2C10.3845 2 11.7378 2.41054 12.889 3.17971C14.0401 3.94888 14.9373 5.04213 15.4672 6.32122C15.997 7.6003 16.1356 9.00777 15.8655 10.3656C15.5954 11.7235 14.9287 12.9708 13.9497 13.9497C12.9708 14.9287 11.7235 15.5954 10.3656 15.8655C9.00777 16.1356 7.6003 15.997 6.32122 15.4672C5.04213 14.9373 3.94888 14.0401 3.17971 12.889C2.41054 11.7378 2 10.3845 2 9C2.00212 7.14413 2.7403 5.36489 4.05259 4.05259C5.36489 2.7403 7.14413 2.00212 9 2V2ZM9 0C6.61305 0 4.32387 0.948212 2.63604 2.63604C0.948212 4.32387 0 6.61305 0 9C0 11.3869 0.948212 13.6761 2.63604 15.364C4.32387 17.0518 6.61305 18 9 18C11.3869 18 13.6761 17.0518 15.364 15.364C17.0518 13.6761 18 11.3869 18 9C18 6.61305 17.0518 4.32387 15.364 2.63604C13.6761 0.948212 11.3869 0 9 0V0Z" />
244
262
  <path fill-rule="evenodd" clip-rule="evenodd" d="M10 5C10 4.44772 9.55228 4 9 4C8.44772 4 8 4.44772 8 5V8H5C4.44772 8 4 8.44772 4 9C4 9.55228 4.44772 10 5 10H8V13C8 13.5523 8.44772 14 9 14C9.55228 14 10 13.5523 10 13V10H13C13.5523 10 14 9.55228 14 9C14 8.44772 13.5523 8 13 8H10V5Z" />
245
263
  </g>
246
264
  </svg>
265
+ `;
266
+ return html`
267
+ ${whiteAddButtonBackgroundFlag ? whiteBackgroundAddButton : translucentBackgroundAddButton}
247
268
  ${this.text ? html`<span class="d2l-label-text">${this.text}</span>` : nothing}
248
269
  `;
249
270
  }
@@ -4,7 +4,7 @@ import { css, html, LitElement } from 'lit';
4
4
  import { getBoundingAncestor, getComposedParent, isComposedAncestor } from '../../helpers/dom.js';
5
5
  import { getComposedActiveElement } from '../../helpers/focus.js';
6
6
  import { getLegacyOffsetParent } from '../../helpers/offsetParent-legacy.js';
7
- import { RtlMixin } from '../../mixins/rtl/rtl-mixin.js';
7
+ import { LoadingCompleteMixin } from '../../mixins/loading-complete/loading-complete-mixin.js';
8
8
  import { styleMap } from 'lit/directives/style-map.js';
9
9
 
10
10
  const mediaQueryList = window.matchMedia('(max-height: 500px)');
@@ -14,7 +14,7 @@ const MINIMUM_TARGET_SIZE = 24;
14
14
  * A wrapper component to display floating workflow buttons. When the normal position of the workflow buttons is below the bottom edge of the viewport, they will dock at the bottom edge. When the normal position becomes visible, they will undock.
15
15
  * @slot - Content to be displayed in the floating container
16
16
  */
17
- class FloatingButtons extends RtlMixin(LitElement) {
17
+ class FloatingButtons extends LoadingCompleteMixin(LitElement) {
18
18
 
19
19
  static get properties() {
20
20
  return {
@@ -26,8 +26,7 @@ class FloatingButtons extends RtlMixin(LitElement) {
26
26
  _containerMarginLeft: { attribute: false, type: String },
27
27
  _containerMarginRight: { attribute: false, type: String },
28
28
  _floating: { type: Boolean, reflect: true },
29
- _innerContainerLeft: { attribute: false, type: String },
30
- _innerContainerRight: { attribute: false, type: String }
29
+ _innerContainerLeft: { attribute: false, type: String }
31
30
  };
32
31
  }
33
32
 
@@ -80,21 +79,14 @@ class FloatingButtons extends RtlMixin(LitElement) {
80
79
  .d2l-floating-buttons-inner-container ::slotted(d2l-button),
81
80
  .d2l-floating-buttons-inner-container ::slotted(button),
82
81
  .d2l-floating-buttons-inner-container ::slotted(.d2l-button) {
82
+ margin-inline-end: 0.75rem !important;
83
83
  margin-bottom: 0.75rem !important;
84
- margin-right: 0.75rem !important;
85
84
  }
86
85
 
87
86
  .d2l-floating-buttons-inner-container ::slotted(d2l-overflow-group) {
88
87
  padding-bottom: 0.75rem !important;
89
88
  }
90
89
 
91
- :host([dir="rtl"]) .d2l-floating-buttons-inner-container ::slotted(d2l-button),
92
- :host([dir="rtl"]) .d2l-floating-buttons-inner-container ::slotted(button),
93
- :host([dir="rtl"]) .d2l-floating-buttons-inner-container ::slotted(.d2l-button) {
94
- margin-left: 0.75rem !important;
95
- margin-right: 0 !important;
96
- }
97
-
98
90
  @media (prefers-reduced-motion: reduce) {
99
91
  :host([_floating]:not([always-float])) .d2l-floating-buttons-container {
100
92
  transition: none;
@@ -110,7 +102,6 @@ class FloatingButtons extends RtlMixin(LitElement) {
110
102
  this._containerMarginRight = '';
111
103
  this._floating = false;
112
104
  this._innerContainerLeft = '';
113
- this._innerContainerRight = '';
114
105
  this._intersectionObserver = null;
115
106
  this._isIntersecting = false;
116
107
  this._recalculateFloating = this._recalculateFloating.bind(this);
@@ -125,13 +116,15 @@ class FloatingButtons extends RtlMixin(LitElement) {
125
116
  // if browser doesn't support IntersectionObserver, we don't float
126
117
  if (typeof(IntersectionObserver) !== 'function') {
127
118
  this._isIntersecting = true;
119
+ this.resolveLoadingComplete();
128
120
  return;
129
121
  }
130
- this._intersectionObserver = this._intersectionObserver || new IntersectionObserver((entries) => {
131
- entries.forEach((entry) => {
122
+ this._intersectionObserver = this._intersectionObserver || new IntersectionObserver(async(entries) => {
123
+ for (const entry of entries) {
132
124
  this._isIntersecting = entry.isIntersecting;
133
- this._recalculateFloating();
134
- });
125
+ await this._recalculateFloating();
126
+ }
127
+ this.resolveLoadingComplete();
135
128
  });
136
129
 
137
130
  // observe intersection of a fake sibling element since host is sticky
@@ -164,13 +157,12 @@ class FloatingButtons extends RtlMixin(LitElement) {
164
157
 
165
158
  render() {
166
159
  const containerStyle = {
167
- marginLeft: this._containerMarginLeft,
168
- marginRight: this._containerMarginRight
160
+ marginInlineStart: this._containerMarginLeft,
161
+ marginInlineEnd: this._containerMarginRight
169
162
  };
170
163
 
171
164
  const innerContainerStyle = {
172
- marginLeft: this._innerContainerLeft,
173
- marginRight: this._innerContainerRight
165
+ marginInlineStart: this._innerContainerLeft
174
166
  };
175
167
 
176
168
  return html`
@@ -193,7 +185,6 @@ class FloatingButtons extends RtlMixin(LitElement) {
193
185
  this._containerMarginLeft = '';
194
186
  this._containerMarginRight = '';
195
187
  this._innerContainerLeft = '';
196
- this._innerContainerRight = '';
197
188
 
198
189
  if (!this._floating) return;
199
190
 
@@ -218,12 +209,8 @@ class FloatingButtons extends RtlMixin(LitElement) {
218
209
  this._containerMarginRight = `-${containerRight}px`;
219
210
  }
220
211
 
221
- if (this.dir !== 'rtl') {
222
- if (containerLeft !== 0) {
223
- this._innerContainerLeft = `${containerLeft}px`;
224
- }
225
- } else {
226
- this._innerContainerRight = `${containerRight}px`;
212
+ if (containerLeft !== 0) {
213
+ this._innerContainerLeft = `${containerLeft}px`;
227
214
  }
228
215
 
229
216
  }
@@ -251,29 +238,30 @@ class FloatingButtons extends RtlMixin(LitElement) {
251
238
 
252
239
  }
253
240
 
254
- _recalculateFloating() {
255
- requestAnimationFrame(() => {
256
-
257
- if (this.alwaysFloat) {
258
- this._floating = true;
259
- this._calcContainerPosition();
260
- return;
261
- }
241
+ async _recalculateFloating() {
242
+ await new Promise(resolve => requestAnimationFrame(resolve));
262
243
 
263
- const viewportIsLessThanMinHeight = mediaQueryList.matches;
264
- if (viewportIsLessThanMinHeight) {
265
- this._floating = false;
266
- this._calcContainerPosition();
267
- return;
268
- }
244
+ if (this.alwaysFloat) {
245
+ this._floating = true;
246
+ this._calcContainerPosition();
247
+ await this.updateComplete;
248
+ return;
249
+ }
269
250
 
270
- const shouldFloat = !this._isIntersecting;
271
- if (shouldFloat !== this._floating) {
272
- this._floating = shouldFloat;
273
- this._calcContainerPosition();
274
- }
251
+ const viewportIsLessThanMinHeight = mediaQueryList.matches;
252
+ if (viewportIsLessThanMinHeight) {
253
+ this._floating = false;
254
+ this._calcContainerPosition();
255
+ await this.updateComplete;
256
+ return;
257
+ }
275
258
 
276
- });
259
+ const shouldFloat = !this._isIntersecting;
260
+ if (shouldFloat !== this._floating) {
261
+ this._floating = shouldFloat;
262
+ this._calcContainerPosition();
263
+ await this.updateComplete;
264
+ }
277
265
  }
278
266
 
279
267
  _scrollIfFloatObsuringFocus() {
@@ -13,6 +13,7 @@ class ListDemoNav extends LitElement {
13
13
 
14
14
  static get properties() {
15
15
  return {
16
+ addButton: { type: Boolean, attribute: 'add-button' },
16
17
  _currentItem: { state: true }
17
18
  };
18
19
  }
@@ -36,6 +37,7 @@ class ListDemoNav extends LitElement {
36
37
 
37
38
  constructor() {
38
39
  super();
40
+ this.addButton = false;
39
41
  this._currentItem = null;
40
42
  }
41
43
 
@@ -43,6 +45,7 @@ class ListDemoNav extends LitElement {
43
45
  return html`
44
46
  <div @d2l-list-items-move="${this._handleListItemsMove}">
45
47
  <d2l-list
48
+ ?add-button="${this.addButton}"
46
49
  grid
47
50
  drag-multiple
48
51
  @d2l-list-item-link-click="${this._handleItemClick}">
@@ -151,7 +154,7 @@ class ListDemoNav extends LitElement {
151
154
  }
152
155
  </d2l-list-item-content>
153
156
  ${hasSubList ? html`
154
- <d2l-list slot="nested" grid>
157
+ <d2l-list slot="nested" grid ?add-button="${this.addButton}">
155
158
  ${repeat(item.items, (subItem) => subItem.key, (subItem) => this._renderItem(subItem))}
156
159
  </d2l-list>`
157
160
  : nothing
@@ -80,7 +80,7 @@
80
80
  </template>
81
81
  </d2l-demo-snippet>
82
82
 
83
- <h2>Side nav list (more complex with drag & drop)</h2>
83
+ <h2>Side nav list (draggable)</h2>
84
84
 
85
85
  <d2l-demo-snippet>
86
86
  <template>
@@ -88,6 +88,14 @@
88
88
  </template>
89
89
  </d2l-demo-snippet>
90
90
 
91
+ <h2>Side nav list (draggable, add-button)</h2>
92
+
93
+ <d2l-demo-snippet>
94
+ <template>
95
+ <d2l-demo-list-nav add-button></d2l-demo-list-nav>
96
+ </template>
97
+ </d2l-demo-snippet>
98
+
91
99
  </d2l-demo-page>
92
100
 
93
101
  </body>
@@ -110,6 +110,7 @@ export const ListItemMixin = superclass => class extends composeMixins(
110
110
  _listItemNewStyles: { type: Boolean, reflect: true, attribute: '_list-item-new-styles' },
111
111
  _showAddButton: { type: Boolean, attribute: '_show-add-button', reflect: true },
112
112
  _siblingHasColor: { state: true },
113
+ _whiteBackgroundAddButton: { type: Boolean, attribute: '_white-background-add-button', reflect: true },
113
114
  };
114
115
  }
115
116
 
@@ -144,11 +145,11 @@ export const ListItemMixin = superclass => class extends composeMixins(
144
145
  position: absolute;
145
146
  width: 100%;
146
147
  }
147
- :host([_has-nested-list-add-button]) [slot="before-content"] {
148
+ :host(:not([_white-background-add-button])[_has-nested-list-add-button]) [slot="before-content"] {
148
149
  border-bottom: 1px solid var(--d2l-color-mica);
149
150
  margin-bottom: -1px;
150
151
  }
151
- :host([_has-nested-list-add-button]:not([selection-disabled]):not([skeleton])[selected]) [slot="before-content"] {
152
+ :host(:not([_white-background-add-button])[_has-nested-list-add-button]:not([selection-disabled]):not([skeleton])[selected]) [slot="before-content"] {
152
153
  border-bottom-color: ${unsafeCSS(useNewStylesFlag ? 'var(--d2l-color-mica)' : '#b6cbe8')}; /* stylelint-disable-line */
153
154
  }
154
155
  :host(:first-of-type) [slot="control-container"]::before {
@@ -172,9 +173,9 @@ export const ListItemMixin = superclass => class extends composeMixins(
172
173
  :host([_focusing-primary-action]) [slot="control-container"]::after,
173
174
  :host([selected]:not([selection-disabled]):not([skeleton])) [slot="control-container"]::before,
174
175
  :host([selected]:not([selection-disabled]):not([skeleton])) [slot="control-container"]::after,
175
- :host([_show-add-button]) [slot="control-container"]::before,
176
+ :host(:not([_white-background-add-button])[_show-add-button]) [slot="control-container"]::before,
176
177
  .hide-bottom-border[slot="control-container"]::after,
177
- :host([_has-nested-list-add-button]) [slot="control-container"]::after,
178
+ :host(:not([_white-background-add-button])[_has-nested-list-add-button]) [slot="control-container"]::after,
178
179
  :host(:first-of-type[_nested]) [slot="control-container"]::before {
179
180
  border-top-color: transparent;
180
181
  }
@@ -331,7 +332,7 @@ export const ListItemMixin = superclass => class extends composeMixins(
331
332
  border-color: var(--d2l-color-mica);
332
333
  margin-bottom: -1px;
333
334
  }
334
- /* below hides the border under the d2l-button-add */
335
+ /* below hides the border under the d2l-button-add; clean up with GAUD-7495-add-button-white-background */
335
336
  :host([_hovering-control]) [slot="outside-control-container"].hide-bottom-border,
336
337
  :host([_hovering-primary-action]) [slot="outside-control-container"].hide-bottom-border,
337
338
  :host([_hovering-selection]) [slot="outside-control-container"].hide-bottom-border,
@@ -479,6 +480,7 @@ export const ListItemMixin = superclass => class extends composeMixins(
479
480
  this._listItemInteractiveEnabled = listItemInteractiveFlag;
480
481
  this._listItemNewStyles = useNewStylesFlag;
481
482
  this._siblingHasColor = false;
483
+ this._whiteBackgroundAddButton = getFlag('GAUD-7495-add-button-white-background', true);
482
484
  }
483
485
 
484
486
  get color() {
@@ -713,7 +715,7 @@ export const ListItemMixin = superclass => class extends composeMixins(
713
715
  'd2l-skeletize': this.color
714
716
  };
715
717
  const bottomBorderClasses = {
716
- 'hide-bottom-border': this._showAddButton && (!this._hasNestedList || this._hasNestedListAddButton)
718
+ 'hide-bottom-border': !this._whiteBackgroundAddButton && (this._showAddButton && (!this._hasNestedList || this._hasNestedListAddButton))
717
719
  };
718
720
 
719
721
  const alignNested = ((this.draggable && this.selectable) || (this.expandable && this.selectable && this.color) || (this._listItemInteractiveEnabled && this.expandable && !this.selectable)) ? 'control' : undefined;
@@ -1039,6 +1039,14 @@
1039
1039
  "description": "Indicates to display buttons as always floating",
1040
1040
  "type": "boolean",
1041
1041
  "default": "false"
1042
+ },
1043
+ {
1044
+ "name": "loadingComplete",
1045
+ "type": "Promise<any>"
1046
+ },
1047
+ {
1048
+ "name": "resolveLoadingComplete",
1049
+ "type": "() => void"
1042
1050
  }
1043
1051
  ],
1044
1052
  "slots": [
@@ -8383,7 +8391,22 @@
8383
8391
  },
8384
8392
  {
8385
8393
  "name": "d2l-demo-list-nav",
8386
- "path": "./components/list/demo/demo-list-nav.js"
8394
+ "path": "./components/list/demo/demo-list-nav.js",
8395
+ "attributes": [
8396
+ {
8397
+ "name": "add-button",
8398
+ "type": "boolean",
8399
+ "default": "false"
8400
+ }
8401
+ ],
8402
+ "properties": [
8403
+ {
8404
+ "name": "addButton",
8405
+ "attribute": "add-button",
8406
+ "type": "boolean",
8407
+ "default": "false"
8408
+ }
8409
+ ]
8387
8410
  },
8388
8411
  {
8389
8412
  "name": "d2l-demo-list-nested-iterations-helper",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brightspace-ui/core",
3
- "version": "3.140.0",
3
+ "version": "3.141.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",