Skip to content

Commit

Permalink
feat(dropdown): add offsetDistance and offsetSkidding properties (#…
Browse files Browse the repository at this point in the history
…11614)

**Related Issue:** #7224 

## Summary

Adds offset properties for dropdown menu placement.
  • Loading branch information
jcfranco authored Feb 24, 2025
1 parent 5cb9502 commit 3feea79
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ describe("calcite-dropdown", () => {

describe("defaults", () => {
defaults("calcite-dropdown", [
{
propertyName: "offsetDistance",
defaultValue: 0,
},
{
propertyName: "offsetSkidding",
defaultValue: 0,
},
{
propertyName: "scale",
defaultValue: "m",
Expand All @@ -52,6 +60,14 @@ describe("calcite-dropdown", () => {

describe("reflects", () => {
reflects("calcite-dropdown", [
{
propertyName: "offsetDistance",
value: 10,
},
{
propertyName: "offsetSkidding",
value: 10,
},
{
propertyName: "scale",
value: "m",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -524,3 +524,19 @@ export const openInAllScales = (): string => html`
</calcite-dropdown>
</div>
`;

export const offsetPlacement = (): string => html`
<calcite-dropdown offset-skidding="10" offset-distance="10" open placement="leading">
<calcite-button icon-start="rectangle-plus" slot="trigger"></calcite-button>
<calcite-dropdown-group group-title="Add to new...">
<calcite-dropdown-item icon-start="nodes-link" selected>Link Chart</calcite-dropdown-item>
<calcite-dropdown-item icon-start="map">Map</calcite-dropdown-item>
</calcite-dropdown-group>
<calcite-dropdown-group group-title="Add to existing...">
<calcite-dropdown-item icon-start="nodes-link" selected>My Link Chart 1</calcite-dropdown-item>
<calcite-dropdown-item icon-start="nodes-link" selected>My Link Chart 2</calcite-dropdown-item>
<calcite-dropdown-item icon-start="map">My Map 1</calcite-dropdown-item>
<calcite-dropdown-item icon-start="map">My Map 2</calcite-dropdown-item>
</calcite-dropdown-group>
</calcite-dropdown>
`;
30 changes: 26 additions & 4 deletions packages/calcite-components/src/components/dropdown/dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,16 @@ export class Dropdown
*/
@property({ reflect: true }) maxItems = 0;

/**
* Offset the position of the component away from the `referenceElement`.
*
* @default 0
*/
@property({ type: Number, reflect: true }) offsetDistance = 0;

/** Offset the position of the component along the `referenceElement`. */
@property({ reflect: true }) offsetSkidding = 0;

/** When `true`, displays and positions the component. */
@property({ reflect: true }) open = false;

Expand Down Expand Up @@ -171,13 +181,23 @@ export class Dropdown
*/
@method()
async reposition(delayed = false): Promise<void> {
const { floatingEl, referenceEl, placement, overlayPositioning, filteredFlipPlacements } = this;
const {
filteredFlipPlacements,
floatingEl,
offsetDistance,
offsetSkidding,
overlayPositioning,
placement,
referenceEl,
} = this;

return reposition(
this,
{
floatingEl,
referenceEl,
offsetDistance,
offsetSkidding,
overlayPositioning,
placement,
flipPlacements: filteredFlipPlacements,
Expand Down Expand Up @@ -257,9 +277,11 @@ export class Dropdown
}

if (
(changes.has("overlayPositioning") &&
(this.hasUpdated || this.overlayPositioning !== "absolute")) ||
(changes.has("placement") && (this.hasUpdated || this.placement !== defaultMenuPlacement))
this.hasUpdated &&
((changes.has("offsetDistance") && this.offsetDistance !== 0) ||
(changes.has("offsetSkidding") && this.offsetSkidding !== 0) ||
(changes.has("overlayPositioning") && this.overlayPositioning !== "absolute") ||
(changes.has("placement") && this.placement !== defaultMenuPlacement))
) {
this.reposition(true);
}
Expand Down
52 changes: 23 additions & 29 deletions packages/calcite-components/src/utils/floating-ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -337,46 +337,40 @@ function getMiddleware({
arrowEl?: SVGSVGElement;
type: UIType;
}): Middleware[] {
const defaultMiddleware = [shift(), hide()];
const middleware = [shift(), hide()];

if (type === "menu") {
return [
...defaultMiddleware,
middleware.push(
flip({
fallbackPlacements: flipPlacements || ["top-start", "top", "top-end", "bottom-start", "bottom", "bottom-end"],
}),
];
);
}

if (type === "popover" || type === "tooltip") {
const middleware: Middleware[] = [
...defaultMiddleware,
offset({
mainAxis: typeof offsetDistance === "number" ? offsetDistance : 0,
crossAxis: typeof offsetSkidding === "number" ? offsetSkidding : 0,
}),
];

if (placement === "auto" || placement === "auto-start" || placement === "auto-end") {
middleware.push(
autoPlacement({ alignment: placement === "auto-start" ? "start" : placement === "auto-end" ? "end" : null }),
);
} else if (!flipDisabled) {
middleware.push(flip(flipPlacements ? { fallbackPlacements: flipPlacements } : {}));
}
middleware.push(
offset({
mainAxis: typeof offsetDistance === "number" ? offsetDistance : 0,
crossAxis: typeof offsetSkidding === "number" ? offsetSkidding : 0,
}),
);

if (arrowEl) {
middleware.push(
arrow({
element: arrowEl,
}),
);
}
if (placement === "auto" || placement === "auto-start" || placement === "auto-end") {
middleware.push(
autoPlacement({ alignment: placement === "auto-start" ? "start" : placement === "auto-end" ? "end" : null }),
);
} else if (!flipDisabled) {
middleware.push(flip(flipPlacements ? { fallbackPlacements: flipPlacements } : {}));
}

return middleware;
if (arrowEl) {
middleware.push(
arrow({
element: arrowEl,
}),
);
}

return [];
return middleware;
}

export function filterValidFlipPlacements(placements: string[], el: HTMLElement): EffectivePlacement[] {
Expand Down

0 comments on commit 3feea79

Please sign in to comment.