@spectrum-web-components/picker 0.14.0 → 0.14.2-overlay.33

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spectrum-web-components/picker",
3
- "version": "0.14.0",
3
+ "version": "0.14.2-overlay.33+499d5fe96",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -66,16 +66,16 @@
66
66
  ],
67
67
  "dependencies": {
68
68
  "@spectrum-web-components/base": "^0.7.4",
69
- "@spectrum-web-components/button": "^0.20.0",
69
+ "@spectrum-web-components/button": "^0.20.2-overlay.33+499d5fe96",
70
70
  "@spectrum-web-components/icon": "^0.12.8",
71
71
  "@spectrum-web-components/icons-ui": "^0.9.8",
72
72
  "@spectrum-web-components/icons-workflow": "^0.9.8",
73
- "@spectrum-web-components/menu": "^0.16.12",
74
- "@spectrum-web-components/overlay": "^0.19.0",
75
- "@spectrum-web-components/popover": "^0.12.12",
73
+ "@spectrum-web-components/menu": "^0.16.14-overlay.33+499d5fe96",
74
+ "@spectrum-web-components/overlay": "^0.19.2-overlay.33+499d5fe96",
75
+ "@spectrum-web-components/popover": "^0.12.14-overlay.33+499d5fe96",
76
76
  "@spectrum-web-components/reactive-controllers": "^0.3.5",
77
- "@spectrum-web-components/shared": "^0.15.5",
78
- "@spectrum-web-components/tray": "^0.5.0"
77
+ "@spectrum-web-components/shared": "^0.15.6-overlay.99+499d5fe96",
78
+ "@spectrum-web-components/tray": "^0.5.1-overlay.37+499d5fe96"
79
79
  },
80
80
  "devDependencies": {
81
81
  "@spectrum-css/picker": "^2.0.0"
@@ -88,5 +88,5 @@
88
88
  "./sync/index.js",
89
89
  "./sync/sp-*.js"
90
90
  ],
91
- "gitHead": "007a762c0257af4cd74e3781d877a8a8343acfd0"
91
+ "gitHead": "499d5fe966f35eb862c1983eb3a42641f213ac6a"
92
92
  }
package/src/Picker.d.ts CHANGED
@@ -1,12 +1,14 @@
1
1
  import { CSSResultArray, PropertyValues, TemplateResult } from '@spectrum-web-components/base';
2
+ import { StyleInfo } from '@spectrum-web-components/base/src/directives.js';
2
3
  import { Focusable } from '@spectrum-web-components/shared/src/focusable.js';
3
4
  import '@spectrum-web-components/icons-ui/icons/sp-icon-chevron100.js';
4
5
  import '@spectrum-web-components/icons-workflow/icons/sp-icon-alert.js';
6
+ import '@spectrum-web-components/overlay/sp-overlay.js';
5
7
  import '@spectrum-web-components/menu/sp-menu.js';
6
- import type { Menu, MenuItem, MenuItemAddedOrUpdatedEvent, MenuItemChildren, MenuItemRemovedEvent } from '@spectrum-web-components/menu';
8
+ import type { Menu, MenuItem, MenuItemChildren } from '@spectrum-web-components/menu';
7
9
  import '@spectrum-web-components/tray/sp-tray.js';
8
10
  import '@spectrum-web-components/popover/sp-popover.js';
9
- import { OverlayOptions, Placement, TriggerInteractions } from '@spectrum-web-components/overlay';
11
+ import { Placement } from '@spectrum-web-components/overlay';
10
12
  import { MatchMediaController } from '@spectrum-web-components/reactive-controllers/src/MatchMedia.js';
11
13
  declare const PickerBase_base: typeof Focusable & {
12
14
  new (...args: any[]): import("@spectrum-web-components/base").SizedElementInterface;
@@ -22,10 +24,6 @@ declare const PickerBase_base: typeof Focusable & {
22
24
  * @fires sp-closed - Announces that the overlay has been closed
23
25
  */
24
26
  export declare class PickerBase extends PickerBase_base {
25
- /**
26
- * @private
27
- */
28
- static openOverlay: (target: HTMLElement, interaction: TriggerInteractions, content: HTMLElement, options: OverlayOptions) => Promise<() => void>;
29
27
  protected isMobile: MatchMediaController;
30
28
  button: HTMLButtonElement;
31
29
  get target(): HTMLButtonElement | this;
@@ -37,8 +35,7 @@ export declare class PickerBase extends PickerBase_base {
37
35
  open: boolean;
38
36
  readonly: boolean;
39
37
  selects: undefined | 'single';
40
- menuItems: MenuItem[];
41
- private restoreChildren?;
38
+ get menuItems(): MenuItem[];
42
39
  optionsMenu: Menu;
43
40
  /**
44
41
  * @type {"auto" | "auto-start" | "auto-end" | "top" | "bottom" | "right" | "left" | "top-start" | "top-end" | "bottom-start" | "bottom-end" | "right-start" | "right-end" | "left-start" | "left-end" | "none"}
@@ -47,9 +44,9 @@ export declare class PickerBase extends PickerBase_base {
47
44
  placement: Placement;
48
45
  quiet: boolean;
49
46
  value: string;
50
- selectedItem?: MenuItem;
51
- private closeOverlay?;
52
- private popoverEl;
47
+ get selectedItem(): MenuItem | undefined;
48
+ set selectedItem(selectedItem: MenuItem | undefined);
49
+ _selectedItem?: MenuItem;
53
50
  protected listRole: 'listbox' | 'menu';
54
51
  protected itemRole: string;
55
52
  constructor();
@@ -64,33 +61,21 @@ export declare class PickerBase extends PickerBase_base {
64
61
  protected onKeydown: (event: KeyboardEvent) => void;
65
62
  setValueFromItem(item: MenuItem, menuChangeEvent?: Event): Promise<void>;
66
63
  protected setMenuItemSelected(item: MenuItem, value: boolean): void;
64
+ private preventToggle;
67
65
  toggle(target?: boolean): void;
68
66
  close(): void;
69
- overlayOpenCallback: () => Promise<void>;
70
- overlayCloseCallback: () => Promise<void>;
71
- private popoverFragment;
72
- private generatePopover;
73
- private openMenu;
74
- protected sizePopover(popover: HTMLElement): void;
75
- private closeMenu;
67
+ protected get containerStyles(): StyleInfo;
76
68
  protected get selectedItemContent(): MenuItemChildren;
69
+ protected set selectedItemContent(selectedItemContent: MenuItemChildren | undefined);
70
+ _selectedItemContent?: MenuItemChildren;
77
71
  protected renderLabelContent(content: Node[]): TemplateResult | Node[];
78
72
  protected get buttonContent(): TemplateResult[];
73
+ protected get renderOverlay(): TemplateResult;
79
74
  protected render(): TemplateResult;
80
75
  protected update(changes: PropertyValues<this>): void;
81
76
  protected get dismissHelper(): TemplateResult;
82
- protected get renderPopover(): TemplateResult;
83
- private _willUpdateItems;
84
- protected itemsUpdated: Promise<void>;
85
- /**
86
- * Acquire the available MenuItems in the Picker by
87
- * direct element query or by assuming the list managed
88
- * by the Menu within the open options overlay.
89
- */
90
- protected updateMenuItems(event?: MenuItemAddedOrUpdatedEvent | MenuItemRemovedEvent): void;
77
+ protected get renderContainer(): TemplateResult;
91
78
  protected manageSelection(): Promise<void>;
92
- private menuStatePromise;
93
- private menuStateResolver;
94
79
  private selectionPromise;
95
80
  private selectionResolver;
96
81
  protected getUpdateComplete(): Promise<boolean>;
package/src/Picker.dev.js CHANGED
@@ -13,10 +13,12 @@ var __decorateClass = (decorators, target, key, kind) => {
13
13
  import {
14
14
  html,
15
15
  nothing,
16
- render,
17
16
  SizedMixin
18
17
  } from "@spectrum-web-components/base";
19
- import { classMap } from "@spectrum-web-components/base/src/directives.js";
18
+ import {
19
+ classMap,
20
+ styleMap
21
+ } from "@spectrum-web-components/base/src/directives.js";
20
22
  import {
21
23
  property,
22
24
  query
@@ -24,15 +26,12 @@ import {
24
26
  import pickerStyles from "./picker.css.js";
25
27
  import chevronStyles from "@spectrum-web-components/icon/src/spectrum-icon-chevron.css.js";
26
28
  import { Focusable } from "@spectrum-web-components/shared/src/focusable.js";
27
- import { reparentChildren } from "@spectrum-web-components/shared/src/reparent-children.js";
28
29
  import "@spectrum-web-components/icons-ui/icons/sp-icon-chevron100.js";
29
30
  import "@spectrum-web-components/icons-workflow/icons/sp-icon-alert.js";
31
+ import "@spectrum-web-components/overlay/sp-overlay.js";
30
32
  import "@spectrum-web-components/menu/sp-menu.js";
31
33
  import "@spectrum-web-components/tray/sp-tray.js";
32
34
  import "@spectrum-web-components/popover/sp-popover.js";
33
- import {
34
- openOverlay
35
- } from "@spectrum-web-components/overlay";
36
35
  import {
37
36
  IS_MOBILE,
38
37
  MatchMediaController
@@ -53,7 +52,6 @@ export class PickerBase extends SizedMixin(Focusable) {
53
52
  this.open = false;
54
53
  this.readonly = false;
55
54
  this.selects = "single";
56
- this.menuItems = [];
57
55
  this.placement = "bottom-start";
58
56
  this.quiet = false;
59
57
  this.value = "";
@@ -67,29 +65,33 @@ export class PickerBase extends SizedMixin(Focusable) {
67
65
  event.preventDefault();
68
66
  this.toggle(true);
69
67
  };
70
- this.overlayOpenCallback = async () => {
71
- this.updateMenuItems();
72
- await this.itemsUpdated;
73
- await this.optionsMenu.updateComplete;
74
- requestAnimationFrame(() => this.menuStateResolver());
75
- };
76
- this.overlayCloseCallback = async () => {
77
- if (this.restoreChildren) {
78
- this.restoreChildren();
79
- this.restoreChildren = void 0;
80
- }
81
- this.close();
82
- requestAnimationFrame(() => this.menuStateResolver());
83
- };
84
- this._willUpdateItems = false;
85
- this.itemsUpdated = Promise.resolve();
86
- this.menuStatePromise = Promise.resolve();
68
+ this.preventToggle = false;
87
69
  this.selectionPromise = Promise.resolve();
88
70
  this.onKeydown = this.onKeydown.bind(this);
71
+ this.addEventListener("focusout", (event) => {
72
+ if (event.relatedTarget && this.contains(event.relatedTarget) || event.target !== this) {
73
+ return;
74
+ }
75
+ this.open = false;
76
+ });
89
77
  }
90
78
  get target() {
91
79
  return this.button;
92
80
  }
81
+ get menuItems() {
82
+ return this.optionsMenu.childItems;
83
+ }
84
+ get selectedItem() {
85
+ return this._selectedItem;
86
+ }
87
+ set selectedItem(selectedItem) {
88
+ this.selectedItemContent = selectedItem ? selectedItem.itemChildren : void 0;
89
+ if (selectedItem === this.selectedItem)
90
+ return;
91
+ const oldSelectedItem = this.selectedItem;
92
+ this._selectedItem = selectedItem;
93
+ this.requestUpdate("selectedItem", oldSelectedItem);
94
+ }
93
95
  get focusElement() {
94
96
  if (this.open) {
95
97
  return this.optionsMenu;
@@ -128,8 +130,8 @@ export class PickerBase extends SizedMixin(Focusable) {
128
130
  handleChange(event) {
129
131
  const target = event.target;
130
132
  const [selected] = target.selectedItems;
133
+ event.stopPropagation();
131
134
  if (event.cancelable) {
132
- event.stopPropagation();
133
135
  this.setValueFromItem(selected, event);
134
136
  } else {
135
137
  this.open = false;
@@ -173,6 +175,8 @@ export class PickerBase extends SizedMixin(Focusable) {
173
175
  item.selected = value;
174
176
  }
175
177
  toggle(target) {
178
+ if (this.preventToggle)
179
+ return;
176
180
  if (this.readonly) {
177
181
  return;
178
182
  }
@@ -184,77 +188,27 @@ export class PickerBase extends SizedMixin(Focusable) {
184
188
  }
185
189
  this.open = false;
186
190
  }
187
- async generatePopover() {
188
- if (!this.popoverFragment) {
189
- this.popoverFragment = document.createDocumentFragment();
190
- }
191
- render(this.renderPopover, this.popoverFragment, { host: this });
192
- this.popoverEl = this.popoverFragment.children[0];
193
- this.optionsMenu = this.popoverEl.children[1];
194
- }
195
- async openMenu() {
196
- let reparentableChildren = [];
197
- const deprecatedMenu = this.querySelector(":scope > sp-menu");
198
- await this.generatePopover();
199
- if (deprecatedMenu) {
200
- reparentableChildren = Array.from(deprecatedMenu.children);
201
- } else {
202
- reparentableChildren = Array.from(this.children).filter(
203
- (element) => {
204
- return !element.hasAttribute("slot");
205
- }
206
- );
207
- }
208
- if (reparentableChildren.length === 0) {
209
- this.menuStateResolver();
210
- return;
211
- }
212
- this.restoreChildren = reparentChildren(reparentableChildren, this.optionsMenu, {
213
- position: "beforeend",
214
- prepareCallback: (el) => {
215
- if (this.value === el.value) {
216
- this.setMenuItemSelected(el, true);
217
- }
218
- return (el2) => {
219
- if (typeof el2.focused !== "undefined") {
220
- el2.focused = false;
221
- }
222
- };
223
- }
224
- });
225
- this.sizePopover(this.popoverEl);
226
- if (true) {
227
- window.__swc.ignoreWarningLevels.deprecation = true;
228
- }
229
- this.closeOverlay = Picker.openOverlay(this, "modal", this.popoverEl, {
230
- placement: this.isMobile.matches ? "none" : this.placement,
231
- receivesFocus: "auto"
232
- });
233
- if (true) {
234
- window.__swc.ignoreWarningLevels.deprecation = false;
235
- }
236
- }
237
- sizePopover(popover) {
191
+ get containerStyles() {
238
192
  if (this.isMobile.matches) {
239
- popover.style.setProperty("--swc-menu-width", `100%`);
240
- return;
193
+ return {
194
+ "--swc-menu-width": "100%"
195
+ };
241
196
  }
242
197
  if (this.quiet)
243
- return;
244
- popover.style.setProperty("min-width", `${this.offsetWidth}px`);
245
- }
246
- async closeMenu() {
247
- if (this.closeOverlay) {
248
- const closeOverlay = this.closeOverlay;
249
- delete this.closeOverlay;
250
- (await closeOverlay)();
251
- }
198
+ return {};
199
+ return {
200
+ "min-width": `${this.offsetWidth}px`
201
+ };
252
202
  }
253
203
  get selectedItemContent() {
254
- if (this.selectedItem) {
255
- return this.selectedItem.itemChildren;
256
- }
257
- return { icon: [], content: [] };
204
+ return this._selectedItemContent || { icon: [], content: [] };
205
+ }
206
+ set selectedItemContent(selectedItemContent) {
207
+ if (selectedItemContent === this.selectedItemContent)
208
+ return;
209
+ const oldContent = this.selectedItemContent;
210
+ this._selectedItemContent = selectedItemContent;
211
+ this.requestUpdate("selectedItemContent", oldContent);
258
212
  }
259
213
  renderLabelContent(content) {
260
214
  if (this.value && this.selectedItem) {
@@ -288,13 +242,40 @@ export class PickerBase extends SizedMixin(Focusable) {
288
242
  `
289
243
  ];
290
244
  }
245
+ get renderOverlay() {
246
+ return html`
247
+ <sp-overlay
248
+ .triggerElement=${this}
249
+ .offset=${0}
250
+ ?open=${this.open}
251
+ .placement=${this.placement}
252
+ type="auto"
253
+ @sp-menu-item-added-or-updated=${this.manageSelection}
254
+ @beforetoggle=${(event) => {
255
+ this.open = event.target.open;
256
+ if (!this.open) {
257
+ this.preventToggle = true;
258
+ requestAnimationFrame(
259
+ () => this.preventToggle = false
260
+ );
261
+ this.optionsMenu.updateSelectedItemIndex();
262
+ this.optionsMenu.closeDescendentOverlays();
263
+ } else {
264
+ this.optionsMenu.focus();
265
+ }
266
+ }}
267
+ >
268
+ ${this.renderContainer}
269
+ </sp-overlay>
270
+ `;
271
+ }
291
272
  // a helper to throw focus to the button is needed because Safari
292
273
  // won't include buttons in the tab order even with tabindex="0"
293
274
  render() {
294
275
  return html`
295
276
  <span
296
277
  id="focus-helper"
297
- tabindex="${this.focused ? "-1" : "0"}"
278
+ tabindex="${this.focused || this.open ? "-1" : "0"}"
298
279
  @focus=${this.onHelperFocus}
299
280
  ></span>
300
281
  <button
@@ -311,6 +292,7 @@ export class PickerBase extends SizedMixin(Focusable) {
311
292
  >
312
293
  ${this.buttonContent}
313
294
  </button>
295
+ ${this.renderOverlay}
314
296
  `;
315
297
  }
316
298
  update(changes) {
@@ -320,18 +302,10 @@ export class PickerBase extends SizedMixin(Focusable) {
320
302
  if (changes.has("disabled") && this.disabled) {
321
303
  this.open = false;
322
304
  }
323
- if (changes.has("open") && (this.open || typeof changes.get("open") !== "undefined")) {
324
- this.menuStatePromise = new Promise(
325
- (res) => this.menuStateResolver = res
326
- );
327
- if (this.open) {
328
- this.openMenu();
329
- } else {
330
- this.closeMenu();
331
- }
332
- }
333
- if (changes.has("value") && !changes.has("selectedItem")) {
334
- this.updateMenuItems();
305
+ if (changes.has("value")) {
306
+ requestAnimationFrame(() => {
307
+ this.manageSelection();
308
+ });
335
309
  }
336
310
  if (true) {
337
311
  if (!this.hasUpdated && this.querySelector("sp-menu")) {
@@ -357,7 +331,7 @@ export class PickerBase extends SizedMixin(Focusable) {
357
331
  </div>
358
332
  `;
359
333
  }
360
- get renderPopover() {
334
+ get renderContainer() {
361
335
  const content = html`
362
336
  ${this.dismissHelper}
363
337
  <sp-menu
@@ -365,7 +339,10 @@ export class PickerBase extends SizedMixin(Focusable) {
365
339
  role="${this.listRole}"
366
340
  @change=${this.handleChange}
367
341
  .selects=${this.selects}
368
- ></sp-menu>
342
+ .selected=${this.value ? [this.value] : []}
343
+ >
344
+ <slot></slot>
345
+ </sp-menu>
369
346
  ${this.dismissHelper}
370
347
  `;
371
348
  if (this.isMobile.matches) {
@@ -373,9 +350,7 @@ export class PickerBase extends SizedMixin(Focusable) {
373
350
  <sp-tray
374
351
  id="popover"
375
352
  role="dialog"
376
- @sp-menu-item-added-or-updated=${this.updateMenuItems}
377
- .overlayOpenCallback=${this.overlayOpenCallback}
378
- .overlayCloseCallback=${this.overlayCloseCallback}
353
+ style=${styleMap(this.containerStyles)}
379
354
  >
380
355
  ${content}
381
356
  </sp-tray>
@@ -385,56 +360,21 @@ export class PickerBase extends SizedMixin(Focusable) {
385
360
  <sp-popover
386
361
  id="popover"
387
362
  role="dialog"
388
- @sp-menu-item-added-or-updated=${this.updateMenuItems}
389
- .overlayOpenCallback=${this.overlayOpenCallback}
390
- .overlayCloseCallback=${this.overlayCloseCallback}
363
+ style=${styleMap(this.containerStyles)}
364
+ placement=${this.placement}
391
365
  >
392
366
  ${content}
393
367
  </sp-popover>
394
368
  `;
395
369
  }
396
- /**
397
- * Acquire the available MenuItems in the Picker by
398
- * direct element query or by assuming the list managed
399
- * by the Menu within the open options overlay.
400
- */
401
- updateMenuItems(event) {
402
- if (this.open && (event == null ? void 0 : event.type) === "sp-menu-item-removed")
403
- return;
404
- if (this._willUpdateItems)
405
- return;
406
- this._willUpdateItems = true;
407
- if ((event == null ? void 0 : event.item) === this.selectedItem) {
408
- this.requestUpdate();
409
- }
410
- let resolve = () => {
411
- return;
412
- };
413
- this.itemsUpdated = new Promise((res) => resolve = res);
414
- window.requestAnimationFrame(async () => {
415
- if (this.open) {
416
- await this.optionsMenu.updateComplete;
417
- this.menuItems = this.optionsMenu.childItems;
418
- } else {
419
- this.menuItems = [
420
- ...this.querySelectorAll(
421
- 'sp-menu-item:not([slot="submenu"] *)'
422
- )
423
- ];
424
- }
425
- this.manageSelection();
426
- resolve();
427
- this._willUpdateItems = false;
428
- });
429
- }
430
370
  async manageSelection() {
431
371
  if (this.selects == null)
432
372
  return;
433
- await this.menuStatePromise;
434
373
  this.selectionPromise = new Promise(
435
374
  (res) => this.selectionResolver = res
436
375
  );
437
376
  let selectedItem;
377
+ await this.optionsMenu.updateComplete;
438
378
  this.menuItems.forEach((item) => {
439
379
  if (this.value === item.value && !item.disabled) {
440
380
  selectedItem = item;
@@ -457,18 +397,10 @@ export class PickerBase extends SizedMixin(Focusable) {
457
397
  }
458
398
  async getUpdateComplete() {
459
399
  const complete = await super.getUpdateComplete();
460
- await this.menuStatePromise;
461
- await this.itemsUpdated;
462
400
  await this.selectionPromise;
463
401
  return complete;
464
402
  }
465
403
  connectedCallback() {
466
- this.updateMenuItems();
467
- this.addEventListener(
468
- "sp-menu-item-added-or-updated",
469
- this.updateMenuItems
470
- );
471
- this.addEventListener("sp-menu-item-removed", this.updateMenuItems);
472
404
  super.connectedCallback();
473
405
  }
474
406
  disconnectedCallback() {
@@ -476,12 +408,6 @@ export class PickerBase extends SizedMixin(Focusable) {
476
408
  super.disconnectedCallback();
477
409
  }
478
410
  }
479
- /**
480
- * @private
481
- */
482
- PickerBase.openOverlay = async (target, interaction, content, options) => {
483
- return await openOverlay(target, interaction, content, options);
484
- };
485
411
  __decorateClass([
486
412
  query("#button")
487
413
  ], PickerBase.prototype, "button", 2);
@@ -506,6 +432,9 @@ __decorateClass([
506
432
  __decorateClass([
507
433
  property({ type: Boolean, reflect: true })
508
434
  ], PickerBase.prototype, "readonly", 2);
435
+ __decorateClass([
436
+ query("sp-menu")
437
+ ], PickerBase.prototype, "optionsMenu", 2);
509
438
  __decorateClass([
510
439
  property()
511
440
  ], PickerBase.prototype, "placement", 2);
@@ -517,7 +446,10 @@ __decorateClass([
517
446
  ], PickerBase.prototype, "value", 2);
518
447
  __decorateClass([
519
448
  property({ attribute: false })
520
- ], PickerBase.prototype, "selectedItem", 2);
449
+ ], PickerBase.prototype, "selectedItem", 1);
450
+ __decorateClass([
451
+ property({ attribute: false })
452
+ ], PickerBase.prototype, "selectedItemContent", 1);
521
453
  export class Picker extends PickerBase {
522
454
  constructor() {
523
455
  super(...arguments);