From 4557cfb2879d641824525931869415ac485038c0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 18 Nov 2025 00:12:12 +0000 Subject: [PATCH 01/40] Initial plan From 578234a79706e782d96dadae4b1cda6630e2264d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 18 Nov 2025 00:25:54 +0000 Subject: [PATCH 02/40] Add FloatingPanel component for Svelte and React with skeleton-common classes Co-authored-by: Hugos68 <63101006+Hugos68@users.noreply.github.com> --- .../src/classes/floating-panel.ts | 15 +++++++ packages/skeleton-common/src/index.ts | 1 + packages/skeleton-react/package.json | 1 + .../floating-panel/anatomy/body.tsx | 24 +++++++++++ .../floating-panel/anatomy/close-trigger.tsx | 25 +++++++++++ .../floating-panel/anatomy/content.tsx | 24 +++++++++++ .../floating-panel/anatomy/control.tsx | 24 +++++++++++ .../floating-panel/anatomy/drag-trigger.tsx | 25 +++++++++++ .../floating-panel/anatomy/header.tsx | 24 +++++++++++ .../floating-panel/anatomy/positioner.tsx | 30 +++++++++++++ .../floating-panel/anatomy/resize-trigger.tsx | 29 +++++++++++++ .../floating-panel/anatomy/root-context.tsx | 13 ++++++ .../floating-panel/anatomy/root-provider.tsx | 13 ++++++ .../floating-panel/anatomy/root.tsx | 15 +++++++ .../floating-panel/anatomy/stage-trigger.tsx | 28 +++++++++++++ .../floating-panel/anatomy/title.tsx | 24 +++++++++++ .../floating-panel/anatomy/trigger.tsx | 24 +++++++++++ .../src/components/floating-panel/index.ts | 16 +++++++ .../floating-panel/modules/anatomy.ts | 30 +++++++++++++ .../floating-panel/modules/provider.ts | 11 +++++ .../floating-panel/modules/root-context.ts | 4 ++ packages/skeleton-react/src/index.ts | 1 + packages/skeleton-svelte/package.json | 1 + .../floating-panel/anatomy/body.svelte | 36 ++++++++++++++++ .../anatomy/close-trigger.svelte | 38 +++++++++++++++++ .../floating-panel/anatomy/content.svelte | 38 +++++++++++++++++ .../floating-panel/anatomy/control.svelte | 38 +++++++++++++++++ .../anatomy/drag-trigger.svelte | 38 +++++++++++++++++ .../floating-panel/anatomy/header.svelte | 38 +++++++++++++++++ .../floating-panel/anatomy/positioner.svelte | 41 ++++++++++++++++++ .../anatomy/resize-trigger.svelte | 42 +++++++++++++++++++ .../anatomy/root-context.svelte | 20 +++++++++ .../anatomy/root-provider.svelte | 20 +++++++++ .../floating-panel/anatomy/root.svelte | 27 ++++++++++++ .../anatomy/stage-trigger.svelte | 40 ++++++++++++++++++ .../floating-panel/anatomy/title.svelte | 36 ++++++++++++++++ .../floating-panel/anatomy/trigger.svelte | 38 +++++++++++++++++ .../src/components/floating-panel/index.ts | 16 +++++++ .../floating-panel/modules/anatomy.ts | 30 +++++++++++++ .../floating-panel/modules/provider.svelte.ts | 8 ++++ .../floating-panel/modules/root-context.ts | 4 ++ packages/skeleton-svelte/src/index.ts | 1 + pnpm-lock.yaml | 23 ++++++++++ pnpm-workspace.yaml | 1 + 44 files changed, 975 insertions(+) create mode 100644 packages/skeleton-common/src/classes/floating-panel.ts create mode 100644 packages/skeleton-react/src/components/floating-panel/anatomy/body.tsx create mode 100644 packages/skeleton-react/src/components/floating-panel/anatomy/close-trigger.tsx create mode 100644 packages/skeleton-react/src/components/floating-panel/anatomy/content.tsx create mode 100644 packages/skeleton-react/src/components/floating-panel/anatomy/control.tsx create mode 100644 packages/skeleton-react/src/components/floating-panel/anatomy/drag-trigger.tsx create mode 100644 packages/skeleton-react/src/components/floating-panel/anatomy/header.tsx create mode 100644 packages/skeleton-react/src/components/floating-panel/anatomy/positioner.tsx create mode 100644 packages/skeleton-react/src/components/floating-panel/anatomy/resize-trigger.tsx create mode 100644 packages/skeleton-react/src/components/floating-panel/anatomy/root-context.tsx create mode 100644 packages/skeleton-react/src/components/floating-panel/anatomy/root-provider.tsx create mode 100644 packages/skeleton-react/src/components/floating-panel/anatomy/root.tsx create mode 100644 packages/skeleton-react/src/components/floating-panel/anatomy/stage-trigger.tsx create mode 100644 packages/skeleton-react/src/components/floating-panel/anatomy/title.tsx create mode 100644 packages/skeleton-react/src/components/floating-panel/anatomy/trigger.tsx create mode 100644 packages/skeleton-react/src/components/floating-panel/index.ts create mode 100644 packages/skeleton-react/src/components/floating-panel/modules/anatomy.ts create mode 100644 packages/skeleton-react/src/components/floating-panel/modules/provider.ts create mode 100644 packages/skeleton-react/src/components/floating-panel/modules/root-context.ts create mode 100644 packages/skeleton-svelte/src/components/floating-panel/anatomy/body.svelte create mode 100644 packages/skeleton-svelte/src/components/floating-panel/anatomy/close-trigger.svelte create mode 100644 packages/skeleton-svelte/src/components/floating-panel/anatomy/content.svelte create mode 100644 packages/skeleton-svelte/src/components/floating-panel/anatomy/control.svelte create mode 100644 packages/skeleton-svelte/src/components/floating-panel/anatomy/drag-trigger.svelte create mode 100644 packages/skeleton-svelte/src/components/floating-panel/anatomy/header.svelte create mode 100644 packages/skeleton-svelte/src/components/floating-panel/anatomy/positioner.svelte create mode 100644 packages/skeleton-svelte/src/components/floating-panel/anatomy/resize-trigger.svelte create mode 100644 packages/skeleton-svelte/src/components/floating-panel/anatomy/root-context.svelte create mode 100644 packages/skeleton-svelte/src/components/floating-panel/anatomy/root-provider.svelte create mode 100644 packages/skeleton-svelte/src/components/floating-panel/anatomy/root.svelte create mode 100644 packages/skeleton-svelte/src/components/floating-panel/anatomy/stage-trigger.svelte create mode 100644 packages/skeleton-svelte/src/components/floating-panel/anatomy/title.svelte create mode 100644 packages/skeleton-svelte/src/components/floating-panel/anatomy/trigger.svelte create mode 100644 packages/skeleton-svelte/src/components/floating-panel/index.ts create mode 100644 packages/skeleton-svelte/src/components/floating-panel/modules/anatomy.ts create mode 100644 packages/skeleton-svelte/src/components/floating-panel/modules/provider.svelte.ts create mode 100644 packages/skeleton-svelte/src/components/floating-panel/modules/root-context.ts 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..33ecf7969c --- /dev/null +++ b/packages/skeleton-common/src/classes/floating-panel.ts @@ -0,0 +1,15 @@ +import { defineSkeletonClasses } from '../internal/define-skeleton-classes.js' with { type: 'macro' }; + +export const classesFloatingPanel = defineSkeletonClasses({ + trigger: '', + positioner: '', + content: '', + header: '', + body: '', + title: '', + resizeTrigger: '', + dragTrigger: '', + stageTrigger: '', + closeTrigger: '', + control: '', +}); 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 a421366796..12352eb474 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..3447028d6d --- /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 { classesFloatingPanel } from '@skeletonlabs/skeleton-common'; +import { mergeProps } from '@zag-js/react'; +import { type ComponentPropsWithoutRef, type ElementType, useContext } from 'react'; + +export interface FloatingPanelBodyProps extends ComponentPropsWithoutRef<'div'> { + element?: ElementType; +} + +export default function Body(props: FloatingPanelBodyProps) { + const { element: Element = 'div', children, ...rest } = props; + const floatingPanel = useContext(RootContext); + if (!floatingPanel) throw new Error('FloatingPanel.Body must be used within FloatingPanel.Root'); + + const attributes = mergeProps( + floatingPanel.getBodyProps(), + { + className: classesFloatingPanel.body, + }, + rest, + ); + + return {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..e4f4fe38d2 --- /dev/null +++ b/packages/skeleton-react/src/components/floating-panel/anatomy/close-trigger.tsx @@ -0,0 +1,25 @@ +import { RootContext } from '../modules/root-context.js'; +import { classesFloatingPanel } from '@skeletonlabs/skeleton-common'; +import { mergeProps } from '@zag-js/react'; +import { type ComponentPropsWithoutRef, type ElementType, useContext } from 'react'; + +export interface FloatingPanelCloseTriggerProps extends ComponentPropsWithoutRef<'button'> { + element?: ElementType; +} + +export default function CloseTrigger(props: FloatingPanelCloseTriggerProps) { + const { element: Element = 'button', children, ...rest } = props; + const floatingPanel = useContext(RootContext); + if (!floatingPanel) + throw new Error('FloatingPanel.CloseTrigger must be used within FloatingPanel.Root'); + + const attributes = mergeProps( + floatingPanel.getCloseTriggerProps(), + { + className: classesFloatingPanel.closeTrigger, + }, + rest, + ); + + return {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..8efd46664e --- /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 { classesFloatingPanel } from '@skeletonlabs/skeleton-common'; +import { mergeProps } from '@zag-js/react'; +import { type ComponentPropsWithoutRef, type ElementType, useContext } from 'react'; + +export interface FloatingPanelContentProps extends ComponentPropsWithoutRef<'div'> { + element?: ElementType; +} + +export default function Content(props: FloatingPanelContentProps) { + const { element: Element = 'div', children, ...rest } = props; + const floatingPanel = useContext(RootContext); + if (!floatingPanel) throw new Error('FloatingPanel.Content must be used within FloatingPanel.Root'); + + const attributes = mergeProps( + floatingPanel.getContentProps(), + { + className: classesFloatingPanel.content, + }, + rest, + ); + + return {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..045e5ad090 --- /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 { classesFloatingPanel } from '@skeletonlabs/skeleton-common'; +import { mergeProps } from '@zag-js/react'; +import { type ComponentPropsWithoutRef, type ElementType, useContext } from 'react'; + +export interface FloatingPanelControlProps extends ComponentPropsWithoutRef<'div'> { + element?: ElementType; +} + +export default function Control(props: FloatingPanelControlProps) { + const { element: Element = 'div', children, ...rest } = props; + const floatingPanel = useContext(RootContext); + if (!floatingPanel) throw new Error('FloatingPanel.Control must be used within FloatingPanel.Root'); + + const attributes = mergeProps( + floatingPanel.getControlProps(), + { + className: classesFloatingPanel.control, + }, + rest, + ); + + return {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..66a4bb5b74 --- /dev/null +++ b/packages/skeleton-react/src/components/floating-panel/anatomy/drag-trigger.tsx @@ -0,0 +1,25 @@ +import { RootContext } from '../modules/root-context.js'; +import { classesFloatingPanel } from '@skeletonlabs/skeleton-common'; +import { mergeProps } from '@zag-js/react'; +import { type ComponentPropsWithoutRef, type ElementType, useContext } from 'react'; + +export interface FloatingPanelDragTriggerProps extends ComponentPropsWithoutRef<'div'> { + element?: ElementType; +} + +export default function DragTrigger(props: FloatingPanelDragTriggerProps) { + const { element: Element = 'div', children, ...rest } = props; + const floatingPanel = useContext(RootContext); + if (!floatingPanel) + throw new Error('FloatingPanel.DragTrigger must be used within FloatingPanel.Root'); + + const attributes = mergeProps( + floatingPanel.getDragTriggerProps(), + { + className: classesFloatingPanel.dragTrigger, + }, + rest, + ); + + return {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..d1c26d40fe --- /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 { classesFloatingPanel } from '@skeletonlabs/skeleton-common'; +import { mergeProps } from '@zag-js/react'; +import { type ComponentPropsWithoutRef, type ElementType, useContext } from 'react'; + +export interface FloatingPanelHeaderProps extends ComponentPropsWithoutRef<'div'> { + element?: ElementType; +} + +export default function Header(props: FloatingPanelHeaderProps) { + const { element: Element = 'div', children, ...rest } = props; + const floatingPanel = useContext(RootContext); + if (!floatingPanel) throw new Error('FloatingPanel.Header must be used within FloatingPanel.Root'); + + const attributes = mergeProps( + floatingPanel.getHeaderProps(), + { + className: classesFloatingPanel.header, + }, + rest, + ); + + return {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..411e465cd5 --- /dev/null +++ b/packages/skeleton-react/src/components/floating-panel/anatomy/positioner.tsx @@ -0,0 +1,30 @@ +import { RootContext } from '../modules/root-context.js'; +import { classesFloatingPanel } from '@skeletonlabs/skeleton-common'; +import { mergeProps } from '@zag-js/react'; +import { Portal } from '@/components/portal/portal.jsx'; +import { type ComponentPropsWithoutRef, type ElementType, useContext } from 'react'; + +export interface FloatingPanelPositionerProps extends ComponentPropsWithoutRef<'div'> { + element?: ElementType; +} + +export default function Positioner(props: FloatingPanelPositionerProps) { + const { element: Element = 'div', children, ...rest } = props; + const floatingPanel = useContext(RootContext); + if (!floatingPanel) + throw new Error('FloatingPanel.Positioner must be used within FloatingPanel.Root'); + + const attributes = mergeProps( + floatingPanel.getPositionerProps(), + { + className: classesFloatingPanel.positioner, + }, + rest, + ); + + return ( + + {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..d11b77d037 --- /dev/null +++ b/packages/skeleton-react/src/components/floating-panel/anatomy/resize-trigger.tsx @@ -0,0 +1,29 @@ +import { RootContext } from '../modules/root-context.js'; +import { classesFloatingPanel } from '@skeletonlabs/skeleton-common'; +import { mergeProps } from '@zag-js/react'; +import { type ResizeTriggerProps, splitResizeTriggerProps } from '@zag-js/floating-panel'; +import { type ComponentPropsWithoutRef, type ElementType, useContext } from 'react'; + +export interface FloatingPanelResizeTriggerProps + extends ComponentPropsWithoutRef<'div'>, + ResizeTriggerProps { + element?: ElementType; +} + +export default function ResizeTrigger(props: FloatingPanelResizeTriggerProps) { + const [resizeTriggerProps, componentProps] = splitResizeTriggerProps(props); + const { element: Element = 'div', children, ...rest } = componentProps; + const floatingPanel = useContext(RootContext); + if (!floatingPanel) + throw new Error('FloatingPanel.ResizeTrigger must be used within FloatingPanel.Root'); + + const attributes = mergeProps( + floatingPanel.getResizeTriggerProps(resizeTriggerProps), + { + className: classesFloatingPanel.resizeTrigger, + }, + rest, + ); + + return {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..6ec7a1af6f --- /dev/null +++ b/packages/skeleton-react/src/components/floating-panel/anatomy/root-context.tsx @@ -0,0 +1,13 @@ +import { RootContext } from '../modules/root-context.js'; +import { type ReactNode, useContext } from 'react'; +import type { useFloatingPanel } from '../modules/provider.js'; + +export interface FloatingPanelRootContextProps { + children: (floatingPanel: ReturnType) => ReactNode; +} + +export default function RootContextComponent(props: FloatingPanelRootContextProps) { + const floatingPanel = useContext(RootContext); + if (!floatingPanel) throw new Error('FloatingPanel.Context must be used within FloatingPanel.Root'); + return <>{props.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..6d8a301740 --- /dev/null +++ b/packages/skeleton-react/src/components/floating-panel/anatomy/root-provider.tsx @@ -0,0 +1,13 @@ +import { RootContext } from '../modules/root-context.js'; +import type { useFloatingPanel } from '../modules/provider.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..07b46ff18e --- /dev/null +++ b/packages/skeleton-react/src/components/floating-panel/anatomy/stage-trigger.tsx @@ -0,0 +1,28 @@ +import { RootContext } from '../modules/root-context.js'; +import { classesFloatingPanel } from '@skeletonlabs/skeleton-common'; +import { mergeProps } from '@zag-js/react'; +import { type StageTriggerProps } from '@zag-js/floating-panel'; +import { type ComponentPropsWithoutRef, type ElementType, useContext } from 'react'; + +export interface FloatingPanelStageTriggerProps + extends ComponentPropsWithoutRef<'button'>, + StageTriggerProps { + element?: ElementType; +} + +export default function StageTrigger(props: FloatingPanelStageTriggerProps) { + const { element: Element = 'button', children, stage, ...rest } = props; + const floatingPanel = useContext(RootContext); + if (!floatingPanel) + throw new Error('FloatingPanel.StageTrigger must be used within FloatingPanel.Root'); + + const attributes = mergeProps( + floatingPanel.getStageTriggerProps({ stage }), + { + className: classesFloatingPanel.stageTrigger, + }, + rest, + ); + + return {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..62d8f67a0e --- /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 { classesFloatingPanel } from '@skeletonlabs/skeleton-common'; +import { mergeProps } from '@zag-js/react'; +import { type ComponentPropsWithoutRef, type ElementType, useContext } from 'react'; + +export interface FloatingPanelTitleProps extends ComponentPropsWithoutRef<'div'> { + element?: ElementType; +} + +export default function Title(props: FloatingPanelTitleProps) { + const { element: Element = 'div', children, ...rest } = props; + const floatingPanel = useContext(RootContext); + if (!floatingPanel) throw new Error('FloatingPanel.Title must be used within FloatingPanel.Root'); + + const attributes = mergeProps( + floatingPanel.getTitleProps(), + { + className: classesFloatingPanel.title, + }, + rest, + ); + + return {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..4a58a23eef --- /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 { classesFloatingPanel } from '@skeletonlabs/skeleton-common'; +import { mergeProps } from '@zag-js/react'; +import { type ComponentPropsWithoutRef, type ElementType, useContext } from 'react'; + +export interface FloatingPanelTriggerProps extends ComponentPropsWithoutRef<'button'> { + element?: ElementType; +} + +export default function Trigger(props: FloatingPanelTriggerProps) { + const { element: Element = 'button', children, ...rest } = props; + const floatingPanel = useContext(RootContext); + if (!floatingPanel) throw new Error('FloatingPanel.Trigger must be used within FloatingPanel.Root'); + + const attributes = mergeProps( + floatingPanel.getTriggerProps(), + { + className: classesFloatingPanel.trigger, + }, + rest, + ); + + return {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..574d554d21 --- /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, + Header: Header, + Body: Body, + Title: Title, + ResizeTrigger: ResizeTrigger, + DragTrigger: DragTrigger, + StageTrigger: StageTrigger, + CloseTrigger: CloseTrigger, + Control: Control, +}); 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..da0eb46268 --- /dev/null +++ b/packages/skeleton-react/src/components/floating-panel/modules/root-context.ts @@ -0,0 +1,4 @@ +import { createContext } from 'react'; +import type { useFloatingPanel } from './provider.js'; + +export const RootContext = createContext | null>(null); 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-svelte/package.json b/packages/skeleton-svelte/package.json index a91b15cecf..9430b98f50 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..f23cb9862f --- /dev/null +++ b/packages/skeleton-svelte/src/components/floating-panel/anatomy/close-trigger.svelte @@ -0,0 +1,38 @@ + + + + +{#if element} + {@render element(attributes)} +{:else} + +{/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..c2f08e3f49 --- /dev/null +++ b/packages/skeleton-svelte/src/components/floating-panel/anatomy/content.svelte @@ -0,0 +1,38 @@ + + + + +{#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..aa15203805 --- /dev/null +++ b/packages/skeleton-svelte/src/components/floating-panel/anatomy/control.svelte @@ -0,0 +1,38 @@ + + + + +{#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..bfde834732 --- /dev/null +++ b/packages/skeleton-svelte/src/components/floating-panel/anatomy/drag-trigger.svelte @@ -0,0 +1,38 @@ + + + + +{#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..f2766a8792 --- /dev/null +++ b/packages/skeleton-svelte/src/components/floating-panel/anatomy/header.svelte @@ -0,0 +1,38 @@ + + + + +{#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..6233bf8967 --- /dev/null +++ b/packages/skeleton-svelte/src/components/floating-panel/anatomy/positioner.svelte @@ -0,0 +1,41 @@ + + + + + + {#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..e0961f6122 --- /dev/null +++ b/packages/skeleton-svelte/src/components/floating-panel/anatomy/resize-trigger.svelte @@ -0,0 +1,42 @@ + + + + +{#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..7a38ef346e --- /dev/null +++ b/packages/skeleton-svelte/src/components/floating-panel/anatomy/stage-trigger.svelte @@ -0,0 +1,40 @@ + + + + +{#if element} + {@render element(attributes)} +{:else} + +{/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..ebe73e6989 --- /dev/null +++ b/packages/skeleton-svelte/src/components/floating-panel/anatomy/trigger.svelte @@ -0,0 +1,38 @@ + + + + +{#if element} + {@render element(attributes)} +{:else} + +{/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..95ca3e579d --- /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, + Header: Header, + Body: Body, + Title: Title, + ResizeTrigger: ResizeTrigger, + DragTrigger: DragTrigger, + StageTrigger: StageTrigger, + CloseTrigger: CloseTrigger, + Control: Control, +}); 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/pnpm-lock.yaml b/pnpm-lock.yaml index 4d66fc4425..429fabd33d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -159,6 +159,9 @@ catalogs: '@zag-js/file-upload': specifier: 1.28.0 version: 1.28.0 + '@zag-js/floating-panel': + specifier: 1.28.0 + version: 1.28.0 '@zag-js/listbox': specifier: 1.28.0 version: 1.28.0 @@ -538,6 +541,9 @@ importers: '@zag-js/file-upload': specifier: 'catalog:' version: 1.28.0 + '@zag-js/floating-panel': + specifier: 'catalog:' + version: 1.28.0 '@zag-js/listbox': specifier: 'catalog:' version: 1.28.0 @@ -656,6 +662,9 @@ importers: '@zag-js/file-upload': specifier: 'catalog:' version: 1.28.0 + '@zag-js/floating-panel': + specifier: 'catalog:' + version: 1.28.0 '@zag-js/listbox': specifier: 'catalog:' version: 1.28.0 @@ -3113,6 +3122,9 @@ packages: '@zag-js/file-utils@1.28.0': resolution: {integrity: sha512-wC5z7Ebp2/nwl2kkFSpOo7RifzS5W1IS2ekca6in2I7zk0c/i8MFZeKQxPYvcY1eP/d08/iNe4NytOdNR2Iz3A==} + '@zag-js/floating-panel@1.28.0': + resolution: {integrity: sha512-RjcofLy3CTOO+qRGX6JUoVabGKfjx5+mr3IpoDYP2U9eez5U//OZAQI29Y6nStgjyHohyGvtkiiytyfmX+/GBA==} + '@zag-js/focus-trap@1.28.0': resolution: {integrity: sha512-WJJKFJCoJY8cvjNzTzsfnzJvf6A8tuiwpMsbTVCNYWhXl8c0i5nPRonZgep5B7h7IzLc6yLEwQ+XxaWvJasWAg==} @@ -8178,6 +8190,17 @@ snapshots: dependencies: '@zag-js/i18n-utils': 1.28.0 + '@zag-js/floating-panel@1.28.0': + dependencies: + '@zag-js/anatomy': 1.28.0 + '@zag-js/core': 1.28.0 + '@zag-js/dom-query': 1.28.0 + '@zag-js/popper': 1.28.0 + '@zag-js/rect-utils': 1.28.0 + '@zag-js/store': 1.28.0 + '@zag-js/types': 1.28.0 + '@zag-js/utils': 1.28.0 + '@zag-js/focus-trap@1.28.0': dependencies: '@zag-js/dom-query': 1.28.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 7226d82f77..f940026695 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -55,6 +55,7 @@ catalog: '@zag-js/date-picker': 1.28.0 '@zag-js/dialog': 1.28.0 '@zag-js/file-upload': 1.28.0 + '@zag-js/floating-panel': 1.28.0 '@zag-js/listbox': 1.28.0 '@zag-js/menu': 1.28.0 '@zag-js/pagination': 1.28.0 From ca08cf133ec64725026f8c60a0700d045d54e167 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 18 Nov 2025 00:30:01 +0000 Subject: [PATCH 03/40] Rewrite React FloatingPanel components to follow skeleton-react conventions Co-authored-by: Hugos68 <63101006+Hugos68@users.noreply.github.com> --- .../floating-panel/anatomy/body.tsx | 16 ++++++------- .../floating-panel/anatomy/close-trigger.tsx | 19 ++++++++------- .../floating-panel/anatomy/content.tsx | 16 ++++++------- .../floating-panel/anatomy/control.tsx | 16 ++++++------- .../floating-panel/anatomy/drag-trigger.tsx | 19 ++++++++------- .../floating-panel/anatomy/header.tsx | 16 ++++++------- .../floating-panel/anatomy/positioner.tsx | 23 ++++++++---------- .../floating-panel/anatomy/resize-trigger.tsx | 22 ++++++++--------- .../floating-panel/anatomy/root-context.tsx | 14 ++++++----- .../floating-panel/anatomy/stage-trigger.tsx | 20 ++++++++-------- .../floating-panel/anatomy/title.tsx | 16 ++++++------- .../floating-panel/anatomy/trigger.tsx | 18 +++++++------- .../floating-panel/modules/root-context.ts | 4 ++-- .../components/floating-panel/test.svelte | 24 +++++++++++++++++++ 14 files changed, 135 insertions(+), 108 deletions(-) create mode 100644 packages/skeleton-svelte/test/components/floating-panel/test.svelte diff --git a/packages/skeleton-react/src/components/floating-panel/anatomy/body.tsx b/packages/skeleton-react/src/components/floating-panel/anatomy/body.tsx index 3447028d6d..f846a03952 100644 --- a/packages/skeleton-react/src/components/floating-panel/anatomy/body.tsx +++ b/packages/skeleton-react/src/components/floating-panel/anatomy/body.tsx @@ -1,16 +1,16 @@ 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 { type ComponentPropsWithoutRef, type ElementType, useContext } from 'react'; +import { use } from 'react'; -export interface FloatingPanelBodyProps extends ComponentPropsWithoutRef<'div'> { - element?: ElementType; -} +export interface FloatingPanelBodyProps extends PropsWithElement<'div'>, HTMLAttributes<'div'> {} export default function Body(props: FloatingPanelBodyProps) { - const { element: Element = 'div', children, ...rest } = props; - const floatingPanel = useContext(RootContext); - if (!floatingPanel) throw new Error('FloatingPanel.Body must be used within FloatingPanel.Root'); + const floatingPanel = use(RootContext); + + const { element, children, ...rest } = props; const attributes = mergeProps( floatingPanel.getBodyProps(), @@ -20,5 +20,5 @@ export default function Body(props: FloatingPanelBodyProps) { rest, ); - return {children}; + 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 index e4f4fe38d2..56d66b0dc9 100644 --- a/packages/skeleton-react/src/components/floating-panel/anatomy/close-trigger.tsx +++ b/packages/skeleton-react/src/components/floating-panel/anatomy/close-trigger.tsx @@ -1,17 +1,18 @@ 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 { type ComponentPropsWithoutRef, type ElementType, useContext } from 'react'; +import { use } from 'react'; -export interface FloatingPanelCloseTriggerProps extends ComponentPropsWithoutRef<'button'> { - element?: ElementType; -} +export interface FloatingPanelCloseTriggerProps + extends PropsWithElement<'button'>, + HTMLAttributes<'button'> {} export default function CloseTrigger(props: FloatingPanelCloseTriggerProps) { - const { element: Element = 'button', children, ...rest } = props; - const floatingPanel = useContext(RootContext); - if (!floatingPanel) - throw new Error('FloatingPanel.CloseTrigger must be used within FloatingPanel.Root'); + const floatingPanel = use(RootContext); + + const { element, children, ...rest } = props; const attributes = mergeProps( floatingPanel.getCloseTriggerProps(), @@ -21,5 +22,5 @@ export default function CloseTrigger(props: FloatingPanelCloseTriggerProps) { rest, ); - return {children}; + return element ? element(attributes) : ; } diff --git a/packages/skeleton-react/src/components/floating-panel/anatomy/content.tsx b/packages/skeleton-react/src/components/floating-panel/anatomy/content.tsx index 8efd46664e..fe6863ed11 100644 --- a/packages/skeleton-react/src/components/floating-panel/anatomy/content.tsx +++ b/packages/skeleton-react/src/components/floating-panel/anatomy/content.tsx @@ -1,16 +1,16 @@ 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 { type ComponentPropsWithoutRef, type ElementType, useContext } from 'react'; +import { use } from 'react'; -export interface FloatingPanelContentProps extends ComponentPropsWithoutRef<'div'> { - element?: ElementType; -} +export interface FloatingPanelContentProps extends PropsWithElement<'div'>, HTMLAttributes<'div'> {} export default function Content(props: FloatingPanelContentProps) { - const { element: Element = 'div', children, ...rest } = props; - const floatingPanel = useContext(RootContext); - if (!floatingPanel) throw new Error('FloatingPanel.Content must be used within FloatingPanel.Root'); + const floatingPanel = use(RootContext); + + const { element, children, ...rest } = props; const attributes = mergeProps( floatingPanel.getContentProps(), @@ -20,5 +20,5 @@ export default function Content(props: FloatingPanelContentProps) { rest, ); - return {children}; + 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 index 045e5ad090..d7ff0cb40e 100644 --- a/packages/skeleton-react/src/components/floating-panel/anatomy/control.tsx +++ b/packages/skeleton-react/src/components/floating-panel/anatomy/control.tsx @@ -1,16 +1,16 @@ 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 { type ComponentPropsWithoutRef, type ElementType, useContext } from 'react'; +import { use } from 'react'; -export interface FloatingPanelControlProps extends ComponentPropsWithoutRef<'div'> { - element?: ElementType; -} +export interface FloatingPanelControlProps extends PropsWithElement<'div'>, HTMLAttributes<'div'> {} export default function Control(props: FloatingPanelControlProps) { - const { element: Element = 'div', children, ...rest } = props; - const floatingPanel = useContext(RootContext); - if (!floatingPanel) throw new Error('FloatingPanel.Control must be used within FloatingPanel.Root'); + const floatingPanel = use(RootContext); + + const { element, children, ...rest } = props; const attributes = mergeProps( floatingPanel.getControlProps(), @@ -20,5 +20,5 @@ export default function Control(props: FloatingPanelControlProps) { rest, ); - return {children}; + 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 index 66a4bb5b74..26cdc20954 100644 --- a/packages/skeleton-react/src/components/floating-panel/anatomy/drag-trigger.tsx +++ b/packages/skeleton-react/src/components/floating-panel/anatomy/drag-trigger.tsx @@ -1,17 +1,18 @@ 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 { type ComponentPropsWithoutRef, type ElementType, useContext } from 'react'; +import { use } from 'react'; -export interface FloatingPanelDragTriggerProps extends ComponentPropsWithoutRef<'div'> { - element?: ElementType; -} +export interface FloatingPanelDragTriggerProps + extends PropsWithElement<'div'>, + HTMLAttributes<'div'> {} export default function DragTrigger(props: FloatingPanelDragTriggerProps) { - const { element: Element = 'div', children, ...rest } = props; - const floatingPanel = useContext(RootContext); - if (!floatingPanel) - throw new Error('FloatingPanel.DragTrigger must be used within FloatingPanel.Root'); + const floatingPanel = use(RootContext); + + const { element, children, ...rest } = props; const attributes = mergeProps( floatingPanel.getDragTriggerProps(), @@ -21,5 +22,5 @@ export default function DragTrigger(props: FloatingPanelDragTriggerProps) { rest, ); - return {children}; + 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 index d1c26d40fe..b42021191e 100644 --- a/packages/skeleton-react/src/components/floating-panel/anatomy/header.tsx +++ b/packages/skeleton-react/src/components/floating-panel/anatomy/header.tsx @@ -1,16 +1,16 @@ 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 { type ComponentPropsWithoutRef, type ElementType, useContext } from 'react'; +import { use } from 'react'; -export interface FloatingPanelHeaderProps extends ComponentPropsWithoutRef<'div'> { - element?: ElementType; -} +export interface FloatingPanelHeaderProps extends PropsWithElement<'div'>, HTMLAttributes<'div'> {} export default function Header(props: FloatingPanelHeaderProps) { - const { element: Element = 'div', children, ...rest } = props; - const floatingPanel = useContext(RootContext); - if (!floatingPanel) throw new Error('FloatingPanel.Header must be used within FloatingPanel.Root'); + const floatingPanel = use(RootContext); + + const { element, children, ...rest } = props; const attributes = mergeProps( floatingPanel.getHeaderProps(), @@ -20,5 +20,5 @@ export default function Header(props: FloatingPanelHeaderProps) { rest, ); - return {children}; + 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 index 411e465cd5..eb6f0a3c6d 100644 --- a/packages/skeleton-react/src/components/floating-panel/anatomy/positioner.tsx +++ b/packages/skeleton-react/src/components/floating-panel/anatomy/positioner.tsx @@ -1,18 +1,19 @@ 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 { Portal } from '@/components/portal/portal.jsx'; -import { type ComponentPropsWithoutRef, type ElementType, useContext } from 'react'; +import { use } from 'react'; -export interface FloatingPanelPositionerProps extends ComponentPropsWithoutRef<'div'> { - element?: ElementType; -} +export interface FloatingPanelPositionerProps + extends PropsWithElement<'div'>, + HTMLAttributes<'div'> {} export default function Positioner(props: FloatingPanelPositionerProps) { - const { element: Element = 'div', children, ...rest } = props; - const floatingPanel = useContext(RootContext); - if (!floatingPanel) - throw new Error('FloatingPanel.Positioner must be used within FloatingPanel.Root'); + const floatingPanel = use(RootContext); + + const { element, children, ...rest } = props; const attributes = mergeProps( floatingPanel.getPositionerProps(), @@ -22,9 +23,5 @@ export default function Positioner(props: FloatingPanelPositionerProps) { rest, ); - return ( - - {children} - - ); + 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 index d11b77d037..b2464ed90a 100644 --- a/packages/skeleton-react/src/components/floating-panel/anatomy/resize-trigger.tsx +++ b/packages/skeleton-react/src/components/floating-panel/anatomy/resize-trigger.tsx @@ -1,21 +1,21 @@ 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 { type ResizeTriggerProps, splitResizeTriggerProps } from '@zag-js/floating-panel'; -import { type ComponentPropsWithoutRef, type ElementType, useContext } from 'react'; +import { splitResizeTriggerProps, type ResizeTriggerProps } from '@zag-js/floating-panel'; +import { use } from 'react'; export interface FloatingPanelResizeTriggerProps - extends ComponentPropsWithoutRef<'div'>, - ResizeTriggerProps { - element?: ElementType; -} + extends ResizeTriggerProps, + PropsWithElement<'div'>, + HTMLAttributes<'div', 'axis'> {} export default function ResizeTrigger(props: FloatingPanelResizeTriggerProps) { + const floatingPanel = use(RootContext); + const [resizeTriggerProps, componentProps] = splitResizeTriggerProps(props); - const { element: Element = 'div', children, ...rest } = componentProps; - const floatingPanel = useContext(RootContext); - if (!floatingPanel) - throw new Error('FloatingPanel.ResizeTrigger must be used within FloatingPanel.Root'); + const { element, children, ...rest } = componentProps; const attributes = mergeProps( floatingPanel.getResizeTriggerProps(resizeTriggerProps), @@ -25,5 +25,5 @@ export default function ResizeTrigger(props: FloatingPanelResizeTriggerProps) { rest, ); - return {children}; + 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 index 6ec7a1af6f..99c2db88b2 100644 --- a/packages/skeleton-react/src/components/floating-panel/anatomy/root-context.tsx +++ b/packages/skeleton-react/src/components/floating-panel/anatomy/root-context.tsx @@ -1,13 +1,15 @@ -import { RootContext } from '../modules/root-context.js'; -import { type ReactNode, useContext } from 'react'; 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 RootContextComponent(props: FloatingPanelRootContextProps) { - const floatingPanel = useContext(RootContext); - if (!floatingPanel) throw new Error('FloatingPanel.Context must be used within FloatingPanel.Root'); - return <>{props.children(floatingPanel)}; +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/stage-trigger.tsx b/packages/skeleton-react/src/components/floating-panel/anatomy/stage-trigger.tsx index 07b46ff18e..c8ecdf94fe 100644 --- a/packages/skeleton-react/src/components/floating-panel/anatomy/stage-trigger.tsx +++ b/packages/skeleton-react/src/components/floating-panel/anatomy/stage-trigger.tsx @@ -1,20 +1,20 @@ 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 { type StageTriggerProps } from '@zag-js/floating-panel'; -import { type ComponentPropsWithoutRef, type ElementType, useContext } from 'react'; +import { use } from 'react'; export interface FloatingPanelStageTriggerProps - extends ComponentPropsWithoutRef<'button'>, - StageTriggerProps { - element?: ElementType; -} + extends StageTriggerProps, + PropsWithElement<'button'>, + HTMLAttributes<'button', 'stage'> {} export default function StageTrigger(props: FloatingPanelStageTriggerProps) { - const { element: Element = 'button', children, stage, ...rest } = props; - const floatingPanel = useContext(RootContext); - if (!floatingPanel) - throw new Error('FloatingPanel.StageTrigger must be used within FloatingPanel.Root'); + const floatingPanel = use(RootContext); + + const { element, children, stage, ...rest } = props; const attributes = mergeProps( floatingPanel.getStageTriggerProps({ stage }), @@ -24,5 +24,5 @@ export default function StageTrigger(props: FloatingPanelStageTriggerProps) { rest, ); - return {children}; + return element ? element(attributes) : ; } diff --git a/packages/skeleton-react/src/components/floating-panel/anatomy/title.tsx b/packages/skeleton-react/src/components/floating-panel/anatomy/title.tsx index 62d8f67a0e..2fff7f3ad0 100644 --- a/packages/skeleton-react/src/components/floating-panel/anatomy/title.tsx +++ b/packages/skeleton-react/src/components/floating-panel/anatomy/title.tsx @@ -1,16 +1,16 @@ 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 { type ComponentPropsWithoutRef, type ElementType, useContext } from 'react'; +import { use } from 'react'; -export interface FloatingPanelTitleProps extends ComponentPropsWithoutRef<'div'> { - element?: ElementType; -} +export interface FloatingPanelTitleProps extends PropsWithElement<'div'>, HTMLAttributes<'div'> {} export default function Title(props: FloatingPanelTitleProps) { - const { element: Element = 'div', children, ...rest } = props; - const floatingPanel = useContext(RootContext); - if (!floatingPanel) throw new Error('FloatingPanel.Title must be used within FloatingPanel.Root'); + const floatingPanel = use(RootContext); + + const { element, children, ...rest } = props; const attributes = mergeProps( floatingPanel.getTitleProps(), @@ -20,5 +20,5 @@ export default function Title(props: FloatingPanelTitleProps) { rest, ); - return {children}; + 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 index 4a58a23eef..8276121646 100644 --- a/packages/skeleton-react/src/components/floating-panel/anatomy/trigger.tsx +++ b/packages/skeleton-react/src/components/floating-panel/anatomy/trigger.tsx @@ -1,16 +1,18 @@ 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 { type ComponentPropsWithoutRef, type ElementType, useContext } from 'react'; +import { use } from 'react'; -export interface FloatingPanelTriggerProps extends ComponentPropsWithoutRef<'button'> { - element?: ElementType; -} +export interface FloatingPanelTriggerProps + extends PropsWithElement<'button'>, + HTMLAttributes<'button'> {} export default function Trigger(props: FloatingPanelTriggerProps) { - const { element: Element = 'button', children, ...rest } = props; - const floatingPanel = useContext(RootContext); - if (!floatingPanel) throw new Error('FloatingPanel.Trigger must be used within FloatingPanel.Root'); + const floatingPanel = use(RootContext); + + const { element, children, ...rest } = props; const attributes = mergeProps( floatingPanel.getTriggerProps(), @@ -20,5 +22,5 @@ export default function Trigger(props: FloatingPanelTriggerProps) { rest, ); - return {children}; + return element ? element(attributes) : ; } 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 index da0eb46268..e8f8f89900 100644 --- a/packages/skeleton-react/src/components/floating-panel/modules/root-context.ts +++ b/packages/skeleton-react/src/components/floating-panel/modules/root-context.ts @@ -1,4 +1,4 @@ -import { createContext } from 'react'; import type { useFloatingPanel } from './provider.js'; +import { createContext } from '@/internal/create-context.js'; -export const RootContext = createContext | null>(null); +export const RootContext = createContext>(); 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..3a1d9526c1 --- /dev/null +++ b/packages/skeleton-svelte/test/components/floating-panel/test.svelte @@ -0,0 +1,24 @@ + + + + Open Panel + + + + + Panel Title + + - + + + X + + + + Panel body content + + + + + From 27b320d87ea5d24de0f5abe0d079490bc1700cc8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 18 Nov 2025 00:36:39 +0000 Subject: [PATCH 04/40] Add tests for FloatingPanel component and remove Portal dependency from positioner Co-authored-by: Hugos68 <63101006+Hugos68@users.noreply.github.com> --- .../floating-panel/anatomy/positioner.tsx | 3 +- .../components/floating-panel/index.test.tsx | 87 +++++++++++++++++++ .../test/components/floating-panel/test.tsx | 28 ++++++ .../floating-panel/anatomy/positioner.svelte | 17 ++-- .../components/floating-panel/index.test.ts | 87 +++++++++++++++++++ 5 files changed, 210 insertions(+), 12 deletions(-) create mode 100644 packages/skeleton-react/test/components/floating-panel/index.test.tsx create mode 100644 packages/skeleton-react/test/components/floating-panel/test.tsx create mode 100644 packages/skeleton-svelte/test/components/floating-panel/index.test.ts diff --git a/packages/skeleton-react/src/components/floating-panel/anatomy/positioner.tsx b/packages/skeleton-react/src/components/floating-panel/anatomy/positioner.tsx index eb6f0a3c6d..f7a5240961 100644 --- a/packages/skeleton-react/src/components/floating-panel/anatomy/positioner.tsx +++ b/packages/skeleton-react/src/components/floating-panel/anatomy/positioner.tsx @@ -3,7 +3,6 @@ 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 { Portal } from '@/components/portal/portal.jsx'; import { use } from 'react'; export interface FloatingPanelPositionerProps @@ -23,5 +22,5 @@ export default function Positioner(props: FloatingPanelPositionerProps) { rest, ); - return {element ? element(attributes) :
{children}
}
; + return element ? element(attributes) :
{children}
; } 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..94b19b6e7d --- /dev/null +++ b/packages/skeleton-react/test/components/floating-panel/test.tsx @@ -0,0 +1,28 @@ +import { FloatingPanel } from '@/index.js'; + +export default function Test() { + return ( + + Open Panel + + + + + Panel Title + + + - + + + + + + X + + + Panel body content + + + + + ); +} diff --git a/packages/skeleton-svelte/src/components/floating-panel/anatomy/positioner.svelte b/packages/skeleton-svelte/src/components/floating-panel/anatomy/positioner.svelte index 6233bf8967..048b217c53 100644 --- a/packages/skeleton-svelte/src/components/floating-panel/anatomy/positioner.svelte +++ b/packages/skeleton-svelte/src/components/floating-panel/anatomy/positioner.svelte @@ -11,7 +11,6 @@ import { RootContext } from '../modules/root-context.js'; import { classesFloatingPanel } from '@skeletonlabs/skeleton-common'; import { mergeProps } from '@zag-js/svelte'; - import Portal from '@/components/portal/portal.svelte'; const props: FloatingPanelPositionerProps = $props(); @@ -30,12 +29,10 @@ ); - - {#if element} - {@render element(attributes)} - {:else} -
- {@render children?.()} -
- {/if} -
+{#if element} + {@render element(attributes)} +{:else} +
+ {@render children?.()} +
+{/if} 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(); + }); + }); +}); From 7517e73b4365ecd95b1fcc99f8001f377cb9085f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 18 Nov 2025 00:40:35 +0000 Subject: [PATCH 05/40] Add documentation and examples for FloatingPanel component Co-authored-by: Hugos68 <63101006+Hugos68@users.noreply.github.com> --- .../test/components/floating-panel/test.tsx | 16 +-- .../components/floating-panel/test.svelte | 14 +- .../floating-panel/react/default.tsx | 39 ++++++ .../floating-panel/react/provider-pattern.tsx | 43 ++++++ .../floating-panel/svelte/default.svelte | 37 ++++++ .../svelte/provider-pattern.svelte | 41 ++++++ .../framework-components/floating-panel.mdx | 123 ++++++++++++++++++ 7 files changed, 295 insertions(+), 18 deletions(-) create mode 100644 sites/skeleton.dev/src/components/examples/framework-components/floating-panel/react/default.tsx create mode 100644 sites/skeleton.dev/src/components/examples/framework-components/floating-panel/react/provider-pattern.tsx create mode 100644 sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/default.svelte create mode 100644 sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/provider-pattern.svelte create mode 100644 sites/skeleton.dev/src/content/docs/framework-components/floating-panel.mdx diff --git a/packages/skeleton-react/test/components/floating-panel/test.tsx b/packages/skeleton-react/test/components/floating-panel/test.tsx index 94b19b6e7d..f7fd2c44cd 100644 --- a/packages/skeleton-react/test/components/floating-panel/test.tsx +++ b/packages/skeleton-react/test/components/floating-panel/test.tsx @@ -3,23 +3,19 @@ import { FloatingPanel } from '@/index.js'; export default function Test() { return ( - Open Panel + - Panel Title + - - - - - - + - - X + + + - Panel body content + diff --git a/packages/skeleton-svelte/test/components/floating-panel/test.svelte b/packages/skeleton-svelte/test/components/floating-panel/test.svelte index 3a1d9526c1..4ca365123f 100644 --- a/packages/skeleton-svelte/test/components/floating-panel/test.svelte +++ b/packages/skeleton-svelte/test/components/floating-panel/test.svelte @@ -3,21 +3,19 @@ - Open Panel + - Panel Title + - - - + - X + + + - - Panel body content - + 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..f7021786fb --- /dev/null +++ b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/react/default.tsx @@ -0,0 +1,39 @@ +import { FloatingPanel, Portal } from '@skeletonlabs/skeleton-react'; +import { 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/provider-pattern.tsx b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/react/provider-pattern.tsx new file mode 100644 index 0000000000..91f5066dbb --- /dev/null +++ b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/react/provider-pattern.tsx @@ -0,0 +1,43 @@ +import { FloatingPanel, Portal, useFloatingPanel } from '@skeletonlabs/skeleton-react'; +import { XIcon } from 'lucide-react'; + +export default function Default() { + const floatingPanel = useFloatingPanel({ + defaultOpen: false, + }); + + function openPanel() { + floatingPanel.setOpen(true); + } + + return ( +
+ + + + Or Click Here + + + + +
+ + Provider Pattern + + + + +
+
+ +

This panel can be controlled programmatically using the provider pattern.

+
+
+
+
+
+
+ ); +} 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..78208f0414 --- /dev/null +++ b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/default.svelte @@ -0,0 +1,37 @@ + + + + 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/provider-pattern.svelte b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/provider-pattern.svelte new file mode 100644 index 0000000000..fe8f53b858 --- /dev/null +++ b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/provider-pattern.svelte @@ -0,0 +1,41 @@ + + +
+ + + + Or Click Here + + + + +
+ + Provider Pattern + + + + +
+
+ +

This panel can be controlled programmatically using the provider pattern.

+
+
+
+
+
+
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..4e128bc449 --- /dev/null +++ b/sites/skeleton.dev/src/content/docs/framework-components/floating-panel.mdx @@ -0,0 +1,123 @@ +--- +title: Floating Panel +description: A draggable, resizable floating panel with minimize/maximize controls. +--- + +import DefaultReact from '@/components/examples/framework-components/floating-panel/react/default'; +import DefaultReactRaw from '@/components/examples/framework-components/floating-panel/react/default?raw'; +import ProviderPatternReact from '@/components/examples/framework-components/floating-panel/react/provider-pattern'; +import ProviderPatternReactRaw from '@/components/examples/framework-components/floating-panel/react/provider-pattern?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 ProviderPatternSvelte from '@/components/examples/framework-components/floating-panel/svelte/provider-pattern.svelte'; +import ProviderPatternSvelteRaw from '@/components/examples/framework-components/floating-panel/svelte/provider-pattern.svelte?raw'; + + + + + + + + + + + + +Breaking convention in Skeleton, this component is provided "headless". Meaning no default styles are applied out of the box. This ensures you retain full control of all styling for maximum flexibility. + +## Provider Pattern + +Use the [Provider Pattern](/docs/[framework]/get-started/fundamentals#provider-pattern) to gain access to the inner component APIs. + + + + + + + + + + + + +## 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 + + + +Please refer to the [Zag.js Floating Panel documentation](https://zagjs.com/components/react/floating-panel) for a full list of props. + + + + +Please refer to the [Zag.js Floating Panel documentation](https://zagjs.com/components/svelte/floating-panel) for a full list of props. + + From aaa416314e24ba40e53eec9281218b834ebdeec0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 18 Nov 2025 00:55:46 +0000 Subject: [PATCH 06/40] Fix TypeScript errors and formatting issues, add stability beta to docs Co-authored-by: Hugos68 <63101006+Hugos68@users.noreply.github.com> --- .../floating-panel/anatomy/close-trigger.tsx | 4 +--- .../components/floating-panel/anatomy/drag-trigger.tsx | 4 +--- .../components/floating-panel/anatomy/positioner.tsx | 4 +--- .../floating-panel/anatomy/resize-trigger.tsx | 9 +++------ .../floating-panel/anatomy/root-provider.tsx | 2 +- .../floating-panel/anatomy/stage-trigger.tsx | 7 ++----- .../src/components/floating-panel/anatomy/trigger.tsx | 4 +--- .../floating-panel/anatomy/close-trigger.svelte | 4 +--- .../components/floating-panel/anatomy/content.svelte | 4 +--- .../components/floating-panel/anatomy/control.svelte | 4 +--- .../floating-panel/anatomy/drag-trigger.svelte | 4 +--- .../components/floating-panel/anatomy/header.svelte | 4 +--- .../floating-panel/anatomy/positioner.svelte | 4 +--- .../floating-panel/anatomy/resize-trigger.svelte | 10 ++++------ .../floating-panel/anatomy/stage-trigger.svelte | 5 +---- .../components/floating-panel/anatomy/trigger.svelte | 4 +--- .../floating-panel/svelte/default.svelte | 2 +- .../floating-panel/svelte/provider-pattern.svelte | 2 +- .../docs/framework-components/floating-panel.mdx | 1 + 19 files changed, 25 insertions(+), 57 deletions(-) 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 index 56d66b0dc9..8a3d31bc8c 100644 --- a/packages/skeleton-react/src/components/floating-panel/anatomy/close-trigger.tsx +++ b/packages/skeleton-react/src/components/floating-panel/anatomy/close-trigger.tsx @@ -5,9 +5,7 @@ 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 interface FloatingPanelCloseTriggerProps extends PropsWithElement<'button'>, HTMLAttributes<'button'> {} export default function CloseTrigger(props: FloatingPanelCloseTriggerProps) { const floatingPanel = use(RootContext); 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 index 26cdc20954..260528c25a 100644 --- a/packages/skeleton-react/src/components/floating-panel/anatomy/drag-trigger.tsx +++ b/packages/skeleton-react/src/components/floating-panel/anatomy/drag-trigger.tsx @@ -5,9 +5,7 @@ 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 interface FloatingPanelDragTriggerProps extends PropsWithElement<'div'>, HTMLAttributes<'div'> {} export default function DragTrigger(props: FloatingPanelDragTriggerProps) { const floatingPanel = use(RootContext); diff --git a/packages/skeleton-react/src/components/floating-panel/anatomy/positioner.tsx b/packages/skeleton-react/src/components/floating-panel/anatomy/positioner.tsx index f7a5240961..b7eab06c65 100644 --- a/packages/skeleton-react/src/components/floating-panel/anatomy/positioner.tsx +++ b/packages/skeleton-react/src/components/floating-panel/anatomy/positioner.tsx @@ -5,9 +5,7 @@ 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 interface FloatingPanelPositionerProps extends PropsWithElement<'div'>, HTMLAttributes<'div'> {} export default function Positioner(props: FloatingPanelPositionerProps) { const floatingPanel = use(RootContext); 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 index b2464ed90a..4e4a967e45 100644 --- a/packages/skeleton-react/src/components/floating-panel/anatomy/resize-trigger.tsx +++ b/packages/skeleton-react/src/components/floating-panel/anatomy/resize-trigger.tsx @@ -2,14 +2,11 @@ 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 { 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', 'axis'> {} +export interface FloatingPanelResizeTriggerProps extends ResizeTriggerProps, PropsWithElement<'div'>, HTMLAttributes<'div'> {} export default function ResizeTrigger(props: FloatingPanelResizeTriggerProps) { const floatingPanel = use(RootContext); @@ -18,7 +15,7 @@ export default function ResizeTrigger(props: FloatingPanelResizeTriggerProps) { const { element, children, ...rest } = componentProps; const attributes = mergeProps( - floatingPanel.getResizeTriggerProps(resizeTriggerProps), + floatingPanel.getResizeTriggerProps(resizeTriggerProps as ResizeTriggerProps), { className: classesFloatingPanel.resizeTrigger, }, 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 index 6d8a301740..1462720c85 100644 --- a/packages/skeleton-react/src/components/floating-panel/anatomy/root-provider.tsx +++ b/packages/skeleton-react/src/components/floating-panel/anatomy/root-provider.tsx @@ -1,5 +1,5 @@ -import { RootContext } from '../modules/root-context.js'; import type { useFloatingPanel } from '../modules/provider.js'; +import { RootContext } from '../modules/root-context.js'; import { type PropsWithChildren } from 'react'; export interface FloatingPanelRootProviderProps extends PropsWithChildren { 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 index c8ecdf94fe..05a76b4eb5 100644 --- a/packages/skeleton-react/src/components/floating-panel/anatomy/stage-trigger.tsx +++ b/packages/skeleton-react/src/components/floating-panel/anatomy/stage-trigger.tsx @@ -2,14 +2,11 @@ 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 { 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', 'stage'> {} +export interface FloatingPanelStageTriggerProps extends StageTriggerProps, PropsWithElement<'button'>, HTMLAttributes<'button'> {} export default function StageTrigger(props: FloatingPanelStageTriggerProps) { const floatingPanel = use(RootContext); diff --git a/packages/skeleton-react/src/components/floating-panel/anatomy/trigger.tsx b/packages/skeleton-react/src/components/floating-panel/anatomy/trigger.tsx index 8276121646..e0a3b651ca 100644 --- a/packages/skeleton-react/src/components/floating-panel/anatomy/trigger.tsx +++ b/packages/skeleton-react/src/components/floating-panel/anatomy/trigger.tsx @@ -5,9 +5,7 @@ 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 interface FloatingPanelTriggerProps extends PropsWithElement<'button'>, HTMLAttributes<'button'> {} export default function Trigger(props: FloatingPanelTriggerProps) { const floatingPanel = use(RootContext); 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 index f23cb9862f..0d6df62047 100644 --- a/packages/skeleton-svelte/src/components/floating-panel/anatomy/close-trigger.svelte +++ b/packages/skeleton-svelte/src/components/floating-panel/anatomy/close-trigger.svelte @@ -2,9 +2,7 @@ import type { HTMLAttributes } from '@/internal/html-attributes.js'; import type { PropsWithElement } from '@/internal/props-with-element.js'; - export interface FloatingPanelCloseTriggerProps - extends PropsWithElement<'button'>, - HTMLAttributes<'button'> {} + export interface FloatingPanelCloseTriggerProps extends PropsWithElement<'button'>, HTMLAttributes<'button'> {} diff --git a/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/provider-pattern.svelte b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/provider-pattern.svelte index fe8f53b858..cc9a82819f 100644 --- a/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/provider-pattern.svelte +++ b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/provider-pattern.svelte @@ -1,6 +1,6 @@ + + + +{#if element} + {@render element(attributes)} +{:else} + +{/if} +``` + +**Index** (`index.ts`): + +- Export all prop types from anatomy files +- Export the main component from modules/anatomy +- Export the hook from modules/provider + +### 4. Implement React Component + +**Directory**: `packages/skeleton-react/src/components/{component-name}/` + +**Provider** (`modules/provider.ts`): + +```typescript +import { type Api, connect, machine, type Props } from '@zag-js/{component-name}'; +import { normalizeProps, useMachine, type PropTypes } from '@zag-js/react'; +import { useId } from 'react'; + +export function use{ComponentName}(props: Omit = {}): Api { + const service = useMachine(machine, { + id: useId(), + ...props, + }); + return connect(service, normalizeProps); +} +``` + +**Root Context** (`modules/root-context.ts`): + +```typescript +import type { use{ComponentName} } from './provider.js'; +import { createContext } from '@/internal/create-context.js'; + +export const RootContext = createContext>(); +``` + +**Anatomy Components** (e.g., `anatomy/trigger.tsx`): + +```typescript +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 { classes{ComponentName} } from '@skeletonlabs/skeleton-common'; +import { mergeProps } from '@zag-js/react'; +import { use } from 'react'; + +export interface {ComponentName}TriggerProps + extends PropsWithElement<'button'>, + HTMLAttributes<'button'> {} + +export default function Trigger(props: {ComponentName}TriggerProps) { + const {componentName} = use(RootContext); + const { element, children, ...rest } = props; + + const attributes = mergeProps( + {componentName}.getTriggerProps(), + { + className: classes{ComponentName}.trigger, + }, + rest, + ); + + return element ? element(attributes) : ; +} +``` + +**Key React Patterns**: + +- Use `use()` hook instead of `useContext()` for context consumption +- Use `className` instead of `class` +- Use `PropsWithElement` and `HTMLAttributes` from internal modules +- Element prop is a render function: `element(attributes)` not `` +- For components with Zag-specific props (not HTML attributes), don't add them to HTMLAttributes second generic +- If using `splitProps` helpers, cast the result as needed: `as ZagPropsType` + +### 5. Create Tests + +**Svelte Tests** (`packages/skeleton-svelte/test/components/{component-name}/`): + +**test.svelte**: + +```svelte + + +<{ComponentName}> + <{ComponentName}.Trigger data-testid="trigger" /> + + +``` + +**index.test.ts**: + +```typescript +import Test from './test.svelte'; +import { render, screen } from '@testing-library/svelte'; +import { describe, expect, it } from 'vitest'; + +describe('{ComponentName}', () => { + describe('Trigger', () => { + it('renders', () => { + render(Test); + expect(screen.getByTestId('trigger')).toBeInTheDocument(); + }); + }); + // ... test all parts +}); +``` + +**React Tests** (`packages/skeleton-react/test/components/{component-name}/`): + +- Follow same pattern with JSX syntax +- Use `client:visible` for hydration + +**Test Conventions**: + +- Don't add dummy text to children slots - just self-close: `` +- Use `data-testid` for all tested elements +- Test all anatomy parts for rendering + +### 6. Create Documentation + +**Main Doc** (`sites/skeleton.dev/src/content/docs/framework-components/{component-name}.mdx`): + +````mdx +--- +title: { Component Name } +description: Brief description of the component +stability: beta +--- + +import DefaultReact from '@/components/examples/framework-components/{component-name}/react/default'; +import DefaultReactRaw from '@/components/examples/framework-components/{component-name}/react/default?raw'; +import ProviderPatternReact from '@/components/examples/framework-components/{component-name}/react/provider-pattern'; +import ProviderPatternReactRaw from '@/components/examples/framework-components/{component-name}/react/provider-pattern?raw'; +import DefaultSvelte from '@/components/examples/framework-components/{component-name}/svelte/default.svelte'; +import DefaultSvelteRaw from '@/components/examples/framework-components/{component-name}/svelte/default.svelte?raw'; +import ProviderPatternSvelte from '@/components/examples/framework-components/{component-name}/svelte/provider-pattern.svelte'; +import ProviderPatternSvelteRaw from '@/components/examples/framework-components/{component-name}/svelte/provider-pattern.svelte?raw'; + + + + + + + + + + + + +Breaking convention in Skeleton, this component is provided "headless". Meaning no default styles are applied out of the box. This ensures you retain full control of all styling for maximum flexibility. + +## Provider Pattern + +Use the [Provider Pattern](/docs/[framework]/get-started/fundamentals#provider-pattern) to gain access to the inner component APIs. + + + + + + + + + + + + +## Anatomy + +Here's an overview of how the {Component Name} component is structured in code: + + + +```tsx +import { {ComponentName} } from '@skeletonlabs/skeleton-react'; + +export default function Anatomy() { + return ( + <{ComponentName}> + + + ); +} +``` +```` + + + + +```svelte + + +<{ComponentName}> + + +``` + + + +## API Reference + + + + + + + +``` + +**Example Files** (`sites/skeleton.dev/src/components/examples/framework-components/{component-name}/`): + +Create both `react/` and `svelte/` subdirectories with: + +- `default.{tsx|svelte}` - Basic usage (NO `defaultOpen` prop unless needed) +- `provider-pattern.{tsx|svelte}` - Provider pattern example + +**Documentation Conventions**: + +- Mark all new components as `stability: beta` +- Use ApiReference component, not links to Zag docs +- Don't use `defaultOpen` in examples (it defaults to false) +- Include "headless" disclaimer +- Show Provider Pattern example +- Show complete Anatomy +- Use ApiReference for API docs + +### 7. Build and Validate + +```bash +# Build packages +pnpm --filter @skeletonlabs/skeleton-common build +pnpm --filter @skeletonlabs/skeleton-svelte build +pnpm --filter @skeletonlabs/skeleton-react build + +# Run checks +pnpm check # TypeScript checks +pnpm format # Format code +pnpm format:check # Verify formatting +pnpm test # Run all tests +``` + +### 8. Common Patterns and Gotchas + +**Type Safety**: + +- When Zag props conflict with HTML attributes, don't exclude them from HTMLAttributes +- Use type assertions when `splitProps` returns partial types: `as ZagPropsType` +- HTMLAttributes second generic is for excluding HTML attribute keys, not Zag-specific props + +**Portal Usage**: + +- Most Zag components don't need Portal wrapper for Positioner +- Check existing components (Dialog, Popover) to see if they use Portal +- FloatingPanel specifically doesn't use Portal + +**Formatting**: + +- Always run `pnpm format` before committing +- Prettier will handle all code style + +**Testing**: + +- All tests must pass before PR +- Don't add dummy content to test components +- Use self-closing tags when no children needed + +**Branch Naming**: + +- Use `feature/{component-name}` for new components +- Example: `feature/floating-panel` + +## Example Workflow + +1. Add dependency to catalog and packages +2. Install with `pnpm install --no-frozen-lockfile` +3. Create classes in skeleton-common +4. Implement Svelte component (provider, anatomy, tests) +5. Implement React component (provider, anatomy, tests) +6. Create documentation and examples +7. Build all packages +8. Run `pnpm check` and `pnpm format` +9. Run all tests +10. Create PR with description following template + +## Quality Checklist + +- [ ] Dependency added to catalog and both framework packages +- [ ] Classes created in skeleton-common +- [ ] Svelte component fully implemented with all anatomy parts +- [ ] React component fully implemented with all anatomy parts +- [ ] Both components follow framework conventions +- [ ] Tests created and passing for both frameworks +- [ ] Documentation created with stability: beta +- [ ] ApiReference component used (not Zag.js links) +- [ ] Examples don't use unnecessary defaultOpen +- [ ] All builds passing +- [ ] `pnpm check` passing +- [ ] `pnpm format:check` passing +- [ ] All tests passing + +## Resources + +- Check existing components for patterns: Dialog, Popover, Menu, Tooltip +- Zag.js documentation: https://zagjs.com +- Skeleton contribution guide: https://skeleton.dev/docs/resources/contribute/ 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 index f7021786fb..f5eb3628fa 100644 --- 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 @@ -3,7 +3,7 @@ import { XIcon, MinusIcon, MaximizeIcon } from 'lucide-react'; export default function Default() { return ( - + Open Panel 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 index b2a7a8e561..b157487280 100644 --- 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 @@ -3,7 +3,7 @@ import { FloatingPanel, Portal } from '@skeletonlabs/skeleton-svelte'; - + Open Panel 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 index 647d61452a..8962bcb27d 100644 --- a/sites/skeleton.dev/src/content/docs/framework-components/floating-panel.mdx +++ b/sites/skeleton.dev/src/content/docs/framework-components/floating-panel.mdx @@ -113,12 +113,8 @@ export default function Anatomy() { ## API Reference - -Please refer to the [Zag.js Floating Panel documentation](https://zagjs.com/components/react/floating-panel) for a full list of props. - + - -Please refer to the [Zag.js Floating Panel documentation](https://zagjs.com/components/svelte/floating-panel) for a full list of props. - + From 6e8cd3ff25867e2140340fd2e8900de429a2dbc9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 18 Nov 2025 12:12:54 +0000 Subject: [PATCH 08/40] Remove agent instructions file Co-authored-by: Hugos68 <63101006+Hugos68@users.noreply.github.com> --- .github/AGENT_INSTRUCTIONS_ZAG_COMPONENTS.md | 450 ------------------- 1 file changed, 450 deletions(-) delete mode 100644 .github/AGENT_INSTRUCTIONS_ZAG_COMPONENTS.md diff --git a/.github/AGENT_INSTRUCTIONS_ZAG_COMPONENTS.md b/.github/AGENT_INSTRUCTIONS_ZAG_COMPONENTS.md deleted file mode 100644 index 3238591a44..0000000000 --- a/.github/AGENT_INSTRUCTIONS_ZAG_COMPONENTS.md +++ /dev/null @@ -1,450 +0,0 @@ -# Custom Agent Instructions for Contributing Zag-based Components to Skeleton - -## Overview - -These instructions are for AI agents contributing new Zag.js-based framework components to the Skeleton UI library. Follow these patterns to ensure consistency with existing components. - -## Repository Structure - -- **skeleton-common**: Shared classes and utilities (`packages/skeleton-common/src/classes/`) -- **skeleton-svelte**: Svelte 5 components (`packages/skeleton-svelte/src/components/`) -- **skeleton-react**: React components (`packages/skeleton-react/src/components/`) -- **Documentation**: Component docs (`sites/skeleton.dev/src/content/docs/framework-components/`) -- **Examples**: Live examples (`sites/skeleton.dev/src/components/examples/framework-components/`) - -## Step-by-Step Implementation Guide - -### 1. Add Zag.js Dependency - -**File**: `pnpm-workspace.yaml` - -- Add the Zag package to the `catalog` section in alphabetical order -- Use version `1.28.0` (or latest stable) - -**Files**: `packages/skeleton-svelte/package.json` and `packages/skeleton-react/package.json` - -- Add dependency in alphabetical order: `"@zag-js/{component-name}": "catalog:"` - -**Install dependencies**: - -```bash -pnpm install --no-frozen-lockfile -``` - -### 2. Create Classes in skeleton-common - -**File**: `packages/skeleton-common/src/classes/{component-name}.ts` - -```typescript -import { defineSkeletonClasses } from '../internal/define-skeleton-classes.js' with { type: 'macro' }; - -export const classes{ComponentName} = defineSkeletonClasses({ - // List all anatomy parts from Zag component - trigger: '', - positioner: '', - content: '', - // ... etc -}); -``` - -**File**: `packages/skeleton-common/src/index.ts` - -- Export the new classes file in alphabetical order - -### 3. Implement Svelte Component - -**Directory**: `packages/skeleton-svelte/src/components/{component-name}/` - -Create subdirectories: - -- `anatomy/` - Individual component parts -- `modules/` - Provider and context logic - -**Provider** (`modules/provider.svelte.ts`): - -```typescript -import { type Api, connect, machine, type Props } from '@zag-js/{component-name}'; -import { normalizeProps, useMachine, type PropTypes } from '@zag-js/svelte'; - -export function use{ComponentName}(props: Props | (() => Props)): () => Api { - const service = useMachine(machine, props); - const {componentName} = $derived(connect(service, normalizeProps)); - return () => {componentName}; -} -``` - -**Root Context** (`modules/root-context.ts`): - -```typescript -import type { use{ComponentName} } from './provider.svelte.js'; -import { createContext } from '@/internal/create-context.js'; - -export const RootContext = createContext>(); -``` - -**Anatomy Module** (`modules/anatomy.ts`): - -```typescript -import Part1 from '../anatomy/part1.svelte'; -// ... import all anatomy parts -import RootContext from '../anatomy/root-context.svelte'; -import RootProvider from '../anatomy/root-provider.svelte'; -import Root from '../anatomy/root.svelte'; - -export const { ComponentName } = Object.assign(Root, { - Provider: RootProvider, - Context: RootContext, - Part1: Part1, - // ... all parts -}); -``` - -**Anatomy Components** (e.g., `anatomy/trigger.svelte`): - -```svelte - - - - -{#if element} - {@render element(attributes)} -{:else} - -{/if} -``` - -**Index** (`index.ts`): - -- Export all prop types from anatomy files -- Export the main component from modules/anatomy -- Export the hook from modules/provider - -### 4. Implement React Component - -**Directory**: `packages/skeleton-react/src/components/{component-name}/` - -**Provider** (`modules/provider.ts`): - -```typescript -import { type Api, connect, machine, type Props } from '@zag-js/{component-name}'; -import { normalizeProps, useMachine, type PropTypes } from '@zag-js/react'; -import { useId } from 'react'; - -export function use{ComponentName}(props: Omit = {}): Api { - const service = useMachine(machine, { - id: useId(), - ...props, - }); - return connect(service, normalizeProps); -} -``` - -**Root Context** (`modules/root-context.ts`): - -```typescript -import type { use{ComponentName} } from './provider.js'; -import { createContext } from '@/internal/create-context.js'; - -export const RootContext = createContext>(); -``` - -**Anatomy Components** (e.g., `anatomy/trigger.tsx`): - -```typescript -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 { classes{ComponentName} } from '@skeletonlabs/skeleton-common'; -import { mergeProps } from '@zag-js/react'; -import { use } from 'react'; - -export interface {ComponentName}TriggerProps - extends PropsWithElement<'button'>, - HTMLAttributes<'button'> {} - -export default function Trigger(props: {ComponentName}TriggerProps) { - const {componentName} = use(RootContext); - const { element, children, ...rest } = props; - - const attributes = mergeProps( - {componentName}.getTriggerProps(), - { - className: classes{ComponentName}.trigger, - }, - rest, - ); - - return element ? element(attributes) : ; -} -``` - -**Key React Patterns**: - -- Use `use()` hook instead of `useContext()` for context consumption -- Use `className` instead of `class` -- Use `PropsWithElement` and `HTMLAttributes` from internal modules -- Element prop is a render function: `element(attributes)` not `` -- For components with Zag-specific props (not HTML attributes), don't add them to HTMLAttributes second generic -- If using `splitProps` helpers, cast the result as needed: `as ZagPropsType` - -### 5. Create Tests - -**Svelte Tests** (`packages/skeleton-svelte/test/components/{component-name}/`): - -**test.svelte**: - -```svelte - - -<{ComponentName}> - <{ComponentName}.Trigger data-testid="trigger" /> - - -``` - -**index.test.ts**: - -```typescript -import Test from './test.svelte'; -import { render, screen } from '@testing-library/svelte'; -import { describe, expect, it } from 'vitest'; - -describe('{ComponentName}', () => { - describe('Trigger', () => { - it('renders', () => { - render(Test); - expect(screen.getByTestId('trigger')).toBeInTheDocument(); - }); - }); - // ... test all parts -}); -``` - -**React Tests** (`packages/skeleton-react/test/components/{component-name}/`): - -- Follow same pattern with JSX syntax -- Use `client:visible` for hydration - -**Test Conventions**: - -- Don't add dummy text to children slots - just self-close: `` -- Use `data-testid` for all tested elements -- Test all anatomy parts for rendering - -### 6. Create Documentation - -**Main Doc** (`sites/skeleton.dev/src/content/docs/framework-components/{component-name}.mdx`): - -````mdx ---- -title: { Component Name } -description: Brief description of the component -stability: beta ---- - -import DefaultReact from '@/components/examples/framework-components/{component-name}/react/default'; -import DefaultReactRaw from '@/components/examples/framework-components/{component-name}/react/default?raw'; -import ProviderPatternReact from '@/components/examples/framework-components/{component-name}/react/provider-pattern'; -import ProviderPatternReactRaw from '@/components/examples/framework-components/{component-name}/react/provider-pattern?raw'; -import DefaultSvelte from '@/components/examples/framework-components/{component-name}/svelte/default.svelte'; -import DefaultSvelteRaw from '@/components/examples/framework-components/{component-name}/svelte/default.svelte?raw'; -import ProviderPatternSvelte from '@/components/examples/framework-components/{component-name}/svelte/provider-pattern.svelte'; -import ProviderPatternSvelteRaw from '@/components/examples/framework-components/{component-name}/svelte/provider-pattern.svelte?raw'; - - - - - - - - - - - - -Breaking convention in Skeleton, this component is provided "headless". Meaning no default styles are applied out of the box. This ensures you retain full control of all styling for maximum flexibility. - -## Provider Pattern - -Use the [Provider Pattern](/docs/[framework]/get-started/fundamentals#provider-pattern) to gain access to the inner component APIs. - - - - - - - - - - - - -## Anatomy - -Here's an overview of how the {Component Name} component is structured in code: - - - -```tsx -import { {ComponentName} } from '@skeletonlabs/skeleton-react'; - -export default function Anatomy() { - return ( - <{ComponentName}> - - - ); -} -``` -```` - - - - -```svelte - - -<{ComponentName}> - - -``` - - - -## API Reference - - - - - - - -``` - -**Example Files** (`sites/skeleton.dev/src/components/examples/framework-components/{component-name}/`): - -Create both `react/` and `svelte/` subdirectories with: - -- `default.{tsx|svelte}` - Basic usage (NO `defaultOpen` prop unless needed) -- `provider-pattern.{tsx|svelte}` - Provider pattern example - -**Documentation Conventions**: - -- Mark all new components as `stability: beta` -- Use ApiReference component, not links to Zag docs -- Don't use `defaultOpen` in examples (it defaults to false) -- Include "headless" disclaimer -- Show Provider Pattern example -- Show complete Anatomy -- Use ApiReference for API docs - -### 7. Build and Validate - -```bash -# Build packages -pnpm --filter @skeletonlabs/skeleton-common build -pnpm --filter @skeletonlabs/skeleton-svelte build -pnpm --filter @skeletonlabs/skeleton-react build - -# Run checks -pnpm check # TypeScript checks -pnpm format # Format code -pnpm format:check # Verify formatting -pnpm test # Run all tests -``` - -### 8. Common Patterns and Gotchas - -**Type Safety**: - -- When Zag props conflict with HTML attributes, don't exclude them from HTMLAttributes -- Use type assertions when `splitProps` returns partial types: `as ZagPropsType` -- HTMLAttributes second generic is for excluding HTML attribute keys, not Zag-specific props - -**Portal Usage**: - -- Most Zag components don't need Portal wrapper for Positioner -- Check existing components (Dialog, Popover) to see if they use Portal -- FloatingPanel specifically doesn't use Portal - -**Formatting**: - -- Always run `pnpm format` before committing -- Prettier will handle all code style - -**Testing**: - -- All tests must pass before PR -- Don't add dummy content to test components -- Use self-closing tags when no children needed - -**Branch Naming**: - -- Use `feature/{component-name}` for new components -- Example: `feature/floating-panel` - -## Example Workflow - -1. Add dependency to catalog and packages -2. Install with `pnpm install --no-frozen-lockfile` -3. Create classes in skeleton-common -4. Implement Svelte component (provider, anatomy, tests) -5. Implement React component (provider, anatomy, tests) -6. Create documentation and examples -7. Build all packages -8. Run `pnpm check` and `pnpm format` -9. Run all tests -10. Create PR with description following template - -## Quality Checklist - -- [ ] Dependency added to catalog and both framework packages -- [ ] Classes created in skeleton-common -- [ ] Svelte component fully implemented with all anatomy parts -- [ ] React component fully implemented with all anatomy parts -- [ ] Both components follow framework conventions -- [ ] Tests created and passing for both frameworks -- [ ] Documentation created with stability: beta -- [ ] ApiReference component used (not Zag.js links) -- [ ] Examples don't use unnecessary defaultOpen -- [ ] All builds passing -- [ ] `pnpm check` passing -- [ ] `pnpm format:check` passing -- [ ] All tests passing - -## Resources - -- Check existing components for patterns: Dialog, Popover, Menu, Tooltip -- Zag.js documentation: https://zagjs.com -- Skeleton contribution guide: https://skeleton.dev/docs/resources/contribute/ From 03bffaadbcfdc0e17cacf1fc327c5e864af759e4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 18 Nov 2025 12:23:46 +0000 Subject: [PATCH 09/40] Remove provider pattern examples from documentation Co-authored-by: Hugos68 <63101006+Hugos68@users.noreply.github.com> --- .../floating-panel/react/provider-pattern.tsx | 43 ------------------- .../svelte/provider-pattern.svelte | 41 ------------------ .../framework-components/floating-panel.mdx | 19 -------- 3 files changed, 103 deletions(-) delete mode 100644 sites/skeleton.dev/src/components/examples/framework-components/floating-panel/react/provider-pattern.tsx delete mode 100644 sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/provider-pattern.svelte diff --git a/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/react/provider-pattern.tsx b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/react/provider-pattern.tsx deleted file mode 100644 index 91f5066dbb..0000000000 --- a/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/react/provider-pattern.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { FloatingPanel, Portal, useFloatingPanel } from '@skeletonlabs/skeleton-react'; -import { XIcon } from 'lucide-react'; - -export default function Default() { - const floatingPanel = useFloatingPanel({ - defaultOpen: false, - }); - - function openPanel() { - floatingPanel.setOpen(true); - } - - return ( -
- - - - Or Click Here - - - - -
- - Provider Pattern - - - - -
-
- -

This panel can be controlled programmatically using the provider pattern.

-
-
-
-
-
-
- ); -} diff --git a/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/provider-pattern.svelte b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/provider-pattern.svelte deleted file mode 100644 index cc9a82819f..0000000000 --- a/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/provider-pattern.svelte +++ /dev/null @@ -1,41 +0,0 @@ - - -
- - - - Or Click Here - - - - -
- - Provider Pattern - - - - -
-
- -

This panel can be controlled programmatically using the provider pattern.

-
-
-
-
-
-
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 index 8962bcb27d..1f6a1a7f55 100644 --- a/sites/skeleton.dev/src/content/docs/framework-components/floating-panel.mdx +++ b/sites/skeleton.dev/src/content/docs/framework-components/floating-panel.mdx @@ -6,12 +6,8 @@ stability: beta import DefaultReact from '@/components/examples/framework-components/floating-panel/react/default'; import DefaultReactRaw from '@/components/examples/framework-components/floating-panel/react/default?raw'; -import ProviderPatternReact from '@/components/examples/framework-components/floating-panel/react/provider-pattern'; -import ProviderPatternReactRaw from '@/components/examples/framework-components/floating-panel/react/provider-pattern?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 ProviderPatternSvelte from '@/components/examples/framework-components/floating-panel/svelte/provider-pattern.svelte'; -import ProviderPatternSvelteRaw from '@/components/examples/framework-components/floating-panel/svelte/provider-pattern.svelte?raw'; @@ -26,21 +22,6 @@ import ProviderPatternSvelteRaw from '@/components/examples/framework-components Breaking convention in Skeleton, this component is provided "headless". Meaning no default styles are applied out of the box. This ensures you retain full control of all styling for maximum flexibility. -## Provider Pattern - -Use the [Provider Pattern](/docs/[framework]/get-started/fundamentals#provider-pattern) to gain access to the inner component APIs. - - - - - - - - - - - - ## Anatomy Here's an overview of how the Floating Panel component is structured in code: From 9f07942aaaefdcb1588675e02f3f8c9ee951db13 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 18 Nov 2025 16:43:08 +0000 Subject: [PATCH 10/40] Add restore button, default styles, and fix overflow issues Co-authored-by: Hugos68 <63101006+Hugos68@users.noreply.github.com> --- packages/skeleton-common/src/classes/floating-panel.ts | 6 +++--- .../framework-components/floating-panel/react/default.tsx | 7 +++++-- .../floating-panel/svelte/default.svelte | 7 +++++-- .../content/docs/framework-components/floating-panel.mdx | 4 ++-- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/packages/skeleton-common/src/classes/floating-panel.ts b/packages/skeleton-common/src/classes/floating-panel.ts index 33ecf7969c..739e916c91 100644 --- a/packages/skeleton-common/src/classes/floating-panel.ts +++ b/packages/skeleton-common/src/classes/floating-panel.ts @@ -2,9 +2,9 @@ import { defineSkeletonClasses } from '../internal/define-skeleton-classes.js' w export const classesFloatingPanel = defineSkeletonClasses({ trigger: '', - positioner: '', - content: '', - header: '', + positioner: 'fixed', + content: 'flex flex-col overflow-x-auto overflow-y-auto', + header: 'w-full overflow-x-hidden', body: '', title: '', resizeTrigger: '', 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 index f5eb3628fa..0ef5ab958b 100644 --- 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 @@ -1,5 +1,5 @@ import { FloatingPanel, Portal } from '@skeletonlabs/skeleton-react'; -import { XIcon, MinusIcon, MaximizeIcon } from 'lucide-react'; +import { ArrowDownLeft, XIcon, MinusIcon, MaximizeIcon } from 'lucide-react'; export default function Default() { return ( @@ -7,7 +7,7 @@ export default function Default() { Open Panel - +
@@ -20,6 +20,9 @@ export default function Default() { + + + 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 index b157487280..ae98fd571f 100644 --- 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 @@ -1,5 +1,5 @@ @@ -7,7 +7,7 @@ Open Panel - +
@@ -20,6 +20,9 @@ + + + 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 index 1f6a1a7f55..b9a2928a9b 100644 --- a/sites/skeleton.dev/src/content/docs/framework-components/floating-panel.mdx +++ b/sites/skeleton.dev/src/content/docs/framework-components/floating-panel.mdx @@ -20,8 +20,6 @@ import DefaultSvelteRaw from '@/components/examples/framework-components/floatin -Breaking convention in Skeleton, this component is provided "headless". Meaning no default styles are applied out of the box. This ensures you retain full control of all styling for maximum flexibility. - ## Anatomy Here's an overview of how the Floating Panel component is structured in code: @@ -45,6 +43,7 @@ export default function Anatomy() { + @@ -78,6 +77,7 @@ export default function Anatomy() { + From 604e0fc590569d609cf2921e09ff8785f85edc02 Mon Sep 17 00:00:00 2001 From: Hugos68 Date: Wed, 19 Nov 2025 13:49:35 +0100 Subject: [PATCH 11/40] reset styles --- .../src/classes/floating-panel.ts | 6 +-- .../floating-panel/react/default.tsx | 44 +++++++++---------- .../floating-panel/svelte/default.svelte | 44 +++++++++---------- 3 files changed, 45 insertions(+), 49 deletions(-) diff --git a/packages/skeleton-common/src/classes/floating-panel.ts b/packages/skeleton-common/src/classes/floating-panel.ts index 739e916c91..33ecf7969c 100644 --- a/packages/skeleton-common/src/classes/floating-panel.ts +++ b/packages/skeleton-common/src/classes/floating-panel.ts @@ -2,9 +2,9 @@ import { defineSkeletonClasses } from '../internal/define-skeleton-classes.js' w export const classesFloatingPanel = defineSkeletonClasses({ trigger: '', - positioner: 'fixed', - content: 'flex flex-col overflow-x-auto overflow-y-auto', - header: 'w-full overflow-x-hidden', + positioner: '', + content: '', + header: '', body: '', title: '', resizeTrigger: '', 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 index 0ef5ab958b..d1d54ebedf 100644 --- 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 @@ -7,33 +7,31 @@ export default function Default() { Open Panel - - -
- - Floating 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/default.svelte b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/default.svelte index ae98fd571f..6612279c80 100644 --- 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 @@ -7,33 +7,31 @@ Open Panel - - -
- - Floating 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.

- +
From ea6e74fa2dd898e90ee33d51cefcf1d4b597f52f Mon Sep 17 00:00:00 2001 From: Hugos68 Date: Wed, 19 Nov 2025 13:50:05 +0100 Subject: [PATCH 12/40] changeset --- .changeset/plenty-birds-bet.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .changeset/plenty-birds-bet.md 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 From b0dd519055f0de3aee2250d222001225d0d8345c Mon Sep 17 00:00:00 2001 From: Hugos68 Date: Wed, 19 Nov 2025 16:41:44 +0100 Subject: [PATCH 13/40] styling and example stuff --- .../src/classes/floating-panel.ts | 16 +++---- .../app/components/floating-panel/page.tsx | 42 +++++++++++++++++++ .../components/floating-panel/+page.svelte | 38 +++++++++++++++++ .../floating-panel/react/default.tsx | 40 +++++++++--------- .../floating-panel/svelte/default.svelte | 40 +++++++++--------- 5 files changed, 128 insertions(+), 48 deletions(-) create mode 100644 playgrounds/skeleton-react/app/components/floating-panel/page.tsx create mode 100644 playgrounds/skeleton-svelte/src/routes/components/floating-panel/+page.svelte diff --git a/packages/skeleton-common/src/classes/floating-panel.ts b/packages/skeleton-common/src/classes/floating-panel.ts index 33ecf7969c..d38bfedf3d 100644 --- a/packages/skeleton-common/src/classes/floating-panel.ts +++ b/packages/skeleton-common/src/classes/floating-panel.ts @@ -3,13 +3,13 @@ import { defineSkeletonClasses } from '../internal/define-skeleton-classes.js' w export const classesFloatingPanel = defineSkeletonClasses({ trigger: '', positioner: '', - content: '', - header: '', - body: '', - title: '', - resizeTrigger: '', + content: 'card overflow-hidden shadow-lg border border-surface-300-800', dragTrigger: '', - stageTrigger: '', - closeTrigger: '', - control: '', + header: 'p-2 flex justify-between items-center bg-surface-200-800 border-b border-surface-300-800', + title: 'flex truncate ', + control: 'flex', + stageTrigger: 'rounded p-2 hover:preset-tonal', + closeTrigger: 'rounded p-2 hover:preset-tonal', + body: 'bg-surface-100-900 p-2 overflow-y-auto h-full', + resizeTrigger: 'size-2', }); 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/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 index d1d54ebedf..86cba2b959 100644 --- 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 @@ -1,5 +1,5 @@ import { FloatingPanel, Portal } from '@skeletonlabs/skeleton-react'; -import { ArrowDownLeft, XIcon, MinusIcon, MaximizeIcon } from 'lucide-react'; +import { MinimizeIcon, XIcon, MinusIcon, MaximizeIcon } from 'lucide-react'; export default function Default() { return ( @@ -8,30 +8,30 @@ export default function Default() { - - + + 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/default.svelte b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/default.svelte index 6612279c80..4b7d642621 100644 --- 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 @@ -1,5 +1,5 @@ @@ -8,30 +8,30 @@ - - + + 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.

+
-
From 87dea56e9d9b44623ab4a5b791e75eb61ad6668b Mon Sep 17 00:00:00 2001 From: Hugos68 Date: Wed, 19 Nov 2025 16:42:40 +0100 Subject: [PATCH 14/40] fixed anatomy --- .../framework-components/floating-panel.mdx | 46 +++++++++---------- 1 file changed, 22 insertions(+), 24 deletions(-) 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 index b9a2928a9b..92f6f9e124 100644 --- a/sites/skeleton.dev/src/content/docs/framework-components/floating-panel.mdx +++ b/sites/skeleton.dev/src/content/docs/framework-components/floating-panel.mdx @@ -36,19 +36,18 @@ export default function Anatomy() { - - + + - - - - - - - - - - + + + + + + + + + @@ -70,19 +69,18 @@ export default function Anatomy() { - - + + - - - - - - - - - - + + + + + + + + + From 8f8c247cef967862de3c55d924c8608584b464f8 Mon Sep 17 00:00:00 2001 From: Hugos68 Date: Wed, 19 Nov 2025 16:44:19 +0100 Subject: [PATCH 15/40] remove unused styles --- packages/skeleton-common/src/classes/floating-panel.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/skeleton-common/src/classes/floating-panel.ts b/packages/skeleton-common/src/classes/floating-panel.ts index d38bfedf3d..779fcca531 100644 --- a/packages/skeleton-common/src/classes/floating-panel.ts +++ b/packages/skeleton-common/src/classes/floating-panel.ts @@ -6,7 +6,7 @@ export const classesFloatingPanel = defineSkeletonClasses({ content: 'card overflow-hidden shadow-lg border border-surface-300-800', dragTrigger: '', header: 'p-2 flex justify-between items-center bg-surface-200-800 border-b border-surface-300-800', - title: 'flex truncate ', + title: '', control: 'flex', stageTrigger: 'rounded p-2 hover:preset-tonal', closeTrigger: 'rounded p-2 hover:preset-tonal', From 73cb4dae3ae9a5b812704898d3c3bb75223bc2f9 Mon Sep 17 00:00:00 2001 From: endigo9740 Date: Wed, 19 Nov 2025 11:49:54 -0600 Subject: [PATCH 16/40] Style improvements --- .../src/classes/floating-panel.ts | 16 ++++++++-------- .../floating-panel/react/default.tsx | 13 +++++++++---- .../floating-panel/svelte/default.svelte | 13 +++++++++---- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/packages/skeleton-common/src/classes/floating-panel.ts b/packages/skeleton-common/src/classes/floating-panel.ts index 779fcca531..afcd6f619c 100644 --- a/packages/skeleton-common/src/classes/floating-panel.ts +++ b/packages/skeleton-common/src/classes/floating-panel.ts @@ -3,13 +3,13 @@ import { defineSkeletonClasses } from '../internal/define-skeleton-classes.js' w export const classesFloatingPanel = defineSkeletonClasses({ trigger: '', positioner: '', - content: 'card overflow-hidden shadow-lg border border-surface-300-800', + content: 'card overflow-hidden shadow-lg border border-surface-300-700', dragTrigger: '', - header: 'p-2 flex justify-between items-center bg-surface-200-800 border-b border-surface-300-800', - title: '', - control: 'flex', - stageTrigger: 'rounded p-2 hover:preset-tonal', - closeTrigger: 'rounded p-2 hover:preset-tonal', - body: 'bg-surface-100-900 p-2 overflow-y-auto h-full', - resizeTrigger: 'size-2', + header: 'p-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', + resizeTrigger: 'size-2 w-0 h-0 border-8 border-surface-300-700 border-l-transparent border-t-transparent', + body: 'h-full bg-surface-100-900 p-4 overflow-y-auto', }); 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 index 86cba2b959..d27de0f1d7 100644 --- 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 @@ -1,5 +1,5 @@ import { FloatingPanel, Portal } from '@skeletonlabs/skeleton-react'; -import { MinimizeIcon, XIcon, MinusIcon, MaximizeIcon } from 'lucide-react'; +import { GripVerticalIcon, MinimizeIcon, XIcon, MinusIcon, MaximizeIcon } from 'lucide-react'; export default function Default() { return ( @@ -10,7 +10,10 @@ export default function Default() { - Floating Panel + + + Floating Panel + @@ -28,8 +31,10 @@ export default function Default() { -

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.

+

+ 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/default.svelte b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/default.svelte index 4b7d642621..5191d9fc1a 100644 --- 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 @@ -1,5 +1,5 @@ @@ -10,7 +10,10 @@ - Floating Panel + + + Floating Panel + @@ -28,8 +31,10 @@ -

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.

+

+ 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. +

From 5988f9c8045ec34a33b7f9b31ca8ca8367ff9c3a Mon Sep 17 00:00:00 2001 From: Hugos68 Date: Thu, 20 Nov 2025 13:18:51 +0100 Subject: [PATCH 17/40] add examples --- .../floating-panel/react/default.tsx | 2 +- .../floating-panel/react/size-constraints.tsx | 45 +++++++++++++++++++ .../floating-panel/svelte/default.svelte | 2 +- .../svelte/size-constraints.svelte | 43 ++++++++++++++++++ .../framework-components/floating-panel.mdx | 20 +++++++++ 5 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 sites/skeleton.dev/src/components/examples/framework-components/floating-panel/react/size-constraints.tsx create mode 100644 sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/size-constraints.svelte 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 index d27de0f1d7..8e4d18480e 100644 --- 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 @@ -12,7 +12,7 @@ export default function Default() { - Floating Panel + Floating Panel 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..3ae00ee4c7 --- /dev/null +++ b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/react/size-constraints.tsx @@ -0,0 +1,45 @@ +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 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/default.svelte b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/default.svelte index 5191d9fc1a..e9bdf11168 100644 --- 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 @@ -12,7 +12,7 @@ - Floating Panel + Floating Panel 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..9f8589b172 --- /dev/null +++ b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/size-constraints.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/content/docs/framework-components/floating-panel.mdx b/sites/skeleton.dev/src/content/docs/framework-components/floating-panel.mdx index 92f6f9e124..3cf3345151 100644 --- a/sites/skeleton.dev/src/content/docs/framework-components/floating-panel.mdx +++ b/sites/skeleton.dev/src/content/docs/framework-components/floating-panel.mdx @@ -6,8 +6,12 @@ stability: beta import DefaultReact from '@/components/examples/framework-components/floating-panel/react/default'; import DefaultReactRaw from '@/components/examples/framework-components/floating-panel/react/default?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 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 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'; @@ -20,6 +24,22 @@ import DefaultSvelteRaw from '@/components/examples/framework-components/floatin +## Size Constraints + +Use the `minSize` and `maxSize` props to set size constraints on the Floating Panel. + + + + + + + + + + + + + ## Anatomy Here's an overview of how the Floating Panel component is structured in code: From b9d3c5c721b48a6210017ac9d9e65eb97525babc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 20 Nov 2025 12:31:11 +0000 Subject: [PATCH 18/40] Add controlled, anchor position, disable dragging, and direction examples Co-authored-by: Hugos68 <63101006+Hugos68@users.noreply.github.com> --- .../floating-panel/react/anchor-position.tsx | 57 ++++++++++++ .../floating-panel/react/controlled.tsx | 87 +++++++++++++++++++ .../floating-panel/react/dir.tsx | 43 +++++++++ .../floating-panel/react/disable-dragging.tsx | 43 +++++++++ .../svelte/anchor-position.svelte | 57 ++++++++++++ .../floating-panel/svelte/controlled.svelte | 65 ++++++++++++++ .../floating-panel/svelte/dir.svelte | 41 +++++++++ .../svelte/disable-dragging.svelte | 41 +++++++++ .../framework-components/floating-panel.mdx | 80 +++++++++++++++++ 9 files changed, 514 insertions(+) create mode 100644 sites/skeleton.dev/src/components/examples/framework-components/floating-panel/react/anchor-position.tsx create mode 100644 sites/skeleton.dev/src/components/examples/framework-components/floating-panel/react/controlled.tsx create mode 100644 sites/skeleton.dev/src/components/examples/framework-components/floating-panel/react/dir.tsx create mode 100644 sites/skeleton.dev/src/components/examples/framework-components/floating-panel/react/disable-dragging.tsx create mode 100644 sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/anchor-position.svelte create mode 100644 sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/controlled.svelte create mode 100644 sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/dir.svelte create mode 100644 sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/disable-dragging.svelte 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..003d3a3ba8 --- /dev/null +++ b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/react/anchor-position.tsx @@ -0,0 +1,57 @@ +import { FloatingPanel, Portal } from '@skeletonlabs/skeleton-react'; +import { GripVerticalIcon, XIcon, MinusIcon, MaximizeIcon, MinimizeIcon } from 'lucide-react'; +import { useRef } from 'react'; + +export default function AnchorPosition() { + const buttonRef = useRef(null); + + return ( +
+ + + + Open Panel + + + + + + + + Anchored Panel + + + + + + + + + + + + + + + + + + +

This panel starts positioned next to the anchor element above.

+

Try dragging it to a new position.

+ +
+
+
+
+
+
+ ); +} 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..b1db119023 --- /dev/null +++ b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/react/controlled.tsx @@ -0,0 +1,87 @@ +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 ( +
+
+
+ +
+
+ + +
+
+ + setOpen(details.open)} + size={size} + onSizeChange={(details) => setSize(details.size)} + > + Open Panel + + + + + + + + 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/dir.tsx b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/react/dir.tsx new file mode 100644 index 0000000000..c611373d97 --- /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 ( + + فتح اللوحة + + + + + + + + لوحة عائمة + + + + + + + + + + + + + + + + + + +

هذه لوحة عائمة بدعم النص من اليمين إلى اليسار (RTL).

+

يمكنك سحبها من العنوان أو تغيير حجمها من الزاوية السفلية اليمنى.

+ +
+
+
+
+
+ ); +} 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..16baab690c --- /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/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..8f66f790db --- /dev/null +++ b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/anchor-position.svelte @@ -0,0 +1,57 @@ + + +
+ + + + Open Panel + + + + + + + + Anchored Panel + + + + + + + + + + + + + + + + + + +

This panel starts positioned next to the anchor element above.

+

Try dragging it to a new position.

+ +
+
+
+
+
+
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..71d98213f2 --- /dev/null +++ b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/controlled.svelte @@ -0,0 +1,65 @@ + + +
+
+
+ +
+
+ + +
+
+ + (open = details.open)} {size} onSizeChange={(details) => (size = details.size)}> + Open Panel + + + + + + + + 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/dir.svelte b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/dir.svelte new file mode 100644 index 0000000000..0c4dedc346 --- /dev/null +++ b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/dir.svelte @@ -0,0 +1,41 @@ + + + + فتح اللوحة + + + + + + + + لوحة عائمة + + + + + + + + + + + + + + + + + + +

هذه لوحة عائمة بدعم النص من اليمين إلى اليسار (RTL).

+

يمكنك سحبها من العنوان أو تغيير حجمها من الزاوية السفلية اليمنى.

+ +
+
+
+
+
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..de8a94bf52 --- /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/content/docs/framework-components/floating-panel.mdx b/sites/skeleton.dev/src/content/docs/framework-components/floating-panel.mdx index 3cf3345151..3095983199 100644 --- a/sites/skeleton.dev/src/content/docs/framework-components/floating-panel.mdx +++ b/sites/skeleton.dev/src/content/docs/framework-components/floating-panel.mdx @@ -4,12 +4,28 @@ description: A draggable, resizable floating panel with minimize/maximize contro 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 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 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'; @@ -40,6 +56,54 @@ Use the `minSize` and `maxSize` props to set size constraints on the Floating Pa +## 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. + + + + + + + + + + + + + +## Disable Dragging + +Disable dragging by setting the `draggable` prop to `false`. The panel will remain in a fixed position but can still be resized. + + + + + + + + + + + + + ## Anatomy Here's an overview of how the Floating Panel component is structured in code: @@ -109,6 +173,22 @@ export default function Anatomy() { +## Direction + +Set the text direction (`ltr` or `rtl`) using the `dir` prop. + + + + + + + + + + + + + ## API Reference From 8b7a16fc9dacb3d9b560f7f87360a2d054bb72e3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 20 Nov 2025 14:46:44 +0000 Subject: [PATCH 19/40] Use English text instead of Arabic in direction example Co-authored-by: Hugos68 <63101006+Hugos68@users.noreply.github.com> --- .../framework-components/floating-panel/react/dir.tsx | 8 ++++---- .../framework-components/floating-panel/svelte/dir.svelte | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) 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 index c611373d97..cc9a2d7fea 100644 --- 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 @@ -4,7 +4,7 @@ import { GripVerticalIcon, XIcon, MinusIcon, MaximizeIcon, MinimizeIcon } from ' export default function Dir() { return ( - فتح اللوحة + Open Panel @@ -12,7 +12,7 @@ export default function Dir() { - لوحة عائمة + Floating Panel @@ -31,8 +31,8 @@ export default function Dir() { -

هذه لوحة عائمة بدعم النص من اليمين إلى اليسار (RTL).

-

يمكنك سحبها من العنوان أو تغيير حجمها من الزاوية السفلية اليمنى.

+

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/dir.svelte b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/dir.svelte index 0c4dedc346..36dffc0eed 100644 --- 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 @@ -4,7 +4,7 @@ - فتح اللوحة + Open Panel @@ -12,7 +12,7 @@ - لوحة عائمة + Floating Panel @@ -31,8 +31,8 @@ -

هذه لوحة عائمة بدعم النص من اليمين إلى اليسار (RTL).

-

يمكنك سحبها من العنوان أو تغيير حجمها من الزاوية السفلية اليمنى.

+

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.

From f85f502993f62c7722b6fd0abd345e38382de7f2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 20 Nov 2025 15:13:29 +0000 Subject: [PATCH 20/40] Use getAnchorPosition instead of defaultPosition with refs in anchor example Co-authored-by: Hugos68 <63101006+Hugos68@users.noreply.github.com> --- .../floating-panel/react/anchor-position.tsx | 19 ++++++----------- .../svelte/anchor-position.svelte | 21 ++++++------------- 2 files changed, 12 insertions(+), 28 deletions(-) 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 index 003d3a3ba8..2d5a850f1c 100644 --- 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 @@ -1,21 +1,14 @@ import { FloatingPanel, Portal } from '@skeletonlabs/skeleton-react'; import { GripVerticalIcon, XIcon, MinusIcon, MaximizeIcon, MinimizeIcon } from 'lucide-react'; -import { useRef } from 'react'; export default function AnchorPosition() { - const buttonRef = useRef(null); - return (
- - ({ + x: boundaryRect.x + boundaryRect.width / 2 - 150, + y: boundaryRect.y + boundaryRect.height / 2 - 100, + })} > Open Panel @@ -44,8 +37,8 @@ export default function AnchorPosition() { -

This panel starts positioned next to the anchor element above.

-

Try dragging it to a new position.

+

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/anchor-position.svelte b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/anchor-position.svelte index 8f66f790db..b8d37d60c5 100644 --- 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 @@ -1,23 +1,14 @@
- - ({ + x: boundaryRect.x + boundaryRect.width / 2 - 150, + y: boundaryRect.y + boundaryRect.height / 2 - 100, + })} > Open Panel @@ -46,8 +37,8 @@ -

This panel starts positioned next to the anchor element above.

-

Try dragging it to a new position.

+

This panel is centered in the viewport using getAnchorPosition.

+

The position is calculated based on the boundary rectangle.

From 3ef7147d8537403acb021554d7f8b7da8d7d3203 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 20 Nov 2025 15:22:49 +0000 Subject: [PATCH 21/40] Remove trigger from controlled example and add disable resizing example Co-authored-by: Hugos68 <63101006+Hugos68@users.noreply.github.com> --- .../floating-panel/react/controlled.tsx | 1 - .../floating-panel/react/disable-resizing.tsx | 42 +++++++++++++++++++ .../floating-panel/svelte/controlled.svelte | 1 - .../svelte/disable-resizing.svelte | 40 ++++++++++++++++++ .../framework-components/floating-panel.mdx | 20 +++++++++ 5 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 sites/skeleton.dev/src/components/examples/framework-components/floating-panel/react/disable-resizing.tsx create mode 100644 sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/disable-resizing.svelte 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 index b1db119023..97e530b55a 100644 --- 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 @@ -47,7 +47,6 @@ export default function Controlled() { size={size} onSizeChange={(details) => setSize(details.size)} > - Open Panel 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..bfb3687f9d --- /dev/null +++ b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/react/disable-resizing.tsx @@ -0,0 +1,42 @@ +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/svelte/controlled.svelte b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/controlled.svelte index 71d98213f2..076e50f923 100644 --- 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 @@ -27,7 +27,6 @@
(open = details.open)} {size} onSizeChange={(details) => (size = details.size)}> - Open Panel 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..6e15535462 --- /dev/null +++ b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/disable-resizing.svelte @@ -0,0 +1,40 @@ + + + + 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/content/docs/framework-components/floating-panel.mdx b/sites/skeleton.dev/src/content/docs/framework-components/floating-panel.mdx index 3095983199..5ece65a86d 100644 --- a/sites/skeleton.dev/src/content/docs/framework-components/floating-panel.mdx +++ b/sites/skeleton.dev/src/content/docs/framework-components/floating-panel.mdx @@ -14,6 +14,8 @@ import DirReact from '@/components/examples/framework-components/floating-panel/ 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 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'; @@ -26,6 +28,8 @@ import DirSvelte from '@/components/examples/framework-components/floating-panel 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 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'; @@ -104,6 +108,22 @@ Disable dragging by setting the `draggable` prop to `false`. The panel will rema +## Disable Resizing + +Disable resizing by setting the `resizable` prop to `false`. The panel will have a fixed size but can still be dragged. + + + + + + + + + + + + + ## Anatomy Here's an overview of how the Floating Panel component is structured in code: From fd13633f9fa269bee293eddacda524151c2f573d Mon Sep 17 00:00:00 2001 From: Hugos68 Date: Thu, 20 Nov 2025 16:51:25 +0100 Subject: [PATCH 22/40] resize triggers, getAnchorPoint fix --- .../src/classes/floating-panel.ts | 2 +- .../floating-panel/react/anchor-position.tsx | 11 ++-- .../floating-panel/react/resize-triggers.tsx | 52 +++++++++++++++++++ .../svelte/anchor-position.svelte | 11 ++-- .../svelte/resize-triggers.svelte | 50 ++++++++++++++++++ .../framework-components/floating-panel.mdx | 19 +++++++ 6 files changed, 136 insertions(+), 9 deletions(-) create mode 100644 sites/skeleton.dev/src/components/examples/framework-components/floating-panel/react/resize-triggers.tsx create mode 100644 sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/resize-triggers.svelte diff --git a/packages/skeleton-common/src/classes/floating-panel.ts b/packages/skeleton-common/src/classes/floating-panel.ts index afcd6f619c..db613b4e2d 100644 --- a/packages/skeleton-common/src/classes/floating-panel.ts +++ b/packages/skeleton-common/src/classes/floating-panel.ts @@ -10,6 +10,6 @@ export const classesFloatingPanel = defineSkeletonClasses({ control: 'flex gap-1', stageTrigger: 'btn-icon hover:preset-tonal', closeTrigger: 'btn-icon hover:preset-tonal', - resizeTrigger: 'size-2 w-0 h-0 border-8 border-surface-300-700 border-l-transparent border-t-transparent', + resizeTrigger: 'size-2', body: 'h-full bg-surface-100-900 p-4 overflow-y-auto', }); 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 index 2d5a850f1c..bc1ab874e7 100644 --- 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 @@ -5,10 +5,13 @@ export default function AnchorPosition() { return (
({ - x: boundaryRect.x + boundaryRect.width / 2 - 150, - y: boundaryRect.y + boundaryRect.height / 2 - 100, - })} + getAnchorPosition={(ctx) => { + 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 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..d10263dee4 --- /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/svelte/anchor-position.svelte b/sites/skeleton.dev/src/components/examples/framework-components/floating-panel/svelte/anchor-position.svelte index b8d37d60c5..b8c6d995cc 100644 --- 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 @@ -5,10 +5,13 @@
({ - x: boundaryRect.x + boundaryRect.width / 2 - 150, - y: boundaryRect.y + boundaryRect.height / 2 - 100, - })} + getAnchorPosition={(ctx) => { + 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 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..9587026866 --- /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/content/docs/framework-components/floating-panel.mdx b/sites/skeleton.dev/src/content/docs/framework-components/floating-panel.mdx index 5ece65a86d..52452d95ca 100644 --- a/sites/skeleton.dev/src/content/docs/framework-components/floating-panel.mdx +++ b/sites/skeleton.dev/src/content/docs/framework-components/floating-panel.mdx @@ -16,6 +16,8 @@ import DisableDraggingReact from '@/components/examples/framework-components/flo 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'; @@ -30,6 +32,8 @@ import DisableDraggingSvelte from '@/components/examples/framework-components/fl 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'; @@ -92,6 +96,21 @@ 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. From 8b417c472a7ccd20dcf50b7245d90cb7ef12a940 Mon Sep 17 00:00:00 2001 From: Hugos68 Date: Thu, 20 Nov 2025 16:52:25 +0100 Subject: [PATCH 23/40] remove hardcoded size --- .../src/classes/floating-panel.ts | 2 +- .../floating-panel/svelte/resize-triggers.svelte | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/skeleton-common/src/classes/floating-panel.ts b/packages/skeleton-common/src/classes/floating-panel.ts index db613b4e2d..39faa470e9 100644 --- a/packages/skeleton-common/src/classes/floating-panel.ts +++ b/packages/skeleton-common/src/classes/floating-panel.ts @@ -10,6 +10,6 @@ export const classesFloatingPanel = defineSkeletonClasses({ control: 'flex gap-1', stageTrigger: 'btn-icon hover:preset-tonal', closeTrigger: 'btn-icon hover:preset-tonal', - resizeTrigger: 'size-2', + resizeTrigger: '', body: 'h-full bg-surface-100-900 p-4 overflow-y-auto', }); 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 index 9587026866..b34e25dffa 100644 --- 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 @@ -35,14 +35,14 @@ 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.

- - - - - - - - + + + + + + + + From ab79bad041fc164129af290d71b6a0765cec2700 Mon Sep 17 00:00:00 2001 From: Hugos68 Date: Thu, 20 Nov 2025 17:14:44 +0100 Subject: [PATCH 24/40] label text --- .../floating-panel/react/controlled.tsx | 10 +++------- .../floating-panel/svelte/controlled.svelte | 10 +++++----- 2 files changed, 8 insertions(+), 12 deletions(-) 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 index 97e530b55a..adf49c5d63 100644 --- 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 @@ -11,31 +11,27 @@ export default function Controlled() {
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 index 076e50f923..ab11cfd0ca 100644 --- 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 @@ -10,18 +10,18 @@
From e8f72b674d81007c5bafd6d2c7c7c88a5f339a81 Mon Sep 17 00:00:00 2001 From: endigo9740 Date: Thu, 20 Nov 2025 10:30:55 -0600 Subject: [PATCH 25/40] Adjust header padding --- packages/skeleton-common/src/classes/floating-panel.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/skeleton-common/src/classes/floating-panel.ts b/packages/skeleton-common/src/classes/floating-panel.ts index 39faa470e9..3cd712aafe 100644 --- a/packages/skeleton-common/src/classes/floating-panel.ts +++ b/packages/skeleton-common/src/classes/floating-panel.ts @@ -5,7 +5,7 @@ export const classesFloatingPanel = defineSkeletonClasses({ positioner: '', content: 'card overflow-hidden shadow-lg border border-surface-300-700', dragTrigger: '', - header: 'p-2 grid grid-cols-[1fr_auto] gap-2 items-center bg-surface-200-800 overflow-y-hidden', + 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', From ab8c3da20694549ada5c92942b92f4cc97e7e93b Mon Sep 17 00:00:00 2001 From: Hugos68 Date: Thu, 20 Nov 2025 17:35:24 +0100 Subject: [PATCH 26/40] fix conflict --- pnpm-lock.yaml | 79 +++++++++++---------------------------------- pnpm-workspace.yaml | 1 + 2 files changed, 20 insertions(+), 60 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 55239bc858..951d919dca 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -157,16 +157,11 @@ catalogs: specifier: 1.29.0 version: 1.29.0 '@zag-js/file-upload': -<<<<<<< HEAD - specifier: 1.28.0 - version: 1.28.0 + specifier: 1.29.0 + version: 1.29.0 '@zag-js/floating-panel': - specifier: 1.28.0 - version: 1.28.0 -======= specifier: 1.29.0 version: 1.29.0 ->>>>>>> main '@zag-js/listbox': specifier: 1.29.0 version: 1.29.0 @@ -545,14 +540,10 @@ importers: version: 1.29.0 '@zag-js/file-upload': specifier: 'catalog:' -<<<<<<< HEAD - version: 1.28.0 + version: 1.29.0 '@zag-js/floating-panel': specifier: 'catalog:' - version: 1.28.0 -======= version: 1.29.0 ->>>>>>> main '@zag-js/listbox': specifier: 'catalog:' version: 1.29.0 @@ -670,14 +661,10 @@ importers: version: 1.29.0 '@zag-js/file-upload': specifier: 'catalog:' -<<<<<<< HEAD - version: 1.28.0 + version: 1.29.0 '@zag-js/floating-panel': specifier: 'catalog:' - version: 1.28.0 -======= version: 1.29.0 ->>>>>>> main '@zag-js/listbox': specifier: 'catalog:' version: 1.29.0 @@ -3141,16 +3128,11 @@ packages: '@zag-js/file-utils@1.29.0': resolution: {integrity: sha512-T9etdsDPrCYTUQmy6/D6Tz2AteK6FVRfJRecxG3OJ7OVv1F6B4zH2SCJgpwnqJj6NBKjXLK1rVYuGzphJXNZ4g==} -<<<<<<< HEAD - '@zag-js/floating-panel@1.28.0': - resolution: {integrity: sha512-RjcofLy3CTOO+qRGX6JUoVabGKfjx5+mr3IpoDYP2U9eez5U//OZAQI29Y6nStgjyHohyGvtkiiytyfmX+/GBA==} + '@zag-js/floating-panel@1.29.0': + resolution: {integrity: sha512-7jT0h3jvOdKjEdBFhXb6vdUmeZTt5o5+d+mEdRCWWm5be35TdkenJw5FoRlzS6CF3e0Xhm7iROl86x2hM9c2Zw==} - '@zag-js/focus-trap@1.28.0': - resolution: {integrity: sha512-WJJKFJCoJY8cvjNzTzsfnzJvf6A8tuiwpMsbTVCNYWhXl8c0i5nPRonZgep5B7h7IzLc6yLEwQ+XxaWvJasWAg==} -======= '@zag-js/focus-trap@1.29.0': resolution: {integrity: sha512-eZCKUT7VJzCHCeIyCdX9hG+c5a4uMnHLA0CBDGFsB69On5wQ3v8N5febbdpEFvJMfOrf93HVK1cNHCZ+rvvQNA==} ->>>>>>> main '@zag-js/focus-visible@1.29.0': resolution: {integrity: sha512-srd/3bNge1EnnanH8yd/S2xiOaREO2w3RFt0Em7oRqRGqtEwuNEd0IS66LpZ4PdgiBrzfxha9yDjY2Co3/4a8A==} @@ -3685,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==} @@ -4171,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 @@ -5267,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'} @@ -6186,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 @@ -6195,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 @@ -7643,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 @@ -7663,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 @@ -8253,22 +8224,18 @@ snapshots: dependencies: '@zag-js/i18n-utils': 1.29.0 -<<<<<<< HEAD - '@zag-js/floating-panel@1.28.0': + '@zag-js/floating-panel@1.29.0': dependencies: - '@zag-js/anatomy': 1.28.0 - '@zag-js/core': 1.28.0 - '@zag-js/dom-query': 1.28.0 - '@zag-js/popper': 1.28.0 - '@zag-js/rect-utils': 1.28.0 - '@zag-js/store': 1.28.0 - '@zag-js/types': 1.28.0 - '@zag-js/utils': 1.28.0 + '@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.28.0': -======= '@zag-js/focus-trap@1.29.0': ->>>>>>> main dependencies: '@zag-js/dom-query': 1.29.0 @@ -8932,8 +8899,6 @@ snapshots: dependencies: base-64: 1.0.0 - devalue@5.4.2: {} - devalue@5.5.0: {} devlop@1.1.0: @@ -9501,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 @@ -10950,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 From 174f1766abe7129cfd0facd4fdfc0e052f4bf3c8 Mon Sep 17 00:00:00 2001 From: Hugos68 Date: Thu, 20 Nov 2025 17:53:49 +0100 Subject: [PATCH 27/40] fix order --- .../framework-components/floating-panel.mdx | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) 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 index 52452d95ca..fb101ee39e 100644 --- a/sites/skeleton.dev/src/content/docs/framework-components/floating-panel.mdx +++ b/sites/skeleton.dev/src/content/docs/framework-components/floating-panel.mdx @@ -143,6 +143,22 @@ Disable resizing by setting the `resizable` prop to `false`. The panel will have +## 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: @@ -212,22 +228,6 @@ export default function Anatomy() { -## Direction - -Set the text direction (`ltr` or `rtl`) using the `dir` prop. - - - - - - - - - - - - - ## API Reference From 91c58922d894606e38b08ba0004f3e15a89bc6af Mon Sep 17 00:00:00 2001 From: Hugos68 Date: Thu, 20 Nov 2025 18:09:30 +0100 Subject: [PATCH 28/40] fix controlled styles --- .../floating-panel/react/controlled.tsx | 49 ++++----- .../floating-panel/svelte/controlled.svelte | 104 +++++++++--------- 2 files changed, 69 insertions(+), 84 deletions(-) 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 index adf49c5d63..149135dfb2 100644 --- 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 @@ -7,34 +7,25 @@ export default function Controlled() { const [size, setSize] = useState({ width: 400, height: 300 }); return ( -
-
-
- -
-
- - -
+ <> +
+ + +
-
+ ); } 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 index ab11cfd0ca..1befe6db2e 100644 --- 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 @@ -6,59 +6,53 @@ let size = $state({ width: 400, height: 300 }); -
-
-
- -
-
- - -
-
- - (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.

- -
-
-
-
-
+
+ + +
+ + (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.

+ +
+
+
+
+
From 0cf8ced1206351a6ab54fc815f489cd2e16a48c7 Mon Sep 17 00:00:00 2001 From: Hugos68 Date: Thu, 20 Nov 2025 19:47:14 +0100 Subject: [PATCH 29/40] cleanup --- .../floating-panel/react/controlled.tsx | 7 +++++-- .../floating-panel/svelte/controlled.svelte | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) 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 index 149135dfb2..91921dff4b 100644 --- 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 @@ -4,11 +4,14 @@ import { useState } from 'react'; export default function Controlled() { const [open, setOpen] = useState(false); - const [size, setSize] = useState({ width: 400, height: 300 }); + const [size, setSize] = useState({ + width: 400, + height: 300, + }); return ( <> -
+