@brightspace-ui/core 2.175.0 → 2.176.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.
@@ -175,6 +175,27 @@ To make your `d2l-button-icon` accessible, use the following properties when app
175
175
  </d2l-button-icon>
176
176
  ```
177
177
 
178
+ ## Add Button [d2l-button-add]
179
+
180
+ The `d2l-button-add` element is for quickly adding new items inline (for example in a list).
181
+
182
+ <!-- docs: demo code properties name:d2l-button-add display:block autoSize:false size:xsmall -->
183
+ ```html
184
+ <script type="module">
185
+ import '@brightspace-ui/core/components/button/button-add.js';
186
+ </script>
187
+ <d2l-button-add text="Add New Item"></d2l-button-add>
188
+ ```
189
+
190
+ <!-- docs: start hidden content -->
191
+ ### Properties
192
+
193
+ | Property | Type | Description |
194
+ |--|--|--|
195
+ | `text` | String, required | 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. |
196
+ | `mode` | String | Display mode of the component. Defaults to "icon" (plus icon is always visible). Other options are "icon-and-text" (plus icon and text are always visible), and "icon-when-interacted" (plus icon is only visible when hover or focus). |
197
+ <!-- docs: end hidden content -->
198
+
178
199
  ## Floating Buttons [d2l-floating-buttons]
179
200
 
180
201
  See [floating buttons](https://github.com/BrightspaceUI/core/tree/main/components/button/floating-buttons.md).
@@ -11,9 +11,9 @@ import { PropertyRequiredMixin } from '../../mixins/property-required/property-r
11
11
  import { RtlMixin } from '../../mixins/rtl/rtl-mixin.js';
12
12
 
13
13
  const MODE = {
14
- ICON: 'icon',
15
- ICON_AND_TEXT: 'icon-and-text',
16
- ICON_WHEN_INTERACTED: 'icon-when-interacted'
14
+ icon: 'icon',
15
+ icon_and_text: 'icon-and-text',
16
+ icon_when_interacted: 'icon-when-interacted'
17
17
  };
18
18
 
19
19
  /**
@@ -23,12 +23,12 @@ class ButtonAdd extends RtlMixin(PropertyRequiredMixin(FocusMixin(LocalizeCoreEl
23
23
  static get properties() {
24
24
  return {
25
25
  /**
26
- * Display mode of the component. Defaults to "icon" (plus icon is always visible). Other options are "icon-and-text" (plus icon and text are always visible), and "icon-when-interacted" (plus icon is only visible when hover or focus).
26
+ * Display mode of the component. Defaults to `icon` (plus icon is always visible). Other options are `icon-and-text` (plus icon and text are always visible), and `icon-when-interacted` (plus icon is only visible when hover or focus).
27
27
  * @type {'icon'|'icon-and-text'|'icon-when-interacted'}
28
28
  */
29
29
  mode: { type: String, reflect: true },
30
30
  /**
31
- * When text-visible is true, the text to show in the button. When text-visible is false, the text to show in the tooltip.
31
+ * 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
32
  * @type {string}
33
33
  */
34
34
  text: { type: String, required: true }
@@ -155,7 +155,7 @@ class ButtonAdd extends RtlMixin(PropertyRequiredMixin(FocusMixin(LocalizeCoreEl
155
155
  constructor() {
156
156
  super();
157
157
 
158
- this.mode = MODE.ICON;
158
+ this.mode = MODE.icon;
159
159
 
160
160
  this._buttonId = getUniqueId();
161
161
  }
@@ -166,13 +166,13 @@ class ButtonAdd extends RtlMixin(PropertyRequiredMixin(FocusMixin(LocalizeCoreEl
166
166
 
167
167
  render() {
168
168
  const text = this.text || this.localize('components.button-add.addItem');
169
- const id = !this.mode !== MODE.ICON_AND_TEXT ? this._buttonId : undefined;
170
- const offset = this.mode === MODE.ICON_WHEN_INTERACTED ? 23 : 18;
169
+ const id = !this.mode !== MODE.icon_and_text ? this._buttonId : undefined;
170
+ const offset = this.mode === MODE.icon_when_interacted ? 23 : 18;
171
171
 
172
- const content = this.mode !== MODE.ICON_AND_TEXT
173
- ? html`<d2l-button-add-icon-text ?visible-on-ancestor="${this.mode === MODE.ICON_WHEN_INTERACTED}" animation-type="opacity"></d2l-button-add-icon-text>`
172
+ const content = this.mode !== MODE.icon_and_text
173
+ ? html`<d2l-button-add-icon-text ?visible-on-ancestor="${this.mode === MODE.icon_when_interacted}" animation-type="opacity"></d2l-button-add-icon-text>`
174
174
  : html`<d2l-button-add-icon-text text="${text}"></d2l-button-add-icon-text>`;
175
- const tooltip = this.mode !== MODE.ICON_AND_TEXT
175
+ const tooltip = this.mode !== MODE.icon_and_text
176
176
  ? html`<d2l-tooltip class="vdiff-target" delay="100" offset="${offset}" for="${this._buttonId}" for-type="label">${text}</d2l-tooltip>`
177
177
  : nothing;
178
178
 
@@ -69,7 +69,12 @@ class ListItemGenericLayout extends RtlMixin(LitElement) {
69
69
  [control-end content-start] minmax(0, auto)
70
70
  [content-end actions-start] minmax(0, min-content)
71
71
  [end actions-end];
72
- grid-template-rows: [main-start] [main-end add-start] [add-end nested-start] [nested-end];
72
+ grid-template-rows:
73
+ [start add-top-start] minmax(0, min-content)
74
+ [add-top-end main-start] minmax(0, min-content)
75
+ [main-end add-start] minmax(0, min-content)
76
+ [add-end nested-start] minmax(0, min-content)
77
+ [nested-end end];
73
78
  }
74
79
 
75
80
  :host([align-nested="control"]) ::slotted([slot="nested"]) {
@@ -92,7 +97,7 @@ class ListItemGenericLayout extends RtlMixin(LitElement) {
92
97
  ::slotted([slot="outside-control-container"]),
93
98
  ::slotted([slot="control-container"]),
94
99
  ::slotted([slot="drop-target"]) {
95
- grid-row: 1 / 2;
100
+ grid-row: 2 / 3;
96
101
  }
97
102
 
98
103
  ::slotted([slot="outside-control"]) {
@@ -169,12 +174,19 @@ class ListItemGenericLayout extends RtlMixin(LitElement) {
169
174
 
170
175
  ::slotted([slot="nested"]) {
171
176
  grid-column: content-start / end;
172
- grid-row: nested-start / nested-end;
177
+ grid-row: nested;
173
178
  }
179
+
174
180
  ::slotted([slot="add"]) {
175
- grid-column: color-start / end;
176
181
  grid-row: add;
177
182
  }
183
+ ::slotted([slot="add-top"]) {
184
+ grid-row: add-top;
185
+ }
186
+ ::slotted([slot="add-top"]),
187
+ ::slotted([slot="add"]) {
188
+ grid-column: color-start / end;
189
+ }
178
190
  `;
179
191
  }
180
192
 
@@ -208,6 +220,8 @@ class ListItemGenericLayout extends RtlMixin(LitElement) {
208
220
 
209
221
  render() {
210
222
  return html`
223
+ <slot name="add-top" class="d2l-cell" data-cell-num="10"></slot>
224
+
211
225
  <slot name="control-container"></slot>
212
226
  <slot name="outside-control-container"></slot>
213
227
 
@@ -60,6 +60,10 @@ export const ListItemMixin = superclass => class extends composeMixins(
60
60
  * @type {string}
61
61
  */
62
62
  color: { type: String },
63
+ /**
64
+ * @ignore
65
+ */
66
+ first: { type: Boolean, reflect: true },
63
67
  /**
64
68
  * Whether to allow the drag target to be the handle only rather than the entire cell
65
69
  * @type {boolean}
@@ -143,6 +147,7 @@ export const ListItemMixin = superclass => class extends composeMixins(
143
147
  :host([selected]:not([selection-disabled]):not([skeleton])) [slot="control-container"]::before,
144
148
  :host([selected]:not([selection-disabled]):not([skeleton])) [slot="control-container"]::after,
145
149
  :host([_show-add-button]) [slot="control-container"]::after,
150
+ :host([_show-add-button]) [slot="control-container"]::before,
146
151
  :host(:first-of-type[_nested]) [slot="control-container"]::before {
147
152
  border-top-color: transparent;
148
153
  }
@@ -380,7 +385,8 @@ export const ListItemMixin = superclass => class extends composeMixins(
380
385
  margin-right: -6px;
381
386
  }
382
387
 
383
- [slot="add"] {
388
+ [slot="add"],
389
+ [slot="add-top"] {
384
390
  margin-bottom: -4px;
385
391
  margin-top: -3px;
386
392
  }
@@ -399,6 +405,7 @@ export const ListItemMixin = superclass => class extends composeMixins(
399
405
 
400
406
  constructor() {
401
407
  super();
408
+ this.first = false;
402
409
  this.noPrimaryAction = false;
403
410
  this.paddingType = 'normal';
404
411
  this._contentId = getUniqueId();
@@ -553,9 +560,10 @@ export const ListItemMixin = superclass => class extends composeMixins(
553
560
  }
554
561
  }
555
562
 
556
- _handleButtonAddClick() {
563
+ _handleButtonAddClick(e) {
564
+ const position = e.target.hasAttribute('data-is-first') ? 'before' : 'after';
557
565
  /** @ignore */
558
- this.dispatchEvent(new CustomEvent('d2l-list-item-add-button-click', { bubbles: true }));
566
+ this.dispatchEvent(new CustomEvent('d2l-list-item-add-button-click', { bubbles: true, detail: { position } }));
559
567
  }
560
568
 
561
569
  _isListItem(node) {
@@ -608,6 +616,7 @@ export const ListItemMixin = superclass => class extends composeMixins(
608
616
  const nestedList = this._getNestedList();
609
617
  if (this._hasNestedList !== !!nestedList) {
610
618
  this._hasNestedList = !!nestedList;
619
+ this._hasNestedListAddButton = nestedList.hasAttribute('add-button');
611
620
  /** @ignore */
612
621
  this.dispatchEvent(new CustomEvent('d2l-list-item-nested-change', { bubbles: true, composed: true }));
613
622
  }
@@ -640,6 +649,11 @@ export const ListItemMixin = superclass => class extends composeMixins(
640
649
  data-separators="${ifDefined(this._separators)}"
641
650
  ?grid-active="${this.role === 'rowgroup'}"
642
651
  ?no-primary-action="${this.noPrimaryAction}">
652
+ ${this._showAddButton && this.first ? html`
653
+ <div slot="add-top">
654
+ <d2l-button-add text="${addButtonText}" mode="icon-when-interacted" @click="${this._handleButtonAddClick}" data-is-first></d2l-button-add>
655
+ </div>
656
+ ` : nothing}
643
657
  <div slot="outside-control-container"></div>
644
658
  ${this._renderDropTarget()}
645
659
  ${this._renderDragHandle(this._renderOutsideControl)}
@@ -682,7 +696,7 @@ export const ListItemMixin = superclass => class extends composeMixins(
682
696
  class="d2l-list-item-actions-container">
683
697
  <slot name="actions" class="d2l-list-item-actions">${actions}</slot>
684
698
  </div>
685
- ${this._showAddButton ? html`
699
+ ${this._showAddButton && !this._hasNestedListAddButton ? html`
686
700
  <div slot="add">
687
701
  <d2l-button-add text="${addButtonText}" mode="icon-when-interacted" @click="${this._handleButtonAddClick}"></d2l-button-add>
688
702
  </div>
@@ -120,6 +120,9 @@ class List extends PageableMixin(SelectionMixin(LitElement)) {
120
120
  --d2l-list-item-color-border-radius: 3px;
121
121
  --d2l-list-item-color-width: 3px;
122
122
  }
123
+ :host([add-button]) ::slotted([slot="controls"]) {
124
+ margin-bottom: calc(6px + 0.4rem); /* controls section margin-bottom + spacing for add-button */
125
+ }
123
126
  `;
124
127
  }
125
128
 
@@ -319,8 +322,12 @@ class List extends PageableMixin(SelectionMixin(LitElement)) {
319
322
 
320
323
  _handleListItemAddButtonClick(e) {
321
324
  e.stopPropagation();
322
- /** Dispatched when the add button directly after the item is clicked. Event detail includes the key of the item directly above where the add button was clicked. */
323
- this.dispatchEvent(new CustomEvent('d2l-list-add-button-click', { detail: { key: e.target.key } }));
325
+ /**
326
+ * Dispatched when the add button directly after the item is clicked. Event detail includes position ('before' or 'after') and key.
327
+ * The key belongs to the list item adjacent to where the new item should be positioned.
328
+ * The position represents where the new item should be positioned relative to the item with that key.
329
+ * */
330
+ this.dispatchEvent(new CustomEvent('d2l-list-add-button-click', { detail: { key: e.target.key, position: e.detail.position } }));
324
331
  }
325
332
 
326
333
  _handleListItemNestedChange(e) {
@@ -368,6 +375,10 @@ class List extends PageableMixin(SelectionMixin(LitElement)) {
368
375
 
369
376
  _handleSlotChange() {
370
377
  this._updateItemShowingCount();
378
+ this.getItems().forEach((item, i) => {
379
+ if (i === 0) item.first = true;
380
+ else item.first = false;
381
+ });
371
382
 
372
383
  /** @ignore */
373
384
  this.dispatchEvent(new CustomEvent('d2l-list-item-showing-count-change', {
@@ -350,29 +350,29 @@
350
350
  "attributes": [
351
351
  {
352
352
  "name": "text",
353
- "description": "When text-visible is true, the text to show in the button. When text-visible is false, the text to show in the tooltip.",
353
+ "description": "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.",
354
354
  "type": "string"
355
355
  },
356
356
  {
357
357
  "name": "mode",
358
- "description": "Display mode of the component. Defaults to \"icon\" (plus icon is always visible). Other options are \"icon-and-text\" (plus icon and text are always visible), and \"icon-when-interacted\" (plus icon is only visible when hover or focus).",
358
+ "description": "Display mode of the component. Defaults to `icon` (plus icon is always visible). Other options are `icon-and-text` (plus icon and text are always visible), and `icon-when-interacted` (plus icon is only visible when hover or focus).",
359
359
  "type": "'icon'|'icon-and-text'|'icon-when-interacted'",
360
- "default": "\"ICON\""
360
+ "default": "\"icon\""
361
361
  }
362
362
  ],
363
363
  "properties": [
364
364
  {
365
365
  "name": "text",
366
366
  "attribute": "text",
367
- "description": "When text-visible is true, the text to show in the button. When text-visible is false, the text to show in the tooltip.",
367
+ "description": "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.",
368
368
  "type": "string"
369
369
  },
370
370
  {
371
371
  "name": "mode",
372
372
  "attribute": "mode",
373
- "description": "Display mode of the component. Defaults to \"icon\" (plus icon is always visible). Other options are \"icon-and-text\" (plus icon and text are always visible), and \"icon-when-interacted\" (plus icon is only visible when hover or focus).",
373
+ "description": "Display mode of the component. Defaults to `icon` (plus icon is always visible). Other options are `icon-and-text` (plus icon and text are always visible), and `icon-when-interacted` (plus icon is only visible when hover or focus).",
374
374
  "type": "'icon'|'icon-and-text'|'icon-when-interacted'",
375
- "default": "\"ICON\""
375
+ "default": "\"icon\""
376
376
  },
377
377
  {
378
378
  "name": "documentLocaleSettings",
@@ -8049,6 +8049,11 @@
8049
8049
  "description": "A color indicator to appear at the beginning of a list item. Expected value is a valid 3, 4, 6, or 8 character CSS color hex code (e.g., #006fbf).",
8050
8050
  "type": "string"
8051
8051
  },
8052
+ {
8053
+ "name": "first",
8054
+ "type": "boolean",
8055
+ "default": "false"
8056
+ },
8052
8057
  {
8053
8058
  "name": "noPrimaryAction",
8054
8059
  "attribute": "no-primary-action",
@@ -8397,6 +8402,11 @@
8397
8402
  "description": "A color indicator to appear at the beginning of a list item. Expected value is a valid 3, 4, 6, or 8 character CSS color hex code (e.g., #006fbf).",
8398
8403
  "type": "string"
8399
8404
  },
8405
+ {
8406
+ "name": "first",
8407
+ "type": "boolean",
8408
+ "default": "false"
8409
+ },
8400
8410
  {
8401
8411
  "name": "noPrimaryAction",
8402
8412
  "attribute": "no-primary-action",
@@ -8872,6 +8882,11 @@
8872
8882
  "description": "A color indicator to appear at the beginning of a list item. Expected value is a valid 3, 4, 6, or 8 character CSS color hex code (e.g., #006fbf).",
8873
8883
  "type": "string"
8874
8884
  },
8885
+ {
8886
+ "name": "first",
8887
+ "type": "boolean",
8888
+ "default": "false"
8889
+ },
8875
8890
  {
8876
8891
  "name": "noPrimaryAction",
8877
8892
  "attribute": "no-primary-action",
@@ -9177,7 +9192,7 @@
9177
9192
  },
9178
9193
  {
9179
9194
  "name": "d2l-list-add-button-click",
9180
- "description": "Dispatched when the add button directly after the item is clicked. Event detail includes the key of the item directly above where the add button was clicked."
9195
+ "description": "Dispatched when the add button directly after the item is clicked. Event detail includes position ('before' or 'after') and key.\nThe key belongs to the list item adjacent to where the new item should be positioned.\nThe position represents where the new item should be positioned relative to the item with that key."
9181
9196
  }
9182
9197
  ],
9183
9198
  "slots": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brightspace-ui/core",
3
- "version": "2.175.0",
3
+ "version": "2.176.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",