From 6d0a312e2092cbd79d06b8d0021076a3ec430248 Mon Sep 17 00:00:00 2001 From: Alex Dixon Date: Fri, 19 Feb 2021 11:19:13 -0600 Subject: [PATCH 1/3] chore: update the docs to what I think the new API should be --- packages/picker/README.md | 224 +++++++++++++++++--------------------- 1 file changed, 98 insertions(+), 126 deletions(-) diff --git a/packages/picker/README.md b/packages/picker/README.md index 531c87d2a58..bd006298fad 100644 --- a/packages/picker/README.md +++ b/packages/picker/README.md @@ -43,26 +43,22 @@ import { Picker } from '@spectrum-web-components/picker'; ```html demo - - Deselect - Select inverse - Feather... - Select and mask... - - Save selection - Make work path - + Deselect + Select inverse + Feather... + Select and mask... + + Save selection + Make work path - - Deselect - Select inverse - Feather... - Select and mask... - - Save selection - Make work path - + Deselect + Select inverse + Feather... + Select and mask... + + Save selection + Make work path ``` @@ -74,26 +70,22 @@ import { Picker } from '@spectrum-web-components/picker'; ```html demo - - Deselect - Select inverse - Feather... - Select and mask... - - Save selection - Make work path - + Deselect + Select inverse + Feather... + Select and mask... + + Save selection + Make work path - - Deselect - Select inverse - Feather... - Select and mask... - - Save selection - Make work path - + Deselect + Select inverse + Feather... + Select and mask... + + Save selection + Make work path ``` @@ -105,26 +97,22 @@ import { Picker } from '@spectrum-web-components/picker'; ```html demo - - Deselect - Select inverse - Feather... - Select and mask... - - Save selection - Make work path - + Deselect + Select inverse + Feather... + Select and mask... + + Save selection + Make work path - - Deselect - Select inverse - Feather... - Select and mask... - - Save selection - Make work path - + Deselect + Select inverse + Feather... + Select and mask... + + Save selection + Make work path ``` @@ -136,26 +124,22 @@ import { Picker } from '@spectrum-web-components/picker'; ```html demo - - Deselect - Select inverse - Feather... - Select and mask... - - Save selection - Make work path - + Deselect + Select inverse + Feather... + Select and mask... + + Save selection + Make work path - - Deselect - Select inverse - Feather... - Select and mask... - - Save selection - Make work path - + Deselect + Select inverse + Feather... + Select and mask... + + Save selection + Make work path ``` @@ -173,15 +157,13 @@ When the `value` of an `` matches the `value` attribute or the trimme label="Select a Country with a very long label, too long in fact" value="item-2" > - - Deselect - Select inverse - Feather... - Select and mask... - - Save selection - Make work path - + Deselect + Select inverse + Feather... + Select and mask... + + Save selection + Make work path ``` @@ -192,15 +174,13 @@ When the `value` of an `` matches the `value` attribute or the trimme label="Select a Country with a very long label, too long in fact" value="Feather..." > - - Deselect - Select inverse - Feather... - Select and mask... - - Save selection - Make work path - + Deselect + Select inverse + Feather... + Select and mask... + + Save selection + Make work path ``` @@ -214,15 +194,13 @@ When the `value` of an `` matches the `value` attribute or the trimme label="Select a Country with a very long label, too long in fact" invalid > - - Deselect - Select inverse - Feather... - Select and mask... - - Save selection - Make work path - + Deselect + Select inverse + Feather... + Select and mask... + + Save selection + Make work path

@@ -232,15 +210,13 @@ When the `value` of an `` matches the `value` attribute or the trimme invalid quiet > - - Deselect - Select inverse - Feather... - Select and mask... - - Save selection - Make work path - + Deselect + Select inverse + Feather... + Select and mask... + + Save selection + Make work path ``` @@ -252,15 +228,13 @@ When the `value` of an `` matches the `value` attribute or the trimme label="Select a Country with a very long label, too long in fact" disabled > - - Deselect - Select inverse - Feather... - Select and mask... - - Save selection - Make work path - + Deselect + Select inverse + Feather... + Select and mask... + + Save selection + Make work path

@@ -270,15 +244,13 @@ When the `value` of an `` matches the `value` attribute or the trimme disabled quiet > - - Deselect - Select inverse - Feather... - Select and mask... - - Save selection - Make work path - + Deselect + Select inverse + Feather... + Select and mask... + + Save selection + Make work path ``` From e92e24e9b6883b0bf357810d6e7281636b072ba6 Mon Sep 17 00:00:00 2001 From: Alex Dixon Date: Fri, 19 Feb 2021 13:57:19 -0600 Subject: [PATCH 2/3] feat: spike at dropping sp-menu For now just reparenting all the children of the element, which works for Picker. --- packages/picker/src/Picker.ts | 68 +++++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 23 deletions(-) diff --git a/packages/picker/src/Picker.ts b/packages/picker/src/Picker.ts index 8cda474f867..9e40b0b1dc4 100644 --- a/packages/picker/src/Picker.ts +++ b/packages/picker/src/Picker.ts @@ -90,6 +90,7 @@ export class PickerBase extends SizedMixin(Focusable) { @property({ type: Boolean, reflect: true }) public open = false; + private reparentableChildren?: Element[]; public optionsMenu?: Menu; /** @@ -116,7 +117,7 @@ export class PickerBase extends SizedMixin(Focusable) { protected listRole = 'listbox'; protected itemRole = 'option'; - private placeholder?: Comment; + private placeholderArray?: Comment[]; public constructor() { super(); @@ -233,17 +234,24 @@ 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.reparentableChildren && + this.placeholderArray && + this.reparentableChildren.length == this.placeholderArray.length + ) { + this.placeholderArray.forEach((item, index) => { + const parentElement = item.parentElement || item.getRootNode(); + + if (parentElement && this.reparentableChildren) { + parentElement.replaceChild( + this.reparentableChildren[index], + item + ); + } + }); } - delete this.placeholder; + delete this.placeholderArray; this.menuStateResolver(); } @@ -253,26 +261,37 @@ 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.placeholderArray = []; + this.reparentableChildren.forEach(() => { + if (this.placeholderArray) { + this.placeholderArray.push( + document.createComment( + 'placeholder for picker child element' + ) + ); + } + }); this.optionsMenu.selectable = true; - const parentElement = - this.optionsMenu.parentElement || this.optionsMenu.getRootNode(); - - if (parentElement) { - parentElement.replaceChild(this.placeholder, this.optionsMenu); - } + this.reparentableChildren.forEach((item, index) => { + const parentElement = item.parentElement || item.getRootNode(); + if (this.placeholderArray && this.optionsMenu && parentElement) { + const placeholderItem = this.placeholderArray[index]; + if (placeholderItem) { + parentElement.replaceChild(placeholderItem, item); + this.optionsMenu.append(item); + } + } + }); - this.popover.append(this.optionsMenu); this.sizePopover(this.popover); const { popover } = this; this.closeOverlay = await Picker.openOverlay(this, 'inline', popover, { @@ -349,14 +368,17 @@ export class PickerBase extends SizedMixin(Focusable) { id="popover" @click=${this.onClick} @sp-overlay-closed=${this.onOverlayClosed} - > + > + + `; } 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); } protected updated(changedProperties: PropertyValues): void { From 168dea9066809e892dd1eaed8890e64850d85b97 Mon Sep 17 00:00:00 2001 From: Alex Dixon Date: Sat, 20 Feb 2021 12:12:06 -0600 Subject: [PATCH 3/3] chore: move the reparenting logic to a re-usable helper --- packages/picker/src/Picker.ts | 48 +++++------------------ packages/shared/src/element-reparenter.ts | 40 +++++++++++++++++++ packages/shared/src/index.ts | 1 + 3 files changed, 50 insertions(+), 39 deletions(-) create mode 100644 packages/shared/src/element-reparenter.ts diff --git a/packages/picker/src/Picker.ts b/packages/picker/src/Picker.ts index 9e40b0b1dc4..f9a5c233210 100644 --- a/packages/picker/src/Picker.ts +++ b/packages/picker/src/Picker.ts @@ -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'; @@ -91,6 +92,7 @@ export class PickerBase extends SizedMixin(Focusable) { public open = false; private reparentableChildren?: Element[]; + private elementReparenter?: ElementReparenter; public optionsMenu?: Menu; /** @@ -117,7 +119,6 @@ export class PickerBase extends SizedMixin(Focusable) { protected listRole = 'listbox'; protected itemRole = 'option'; - private placeholderArray?: Comment[]; public constructor() { super(); @@ -234,24 +235,10 @@ export class PickerBase extends SizedMixin(Focusable) { protected onOverlayClosed(): void { this.close(); - if ( - this.reparentableChildren && - this.placeholderArray && - this.reparentableChildren.length == this.placeholderArray.length - ) { - this.placeholderArray.forEach((item, index) => { - const parentElement = item.parentElement || item.getRootNode(); - - if (parentElement && this.reparentableChildren) { - parentElement.replaceChild( - this.reparentableChildren[index], - item - ); - } - }); + if (this.elementReparenter) { + this.elementReparenter.restore(); } - - delete this.placeholderArray; + this.elementReparenter = undefined; this.menuStateResolver(); } @@ -268,30 +255,13 @@ export class PickerBase extends SizedMixin(Focusable) { return; } - this.placeholderArray = []; - this.reparentableChildren.forEach(() => { - if (this.placeholderArray) { - this.placeholderArray.push( - document.createComment( - 'placeholder for picker child element' - ) - ); - } - }); + this.elementReparenter = new ElementReparenter( + this.reparentableChildren, + this.optionsMenu + ); this.optionsMenu.selectable = true; - this.reparentableChildren.forEach((item, index) => { - const parentElement = item.parentElement || item.getRootNode(); - if (this.placeholderArray && this.optionsMenu && parentElement) { - const placeholderItem = this.placeholderArray[index]; - if (placeholderItem) { - parentElement.replaceChild(placeholderItem, item); - this.optionsMenu.append(item); - } - } - }); - this.sizePopover(this.popover); const { popover } = this; this.closeOverlay = await Picker.openOverlay(this, 'inline', popover, { diff --git a/packages/shared/src/element-reparenter.ts b/packages/shared/src/element-reparenter.ts new file mode 100644 index 00000000000..06b6ee7de1e --- /dev/null +++ b/packages/shared/src/element-reparenter.ts @@ -0,0 +1,40 @@ +export default class ElementReparenter { + private placeholderItems: Comment[]; + private srcElements: Element[]; + + constructor(srcElements: Element[], newParent: Element) { + this.placeholderItems = []; + this.srcElements = srcElements; + + for (let index = 0; index < srcElements.length; ++index) { + const placeholderItem: Comment = document.createComment( + 'placeholder for reparented element' + ); + this.placeholderItems.push(placeholderItem); + + const srcElement = srcElements[index]; + const parentElement = + srcElement.parentElement || srcElement.getRootNode(); + parentElement.replaceChild(placeholderItem, srcElement); + newParent.append(srcElement); + } + } + + public restore(): void { + if (this.srcElements.length != this.placeholderItems.length) { + throw new Error( + 'Unexpected length mismatch attempting to restore parent elements' + ); + } + for (let index = 0; index < this.srcElements.length; ++index) { + const srcElement = this.srcElements[index]; + const placeholderItem = this.placeholderItems[index]; + const parentElement = + placeholderItem.parentElement || placeholderItem.getRootNode(); + parentElement.replaceChild(srcElement, placeholderItem); + delete this.placeholderItems[index]; + } + this.placeholderItems = []; + this.srcElements = []; + } +} diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index c0767f0fd42..f9f73c05d63 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -16,3 +16,4 @@ export * from './get-active-element.js'; export * from './like-anchor.js'; export * from './observe-slot-presence.js'; export * from './observe-slot-text.js'; +export * from './element-reparenter.js';