diff --git a/.changeset/plenty-birds-bet.md b/.changeset/plenty-birds-bet.md
new file mode 100644
index 0000000000..69583f1659
--- /dev/null
+++ b/.changeset/plenty-birds-bet.md
@@ -0,0 +1,8 @@
+---
+"@skeletonlabs/skeleton-common": minor
+"@skeletonlabs/skeleton-svelte": minor
+"@skeletonlabs/skeleton-react": minor
+---
+
+feature: `Floating Panel` component
+
\ No newline at end of file
diff --git a/packages/skeleton-common/src/classes/floating-panel.ts b/packages/skeleton-common/src/classes/floating-panel.ts
new file mode 100644
index 0000000000..68ad457431
--- /dev/null
+++ b/packages/skeleton-common/src/classes/floating-panel.ts
@@ -0,0 +1,16 @@
+import { defineSkeletonClasses } from '../internal/define-skeleton-classes.js' with { type: 'macro' };
+
+export const classesFloatingPanel = defineSkeletonClasses({
+ trigger: '',
+ positioner: '',
+ content: 'card overflow-hidden shadow-lg border border-surface-300-700',
+ dragTrigger: '',
+ header: 'px-4 py-2 grid grid-cols-[1fr_auto] gap-2 items-center bg-surface-200-800 overflow-y-hidden',
+ title: 'flex justify-start items-center gap-2 whitespace-nowrap',
+ control: 'flex gap-1',
+ stageTrigger: 'btn-icon hover:preset-tonal',
+ closeTrigger: 'btn-icon hover:preset-tonal',
+ body: 'h-full bg-surface-100-900 p-4 overflow-y-auto',
+ // TODO: Remove explicit data-[axis=n]:top-0 when https://github.com/chakra-ui/zag/pull/2863 is merged and released
+ resizeTrigger: 'data-[axis*=n]:h-2 data-[axis*=s]:h-2 data-[axis*=e]:w-2 data-[axis*=w]:w-2 data-[axis=n]:top-0',
+});
diff --git a/packages/skeleton-common/src/index.ts b/packages/skeleton-common/src/index.ts
index 33a4ff6202..ad96d4ae1d 100644
--- a/packages/skeleton-common/src/index.ts
+++ b/packages/skeleton-common/src/index.ts
@@ -6,6 +6,7 @@ export * from './classes/combobox.js';
export * from './classes/date-picker.js';
export * from './classes/dialog.js';
export * from './classes/file-upload.js';
+export * from './classes/floating-panel.js';
export * from './classes/listbox.js';
export * from './classes/menu.js';
export * from './classes/navigation.js';
diff --git a/packages/skeleton-react/package.json b/packages/skeleton-react/package.json
index bded9d74e8..c4b3f6ea12 100644
--- a/packages/skeleton-react/package.json
+++ b/packages/skeleton-react/package.json
@@ -38,6 +38,7 @@
"@zag-js/date-picker": "catalog:",
"@zag-js/dialog": "catalog:",
"@zag-js/file-upload": "catalog:",
+ "@zag-js/floating-panel": "catalog:",
"@zag-js/listbox": "catalog:",
"@zag-js/menu": "catalog:",
"@zag-js/pagination": "catalog:",
diff --git a/packages/skeleton-react/src/components/floating-panel/anatomy/body.tsx b/packages/skeleton-react/src/components/floating-panel/anatomy/body.tsx
new file mode 100644
index 0000000000..f846a03952
--- /dev/null
+++ b/packages/skeleton-react/src/components/floating-panel/anatomy/body.tsx
@@ -0,0 +1,24 @@
+import { RootContext } from '../modules/root-context.js';
+import type { HTMLAttributes } from '@/internal/html-attributes.js';
+import type { PropsWithElement } from '@/internal/props-with-element.js';
+import { classesFloatingPanel } from '@skeletonlabs/skeleton-common';
+import { mergeProps } from '@zag-js/react';
+import { use } from 'react';
+
+export interface FloatingPanelBodyProps extends PropsWithElement<'div'>, HTMLAttributes<'div'> {}
+
+export default function Body(props: FloatingPanelBodyProps) {
+ const floatingPanel = use(RootContext);
+
+ const { element, children, ...rest } = props;
+
+ const attributes = mergeProps(
+ floatingPanel.getBodyProps(),
+ {
+ className: classesFloatingPanel.body,
+ },
+ rest,
+ );
+
+ return element ? element(attributes) :
{children}
;
+}
diff --git a/packages/skeleton-react/src/components/floating-panel/anatomy/close-trigger.tsx b/packages/skeleton-react/src/components/floating-panel/anatomy/close-trigger.tsx
new file mode 100644
index 0000000000..8a3d31bc8c
--- /dev/null
+++ b/packages/skeleton-react/src/components/floating-panel/anatomy/close-trigger.tsx
@@ -0,0 +1,24 @@
+import { RootContext } from '../modules/root-context.js';
+import type { HTMLAttributes } from '@/internal/html-attributes.js';
+import type { PropsWithElement } from '@/internal/props-with-element.js';
+import { classesFloatingPanel } from '@skeletonlabs/skeleton-common';
+import { mergeProps } from '@zag-js/react';
+import { use } from 'react';
+
+export interface FloatingPanelCloseTriggerProps extends PropsWithElement<'button'>, HTMLAttributes<'button'> {}
+
+export default function CloseTrigger(props: FloatingPanelCloseTriggerProps) {
+ const floatingPanel = use(RootContext);
+
+ const { element, children, ...rest } = props;
+
+ const attributes = mergeProps(
+ floatingPanel.getCloseTriggerProps(),
+ {
+ className: classesFloatingPanel.closeTrigger,
+ },
+ rest,
+ );
+
+ return element ? element(attributes) : {children} ;
+}
diff --git a/packages/skeleton-react/src/components/floating-panel/anatomy/content.tsx b/packages/skeleton-react/src/components/floating-panel/anatomy/content.tsx
new file mode 100644
index 0000000000..fe6863ed11
--- /dev/null
+++ b/packages/skeleton-react/src/components/floating-panel/anatomy/content.tsx
@@ -0,0 +1,24 @@
+import { RootContext } from '../modules/root-context.js';
+import type { HTMLAttributes } from '@/internal/html-attributes.js';
+import type { PropsWithElement } from '@/internal/props-with-element.js';
+import { classesFloatingPanel } from '@skeletonlabs/skeleton-common';
+import { mergeProps } from '@zag-js/react';
+import { use } from 'react';
+
+export interface FloatingPanelContentProps extends PropsWithElement<'div'>, HTMLAttributes<'div'> {}
+
+export default function Content(props: FloatingPanelContentProps) {
+ const floatingPanel = use(RootContext);
+
+ const { element, children, ...rest } = props;
+
+ const attributes = mergeProps(
+ floatingPanel.getContentProps(),
+ {
+ className: classesFloatingPanel.content,
+ },
+ rest,
+ );
+
+ return element ? element(attributes) : {children}
;
+}
diff --git a/packages/skeleton-react/src/components/floating-panel/anatomy/control.tsx b/packages/skeleton-react/src/components/floating-panel/anatomy/control.tsx
new file mode 100644
index 0000000000..d7ff0cb40e
--- /dev/null
+++ b/packages/skeleton-react/src/components/floating-panel/anatomy/control.tsx
@@ -0,0 +1,24 @@
+import { RootContext } from '../modules/root-context.js';
+import type { HTMLAttributes } from '@/internal/html-attributes.js';
+import type { PropsWithElement } from '@/internal/props-with-element.js';
+import { classesFloatingPanel } from '@skeletonlabs/skeleton-common';
+import { mergeProps } from '@zag-js/react';
+import { use } from 'react';
+
+export interface FloatingPanelControlProps extends PropsWithElement<'div'>, HTMLAttributes<'div'> {}
+
+export default function Control(props: FloatingPanelControlProps) {
+ const floatingPanel = use(RootContext);
+
+ const { element, children, ...rest } = props;
+
+ const attributes = mergeProps(
+ floatingPanel.getControlProps(),
+ {
+ className: classesFloatingPanel.control,
+ },
+ rest,
+ );
+
+ return element ? element(attributes) : {children}
;
+}
diff --git a/packages/skeleton-react/src/components/floating-panel/anatomy/drag-trigger.tsx b/packages/skeleton-react/src/components/floating-panel/anatomy/drag-trigger.tsx
new file mode 100644
index 0000000000..260528c25a
--- /dev/null
+++ b/packages/skeleton-react/src/components/floating-panel/anatomy/drag-trigger.tsx
@@ -0,0 +1,24 @@
+import { RootContext } from '../modules/root-context.js';
+import type { HTMLAttributes } from '@/internal/html-attributes.js';
+import type { PropsWithElement } from '@/internal/props-with-element.js';
+import { classesFloatingPanel } from '@skeletonlabs/skeleton-common';
+import { mergeProps } from '@zag-js/react';
+import { use } from 'react';
+
+export interface FloatingPanelDragTriggerProps extends PropsWithElement<'div'>, HTMLAttributes<'div'> {}
+
+export default function DragTrigger(props: FloatingPanelDragTriggerProps) {
+ const floatingPanel = use(RootContext);
+
+ const { element, children, ...rest } = props;
+
+ const attributes = mergeProps(
+ floatingPanel.getDragTriggerProps(),
+ {
+ className: classesFloatingPanel.dragTrigger,
+ },
+ rest,
+ );
+
+ return element ? element(attributes) : {children}
;
+}
diff --git a/packages/skeleton-react/src/components/floating-panel/anatomy/header.tsx b/packages/skeleton-react/src/components/floating-panel/anatomy/header.tsx
new file mode 100644
index 0000000000..b42021191e
--- /dev/null
+++ b/packages/skeleton-react/src/components/floating-panel/anatomy/header.tsx
@@ -0,0 +1,24 @@
+import { RootContext } from '../modules/root-context.js';
+import type { HTMLAttributes } from '@/internal/html-attributes.js';
+import type { PropsWithElement } from '@/internal/props-with-element.js';
+import { classesFloatingPanel } from '@skeletonlabs/skeleton-common';
+import { mergeProps } from '@zag-js/react';
+import { use } from 'react';
+
+export interface FloatingPanelHeaderProps extends PropsWithElement<'div'>, HTMLAttributes<'div'> {}
+
+export default function Header(props: FloatingPanelHeaderProps) {
+ const floatingPanel = use(RootContext);
+
+ const { element, children, ...rest } = props;
+
+ const attributes = mergeProps(
+ floatingPanel.getHeaderProps(),
+ {
+ className: classesFloatingPanel.header,
+ },
+ rest,
+ );
+
+ return element ? element(attributes) : {children}
;
+}
diff --git a/packages/skeleton-react/src/components/floating-panel/anatomy/positioner.tsx b/packages/skeleton-react/src/components/floating-panel/anatomy/positioner.tsx
new file mode 100644
index 0000000000..b7eab06c65
--- /dev/null
+++ b/packages/skeleton-react/src/components/floating-panel/anatomy/positioner.tsx
@@ -0,0 +1,24 @@
+import { RootContext } from '../modules/root-context.js';
+import type { HTMLAttributes } from '@/internal/html-attributes.js';
+import type { PropsWithElement } from '@/internal/props-with-element.js';
+import { classesFloatingPanel } from '@skeletonlabs/skeleton-common';
+import { mergeProps } from '@zag-js/react';
+import { use } from 'react';
+
+export interface FloatingPanelPositionerProps extends PropsWithElement<'div'>, HTMLAttributes<'div'> {}
+
+export default function Positioner(props: FloatingPanelPositionerProps) {
+ const floatingPanel = use(RootContext);
+
+ const { element, children, ...rest } = props;
+
+ const attributes = mergeProps(
+ floatingPanel.getPositionerProps(),
+ {
+ className: classesFloatingPanel.positioner,
+ },
+ rest,
+ );
+
+ return element ? element(attributes) : {children}
;
+}
diff --git a/packages/skeleton-react/src/components/floating-panel/anatomy/resize-trigger.tsx b/packages/skeleton-react/src/components/floating-panel/anatomy/resize-trigger.tsx
new file mode 100644
index 0000000000..4e4a967e45
--- /dev/null
+++ b/packages/skeleton-react/src/components/floating-panel/anatomy/resize-trigger.tsx
@@ -0,0 +1,26 @@
+import { RootContext } from '../modules/root-context.js';
+import type { HTMLAttributes } from '@/internal/html-attributes.js';
+import type { PropsWithElement } from '@/internal/props-with-element.js';
+import { classesFloatingPanel } from '@skeletonlabs/skeleton-common';
+import { splitResizeTriggerProps, type ResizeTriggerProps } from '@zag-js/floating-panel';
+import { mergeProps } from '@zag-js/react';
+import { use } from 'react';
+
+export interface FloatingPanelResizeTriggerProps extends ResizeTriggerProps, PropsWithElement<'div'>, HTMLAttributes<'div'> {}
+
+export default function ResizeTrigger(props: FloatingPanelResizeTriggerProps) {
+ const floatingPanel = use(RootContext);
+
+ const [resizeTriggerProps, componentProps] = splitResizeTriggerProps(props);
+ const { element, children, ...rest } = componentProps;
+
+ const attributes = mergeProps(
+ floatingPanel.getResizeTriggerProps(resizeTriggerProps as ResizeTriggerProps),
+ {
+ className: classesFloatingPanel.resizeTrigger,
+ },
+ rest,
+ );
+
+ return element ? element(attributes) : {children}
;
+}
diff --git a/packages/skeleton-react/src/components/floating-panel/anatomy/root-context.tsx b/packages/skeleton-react/src/components/floating-panel/anatomy/root-context.tsx
new file mode 100644
index 0000000000..99c2db88b2
--- /dev/null
+++ b/packages/skeleton-react/src/components/floating-panel/anatomy/root-context.tsx
@@ -0,0 +1,15 @@
+import type { useFloatingPanel } from '../modules/provider.js';
+import { RootContext as RootContext_ } from '../modules/root-context.js';
+import { type ReactNode, use } from 'react';
+
+export interface FloatingPanelRootContextProps {
+ children: (floatingPanel: ReturnType) => ReactNode;
+}
+
+export default function RootContext(props: FloatingPanelRootContextProps) {
+ const floatingPanel = use(RootContext_);
+
+ const { children } = props;
+
+ return children(floatingPanel);
+}
diff --git a/packages/skeleton-react/src/components/floating-panel/anatomy/root-provider.tsx b/packages/skeleton-react/src/components/floating-panel/anatomy/root-provider.tsx
new file mode 100644
index 0000000000..1462720c85
--- /dev/null
+++ b/packages/skeleton-react/src/components/floating-panel/anatomy/root-provider.tsx
@@ -0,0 +1,13 @@
+import type { useFloatingPanel } from '../modules/provider.js';
+import { RootContext } from '../modules/root-context.js';
+import { type PropsWithChildren } from 'react';
+
+export interface FloatingPanelRootProviderProps extends PropsWithChildren {
+ value: ReturnType;
+}
+
+export default function RootProvider(props: FloatingPanelRootProviderProps) {
+ const { children, value: floatingPanel } = props;
+
+ return {children} ;
+}
diff --git a/packages/skeleton-react/src/components/floating-panel/anatomy/root.tsx b/packages/skeleton-react/src/components/floating-panel/anatomy/root.tsx
new file mode 100644
index 0000000000..36aa9d2de7
--- /dev/null
+++ b/packages/skeleton-react/src/components/floating-panel/anatomy/root.tsx
@@ -0,0 +1,15 @@
+import { useFloatingPanel } from '../modules/provider.js';
+import { RootContext } from '../modules/root-context.js';
+import { type Props, splitProps } from '@zag-js/floating-panel';
+import { type PropsWithChildren } from 'react';
+
+export interface FloatingPanelRootProps extends PropsWithChildren, Omit {}
+
+export default function Root(props: FloatingPanelRootProps) {
+ const [floatingPanelProps, componentProps] = splitProps(props);
+ const { children } = componentProps;
+
+ const floatingPanel = useFloatingPanel(floatingPanelProps);
+
+ return {children} ;
+}
diff --git a/packages/skeleton-react/src/components/floating-panel/anatomy/stage-trigger.tsx b/packages/skeleton-react/src/components/floating-panel/anatomy/stage-trigger.tsx
new file mode 100644
index 0000000000..05a76b4eb5
--- /dev/null
+++ b/packages/skeleton-react/src/components/floating-panel/anatomy/stage-trigger.tsx
@@ -0,0 +1,25 @@
+import { RootContext } from '../modules/root-context.js';
+import type { HTMLAttributes } from '@/internal/html-attributes.js';
+import type { PropsWithElement } from '@/internal/props-with-element.js';
+import { classesFloatingPanel } from '@skeletonlabs/skeleton-common';
+import { type StageTriggerProps } from '@zag-js/floating-panel';
+import { mergeProps } from '@zag-js/react';
+import { use } from 'react';
+
+export interface FloatingPanelStageTriggerProps extends StageTriggerProps, PropsWithElement<'button'>, HTMLAttributes<'button'> {}
+
+export default function StageTrigger(props: FloatingPanelStageTriggerProps) {
+ const floatingPanel = use(RootContext);
+
+ const { element, children, stage, ...rest } = props;
+
+ const attributes = mergeProps(
+ floatingPanel.getStageTriggerProps({ stage }),
+ {
+ className: classesFloatingPanel.stageTrigger,
+ },
+ rest,
+ );
+
+ return element ? element(attributes) : {children} ;
+}
diff --git a/packages/skeleton-react/src/components/floating-panel/anatomy/title.tsx b/packages/skeleton-react/src/components/floating-panel/anatomy/title.tsx
new file mode 100644
index 0000000000..2fff7f3ad0
--- /dev/null
+++ b/packages/skeleton-react/src/components/floating-panel/anatomy/title.tsx
@@ -0,0 +1,24 @@
+import { RootContext } from '../modules/root-context.js';
+import type { HTMLAttributes } from '@/internal/html-attributes.js';
+import type { PropsWithElement } from '@/internal/props-with-element.js';
+import { classesFloatingPanel } from '@skeletonlabs/skeleton-common';
+import { mergeProps } from '@zag-js/react';
+import { use } from 'react';
+
+export interface FloatingPanelTitleProps extends PropsWithElement<'div'>, HTMLAttributes<'div'> {}
+
+export default function Title(props: FloatingPanelTitleProps) {
+ const floatingPanel = use(RootContext);
+
+ const { element, children, ...rest } = props;
+
+ const attributes = mergeProps(
+ floatingPanel.getTitleProps(),
+ {
+ className: classesFloatingPanel.title,
+ },
+ rest,
+ );
+
+ return element ? element(attributes) : {children}
;
+}
diff --git a/packages/skeleton-react/src/components/floating-panel/anatomy/trigger.tsx b/packages/skeleton-react/src/components/floating-panel/anatomy/trigger.tsx
new file mode 100644
index 0000000000..e0a3b651ca
--- /dev/null
+++ b/packages/skeleton-react/src/components/floating-panel/anatomy/trigger.tsx
@@ -0,0 +1,24 @@
+import { RootContext } from '../modules/root-context.js';
+import type { HTMLAttributes } from '@/internal/html-attributes.js';
+import type { PropsWithElement } from '@/internal/props-with-element.js';
+import { classesFloatingPanel } from '@skeletonlabs/skeleton-common';
+import { mergeProps } from '@zag-js/react';
+import { use } from 'react';
+
+export interface FloatingPanelTriggerProps extends PropsWithElement<'button'>, HTMLAttributes<'button'> {}
+
+export default function Trigger(props: FloatingPanelTriggerProps) {
+ const floatingPanel = use(RootContext);
+
+ const { element, children, ...rest } = props;
+
+ const attributes = mergeProps(
+ floatingPanel.getTriggerProps(),
+ {
+ className: classesFloatingPanel.trigger,
+ },
+ rest,
+ );
+
+ return element ? element(attributes) : {children} ;
+}
diff --git a/packages/skeleton-react/src/components/floating-panel/index.ts b/packages/skeleton-react/src/components/floating-panel/index.ts
new file mode 100644
index 0000000000..37cd75579f
--- /dev/null
+++ b/packages/skeleton-react/src/components/floating-panel/index.ts
@@ -0,0 +1,16 @@
+export type { FloatingPanelBodyProps } from './anatomy/body.jsx';
+export type { FloatingPanelCloseTriggerProps } from './anatomy/close-trigger.jsx';
+export type { FloatingPanelContentProps } from './anatomy/content.jsx';
+export type { FloatingPanelControlProps } from './anatomy/control.jsx';
+export type { FloatingPanelDragTriggerProps } from './anatomy/drag-trigger.jsx';
+export type { FloatingPanelHeaderProps } from './anatomy/header.jsx';
+export type { FloatingPanelPositionerProps } from './anatomy/positioner.jsx';
+export type { FloatingPanelResizeTriggerProps } from './anatomy/resize-trigger.jsx';
+export type { FloatingPanelRootProps } from './anatomy/root.jsx';
+export type { FloatingPanelRootContextProps } from './anatomy/root-context.jsx';
+export type { FloatingPanelRootProviderProps } from './anatomy/root-provider.jsx';
+export type { FloatingPanelStageTriggerProps } from './anatomy/stage-trigger.jsx';
+export type { FloatingPanelTitleProps } from './anatomy/title.jsx';
+export type { FloatingPanelTriggerProps } from './anatomy/trigger.jsx';
+export { FloatingPanel } from './modules/anatomy.js';
+export { useFloatingPanel } from './modules/provider.js';
diff --git a/packages/skeleton-react/src/components/floating-panel/modules/anatomy.ts b/packages/skeleton-react/src/components/floating-panel/modules/anatomy.ts
new file mode 100644
index 0000000000..22b192c13d
--- /dev/null
+++ b/packages/skeleton-react/src/components/floating-panel/modules/anatomy.ts
@@ -0,0 +1,30 @@
+import Body from '../anatomy/body.jsx';
+import CloseTrigger from '../anatomy/close-trigger.jsx';
+import Content from '../anatomy/content.jsx';
+import Control from '../anatomy/control.jsx';
+import DragTrigger from '../anatomy/drag-trigger.jsx';
+import Header from '../anatomy/header.jsx';
+import Positioner from '../anatomy/positioner.jsx';
+import ResizeTrigger from '../anatomy/resize-trigger.jsx';
+import RootContext from '../anatomy/root-context.jsx';
+import RootProvider from '../anatomy/root-provider.jsx';
+import Root from '../anatomy/root.jsx';
+import StageTrigger from '../anatomy/stage-trigger.jsx';
+import Title from '../anatomy/title.jsx';
+import Trigger from '../anatomy/trigger.jsx';
+
+export const FloatingPanel = Object.assign(Root, {
+ Provider: RootProvider,
+ Context: RootContext,
+ Trigger: Trigger,
+ Positioner: Positioner,
+ Content: Content,
+ DragTrigger: DragTrigger,
+ Header: Header,
+ Title: Title,
+ Control: Control,
+ StageTrigger: StageTrigger,
+ CloseTrigger: CloseTrigger,
+ Body: Body,
+ ResizeTrigger: ResizeTrigger,
+});
diff --git a/packages/skeleton-react/src/components/floating-panel/modules/provider.ts b/packages/skeleton-react/src/components/floating-panel/modules/provider.ts
new file mode 100644
index 0000000000..0ad22e2963
--- /dev/null
+++ b/packages/skeleton-react/src/components/floating-panel/modules/provider.ts
@@ -0,0 +1,11 @@
+import { type Api, connect, machine, type Props } from '@zag-js/floating-panel';
+import { normalizeProps, useMachine, type PropTypes } from '@zag-js/react';
+import { useId } from 'react';
+
+export function useFloatingPanel(props: Omit = {}): Api {
+ const service = useMachine(machine, {
+ id: useId(),
+ ...props,
+ });
+ return connect(service, normalizeProps);
+}
diff --git a/packages/skeleton-react/src/components/floating-panel/modules/root-context.ts b/packages/skeleton-react/src/components/floating-panel/modules/root-context.ts
new file mode 100644
index 0000000000..e8f8f89900
--- /dev/null
+++ b/packages/skeleton-react/src/components/floating-panel/modules/root-context.ts
@@ -0,0 +1,4 @@
+import type { useFloatingPanel } from './provider.js';
+import { createContext } from '@/internal/create-context.js';
+
+export const RootContext = createContext>();
diff --git a/packages/skeleton-react/src/index.ts b/packages/skeleton-react/src/index.ts
index c47913103c..dd95233e25 100644
--- a/packages/skeleton-react/src/index.ts
+++ b/packages/skeleton-react/src/index.ts
@@ -6,6 +6,7 @@ export * from './components/combobox/index.js';
export * from './components/date-picker/index.js';
export * from './components/dialog/index.js';
export * from './components/file-upload/index.js';
+export * from './components/floating-panel/index.js';
export * from './components/listbox/index.js';
export * from './components/menu/index.js';
export * from './components/navigation/index.js';
diff --git a/packages/skeleton-react/test/components/floating-panel/index.test.tsx b/packages/skeleton-react/test/components/floating-panel/index.test.tsx
new file mode 100644
index 0000000000..e03bc83b2a
--- /dev/null
+++ b/packages/skeleton-react/test/components/floating-panel/index.test.tsx
@@ -0,0 +1,87 @@
+import Test from './test.jsx';
+import { render, screen } from '@testing-library/react';
+import { describe, expect, it } from 'vitest';
+
+describe('FloatingPanel', () => {
+ describe('Trigger', () => {
+ it('renders', () => {
+ render( );
+ expect(screen.getByTestId('trigger')).toBeInTheDocument();
+ });
+ });
+
+ describe('Positioner', () => {
+ it('renders', () => {
+ render( );
+ expect(screen.getByTestId('positioner')).toBeInTheDocument();
+ });
+ });
+
+ describe('Content', () => {
+ it('renders', () => {
+ render( );
+ expect(screen.getByTestId('content')).toBeInTheDocument();
+ });
+ });
+
+ describe('Header', () => {
+ it('renders', () => {
+ render( );
+ expect(screen.getByTestId('header')).toBeInTheDocument();
+ });
+ });
+
+ describe('Body', () => {
+ it('renders', () => {
+ render( );
+ expect(screen.getByTestId('body')).toBeInTheDocument();
+ });
+ });
+
+ describe('Title', () => {
+ it('renders', () => {
+ render( );
+ expect(screen.getByTestId('title')).toBeInTheDocument();
+ });
+ });
+
+ describe('DragTrigger', () => {
+ it('renders', () => {
+ render( );
+ expect(screen.getByTestId('drag-trigger')).toBeInTheDocument();
+ });
+ });
+
+ describe('ResizeTrigger', () => {
+ it('renders', () => {
+ render( );
+ expect(screen.getByTestId('resize-trigger')).toBeInTheDocument();
+ });
+ });
+
+ describe('StageTrigger', () => {
+ it('renders minimized trigger', () => {
+ render( );
+ expect(screen.getByTestId('stage-trigger-minimized')).toBeInTheDocument();
+ });
+
+ it('renders maximized trigger', () => {
+ render( );
+ expect(screen.getByTestId('stage-trigger-maximized')).toBeInTheDocument();
+ });
+ });
+
+ describe('CloseTrigger', () => {
+ it('renders', () => {
+ render( );
+ expect(screen.getByTestId('close-trigger')).toBeInTheDocument();
+ });
+ });
+
+ describe('Control', () => {
+ it('renders', () => {
+ render( );
+ expect(screen.getByTestId('control')).toBeInTheDocument();
+ });
+ });
+});
diff --git a/packages/skeleton-react/test/components/floating-panel/test.tsx b/packages/skeleton-react/test/components/floating-panel/test.tsx
new file mode 100644
index 0000000000..f7fd2c44cd
--- /dev/null
+++ b/packages/skeleton-react/test/components/floating-panel/test.tsx
@@ -0,0 +1,24 @@
+import { FloatingPanel } from '@/index.js';
+
+export default function Test() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/packages/skeleton-svelte/package.json b/packages/skeleton-svelte/package.json
index e156c2fb63..467ad351da 100644
--- a/packages/skeleton-svelte/package.json
+++ b/packages/skeleton-svelte/package.json
@@ -39,6 +39,7 @@
"@zag-js/date-picker": "catalog:",
"@zag-js/dialog": "catalog:",
"@zag-js/file-upload": "catalog:",
+ "@zag-js/floating-panel": "catalog:",
"@zag-js/listbox": "catalog:",
"@zag-js/menu": "catalog:",
"@zag-js/pagination": "catalog:",
diff --git a/packages/skeleton-svelte/src/components/floating-panel/anatomy/body.svelte b/packages/skeleton-svelte/src/components/floating-panel/anatomy/body.svelte
new file mode 100644
index 0000000000..f3766d1809
--- /dev/null
+++ b/packages/skeleton-svelte/src/components/floating-panel/anatomy/body.svelte
@@ -0,0 +1,36 @@
+
+
+
+
+{#if element}
+ {@render element(attributes)}
+{:else}
+
+ {@render children?.()}
+
+{/if}
diff --git a/packages/skeleton-svelte/src/components/floating-panel/anatomy/close-trigger.svelte b/packages/skeleton-svelte/src/components/floating-panel/anatomy/close-trigger.svelte
new file mode 100644
index 0000000000..0d6df62047
--- /dev/null
+++ b/packages/skeleton-svelte/src/components/floating-panel/anatomy/close-trigger.svelte
@@ -0,0 +1,36 @@
+
+
+
+
+{#if element}
+ {@render element(attributes)}
+{:else}
+
+ {@render children?.()}
+
+{/if}
diff --git a/packages/skeleton-svelte/src/components/floating-panel/anatomy/content.svelte b/packages/skeleton-svelte/src/components/floating-panel/anatomy/content.svelte
new file mode 100644
index 0000000000..c29cfa511a
--- /dev/null
+++ b/packages/skeleton-svelte/src/components/floating-panel/anatomy/content.svelte
@@ -0,0 +1,36 @@
+
+
+
+
+{#if element}
+ {@render element(attributes)}
+{:else}
+
+ {@render children?.()}
+
+{/if}
diff --git a/packages/skeleton-svelte/src/components/floating-panel/anatomy/control.svelte b/packages/skeleton-svelte/src/components/floating-panel/anatomy/control.svelte
new file mode 100644
index 0000000000..59c28d8019
--- /dev/null
+++ b/packages/skeleton-svelte/src/components/floating-panel/anatomy/control.svelte
@@ -0,0 +1,36 @@
+
+
+
+
+{#if element}
+ {@render element(attributes)}
+{:else}
+
+ {@render children?.()}
+
+{/if}
diff --git a/packages/skeleton-svelte/src/components/floating-panel/anatomy/drag-trigger.svelte b/packages/skeleton-svelte/src/components/floating-panel/anatomy/drag-trigger.svelte
new file mode 100644
index 0000000000..17e16af6ea
--- /dev/null
+++ b/packages/skeleton-svelte/src/components/floating-panel/anatomy/drag-trigger.svelte
@@ -0,0 +1,36 @@
+
+
+
+
+{#if element}
+ {@render element(attributes)}
+{:else}
+
+ {@render children?.()}
+
+{/if}
diff --git a/packages/skeleton-svelte/src/components/floating-panel/anatomy/header.svelte b/packages/skeleton-svelte/src/components/floating-panel/anatomy/header.svelte
new file mode 100644
index 0000000000..29ee5e0bb3
--- /dev/null
+++ b/packages/skeleton-svelte/src/components/floating-panel/anatomy/header.svelte
@@ -0,0 +1,36 @@
+
+
+
+
+{#if element}
+ {@render element(attributes)}
+{:else}
+
+ {@render children?.()}
+
+{/if}
diff --git a/packages/skeleton-svelte/src/components/floating-panel/anatomy/positioner.svelte b/packages/skeleton-svelte/src/components/floating-panel/anatomy/positioner.svelte
new file mode 100644
index 0000000000..e28c338e92
--- /dev/null
+++ b/packages/skeleton-svelte/src/components/floating-panel/anatomy/positioner.svelte
@@ -0,0 +1,36 @@
+
+
+
+
+{#if element}
+ {@render element(attributes)}
+{:else}
+
+ {@render children?.()}
+
+{/if}
diff --git a/packages/skeleton-svelte/src/components/floating-panel/anatomy/resize-trigger.svelte b/packages/skeleton-svelte/src/components/floating-panel/anatomy/resize-trigger.svelte
new file mode 100644
index 0000000000..3da7412f8b
--- /dev/null
+++ b/packages/skeleton-svelte/src/components/floating-panel/anatomy/resize-trigger.svelte
@@ -0,0 +1,40 @@
+
+
+
+
+{#if element}
+ {@render element(attributes)}
+{:else}
+
+ {@render children?.()}
+
+{/if}
diff --git a/packages/skeleton-svelte/src/components/floating-panel/anatomy/root-context.svelte b/packages/skeleton-svelte/src/components/floating-panel/anatomy/root-context.svelte
new file mode 100644
index 0000000000..4b2c550a57
--- /dev/null
+++ b/packages/skeleton-svelte/src/components/floating-panel/anatomy/root-context.svelte
@@ -0,0 +1,20 @@
+
+
+
+
+{@render children(floatingPanel)}
diff --git a/packages/skeleton-svelte/src/components/floating-panel/anatomy/root-provider.svelte b/packages/skeleton-svelte/src/components/floating-panel/anatomy/root-provider.svelte
new file mode 100644
index 0000000000..847cd0944e
--- /dev/null
+++ b/packages/skeleton-svelte/src/components/floating-panel/anatomy/root-provider.svelte
@@ -0,0 +1,20 @@
+
+
+
+
+{@render children?.()}
diff --git a/packages/skeleton-svelte/src/components/floating-panel/anatomy/root.svelte b/packages/skeleton-svelte/src/components/floating-panel/anatomy/root.svelte
new file mode 100644
index 0000000000..4eeaffb844
--- /dev/null
+++ b/packages/skeleton-svelte/src/components/floating-panel/anatomy/root.svelte
@@ -0,0 +1,27 @@
+
+
+
+
+{@render children?.()}
diff --git a/packages/skeleton-svelte/src/components/floating-panel/anatomy/stage-trigger.svelte b/packages/skeleton-svelte/src/components/floating-panel/anatomy/stage-trigger.svelte
new file mode 100644
index 0000000000..143ca5f17c
--- /dev/null
+++ b/packages/skeleton-svelte/src/components/floating-panel/anatomy/stage-trigger.svelte
@@ -0,0 +1,37 @@
+
+
+
+
+{#if element}
+ {@render element(attributes)}
+{:else}
+
+ {@render children?.()}
+
+{/if}
diff --git a/packages/skeleton-svelte/src/components/floating-panel/anatomy/title.svelte b/packages/skeleton-svelte/src/components/floating-panel/anatomy/title.svelte
new file mode 100644
index 0000000000..068c478c23
--- /dev/null
+++ b/packages/skeleton-svelte/src/components/floating-panel/anatomy/title.svelte
@@ -0,0 +1,36 @@
+
+
+
+
+{#if element}
+ {@render element(attributes)}
+{:else}
+
+ {@render children?.()}
+
+{/if}
diff --git a/packages/skeleton-svelte/src/components/floating-panel/anatomy/trigger.svelte b/packages/skeleton-svelte/src/components/floating-panel/anatomy/trigger.svelte
new file mode 100644
index 0000000000..11b31f87d2
--- /dev/null
+++ b/packages/skeleton-svelte/src/components/floating-panel/anatomy/trigger.svelte
@@ -0,0 +1,36 @@
+
+
+
+
+{#if element}
+ {@render element(attributes)}
+{:else}
+
+ {@render children?.()}
+
+{/if}
diff --git a/packages/skeleton-svelte/src/components/floating-panel/index.ts b/packages/skeleton-svelte/src/components/floating-panel/index.ts
new file mode 100644
index 0000000000..7c0a87d3a1
--- /dev/null
+++ b/packages/skeleton-svelte/src/components/floating-panel/index.ts
@@ -0,0 +1,16 @@
+export type { FloatingPanelBodyProps } from './anatomy/body.svelte';
+export type { FloatingPanelCloseTriggerProps } from './anatomy/close-trigger.svelte';
+export type { FloatingPanelContentProps } from './anatomy/content.svelte';
+export type { FloatingPanelControlProps } from './anatomy/control.svelte';
+export type { FloatingPanelDragTriggerProps } from './anatomy/drag-trigger.svelte';
+export type { FloatingPanelHeaderProps } from './anatomy/header.svelte';
+export type { FloatingPanelPositionerProps } from './anatomy/positioner.svelte';
+export type { FloatingPanelResizeTriggerProps } from './anatomy/resize-trigger.svelte';
+export type { FloatingPanelRootProps } from './anatomy/root.svelte';
+export type { FloatingPanelRootContextProps } from './anatomy/root-context.svelte';
+export type { FloatingPanelRootProviderProps } from './anatomy/root-provider.svelte';
+export type { FloatingPanelStageTriggerProps } from './anatomy/stage-trigger.svelte';
+export type { FloatingPanelTitleProps } from './anatomy/title.svelte';
+export type { FloatingPanelTriggerProps } from './anatomy/trigger.svelte';
+export { FloatingPanel } from './modules/anatomy.js';
+export { useFloatingPanel } from './modules/provider.svelte.js';
diff --git a/packages/skeleton-svelte/src/components/floating-panel/modules/anatomy.ts b/packages/skeleton-svelte/src/components/floating-panel/modules/anatomy.ts
new file mode 100644
index 0000000000..e52530015b
--- /dev/null
+++ b/packages/skeleton-svelte/src/components/floating-panel/modules/anatomy.ts
@@ -0,0 +1,30 @@
+import Body from '../anatomy/body.svelte';
+import CloseTrigger from '../anatomy/close-trigger.svelte';
+import Content from '../anatomy/content.svelte';
+import Control from '../anatomy/control.svelte';
+import DragTrigger from '../anatomy/drag-trigger.svelte';
+import Header from '../anatomy/header.svelte';
+import Positioner from '../anatomy/positioner.svelte';
+import ResizeTrigger from '../anatomy/resize-trigger.svelte';
+import RootContext from '../anatomy/root-context.svelte';
+import RootProvider from '../anatomy/root-provider.svelte';
+import Root from '../anatomy/root.svelte';
+import StageTrigger from '../anatomy/stage-trigger.svelte';
+import Title from '../anatomy/title.svelte';
+import Trigger from '../anatomy/trigger.svelte';
+
+export const FloatingPanel = Object.assign(Root, {
+ Provider: RootProvider,
+ Context: RootContext,
+ Trigger: Trigger,
+ Positioner: Positioner,
+ Content: Content,
+ DragTrigger: DragTrigger,
+ Header: Header,
+ Title: Title,
+ Control: Control,
+ StageTrigger: StageTrigger,
+ CloseTrigger: CloseTrigger,
+ Body: Body,
+ ResizeTrigger: ResizeTrigger,
+});
diff --git a/packages/skeleton-svelte/src/components/floating-panel/modules/provider.svelte.ts b/packages/skeleton-svelte/src/components/floating-panel/modules/provider.svelte.ts
new file mode 100644
index 0000000000..8a6c1d68ef
--- /dev/null
+++ b/packages/skeleton-svelte/src/components/floating-panel/modules/provider.svelte.ts
@@ -0,0 +1,8 @@
+import { type Api, connect, machine, type Props } from '@zag-js/floating-panel';
+import { normalizeProps, useMachine, type PropTypes } from '@zag-js/svelte';
+
+export function useFloatingPanel(props: Props | (() => Props)): () => Api {
+ const service = useMachine(machine, props);
+ const floatingPanel = $derived(connect(service, normalizeProps));
+ return () => floatingPanel;
+}
diff --git a/packages/skeleton-svelte/src/components/floating-panel/modules/root-context.ts b/packages/skeleton-svelte/src/components/floating-panel/modules/root-context.ts
new file mode 100644
index 0000000000..887ef787fc
--- /dev/null
+++ b/packages/skeleton-svelte/src/components/floating-panel/modules/root-context.ts
@@ -0,0 +1,4 @@
+import type { useFloatingPanel } from './provider.svelte.js';
+import { createContext } from '@/internal/create-context.js';
+
+export const RootContext = createContext>();
diff --git a/packages/skeleton-svelte/src/index.ts b/packages/skeleton-svelte/src/index.ts
index c47913103c..dd95233e25 100644
--- a/packages/skeleton-svelte/src/index.ts
+++ b/packages/skeleton-svelte/src/index.ts
@@ -6,6 +6,7 @@ export * from './components/combobox/index.js';
export * from './components/date-picker/index.js';
export * from './components/dialog/index.js';
export * from './components/file-upload/index.js';
+export * from './components/floating-panel/index.js';
export * from './components/listbox/index.js';
export * from './components/menu/index.js';
export * from './components/navigation/index.js';
diff --git a/packages/skeleton-svelte/test/components/floating-panel/index.test.ts b/packages/skeleton-svelte/test/components/floating-panel/index.test.ts
new file mode 100644
index 0000000000..37bf3419d4
--- /dev/null
+++ b/packages/skeleton-svelte/test/components/floating-panel/index.test.ts
@@ -0,0 +1,87 @@
+import Test from './test.svelte';
+import { render, screen } from '@testing-library/svelte';
+import { describe, expect, it } from 'vitest';
+
+describe('FloatingPanel', () => {
+ describe('Trigger', () => {
+ it('renders', () => {
+ render(Test);
+ expect(screen.getByTestId('trigger')).toBeInTheDocument();
+ });
+ });
+
+ describe('Positioner', () => {
+ it('renders', () => {
+ render(Test);
+ expect(screen.getByTestId('positioner')).toBeInTheDocument();
+ });
+ });
+
+ describe('Content', () => {
+ it('renders', () => {
+ render(Test);
+ expect(screen.getByTestId('content')).toBeInTheDocument();
+ });
+ });
+
+ describe('Header', () => {
+ it('renders', () => {
+ render(Test);
+ expect(screen.getByTestId('header')).toBeInTheDocument();
+ });
+ });
+
+ describe('Body', () => {
+ it('renders', () => {
+ render(Test);
+ expect(screen.getByTestId('body')).toBeInTheDocument();
+ });
+ });
+
+ describe('Title', () => {
+ it('renders', () => {
+ render(Test);
+ expect(screen.getByTestId('title')).toBeInTheDocument();
+ });
+ });
+
+ describe('DragTrigger', () => {
+ it('renders', () => {
+ render(Test);
+ expect(screen.getByTestId('drag-trigger')).toBeInTheDocument();
+ });
+ });
+
+ describe('ResizeTrigger', () => {
+ it('renders', () => {
+ render(Test);
+ expect(screen.getByTestId('resize-trigger')).toBeInTheDocument();
+ });
+ });
+
+ describe('StageTrigger', () => {
+ it('renders minimized trigger', () => {
+ render(Test);
+ expect(screen.getByTestId('stage-trigger-minimized')).toBeInTheDocument();
+ });
+
+ it('renders maximized trigger', () => {
+ render(Test);
+ expect(screen.getByTestId('stage-trigger-maximized')).toBeInTheDocument();
+ });
+ });
+
+ describe('CloseTrigger', () => {
+ it('renders', () => {
+ render(Test);
+ expect(screen.getByTestId('close-trigger')).toBeInTheDocument();
+ });
+ });
+
+ describe('Control', () => {
+ it('renders', () => {
+ render(Test);
+ expect(screen.getByTestId('control')).toBeInTheDocument();
+ });
+ });
+});
diff --git a/packages/skeleton-svelte/test/components/floating-panel/test.svelte b/packages/skeleton-svelte/test/components/floating-panel/test.svelte
new file mode 100644
index 0000000000..4ca365123f
--- /dev/null
+++ b/packages/skeleton-svelte/test/components/floating-panel/test.svelte
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/playgrounds/skeleton-react/app/components/floating-panel/page.tsx b/playgrounds/skeleton-react/app/components/floating-panel/page.tsx
new file mode 100644
index 0000000000..ec82022429
--- /dev/null
+++ b/playgrounds/skeleton-react/app/components/floating-panel/page.tsx
@@ -0,0 +1,42 @@
+'use client';
+
+import { FloatingPanel, Portal } from '@skeletonlabs/skeleton-react';
+import { XIcon, MinusIcon, MaximizeIcon, MinimizeIcon } from 'lucide-react';
+
+export default function Page() {
+ return (
+
+ Open Panel
+
+
+
+
+
+ Floating Panel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This is a floating panel that can be dragged, resized, minimized, and maximized.
+ Try dragging from the header or resizing from the bottom-right corner.
+
+
+
+
+
+
+ );
+}
diff --git a/playgrounds/skeleton-svelte/src/routes/components/floating-panel/+page.svelte b/playgrounds/skeleton-svelte/src/routes/components/floating-panel/+page.svelte
new file mode 100644
index 0000000000..4b7d642621
--- /dev/null
+++ b/playgrounds/skeleton-svelte/src/routes/components/floating-panel/+page.svelte
@@ -0,0 +1,38 @@
+
+
+
+ Open Panel
+
+
+
+
+
+ Floating Panel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This is a floating panel that can be dragged, resized, minimized, and maximized.
+ Try dragging from the header or resizing from the bottom-right corner.
+
+
+
+
+
+
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index af68393221..951d919dca 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -159,6 +159,9 @@ catalogs:
'@zag-js/file-upload':
specifier: 1.29.0
version: 1.29.0
+ '@zag-js/floating-panel':
+ specifier: 1.29.0
+ version: 1.29.0
'@zag-js/listbox':
specifier: 1.29.0
version: 1.29.0
@@ -538,6 +541,9 @@ importers:
'@zag-js/file-upload':
specifier: 'catalog:'
version: 1.29.0
+ '@zag-js/floating-panel':
+ specifier: 'catalog:'
+ version: 1.29.0
'@zag-js/listbox':
specifier: 'catalog:'
version: 1.29.0
@@ -656,6 +662,9 @@ importers:
'@zag-js/file-upload':
specifier: 'catalog:'
version: 1.29.0
+ '@zag-js/floating-panel':
+ specifier: 'catalog:'
+ version: 1.29.0
'@zag-js/listbox':
specifier: 'catalog:'
version: 1.29.0
@@ -3119,6 +3128,9 @@ packages:
'@zag-js/file-utils@1.29.0':
resolution: {integrity: sha512-T9etdsDPrCYTUQmy6/D6Tz2AteK6FVRfJRecxG3OJ7OVv1F6B4zH2SCJgpwnqJj6NBKjXLK1rVYuGzphJXNZ4g==}
+ '@zag-js/floating-panel@1.29.0':
+ resolution: {integrity: sha512-7jT0h3jvOdKjEdBFhXb6vdUmeZTt5o5+d+mEdRCWWm5be35TdkenJw5FoRlzS6CF3e0Xhm7iROl86x2hM9c2Zw==}
+
'@zag-js/focus-trap@1.29.0':
resolution: {integrity: sha512-eZCKUT7VJzCHCeIyCdX9hG+c5a4uMnHLA0CBDGFsB69On5wQ3v8N5febbdpEFvJMfOrf93HVK1cNHCZ+rvvQNA==}
@@ -3655,9 +3667,6 @@ packages:
resolution: {integrity: sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ==}
engines: {node: '>=18'}
- devalue@5.4.2:
- resolution: {integrity: sha512-MwPZTKEPK2k8Qgfmqrd48ZKVvzSQjgW0lXLxiIBA8dQjtf/6mw6pggHNLcyDKyf+fI6eXxlQwPsfaCMTU5U+Bw==}
-
devalue@5.5.0:
resolution: {integrity: sha512-69sM5yrHfFLJt0AZ9QqZXGCPfJ7fQjvpln3Rq5+PS03LD32Ost1Q9N+eEnaQwGRIriKkMImXD56ocjQmfjbV3w==}
@@ -4141,10 +4150,6 @@ packages:
resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==}
hasBin: true
- js-yaml@4.1.0:
- resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
- hasBin: true
-
js-yaml@4.1.1:
resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==}
hasBin: true
@@ -5237,10 +5242,6 @@ packages:
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
engines: {node: '>=8'}
- smol-toml@1.4.2:
- resolution: {integrity: sha512-rInDH6lCNiEyn3+hH8KVGFdbjc099j47+OSgbMrfDYX1CmXLfdKd7qi6IfcWj2wFxvSVkuI46M+wPGYfEOEj6g==}
- engines: {node: '>= 18'}
-
smol-toml@1.5.2:
resolution: {integrity: sha512-QlaZEqcAH3/RtNyet1IPIYPsEWAaYyXXv1Krsi+1L/QHppjX4Ifm8MQsBISz9vE8cHicIq3clogsheili5vhaQ==}
engines: {node: '>= 18'}
@@ -6156,7 +6157,7 @@ snapshots:
hast-util-from-html: 2.0.3
hast-util-to-text: 4.0.2
import-meta-resolve: 4.2.0
- js-yaml: 4.1.0
+ js-yaml: 4.1.1
mdast-util-definitions: 6.0.0
rehype-raw: 7.0.0
rehype-stringify: 10.0.1
@@ -6165,7 +6166,7 @@ snapshots:
remark-rehype: 11.1.2
remark-smartypants: 3.0.2
shiki: 3.15.0
- smol-toml: 1.4.2
+ smol-toml: 1.5.2
unified: 11.0.5
unist-util-remove-position: 5.0.0
unist-util-visit: 5.0.0
@@ -7613,7 +7614,7 @@ snapshots:
'@types/cookie': 0.6.0
acorn: 8.15.0
cookie: 0.6.0
- devalue: 5.4.2
+ devalue: 5.5.0
esm-env: 1.2.2
kleur: 4.1.5
magic-string: 0.30.21
@@ -7633,7 +7634,7 @@ snapshots:
'@types/cookie': 0.6.0
acorn: 8.15.0
cookie: 0.6.0
- devalue: 5.4.2
+ devalue: 5.5.0
esm-env: 1.2.2
kleur: 4.1.5
magic-string: 0.30.21
@@ -8223,6 +8224,17 @@ snapshots:
dependencies:
'@zag-js/i18n-utils': 1.29.0
+ '@zag-js/floating-panel@1.29.0':
+ dependencies:
+ '@zag-js/anatomy': 1.29.0
+ '@zag-js/core': 1.29.0
+ '@zag-js/dom-query': 1.29.0
+ '@zag-js/popper': 1.29.0
+ '@zag-js/rect-utils': 1.29.0
+ '@zag-js/store': 1.29.0
+ '@zag-js/types': 1.29.0
+ '@zag-js/utils': 1.29.0
+
'@zag-js/focus-trap@1.29.0':
dependencies:
'@zag-js/dom-query': 1.29.0
@@ -8887,8 +8899,6 @@ snapshots:
dependencies:
base-64: 1.0.0
- devalue@5.4.2: {}
-
devalue@5.5.0: {}
devlop@1.1.0:
@@ -9456,10 +9466,6 @@ snapshots:
argparse: 1.0.10
esprima: 4.0.1
- js-yaml@4.1.0:
- dependencies:
- argparse: 2.0.1
-
js-yaml@4.1.1:
dependencies:
argparse: 2.0.1
@@ -10905,8 +10911,6 @@ snapshots:
slash@3.0.0: {}
- smol-toml@1.4.2: {}
-
smol-toml@1.5.2: {}
source-map-js@1.2.1: {}
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index 5f2ac8f0a2..7f4bd2b632 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -55,6 +55,7 @@ catalog:
'@zag-js/date-picker': 1.29.0
'@zag-js/dialog': 1.29.0
'@zag-js/file-upload': 1.29.0
+ '@zag-js/floating-panel': 1.29.0
'@zag-js/listbox': 1.29.0
'@zag-js/menu': 1.29.0
'@zag-js/pagination': 1.29.0
diff --git a/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/react/anchor-position.tsx b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/react/anchor-position.tsx
new file mode 100644
index 0000000000..706066cdfc
--- /dev/null
+++ b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/react/anchor-position.tsx
@@ -0,0 +1,53 @@
+import { FloatingPanel, Portal } from '@skeletonlabs/skeleton-react';
+import { GripVerticalIcon, XIcon, MinusIcon, MaximizeIcon, MinimizeIcon } from 'lucide-react';
+
+export default function AnchorPosition() {
+ return (
+
+
{
+ if (!ctx.triggerRect) return { x: 0, y: 0 };
+ return {
+ x: ctx.triggerRect.x + ctx.triggerRect.width / 2,
+ y: ctx.triggerRect.y + ctx.triggerRect.height / 2,
+ };
+ }}
+ >
+ Open Panel
+
+
+
+
+
+
+
+ Anchored Panel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This panel is centered in the viewport using getAnchorPosition.
+ The position is calculated based on the boundary rectangle.
+
+
+
+
+
+
+
+ );
+}
diff --git a/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/react/controlled.tsx b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/react/controlled.tsx
new file mode 100644
index 0000000000..629576b3c9
--- /dev/null
+++ b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/react/controlled.tsx
@@ -0,0 +1,81 @@
+import { FloatingPanel, Portal } from '@skeletonlabs/skeleton-react';
+import { GripVerticalIcon, XIcon, MinusIcon, MaximizeIcon, MinimizeIcon } from 'lucide-react';
+import { useState } from 'react';
+
+export default function Controlled() {
+ const [open, setOpen] = useState(false);
+ const [size, setSize] = useState({
+ width: 400,
+ height: 300,
+ });
+
+ return (
+ <>
+
+
+ Open
+ setOpen(e.currentTarget.checked)} />
+
+
+ Width:
+ setSize({ ...size, width: Number(e.target.value) })}
+ />
+
+
+ Height:
+ setSize({ ...size, height: Number(e.target.value) })}
+ />
+
+
+
+ setOpen(details.open)}
+ size={size}
+ onSizeChange={(details) => setSize(details.size)}
+ >
+
+
+
+
+
+
+
+ Controlled Panel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This panel's open state and size are controlled via the inputs above.
+ Try changing the values or resizing/closing the panel to see the inputs update.
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/react/default.tsx b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/react/default.tsx
new file mode 100644
index 0000000000..dc527b88b6
--- /dev/null
+++ b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/react/default.tsx
@@ -0,0 +1,45 @@
+import { FloatingPanel, Portal } from '@skeletonlabs/skeleton-react';
+import { GripVerticalIcon, MinimizeIcon, XIcon, MinusIcon, MaximizeIcon } from 'lucide-react';
+
+export default function Default() {
+ return (
+
+ Open Panel
+
+
+
+
+
+
+
+ Floating Panel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This is a floating panel that can be dragged, resized, minimized, and maximized. Try dragging from the header or resizing
+ from the bottom-right corner.
+
+
+
+
+
+
+
+ );
+}
diff --git a/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/react/dir.tsx b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/react/dir.tsx
new file mode 100644
index 0000000000..5df5374815
--- /dev/null
+++ b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/react/dir.tsx
@@ -0,0 +1,43 @@
+import { FloatingPanel, Portal } from '@skeletonlabs/skeleton-react';
+import { GripVerticalIcon, XIcon, MinusIcon, MaximizeIcon, MinimizeIcon } from 'lucide-react';
+
+export default function Dir() {
+ return (
+
+ Open Panel
+
+
+
+
+
+
+
+ Floating Panel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This is a floating panel with right-to-left (RTL) direction.
+ You can drag it from the header or resize it from the bottom-right corner.
+
+
+
+
+
+
+ );
+}
diff --git a/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/react/disable-dragging.tsx b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/react/disable-dragging.tsx
new file mode 100644
index 0000000000..a0af80b9d7
--- /dev/null
+++ b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/react/disable-dragging.tsx
@@ -0,0 +1,43 @@
+import { FloatingPanel, Portal } from '@skeletonlabs/skeleton-react';
+import { GripVerticalIcon, XIcon, MinusIcon, MaximizeIcon, MinimizeIcon } from 'lucide-react';
+
+export default function DisableDragging() {
+ return (
+
+ Open Panel
+
+
+
+
+
+
+
+ Fixed Position Panel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This panel cannot be dragged - the position is fixed.
+ However, it can still be resized from the bottom-right corner.
+
+
+
+
+
+
+ );
+}
diff --git a/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/react/disable-resizing.tsx b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/react/disable-resizing.tsx
new file mode 100644
index 0000000000..39f6523344
--- /dev/null
+++ b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/react/disable-resizing.tsx
@@ -0,0 +1,43 @@
+import { FloatingPanel, Portal } from '@skeletonlabs/skeleton-react';
+import { GripVerticalIcon, XIcon, MinusIcon, MaximizeIcon, MinimizeIcon } from 'lucide-react';
+
+export default function DisableResizing() {
+ return (
+
+ Open Panel
+
+
+
+
+
+
+
+ Fixed Size Panel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This panel cannot be resized.
+ Try dragging the edges - they won't respond.
+
+
+
+
+
+
+ );
+}
diff --git a/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/react/resize-triggers.tsx b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/react/resize-triggers.tsx
new file mode 100644
index 0000000000..81dbb70b41
--- /dev/null
+++ b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/react/resize-triggers.tsx
@@ -0,0 +1,52 @@
+import { FloatingPanel, Portal } from '@skeletonlabs/skeleton-react';
+import { GripVerticalIcon, MinimizeIcon, XIcon, MinusIcon, MaximizeIcon } from 'lucide-react';
+
+export default function Default() {
+ return (
+
+ Open Panel
+
+
+
+
+
+
+
+ Floating Panel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This is a floating panel that can be dragged, resized, minimized, and maximized. Try dragging from the header or resizing
+ from the bottom-right corner.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/react/size-constraints.tsx b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/react/size-constraints.tsx
new file mode 100644
index 0000000000..b080be4552
--- /dev/null
+++ b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/react/size-constraints.tsx
@@ -0,0 +1,43 @@
+import { FloatingPanel, Portal } from '@skeletonlabs/skeleton-react';
+import { GripVerticalIcon, MinimizeIcon, XIcon, MinusIcon, MaximizeIcon } from 'lucide-react';
+
+export default function SizeConstraints() {
+ return (
+
+ Open Panel
+
+
+
+
+
+
+
+ Floating Panel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This panel has size constraints applied: minimum 300x200 pixels and maximum 900x600 pixels.
+ Try resizing from the bottom-right corner - the panel will respect these boundaries.
+
+
+
+
+
+
+ );
+}
diff --git a/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/anchor-position.svelte b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/anchor-position.svelte
new file mode 100644
index 0000000000..6601af37b0
--- /dev/null
+++ b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/anchor-position.svelte
@@ -0,0 +1,51 @@
+
+
+
+
{
+ if (!ctx.triggerRect) return { x: 0, y: 0 };
+ return {
+ x: ctx.triggerRect.x + ctx.triggerRect.width / 2,
+ y: ctx.triggerRect.y + ctx.triggerRect.height / 2,
+ };
+ }}
+ >
+ Open Panel
+
+
+
+
+
+
+
+ Anchored Panel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This panel is centered in the viewport using getAnchorPosition.
+ The position is calculated based on the boundary rectangle.
+
+
+
+
+
+
+
diff --git a/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/controlled.svelte b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/controlled.svelte
new file mode 100644
index 0000000000..3440096ca7
--- /dev/null
+++ b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/controlled.svelte
@@ -0,0 +1,61 @@
+
+
+
+
+ Open
+
+
+
+ Width:
+
+
+
+ Height:
+
+
+
+
+ (open = details.open)} {size} onSizeChange={(details) => (size = details.size)}>
+
+
+
+
+
+
+
+ Controlled Panel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This panel's open state and size are controlled via the inputs above.
+ Try changing the values or resizing/closing the panel to see the inputs update.
+
+
+
+
+
+
diff --git a/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/default.svelte b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/default.svelte
new file mode 100644
index 0000000000..7b435b4806
--- /dev/null
+++ b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/default.svelte
@@ -0,0 +1,43 @@
+
+
+
+ Open Panel
+
+
+
+
+
+
+
+ Floating Panel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This is a floating panel that can be dragged, resized, minimized, and maximized. Try dragging from the header or resizing from
+ the bottom-right corner.
+
+
+
+
+
+
+
diff --git a/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/dir.svelte b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/dir.svelte
new file mode 100644
index 0000000000..afb59813dd
--- /dev/null
+++ b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/dir.svelte
@@ -0,0 +1,41 @@
+
+
+
+ Open Panel
+
+
+
+
+
+
+
+ Floating Panel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This is a floating panel with right-to-left (RTL) direction.
+ You can drag it from the header or resize it from the bottom-right corner.
+
+
+
+
+
+
diff --git a/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/disable-dragging.svelte b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/disable-dragging.svelte
new file mode 100644
index 0000000000..b144152e7a
--- /dev/null
+++ b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/disable-dragging.svelte
@@ -0,0 +1,41 @@
+
+
+
+ Open Panel
+
+
+
+
+
+
+
+ Fixed Position Panel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This panel cannot be dragged - the position is fixed.
+ However, it can still be resized from the bottom-right corner.
+
+
+
+
+
+
diff --git a/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/disable-resizing.svelte b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/disable-resizing.svelte
new file mode 100644
index 0000000000..3a7c5cd583
--- /dev/null
+++ b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/disable-resizing.svelte
@@ -0,0 +1,41 @@
+
+
+
+ Open Panel
+
+
+
+
+
+
+
+ Fixed Size Panel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This panel cannot be resized.
+ Try dragging the edges - they won't respond.
+
+
+
+
+
+
diff --git a/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/resize-triggers.svelte b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/resize-triggers.svelte
new file mode 100644
index 0000000000..9ea27c30d2
--- /dev/null
+++ b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/resize-triggers.svelte
@@ -0,0 +1,50 @@
+
+
+
+ Open Panel
+
+
+
+
+
+
+
+ Floating Panel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This is a floating panel that can be dragged, resized, minimized, and maximized. Try dragging from the header or resizing from
+ the bottom-right corner.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/size-constraints.svelte b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/size-constraints.svelte
new file mode 100644
index 0000000000..d2343b3899
--- /dev/null
+++ b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/size-constraints.svelte
@@ -0,0 +1,41 @@
+
+
+
+ Open Panel
+
+
+
+
+
+
+
+ Floating Panel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This panel has size constraints applied: minimum 300x200 pixels and maximum 900x600 pixels.
+ Try resizing from the bottom-right corner - the panel will respect these boundaries.
+
+
+
+
+
+
diff --git a/sites/skeleton.dev/src/content/docs/framework-components/floating-panel.mdx b/sites/skeleton.dev/src/content/docs/framework-components/floating-panel.mdx
new file mode 100644
index 0000000000..514181aad4
--- /dev/null
+++ b/sites/skeleton.dev/src/content/docs/framework-components/floating-panel.mdx
@@ -0,0 +1,236 @@
+---
+title: Floating Panel
+description: A draggable, resizable floating panel with minimize/maximize controls.
+stability: beta
+---
+
+import AnchorPositionReact from '@/components/examples/framework-components/floating-panel/react/anchor-position';
+import AnchorPositionReactRaw from '@/components/examples/framework-components/floating-panel/react/anchor-position?raw';
+import ControlledReact from '@/components/examples/framework-components/floating-panel/react/controlled';
+import ControlledReactRaw from '@/components/examples/framework-components/floating-panel/react/controlled?raw';
+import DefaultReact from '@/components/examples/framework-components/floating-panel/react/default';
+import DefaultReactRaw from '@/components/examples/framework-components/floating-panel/react/default?raw';
+import DirReact from '@/components/examples/framework-components/floating-panel/react/dir';
+import DirReactRaw from '@/components/examples/framework-components/floating-panel/react/dir?raw';
+import DisableDraggingReact from '@/components/examples/framework-components/floating-panel/react/disable-dragging';
+import DisableDraggingReactRaw from '@/components/examples/framework-components/floating-panel/react/disable-dragging?raw';
+import DisableResizingReact from '@/components/examples/framework-components/floating-panel/react/disable-resizing';
+import DisableResizingReactRaw from '@/components/examples/framework-components/floating-panel/react/disable-resizing?raw';
+import ResizeTriggersReact from '@/components/examples/framework-components/floating-panel/react/resize-triggers';
+import ResizeTriggersReactRaw from '@/components/examples/framework-components/floating-panel/react/resize-triggers?raw';
+import SizeConstraintsReact from '@/components/examples/framework-components/floating-panel/react/size-constraints';
+import SizeConstraintsReactRaw from '@/components/examples/framework-components/floating-panel/react/size-constraints?raw';
+import AnchorPositionSvelte from '@/components/examples/framework-components/floating-panel/svelte/anchor-position.svelte';
+import AnchorPositionSvelteRaw from '@/components/examples/framework-components/floating-panel/svelte/anchor-position.svelte?raw';
+import ControlledSvelte from '@/components/examples/framework-components/floating-panel/svelte/controlled.svelte';
+import ControlledSvelteRaw from '@/components/examples/framework-components/floating-panel/svelte/controlled.svelte?raw';
+import DefaultSvelte from '@/components/examples/framework-components/floating-panel/svelte/default.svelte';
+import DefaultSvelteRaw from '@/components/examples/framework-components/floating-panel/svelte/default.svelte?raw';
+import DirSvelte from '@/components/examples/framework-components/floating-panel/svelte/dir.svelte';
+import DirSvelteRaw from '@/components/examples/framework-components/floating-panel/svelte/dir.svelte?raw';
+import DisableDraggingSvelte from '@/components/examples/framework-components/floating-panel/svelte/disable-dragging.svelte';
+import DisableDraggingSvelteRaw from '@/components/examples/framework-components/floating-panel/svelte/disable-dragging.svelte?raw';
+import DisableResizingSvelte from '@/components/examples/framework-components/floating-panel/svelte/disable-resizing.svelte';
+import DisableResizingSvelteRaw from '@/components/examples/framework-components/floating-panel/svelte/disable-resizing.svelte?raw';
+import ResizeTriggersSvelte from '@/components/examples/framework-components/floating-panel/svelte/resize-triggers.svelte';
+import ResizeTriggersSvelteRaw from '@/components/examples/framework-components/floating-panel/svelte/resize-triggers.svelte?raw';
+import SizeConstraintsSvelte from '@/components/examples/framework-components/floating-panel/svelte/size-constraints.svelte';
+import SizeConstraintsSvelteRaw from '@/components/examples/framework-components/floating-panel/svelte/size-constraints.svelte?raw';
+
+
+
+
+
+
+
+
+
+
+
+
+## Size Constraints
+
+Use the `minSize` and `maxSize` props to set size constraints on the Floating Panel.
+
+
+
+
+
+
+
+
+
+
+
+
+
+## Controlled
+
+Control the open state and size of the Floating Panel with your own state.
+
+
+
+
+
+
+
+
+
+
+
+
+
+## Anchor Position
+
+Position the panel relative to another element using the `defaultPosition` prop.
+
+
+
+
+
+
+
+
+
+
+
+
+
+## Resize Triggers
+
+Add resize triggers on all sides and corners of the Floating Panel using the `axis` prop.
+
+
+
+
+
+
+
+
+
+
+
+
+## Disable Dragging
+
+Disable dragging by setting the `draggable` prop to `false`. The panel will remain in a fixed position but can still be resized.
+
+
+
+
+
+
+
+
+
+
+
+
+
+## Disable Resizing
+
+Disable resizing by setting the `resizable` prop to `false`. The panel will have a fixed size but can still be dragged.
+
+
+
+
+
+
+
+
+
+
+
+
+
+## Direction
+
+Set the text direction (`ltr` or `rtl`) using the `dir` prop.
+
+
+
+
+
+
+
+
+
+
+
+
+
+## Anatomy
+
+Here's an overview of how the Floating Panel component is structured in code:
+
+
+
+```tsx
+import { FloatingPanel, Portal } from '@skeletonlabs/skeleton-react';
+
+export default function Anatomy() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+```
+
+
+
+
+```svelte
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+
+
+## API Reference
+
+
+
+
+
+
+