Skip to content

Commit

Permalink
feat(dropdowns): added support for floating-ui (#2381)
Browse files Browse the repository at this point in the history
  • Loading branch information
agliga authored Jan 24, 2025
1 parent aad6f91 commit 02f0cd8
Show file tree
Hide file tree
Showing 17 changed files with 161 additions and 29 deletions.
5 changes: 5 additions & 0 deletions .changeset/short-crabs-retire.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@ebay/ebayui-core": major
---

feat(dropdowns): added support for floating-ui
54 changes: 54 additions & 0 deletions src/common/dropdown/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import {
autoUpdate,
flip,
computePosition,
shift,
offset,
type ReferenceElement
} from "@floating-ui/dom";

interface DropdownUtilOptions {
reverse?: boolean;
offset?: number
}

export class DropdownUtil {
declare host: ReferenceElement;
declare overlay: HTMLElement;
declare cleanupFn: any;
declare options: DropdownUtilOptions;

constructor(host: HTMLElement, overlay: HTMLElement, options?: DropdownUtilOptions) {
this.host = host as ReferenceElement;
this.overlay = overlay as HTMLElement;
this.options = options ?? {};
}

show() {
this.cleanupFn = autoUpdate(
this.host,
this.overlay,
this.update.bind(this),
);
}

update() {
computePosition(this.host, this.overlay, {
placement: this.options.reverse ? "bottom-end" : "bottom-start",
middleware: [offset(this.options.offset ?? 4), flip(), shift()],
}).then(({ x, y }) => {
Object.assign(this.overlay.style, {
left: `${x}px`,
top: `${y}px`,
});
});
}

cleanup() {
this.cleanupFn?.();
}

hide() {
if (this.cleanup) this.cleanup();
}
}
2 changes: 2 additions & 0 deletions src/components/ebay-chips-combobox/index.marko
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ $ const {
$ const options = [...option || []].map((o) => o.text)

<span
key="root"
class=[
"chips-combobox",
fluid && "chips-combobox--fluid",
Expand Down Expand Up @@ -43,6 +44,7 @@ $ const options = [...option || []].map((o) => o.text)
onExpand("emit", "expand")
onCollapse("emit", "collapse")
disabled=disabled
dropdown-element=() => component.getEl("root")
chevron-size="large"
...comboboxInput
autocomplete="list"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ exports[`ebay-chips-combobox > renders default 1`] = `
aria-expanded="false"
aria-haspopup="listbox"
autocomplete="off"
[33mid[39m=[32m"s0-0-4-input"[39m
[33mid[39m=[32m"s0-0-3-input"[39m
placeholder="Add item"
role="combobox"
type="text"
Expand Down Expand Up @@ -92,12 +92,12 @@ exports[`ebay-chips-combobox > renders with chips already selected 1`] = `
>
<span
class="chip__text"
[33mid[39m=[32m"s0-0-3[0]-text"[39m
[33mid[39m=[32m"s0-0-2[0]-text"[39m
>
Option 1
</span>
<button
[33maria-describedby[39m=[32m"s0-0-3[0]-text"[39m
[33maria-describedby[39m=[32m"s0-0-2[0]-text"[39m
aria-label="Remove Option 1"
class="chip__button"
type="button"
Expand Down Expand Up @@ -130,12 +130,12 @@ exports[`ebay-chips-combobox > renders with chips already selected 1`] = `
>
<span
class="chip__text"
[33mid[39m=[32m"s0-0-3[1]-text"[39m
[33mid[39m=[32m"s0-0-2[1]-text"[39m
>
Option 3
</span>
<button
[33maria-describedby[39m=[32m"s0-0-3[1]-text"[39m
[33maria-describedby[39m=[32m"s0-0-2[1]-text"[39m
aria-label="Remove Option 3"
class="chip__button"
type="button"
Expand All @@ -158,12 +158,12 @@ exports[`ebay-chips-combobox > renders with chips already selected 1`] = `
>
<span
class="chip__text"
[33mid[39m=[32m"s0-0-3[2]-text"[39m
[33mid[39m=[32m"s0-0-2[2]-text"[39m
>
Custom Option
</span>
<button
[33maria-describedby[39m=[32m"s0-0-3[2]-text"[39m
[33maria-describedby[39m=[32m"s0-0-2[2]-text"[39m
aria-label="Remove Custom Option"
class="chip__button"
type="button"
Expand Down Expand Up @@ -192,7 +192,7 @@ exports[`ebay-chips-combobox > renders with chips already selected 1`] = `
aria-expanded="false"
aria-haspopup="listbox"
autocomplete="off"
[33mid[39m=[32m"s0-0-4-input"[39m
[33mid[39m=[32m"s0-0-3-input"[39m
placeholder="Add item"
role="combobox"
type="text"
Expand Down
16 changes: 16 additions & 0 deletions src/components/ebay-combobox/component.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createLinear } from "makeup-active-descendant";
import FloatingLabel from "makeup-floating-label";
import Expander from "makeup-expander";
import { DropdownUtil } from "../../common/dropdown";
import { scroll } from "../../common/element-scroll";
import * as eventUtils from "../../common/event-utils";
import safeRegex from "../../common/build-safe-regex";
Expand Down Expand Up @@ -34,6 +35,11 @@ interface ComboboxInput extends Omit<Marko.HTML.Input, `on${string}`> {
}>;
options?: Marko.AttrTag<ComboboxOption>;
"chevron-size"?: "large";
/**
* For internal use only. Used when combobox container changes.
* @returns The dropdown element to be used for the combobox
*/
"dropdown-element"?: () => HTMLElement;
"on-focus"?: (event: ComboboxEvent) => void;
"on-button-click"?: (event: { originalEvent: MouseEvent }) => void;
"on-expand"?: () => void;
Expand Down Expand Up @@ -63,6 +69,7 @@ export default class Combobox extends Marko.Component<Input, State> {
declare expanded?: boolean;
declare expandedChange: boolean;
declare _floatingLabel: any;
declare dropdownUtil: DropdownUtil;

focus() {
(this.getEl("combobox") as HTMLElement).focus();
Expand Down Expand Up @@ -119,11 +126,13 @@ export default class Combobox extends Marko.Component<Input, State> {

handleExpand() {
this.setSelectedView();
this.dropdownUtil.show();
this.emit("expand");
}

handleCollapse() {
this.activeDescendant.reset();
this.dropdownUtil.hide();
this.emit("collapse");
}

Expand Down Expand Up @@ -230,6 +239,7 @@ export default class Combobox extends Marko.Component<Input, State> {
this.expandedChange = input.expanded !== this.expanded;
if (this.expandedChange) {
this.expander.expanded = input.expanded;

}
}
this.expanded = input.expanded;
Expand Down Expand Up @@ -307,9 +317,15 @@ export default class Combobox extends Marko.Component<Input, State> {
}
}

this.dropdownUtil = new DropdownUtil(this.input.dropdownElement?.() ?? this.getEl("combobox"), this.getEl("listbox"))
if (this.isExpanded()) {
this.dropdownUtil.show();
}

if (this.input.floatingLabel) {
this._setupFloatingLabel();
}

}

_cleanupMakeup() {
Expand Down
1 change: 1 addition & 0 deletions src/components/ebay-combobox/index.marko
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ $ const {
borderless,
autocomplete,
options,
dropdownElement,
floatingLabel,
listSelection,
expanded,
Expand Down
7 changes: 7 additions & 0 deletions src/components/ebay-date-textbox/component.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Expander from "makeup-expander";
import { DropdownUtil } from "../../common/dropdown";
import { type DayISO, dateArgToISO } from "../../common/dates/date-utils";
import type { WithNormalizedProps } from "../../global";
import type { AttrString } from "marko/tags-html";
Expand Down Expand Up @@ -56,6 +57,7 @@ interface State {

class DateTextbox extends Marko.Component<Input, State> {
declare expander: any;
declare dropdownUtil: DropdownUtil;

onCreate() {
this.state = {
Expand All @@ -73,10 +75,13 @@ class DateTextbox extends Marko.Component<Input, State> {
expandOnClick: true,
autoCollapse: true,
});

this.dropdownUtil = new DropdownUtil(this.el as HTMLElement, this.getEl("popover"))
}

onDestroy() {
this.expander?.destroy();
this.dropdownUtil?.cleanup();
}

onInput(input: Input) {
Expand Down Expand Up @@ -117,10 +122,12 @@ class DateTextbox extends Marko.Component<Input, State> {
openPopover() {
this.calculateNumMonths();
this.state.popover = true;
this.dropdownUtil.show();
}

closePopover() {
this.state.popover = false;
this.dropdownUtil.hide();
}

onPopoverSelect({ iso }: { iso: DayISO }) {
Expand Down
13 changes: 13 additions & 0 deletions src/components/ebay-fake-menu-button/component.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Expander from "makeup-expander";
import * as eventUtils from "../../common/event-utils";
import { DropdownUtil } from "../../common/dropdown";
import type { MenuEvent } from "../ebay-fake-menu/component";
import type {
Input as FakeMenuInput,
Expand Down Expand Up @@ -38,6 +39,7 @@ export interface Input extends WithNormalizedProps<FakeMenuButtonInput> {}

class FakeMenuButton extends Marko.Component<Input> {
declare expander: any;
declare dropdownUtil: DropdownUtil;

handleMenuKeydown({ el, originalEvent, index }: MenuEvent) {
eventUtils.handleActionKeydown(originalEvent as KeyboardEvent, () => {
Expand All @@ -59,10 +61,12 @@ class FakeMenuButton extends Marko.Component<Input> {
}

handleExpand() {
this.dropdownUtil.show();
this.emitComponentEvent({ eventType: "expand" });
}

handleCollapse() {
this.dropdownUtil.hide();
this.emitComponentEvent({ eventType: "collapse" });
}

Expand Down Expand Up @@ -129,12 +133,21 @@ class FakeMenuButton extends Marko.Component<Input> {
autoCollapse: true,
alwaysDoFocusManagement: true,
});

this.dropdownUtil = new DropdownUtil(
this.getEl("button"),
this.getEl("content"),
{
reverse: this.input.reverse,
},
);
}

_cleanupMakeup() {
if (this.expander) {
this.expander.destroy();
}
this.dropdownUtil?.cleanup();
}
}

Expand Down
13 changes: 13 additions & 0 deletions src/components/ebay-filter-menu-button/component.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Expander from "makeup-expander";
import { DropdownUtil } from "../../common/dropdown";
import * as eventUtils from "../../common/event-utils";
import setupMenu, {
MenuUtils,
Expand Down Expand Up @@ -49,6 +50,7 @@ export interface Input extends WithNormalizedProps<FilterMenuButtonInput> {}

export default class extends MenuUtils<Input, MenuState> {
declare _expander: any;
declare dropdownUtil: DropdownUtil;

onCreate() {
setupMenu(this);
Expand Down Expand Up @@ -88,10 +90,12 @@ export default class extends MenuUtils<Input, MenuState> {
}

handleExpand({ originalEvent }: FilterMenuEvent) {
this.dropdownUtil.show();
this._emitComponentEvent("expand", originalEvent);
}

handleCollapse({ originalEvent }: FilterMenuEvent) {
this.dropdownUtil.hide();
(this.getEl("button") as HTMLElement).focus();
this._emitComponentEvent("collapse", originalEvent);
}
Expand Down Expand Up @@ -157,12 +161,21 @@ export default class extends MenuUtils<Input, MenuState> {
autoCollapse: true,
alwaysDoFocusManagement: true,
});
this.dropdownUtil = new DropdownUtil(
this.getEl("button"),
this.getEl("menu"),
{
offset: 8
}
);

}

_cleanupMakeup() {
if (this._expander) {
this._expander.destroy();
this._expander = undefined;
}
this.dropdownUtil?.cleanup();
}
}
1 change: 1 addition & 0 deletions src/components/ebay-filter-menu-button/index.marko
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ $ const {
</span>
</button>
<ebay-filter-menu
key="menu"
classPrefix="filter-menu-button"
variant=variant
type=inputType
Expand Down
Loading

0 comments on commit 02f0cd8

Please sign in to comment.