Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
224 changes: 98 additions & 126 deletions packages/picker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,26 +43,22 @@ import { Picker } from '@spectrum-web-components/picker';
```html demo
<sp-field-group>
<sp-picker size="s" label="Selection type">
<sp-menu>
<sp-menu-item>Deselect</sp-menu-item>
<sp-menu-item>Select inverse</sp-menu-item>
<sp-menu-item>Feather...</sp-menu-item>
<sp-menu-item>Select and mask...</sp-menu-item>
<sp-menu-divider></sp-menu-divider>
<sp-menu-item>Save selection</sp-menu-item>
<sp-menu-item disabled>Make work path</sp-menu-item>
</sp-menu>
<sp-menu-item>Deselect</sp-menu-item>
<sp-menu-item>Select inverse</sp-menu-item>
<sp-menu-item>Feather...</sp-menu-item>
<sp-menu-item>Select and mask...</sp-menu-item>
<sp-menu-divider></sp-menu-divider>
<sp-menu-item>Save selection</sp-menu-item>
<sp-menu-item disabled>Make work path</sp-menu-item>
</sp-picker>
<sp-picker quiet size="s" label="Selection type">
<sp-menu>
<sp-menu-item>Deselect</sp-menu-item>
<sp-menu-item>Select inverse</sp-menu-item>
<sp-menu-item>Feather...</sp-menu-item>
<sp-menu-item>Select and mask...</sp-menu-item>
<sp-menu-divider></sp-menu-divider>
<sp-menu-item>Save selection</sp-menu-item>
<sp-menu-item disabled>Make work path</sp-menu-item>
</sp-menu>
<sp-menu-item>Deselect</sp-menu-item>
<sp-menu-item>Select inverse</sp-menu-item>
<sp-menu-item>Feather...</sp-menu-item>
<sp-menu-item>Select and mask...</sp-menu-item>
<sp-menu-divider></sp-menu-divider>
<sp-menu-item>Save selection</sp-menu-item>
<sp-menu-item disabled>Make work path</sp-menu-item>
</sp-picker>
</sp-field-group>
```
Expand All @@ -74,26 +70,22 @@ import { Picker } from '@spectrum-web-components/picker';
```html demo
<sp-field-group>
<sp-picker size="m" label="Selection type">
<sp-menu>
<sp-menu-item>Deselect</sp-menu-item>
<sp-menu-item>Select inverse</sp-menu-item>
<sp-menu-item>Feather...</sp-menu-item>
<sp-menu-item>Select and mask...</sp-menu-item>
<sp-menu-divider></sp-menu-divider>
<sp-menu-item>Save selection</sp-menu-item>
<sp-menu-item disabled>Make work path</sp-menu-item>
</sp-menu>
<sp-menu-item>Deselect</sp-menu-item>
<sp-menu-item>Select inverse</sp-menu-item>
<sp-menu-item>Feather...</sp-menu-item>
<sp-menu-item>Select and mask...</sp-menu-item>
<sp-menu-divider></sp-menu-divider>
<sp-menu-item>Save selection</sp-menu-item>
<sp-menu-item disabled>Make work path</sp-menu-item>
</sp-picker>
<sp-picker quiet size="m" label="Selection type">
<sp-menu>
<sp-menu-item>Deselect</sp-menu-item>
<sp-menu-item>Select inverse</sp-menu-item>
<sp-menu-item>Feather...</sp-menu-item>
<sp-menu-item>Select and mask...</sp-menu-item>
<sp-menu-divider></sp-menu-divider>
<sp-menu-item>Save selection</sp-menu-item>
<sp-menu-item disabled>Make work path</sp-menu-item>
</sp-menu>
<sp-menu-item>Deselect</sp-menu-item>
<sp-menu-item>Select inverse</sp-menu-item>
<sp-menu-item>Feather...</sp-menu-item>
<sp-menu-item>Select and mask...</sp-menu-item>
<sp-menu-divider></sp-menu-divider>
<sp-menu-item>Save selection</sp-menu-item>
<sp-menu-item disabled>Make work path</sp-menu-item>
</sp-picker>
</sp-field-group>
```
Expand All @@ -105,26 +97,22 @@ import { Picker } from '@spectrum-web-components/picker';
```html demo
<sp-field-group>
<sp-picker size="l" label="Selection type">
<sp-menu>
<sp-menu-item>Deselect</sp-menu-item>
<sp-menu-item>Select inverse</sp-menu-item>
<sp-menu-item>Feather...</sp-menu-item>
<sp-menu-item>Select and mask...</sp-menu-item>
<sp-menu-divider></sp-menu-divider>
<sp-menu-item>Save selection</sp-menu-item>
<sp-menu-item disabled>Make work path</sp-menu-item>
</sp-menu>
<sp-menu-item>Deselect</sp-menu-item>
<sp-menu-item>Select inverse</sp-menu-item>
<sp-menu-item>Feather...</sp-menu-item>
<sp-menu-item>Select and mask...</sp-menu-item>
<sp-menu-divider></sp-menu-divider>
<sp-menu-item>Save selection</sp-menu-item>
<sp-menu-item disabled>Make work path</sp-menu-item>
</sp-picker>
<sp-picker quiet size="l" label="Selection type">
<sp-menu>
<sp-menu-item>Deselect</sp-menu-item>
<sp-menu-item>Select inverse</sp-menu-item>
<sp-menu-item>Feather...</sp-menu-item>
<sp-menu-item>Select and mask...</sp-menu-item>
<sp-menu-divider></sp-menu-divider>
<sp-menu-item>Save selection</sp-menu-item>
<sp-menu-item disabled>Make work path</sp-menu-item>
</sp-menu>
<sp-menu-item>Deselect</sp-menu-item>
<sp-menu-item>Select inverse</sp-menu-item>
<sp-menu-item>Feather...</sp-menu-item>
<sp-menu-item>Select and mask...</sp-menu-item>
<sp-menu-divider></sp-menu-divider>
<sp-menu-item>Save selection</sp-menu-item>
<sp-menu-item disabled>Make work path</sp-menu-item>
</sp-picker>
</sp-field-group>
```
Expand All @@ -136,26 +124,22 @@ import { Picker } from '@spectrum-web-components/picker';
```html demo
<sp-field-group>
<sp-picker size="xl" label="Selection type">
<sp-menu>
<sp-menu-item>Deselect</sp-menu-item>
<sp-menu-item>Select inverse</sp-menu-item>
<sp-menu-item>Feather...</sp-menu-item>
<sp-menu-item>Select and mask...</sp-menu-item>
<sp-menu-divider></sp-menu-divider>
<sp-menu-item>Save selection</sp-menu-item>
<sp-menu-item disabled>Make work path</sp-menu-item>
</sp-menu>
<sp-menu-item>Deselect</sp-menu-item>
<sp-menu-item>Select inverse</sp-menu-item>
<sp-menu-item>Feather...</sp-menu-item>
<sp-menu-item>Select and mask...</sp-menu-item>
<sp-menu-divider></sp-menu-divider>
<sp-menu-item>Save selection</sp-menu-item>
<sp-menu-item disabled>Make work path</sp-menu-item>
</sp-picker>
<sp-picker quiet size="xl" label="Selection type">
<sp-menu>
<sp-menu-item>Deselect</sp-menu-item>
<sp-menu-item>Select inverse</sp-menu-item>
<sp-menu-item>Feather...</sp-menu-item>
<sp-menu-item>Select and mask...</sp-menu-item>
<sp-menu-divider></sp-menu-divider>
<sp-menu-item>Save selection</sp-menu-item>
<sp-menu-item disabled>Make work path</sp-menu-item>
</sp-menu>
<sp-menu-item>Deselect</sp-menu-item>
<sp-menu-item>Select inverse</sp-menu-item>
<sp-menu-item>Feather...</sp-menu-item>
<sp-menu-item>Select and mask...</sp-menu-item>
<sp-menu-divider></sp-menu-divider>
<sp-menu-item>Save selection</sp-menu-item>
<sp-menu-item disabled>Make work path</sp-menu-item>
</sp-picker>
</sp-field-group>
```
Expand All @@ -173,15 +157,13 @@ When the `value` of an `<sp-picker>` matches the `value` attribute or the trimme
label="Select a Country with a very long label, too long in fact"
value="item-2"
>
<sp-menu>
<sp-menu-item value="item-1">Deselect</sp-menu-item>
<sp-menu-item value="item-2">Select inverse</sp-menu-item>
<sp-menu-item value="item-3">Feather...</sp-menu-item>
<sp-menu-item value="item-4">Select and mask...</sp-menu-item>
<sp-menu-divider></sp-menu-divider>
<sp-menu-item value="item-5">Save selection</sp-menu-item>
<sp-menu-item disabled value="item-6">Make work path</sp-menu-item>
</sp-menu>
<sp-menu-item value="item-1">Deselect</sp-menu-item>
<sp-menu-item value="item-2">Select inverse</sp-menu-item>
<sp-menu-item value="item-3">Feather...</sp-menu-item>
<sp-menu-item value="item-4">Select and mask...</sp-menu-item>
<sp-menu-divider></sp-menu-divider>
<sp-menu-item value="item-5">Save selection</sp-menu-item>
<sp-menu-item disabled value="item-6">Make work path</sp-menu-item>
</sp-picker>
```

Expand All @@ -192,15 +174,13 @@ When the `value` of an `<sp-picker>` matches the `value` attribute or the trimme
label="Select a Country with a very long label, too long in fact"
value="Feather..."
>
<sp-menu>
<sp-menu-item>Deselect</sp-menu-item>
<sp-menu-item>Select inverse</sp-menu-item>
<sp-menu-item>Feather...</sp-menu-item>
<sp-menu-item>Select and mask...</sp-menu-item>
<sp-menu-divider></sp-menu-divider>
<sp-menu-item>Save selection</sp-menu-item>
<sp-menu-item>Make work path</sp-menu-item>
</sp-menu>
<sp-menu-item>Deselect</sp-menu-item>
<sp-menu-item>Select inverse</sp-menu-item>
<sp-menu-item>Feather...</sp-menu-item>
<sp-menu-item>Select and mask...</sp-menu-item>
<sp-menu-divider></sp-menu-divider>
<sp-menu-item>Save selection</sp-menu-item>
<sp-menu-item>Make work path</sp-menu-item>
</sp-picker>
```

Expand All @@ -214,15 +194,13 @@ When the `value` of an `<sp-picker>` matches the `value` attribute or the trimme
label="Select a Country with a very long label, too long in fact"
invalid
>
<sp-menu>
<sp-menu-item>Deselect</sp-menu-item>
<sp-menu-item>Select inverse</sp-menu-item>
<sp-menu-item>Feather...</sp-menu-item>
<sp-menu-item>Select and mask...</sp-menu-item>
<sp-menu-divider></sp-menu-divider>
<sp-menu-item>Save selection</sp-menu-item>
<sp-menu-item disabled>Make work path</sp-menu-item>
</sp-menu>
<sp-menu-item>Deselect</sp-menu-item>
<sp-menu-item>Select inverse</sp-menu-item>
<sp-menu-item>Feather...</sp-menu-item>
<sp-menu-item>Select and mask...</sp-menu-item>
<sp-menu-divider></sp-menu-divider>
<sp-menu-item>Save selection</sp-menu-item>
<sp-menu-item disabled>Make work path</sp-menu-item>
</sp-picker>
<br />
<br />
Expand All @@ -232,15 +210,13 @@ When the `value` of an `<sp-picker>` matches the `value` attribute or the trimme
invalid
quiet
>
<sp-menu>
<sp-menu-item>Deselect</sp-menu-item>
<sp-menu-item>Select inverse</sp-menu-item>
<sp-menu-item>Feather...</sp-menu-item>
<sp-menu-item>Select and mask...</sp-menu-item>
<sp-menu-divider></sp-menu-divider>
<sp-menu-item>Save selection</sp-menu-item>
<sp-menu-item disabled>Make work path</sp-menu-item>
</sp-menu>
<sp-menu-item>Deselect</sp-menu-item>
<sp-menu-item>Select inverse</sp-menu-item>
<sp-menu-item>Feather...</sp-menu-item>
<sp-menu-item>Select and mask...</sp-menu-item>
<sp-menu-divider></sp-menu-divider>
<sp-menu-item>Save selection</sp-menu-item>
<sp-menu-item disabled>Make work path</sp-menu-item>
</sp-picker>
```

Expand All @@ -252,15 +228,13 @@ When the `value` of an `<sp-picker>` matches the `value` attribute or the trimme
label="Select a Country with a very long label, too long in fact"
disabled
>
<sp-menu>
<sp-menu-item>Deselect</sp-menu-item>
<sp-menu-item>Select inverse</sp-menu-item>
<sp-menu-item>Feather...</sp-menu-item>
<sp-menu-item>Select and mask...</sp-menu-item>
<sp-menu-divider></sp-menu-divider>
<sp-menu-item>Save selection</sp-menu-item>
<sp-menu-item disabled>Make work path</sp-menu-item>
</sp-menu>
<sp-menu-item>Deselect</sp-menu-item>
<sp-menu-item>Select inverse</sp-menu-item>
<sp-menu-item>Feather...</sp-menu-item>
<sp-menu-item>Select and mask...</sp-menu-item>
<sp-menu-divider></sp-menu-divider>
<sp-menu-item>Save selection</sp-menu-item>
<sp-menu-item disabled>Make work path</sp-menu-item>
</sp-picker>
<br />
<br />
Expand All @@ -270,15 +244,13 @@ When the `value` of an `<sp-picker>` matches the `value` attribute or the trimme
disabled
quiet
>
<sp-menu>
<sp-menu-item>Deselect</sp-menu-item>
<sp-menu-item>Select inverse</sp-menu-item>
<sp-menu-item>Feather...</sp-menu-item>
<sp-menu-item>Select and mask...</sp-menu-item>
<sp-menu-divider></sp-menu-divider>
<sp-menu-item>Save selection</sp-menu-item>
<sp-menu-item disabled>Make work path</sp-menu-item>
</sp-menu>
<sp-menu-item>Deselect</sp-menu-item>
<sp-menu-item>Select inverse</sp-menu-item>
<sp-menu-item>Feather...</sp-menu-item>
<sp-menu-item>Select and mask...</sp-menu-item>
<sp-menu-divider></sp-menu-divider>
<sp-menu-item>Save selection</sp-menu-item>
<sp-menu-item disabled>Make work path</sp-menu-item>
</sp-picker>
```

Expand Down
40 changes: 16 additions & 24 deletions packages/picker/src/Picker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import pickerStyles from './picker.css.js';
import chevronStyles from '@spectrum-web-components/icon/src/spectrum-icon-chevron.css.js';

import { Focusable } from '@spectrum-web-components/shared/src/focusable.js';
import ElementReparenter from '@spectrum-web-components/shared/src/element-reparenter.js';
import '@spectrum-web-components/icon/sp-icon.js';
import { Chevron100Icon } from '@spectrum-web-components/icons-ui';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-alert.js';
Expand Down Expand Up @@ -90,6 +91,8 @@ export class PickerBase extends SizedMixin(Focusable) {
@property({ type: Boolean, reflect: true })
public open = false;

private reparentableChildren?: Element[];
private elementReparenter?: ElementReparenter;
public optionsMenu?: Menu;

/**
Expand All @@ -116,7 +119,6 @@ export class PickerBase extends SizedMixin(Focusable) {

protected listRole = 'listbox';
protected itemRole = 'option';
private placeholder?: Comment;

public constructor() {
super();
Expand Down Expand Up @@ -233,17 +235,10 @@ export class PickerBase extends SizedMixin(Focusable) {

protected onOverlayClosed(): void {
this.close();
if (this.optionsMenu && this.placeholder) {
const parentElement =
this.placeholder.parentElement ||
this.placeholder.getRootNode();

if (parentElement) {
parentElement.replaceChild(this.optionsMenu, this.placeholder);
}
if (this.elementReparenter) {
this.elementReparenter.restore();
}

delete this.placeholder;
this.elementReparenter = undefined;

this.menuStateResolver();
}
Expand All @@ -253,26 +248,20 @@ export class PickerBase extends SizedMixin(Focusable) {
if (
!this.popover ||
!this.optionsMenu ||
this.optionsMenu.children.length === 0
!this.reparentableChildren ||
this.reparentableChildren.length === 0
) {
this.menuStateResolver();
return;
}

this.placeholder = document.createComment(
'placeholder for optionsMenu'
this.elementReparenter = new ElementReparenter(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Being you've chosen to make this an object oriented API would it make better sense to recycle the reparenter rather than newing it every time?

I could alternatively see it as a functional API, a la:

import { reparentChildren } from 'shared';

this.reclaimChildren = reparentChildren(children, target); 

if (this.reclaimElements) {
    this.reclaimChildren();
    this.reclaimChildren = undefined;
}

I know right, a web component admiring suggesting a functional API... 😏

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the realm of a class, you will find many parallels with reactive controller approach that the Polymer team is formalizing for the upcoming release of lit-element: https://github.com/Polymer/lit-html/blob/lit-next/packages/reactive-element/CHANGELOG.md

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The functional approach definitely seems better here, and I even tried that first. I was passing back state and not a callback though which I didn't like.

this.reparentableChildren,
this.optionsMenu
);

this.optionsMenu.selectable = true;

const parentElement =
this.optionsMenu.parentElement || this.optionsMenu.getRootNode();

if (parentElement) {
parentElement.replaceChild(this.placeholder, this.optionsMenu);
}

this.popover.append(this.optionsMenu);
this.sizePopover(this.popover);
const { popover } = this;
this.closeOverlay = await Picker.openOverlay(this, 'inline', popover, {
Expand Down Expand Up @@ -349,14 +338,17 @@ export class PickerBase extends SizedMixin(Focusable) {
id="popover"
@click=${this.onClick}
@sp-overlay-closed=${this.onOverlayClosed}
></sp-popover>
>
<sp-menu id="menu"></sp-menu>
</sp-popover>
`;
}

protected firstUpdated(changedProperties: PropertyValues): void {
super.firstUpdated(changedProperties);

this.optionsMenu = this.querySelector('sp-menu') as Menu;
this.optionsMenu = this.shadowRoot.querySelector('sp-menu') as Menu;
this.reparentableChildren = Array.from(this.children);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we'll want to keep the existing usage backward-compatible for now. So here we'd look for an sp-menu and just reparent the children of that (and I was thinking of adding a console.warn DEPRECATION notice for that).

}

protected updated(changedProperties: PropertyValues): void {
Expand Down
Loading