Repository: skeletonlabs/floating-ui-svelte
Pull request #73 (2024-05-09 21:59:16)

Remove API docs from README
Also removes the packageManager I added to package.json accidentally

vercel[bot]: [vc]: #po3mlNPjBqQwrz1azOvcXUjJ809YM1duR7pPrThXbJA=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJmbG9hdGluZy11aS1zdmVsdGUiLCJyb290RGlyZWN0b3J5IjpudWxsLCJpbnNwZWN0b3JVcmwiOiJodHRwczovL3ZlcmNlbC5jb20vc2tlbGV0b24tbGFicy9mbG9hdGluZy11aS1zdmVsdGUvN1llWmF1UndVWXVqWlhqcmdLWHJSN1Jrc1pKZiIsInByZXZpZXdVcmwiOiJmbG9hdGluZy11aS1zdmVsdGUtZ2l0LWZvcmstamFha2tvbmFrYXphLWMtNDVmNjdjLXNrZWxldG9uLWxhYnMudmVyY2VsLmFwcCIsIm5leHRDb21taXRTdGF0dXMiOiJQRU5ESU5HIiwibGl2ZUZlZWRiYWNrIjp7InJlc29sdmVkIjowLCJ1bnJlc29sdmVkIjowLCJ0b3RhbCI6MCwibGluayI6ImZsb2F0aW5nLXVpLXN2ZWx0ZS1naXQtZm9yay1qYWFra29uYWthemEtYy00NWY2N2Mtc2tlbGV0b24tbGFicy52ZXJjZWwuYXBwIn19XX0=
**The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)

| Name | Status | Preview | Comments | Updated (UTC) |
| :--- | :----- | :------ | :------- | :------ |
| **floating-ui-svelte** | 🔄 Building ([Inspect](https://vercel.com/skeleton-labs/floating-ui-svelte/7YeZauRwUYujZXjrgKXrR7RksZJf)) | [Visit Preview](https://vercel.live/open-feedback/floating-ui-svelte-git-fork-jaakkonakaza-c-45f67c-skeleton-labs.vercel.app?via=pr-comment-visit-preview-link&passThrough=1) | 💬 [**Add feedback**](https://vercel.live/open-feedback/floating-ui-svelte-git-fork-jaakkonakaza-c-45f67c-skeleton-labs.vercel.app?via=pr-comment-feedback-link) | May 9, 2024 9:59pm |



Patch:
diff --git a/README.md b/README.md
index 604d3a3..0f14edc 100644
--- a/README.md
+++ b/README.md
@@ -2,259 +2,6 @@
 
 A Floating UI wrapper for Svelte.
 
-## Installation
-
-```bash
-npm install @skeletonlabs/floating-ui-svelte
-# pnpm install @skeletonlabs/floating-ui-svelte
-# yarn install @skeletonlabs/floating-ui-svelte
-# bun install @skeletonlabs/floating-ui-svelte
-```
-
-## Usage
-
-### Making elements "float"
-
-We want it to float on top of the UI though, so it doesn’t disrupt the flow of the document. Add this class to all floating elements. Note that Floating UI does not have opinions about how your elements stack on the z-axis.
-
-```css
-.floating {
-	width: max-content;
-	position: absolute;
-	top: 0;
-	left: 0;
-}
-
-```
-
-### The Basics
-
-Import the desired hook or component from floating-ui-svelte. [View each example](https://floating-ui-svelte.vercel.app/) for additional guidance.
-
-```js
-import { useFloating, type UseFloatingOptions } from '@skeletonlabs/floating-ui-svelte';
-
-const options: UseFloatingOptions = { /* ... */ };
-const floating = useFloating(options);
-```
-
-## API
-
-### useFloating
-
-The `useFloating` Svelte hook acts as a controller for all other Floating UI Svelte features. It handles positioning your floating elements (tooltips, popovers, etc.) relative to an anchored element. Automatically calculates the best placement and updates it as needed, providing access to properties for position and style.
-
-#### Usage
-
-```html
-<script lang="ts">
-	import { useFloating } from '@skeletonlabs/floating-ui-svelte';
-
-	const floating = useFloating();
-</script>
-
-<button bind:this="{floating.elements.reference}">Reference</button>
-<div bind:this="{floating.elements.floating}" style="{floating.floatingStyles}" class="floating">Floating</div>
-```
-
-> [!WARNING]
-> Destructured variables are not supported as this would break reactivity.
-
-#### Options
-
-| Property | Description | Type | Default Value |
-| -------- | ----------- | ---- | ------------- |
-| open | Represents the open/close state of the floating element. | boolean | true |
-| onOpenChange | Event handler that can be invoked whenever the open state changes. | (open: boolean, event?: Event, reason?: OpenChangeReason) => void | - |
-| placement | Where to place the floating element relative to its reference element. | Placement | 'bottom' |
-| strategy | The type of CSS position property to use. | Strategy | 'absolute' |
-| middleware | Supports all [Floating UI middleware](https://floating-ui.com/docs/middleware). | Array<Middleware \| undefined \| null \| false> | undefined     |
-| transform | Whether to use `transform` instead of `top` and `left` styles to position the floating element (`floatingStyles`). | boolean | true          |
-| elements | The reference and floating elements. | FloatingElements | - |
-| whileElementsMounted | Callback to handle mounting/unmounting of the elements. | (reference: ReferenceElement, floating: FloatingElement, update: () => void) => () => void | - |
-
-#### Return Value
-
-| Property | Description | Type |
-| -------- | ----------- | ---- |
-| x | The x-coord of the floating element. | number |
-| y | The y-coord of the floating element. | number |
-| placement | The stateful placement, which can be different from the initial `placement` passed as options. | Placement |
-| strategy | The stateful strategy, which can be different from the initial `strategy` passed as options. | Strategy |
-| middlewareData | Additional data from middleware. | MiddlewareData  |
-| isPositioned   | The boolean that let you know if the floating element has been positioned. | boolean |
-| floatingStyles | CSS styles to apply to the floating element to position it. | string |
-| elements | The reference and floating elements. | FloatingElements |
-| update | The function to update floating position manually. | () => void |
-| context | Context object containing internal logic to alter the behavior of the floating element. | FloatingContext |
-
-### useInteractions
-
-The `useInteractions` Svelte hook allows you to consume multiple interactions. It ensures that event listeners from different hooks are properly registered instead of being overruled by one another.
-
-#### Usage
-
-```html
-<script>
-	import { useFloating, useInteractions, useHover, useFocus } from '@skeletonlabs/floating-ui-svelte';
-
-	const floating = useFloating();
-
-	const hover = useHover(floating.context);
-	const focus = useFocus(floating.context);
-
-	const interactions = useInteractions([hover, focus]);
-</script>
-
-<div {...interactions.getReferenceProps()}>Reference</div>
-<div {...interactions.getFloatingProps()}>Floating</div>
-```
-
-If you want to apply an event handler the an element that is using a props getter make sure to pass them through the getter instead of applying them directly:
-```diff
-- <div {...interactions.getReferenceProps()} onclick={/* ... */}>Reference</div>
-+ <div {...interactions.getReferenceProps({ onclick: /* ... */})}>Reference</div>
-```
-This will ensure all event handlers will be registered rather being overruled by eachother.
-
-#### Return Value
-
-| Property | Description | Type |
-| -------- | ----------- | ---- |
-| getReferenceProps | The merged attributes for the `reference` element. | (userProps?: HTMLAttributes<Element>) => Record<string, unknown> |
-| getFloatingProps | The merged attributes for the `floating` element. | (userProps?: HTMLAttributes<Element>) => Record<string, unknown> |
-| getItemProps | The merged attributes for when dealing with a list inside the `floating` element. | (userProps?: HTMLAttributes<Element> & ExtendedUserProps) => Record<string, unknown> |
-
-### useHover
-
-#### Usage
-
-```html
-<script>
-	import { useFloating, useInteractions, useHover } from '@skeletonlabs/floating-ui-svelte';
-
-	const floating = useFloating();
-	const hover = useHover(floating.context);
-	const interactions = useInteractions([hover]);
-</script>
-
-<button bind:this="{floating.elements.reference}"  {...interactions.getReferenceProps()}>Reference</button>
-<div bind:this="{floating.elements.floating}" style="{floating.floatingStyles}" {...interactions.getFloatingProps()}>Tooltip</div>
-```
-
-#### Options
-
-| Property | Description | Type | Default Value |
-| -------- | ----------- | ---- | ------------- |
-| enabled | Enables the hook. | boolean | true |
-| mouseOnly | Only allow pointers of type mouse to trigger the hover (thus excluding pens and touchscreens). | boolean | false |
-| delay | Time in ms that will delay the change of the open state. Also accepts an object with open and close properties for finer grained control. | number | 0 |
-| restMs | Time in ms that the pointer must rest on the reference element before the open state is set to true. | number | 0 |
-| move | Whether moving the pointer over the floating element will open it, without a regular hover event required. | boolean | true |
-| handleClose | Callback to handle the closing of the floating element. | HandleCloseFn | null |
-
-### useFocus
-
-(tbd)
-
-### useClick
-
-(tbd)
-
-### useRole
-
-#### Usage
-
-```html
-<script>
-	import { useFloating, useInteractions, useRole } from '@skeletonlabs/floating-ui-svelte';
-
-	const floating = useFloating();
-	const role = useRole(floating.context, { role: 'tooltip' });
-	const interactions = useInteractions([role]);
-</script>
-
-<button bind:this="{floating.elements.reference}" {...interactions.getReferenceProps()}>Reference</button>
-<div bind:this="{floating.elements.floating}" style="{floating.floatingStyles}" {...interactions.getFloatingProps()}>Tooltip</div>
-```
-
-#### Options
-
-| Property | Description | Type | Default Value |
-| -------- | ----------- | ---- | ------------- |
-| enabled | Enables the interaction | boolean | true |
-| role | The role that the floating element should be | [AriaRole](https://floating-ui.com/docs/useRole#native-roles) \| [ComponentRole](https://floating-ui.com/docs/useRole#component-roles) | 'dialog' |
-
-### useDismiss
-
-(tbd)
-
-### useTransition
-
-(tbd)
-
-### FloatingArrow
-
-Renders a customizable `<svg>` pointing arrow triangle inside the floating element that gets automatically positioned.
-
-```html
-<script lang="ts">
-	import { arrow, useFloating, FloatingArrow, autoUpdate, offset } from '$lib/index.js';
-
-	let arrowRef: HTMLElement | null = $state(null);
-
-	const floating = useFloating({
-		get middleware() {
-			return [
-				offset(10),
-				arrowRef && arrow({ element: arrowRef })
-			];
-		}
-	});
-</script>
-
-<button bind:this="{floating.elements.reference}">Reference</button>
-<div bind:this="{floating.elements.floating}" style="{floating.floatingStyles}" class="floating">
-	<div>Floating</div>
-	<FloatingArrow
-		bind:ref={arrowRef}
-		context={floating.context}
-		classes="fill-surface-500"
-	/>
-</div>
-```
-
-#### Props
-
-| Prop | Description | Default | Type |
-| -------- | ----------- | ---- | ---- |
-| ref* | Binded element reference. | - | HTMLElement, null |
-| context* | The context object returned from useFloating(). | - | FloatingContext |
-| width | The width of the arrow. | `14` | number |
-| height | The height of the arrow. | `7` | number |
-| tipRadius | The radius (rounding) of the arrow tip. | `0` (sharp) | number |
-| staticOffset | A static offset override of the arrow from the floating element edge. Often desirable if the floating element is smaller than the reference element along the relevant axis and has an edge alignment (`start`/`end`). | `undefined` (use dynamic path) | string, number, null |
-| d | A custom path for the arrow. Useful if you want fancy rounding. The path should be inside a square SVG and placed at the `bottom` of it. The path is designed for the 'bottom' placement, which will be rotated for other placements. | `"black"` (browser default) | string |
-| fill | The color of the arrow. | xxx | string |
-| stroke | The stroke (border) color of the arrow. This must match (or be less than) the floating element’s border width. | `"none"` | string |
-| strokeWidth | The stroke (border) width of the arrow. | `0` | number |
-
-#### Utility Classes and Styles
-
-Provide artibrary utility classes using the standard attribute.
-
-```html
-<FloatingArrow class="fill-white" />
-```
-
-### FloatingOverlay
-
-(tbd)
-
-### FloatingFocusManager
-
-(tbd)
-
 ## Attribution
 
 Based on [Floating UI](https://github.com/floating-ui/floating-ui) and [Floating UI React](https://floating-ui.com/docs/react). Maintained by [Hugo Korte](https://github.com/Hugos68), [Skeleton Labs](https://www.skeletonlabs.co/), and the [Svelte community](https://svelte.dev/).
diff --git a/package.json b/package.json
index c3488cb..6e291b0 100644
--- a/package.json
+++ b/package.json
@@ -68,6 +68,5 @@
 	},
 	"svelte": "./dist/index.js",
 	"types": "./dist/index.d.ts",
-	"type": "module",
-	"packageManager": "pnpm@9.1.0+sha512.67f5879916a9293e5cf059c23853d571beaf4f753c707f40cb22bed5fb1578c6aad3b6c4107ccb3ba0b35be003eb621a16471ac836c87beb53f9d54bb4612724"
+	"type": "module"
 }
diff --git a/src/lib/components/FloatingArrow/FloatingArrow.svelte b/src/lib/components/FloatingArrow/FloatingArrow.svelte
index 01e71b7..de0275e 100644
--- a/src/lib/components/FloatingArrow/FloatingArrow.svelte
+++ b/src/lib/components/FloatingArrow/FloatingArrow.svelte
@@ -60,8 +60,8 @@
 		return alignment === 'end' ? 'right' : 'left';
 	});
 
-	const arrowX = $derived(arrow?.x != null ? staticOffset || arrow.x : '');
-	const arrowY = $derived(arrow?.y != null ? staticOffset || arrow.y : '');
+	const arrowX = $derived(arrow?.x != null ? staticOffset || `${arrow.x}px` : '');
+	const arrowY = $derived(arrow?.y != null ? staticOffset || `${arrow.y}px` : '');
 
 	const dValue = $derived(
 		d ||
diff --git a/src/lib/hooks/useFloating/index.svelte.ts b/src/lib/hooks/useFloating/index.svelte.ts
index 8da9e69..b2863f4 100644
--- a/src/lib/hooks/useFloating/index.svelte.ts
+++ b/src/lib/hooks/useFloating/index.svelte.ts
@@ -79,6 +79,12 @@ interface UseFloatingOptions {
 		floating: FloatingElement,
 		update: () => void,
 	) => () => void;
+
+	/**
+	 * Unique node id when using `FloatingTree`.
+	 * @default undefined
+	 */
+	nodeId?: string;
 }
 
 interface UseFloatingData {
@@ -197,16 +203,17 @@ interface UseFloatingReturn extends UseFloatingData {
  * Hook for managing floating elements.
  */
 function useFloating(options: UseFloatingOptions = {}): UseFloatingReturn {
+	const elements = $state(options.elements ?? {});
 	const {
 		placement = 'bottom',
 		strategy = 'absolute',
 		middleware = [],
 		transform = true,
 		open = true,
-		onOpenChange = noop,
+		onOpenChange: unstableOnOpenChange = noop,
 		whileElementsMounted,
+		nodeId,
 	} = $derived(options);
-	const elements = $state(options.elements ?? {});
 	const floatingStyles = $derived.by(() => {
 		const initialStyles = {
 			position: strategy,
@@ -236,6 +243,15 @@ function useFloating(options: UseFloatingOptions = {}): UseFloatingReturn {
 		});
 	});
 
+	const events = createPubSub();
+	const data: ContextData = $state({});
+
+	const onOpenChange = (open: boolean, event?: Event, reason?: OpenChangeReason) => {
+		data.openEvent = open ? event : undefined;
+		events.emit('openchange', { open, event, reason });
+		unstableOnOpenChange(open, event, reason);
+	};
+
 	const state: UseFloatingData = $state({
 		x: 0,
 		y: 0,
@@ -246,6 +262,14 @@ function useFloating(options: UseFloatingOptions = {}): UseFloatingReturn {
 	});
 
 	const context: FloatingContext = $state({
+		data,
+		events,
+		elements,
+		onOpenChange,
+		floatingId: useId(),
+		get nodeId() {
+			return nodeId;
+		},
 		get x() {
 			return state.x;
 		},
@@ -267,15 +291,6 @@ function useFloating(options: UseFloatingOptions = {}): UseFloatingReturn {
 		get open() {
 			return open;
 		},
-		get onOpenChange() {
-			return onOpenChange;
-		},
-		events: createPubSub(),
-		data: {},
-		// TODO: Ensure nodeId works the same way as in @floating-ui/react
-		nodeId: undefined,
-		floatingId: useId(),
-		elements,
 	});
 
 	const update = async () => {
@@ -329,6 +344,9 @@ function useFloating(options: UseFloatingOptions = {}): UseFloatingReturn {
 	});
 
 	return {
+		update,
+		context,
+		elements,
 		get x() {
 			return state.x;
 		},
@@ -353,9 +371,6 @@ function useFloating(options: UseFloatingOptions = {}): UseFloatingReturn {
 		get floatingStyles() {
 			return floatingStyles;
 		},
-		elements,
-		update,
-		context,
 	};
 }
 
diff --git a/src/lib/hooks/useHover/index.svelte.ts b/src/lib/hooks/useHover/index.svelte.ts
index 6dfe6ad..38bab28 100644
--- a/src/lib/hooks/useHover/index.svelte.ts
+++ b/src/lib/hooks/useHover/index.svelte.ts
@@ -91,6 +91,15 @@ function getDelay(
 }
 
 function useHover(context: FloatingContext, options: UseHoverOptions = {}): ElementProps {
+	const {
+		enabled = true,
+		mouseOnly = false,
+		delay = 0,
+		restMs = 0,
+		move = true,
+		handleClose = null,
+	} = $derived(options);
+
 	const {
 		open,
 		onOpenChange,
@@ -99,13 +108,6 @@ function useHover(context: FloatingContext, options: UseHoverOptions = {}): Elem
 		elements: { reference, floating },
 	} = $derived(context);
 
-	const enabled = $derived(options.enabled ?? true);
-	const mouseOnly = $derived(options.mouseOnly ?? false);
-	const delay = $derived(options.delay ?? 0);
-	const restMs = $derived(options.restMs ?? 0);
-	const move = $derived(options.move ?? true);
-	const handleClose = $derived(options.handleClose ?? null);
-
 	// const tree = useFloatingTree();
 	// const parentId = useFloatingParentNodeId();
 	let pointerType: string | undefined = undefined;
diff --git a/src/routes/(inner)/api/use-floating/data.ts b/src/routes/(inner)/api/use-floating/data.ts
index 48743d5..461b9b3 100644
--- a/src/routes/(inner)/api/use-floating/data.ts
+++ b/src/routes/(inner)/api/use-floating/data.ts
@@ -44,13 +44,19 @@ export const tableOptions: TableData[] = [
 		property: `elements`,
 		description: `The reference and floating elements.`,
 		type: `FloatingElements`,
-		default: ``,
+		default: `{}`,
 	},
 	{
 		property: `whileElementsMounted`,
 		description: `Callback to handle mounting/unmounting of the elements.`,
-		type: `(reference: ReferenceElement, floating: FloatingElement, update: () => void) => () => void`,
-		default: ``,
+		type: `((reference: ReferenceElement, floating: FloatingElement, update: () => void) => () => void) | undefined`,
+		default: `undefined`,
+	},
+	{
+		property: 'nodeId',
+		description: 'A unique node ID for the floating element when using a `FloatingTree`.',
+		type: 'string | undefined',
+		default: 'undefined',
 	},
 ];
 


Test patch:
diff --git a/src/lib/hooks/useFloating/index.test.svelte.ts b/src/lib/hooks/useFloating/index.test.svelte.ts
index fad5e2e..7b9ab88 100644
--- a/src/lib/hooks/useFloating/index.test.svelte.ts
+++ b/src/lib/hooks/useFloating/index.test.svelte.ts
@@ -873,5 +873,24 @@ describe('useFloating', () => {
 				});
 			}),
 		);
+		it(
+			'updates nodeId reactively',
+			withEffect(async () => {
+				let nodeId = $state(useId());
+				const floating = useFloating({
+					get nodeId() {
+						return nodeId;
+					},
+				});
+
+				expect(floating.context.nodeId).toBe(nodeId);
+
+				nodeId = useId();
+
+				await vi.waitFor(() => {
+					expect(floating.context.nodeId).toBe(nodeId);
+				});
+			}),
+		);
 	});
 });