@langchain/macaw-components 1.0.0-beta.1 → 1.0.0-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_chunks/{LoadingIndicator-DgtU9aqD.js → LoadingIndicator-CDcArsiM.js} +13 -12
- package/dist/_chunks/LoadingIndicator-CDcArsiM.js.map +1 -0
- package/dist/_chunks/render-attachment-icon-BWBEMxXZ.js +270 -0
- package/dist/_chunks/render-attachment-icon-BWBEMxXZ.js.map +1 -0
- package/dist/catalog.json +527 -408
- package/dist/components/Attachment/Attachment.d.ts +21 -0
- package/dist/components/Attachment/Attachment.js +165 -0
- package/dist/components/Attachment/Attachment.js.map +1 -0
- package/dist/components/Attachment/AttachmentList.d.ts +5 -0
- package/dist/components/Attachment/AttachmentList.js +32 -0
- package/dist/components/Attachment/AttachmentList.js.map +1 -0
- package/dist/components/Attachment/index.d.ts +4 -0
- package/dist/components/Attachment/index.js +3 -0
- package/dist/components/Attachment/render-attachment-icon.d.ts +3 -0
- package/dist/components/Attachment/render-attachment-icon.js +2 -0
- package/dist/components/Avatar/Avatar.js +2 -2
- package/dist/components/Badge/Badge.d.ts +2 -0
- package/dist/components/Badge/Badge.js +12 -1
- package/dist/components/Badge/Badge.js.map +1 -1
- package/dist/components/Banner/Banner.js +1 -1
- package/dist/components/Button/Button.js +1 -1
- package/dist/components/Button/constants.js +2 -2
- package/dist/components/Button/constants.js.map +1 -1
- package/dist/components/ChartCard/ChartCard.d.ts +1 -4
- package/dist/components/ChartCard/ChartCard.js +2 -2
- package/dist/components/ChartCard/ChartCard.js.map +1 -1
- package/dist/components/ChartCard/ChartCardSkeleton.d.ts +1 -1
- package/dist/components/ChartCard/ChartCardSkeleton.js +37 -0
- package/dist/components/ChartCard/ChartCardSkeleton.js.map +1 -1
- package/dist/components/ChartLegend/ChartLegend.js +2 -2
- package/dist/components/ChartLegend/ChartLegendItemRenderer.js +2 -2
- package/dist/components/ChartLegend/ChartLegendItemRenderer.js.map +1 -1
- package/dist/components/Code/Code.utils.d.ts +3 -2
- package/dist/components/Code/Code.utils.js +7 -3
- package/dist/components/Code/Code.utils.js.map +1 -1
- package/dist/components/Code/CodeLanguageSelect.d.ts +7 -0
- package/dist/components/Code/CodeLanguageSelect.js +29 -0
- package/dist/components/Code/CodeLanguageSelect.js.map +1 -0
- package/dist/components/Code/CodeLite.js +3 -0
- package/dist/components/Code/CodeLite.js.map +1 -1
- package/dist/components/Code/CodeToolbar.d.ts +7 -0
- package/dist/components/Code/CodeToolbar.js +19 -0
- package/dist/components/Code/CodeToolbar.js.map +1 -0
- package/dist/components/Code/codeLanguages.d.ts +7 -0
- package/dist/components/Code/codeLanguages.js +59 -0
- package/dist/components/Code/codeLanguages.js.map +1 -0
- package/dist/components/Code/index.d.ts +15 -2
- package/dist/components/Code/index.js +70 -50
- package/dist/components/Code/index.js.map +1 -1
- package/dist/components/Code/types.d.ts +1 -1
- package/dist/components/Command/Command.d.ts +10 -13
- package/dist/components/Command/Command.js +7 -2
- package/dist/components/Command/Command.js.map +1 -1
- package/dist/components/Command/CommandMenu.d.ts +7 -1
- package/dist/components/Command/CommandMenu.js +17 -9
- package/dist/components/Command/CommandMenu.js.map +1 -1
- package/dist/components/ContextMenu/ContextMenu.d.ts +6 -0
- package/dist/components/ContextMenu/ContextMenu.js +3 -2
- package/dist/components/ContextMenu/ContextMenu.js.map +1 -1
- package/dist/components/CopyButton/CopyButton.js +2 -2
- package/dist/components/Dialog/index.js +2 -2
- package/dist/components/DropdownMenu/DropdownMenu.d.ts +11 -0
- package/dist/components/DropdownMenu/DropdownMenu.js +5 -4
- package/dist/components/DropdownMenu/DropdownMenu.js.map +1 -1
- package/dist/components/ErrorMessage/ErrorMessage.js +1 -1
- package/dist/components/GroupedTabs/GroupedTabs.d.ts +5 -2
- package/dist/components/GroupedTabs/GroupedTabs.js +33 -26
- package/dist/components/GroupedTabs/GroupedTabs.js.map +1 -1
- package/dist/components/IconButton/IconButton.js +2 -2
- package/dist/components/Input/Input.js +1 -1
- package/dist/components/LineChart/LineChart.d.ts +2 -2
- package/dist/components/LineChart/LineChart.js +3 -1
- package/dist/components/LineChart/LineChart.js.map +1 -1
- package/dist/components/LineChart/LineChart.types.d.ts +16 -0
- package/dist/components/LineChart/LineChartPlot.d.ts +3 -2
- package/dist/components/LineChart/LineChartPlot.js +8 -1
- package/dist/components/LineChart/LineChartPlot.js.map +1 -1
- package/dist/components/LineChart/LineChartVerticalMarkers.d.ts +8 -0
- package/dist/components/LineChart/LineChartVerticalMarkers.js +47 -0
- package/dist/components/LineChart/LineChartVerticalMarkers.js.map +1 -0
- package/dist/components/LineChart/index.d.ts +1 -1
- package/dist/components/Logo/index.js +11 -6
- package/dist/components/Logo/index.js.map +1 -1
- package/dist/components/MetricChart/MetricChart.d.ts +2 -0
- package/dist/components/MetricChart/MetricChart.js +15 -4
- package/dist/components/MetricChart/MetricChart.js.map +1 -1
- package/dist/components/MetricChart/index.d.ts +1 -1
- package/dist/components/Pane/Pane.js +2 -2
- package/dist/components/SankeyChart/AnimatedSankeyLink.d.ts +17 -0
- package/dist/components/SankeyChart/AnimatedSankeyLink.js +44 -0
- package/dist/components/SankeyChart/AnimatedSankeyLink.js.map +1 -0
- package/dist/components/SankeyChart/AnimatedSankeyNode.d.ts +16 -0
- package/dist/components/SankeyChart/AnimatedSankeyNode.js +42 -0
- package/dist/components/SankeyChart/AnimatedSankeyNode.js.map +1 -0
- package/dist/components/SankeyChart/SankeyChart.d.ts +42 -0
- package/dist/components/SankeyChart/SankeyChart.js +185 -0
- package/dist/components/SankeyChart/SankeyChart.js.map +1 -0
- package/dist/components/SankeyChart/SankeyChart.types.d.ts +90 -0
- package/dist/components/SankeyChart/SankeyChart.types.js +0 -0
- package/dist/components/SankeyChart/SankeyChart.utils.d.ts +21 -0
- package/dist/components/SankeyChart/SankeyChart.utils.js +299 -0
- package/dist/components/SankeyChart/SankeyChart.utils.js.map +1 -0
- package/dist/components/SankeyChart/SankeyNodeLabel.d.ts +10 -0
- package/dist/components/SankeyChart/SankeyNodeLabel.js +30 -0
- package/dist/components/SankeyChart/SankeyNodeLabel.js.map +1 -0
- package/dist/components/SankeyChart/SankeyNodeLabel.utils.d.ts +9 -0
- package/dist/components/SankeyChart/SankeyNodeLabel.utils.js +29 -0
- package/dist/components/SankeyChart/SankeyNodeLabel.utils.js.map +1 -0
- package/dist/components/SankeyChart/constants.d.ts +22 -0
- package/dist/components/SankeyChart/constants.js +27 -0
- package/dist/components/SankeyChart/constants.js.map +1 -0
- package/dist/components/SankeyChart/index.d.ts +5 -0
- package/dist/components/SankeyChart/index.js +4 -0
- package/dist/components/Select/Select.js +5 -5
- package/dist/components/Select/Select.js.map +1 -1
- package/dist/components/Select/SelectPrimitive.d.ts +6 -0
- package/dist/components/Select/SelectPrimitive.js +3 -2
- package/dist/components/Select/SelectPrimitive.js.map +1 -1
- package/dist/components/Slider/Slider.js +10 -3
- package/dist/components/Slider/Slider.js.map +1 -1
- package/dist/components/SplitViewPane/SplitViewPane.d.ts +2 -1
- package/dist/components/SplitViewPane/SplitViewPane.js +113 -219
- package/dist/components/SplitViewPane/SplitViewPane.js.map +1 -1
- package/dist/components/SplitViewPane/SplitViewPaneHeader.js +2 -2
- package/dist/components/SplitViewPane/useShowChildren.d.ts +5 -0
- package/dist/components/SplitViewPane/useShowChildren.js +14 -0
- package/dist/components/SplitViewPane/useShowChildren.js.map +1 -0
- package/dist/components/SplitViewPane/useSplitViewPaneResize.d.ts +21 -0
- package/dist/components/SplitViewPane/useSplitViewPaneResize.js +145 -0
- package/dist/components/SplitViewPane/useSplitViewPaneResize.js.map +1 -0
- package/dist/components/Textarea/Textarea.d.ts +4 -3
- package/dist/components/Textarea/Textarea.js +7 -21
- package/dist/components/Textarea/Textarea.js.map +1 -1
- package/dist/components/ThinkingState/LoadingIndicator.d.ts +1 -0
- package/dist/components/ThinkingState/LoadingIndicator.js +1 -1
- package/dist/components/ThinkingState/ThinkingState.d.ts +1 -0
- package/dist/components/ThinkingState/ThinkingState.js +5 -4
- package/dist/components/ThinkingState/ThinkingState.js.map +1 -1
- package/dist/components/ThinkingState/index.js +1 -1
- package/dist/components/Toast/index.js +33 -28
- package/dist/components/Toast/index.js.map +1 -1
- package/dist/components/Tooltip/Tooltip.js +2 -2
- package/dist/components/Typeahead/Typeahead.js +18 -8
- package/dist/components/Typeahead/Typeahead.js.map +1 -1
- package/dist/components/Typeahead/Typeahead.types.d.ts +2 -0
- package/dist/components/Typeahead/TypeaheadParts.d.ts +3 -1
- package/dist/components/Typeahead/TypeaheadParts.js +7 -6
- package/dist/components/Typeahead/TypeaheadParts.js.map +1 -1
- package/dist/components/Typeahead/TypeaheadView.js +19 -13
- package/dist/components/Typeahead/TypeaheadView.js.map +1 -1
- package/dist/components/Typeahead/useTypeaheadDomSync.d.ts +1 -1
- package/dist/components/Typeahead/useTypeaheadDomSync.js +14 -8
- package/dist/components/Typeahead/useTypeaheadDomSync.js.map +1 -1
- package/dist/components/Typeahead/useTypeaheadUncontrolledInput.d.ts +2 -1
- package/dist/components/Typeahead/useTypeaheadUncontrolledInput.js +10 -9
- package/dist/components/Typeahead/useTypeaheadUncontrolledInput.js.map +1 -1
- package/dist/components/UnsavedChangesDialog.js +2 -2
- package/dist/components/index.d.ts +3 -1
- package/dist/components/index.js +7 -5
- package/dist/components.css +22 -18
- package/dist/hooks/useDebouncedCommit.d.ts +14 -0
- package/dist/hooks/useDebouncedCommit.js +35 -0
- package/dist/hooks/useDebouncedCommit.js.map +1 -0
- package/dist/icons/LongTailPhosphorIcons.js +1 -1
- package/dist/icons/PaddedPhosphorIcons.d.ts +1 -0
- package/dist/icons/PaddedPhosphorIcons.js +4 -2
- package/dist/icons/PaddedPhosphorIcons.js.map +1 -1
- package/dist/icons/WeightedPhosphorIcons.d.ts +1 -0
- package/dist/icons/WeightedPhosphorIcons.js +3 -1
- package/dist/icons/WeightedPhosphorIcons.js.map +1 -1
- package/dist/icons/index.js +3 -3
- package/dist/index.js +7 -5
- package/dist/utilities.css +1 -1
- package/dist/utils/chartColors.d.ts +2 -1
- package/dist/utils/chartColors.js +2 -1
- package/dist/utils/chartColors.js.map +1 -1
- package/dist/utils/componentSizes.d.ts +0 -1
- package/dist/utils/componentSizes.js.map +1 -1
- package/dist/utils/lazy-codemirror-loader.d.ts +0 -2
- package/dist/utils/lazy-codemirror-loader.js +1 -3
- package/dist/utils/lazy-codemirror-loader.js.map +1 -1
- package/docs/DESIGN.md +17 -0
- package/docs/STYLES.md +4 -1
- package/package.json +21 -8
- package/dist/_chunks/LoadingIndicator-DgtU9aqD.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SplitViewPane.js","names":[],"sources":["../../../src/components/SplitViewPane/SplitViewPane.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport {\n Fragment,\n createContext,\n useCallback,\n useContext,\n useEffect,\n useId,\n useLayoutEffect,\n useRef,\n useState,\n} from 'react';\n\nimport { useHotkeys } from 'react-hotkeys-hook';\n\nimport { Transition, TransitionChild } from '@headlessui/react';\nimport * as Portal from '@radix-ui/react-portal';\n\nimport { cn } from '../../utils/cn';\nimport { useZIndex } from '../../utils/useZIndex';\nimport { ZIndexProvider } from '../../utils/ZIndexContext';\nimport zIndices from '../../utils/zIndices';\nimport { createPortalSlot } from '../PortalSlot';\nimport UnsavedChangesDialog from '../UnsavedChangesDialog';\nimport { registerOpenPane } from './openPaneRegistry';\nimport {\n HeaderTitleActionSlot,\n SplitViewPaneHeader,\n} from './SplitViewPaneHeader';\n\nexport { HeaderTitleActionSlot } from './SplitViewPaneHeader';\n\nconst PaneDepth = createContext<number>(0);\n\nconst useIsomorphicLayoutEffect =\n typeof window === 'undefined' ? useEffect : useLayoutEffect;\n\nconst MINIMUM_LEFT_MARGIN_PX = 100;\nconst MINIMUM_PANE_WIDTH_PX = 500;\nconst KEYBOARD_RESIZE_STEP_PX = 10;\n\nconst MAX_INITIAL_PANE_WIDTH_PX = 1280;\nconst INITIAL_PANE_WIDTH_RATIO = 2 / 3;\n\nconst RESIZE_HANDLE_OUTSET_PX = 8;\nconst RESIZE_HANDLE_WIDTH_PX = 20;\n\nconst PANE_TRANSITION_CLASSES = {\n enter: 'duration-normal motion-reduce:duration-0',\n enterFrom: 'opacity-0',\n enterTo: 'animate-slide-left opacity-100 motion-reduce:animate-none',\n leave: 'duration-normal motion-reduce:duration-0',\n leaveFrom: 'opacity-100',\n leaveTo: 'animate-slide-right opacity-0 motion-reduce:animate-none',\n};\nconst getClientWidth = () => {\n if (typeof document === 'undefined' || typeof window === 'undefined') {\n return 0;\n }\n\n return Math.max(\n document.documentElement.clientWidth || 0,\n window.innerWidth || 0\n );\n};\n\nfunction getAvailableWidth(element?: HTMLDivElement | null) {\n if (typeof document === 'undefined') return 0;\n const style = getComputedStyle(element ?? document.documentElement);\n const reservedRight = Number.parseFloat(\n element ? style.right : style.getPropertyValue('--polly-chat-width')\n );\n return Math.max(0, getClientWidth() - (reservedRight || 0));\n}\n\nfunction clampLeft(\n value: number,\n width: number,\n minPaneWidthPx: number = MINIMUM_PANE_WIDTH_PX\n) {\n const maximumLeft = Math.max(0, width - minPaneWidthPx);\n return Math.min(maximumLeft, Math.max(MINIMUM_LEFT_MARGIN_PX, value));\n}\n\nfunction defaultLeft(\n props: {\n offsetPx: number;\n sidePaneId?: string;\n defaultWidthPx?: number;\n minWidthPx?: number;\n },\n width: number\n) {\n if (props.sidePaneId != null && typeof window !== 'undefined') {\n const value = window.localStorage.getItem(\n `SidePanelGroup:sizes:${props.sidePaneId}`\n );\n const pastRatio = Number.parseFloat(value ?? '');\n\n if (!Number.isNaN(pastRatio)) {\n const desiredWidth = width * pastRatio - props.offsetPx;\n return clampLeft(width - desiredWidth, width, props.minWidthPx);\n }\n }\n\n const desiredWidth =\n Math.min(\n props.defaultWidthPx ?? MAX_INITIAL_PANE_WIDTH_PX,\n width * INITIAL_PANE_WIDTH_RATIO\n ) - props.offsetPx;\n\n return clampLeft(width - desiredWidth, width, props.minWidthPx);\n}\n\nfunction useShowChildren({\n open,\n children,\n}: {\n open: boolean;\n children: ReactNode;\n}) {\n const [showChildren, setShowChildren] = useState<ReactNode>(children);\n useIsomorphicLayoutEffect(() => {\n if (open) setShowChildren(children);\n }, [open, children]);\n return showChildren;\n}\n\nfunction useResize(props: {\n offsetPx: number;\n sidePaneId?: string;\n defaultWidthPx?: number;\n overrideWidthPx?: number;\n minWidthPx?: number;\n open: boolean;\n}) {\n const target = useRef<HTMLDivElement>(null);\n const container = useRef<HTMLDivElement>(null);\n const [element, setElement] = useState<HTMLDivElement | null>(null);\n const containerRef = useCallback((node: HTMLDivElement | null) => {\n container.current = node;\n setElement(node);\n }, []);\n\n const resizing = useRef(false);\n const initialProps = useRef(props);\n const [width, setWidth] = useState(0);\n const [left, setLeft] = useState(MINIMUM_LEFT_MARGIN_PX);\n\n useIsomorphicLayoutEffect(() => {\n const width = getAvailableWidth();\n setWidth(width);\n setLeft(defaultLeft(initialProps.current, width));\n }, []);\n\n useEffect(() => {\n if (props.overrideWidthPx) {\n const desiredWidth = props.overrideWidthPx - props.offsetPx;\n\n setLeft(clampLeft(width - desiredWidth, width, props.minWidthPx));\n }\n }, [props.overrideWidthPx, props.offsetPx, props.minWidthPx, width]);\n\n const onMouseDown = useCallback((e: React.MouseEvent<HTMLDivElement>) => {\n e.preventDefault();\n resizing.current = true;\n if (target.current) {\n target.current.dataset.resizeHandleActive = 'true';\n }\n }, []);\n\n const onMouseUp = useCallback(() => {\n if (resizing.current) {\n resizing.current = false;\n if (target.current) {\n delete target.current.dataset.resizeHandleActive;\n }\n }\n }, []);\n\n const doResizeBounded = useCallback(\n (desiredX: number) => {\n if (container.current) {\n const width = getAvailableWidth(container.current);\n const bounded = clampLeft(desiredX, width, props.minWidthPx);\n\n if (props.sidePaneId != null && width > 0) {\n const ratio = (width - bounded) / width;\n window.localStorage.setItem(\n `SidePanelGroup:sizes:${props.sidePaneId}`,\n ratio.toString()\n );\n }\n\n container.current.style.left = `${bounded}px`;\n if (target.current) {\n target.current.style.left = `${bounded - RESIZE_HANDLE_OUTSET_PX}px`;\n target.current.setAttribute('aria-valuenow', bounded.toString());\n }\n setLeft(bounded);\n }\n },\n [props.sidePaneId, props.minWidthPx]\n );\n\n const onMouseMove = useCallback(\n (e: MouseEvent) => {\n if (resizing.current && container.current) {\n e.preventDefault();\n doResizeBounded(e.clientX);\n }\n },\n [doResizeBounded]\n );\n\n const onResize = useCallback(() => {\n setWidth(getAvailableWidth(container.current));\n if (container.current) {\n // Inline position excludes the slide transition's visual transform.\n doResizeBounded(Number.parseFloat(container.current.style.left));\n }\n }, [doResizeBounded]);\n\n const onKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {\n const desiredLeft =\n e.key === 'ArrowLeft'\n ? left - KEYBOARD_RESIZE_STEP_PX\n : e.key === 'ArrowRight'\n ? left + KEYBOARD_RESIZE_STEP_PX\n : e.key === 'Home'\n ? 0\n : e.key === 'End'\n ? width\n : undefined;\n if (desiredLeft === undefined) return;\n\n e.preventDefault();\n doResizeBounded(desiredLeft);\n };\n\n useIsomorphicLayoutEffect(() => {\n if (!element) return;\n onResize();\n const observer = new ResizeObserver(onResize);\n observer.observe(element);\n return () => observer.disconnect();\n }, [element, onResize]);\n\n useEffect(() => {\n window.addEventListener('resize', onResize);\n document.addEventListener('mouseup', onMouseUp);\n document.addEventListener('mousemove', onMouseMove);\n return () => {\n window.removeEventListener('resize', onResize);\n document.removeEventListener('mouseup', onMouseUp);\n document.removeEventListener('mousemove', onMouseMove);\n };\n }, [onMouseMove, onMouseUp, onResize]);\n\n useEffect(() => {\n // A closed pane has no element to resize, so restore valid bounds on reopen.\n if (props.open && left !== clampLeft(left, width, props.minWidthPx)) {\n setLeft(defaultLeft(props, width));\n }\n }, [props, left, width]);\n\n return {\n left,\n width,\n onMouseDown,\n onKeyDown,\n target,\n container,\n containerRef,\n };\n}\n\nexport const CustomHeaderSlot = createPortalSlot();\n\nexport type SplitViewPaneProps = {\n open: boolean;\n children: ReactNode;\n className?: string;\n sidePaneId?: string;\n hideArrows?: boolean;\n customHeader?: ReactNode;\n defaultWidthPx?: number;\n overrideWidthPx?: number;\n minWidthPx?: number;\n useCustomHeaderSlot?: boolean;\n requireConfirmationOnClose?: boolean;\n dismissOnOutsideClick?: boolean;\n scrollContainer?: 'outer' | 'inner';\n headerClassName?: string;\n} & (\n | {\n title?: undefined;\n onClose?: undefined;\n onExpand?: undefined;\n onNext?: undefined;\n onPrevious?: undefined;\n }\n | {\n title?: ReactNode;\n onClose: () => void;\n onExpand?: (e: React.MouseEvent<HTMLButtonElement>) => void;\n onNext?: () => void;\n onPrevious?: () => void;\n }\n);\n\nexport function SplitViewPane({\n open,\n onClose,\n onExpand,\n onNext,\n onPrevious,\n children,\n title,\n className,\n sidePaneId,\n hideArrows,\n customHeader,\n useCustomHeaderSlot,\n defaultWidthPx,\n overrideWidthPx,\n minWidthPx,\n requireConfirmationOnClose = false,\n dismissOnOutsideClick = false,\n scrollContainer = 'outer',\n headerClassName,\n}: SplitViewPaneProps) {\n const depth = useContext(PaneDepth);\n const showChildren = useShowChildren({ children, open });\n const {\n left,\n width,\n onMouseDown,\n onKeyDown,\n target,\n container,\n containerRef,\n } = useResize({\n offsetPx: depth * 4.5 * 16,\n sidePaneId,\n defaultWidthPx,\n overrideWidthPx,\n minWidthPx,\n open,\n });\n const onCloseRef = useRef(onClose);\n onCloseRef.current = onClose;\n const [showConfirmModal, setShowConfirmModal] = useState(false);\n const paneZIndex = useZIndex(zIndices.pane);\n const paneId = useId();\n const titleId =\n title && !customHeader && !useCustomHeaderSlot\n ? `${paneId}-title`\n : undefined;\n const maximumLeft = Math.max(\n 0,\n width - (minWidthPx ?? MINIMUM_PANE_WIDTH_PX)\n );\n const minimumLeft = Math.min(MINIMUM_LEFT_MARGIN_PX, maximumLeft);\n\n const handleCloseClick = useCallback(\n (e?: Event) => {\n if (requireConfirmationOnClose) {\n e?.preventDefault();\n e?.stopPropagation();\n setShowConfirmModal(true);\n } else {\n onCloseRef.current?.();\n }\n },\n [requireConfirmationOnClose]\n );\n\n const handleCloseClickRef = useRef(handleCloseClick);\n handleCloseClickRef.current = handleCloseClick;\n\n useEffect(() => {\n if (!open) return;\n return registerOpenPane(\n paneId,\n paneZIndex,\n container,\n (event) => handleCloseClickRef.current(event),\n dismissOnOutsideClick\n ? (event) => handleCloseClickRef.current(event)\n : undefined\n );\n }, [container, dismissOnOutsideClick, open, paneId, paneZIndex]);\n\n const nextRef = useRef(onNext);\n nextRef.current = onNext;\n\n const prevRef = useRef(onPrevious);\n prevRef.current = onPrevious;\n\n useHotkeys(\n 'j',\n () => {\n nextRef.current?.();\n },\n { enabled: open && onNext != null }\n );\n\n useHotkeys(\n 'k',\n () => {\n prevRef.current?.();\n },\n { enabled: open && onPrevious != null }\n );\n\n const renderHeader = () => {\n if (customHeader) {\n return customHeader;\n }\n if (useCustomHeaderSlot) {\n return <CustomHeaderSlot.Slot />;\n }\n\n return (\n <SplitViewPaneHeader\n title={title}\n open={open}\n onClose={handleCloseClick}\n onExpand={onExpand}\n onNext={onNext}\n onPrevious={onPrevious}\n hideArrows={hideArrows}\n headerClassName={headerClassName}\n titleId={titleId}\n />\n );\n };\n\n return (\n <CustomHeaderSlot.Context>\n <HeaderTitleActionSlot.Context>\n <Portal.Root>\n <Transition show={open} as={Fragment}>\n <div className=\"relative\" style={{ zIndex: paneZIndex }}>\n <TransitionChild as={Fragment} {...PANE_TRANSITION_CLASSES}>\n <div\n ref={containerRef}\n style={{ left, right: `var(--polly-chat-width, 0px)` }}\n className={cn(\n 'fixed inset-0 shadow-lg',\n !open && 'pointer-events-none',\n scrollContainer === 'outer'\n ? 'overflow-y-auto overscroll-y-none'\n : 'overflow-hidden'\n )}\n data-testid=\"split-view-pane\"\n role={titleId ? 'region' : undefined}\n aria-labelledby={titleId}\n >\n <div\n className={cn(\n 'relative flex flex-col items-stretch bg-elevated',\n scrollContainer === 'outer' ? 'min-h-full' : 'h-full'\n )}\n >\n <ZIndexProvider value={paneZIndex}>\n {renderHeader()}\n <PaneDepth.Provider value={depth + 1}>\n <div\n className={cn(\n 'flex flex-1 flex-col',\n scrollContainer === 'inner' &&\n 'min-h-0 overflow-y-auto overscroll-y-none',\n className\n )}\n >\n {showChildren}\n </div>\n </PaneDepth.Provider>\n </ZIndexProvider>\n </div>\n </div>\n </TransitionChild>\n <TransitionChild as={Fragment} {...PANE_TRANSITION_CLASSES}>\n <div\n ref={target}\n role=\"separator\"\n aria-label=\"Resize pane\"\n aria-orientation=\"vertical\"\n aria-valuemin={minimumLeft}\n aria-valuemax={maximumLeft}\n aria-valuenow={left}\n tabIndex={0}\n data-split-view-pane-resize-handle\n data-testid=\"split-view-pane-resize-handle\"\n className={cn(\n 'group fixed inset-y-0 cursor-col-resize focus-visible:outline-none',\n !open && 'pointer-events-none'\n )}\n style={{\n left: left - RESIZE_HANDLE_OUTSET_PX,\n width: RESIZE_HANDLE_WIDTH_PX,\n zIndex: zIndices.resizeHandle,\n }}\n onMouseDown={onMouseDown}\n onKeyDown={onKeyDown}\n >\n <div\n className=\"absolute inset-y-0 w-0 border-l border-subtle transition-all duration-normal group-hover:border-l-2 group-focus-visible:border-l-2 group-focus-visible:border-l-brand group-data-[resize-handle-active]:border-l-brand motion-reduce:transition-none\"\n style={{ left: RESIZE_HANDLE_OUTSET_PX }}\n />\n </div>\n </TransitionChild>\n </div>\n </Transition>\n </Portal.Root>\n <UnsavedChangesDialog\n isOpen={showConfirmModal}\n onClose={() => setShowConfirmModal(false)}\n onDiscard={() => {\n setShowConfirmModal(false);\n onCloseRef.current?.();\n }}\n title=\"Unsaved Changes\"\n description=\"You may have unsaved changes. Are you sure you want to exit?\"\n discardCopy=\"Exit\"\n />\n </HeaderTitleActionSlot.Context>\n </CustomHeaderSlot.Context>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SplitViewPane.js","names":[],"sources":["../../../src/components/SplitViewPane/SplitViewPane.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport {\n Fragment,\n createContext,\n useCallback,\n useContext,\n useEffect,\n useId,\n useRef,\n useState,\n} from 'react';\n\nimport { useHotkeys } from 'react-hotkeys-hook';\n\nimport { Transition, TransitionChild } from '@headlessui/react';\nimport * as Portal from '@radix-ui/react-portal';\n\nimport { cn } from '../../utils/cn';\nimport { useZIndex } from '../../utils/useZIndex';\nimport { ZIndexProvider } from '../../utils/ZIndexContext';\nimport zIndices from '../../utils/zIndices';\nimport { createPortalSlot } from '../PortalSlot';\nimport UnsavedChangesDialog from '../UnsavedChangesDialog';\nimport { registerOpenPane } from './openPaneRegistry';\nimport {\n HeaderTitleActionSlot,\n SplitViewPaneHeader,\n} from './SplitViewPaneHeader';\nimport { useShowChildren } from './useShowChildren';\nimport {\n MINIMUM_LEFT_MARGIN_PX,\n MINIMUM_PANE_WIDTH_PX,\n RESIZE_HANDLE_OUTSET_PX,\n RESIZE_HANDLE_WIDTH_PX,\n useSplitViewPaneResize,\n} from './useSplitViewPaneResize';\n\nexport { HeaderTitleActionSlot } from './SplitViewPaneHeader';\n\nconst PaneDepth = createContext<number>(0);\n\nconst PANE_TRANSITION_CLASSES = {\n enter: 'duration-normal motion-reduce:duration-0',\n enterFrom: 'opacity-0',\n enterTo: 'animate-slide-left opacity-100 motion-reduce:animate-none',\n leave: 'duration-normal motion-reduce:duration-0',\n leaveFrom: 'opacity-100',\n leaveTo: 'animate-slide-right opacity-0 motion-reduce:animate-none',\n};\n\nexport const CustomHeaderSlot = createPortalSlot();\n\nfunction PaneShell({\n children,\n showConfirmModal,\n onDismissConfirm,\n onDiscard,\n}: {\n children: ReactNode;\n showConfirmModal: boolean;\n onDismissConfirm: () => void;\n onDiscard: () => void;\n}) {\n return (\n <CustomHeaderSlot.Context>\n <HeaderTitleActionSlot.Context>\n {children}\n <UnsavedChangesDialog\n isOpen={showConfirmModal}\n onClose={onDismissConfirm}\n onDiscard={onDiscard}\n title=\"Unsaved Changes\"\n description=\"You may have unsaved changes. Are you sure you want to exit?\"\n discardCopy=\"Exit\"\n />\n </HeaderTitleActionSlot.Context>\n </CustomHeaderSlot.Context>\n );\n}\n\nfunction PaneBody({\n paneZIndex,\n depth,\n header,\n className,\n children,\n}: {\n paneZIndex: number;\n depth: number;\n header: ReactNode;\n className?: string;\n children: ReactNode;\n}) {\n return (\n <ZIndexProvider value={paneZIndex}>\n {header}\n <PaneDepth.Provider value={depth + 1}>\n <div className={cn('flex flex-1 flex-col', className)}>{children}</div>\n </PaneDepth.Provider>\n </ZIndexProvider>\n );\n}\n\nexport type SplitViewPaneProps = {\n open: boolean;\n children: ReactNode;\n className?: string;\n variant?: 'overlay' | 'inline';\n sidePaneId?: string;\n hideArrows?: boolean;\n customHeader?: ReactNode;\n defaultWidthPx?: number;\n overrideWidthPx?: number;\n minWidthPx?: number;\n useCustomHeaderSlot?: boolean;\n requireConfirmationOnClose?: boolean;\n dismissOnOutsideClick?: boolean;\n scrollContainer?: 'outer' | 'inner';\n headerClassName?: string;\n} & (\n | {\n title?: undefined;\n onClose?: undefined;\n onExpand?: undefined;\n onNext?: undefined;\n onPrevious?: undefined;\n }\n | {\n title?: ReactNode;\n onClose: () => void;\n onExpand?: (e: React.MouseEvent<HTMLButtonElement>) => void;\n onNext?: () => void;\n onPrevious?: () => void;\n }\n);\n\nexport function SplitViewPane({\n open,\n onClose,\n onExpand,\n onNext,\n onPrevious,\n children,\n title,\n className,\n variant = 'overlay',\n sidePaneId,\n hideArrows,\n customHeader,\n useCustomHeaderSlot,\n defaultWidthPx,\n overrideWidthPx,\n minWidthPx,\n requireConfirmationOnClose = false,\n dismissOnOutsideClick = false,\n scrollContainer = 'outer',\n headerClassName,\n}: SplitViewPaneProps) {\n const depth = useContext(PaneDepth);\n const showChildren = useShowChildren({ children, open });\n const inline = variant === 'inline';\n const {\n left,\n width,\n onMouseDown,\n onKeyDown,\n target,\n container,\n containerRef,\n } = useSplitViewPaneResize({\n offsetPx: depth * 4.5 * 16,\n sidePaneId,\n defaultWidthPx,\n overrideWidthPx,\n minWidthPx,\n open,\n });\n const onCloseRef = useRef(onClose);\n onCloseRef.current = onClose;\n const [showConfirmModal, setShowConfirmModal] = useState(false);\n const paneZIndex = useZIndex(zIndices.pane);\n const paneId = useId();\n const titleId =\n title && !customHeader && !useCustomHeaderSlot\n ? `${paneId}-title`\n : undefined;\n const maximumLeft = Math.max(\n 0,\n width - (minWidthPx ?? MINIMUM_PANE_WIDTH_PX)\n );\n const minimumLeft = Math.min(MINIMUM_LEFT_MARGIN_PX, maximumLeft);\n\n const handleCloseClick = useCallback(\n (e?: Event) => {\n if (requireConfirmationOnClose) {\n e?.preventDefault();\n e?.stopPropagation();\n setShowConfirmModal(true);\n } else {\n onCloseRef.current?.();\n }\n },\n [requireConfirmationOnClose]\n );\n\n const handleCloseClickRef = useRef(handleCloseClick);\n handleCloseClickRef.current = handleCloseClick;\n\n useEffect(() => {\n // Only overlay panes join the registry. An inline pane can outlive every\n // overlay on the page, and as the topmost entry it would swallow Escape\n // and block outside-click dismissal for the overlay panes above it.\n if (!open || inline) return;\n return registerOpenPane(\n paneId,\n paneZIndex,\n container,\n (event) => handleCloseClickRef.current(event),\n dismissOnOutsideClick\n ? (event) => handleCloseClickRef.current(event)\n : undefined\n );\n }, [container, dismissOnOutsideClick, inline, open, paneId, paneZIndex]);\n\n const nextRef = useRef(onNext);\n nextRef.current = onNext;\n\n const prevRef = useRef(onPrevious);\n prevRef.current = onPrevious;\n\n useHotkeys(\n 'j',\n () => {\n nextRef.current?.();\n },\n { enabled: open && onNext != null }\n );\n\n useHotkeys(\n 'k',\n () => {\n prevRef.current?.();\n },\n { enabled: open && onPrevious != null }\n );\n\n const renderHeader = () => {\n if (customHeader) {\n return customHeader;\n }\n if (useCustomHeaderSlot) {\n return <CustomHeaderSlot.Slot />;\n }\n // SplitViewPaneHeader always draws a close button and the arrows, so a pane\n // with no header content of its own would get a stray control bar.\n if (title == null && onClose == null) {\n return null;\n }\n\n return (\n <SplitViewPaneHeader\n title={title}\n open={open}\n onClose={handleCloseClick}\n onExpand={onExpand}\n onNext={onNext}\n onPrevious={onPrevious}\n hideArrows={hideArrows}\n headerClassName={headerClassName}\n titleId={titleId}\n />\n );\n };\n\n const dismissConfirm = () => setShowConfirmModal(false);\n const discardAndClose = () => {\n setShowConfirmModal(false);\n onCloseRef.current?.();\n };\n\n return (\n <PaneShell\n showConfirmModal={showConfirmModal}\n onDismissConfirm={dismissConfirm}\n onDiscard={discardAndClose}\n >\n {inline ? (\n // An inline pane keeps its children mounted so collapsing can animate;\n // an overlay pane unmounts them on close via useShowChildren.\n <div\n inert={!open}\n className={cn(\n 'grid shrink-0 transition-[grid-template-columns] duration-normal ease-out motion-reduce:transition-none',\n open ? 'grid-cols-[1fr]' : 'grid-cols-[0fr]'\n )}\n data-testid=\"split-view-pane\"\n role={titleId ? 'region' : undefined}\n aria-labelledby={titleId}\n >\n <div className=\"flex min-w-0 flex-col overflow-y-auto overflow-x-hidden\">\n <PaneBody\n paneZIndex={paneZIndex}\n depth={depth}\n header={renderHeader()}\n className={className}\n >\n {children}\n </PaneBody>\n </div>\n </div>\n ) : (\n <Portal.Root>\n <Transition show={open} as={Fragment}>\n <div className=\"relative\" style={{ zIndex: paneZIndex }}>\n <TransitionChild as={Fragment} {...PANE_TRANSITION_CLASSES}>\n <div\n ref={containerRef}\n style={{ left, right: `var(--polly-chat-width, 0px)` }}\n className={cn(\n 'fixed inset-0 shadow-lg',\n !open && 'pointer-events-none',\n scrollContainer === 'outer'\n ? 'overflow-y-auto overscroll-y-none'\n : 'overflow-hidden'\n )}\n data-testid=\"split-view-pane\"\n role={titleId ? 'region' : undefined}\n aria-labelledby={titleId}\n >\n <div\n className={cn(\n 'relative flex flex-col items-stretch bg-elevated',\n scrollContainer === 'outer' ? 'min-h-full' : 'h-full'\n )}\n >\n <PaneBody\n paneZIndex={paneZIndex}\n depth={depth}\n header={renderHeader()}\n className={cn(\n scrollContainer === 'inner' &&\n 'min-h-0 overflow-y-auto overscroll-y-none',\n className\n )}\n >\n {showChildren}\n </PaneBody>\n </div>\n </div>\n </TransitionChild>\n <TransitionChild as={Fragment} {...PANE_TRANSITION_CLASSES}>\n <div\n ref={target}\n role=\"separator\"\n aria-label=\"Resize pane\"\n aria-orientation=\"vertical\"\n aria-valuemin={minimumLeft}\n aria-valuemax={maximumLeft}\n aria-valuenow={left}\n tabIndex={0}\n data-split-view-pane-resize-handle\n data-testid=\"split-view-pane-resize-handle\"\n className={cn(\n 'group fixed inset-y-0 cursor-col-resize focus-visible:outline-none',\n !open && 'pointer-events-none'\n )}\n style={{\n left: left - RESIZE_HANDLE_OUTSET_PX,\n width: RESIZE_HANDLE_WIDTH_PX,\n zIndex: zIndices.resizeHandle,\n }}\n onMouseDown={onMouseDown}\n onKeyDown={onKeyDown}\n >\n <div\n className=\"absolute inset-y-0 w-0 border-l border-subtle transition-all duration-normal group-hover:border-l-2 group-focus-visible:border-l-2 group-focus-visible:border-l-brand group-data-[resize-handle-active]:border-l-brand motion-reduce:transition-none\"\n style={{ left: RESIZE_HANDLE_OUTSET_PX }}\n />\n </div>\n </TransitionChild>\n </div>\n </Transition>\n </Portal.Root>\n )}\n </PaneShell>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { cn } from "../../utils/cn.js";
|
|
2
|
-
import zIndices from "../../utils/zIndices.js";
|
|
3
2
|
import { Text } from "../Text/Text.js";
|
|
4
|
-
import
|
|
3
|
+
import zIndices from "../../utils/zIndices.js";
|
|
5
4
|
import { IconButton } from "../IconButton/IconButton.js";
|
|
5
|
+
import { CaretDoubleRightIcon, CaretDownIcon, CaretUpIcon } from "../../icons/PaddedPhosphorIcons.js";
|
|
6
6
|
import { Kbd } from "../Kbd/Kbd.js";
|
|
7
7
|
import { createPortalSlot } from "../PortalSlot.js";
|
|
8
8
|
import { useLayoutEffect, useState } from "react";
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { useEffect, useLayoutEffect, useState } from "react";
|
|
2
|
+
//#region packages/components/src/components/SplitViewPane/useShowChildren.ts
|
|
3
|
+
var useIsomorphicLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
|
|
4
|
+
function useShowChildren({ open, children }) {
|
|
5
|
+
const [showChildren, setShowChildren] = useState(children);
|
|
6
|
+
useIsomorphicLayoutEffect(() => {
|
|
7
|
+
if (open) setShowChildren(children);
|
|
8
|
+
}, [open, children]);
|
|
9
|
+
return showChildren;
|
|
10
|
+
}
|
|
11
|
+
//#endregion
|
|
12
|
+
export { useShowChildren };
|
|
13
|
+
|
|
14
|
+
//# sourceMappingURL=useShowChildren.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useShowChildren.js","names":[],"sources":["../../../src/components/SplitViewPane/useShowChildren.ts"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { useEffect, useLayoutEffect, useState } from 'react';\n\nconst useIsomorphicLayoutEffect =\n typeof window === 'undefined' ? useEffect : useLayoutEffect;\n\nexport function useShowChildren({\n open,\n children,\n}: {\n open: boolean;\n children: ReactNode;\n}) {\n const [showChildren, setShowChildren] = useState<ReactNode>(children);\n useIsomorphicLayoutEffect(() => {\n if (open) setShowChildren(children);\n }, [open, children]);\n return showChildren;\n}\n"],"mappings":";;AAGA,IAAM,4BACJ,OAAO,WAAW,cAAc,YAAY;AAE9C,SAAgB,gBAAgB,EAC9B,MACA,YAIC;CACD,MAAM,CAAC,cAAc,mBAAmB,SAAoB,QAAQ;CACpE,gCAAgC;EAC9B,IAAI,MAAM,gBAAgB,QAAQ;CACpC,GAAG,CAAC,MAAM,QAAQ,CAAC;CACnB,OAAO;AACT"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export declare const MINIMUM_LEFT_MARGIN_PX = 100;
|
|
2
|
+
export declare const MINIMUM_PANE_WIDTH_PX = 500;
|
|
3
|
+
export declare const RESIZE_HANDLE_OUTSET_PX = 8;
|
|
4
|
+
export declare const RESIZE_HANDLE_WIDTH_PX = 20;
|
|
5
|
+
export declare function clampLeft(value: number, width: number, minPaneWidthPx?: number): number;
|
|
6
|
+
export declare function useSplitViewPaneResize(props: {
|
|
7
|
+
offsetPx: number;
|
|
8
|
+
sidePaneId?: string;
|
|
9
|
+
defaultWidthPx?: number;
|
|
10
|
+
overrideWidthPx?: number;
|
|
11
|
+
minWidthPx?: number;
|
|
12
|
+
open: boolean;
|
|
13
|
+
}): {
|
|
14
|
+
left: number;
|
|
15
|
+
width: number;
|
|
16
|
+
onMouseDown: (e: React.MouseEvent<HTMLDivElement>) => void;
|
|
17
|
+
onKeyDown: (e: React.KeyboardEvent<HTMLDivElement>) => void;
|
|
18
|
+
target: import("react").RefObject<HTMLDivElement | null>;
|
|
19
|
+
container: import("react").RefObject<HTMLDivElement | null>;
|
|
20
|
+
containerRef: (node: HTMLDivElement | null) => void;
|
|
21
|
+
};
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
2
|
+
//#region packages/components/src/components/SplitViewPane/useSplitViewPaneResize.ts
|
|
3
|
+
var useIsomorphicLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
|
|
4
|
+
var MINIMUM_LEFT_MARGIN_PX = 100;
|
|
5
|
+
var MINIMUM_PANE_WIDTH_PX = 500;
|
|
6
|
+
var KEYBOARD_RESIZE_STEP_PX = 10;
|
|
7
|
+
var MAX_INITIAL_PANE_WIDTH_PX = 1280;
|
|
8
|
+
var INITIAL_PANE_WIDTH_RATIO = 2 / 3;
|
|
9
|
+
var RESIZE_HANDLE_OUTSET_PX = 8;
|
|
10
|
+
var RESIZE_HANDLE_WIDTH_PX = 20;
|
|
11
|
+
var getClientWidth = () => {
|
|
12
|
+
if (typeof document === "undefined" || typeof window === "undefined") return 0;
|
|
13
|
+
return Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0);
|
|
14
|
+
};
|
|
15
|
+
function getAvailableWidth(element) {
|
|
16
|
+
if (typeof document === "undefined") return 0;
|
|
17
|
+
const style = getComputedStyle(element ?? document.documentElement);
|
|
18
|
+
const reservedRight = Number.parseFloat(element ? style.right : style.getPropertyValue("--polly-chat-width"));
|
|
19
|
+
return Math.max(0, getClientWidth() - (reservedRight || 0));
|
|
20
|
+
}
|
|
21
|
+
function clampLeft(value, width, minPaneWidthPx = 500) {
|
|
22
|
+
const maximumLeft = Math.max(0, width - minPaneWidthPx);
|
|
23
|
+
return Math.min(maximumLeft, Math.max(100, value));
|
|
24
|
+
}
|
|
25
|
+
function defaultLeft(props, width) {
|
|
26
|
+
if (props.sidePaneId != null && typeof window !== "undefined") {
|
|
27
|
+
const value = window.localStorage.getItem(`SidePanelGroup:sizes:${props.sidePaneId}`);
|
|
28
|
+
const pastRatio = Number.parseFloat(value ?? "");
|
|
29
|
+
if (!Number.isNaN(pastRatio)) return clampLeft(width - (width * pastRatio - props.offsetPx), width, props.minWidthPx);
|
|
30
|
+
}
|
|
31
|
+
return clampLeft(width - (Math.min(props.defaultWidthPx ?? MAX_INITIAL_PANE_WIDTH_PX, width * INITIAL_PANE_WIDTH_RATIO) - props.offsetPx), width, props.minWidthPx);
|
|
32
|
+
}
|
|
33
|
+
function useSplitViewPaneResize(props) {
|
|
34
|
+
const target = useRef(null);
|
|
35
|
+
const container = useRef(null);
|
|
36
|
+
const [element, setElement] = useState(null);
|
|
37
|
+
const containerRef = useCallback((node) => {
|
|
38
|
+
container.current = node;
|
|
39
|
+
setElement(node);
|
|
40
|
+
}, []);
|
|
41
|
+
const resizing = useRef(false);
|
|
42
|
+
const initialProps = useRef(props);
|
|
43
|
+
const [width, setWidth] = useState(0);
|
|
44
|
+
const [left, setLeft] = useState(100);
|
|
45
|
+
useIsomorphicLayoutEffect(() => {
|
|
46
|
+
const width = getAvailableWidth();
|
|
47
|
+
setWidth(width);
|
|
48
|
+
setLeft(defaultLeft(initialProps.current, width));
|
|
49
|
+
}, []);
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
if (props.overrideWidthPx) {
|
|
52
|
+
const desiredWidth = props.overrideWidthPx - props.offsetPx;
|
|
53
|
+
setLeft(clampLeft(width - desiredWidth, width, props.minWidthPx));
|
|
54
|
+
}
|
|
55
|
+
}, [
|
|
56
|
+
props.overrideWidthPx,
|
|
57
|
+
props.offsetPx,
|
|
58
|
+
props.minWidthPx,
|
|
59
|
+
width
|
|
60
|
+
]);
|
|
61
|
+
const onMouseDown = useCallback((e) => {
|
|
62
|
+
e.preventDefault();
|
|
63
|
+
resizing.current = true;
|
|
64
|
+
if (target.current) target.current.dataset.resizeHandleActive = "true";
|
|
65
|
+
}, []);
|
|
66
|
+
const onMouseUp = useCallback(() => {
|
|
67
|
+
if (resizing.current) {
|
|
68
|
+
resizing.current = false;
|
|
69
|
+
if (target.current) delete target.current.dataset.resizeHandleActive;
|
|
70
|
+
}
|
|
71
|
+
}, []);
|
|
72
|
+
const doResizeBounded = useCallback((desiredX) => {
|
|
73
|
+
if (container.current) {
|
|
74
|
+
const width = getAvailableWidth(container.current);
|
|
75
|
+
const bounded = clampLeft(desiredX, width, props.minWidthPx);
|
|
76
|
+
if (props.sidePaneId != null && width > 0) {
|
|
77
|
+
const ratio = (width - bounded) / width;
|
|
78
|
+
window.localStorage.setItem(`SidePanelGroup:sizes:${props.sidePaneId}`, ratio.toString());
|
|
79
|
+
}
|
|
80
|
+
container.current.style.left = `${bounded}px`;
|
|
81
|
+
if (target.current) {
|
|
82
|
+
target.current.style.left = `${bounded - 8}px`;
|
|
83
|
+
target.current.setAttribute("aria-valuenow", bounded.toString());
|
|
84
|
+
}
|
|
85
|
+
setLeft(bounded);
|
|
86
|
+
}
|
|
87
|
+
}, [props.sidePaneId, props.minWidthPx]);
|
|
88
|
+
const onMouseMove = useCallback((e) => {
|
|
89
|
+
if (resizing.current && container.current) {
|
|
90
|
+
e.preventDefault();
|
|
91
|
+
doResizeBounded(e.clientX);
|
|
92
|
+
}
|
|
93
|
+
}, [doResizeBounded]);
|
|
94
|
+
const onResize = useCallback(() => {
|
|
95
|
+
setWidth(getAvailableWidth(container.current));
|
|
96
|
+
if (container.current) doResizeBounded(Number.parseFloat(container.current.style.left));
|
|
97
|
+
}, [doResizeBounded]);
|
|
98
|
+
const onKeyDown = (e) => {
|
|
99
|
+
const desiredLeft = e.key === "ArrowLeft" ? left - KEYBOARD_RESIZE_STEP_PX : e.key === "ArrowRight" ? left + KEYBOARD_RESIZE_STEP_PX : e.key === "Home" ? 0 : e.key === "End" ? width : void 0;
|
|
100
|
+
if (desiredLeft === void 0) return;
|
|
101
|
+
e.preventDefault();
|
|
102
|
+
doResizeBounded(desiredLeft);
|
|
103
|
+
};
|
|
104
|
+
useIsomorphicLayoutEffect(() => {
|
|
105
|
+
if (!element) return;
|
|
106
|
+
onResize();
|
|
107
|
+
const observer = new ResizeObserver(onResize);
|
|
108
|
+
observer.observe(element);
|
|
109
|
+
return () => observer.disconnect();
|
|
110
|
+
}, [element, onResize]);
|
|
111
|
+
useEffect(() => {
|
|
112
|
+
window.addEventListener("resize", onResize);
|
|
113
|
+
document.addEventListener("mouseup", onMouseUp);
|
|
114
|
+
document.addEventListener("mousemove", onMouseMove);
|
|
115
|
+
return () => {
|
|
116
|
+
window.removeEventListener("resize", onResize);
|
|
117
|
+
document.removeEventListener("mouseup", onMouseUp);
|
|
118
|
+
document.removeEventListener("mousemove", onMouseMove);
|
|
119
|
+
};
|
|
120
|
+
}, [
|
|
121
|
+
onMouseMove,
|
|
122
|
+
onMouseUp,
|
|
123
|
+
onResize
|
|
124
|
+
]);
|
|
125
|
+
useEffect(() => {
|
|
126
|
+
if (props.open && left !== clampLeft(left, width, props.minWidthPx)) setLeft(defaultLeft(props, width));
|
|
127
|
+
}, [
|
|
128
|
+
props,
|
|
129
|
+
left,
|
|
130
|
+
width
|
|
131
|
+
]);
|
|
132
|
+
return {
|
|
133
|
+
left,
|
|
134
|
+
width,
|
|
135
|
+
onMouseDown,
|
|
136
|
+
onKeyDown,
|
|
137
|
+
target,
|
|
138
|
+
container,
|
|
139
|
+
containerRef
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
//#endregion
|
|
143
|
+
export { MINIMUM_LEFT_MARGIN_PX, MINIMUM_PANE_WIDTH_PX, RESIZE_HANDLE_OUTSET_PX, RESIZE_HANDLE_WIDTH_PX, clampLeft, useSplitViewPaneResize };
|
|
144
|
+
|
|
145
|
+
//# sourceMappingURL=useSplitViewPaneResize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useSplitViewPaneResize.js","names":[],"sources":["../../../src/components/SplitViewPane/useSplitViewPaneResize.ts"],"sourcesContent":["import {\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n} from 'react';\n\nconst useIsomorphicLayoutEffect =\n typeof window === 'undefined' ? useEffect : useLayoutEffect;\n\nexport const MINIMUM_LEFT_MARGIN_PX = 100;\nexport const MINIMUM_PANE_WIDTH_PX = 500;\nconst KEYBOARD_RESIZE_STEP_PX = 10;\nconst MAX_INITIAL_PANE_WIDTH_PX = 1280;\nconst INITIAL_PANE_WIDTH_RATIO = 2 / 3;\nexport const RESIZE_HANDLE_OUTSET_PX = 8;\nexport const RESIZE_HANDLE_WIDTH_PX = 20;\n\nconst getClientWidth = () => {\n if (typeof document === 'undefined' || typeof window === 'undefined')\n return 0;\n return Math.max(\n document.documentElement.clientWidth || 0,\n window.innerWidth || 0\n );\n};\n\nfunction getAvailableWidth(element?: HTMLDivElement | null) {\n if (typeof document === 'undefined') return 0;\n const style = getComputedStyle(element ?? document.documentElement);\n const reservedRight = Number.parseFloat(\n element ? style.right : style.getPropertyValue('--polly-chat-width')\n );\n return Math.max(0, getClientWidth() - (reservedRight || 0));\n}\n\nexport function clampLeft(\n value: number,\n width: number,\n minPaneWidthPx: number = MINIMUM_PANE_WIDTH_PX\n) {\n const maximumLeft = Math.max(0, width - minPaneWidthPx);\n return Math.min(maximumLeft, Math.max(MINIMUM_LEFT_MARGIN_PX, value));\n}\n\nfunction defaultLeft(\n props: {\n offsetPx: number;\n sidePaneId?: string;\n defaultWidthPx?: number;\n minWidthPx?: number;\n },\n width: number\n) {\n if (props.sidePaneId != null && typeof window !== 'undefined') {\n const value = window.localStorage.getItem(\n `SidePanelGroup:sizes:${props.sidePaneId}`\n );\n const pastRatio = Number.parseFloat(value ?? '');\n\n if (!Number.isNaN(pastRatio)) {\n const desiredWidth = width * pastRatio - props.offsetPx;\n return clampLeft(width - desiredWidth, width, props.minWidthPx);\n }\n }\n\n const desiredWidth =\n Math.min(\n props.defaultWidthPx ?? MAX_INITIAL_PANE_WIDTH_PX,\n width * INITIAL_PANE_WIDTH_RATIO\n ) - props.offsetPx;\n\n return clampLeft(width - desiredWidth, width, props.minWidthPx);\n}\n\nexport function useSplitViewPaneResize(props: {\n offsetPx: number;\n sidePaneId?: string;\n defaultWidthPx?: number;\n overrideWidthPx?: number;\n minWidthPx?: number;\n open: boolean;\n}) {\n const target = useRef<HTMLDivElement>(null);\n const container = useRef<HTMLDivElement>(null);\n const [element, setElement] = useState<HTMLDivElement | null>(null);\n const containerRef = useCallback((node: HTMLDivElement | null) => {\n container.current = node;\n setElement(node);\n }, []);\n\n const resizing = useRef(false);\n const initialProps = useRef(props);\n const [width, setWidth] = useState(0);\n const [left, setLeft] = useState(MINIMUM_LEFT_MARGIN_PX);\n\n useIsomorphicLayoutEffect(() => {\n const width = getAvailableWidth();\n setWidth(width);\n setLeft(defaultLeft(initialProps.current, width));\n }, []);\n\n useEffect(() => {\n if (props.overrideWidthPx) {\n const desiredWidth = props.overrideWidthPx - props.offsetPx;\n\n setLeft(clampLeft(width - desiredWidth, width, props.minWidthPx));\n }\n }, [props.overrideWidthPx, props.offsetPx, props.minWidthPx, width]);\n\n const onMouseDown = useCallback((e: React.MouseEvent<HTMLDivElement>) => {\n e.preventDefault();\n resizing.current = true;\n if (target.current) {\n target.current.dataset.resizeHandleActive = 'true';\n }\n }, []);\n\n const onMouseUp = useCallback(() => {\n if (resizing.current) {\n resizing.current = false;\n if (target.current) {\n delete target.current.dataset.resizeHandleActive;\n }\n }\n }, []);\n\n const doResizeBounded = useCallback(\n (desiredX: number) => {\n if (container.current) {\n const width = getAvailableWidth(container.current);\n const bounded = clampLeft(desiredX, width, props.minWidthPx);\n\n if (props.sidePaneId != null && width > 0) {\n const ratio = (width - bounded) / width;\n window.localStorage.setItem(\n `SidePanelGroup:sizes:${props.sidePaneId}`,\n ratio.toString()\n );\n }\n\n container.current.style.left = `${bounded}px`;\n if (target.current) {\n target.current.style.left = `${bounded - RESIZE_HANDLE_OUTSET_PX}px`;\n target.current.setAttribute('aria-valuenow', bounded.toString());\n }\n setLeft(bounded);\n }\n },\n [props.sidePaneId, props.minWidthPx]\n );\n\n const onMouseMove = useCallback(\n (e: MouseEvent) => {\n if (resizing.current && container.current) {\n e.preventDefault();\n doResizeBounded(e.clientX);\n }\n },\n [doResizeBounded]\n );\n\n const onResize = useCallback(() => {\n setWidth(getAvailableWidth(container.current));\n if (container.current) {\n // Inline position excludes the slide transition's visual transform.\n doResizeBounded(Number.parseFloat(container.current.style.left));\n }\n }, [doResizeBounded]);\n\n const onKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {\n const desiredLeft =\n e.key === 'ArrowLeft'\n ? left - KEYBOARD_RESIZE_STEP_PX\n : e.key === 'ArrowRight'\n ? left + KEYBOARD_RESIZE_STEP_PX\n : e.key === 'Home'\n ? 0\n : e.key === 'End'\n ? width\n : undefined;\n if (desiredLeft === undefined) return;\n\n e.preventDefault();\n doResizeBounded(desiredLeft);\n };\n\n useIsomorphicLayoutEffect(() => {\n if (!element) return;\n onResize();\n const observer = new ResizeObserver(onResize);\n observer.observe(element);\n return () => observer.disconnect();\n }, [element, onResize]);\n\n useEffect(() => {\n window.addEventListener('resize', onResize);\n document.addEventListener('mouseup', onMouseUp);\n document.addEventListener('mousemove', onMouseMove);\n return () => {\n window.removeEventListener('resize', onResize);\n document.removeEventListener('mouseup', onMouseUp);\n document.removeEventListener('mousemove', onMouseMove);\n };\n }, [onMouseMove, onMouseUp, onResize]);\n\n useEffect(() => {\n // A closed pane has no element to resize, so restore valid bounds on reopen.\n if (props.open && left !== clampLeft(left, width, props.minWidthPx)) {\n setLeft(defaultLeft(props, width));\n }\n }, [props, left, width]);\n\n return {\n left,\n width,\n onMouseDown,\n onKeyDown,\n target,\n container,\n containerRef,\n };\n}\n"],"mappings":"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"}
|
|
@@ -17,12 +17,13 @@ export interface TextareaProps {
|
|
|
17
17
|
hintText?: string;
|
|
18
18
|
/** Current textarea value */
|
|
19
19
|
value?: string;
|
|
20
|
-
/**
|
|
20
|
+
/**
|
|
21
|
+
* Update controlled value synchronously. Keep asynchronous persistence in
|
|
22
|
+
* the owner, using useDebouncedCommit when writes must be debounced and ordered.
|
|
23
|
+
*/
|
|
21
24
|
onChange: (value: string) => void;
|
|
22
25
|
/** Placeholder text */
|
|
23
26
|
placeholder?: string;
|
|
24
|
-
/** Debounce delay in milliseconds (default: 300) */
|
|
25
|
-
debounceMs?: number;
|
|
26
27
|
/** Additional CSS classes */
|
|
27
28
|
className?: string;
|
|
28
29
|
/** Additional CSS classes applied to the inner <textarea> element */
|
|
@@ -3,29 +3,17 @@ import { CONTROL_SIZES } from "../../utils/componentSizes.js";
|
|
|
3
3
|
import { Text } from "../Text/Text.js";
|
|
4
4
|
import { mergeRefs } from "../../utils/merge-refs.js";
|
|
5
5
|
import { INPUT_HORIZONTAL_PADDING_CLASSES, INPUT_RADIUS_CLASSES, INPUT_TEXT_CLASSES } from "../Input/inputStyles.js";
|
|
6
|
-
import { forwardRef, useCallback,
|
|
6
|
+
import { forwardRef, useCallback, useId, useLayoutEffect, useRef, useState } from "react";
|
|
7
7
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
-
import { useDebouncedCallback } from "use-debounce";
|
|
9
8
|
//#region packages/components/src/components/Textarea/Textarea.tsx
|
|
10
9
|
var DEFAULT_AUTO_RESIZE_MAX_HEIGHT_PX = 200;
|
|
11
|
-
var Textarea = forwardRef(({ size = "lg", leftDecorator, rightDecorator, disabled = false, isError = false, label, hintText, value, onChange, placeholder,
|
|
10
|
+
var Textarea = forwardRef(({ size = "lg", leftDecorator, rightDecorator, disabled = false, isError = false, label, hintText, value, onChange, placeholder, className, inputClassName, id, required = false, autoFocus = false, rows = 3, resize = "vertical", autoResize = false, maxHeight = DEFAULT_AUTO_RESIZE_MAX_HEIGHT_PX, variant = "default", "aria-describedby": ariaDescribedBy, ...rest }, ref) => {
|
|
12
11
|
const generatedId = useId();
|
|
13
12
|
const textareaId = id ?? generatedId;
|
|
14
13
|
const hintTextId = `${textareaId}-hint`;
|
|
15
14
|
const describedBy = [hintText ? hintTextId : void 0, ariaDescribedBy].filter(Boolean).join(" ") || void 0;
|
|
16
|
-
const shouldDebounce = debounceMs > 0;
|
|
17
15
|
const [internalValue, setInternalValue] = useState(value);
|
|
18
|
-
const debouncedOnChange = useDebouncedCallback(onChange, debounceMs);
|
|
19
16
|
const textareaRef = useRef(null);
|
|
20
|
-
useEffect(() => {
|
|
21
|
-
setInternalValue(value);
|
|
22
|
-
}, [value]);
|
|
23
|
-
useEffect(() => {
|
|
24
|
-
if (!shouldDebounce) return;
|
|
25
|
-
return () => {
|
|
26
|
-
debouncedOnChange.flush();
|
|
27
|
-
};
|
|
28
|
-
}, [shouldDebounce, debouncedOnChange]);
|
|
29
17
|
const runAutoResize = useCallback(() => {
|
|
30
18
|
const el = textareaRef.current;
|
|
31
19
|
if (!el || !autoResize) return;
|
|
@@ -48,14 +36,14 @@ var Textarea = forwardRef(({ size = "lg", leftDecorator, rightDecorator, disable
|
|
|
48
36
|
return () => ro.disconnect();
|
|
49
37
|
}, [
|
|
50
38
|
autoResize,
|
|
39
|
+
value,
|
|
51
40
|
internalValue,
|
|
52
41
|
runAutoResize
|
|
53
42
|
]);
|
|
54
43
|
const handleTextareaChange = (e) => {
|
|
55
44
|
const newValue = e.target.value;
|
|
56
45
|
setInternalValue(newValue);
|
|
57
|
-
|
|
58
|
-
else onChange(newValue);
|
|
46
|
+
onChange(newValue);
|
|
59
47
|
};
|
|
60
48
|
const textareaContainerClasses = cn("flex w-full gap-space-2", variant === "default" && [
|
|
61
49
|
"border bg-transparent transition-[border-color,box-shadow,background-color]",
|
|
@@ -87,8 +75,6 @@ var Textarea = forwardRef(({ size = "lg", leftDecorator, rightDecorator, disable
|
|
|
87
75
|
"mt-0.5": size === "md",
|
|
88
76
|
"mt-space-1": size === "lg"
|
|
89
77
|
});
|
|
90
|
-
const leftDecoratorClasses = cn(decoratorClasses);
|
|
91
|
-
const rightDecoratorClasses = cn(decoratorClasses);
|
|
92
78
|
const labelClasses = cn("mb-space-1 block font-medium text-primary", disabled && "text-disabled");
|
|
93
79
|
const hintTextClasses = cn("mt-space-1 text-xs", isError ? "text-error-secondary" : "text-tertiary");
|
|
94
80
|
const containerClasses = cn("w-full", className);
|
|
@@ -109,13 +95,13 @@ var Textarea = forwardRef(({ size = "lg", leftDecorator, rightDecorator, disable
|
|
|
109
95
|
className: textareaContainerClasses,
|
|
110
96
|
children: [
|
|
111
97
|
leftDecorator && /* @__PURE__ */ jsx("div", {
|
|
112
|
-
className:
|
|
98
|
+
className: decoratorClasses,
|
|
113
99
|
children: leftDecorator
|
|
114
100
|
}),
|
|
115
101
|
/* @__PURE__ */ jsx("textarea", {
|
|
116
102
|
ref: mergeRefs([textareaRef, ref]),
|
|
117
103
|
id: textareaId,
|
|
118
|
-
value
|
|
104
|
+
value,
|
|
119
105
|
onChange: handleTextareaChange,
|
|
120
106
|
placeholder,
|
|
121
107
|
disabled,
|
|
@@ -128,7 +114,7 @@ var Textarea = forwardRef(({ size = "lg", leftDecorator, rightDecorator, disable
|
|
|
128
114
|
...rest
|
|
129
115
|
}),
|
|
130
116
|
rightDecorator && /* @__PURE__ */ jsx("div", {
|
|
131
|
-
className:
|
|
117
|
+
className: decoratorClasses,
|
|
132
118
|
children: rightDecorator
|
|
133
119
|
})
|
|
134
120
|
]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Textarea.js","names":[],"sources":["../../../src/components/Textarea/Textarea.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useLayoutEffect,\n useRef,\n useState,\n} from 'react';\n\nimport { useDebouncedCallback } from 'use-debounce';\n\nimport { cn } from '../../utils/cn';\nimport { CONTROL_SIZES } from '../../utils/componentSizes';\nimport { mergeRefs } from '../../utils/merge-refs';\nimport {\n INPUT_HORIZONTAL_PADDING_CLASSES,\n INPUT_RADIUS_CLASSES,\n INPUT_TEXT_CLASSES,\n} from '../Input/inputStyles';\nimport type { InputSize } from '../Input/inputStyles';\nimport { Text } from '../Text';\n\nconst DEFAULT_AUTO_RESIZE_MAX_HEIGHT_PX = 200;\n\nexport interface TextareaProps {\n /** First-row metrics. Total height remains intrinsic to rows and content. */\n size?: InputSize;\n /** Left side decorator/icon */\n leftDecorator?: ReactNode;\n /** Right side decorator/icon */\n rightDecorator?: ReactNode;\n /** Whether the textarea is disabled */\n disabled?: boolean;\n /** Whether the textarea has an error state */\n isError?: boolean;\n /** Label text displayed above the textarea */\n label?: string | ReactNode;\n /** Hint text displayed below the textarea */\n hintText?: string;\n /** Current textarea value */\n value?: string;\n /** Change handler that will be debounced */\n onChange: (value: string) => void;\n /** Placeholder text */\n placeholder?: string;\n /** Debounce delay in milliseconds (default: 300) */\n debounceMs?: number;\n /** Additional CSS classes */\n className?: string;\n /** Additional CSS classes applied to the inner <textarea> element */\n inputClassName?: string;\n /** Textarea ID for accessibility */\n id?: string;\n /** Whether the field is required */\n required?: boolean;\n /** Auto focus on mount */\n autoFocus?: boolean;\n /** Number of visible rows */\n rows?: number;\n /** Whether the textarea can be resized */\n resize?: 'none' | 'vertical' | 'horizontal' | 'both';\n /** When true, textarea height grows with content up to maxHeight (resize is forced to none) */\n autoResize?: boolean;\n /** Max height in px when autoResize is true (default: 200) */\n maxHeight?: number;\n /** Visual variant: default (bordered container) or plain (borderless, for embedding) */\n variant?: 'default' | 'plain';\n}\n\nexport const Textarea = forwardRef<\n HTMLTextAreaElement,\n TextareaProps &\n Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, keyof TextareaProps>\n>(\n (\n {\n size = 'lg',\n leftDecorator,\n rightDecorator,\n disabled = false,\n isError = false,\n label,\n hintText,\n value,\n onChange,\n placeholder,\n debounceMs = 0,\n className,\n inputClassName,\n id,\n required = false,\n autoFocus = false,\n rows = 3,\n resize = 'vertical',\n autoResize = false,\n maxHeight = DEFAULT_AUTO_RESIZE_MAX_HEIGHT_PX,\n variant = 'default',\n 'aria-describedby': ariaDescribedBy,\n ...rest\n },\n ref\n ) => {\n const generatedId = useId();\n const textareaId = id ?? generatedId;\n const hintTextId = `${textareaId}-hint`;\n const describedBy =\n [hintText ? hintTextId : undefined, ariaDescribedBy]\n .filter(Boolean)\n .join(' ') || undefined;\n const shouldDebounce = debounceMs > 0;\n const [internalValue, setInternalValue] = useState(value);\n const debouncedOnChange = useDebouncedCallback(onChange, debounceMs);\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n\n // Update internal value when external value changes\n useEffect(() => {\n setInternalValue(value);\n }, [value]);\n\n // Flush pending debounced onChange on unmount so the last edit is not lost\n useEffect(() => {\n if (!shouldDebounce) return;\n return () => {\n debouncedOnChange.flush();\n };\n }, [shouldDebounce, debouncedOnChange]);\n\n // Auto-resize: grow with content up to maxHeight.\n // Run on value change and on element resize (e.g. container width change so text wraps).\n const runAutoResize = useCallback(() => {\n const el = textareaRef.current;\n if (!el || !autoResize) return;\n el.style.height = 'auto';\n const contentHeight = Math.min(el.scrollHeight, maxHeight);\n el.style.height = `${contentHeight}px`;\n el.style.overflowY = el.scrollHeight > maxHeight ? 'auto' : 'hidden';\n }, [autoResize, maxHeight]);\n\n useLayoutEffect(() => {\n const el = textareaRef.current;\n if (!el) return;\n if (!autoResize) {\n el.style.height = '';\n el.style.overflowY = '';\n return;\n }\n\n runAutoResize();\n\n const ro = new ResizeObserver(runAutoResize);\n ro.observe(el);\n return () => ro.disconnect();\n }, [autoResize, internalValue, runAutoResize]);\n\n const handleTextareaChange = (\n e: React.ChangeEvent<HTMLTextAreaElement>\n ) => {\n const newValue = e.target.value;\n setInternalValue(newValue);\n if (shouldDebounce) {\n debouncedOnChange(newValue);\n } else {\n onChange(newValue);\n }\n };\n\n // Styled container that holds the textarea and decorators\n const textareaContainerClasses = cn(\n 'flex w-full gap-space-2',\n variant === 'default' && [\n 'border bg-transparent transition-[border-color,box-shadow,background-color]',\n CONTROL_SIZES[size].minHeightClassName,\n INPUT_HORIZONTAL_PADDING_CLASSES[size],\n INPUT_RADIUS_CLASSES[size],\n {\n 'py-px': size === 'xs',\n 'py-0.5': size === 'sm',\n 'py-space-1': size === 'md',\n 'py-space-2': size === 'lg',\n },\n 'focus-within:outline-none',\n 'border-subtle',\n !disabled && !isError && '[&:hover:not(:focus-within)]:border-default',\n !isError &&\n 'focus-within:border-focus focus-within:shadow-[0_0_0_2px_var(--border-focus)]',\n isError &&\n 'border-error-strong focus-within:border-error-strong focus-within:shadow-[0_0_0_2px_var(--border-error-strong)]',\n disabled && 'cursor-not-allowed bg-disabled opacity-50',\n ],\n leftDecorator || rightDecorator ? 'items-start' : 'items-center'\n );\n\n // Unstyled textarea that fills the container\n const effectiveResize = autoResize ? 'none' : resize;\n const textareaClasses = cn(\n 'max-w-full flex-1 border-none bg-transparent p-0 outline-none',\n INPUT_TEXT_CLASSES[size],\n 'placeholder:text-placeholder',\n disabled && 'cursor-not-allowed',\n // Resize handling (autoResize forces none)\n {\n 'resize-none': effectiveResize === 'none',\n 'resize-y': effectiveResize === 'vertical',\n 'resize-x': effectiveResize === 'horizontal',\n resize: effectiveResize === 'both',\n },\n inputClassName\n );\n\n // Decorator styles\n const decoratorClasses = cn('flex items-center text-tertiary', {\n 'mt-0': size === 'xs' || size === 'sm',\n 'mt-0.5': size === 'md',\n 'mt-space-1': size === 'lg',\n });\n const leftDecoratorClasses = cn(decoratorClasses);\n const rightDecoratorClasses = cn(decoratorClasses);\n\n const labelClasses = cn(\n 'mb-space-1 block font-medium text-primary',\n disabled && 'text-disabled'\n );\n\n const hintTextClasses = cn(\n 'mt-space-1 text-xs',\n isError ? 'text-error-secondary' : 'text-tertiary'\n );\n\n const containerClasses = cn('w-full', className);\n\n return (\n <div className={containerClasses}>\n {label && (\n <Text\n as=\"label\"\n htmlFor={textareaId}\n className={labelClasses}\n variant=\"sm\"\n >\n {label}\n {required && (\n <span className=\"ml-space-1 text-error-secondary\">*</span>\n )}\n </Text>\n )}\n\n <div className={textareaContainerClasses}>\n {leftDecorator && (\n <div className={leftDecoratorClasses}>{leftDecorator}</div>\n )}\n\n {/* eslint-disable-next-line react/forbid-elements */}\n <textarea\n ref={mergeRefs([textareaRef, ref])}\n id={textareaId}\n value={internalValue ?? ''}\n onChange={handleTextareaChange}\n placeholder={placeholder}\n disabled={disabled}\n required={required}\n autoFocus={autoFocus}\n rows={rows}\n aria-describedby={describedBy}\n aria-invalid={isError || undefined}\n className={textareaClasses}\n {...rest}\n />\n\n {rightDecorator && (\n <div className={rightDecoratorClasses}>{rightDecorator}</div>\n )}\n </div>\n\n {hintText && (\n <Text\n id={hintTextId}\n className={hintTextClasses}\n variant=\"xs\"\n weight=\"normal\"\n >\n {hintText}\n </Text>\n )}\n </div>\n );\n }\n);\n\nTextarea.displayName = 'Textarea';\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Textarea.js","names":[],"sources":["../../../src/components/Textarea/Textarea.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport {\n forwardRef,\n useCallback,\n useId,\n useLayoutEffect,\n useRef,\n useState,\n} from 'react';\n\nimport { cn } from '../../utils/cn';\nimport { CONTROL_SIZES } from '../../utils/componentSizes';\nimport { mergeRefs } from '../../utils/merge-refs';\nimport {\n INPUT_HORIZONTAL_PADDING_CLASSES,\n INPUT_RADIUS_CLASSES,\n INPUT_TEXT_CLASSES,\n} from '../Input/inputStyles';\nimport type { InputSize } from '../Input/inputStyles';\nimport { Text } from '../Text';\n\nconst DEFAULT_AUTO_RESIZE_MAX_HEIGHT_PX = 200;\n\nexport interface TextareaProps {\n /** First-row metrics. Total height remains intrinsic to rows and content. */\n size?: InputSize;\n /** Left side decorator/icon */\n leftDecorator?: ReactNode;\n /** Right side decorator/icon */\n rightDecorator?: ReactNode;\n /** Whether the textarea is disabled */\n disabled?: boolean;\n /** Whether the textarea has an error state */\n isError?: boolean;\n /** Label text displayed above the textarea */\n label?: string | ReactNode;\n /** Hint text displayed below the textarea */\n hintText?: string;\n /** Current textarea value */\n value?: string;\n /**\n * Update controlled value synchronously. Keep asynchronous persistence in\n * the owner, using useDebouncedCommit when writes must be debounced and ordered.\n */\n onChange: (value: string) => void;\n /** Placeholder text */\n placeholder?: string;\n /** Additional CSS classes */\n className?: string;\n /** Additional CSS classes applied to the inner <textarea> element */\n inputClassName?: string;\n /** Textarea ID for accessibility */\n id?: string;\n /** Whether the field is required */\n required?: boolean;\n /** Auto focus on mount */\n autoFocus?: boolean;\n /** Number of visible rows */\n rows?: number;\n /** Whether the textarea can be resized */\n resize?: 'none' | 'vertical' | 'horizontal' | 'both';\n /** When true, textarea height grows with content up to maxHeight (resize is forced to none) */\n autoResize?: boolean;\n /** Max height in px when autoResize is true (default: 200) */\n maxHeight?: number;\n /** Visual variant: default (bordered container) or plain (borderless, for embedding) */\n variant?: 'default' | 'plain';\n}\n\nexport const Textarea = forwardRef<\n HTMLTextAreaElement,\n TextareaProps &\n Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, keyof TextareaProps>\n>(\n (\n {\n size = 'lg',\n leftDecorator,\n rightDecorator,\n disabled = false,\n isError = false,\n label,\n hintText,\n value,\n onChange,\n placeholder,\n className,\n inputClassName,\n id,\n required = false,\n autoFocus = false,\n rows = 3,\n resize = 'vertical',\n autoResize = false,\n maxHeight = DEFAULT_AUTO_RESIZE_MAX_HEIGHT_PX,\n variant = 'default',\n 'aria-describedby': ariaDescribedBy,\n ...rest\n },\n ref\n ) => {\n const generatedId = useId();\n const textareaId = id ?? generatedId;\n const hintTextId = `${textareaId}-hint`;\n const describedBy =\n [hintText ? hintTextId : undefined, ariaDescribedBy]\n .filter(Boolean)\n .join(' ') || undefined;\n const [internalValue, setInternalValue] = useState(value);\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n\n // Auto-resize: grow with content up to maxHeight.\n // Run on value change and on element resize (e.g. container width change so text wraps).\n const runAutoResize = useCallback(() => {\n const el = textareaRef.current;\n if (!el || !autoResize) return;\n el.style.height = 'auto';\n const contentHeight = Math.min(el.scrollHeight, maxHeight);\n el.style.height = `${contentHeight}px`;\n el.style.overflowY = el.scrollHeight > maxHeight ? 'auto' : 'hidden';\n }, [autoResize, maxHeight]);\n\n useLayoutEffect(() => {\n const el = textareaRef.current;\n if (!el) return;\n if (!autoResize) {\n el.style.height = '';\n el.style.overflowY = '';\n return;\n }\n\n runAutoResize();\n\n const ro = new ResizeObserver(runAutoResize);\n ro.observe(el);\n return () => ro.disconnect();\n }, [autoResize, value, internalValue, runAutoResize]);\n\n const handleTextareaChange = (\n e: React.ChangeEvent<HTMLTextAreaElement>\n ) => {\n const newValue = e.target.value;\n setInternalValue(newValue);\n onChange(newValue);\n };\n\n const textareaContainerClasses = cn(\n 'flex w-full gap-space-2',\n variant === 'default' && [\n 'border bg-transparent transition-[border-color,box-shadow,background-color]',\n CONTROL_SIZES[size].minHeightClassName,\n INPUT_HORIZONTAL_PADDING_CLASSES[size],\n INPUT_RADIUS_CLASSES[size],\n {\n 'py-px': size === 'xs',\n 'py-0.5': size === 'sm',\n 'py-space-1': size === 'md',\n 'py-space-2': size === 'lg',\n },\n 'focus-within:outline-none',\n 'border-subtle',\n !disabled && !isError && '[&:hover:not(:focus-within)]:border-default',\n !isError &&\n 'focus-within:border-focus focus-within:shadow-[0_0_0_2px_var(--border-focus)]',\n isError &&\n 'border-error-strong focus-within:border-error-strong focus-within:shadow-[0_0_0_2px_var(--border-error-strong)]',\n disabled && 'cursor-not-allowed bg-disabled opacity-50',\n ],\n leftDecorator || rightDecorator ? 'items-start' : 'items-center'\n );\n\n const effectiveResize = autoResize ? 'none' : resize;\n const textareaClasses = cn(\n 'max-w-full flex-1 border-none bg-transparent p-0 outline-none',\n INPUT_TEXT_CLASSES[size],\n 'placeholder:text-placeholder',\n disabled && 'cursor-not-allowed',\n // Resize handling (autoResize forces none)\n {\n 'resize-none': effectiveResize === 'none',\n 'resize-y': effectiveResize === 'vertical',\n 'resize-x': effectiveResize === 'horizontal',\n resize: effectiveResize === 'both',\n },\n inputClassName\n );\n\n const decoratorClasses = cn('flex items-center text-tertiary', {\n 'mt-0': size === 'xs' || size === 'sm',\n 'mt-0.5': size === 'md',\n 'mt-space-1': size === 'lg',\n });\n\n const labelClasses = cn(\n 'mb-space-1 block font-medium text-primary',\n disabled && 'text-disabled'\n );\n\n const hintTextClasses = cn(\n 'mt-space-1 text-xs',\n isError ? 'text-error-secondary' : 'text-tertiary'\n );\n\n const containerClasses = cn('w-full', className);\n\n return (\n <div className={containerClasses}>\n {label && (\n <Text\n as=\"label\"\n htmlFor={textareaId}\n className={labelClasses}\n variant=\"sm\"\n >\n {label}\n {required && (\n <span className=\"ml-space-1 text-error-secondary\">*</span>\n )}\n </Text>\n )}\n\n <div className={textareaContainerClasses}>\n {leftDecorator && (\n <div className={decoratorClasses}>{leftDecorator}</div>\n )}\n\n {/* eslint-disable-next-line react/forbid-elements */}\n <textarea\n ref={mergeRefs([textareaRef, ref])}\n id={textareaId}\n value={value}\n onChange={handleTextareaChange}\n placeholder={placeholder}\n disabled={disabled}\n required={required}\n autoFocus={autoFocus}\n rows={rows}\n aria-describedby={describedBy}\n aria-invalid={isError || undefined}\n className={textareaClasses}\n {...rest}\n />\n\n {rightDecorator && (\n <div className={decoratorClasses}>{rightDecorator}</div>\n )}\n </div>\n\n {hintText && (\n <Text\n id={hintTextId}\n className={hintTextClasses}\n variant=\"xs\"\n weight=\"normal\"\n >\n {hintText}\n </Text>\n )}\n </div>\n );\n }\n);\n\nTextarea.displayName = 'Textarea';\n"],"mappings":"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"}
|
|
@@ -12,6 +12,7 @@ interface LoadingIndicatorProps extends Omit<ComponentPropsWithRef<'span'>, 'chi
|
|
|
12
12
|
tone?: 'neutral' | 'brand';
|
|
13
13
|
/** Reduced-motion preferences always take precedence. */
|
|
14
14
|
animated?: boolean;
|
|
15
|
+
speed?: 'normal' | 'slow';
|
|
15
16
|
}
|
|
16
17
|
/** Decorative indicator. Announce loading on the surrounding control or region. */
|
|
17
18
|
declare const LoadingIndicator: import("react").ForwardRefExoticComponent<Omit<LoadingIndicatorProps, "ref"> & import("react").RefAttributes<HTMLSpanElement>>;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as LoadingIndicator } from "../../_chunks/LoadingIndicator-
|
|
1
|
+
import { t as LoadingIndicator } from "../../_chunks/LoadingIndicator-CDcArsiM.js";
|
|
2
2
|
export { LoadingIndicator };
|
|
@@ -8,6 +8,7 @@ interface ThinkingStateProps extends Omit<ComponentPropsWithRef<'div'>, 'childre
|
|
|
8
8
|
tone?: LoadingIndicatorProps['tone'];
|
|
9
9
|
/** Full animates the grid and label; subtle animates only the grid; none is static. */
|
|
10
10
|
motion?: 'full' | 'subtle' | 'none';
|
|
11
|
+
speed?: LoadingIndicatorProps['speed'];
|
|
11
12
|
/** Opt in to elapsed time for longer waits. Hidden by default. */
|
|
12
13
|
showElapsed?: boolean;
|
|
13
14
|
/** Supply elapsed milliseconds from the operation to disable the internal clock. */
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { cn } from "../../utils/cn.js";
|
|
2
2
|
import { Text } from "../Text/Text.js";
|
|
3
|
-
import { n as ThinkingState_module_default, t as LoadingIndicator } from "../../_chunks/LoadingIndicator-
|
|
3
|
+
import { n as ThinkingState_module_default, t as LoadingIndicator } from "../../_chunks/LoadingIndicator-CDcArsiM.js";
|
|
4
4
|
import { ThinkingStateTimer } from "./ThinkingStateTimer.js";
|
|
5
5
|
import { forwardRef } from "react";
|
|
6
6
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
7
|
//#region packages/components/src/components/ThinkingState/ThinkingState.tsx
|
|
8
8
|
/** Inline status for active AI work. Remount with a new key for a new operation. */
|
|
9
|
-
var ThinkingState = forwardRef(function ThinkingState({ label = "Thinking", variant = "drive", size = "sm", tone = "neutral", motion = "full", showElapsed = false, elapsedMs, timerPrecision = "seconds", className, ...props }, ref) {
|
|
9
|
+
var ThinkingState = forwardRef(function ThinkingState({ label = "Thinking", variant = "drive", size = "sm", tone = "neutral", motion = "full", speed = "normal", showElapsed = false, elapsedMs, timerPrecision = "seconds", className, ...props }, ref) {
|
|
10
10
|
return /* @__PURE__ */ jsxs("div", {
|
|
11
11
|
...props,
|
|
12
12
|
ref,
|
|
@@ -15,7 +15,8 @@ var ThinkingState = forwardRef(function ThinkingState({ label = "Thinking", vari
|
|
|
15
15
|
variant,
|
|
16
16
|
size,
|
|
17
17
|
tone,
|
|
18
|
-
animated: motion !== "none"
|
|
18
|
+
animated: motion !== "none",
|
|
19
|
+
speed
|
|
19
20
|
}), /* @__PURE__ */ jsxs("span", {
|
|
20
21
|
className: "inline-flex min-w-0 items-baseline gap-space-2",
|
|
21
22
|
children: [/* @__PURE__ */ jsx(Text, {
|
|
@@ -25,7 +26,7 @@ var ThinkingState = forwardRef(function ThinkingState({ label = "Thinking", vari
|
|
|
25
26
|
role: "status",
|
|
26
27
|
"aria-live": "polite",
|
|
27
28
|
"aria-atomic": "true",
|
|
28
|
-
className: cn("min-w-0 break-words leading-4", motion === "full" ? ThinkingState_module_default.shimmer : "text-secondary", motion === "full" && tone === "brand" && ThinkingState_module_default.brandShimmer),
|
|
29
|
+
className: cn("min-w-0 break-words leading-4", motion === "full" ? ThinkingState_module_default.shimmer : "text-secondary", motion === "full" && tone === "brand" && ThinkingState_module_default.brandShimmer, speed === "slow" && ThinkingState_module_default.slow),
|
|
29
30
|
children: label
|
|
30
31
|
}), /* @__PURE__ */ jsx(ThinkingStateTimer, {
|
|
31
32
|
visible: showElapsed,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThinkingState.js","names":[],"sources":["../../../src/components/ThinkingState/ThinkingState.tsx"],"sourcesContent":["import { forwardRef } from 'react';\nimport type { ComponentPropsWithRef, ReactNode } from 'react';\n\nimport { cn } from '../../utils/cn';\nimport { Text } from '../Text';\nimport {\n LoadingIndicator,\n type LoadingIndicatorProps,\n} from './LoadingIndicator';\nimport { ThinkingStateTimer } from './ThinkingStateTimer';\n\nimport styles from './ThinkingState.module.css';\n\ninterface ThinkingStateProps extends Omit<\n ComponentPropsWithRef<'div'>,\n 'children'\n> {\n /** Short, noninteractive description of the work in progress. */\n label?: ReactNode;\n variant?: LoadingIndicatorProps['variant'];\n size?: LoadingIndicatorProps['size'];\n tone?: LoadingIndicatorProps['tone'];\n /** Full animates the grid and label; subtle animates only the grid; none is static. */\n motion?: 'full' | 'subtle' | 'none';\n /** Opt in to elapsed time for longer waits. Hidden by default. */\n showElapsed?: boolean;\n /** Supply elapsed milliseconds from the operation to disable the internal clock. */\n elapsedMs?: number;\n /** Seconds updates once per second; tenths preserves the finer-grained source timer. */\n timerPrecision?: 'seconds' | 'tenths';\n}\n\n/** Inline status for active AI work. Remount with a new key for a new operation. */\nconst ThinkingState = forwardRef<HTMLDivElement, ThinkingStateProps>(\n function ThinkingState(\n {\n label = 'Thinking',\n variant = 'drive',\n size = 'sm',\n tone = 'neutral',\n motion = 'full',\n showElapsed = false,\n elapsedMs,\n timerPrecision = 'seconds',\n className,\n ...props\n },\n ref\n ) {\n return (\n <div\n {...props}\n ref={ref}\n className={cn(\n 'inline-flex max-w-full items-center gap-space-2 align-middle',\n className\n )}\n >\n <LoadingIndicator\n variant={variant}\n size={size}\n tone={tone}\n animated={motion !== 'none'}\n />\n <span className=\"inline-flex min-w-0 items-baseline gap-space-2\">\n <Text\n as=\"span\"\n variant={size === 'sm' ? 'sm' : 'md'}\n weight=\"medium\"\n role=\"status\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n className={cn(\n 'min-w-0 break-words leading-4',\n motion === 'full' ? styles.shimmer : 'text-secondary',\n motion === 'full' && tone === 'brand' && styles.brandShimmer\n )}\n >\n {label}\n </Text>\n <ThinkingStateTimer\n visible={showElapsed}\n elapsedMs={elapsedMs}\n precision={timerPrecision}\n size={size}\n />\n </span>\n </div>\n );\n }\n);\n\nexport { ThinkingState };\nexport type { ThinkingStateProps };\n"],"mappings":";;;;;;;;
|
|
1
|
+
{"version":3,"file":"ThinkingState.js","names":[],"sources":["../../../src/components/ThinkingState/ThinkingState.tsx"],"sourcesContent":["import { forwardRef } from 'react';\nimport type { ComponentPropsWithRef, ReactNode } from 'react';\n\nimport { cn } from '../../utils/cn';\nimport { Text } from '../Text';\nimport {\n LoadingIndicator,\n type LoadingIndicatorProps,\n} from './LoadingIndicator';\nimport { ThinkingStateTimer } from './ThinkingStateTimer';\n\nimport styles from './ThinkingState.module.css';\n\ninterface ThinkingStateProps extends Omit<\n ComponentPropsWithRef<'div'>,\n 'children'\n> {\n /** Short, noninteractive description of the work in progress. */\n label?: ReactNode;\n variant?: LoadingIndicatorProps['variant'];\n size?: LoadingIndicatorProps['size'];\n tone?: LoadingIndicatorProps['tone'];\n /** Full animates the grid and label; subtle animates only the grid; none is static. */\n motion?: 'full' | 'subtle' | 'none';\n speed?: LoadingIndicatorProps['speed'];\n /** Opt in to elapsed time for longer waits. Hidden by default. */\n showElapsed?: boolean;\n /** Supply elapsed milliseconds from the operation to disable the internal clock. */\n elapsedMs?: number;\n /** Seconds updates once per second; tenths preserves the finer-grained source timer. */\n timerPrecision?: 'seconds' | 'tenths';\n}\n\n/** Inline status for active AI work. Remount with a new key for a new operation. */\nconst ThinkingState = forwardRef<HTMLDivElement, ThinkingStateProps>(\n function ThinkingState(\n {\n label = 'Thinking',\n variant = 'drive',\n size = 'sm',\n tone = 'neutral',\n motion = 'full',\n speed = 'normal',\n showElapsed = false,\n elapsedMs,\n timerPrecision = 'seconds',\n className,\n ...props\n },\n ref\n ) {\n return (\n <div\n {...props}\n ref={ref}\n className={cn(\n 'inline-flex max-w-full items-center gap-space-2 align-middle',\n className\n )}\n >\n <LoadingIndicator\n variant={variant}\n size={size}\n tone={tone}\n animated={motion !== 'none'}\n speed={speed}\n />\n <span className=\"inline-flex min-w-0 items-baseline gap-space-2\">\n <Text\n as=\"span\"\n variant={size === 'sm' ? 'sm' : 'md'}\n weight=\"medium\"\n role=\"status\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n className={cn(\n 'min-w-0 break-words leading-4',\n motion === 'full' ? styles.shimmer : 'text-secondary',\n motion === 'full' && tone === 'brand' && styles.brandShimmer,\n speed === 'slow' && styles.slow\n )}\n >\n {label}\n </Text>\n <ThinkingStateTimer\n visible={showElapsed}\n elapsedMs={elapsedMs}\n precision={timerPrecision}\n size={size}\n />\n </span>\n </div>\n );\n }\n);\n\nexport { ThinkingState };\nexport type { ThinkingStateProps };\n"],"mappings":";;;;;;;;AAkCA,IAAM,gBAAgB,WACpB,SAAS,cACP,EACE,QAAQ,YACR,UAAU,SACV,OAAO,MACP,OAAO,WACP,SAAS,QACT,QAAQ,UACR,cAAc,OACd,WACA,iBAAiB,WACjB,WACA,GAAG,SAEL,KACA;CACA,OACE,qBAAC,OAAD;EACE,GAAI;EACC;EACL,WAAW,GACT,gEACA,SACF;EANF,UAAA,CAQE,oBAAC,kBAAD;GACW;GACH;GACA;GACN,UAAU,WAAW;GACd;EACR,CAAA,GACD,qBAAC,QAAD;GAAM,WAAU;GAAhB,UAAA,CACE,oBAAC,MAAD;IACE,IAAG;IACH,SAAS,SAAS,OAAO,OAAO;IAChC,QAAO;IACP,MAAK;IACL,aAAU;IACV,eAAY;IACZ,WAAW,GACT,iCACA,WAAW,SAAS,6BAAO,UAAU,kBACrC,WAAW,UAAU,SAAS,WAAW,6BAAO,cAChD,UAAU,UAAU,6BAAO,IAC7B;IAEC,UAAA;GACG,CAAA,GACN,oBAAC,oBAAD;IACE,SAAS;IACE;IACX,WAAW;IACL;GACP,CAAA,CACG;EACH,CAAA,CAAA;;AAET,CACF"}
|