@spectrum-web-components/picker 0.41.2 → 0.42.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.
package/README.md CHANGED
@@ -414,7 +414,7 @@ When the `value` of an `<sp-picker>` matches the `value` attribute or the trimme
414
414
  </sp-picker>
415
415
  <br />
416
416
  <br />
417
- <sp-field-label for="picker-disabled">Quiet:</sp-field-label>
417
+ <sp-field-label for="picker-disabled-quiet">Quiet:</sp-field-label>
418
418
  <sp-picker
419
419
  label="Select a Country with a very long label, too long in fact"
420
420
  disabled
@@ -431,6 +431,34 @@ When the `value` of an `<sp-picker>` matches the `value` attribute or the trimme
431
431
  </sp-picker>
432
432
  ```
433
433
 
434
+ ### Pending
435
+
436
+ When in pending state, `<sp-picker>` elements will not respond to click events and will be delivered with a `<sp-progress-circle>` to visually outline that it is pending. It will not toggle open or display its `<sp-menu-item>` descendants until the attribute is removed.
437
+
438
+ ```html
439
+ <sp-field-label for="picker-loading">Standard:</sp-field-label>
440
+ <sp-picker id="picker-loading" label="Loading blending modes..." pending>
441
+ <sp-menu-item>Pass through</sp-menu-item>
442
+ <sp-menu-item>Normal</sp-menu-item>
443
+ <sp-menu-item>Multiply</sp-menu-item>
444
+ <sp-menu-item>Screen</sp-menu-item>
445
+ </sp-picker>
446
+ <br />
447
+ <br />
448
+ <sp-field-label for="picker-loading-quiet">Quiet:</sp-field-label>
449
+ <sp-picker
450
+ id="picker-loading-quiet"
451
+ label="Loading blending modes..."
452
+ pending
453
+ quiet
454
+ >
455
+ <sp-menu-item>Pass through</sp-menu-item>
456
+ <sp-menu-item>Normal</sp-menu-item>
457
+ <sp-menu-item>Multiply</sp-menu-item>
458
+ <sp-menu-item>Screen</sp-menu-item>
459
+ </sp-picker>
460
+ ```
461
+
434
462
  ## Accessibility
435
463
 
436
464
  To render accessibly, an `<sp-picker>` element should be paired with an `<sp-field-label>` element that has a `for` attribute referencing the `id` of the `<sp-picker>` element. For an accessible label that renders within the bounds of the picker itself, but still fulfills the accessibility contract, use the `label` attribute or a `<span slot="label">` as a child element of `<sp-picker>`.
@@ -113,6 +113,29 @@
113
113
  "attribute": "invalid",
114
114
  "reflects": true
115
115
  },
116
+ {
117
+ "kind": "field",
118
+ "name": "pending",
119
+ "type": {
120
+ "text": "boolean"
121
+ },
122
+ "privacy": "public",
123
+ "default": "false",
124
+ "description": "Whether the items are currently loading.",
125
+ "attribute": "pending",
126
+ "reflects": true
127
+ },
128
+ {
129
+ "kind": "field",
130
+ "name": "pendingLabel",
131
+ "type": {
132
+ "text": "string"
133
+ },
134
+ "privacy": "public",
135
+ "default": "'Pending'",
136
+ "description": "Defines a string value that labels the Picker while it is in pending state.",
137
+ "attribute": "pending-label"
138
+ },
116
139
  {
117
140
  "kind": "field",
118
141
  "name": "label",
@@ -542,6 +565,24 @@
542
565
  }
543
566
  ]
544
567
  },
568
+ {
569
+ "kind": "method",
570
+ "name": "handleBeforetoggle",
571
+ "privacy": "protected",
572
+ "return": {
573
+ "type": {
574
+ "text": "void"
575
+ }
576
+ },
577
+ "parameters": [
578
+ {
579
+ "name": "event",
580
+ "type": {
581
+ "text": "Event & {\n target: Overlay;\n newState: 'open' | 'closed';\n }"
582
+ }
583
+ }
584
+ ]
585
+ },
545
586
  {
546
587
  "kind": "method",
547
588
  "name": "renderLabelContent",
@@ -842,6 +883,24 @@
842
883
  "default": "false",
843
884
  "fieldName": "invalid"
844
885
  },
886
+ {
887
+ "name": "pending",
888
+ "type": {
889
+ "text": "boolean"
890
+ },
891
+ "default": "false",
892
+ "description": "Whether the items are currently loading.",
893
+ "fieldName": "pending"
894
+ },
895
+ {
896
+ "name": "pending-label",
897
+ "type": {
898
+ "text": "string"
899
+ },
900
+ "default": "'Pending'",
901
+ "description": "Defines a string value that labels the Picker while it is in pending state.",
902
+ "fieldName": "pendingLabel"
903
+ },
845
904
  {
846
905
  "name": "label",
847
906
  "type": {
@@ -1043,6 +1102,37 @@
1043
1102
  "module": "src/Picker.js"
1044
1103
  }
1045
1104
  },
1105
+ {
1106
+ "kind": "field",
1107
+ "name": "pending",
1108
+ "type": {
1109
+ "text": "boolean"
1110
+ },
1111
+ "privacy": "public",
1112
+ "default": "false",
1113
+ "description": "Whether the items are currently loading.",
1114
+ "attribute": "pending",
1115
+ "reflects": true,
1116
+ "inheritedFrom": {
1117
+ "name": "PickerBase",
1118
+ "module": "src/Picker.js"
1119
+ }
1120
+ },
1121
+ {
1122
+ "kind": "field",
1123
+ "name": "pendingLabel",
1124
+ "type": {
1125
+ "text": "string"
1126
+ },
1127
+ "privacy": "public",
1128
+ "default": "'Pending'",
1129
+ "description": "Defines a string value that labels the Picker while it is in pending state.",
1130
+ "attribute": "pending-label",
1131
+ "inheritedFrom": {
1132
+ "name": "PickerBase",
1133
+ "module": "src/Picker.js"
1134
+ }
1135
+ },
1046
1136
  {
1047
1137
  "kind": "field",
1048
1138
  "name": "label",
@@ -1598,6 +1688,28 @@
1598
1688
  "module": "src/Picker.js"
1599
1689
  }
1600
1690
  },
1691
+ {
1692
+ "kind": "method",
1693
+ "name": "handleBeforetoggle",
1694
+ "privacy": "protected",
1695
+ "return": {
1696
+ "type": {
1697
+ "text": "void"
1698
+ }
1699
+ },
1700
+ "parameters": [
1701
+ {
1702
+ "name": "event",
1703
+ "type": {
1704
+ "text": "Event & {\n target: Overlay;\n newState: 'open' | 'closed';\n }"
1705
+ }
1706
+ }
1707
+ ],
1708
+ "inheritedFrom": {
1709
+ "name": "PickerBase",
1710
+ "module": "src/Picker.js"
1711
+ }
1712
+ },
1601
1713
  {
1602
1714
  "kind": "method",
1603
1715
  "name": "renderLabelContent",
@@ -2029,6 +2141,32 @@
2029
2141
  "module": "src/Picker.ts"
2030
2142
  }
2031
2143
  },
2144
+ {
2145
+ "name": "pending",
2146
+ "type": {
2147
+ "text": "boolean"
2148
+ },
2149
+ "default": "false",
2150
+ "description": "Whether the items are currently loading.",
2151
+ "fieldName": "pending",
2152
+ "inheritedFrom": {
2153
+ "name": "PickerBase",
2154
+ "module": "src/Picker.ts"
2155
+ }
2156
+ },
2157
+ {
2158
+ "name": "pending-label",
2159
+ "type": {
2160
+ "text": "string"
2161
+ },
2162
+ "default": "'Pending'",
2163
+ "description": "Defines a string value that labels the Picker while it is in pending state.",
2164
+ "fieldName": "pendingLabel",
2165
+ "inheritedFrom": {
2166
+ "name": "PickerBase",
2167
+ "module": "src/Picker.ts"
2168
+ }
2169
+ },
2032
2170
  {
2033
2171
  "name": "label",
2034
2172
  "type": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spectrum-web-components/picker",
3
- "version": "0.41.2",
3
+ "version": "0.42.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -65,22 +65,23 @@
65
65
  "lit-html"
66
66
  ],
67
67
  "dependencies": {
68
- "@spectrum-web-components/base": "^0.41.2",
69
- "@spectrum-web-components/button": "^0.41.2",
70
- "@spectrum-web-components/field-label": "^0.41.2",
71
- "@spectrum-web-components/icon": "^0.41.2",
72
- "@spectrum-web-components/icons-ui": "^0.41.2",
73
- "@spectrum-web-components/icons-workflow": "^0.41.2",
74
- "@spectrum-web-components/menu": "^0.41.2",
75
- "@spectrum-web-components/overlay": "^0.41.2",
76
- "@spectrum-web-components/popover": "^0.41.2",
77
- "@spectrum-web-components/reactive-controllers": "^0.41.2",
78
- "@spectrum-web-components/shared": "^0.41.2",
79
- "@spectrum-web-components/tooltip": "^0.41.2",
80
- "@spectrum-web-components/tray": "^0.41.2"
68
+ "@spectrum-web-components/base": "^0.42.0",
69
+ "@spectrum-web-components/button": "^0.42.0",
70
+ "@spectrum-web-components/field-label": "^0.42.0",
71
+ "@spectrum-web-components/icon": "^0.42.0",
72
+ "@spectrum-web-components/icons-ui": "^0.42.0",
73
+ "@spectrum-web-components/icons-workflow": "^0.42.0",
74
+ "@spectrum-web-components/menu": "^0.42.0",
75
+ "@spectrum-web-components/overlay": "^0.42.0",
76
+ "@spectrum-web-components/popover": "^0.42.0",
77
+ "@spectrum-web-components/progress-circle": "^0.42.0",
78
+ "@spectrum-web-components/reactive-controllers": "^0.42.0",
79
+ "@spectrum-web-components/shared": "^0.42.0",
80
+ "@spectrum-web-components/tooltip": "^0.42.0",
81
+ "@spectrum-web-components/tray": "^0.42.0"
81
82
  },
82
83
  "devDependencies": {
83
- "@spectrum-css/picker": "^7.2.5"
84
+ "@spectrum-css/picker": "^7.2.6"
84
85
  },
85
86
  "types": "./src/index.d.ts",
86
87
  "customElements": "custom-elements.json",
@@ -90,5 +91,5 @@
90
91
  "./sync/index.js",
91
92
  "./sync/sp-*.js"
92
93
  ],
93
- "gitHead": "78c3f16b08c9133c9e5ca88d0c9fef5ea7d2ab87"
94
+ "gitHead": "9b3bd55ff8e8f9438817255976e77fd456b19d72"
94
95
  }
package/src/Picker.d.ts CHANGED
@@ -8,7 +8,7 @@ import '@spectrum-web-components/menu/sp-menu.js';
8
8
  import type { Menu, MenuItem, MenuItemChildren } from '@spectrum-web-components/menu';
9
9
  import { Placement } from '@spectrum-web-components/overlay';
10
10
  import { MatchMediaController } from '@spectrum-web-components/reactive-controllers/src/MatchMedia.js';
11
- import type { Overlay } from '@spectrum-web-components/overlay/src/Overlay.js';
11
+ import { Overlay } from '@spectrum-web-components/overlay/src/Overlay.js';
12
12
  import type { FieldLabel } from '@spectrum-web-components/field-label';
13
13
  export declare const DESCRIPTION_ID = "option-picker";
14
14
  declare const PickerBase_base: typeof Focusable & {
@@ -24,6 +24,10 @@ export declare class PickerBase extends PickerBase_base {
24
24
  focused: boolean;
25
25
  icons?: 'only' | 'none';
26
26
  invalid: boolean;
27
+ /** Whether the items are currently loading. */
28
+ pending: boolean;
29
+ /** Defines a string value that labels the Picker while it is in pending state. */
30
+ pendingLabel: string;
27
31
  label?: string;
28
32
  open: boolean;
29
33
  readonly: boolean;
@@ -69,6 +73,10 @@ export declare class PickerBase extends PickerBase_base {
69
73
  protected handleTooltipSlotchange(event: Event & {
70
74
  target: HTMLSlotElement;
71
75
  }): void;
76
+ protected handleBeforetoggle(event: Event & {
77
+ target: Overlay;
78
+ newState: 'open' | 'closed';
79
+ }): void;
72
80
  protected renderLabelContent(content: Node[]): TemplateResult | Node[];
73
81
  protected get buttonContent(): TemplateResult[];
74
82
  applyFocusElementLabel: (value: string, labelElement: FieldLabel) => void;
package/src/Picker.dev.js CHANGED
@@ -18,7 +18,8 @@ import {
18
18
  import {
19
19
  classMap,
20
20
  ifDefined,
21
- styleMap
21
+ styleMap,
22
+ when
22
23
  } from "@spectrum-web-components/base/src/directives.js";
23
24
  import {
24
25
  property,
@@ -50,6 +51,8 @@ export class PickerBase extends SizedMixin(Focusable, { noDefaultSize: true }) {
50
51
  this.disabled = false;
51
52
  this.focused = false;
52
53
  this.invalid = false;
54
+ this.pending = false;
55
+ this.pendingLabel = "Pending";
53
56
  this.open = false;
54
57
  this.readonly = false;
55
58
  this.selects = "single";
@@ -142,15 +145,21 @@ export class PickerBase extends SizedMixin(Focusable, { noDefaultSize: true }) {
142
145
  }
143
146
  this.pointerdownState = this.open;
144
147
  this.preventNextToggle = "maybe";
148
+ let cleanupAction = 0;
145
149
  const cleanup = () => {
146
- document.removeEventListener("pointerup", cleanup);
147
- document.removeEventListener("pointercancel", cleanup);
148
- requestAnimationFrame(() => {
149
- this.preventNextToggle = "no";
150
+ cancelAnimationFrame(cleanupAction);
151
+ cleanupAction = requestAnimationFrame(async () => {
152
+ document.removeEventListener("pointerup", cleanup);
153
+ document.removeEventListener("pointercancel", cleanup);
154
+ this.button.removeEventListener("click", cleanup);
155
+ requestAnimationFrame(() => {
156
+ this.preventNextToggle = "no";
157
+ });
150
158
  });
151
159
  };
152
160
  document.addEventListener("pointerup", cleanup);
153
161
  document.addEventListener("pointercancel", cleanup);
162
+ this.button.addEventListener("click", cleanup);
154
163
  this.handleActivate();
155
164
  }
156
165
  handleButtonFocus(event) {
@@ -181,6 +190,7 @@ export class PickerBase extends SizedMixin(Focusable, { noDefaultSize: true }) {
181
190
  this.button.focus();
182
191
  }
183
192
  handleChange(event) {
193
+ this.preventNextToggle = "no";
184
194
  const target = event.target;
185
195
  const [selected] = target.selectedItems;
186
196
  event.stopPropagation();
@@ -233,7 +243,7 @@ export class PickerBase extends SizedMixin(Focusable, { noDefaultSize: true }) {
233
243
  item.selected = value;
234
244
  }
235
245
  toggle(target) {
236
- if (this.readonly) {
246
+ if (this.readonly || this.pending) {
237
247
  return;
238
248
  }
239
249
  this.open = typeof target !== "undefined" ? target : !this.open;
@@ -265,6 +275,22 @@ export class PickerBase extends SizedMixin(Focusable, { noDefaultSize: true }) {
265
275
  handleTooltipSlotchange(event) {
266
276
  this.tooltipEl = event.target.assignedElements()[0];
267
277
  }
278
+ handleBeforetoggle(event) {
279
+ if (event.composedPath()[0] !== event.target) {
280
+ return;
281
+ }
282
+ if (event.newState === "closed") {
283
+ if (this.preventNextToggle === "no") {
284
+ this.open = false;
285
+ } else if (!this.pointerdownState) {
286
+ this.overlayElement.manuallyKeepOpen();
287
+ }
288
+ }
289
+ if (!this.open) {
290
+ this.optionsMenu.updateSelectedItemIndex();
291
+ this.optionsMenu.closeDescendentOverlays();
292
+ }
293
+ }
268
294
  renderLabelContent(content) {
269
295
  if (this.value && this.selectedItem) {
270
296
  return content;
@@ -313,11 +339,23 @@ export class PickerBase extends SizedMixin(Focusable, { noDefaultSize: true }) {
313
339
  ` : html`
314
340
  <span hidden id="applied-label">${appliedLabel}</span>
315
341
  `}
316
- ${this.invalid ? html`
342
+ ${this.invalid && !this.pending ? html`
317
343
  <sp-icon-alert
318
344
  class="validation-icon"
319
345
  ></sp-icon-alert>
320
346
  ` : nothing}
347
+ ${when(this.pending, () => {
348
+ import("@spectrum-web-components/progress-circle/sp-progress-circle.js");
349
+ return html`
350
+ <sp-progress-circle
351
+ id="loader"
352
+ size="s"
353
+ indeterminate
354
+ aria-valuetext=${this.pendingLabel}
355
+ class="progress-circle"
356
+ ></sp-progress-circle>
357
+ `;
358
+ })}
321
359
  <sp-icon-chevron100
322
360
  class="picker ${chevronClass[this.size]}"
323
361
  ></sp-icon-chevron100>
@@ -343,18 +381,7 @@ export class PickerBase extends SizedMixin(Focusable, { noDefaultSize: true }) {
343
381
  .type=${this.isMobile.matches ? "modal" : "auto"}
344
382
  .receivesFocus=${"true"}
345
383
  .willPreventClose=${this.preventNextToggle !== "no" && this.open && this.dependenciesLoaded}
346
- @beforetoggle=${(event) => {
347
- if (event.composedPath()[0] !== event.target) {
348
- return;
349
- }
350
- if (event.newState === "closed") {
351
- this.open = false;
352
- }
353
- if (!this.open) {
354
- this.optionsMenu.updateSelectedItemIndex();
355
- this.optionsMenu.closeDescendentOverlays();
356
- }
357
- }}
384
+ @beforetoggle=${this.handleBeforetoggle}
358
385
  >
359
386
  ${container}
360
387
  </sp-overlay>
@@ -385,7 +412,7 @@ export class PickerBase extends SizedMixin(Focusable, { noDefaultSize: true }) {
385
412
  aria-describedby="tooltip"
386
413
  aria-expanded=${this.open ? "true" : "false"}
387
414
  aria-haspopup="true"
388
- aria-labelledby="icon label applied-label"
415
+ aria-labelledby="loader icon label applied-label"
389
416
  id="button"
390
417
  class=${ifDefined(
391
418
  this.labelAlignment ? `label-${this.labelAlignment}` : void 0
@@ -414,6 +441,9 @@ export class PickerBase extends SizedMixin(Focusable, { noDefaultSize: true }) {
414
441
  if (changes.has("disabled") && this.disabled) {
415
442
  this.open = false;
416
443
  }
444
+ if (changes.has("pending") && this.pending) {
445
+ this.open = false;
446
+ }
417
447
  if (changes.has("value")) {
418
448
  this.shouldScheduleManageSelection();
419
449
  }
@@ -623,6 +653,12 @@ __decorateClass([
623
653
  __decorateClass([
624
654
  property({ type: Boolean, reflect: true })
625
655
  ], PickerBase.prototype, "invalid", 2);
656
+ __decorateClass([
657
+ property({ type: Boolean, reflect: true })
658
+ ], PickerBase.prototype, "pending", 2);
659
+ __decorateClass([
660
+ property({ type: String, attribute: "pending-label" })
661
+ ], PickerBase.prototype, "pendingLabel", 2);
626
662
  __decorateClass([
627
663
  property()
628
664
  ], PickerBase.prototype, "label", 2);
@@ -665,7 +701,7 @@ export class Picker extends PickerBase {
665
701
  this.handleKeydown = (event) => {
666
702
  const { code } = event;
667
703
  this.focused = true;
668
- if (!code.startsWith("Arrow") || this.readonly) {
704
+ if (!code.startsWith("Arrow") || this.readonly || this.pending) {
669
705
  return;
670
706
  }
671
707
  if (code === "ArrowUp" || code === "ArrowDown") {