From 05b70df1d38e18c8f2a129f5ac7a827e02630b2c Mon Sep 17 00:00:00 2001 From: Hunter Johnston Date: Sat, 2 Dec 2023 14:12:08 -0500 Subject: [PATCH] add more floating things --- src/content/api-reference/context-menu.ts | 55 +++++-- src/content/api-reference/floating.ts | 69 +++++++-- src/content/api-reference/helpers.ts | 10 +- src/lib/bits/context-menu/_types.ts | 40 +----- .../components/date-picker-content.svelte | 6 +- .../date-range-picker-content.svelte | 6 +- src/lib/bits/floating/_types.ts | 134 +++++++++++------- src/lib/bits/floating/floating-config.ts | 16 +-- src/lib/bits/floating/helpers.ts | 26 +--- .../components/link-preview-content.svelte | 6 +- .../bits/menu/components/menu-content.svelte | 6 +- .../menu/components/menu-sub-content.svelte | 6 +- src/lib/bits/menu/ctx.ts | 17 +-- .../tooltip/components/tooltip-content.svelte | 6 +- 14 files changed, 251 insertions(+), 152 deletions(-) diff --git a/src/content/api-reference/context-menu.ts b/src/content/api-reference/context-menu.ts index 882542150..24983ed6c 100644 --- a/src/content/api-reference/context-menu.ts +++ b/src/content/api-reference/context-menu.ts @@ -3,7 +3,14 @@ import { menu } from "./menu"; import type * as Menu from "$lib/bits/menu/_types"; import type * as ContextMenu from "$lib/bits/context-menu/_types"; import * as C from "@/content/constants.js"; -import { transitionProps, asChild, union, builderAndAttrsSlotProps } from "./helpers"; +import { + transitionProps, + asChild, + union, + builderAndAttrsSlotProps, + enums, + seeFloating +} from "./helpers"; export const root: APISchema = { title: "Root", @@ -26,31 +33,63 @@ export const content: APISchema = { alignOffset: { type: C.NUMBER, default: "0", - description: "An offset in pixels from the 'start' or 'end' alignment options." + description: seeFloating( + "An offset in pixels from the 'start' or 'end' alignment options.", + "https://floating-ui.com/docs/offset#options" + ) }, avoidCollisions: { type: C.BOOLEAN, default: C.TRUE, - description: - "When `true`, overrides the `side` and `align` options to prevent collisions with the boundary edges." + description: seeFloating( + "When `true`, overrides the `side` and `align` options to prevent collisions with the boundary edges.", + "https://floating-ui.com/docs/flip" + ) }, collisionBoundary: { type: { type: C.UNION, definition: union("'clippingAncestors'", "Element", "Array", "Rect") }, - description: "A boundary element or array of elements to check for collisions against." + description: seeFloating( + "A boundary element or array of elements to check for collisions against.", + "https://floating-ui.com/docs/detectoverflow#boundary" + ) }, collisionPadding: { type: C.NUMBER, default: "0", - description: - "The amount in pixels of virtual padding around the viewport edges to check for overflow which will cause a collision." + description: seeFloating( + "The amount in pixels of virtual padding around the viewport edges to check for overflow which will cause a collision.", + "https://floating-ui.com/docs/detectOverflow#padding" + ) }, fitViewport: { type: C.BOOLEAN, default: C.FALSE, - description: "Whether the floating element should be constrained to the viewport." + description: seeFloating( + "Whether the floating element should be constrained to the viewport.", + "https://floating-ui.com/docs/size" + ) + }, + strategy: { + type: { + type: C.ENUM, + definition: enums("absolute", "fixed") + }, + default: "absolute", + description: seeFloating( + "The positioning strategy to use for the floating element.", + "https://floating-ui.com/docs/computeposition#strategy" + ) + }, + overlap: { + type: C.BOOLEAN, + default: C.FALSE, + description: seeFloating( + "Whether the floating element can overlap the reference element.", + "https://floating-ui.com/docs/shift#options" + ) } }, slotProps: { ...builderAndAttrsSlotProps }, diff --git a/src/content/api-reference/floating.ts b/src/content/api-reference/floating.ts index 67c705ee1..681e7c097 100644 --- a/src/content/api-reference/floating.ts +++ b/src/content/api-reference/floating.ts @@ -1,5 +1,5 @@ import * as C from "@/content/constants.js"; -import { enums, union } from "./helpers.js"; +import { enums, seeFloating, union } from "./helpers.js"; export const floatingPositioning = { side: { @@ -7,53 +7,96 @@ export const floatingPositioning = { type: C.ENUM, definition: enums("top", "right", "bottom", "left") }, - description: - "The preferred side of the anchor to render against when open. Will be reversed when collisions occur." + description: seeFloating( + "The preferred side of the anchor to render against when open. Will be reversed when collisions occur.", + "https://floating-ui.com/docs/computePosition#placement" + ) }, sideOffset: { type: C.NUMBER, default: "0", - description: "The amount of offset to apply to the menu's position on the x-axis." + description: seeFloating( + "The amount of offset to apply to the menu's position on the x-axis.", + "https://floating-ui.com/docs/offset#options" + ) }, align: { type: { type: C.ENUM, definition: enums("start", "center", "end") }, - description: "The preferred alignment of the anchor to render against when open." + description: seeFloating( + "The preferred alignment of the anchor to render against when open.", + "https://floating-ui.com/docs/computePosition#placement" + ) }, alignOffset: { type: C.NUMBER, default: "0", - description: "An offset in pixels from the 'start' or 'end' alignment options." + description: seeFloating( + "An offset in pixels from the 'start' or 'end' alignment options.", + "https://floating-ui.com/docs/offset#options" + ) }, avoidCollisions: { type: C.BOOLEAN, default: C.TRUE, - description: - "When `true`, overrides the `side` and `align` options to prevent collisions with the boundary edges." + description: seeFloating( + "When `true`, overrides the `side` and `align` options to prevent collisions with the boundary edges.", + "https://floating-ui.com/docs/flip" + ) }, collisionBoundary: { type: { type: C.UNION, definition: union("'clippingAncestors'", "Element", "Array", "Rect") }, - description: "A boundary element or array of elements to check for collisions against." + description: seeFloating( + "A boundary element or array of elements to check for collisions against.", + "https://floating-ui.com/docs/detectoverflow#boundary" + ) }, collisionPadding: { type: C.NUMBER, default: "0", - description: - "The amount in pixels of virtual padding around the viewport edges to check for overflow which will cause a collision." + description: seeFloating( + "The amount in pixels of virtual padding around the viewport edges to check for overflow which will cause a collision.", + "https://floating-ui.com/docs/detectOverflow#padding" + ) }, fitViewport: { type: C.BOOLEAN, default: C.FALSE, - description: "Whether the floating element should be constrained to the viewport." + description: seeFloating( + "Whether the floating element should be constrained to the viewport.", + "https://floating-ui.com/docs/size" + ) }, sameWidth: { type: C.BOOLEAN, default: C.FALSE, - description: "Whether the content should be the same width as the trigger." + description: seeFloating( + "Whether the content should be the same width as the trigger.", + "https://floating-ui.com/docs/size" + ) + }, + strategy: { + type: { + type: C.ENUM, + definition: enums("absolute", "fixed") + }, + default: "absolute", + description: seeFloating( + "The positioning strategy to use for the floating element.", + "https://floating-ui.com/docs/computeposition#strategy" + ) + }, + overlap: { + type: C.BOOLEAN, + default: C.FALSE, + description: seeFloating( + "Whether the floating element can overlap the reference element.", + "https://floating-ui.com/docs/shift#options" + ) } }; diff --git a/src/content/api-reference/helpers.ts b/src/content/api-reference/helpers.ts index 66f728130..a260e62b4 100644 --- a/src/content/api-reference/helpers.ts +++ b/src/content/api-reference/helpers.ts @@ -93,9 +93,17 @@ export function portalProp(compName = "content") { } export function union(...types: string[]): string { - return types.join(" | "); + return types + .join(" | ") + .replaceAll(" ", " ") + .replaceAll("<", "<") + .replaceAll(">", ">"); } export function enums(...values: string[]): string { return values.map((value) => `'${value}'`).join(" | "); } + +export function seeFloating(content: string, link: string) { + return `${content} [Floating UI reference](${link}).`; +} diff --git a/src/lib/bits/context-menu/_types.ts b/src/lib/bits/context-menu/_types.ts index ecab902a7..0152f4abd 100644 --- a/src/lib/bits/context-menu/_types.ts +++ b/src/lib/bits/context-menu/_types.ts @@ -1,42 +1,10 @@ import type { AsChild, Transition, TransitionProps } from "$lib/internal/index.js"; -import type { Boundary } from "../floating/_types"; +import type { FloatingProps } from "$lib/bits/floating/_types.js"; + +type ContextFloatingProps = Omit; export type ContentProps< T extends Transition = Transition, In extends Transition = Transition, Out extends Transition = Transition -> = Expand< - { - /** - * An offset in pixels from the "start" or "end" alignment options. - */ - alignOffset?: number; - - /** - * When `true`, overrides the `side` and `align` options to prevent collisions - * with the boundary edges. - * - * @defaultValue `true` - */ - avoidCollisions?: boolean; - - /** - * The amount in pixels of virtual padding around the viewport edges to check - * for overflow which will cause a collision. - */ - collisionPadding?: number; - - /** - * A boundary element or array of elements to check for collisions against. - */ - collisionBoundary?: Boundary; - - /** - * Whether the floating element should be constrained to the viewport. - * - * @defaultValue `false` - */ - fitViewport?: boolean; - } & TransitionProps & - AsChild ->; +> = Expand & AsChild>; diff --git a/src/lib/bits/date-picker/components/date-picker-content.svelte b/src/lib/bits/date-picker/components/date-picker-content.svelte index f06066bfa..b237fcaff 100644 --- a/src/lib/bits/date-picker/components/date-picker-content.svelte +++ b/src/lib/bits/date-picker/components/date-picker-content.svelte @@ -27,6 +27,8 @@ export let collisionBoundary: $$Props["collisionBoundary"] = undefined; export let sameWidth: $$Props["sameWidth"] = false; export let fitViewport: $$Props["fitViewport"] = false; + export let strategy: $$Props["strategy"] = "absolute"; + export let overlap: $$Props["overlap"] = false; const { elements: { content }, @@ -54,7 +56,9 @@ avoidCollisions, collisionBoundary, sameWidth, - fitViewport + fitViewport, + strategy, + overlap }); diff --git a/src/lib/bits/date-range-picker/components/date-range-picker-content.svelte b/src/lib/bits/date-range-picker/components/date-range-picker-content.svelte index f06066bfa..b237fcaff 100644 --- a/src/lib/bits/date-range-picker/components/date-range-picker-content.svelte +++ b/src/lib/bits/date-range-picker/components/date-range-picker-content.svelte @@ -27,6 +27,8 @@ export let collisionBoundary: $$Props["collisionBoundary"] = undefined; export let sameWidth: $$Props["sameWidth"] = false; export let fitViewport: $$Props["fitViewport"] = false; + export let strategy: $$Props["strategy"] = "absolute"; + export let overlap: $$Props["overlap"] = false; const { elements: { content }, @@ -54,7 +56,9 @@ avoidCollisions, collisionBoundary, sameWidth, - fitViewport + fitViewport, + strategy, + overlap }); diff --git a/src/lib/bits/floating/_types.ts b/src/lib/bits/floating/_types.ts index 7e10ae6e0..39b701811 100644 --- a/src/lib/bits/floating/_types.ts +++ b/src/lib/bits/floating/_types.ts @@ -15,64 +15,92 @@ export type Rect = { height: number; }; -export type ContentProps< - T extends Transition = Transition, - In extends Transition = Transition, - Out extends Transition = Transition -> = Expand< - { - /** - * The preferred side of the anchor to render against when open. - * Will be reversed when collisions occur. - */ - side?: "top" | "right" | "bottom" | "left"; +export type FloatingProps = { + /** + * The preferred side of the anchor to render against when open. + * Will be reversed when collisions occur. + * + * @see https://floating-ui.com/docs/computePosition#placement + */ + side?: "top" | "right" | "bottom" | "left"; - /** - * The preferred alignment of the anchor to render against when open. - * This may change when collisions occur. - */ - align?: "start" | "center" | "end"; + /** + * The preferred alignment of the anchor to render against when open. + * This may change when collisions occur. + * + * @see https://floating-ui.com/docs/computePosition#placement + */ + align?: "start" | "center" | "end"; - /** - * An offset in pixels from the "start" or "end" alignment options. - */ - alignOffset?: number; + /** + * An offset in pixels from the "start" or "end" alignment options. + * @see https://floating-ui.com/docs/offset#options + */ + alignOffset?: number; - /** - * The distance in pixels from the anchor to the floating element. - */ - sideOffset?: number; + /** + * The distance in pixels from the anchor to the floating element. + * @see https://floating-ui.com/docs/offset#options + */ + sideOffset?: number; - /** - * Whether the content should be the same width as the trigger. - */ - sameWidth?: boolean; + /** + * Whether the content should be the same width as the trigger. + * + * @see https://floating-ui.com/docs/size + */ + sameWidth?: boolean; - /** - * When `true`, overrides the `side` and `align` options to prevent collisions - * with the boundary edges. - * - * @defaultValue `true` - */ - avoidCollisions?: boolean; + /** + * When `true`, overrides the `side` and `align` options to prevent collisions + * with the boundary edges. + * + * @default true + * @see https://floating-ui.com/docs/flip + */ + avoidCollisions?: boolean; - /** - * The amount in pixels of virtual padding around the viewport edges to check - * for overflow which will cause a collision. - */ - collisionPadding?: number; + /** + * The amount in pixels of virtual padding around the viewport edges to check + * for overflow which will cause a collision. + * + * @default 8 + * @see https://floating-ui.com/docs/detectOverflow#padding + */ + collisionPadding?: number; - /** - * A boundary element or array of elements to check for collisions against. - */ - collisionBoundary?: Boundary; + /** + * A boundary element or array of elements to check for collisions against. + * + * @see https://floating-ui.com/docs/detectoverflow#boundary + */ + collisionBoundary?: Boundary; - /** - * Whether the floating element should be constrained to the viewport. - * - * @defaultValue `false` - */ - fitViewport?: boolean; - } & TransitionProps & - AsChild ->; + /** + * Whether the floating element should be constrained to the viewport. + * + * @default false + * @see https://floating-ui.com/docs/size + */ + fitViewport?: boolean; + + /** + * The positioning strategy to use for the floating element. + * @see https://floating-ui.com/docs/computeposition#strategy + */ + strategy?: "absolute" | "fixed"; + + /** + * Whether the floating element can overlap the reference element. + * @default false + * + * @see https://floating-ui.com/docs/shift#options + */ + overlap?: boolean; +}; + +export type ContentProps< + T extends Transition = Transition, + In extends Transition = Transition, + Out extends Transition = Transition +> = Expand & AsChild>; diff --git a/src/lib/bits/floating/floating-config.ts b/src/lib/bits/floating/floating-config.ts index 600085a0f..d28321bc0 100644 --- a/src/lib/bits/floating/floating-config.ts +++ b/src/lib/bits/floating/floating-config.ts @@ -1,7 +1,7 @@ export type FloatingConfig = { /** * The initial placement of the floating element. - * @defaultValue `"top"` + * @default `"top"` * * @see https://floating-ui.com/docs/computePosition#placement */ @@ -21,7 +21,7 @@ export type FloatingConfig = { /** * The strategy to use for positioning. - * @defaultValue `"absolute"` + * @default `"absolute"` * * @see https://floating-ui.com/docs/computePosition#placement */ @@ -36,7 +36,7 @@ export type FloatingConfig = { /** * The main axis offset or gap between the reference and floating elements. - * @defaultValue `5` + * @default `5` * * @see https://floating-ui.com/docs/offset#options */ @@ -44,7 +44,7 @@ export type FloatingConfig = { /** * The virtual padding around the viewport edges to check for overflow. - * @defaultValue `8` + * @default `8` * * @see https://floating-ui.com/docs/detectOverflow#padding */ @@ -52,7 +52,7 @@ export type FloatingConfig = { /** * Whether to flip the placement. - * @defaultValue `true` + * @default `true` * * @see https://floating-ui.com/docs/flip */ @@ -60,7 +60,7 @@ export type FloatingConfig = { /** * Whether the floating element can overlap the reference element. - * @defaultValue `false` + * @default `false` * * @see https://floating-ui.com/docs/shift#options */ @@ -68,7 +68,7 @@ export type FloatingConfig = { /** * Whether to make the floating element same width as the reference element. - * @defaultValue `false` + * @default `false` * * @see https://floating-ui.com/docs/size */ @@ -76,7 +76,7 @@ export type FloatingConfig = { /** * Whether the floating element should fit the viewport. - * @defaultValue `false` + * @default `false` * * @see https://floating-ui.com/docs/size */ diff --git a/src/lib/bits/floating/helpers.ts b/src/lib/bits/floating/helpers.ts index 44f4700ac..c2dff351b 100644 --- a/src/lib/bits/floating/helpers.ts +++ b/src/lib/bits/floating/helpers.ts @@ -1,21 +1,9 @@ import type { Writable } from "svelte/store"; import type { FloatingConfig } from "./floating-config"; -import type { Boundary } from "./_types.js"; +import type { FloatingProps } from "./_types.js"; -export type Side = "top" | "right" | "bottom" | "left"; -export type Align = "start" | "center" | "end"; - -export type PositioningProps = { - side?: Side; - align?: Align; - sideOffset?: number; - alignOffset?: number; - collisionPadding?: number; - avoidCollisions?: boolean; - sameWidth?: boolean; - collisionBoundary?: Boundary; - fitViewport?: boolean; -}; +type Side = "top" | "right" | "bottom" | "left"; +type Align = "start" | "center" | "end"; const defaultPositioningProps = { side: "bottom", @@ -26,16 +14,16 @@ const defaultPositioningProps = { avoidCollisions: true, collisionPadding: 8, fitViewport: false -} satisfies PositioningProps; +} satisfies FloatingProps; export function getPositioningUpdater(store: Writable) { - return (props: PositioningProps = {}) => { + return (props: FloatingProps = {}) => { return updatePositioning(store, props); }; } -export function updatePositioning(store: Writable, props: PositioningProps) { - const withDefaults = { ...defaultPositioningProps, ...props } satisfies PositioningProps; +export function updatePositioning(store: Writable, props: FloatingProps) { + const withDefaults = { ...defaultPositioningProps, ...props } satisfies FloatingProps; store.update((prev) => { return { ...prev, diff --git a/src/lib/bits/link-preview/components/link-preview-content.svelte b/src/lib/bits/link-preview/components/link-preview-content.svelte index b8121819e..cd81821a6 100644 --- a/src/lib/bits/link-preview/components/link-preview-content.svelte +++ b/src/lib/bits/link-preview/components/link-preview-content.svelte @@ -28,6 +28,8 @@ export let collisionBoundary: $$Props["collisionBoundary"] = undefined; export let sameWidth: $$Props["sameWidth"] = false; export let fitViewport: $$Props["fitViewport"] = false; + export let strategy: $$Props["strategy"] = "absolute"; + export let overlap: $$Props["overlap"] = false; const { elements: { content }, @@ -57,7 +59,9 @@ avoidCollisions, collisionBoundary, sameWidth, - fitViewport + fitViewport, + strategy, + overlap }); diff --git a/src/lib/bits/menu/components/menu-content.svelte b/src/lib/bits/menu/components/menu-content.svelte index d68fb43c8..d5c14ecaa 100644 --- a/src/lib/bits/menu/components/menu-content.svelte +++ b/src/lib/bits/menu/components/menu-content.svelte @@ -28,6 +28,8 @@ export let collisionBoundary: $$Props["collisionBoundary"] = undefined; export let sameWidth: $$Props["sameWidth"] = false; export let fitViewport: $$Props["fitViewport"] = false; + export let strategy: $$Props["strategy"] = "absolute"; + export let overlap: $$Props["overlap"] = false; const { elements: { menu }, @@ -56,7 +58,9 @@ avoidCollisions, collisionBoundary, sameWidth, - fitViewport + fitViewport, + strategy, + overlap }); diff --git a/src/lib/bits/menu/components/menu-sub-content.svelte b/src/lib/bits/menu/components/menu-sub-content.svelte index 966dde7a7..56cb1d5d8 100644 --- a/src/lib/bits/menu/components/menu-sub-content.svelte +++ b/src/lib/bits/menu/components/menu-sub-content.svelte @@ -27,6 +27,8 @@ export let collisionBoundary: $$Props["collisionBoundary"] = undefined; export let sameWidth: $$Props["sameWidth"] = false; export let fitViewport: $$Props["fitViewport"] = false; + export let strategy: $$Props["strategy"] = "absolute"; + export let overlap: $$Props["overlap"] = false; const { elements: { subMenu }, @@ -55,7 +57,9 @@ avoidCollisions, collisionBoundary, sameWidth, - fitViewport + fitViewport, + strategy, + overlap }); diff --git a/src/lib/bits/menu/ctx.ts b/src/lib/bits/menu/ctx.ts index a021666ca..0011bdc45 100644 --- a/src/lib/bits/menu/ctx.ts +++ b/src/lib/bits/menu/ctx.ts @@ -17,8 +17,9 @@ import { } from "@melt-ui/svelte"; import { getContext, setContext } from "svelte"; import type { Readable, Writable } from "svelte/store"; -import { getPositioningUpdater, type PositioningProps } from "../floating/helpers"; -import type { FloatingConfig } from "../floating/floating-config"; +import { getPositioningUpdater } from "../floating/helpers.js"; +import type { FloatingConfig } from "../floating/floating-config.js"; +import type { FloatingProps } from "../floating/_types.js"; const NAME = "menu"; const SUB_NAME = "menu-submenu"; @@ -148,10 +149,10 @@ export function setArrow(size = 8) { const defaultPlacement = { side: "bottom", align: "center" -} satisfies PositioningProps; +} satisfies FloatingProps; -export function updatePositioning(props: PositioningProps) { - const withDefaults = { ...defaultPlacement, ...props } satisfies PositioningProps; +export function updatePositioning(props: FloatingProps) { + const withDefaults = { ...defaultPlacement, ...props } satisfies FloatingProps; const { options: { positioning } } = getCtx(); @@ -163,10 +164,10 @@ export function updatePositioning(props: PositioningProps) { const defaultSubPlacement = { side: "right", align: "start" -} satisfies PositioningProps; +} satisfies FloatingProps; -export function updateSubPositioning(props: PositioningProps) { - const withDefaults = { ...defaultSubPlacement, ...props } satisfies PositioningProps; +export function updateSubPositioning(props: FloatingProps) { + const withDefaults = { ...defaultSubPlacement, ...props } satisfies FloatingProps; const { options: { positioning } } = getSubmenuCtx(); diff --git a/src/lib/bits/tooltip/components/tooltip-content.svelte b/src/lib/bits/tooltip/components/tooltip-content.svelte index b39aed774..cdcdabfb6 100644 --- a/src/lib/bits/tooltip/components/tooltip-content.svelte +++ b/src/lib/bits/tooltip/components/tooltip-content.svelte @@ -28,6 +28,8 @@ export let collisionBoundary: $$Props["collisionBoundary"] = undefined; export let sameWidth: $$Props["sameWidth"] = false; export let fitViewport: $$Props["fitViewport"] = false; + export let strategy: $$Props["strategy"] = "absolute"; + export let overlap: $$Props["overlap"] = false; const { elements: { content }, @@ -52,7 +54,9 @@ avoidCollisions, collisionBoundary, sameWidth, - fitViewport + fitViewport, + strategy, + overlap });