{
  "docPath": "layout/draggable-panel.tsx",
  "example": "import { useState } from \"react\";\nimport { Flex } from \"@godxjp/ui/layout\";\nimport { DraggablePanel } from \"@godxjp/ui/lab\";\nimport { Text } from \"@godxjp/ui/general\";\n\nfunction Assistant() {\n  const [position, setPosition] = useState({ x: 0, y: 0 });\n  return (\n    <DraggablePanel\n      title=\"アシスタント\"\n      placement=\"bottom-end\"\n      position={position}\n      onPositionChange={setPosition}\n    >\n      <Flex direction=\"col\" gap=\"sm\">\n        <Text size=\"sm\">この請求書の消費税区分について質問できます。</Text>\n      </Flex>\n    </DraggablePanel>\n  );\n}",
  "group": "layout",
  "importPath": "@godxjp/ui/lab",
  "name": "DraggablePanel",
  "props": [
    {
      "description": "Panel name, shown in the title bar. A string also becomes the region's accessible name.",
      "name": "title",
      "required": true,
      "type": "ReactNode"
    },
    {
      "description": "Panel body. Text stays selectable: only the title bar starts a drag.",
      "name": "children",
      "type": "ReactNode"
    },
    {
      "description": "Trailing slot in the title bar, before the close control.",
      "name": "extra",
      "type": "ReactNode"
    },
    {
      "defaultValue": "\"bottom-end\"",
      "description": "Resting corner before any movement. Logical directions, so it mirrors for an RTL locale with no extra work.",
      "name": "placement",
      "type": "\"top-start\" | \"top-end\" | \"bottom-start\" | \"bottom-end\""
    },
    {
      "defaultValue": "\"md\"",
      "description": "Panel width from the token ladder (18rem / 22rem / 28rem / xl → --centered-shell-width-md).",
      "name": "width",
      "type": "\"sm\" | \"md\" | \"lg\" | \"xl\""
    },
    {
      "defaultValue": "\"both\"",
      "description": "react-draggable's axis, ported verbatim. `none` keeps the panel mounted but pinned where it started.",
      "name": "axis",
      "type": "\"both\" | \"x\" | \"y\" | \"none\""
    },
    {
      "defaultValue": "\"viewport\"",
      "description": "react-draggable's bounds. `viewport` keeps the whole panel on screen so it can never be thrown off-screen and stranded. Its selector and {left, top, right, bottom} forms are deliberately absent: a selector reaches into internal DOM, and the object is spelled in physical directions that cannot mirror under RTL.",
      "name": "bounds",
      "type": "\"viewport\" | \"none\""
    },
    {
      "description": "react-draggable's controlled and uncontrolled offset from the resting corner, in CSS pixels on the physical axes. Pass `position` together with `onPositionChange` to own the value.",
      "name": "position / defaultPosition",
      "type": "{ x: number; y: number }"
    },
    {
      "description": "Fires with the CLAMPED offset after every pointer frame and every keyboard nudge. The panel reports where it is and never remembers it — where that is stored, and per what scope, is your decision.",
      "name": "onPositionChange",
      "type": "(position: { x: number; y: number }) => void"
    },
    {
      "description": "Presence renders the close control in the title bar (antd Modal's onCancel). Omit it for a panel the page controls entirely.",
      "name": "onClose",
      "type": "() => void"
    },
    {
      "description": "Optional localized strings for the close and move controls. Each key wins over t() when set. For script-injected embeds that cannot mount AppProvider without restyling the host page (gh#606).",
      "name": "labels",
      "type": "{ close?: string; move?: string }"
    },
    {
      "defaultValue": "false",
      "description": "react-draggable's disabled. The panel stays exactly where it is and the handle stops moving it, by pointer and by keyboard alike.",
      "name": "disabled",
      "type": "boolean"
    },
    {
      "description": "Root class. Geometry lives in the --draggable-panel-* tokens.",
      "name": "className",
      "type": "string"
    }
  ],
  "related": [
    "ResizablePanel — resizes panes WITHIN a layout (react-resizable-panels). It changes how much room a pane gets; it cannot move a floating element around the viewport.",
    "Sheet — a panel pinned to an edge of the screen, with a scrim and focus handling. Reach for it when the panel is a step in a flow rather than a companion to the page.",
    "Popover — anchored to its trigger and closed on outside press. Reach for it for something short-lived beside a control, not for a surface that stays.",
    "Dialog — modal and focus-trapping. Reach for it when the page behind must be unusable until the person answers."
  ],
  "rules": [
    23,
    44
  ],
  "storyPath": "layout/draggable-panel.tsx",
  "tagline": "A floating surface the person using it can MOVE — drag it by the title-bar handle, or focus the handle and nudge it with the arrow keys. Bounded to the viewport, position reported through onPositionChange and never stored by the library.",
  "tier": "lab",
  "usage": [
    "DO import from `@godxjp/ui/lab` (the opt-in lab subpath): `import { DraggablePanel } from \"@godxjp/ui/lab\";`",
    "DO treat the position as yours. The panel reports it and stores nothing, so persisting it per user, per workspace or not at all is a decision you make.",
    "DO keep the body text short enough to read at 22rem, or let it scroll — the body is its own scroll region and the panel is capped at 70vh.",
    "DON'T hand-roll this with a fixed-position div and pointer maths: that is page-local CSS ui-audit blocks, and every consumer that floats anything would write it again.",
    "DON'T reach for it when the surface should interrupt. This is a labelled region, not a dialog: it traps no focus, dims no page and announces nothing. Use Dialog when the decision must be made before anything else can happen.",
    "DON'T drag from the whole surface by wrapping the body in your own pointer handler — dragging from anywhere makes the text unselectable in a panel whose whole purpose is text."
  ],
  "useCases": [
    "A floating assistant or chat box over a data-dense screen, moved aside when it covers the column being asked about.",
    "A persistent calculator, unit converter or note pad kept on screen while the person works in the page behind it.",
    "A live preview or inspector panel positioned wherever the reviewer wants it for the screen they are on.",
    "A media or call tile that has to stay visible but must not sit on top of the form being filled in."
  ]
}
