@hjmds/react 1.48.0 → 1.49.0

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.
@@ -1 +1 @@
1
- {"version":3,"file":"image-choice.js","sourceRoot":"","sources":["../src/image-choice.tsx"],"names":[],"mappings":"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","sourcesContent":["\"use client\";\n\nimport { useEffect, useId, useLayoutEffect, useRef, useState, type CSSProperties, type ReactNode } from \"react\";\nimport { Text } from \"./layout.js\";\nimport { useOptionalHjmTheme } from \"./provider.js\";\nimport { typography } from \"@hjmds/design-contracts/foundations\";\nimport {\n imageChoiceRecipe, resolveImageChoiceCaptionLines, resolveImageChoiceColumns, resolveImageChoiceOuterSize, resolveImageChoiceTileHeight, resolveImageChoiceToggle,\n validateImageChoiceItemLabelLines, validateImageChoiceItems, type ImageChoiceItemDescriptor, type ImageChoiceItemLabelLines,\n} from \"@hjmds/design-contracts/components/image-choice\";\nimport { resolveVirtualWindow, virtualListRecipe } from \"@hjmds/design-contracts/components/virtual-list\";\n\nexport type ImageChoiceItem = ImageChoiceItemDescriptor & Readonly<{ image: ReactNode }>;\nexport type ImageChoiceVirtualization = Readonly<{\n /** Height of the scrolling viewport in px. The group scrolls itself, so do not also put it in a scrolling parent. */\n height: number;\n /** Rows kept mounted above and below the visible window. */\n overscan?: number;\n}>;\ntype Base = Readonly<{\n /** Localized group name read before the choices. */\n label: string;\n /** Visible caption above the tiles; omit when the screen heading already names the choice. */\n showLabel?: boolean;\n /**\n * Shows each item's `label` under its tile (one ellipsized line), e.g. a person's name in a recipient picker.\n * The tile keeps the same accessible name, so turning this on does not change what assistive tech reads.\n */\n showItemLabels?: boolean;\n /** Lines the visible item label wraps to before the ellipsis (default 1). Every tile reserves the same height. */\n itemLabelLines?: ImageChoiceItemLabelLines;\n items: readonly ImageChoiceItem[];\n disabled?: boolean;\n className?: string;\n /**\n * Long lists (hundreds of people): only the rows near the viewport are mounted. Tiles keep their size; the column\n * count follows the measured width.\n */\n virtualized?: ImageChoiceVirtualization;\n}>;\nexport type ImageChoiceGroupProps = Base & (\n | Readonly<{ selectionMode: \"single\"; value: string | null; onValueChange(value: string): void }>\n | Readonly<{ selectionMode: \"multiple\"; value: readonly string[]; onValueChange(value: string[]): void }>\n);\n\n/**\n * Picks avatars or photos by image (2026-10-16). Tiles are square, wrap to the available width, and show\n * a brand ring plus a check when chosen; assistive tech hears radios (single) or checkboxes (multiple).\n */\nexport function ImageChoiceGroup(props: ImageChoiceGroupProps) {\n const { label, showLabel = false, showItemLabels = false, itemLabelLines = imageChoiceRecipe.itemLabelLines, items, disabled = false, className, virtualized } = props;\n validateImageChoiceItems(items);\n validateImageChoiceItemLabelLines(itemLabelLines);\n const theme = useOptionalHjmTheme();\n const baseId = useId();\n const isSelected = (id: string) => props.selectionMode === \"single\" ? props.value === id : props.value.includes(id);\n const choose = (id: string) => {\n if (props.selectionMode === \"single\") props.onValueChange(resolveImageChoiceToggle(\"single\", props.value, id));\n else props.onValueChange(resolveImageChoiceToggle(\"multiple\", props.value, id));\n };\n const role = props.selectionMode === \"single\" ? \"radio\" : \"checkbox\";\n const groupRole = props.selectionMode === \"single\" ? \"radiogroup\" : \"group\";\n const captionLines = resolveImageChoiceCaptionLines({ showItemLabels, itemLabelLines, hasDescription: items.some(item => item.description !== undefined) });\n const style = { \"--hjm-image-choice-size\": `${imageChoiceRecipe.tileSize}px`, \"--hjm-image-choice-gap\": `${imageChoiceRecipe.gap}px`, \"--hjm-image-choice-radius\": `${imageChoiceRecipe.radius}px`, \"--hjm-image-choice-ring\": `${imageChoiceRecipe.ringWidth}px`, \"--hjm-image-choice-offset\": `${imageChoiceRecipe.ringOffset}px`, \"--hjm-image-choice-check\": `${imageChoiceRecipe.checkSize}px`, \"--hjm-image-choice-item-label-gap\": `${imageChoiceRecipe.itemLabelGap}px`, \"--hjm-image-choice-caption-lines\": captionLines, \"--hjm-image-choice-viewport-inset\": `${imageChoiceRecipe.virtualViewportInset}px` } as CSSProperties;\n const renderTile = (item: ImageChoiceItem, index: number) => {\n const selected = isSelected(item.id);\n const descriptionId = item.description === undefined ? undefined : `${baseId}-description-${index}`;\n return <button key={item.id} type=\"button\" role={role} aria-checked={selected} aria-label={item.label} aria-describedby={descriptionId} className=\"hjm-image-choice__tile\" data-selected={selected || undefined}\n disabled={disabled || item.disabled} onClick={() => choose(item.id)}>\n {/* The ring lives on an inner frame so an item label can sit below it inside the same button (one tap target). */}\n <span className=\"hjm-image-choice__ring\">\n <span className=\"hjm-image-choice__image\">{item.image}</span>\n {selected ? <span className=\"hjm-image-choice__check\" aria-hidden=\"true\">✓</span> : null}\n </span>\n {/* Every tile reserves the same caption block, so wrapped rows stay even and virtual rows share one height.\n aria-label already names the tile; the description is read once through aria-describedby. */}\n {captionLines > 0 ? <span className=\"hjm-image-choice__caption\">\n {showItemLabels ? <Text variant={imageChoiceRecipe.itemLabelVariant} className=\"hjm-image-choice__item-label\" aria-hidden=\"true\" data-lines={itemLabelLines === 1 ? undefined : itemLabelLines}>{item.label}</Text> : null}\n {item.description === undefined ? null : <Text id={descriptionId} variant={imageChoiceRecipe.itemDescriptionVariant} tone={imageChoiceRecipe.itemDescriptionTone} className=\"hjm-image-choice__item-description\" aria-hidden=\"true\">{item.description}</Text>}\n </span> : null}\n </button>;\n };\n // First-paint estimate in px; the rendered row is measured afterwards because caption line height is rem-based.\n const estimatedTileHeight = resolveImageChoiceTileHeight(captionLines, typography[imageChoiceRecipe.itemLabelVariant].lineHeight * (theme?.environment.textScale ?? 1));\n return <div className={[\"hjm-image-choice\", className].filter(Boolean).join(\" \")} style={style}>\n {showLabel ? <span className=\"hjm-image-choice__label\">{label}</span> : null}\n {virtualized\n ? <VirtualTileRows items={items} renderTile={renderTile} label={label} role={groupRole} height={virtualized.height} overscan={virtualized.overscan ?? virtualListRecipe.overscan} estimatedTileHeight={estimatedTileHeight} />\n : <div className=\"hjm-image-choice__tiles\" role={groupRole} aria-label={label}>{items.map(renderTile)}</div>}\n </div>;\n}\n\n/**\n * Windowed rows of tiles. It reuses the VirtualList window math (`resolveVirtualWindow`) but not the VirtualList\n * component: that one names its rows as list items and moves focus row by row, which would replace the radio/checkbox\n * group this control is. Tiles stay in the tab order; focusing one scrolls it into view and the window follows.\n */\nfunction VirtualTileRows({ items, renderTile, label, role, height, overscan, estimatedTileHeight }: Readonly<{\n items: readonly ImageChoiceItem[]; renderTile: (item: ImageChoiceItem, index: number) => ReactNode; label: string;\n role: \"radiogroup\" | \"group\"; height: number; overscan: number; estimatedTileHeight: number;\n}>) {\n const host = useRef<HTMLDivElement>(null);\n const [width, setWidth] = useState(0);\n const [measuredTileHeight, setMeasuredTileHeight] = useState(0);\n const [scrollTop, setScrollTop] = useState(0);\n const [focusedRow, setFocusedRow] = useState<number | null>(null);\n useEffect(() => {\n if (!host.current) return;\n // Measure the slot itself (content box: inside the inset and the scrollbar); sheets and sidebars change it.\n const observer = new ResizeObserver(([entry]) => setWidth(entry?.contentRect.width ?? 0));\n observer.observe(host.current);\n return () => observer.disconnect();\n }, []);\n useLayoutEffect(() => {\n // Every row is one tile high (the caption block is reserved), so any mounted row gives the real stride.\n const rendered = host.current?.querySelector<HTMLElement>(\".hjm-image-choice__row\")?.offsetHeight ?? 0;\n if (rendered > 0 && rendered !== measuredTileHeight) setMeasuredTileHeight(rendered);\n });\n const columns = width > 0 ? Math.min(items.length, resolveImageChoiceColumns(width)) : 0;\n const stride = (measuredTileHeight || estimatedTileHeight) + imageChoiceRecipe.gap;\n const rowCount = columns ? Math.ceil(items.length / columns) : 0;\n const range = resolveVirtualWindow(rowCount, stride, height, scrollTop, overscan);\n // Keep the focused tile's row mounted when scrolling moves it out of the window, so focus is not dropped to body.\n const rows = Array.from({ length: range.end - range.start }, (_, i) => range.start + i);\n if (focusedRow !== null && focusedRow < rowCount && !rows.includes(focusedRow)) rows.push(focusedRow);\n rows.sort((first, second) => first - second); // DOM order is the tab order\n return <div ref={host} className=\"hjm-image-choice__viewport\" role={role} aria-label={label} style={{ height }}\n onScroll={event => setScrollTop(event.currentTarget.scrollTop)}>\n {columns ? <div style={{ position: \"relative\", height: Math.max(0, range.totalHeight - imageChoiceRecipe.gap) }}>\n {rows.map(row => <div key={row} className=\"hjm-image-choice__row\" style={{ top: row * stride, display: \"grid\", gridTemplateColumns: `repeat(${columns}, ${resolveImageChoiceOuterSize()}px)`, justifyContent: \"space-between\" }}\n onFocusCapture={() => setFocusedRow(row)} onBlurCapture={event => { if (!event.currentTarget.contains(event.relatedTarget)) setFocusedRow(current => current === row ? null : current); }}>\n {items.slice(row * columns, row * columns + columns).map((item, offset) => renderTile(item, row * columns + offset))}\n </div>)}\n </div> : null}\n </div>;\n}\n"]}
1
+ {"version":3,"file":"image-choice.js","sourceRoot":"","sources":["../src/image-choice.tsx"],"names":[],"mappings":"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","sourcesContent":["\"use client\";\n\nimport { useEffect, useId, useLayoutEffect, useRef, useState, type CSSProperties, type ReactNode } from \"react\";\nimport { Text } from \"./layout.js\";\nimport { useOptionalHjmTheme } from \"./provider.js\";\nimport { typography } from \"@hjmds/design-contracts/foundations\";\nimport {\n imageChoiceRecipe, resolveImageChoiceCaptionLines, resolveImageChoiceColumns, resolveImageChoiceOuterSize, resolveImageChoiceTileHeight, resolveImageChoiceToggle,\n validateImageChoiceItemLabelLines, validateImageChoiceItems, type ImageChoiceItemDescriptor, type ImageChoiceItemLabelLines,\n} from \"@hjmds/design-contracts/components/image-choice\";\nimport { resolveVirtualWindow, virtualListRecipe } from \"@hjmds/design-contracts/components/virtual-list\";\n\nexport type ImageChoiceItem = ImageChoiceItemDescriptor & Readonly<{ image: ReactNode }>;\nexport type ImageChoiceVirtualization = Readonly<{\n /** Height of the scrolling viewport in px. The group scrolls itself, so do not also put it in a scrolling parent. */\n height: number;\n /** Rows kept mounted above and below the visible window. */\n overscan?: number;\n}>;\ntype Base = Readonly<{\n /** Localized group name read before the choices. */\n label: string;\n /** Visible caption above the tiles; omit when the screen heading already names the choice. */\n showLabel?: boolean;\n /**\n * Shows each item's `label` under its tile (one ellipsized line), e.g. a person's name in a recipient picker.\n * The tile keeps the same accessible name, so turning this on does not change what assistive tech reads.\n */\n showItemLabels?: boolean;\n /** Lines the visible item label wraps to before the ellipsis (default 1). Every tile reserves the same height. */\n itemLabelLines?: ImageChoiceItemLabelLines;\n items: readonly ImageChoiceItem[];\n disabled?: boolean;\n className?: string;\n /**\n * Long lists (hundreds of people): only the rows near the viewport are mounted. Tiles keep their size; the column\n * count follows the measured width.\n */\n virtualized?: ImageChoiceVirtualization;\n}>;\nexport type ImageChoiceGroupProps = Base & (\n | Readonly<{ selectionMode: \"single\"; value: string | null; onValueChange(value: string): void }>\n | Readonly<{ selectionMode: \"multiple\"; value: readonly string[]; onValueChange(value: string[]): void }>\n);\n\n/**\n * Picks avatars or photos by image (2026-10-16). Tiles are square, wrap to the available width, and show\n * a brand ring plus a check when chosen; assistive tech hears radios (single) or checkboxes (multiple).\n */\nexport function ImageChoiceGroup(props: ImageChoiceGroupProps) {\n const { label, showLabel = false, showItemLabels = false, itemLabelLines = imageChoiceRecipe.itemLabelLines, items, disabled = false, className, virtualized } = props;\n validateImageChoiceItems(items);\n validateImageChoiceItemLabelLines(itemLabelLines);\n const theme = useOptionalHjmTheme();\n const baseId = useId();\n const isSelected = (id: string) => props.selectionMode === \"single\" ? props.value === id : props.value.includes(id);\n const choose = (id: string) => {\n if (props.selectionMode === \"single\") props.onValueChange(resolveImageChoiceToggle(\"single\", props.value, id));\n else props.onValueChange(resolveImageChoiceToggle(\"multiple\", props.value, id));\n };\n const role = props.selectionMode === \"single\" ? \"radio\" : \"checkbox\";\n const groupRole = props.selectionMode === \"single\" ? \"radiogroup\" : \"group\";\n const captionLines = resolveImageChoiceCaptionLines({ showItemLabels, itemLabelLines, hasDescription: items.some(item => item.description !== undefined) });\n const style = { \"--hjm-image-choice-size\": `${imageChoiceRecipe.tileSize}px`, \"--hjm-image-choice-gap\": `${imageChoiceRecipe.gap}px`, \"--hjm-image-choice-radius\": `${imageChoiceRecipe.radius}px`, \"--hjm-image-choice-ring\": `${imageChoiceRecipe.ringWidth}px`, \"--hjm-image-choice-offset\": `${imageChoiceRecipe.ringOffset}px`, \"--hjm-image-choice-check\": `${imageChoiceRecipe.checkSize}px`, \"--hjm-image-choice-item-label-gap\": `${imageChoiceRecipe.itemLabelGap}px`, \"--hjm-image-choice-caption-lines\": captionLines, \"--hjm-image-choice-viewport-inset\": `${imageChoiceRecipe.virtualViewportInset}px` } as CSSProperties;\n const renderTile = (item: ImageChoiceItem, index: number) => {\n const selected = isSelected(item.id);\n const descriptionId = item.description === undefined ? undefined : `${baseId}-description-${index}`;\n return <button key={item.id} type=\"button\" role={role} aria-checked={selected} aria-label={item.label} aria-describedby={descriptionId} className=\"hjm-image-choice__tile\" data-selected={selected || undefined}\n disabled={disabled || item.disabled} onClick={() => choose(item.id)}>\n {/* The ring lives on an inner frame so an item label can sit below it inside the same button (one tap target). */}\n <span className=\"hjm-image-choice__ring\">\n <span className=\"hjm-image-choice__image\">{item.image}</span>\n {selected ? <span className=\"hjm-image-choice__check\" aria-hidden=\"true\">✓</span> : null}\n </span>\n {/* Every tile reserves the same caption block, so wrapped rows stay even and virtual rows share one height.\n aria-label already names the tile; the description is read once through aria-describedby. */}\n {captionLines > 0 ? <span className=\"hjm-image-choice__caption\">\n {showItemLabels ? <Text variant={imageChoiceRecipe.itemLabelVariant} className=\"hjm-image-choice__item-label\" aria-hidden=\"true\" data-lines={itemLabelLines === 1 ? undefined : itemLabelLines}>{item.label}</Text> : null}\n {item.description === undefined ? null : <Text id={descriptionId} variant={imageChoiceRecipe.itemDescriptionVariant} tone={imageChoiceRecipe.itemDescriptionTone} className=\"hjm-image-choice__item-description\" aria-hidden=\"true\">{item.description}</Text>}\n </span> : null}\n </button>;\n };\n // First-paint estimate in px; the rendered row is measured afterwards because caption line height is rem-based.\n const estimatedTileHeight = resolveImageChoiceTileHeight(captionLines, typography[imageChoiceRecipe.itemLabelVariant].lineHeight * (theme?.environment.textScale ?? 1));\n return <div className={[\"hjm-image-choice\", className].filter(Boolean).join(\" \")} style={style}>\n {showLabel ? <span className=\"hjm-image-choice__label\">{label}</span> : null}\n {virtualized\n ? <VirtualTileRows items={items} renderTile={renderTile} label={label} role={groupRole} height={virtualized.height} overscan={virtualized.overscan ?? virtualListRecipe.overscan} estimatedTileHeight={estimatedTileHeight} />\n : <div className=\"hjm-image-choice__tiles\" role={groupRole} aria-label={label}>{items.map(renderTile)}</div>}\n </div>;\n}\n\n/**\n * Windowed rows of tiles. It reuses the VirtualList window math (`resolveVirtualWindow`) but not the VirtualList\n * component: that one names its rows as list items and moves focus row by row, which would replace the radio/checkbox\n * group this control is. Tiles stay in the tab order; focusing one scrolls it into view and the window follows.\n */\nfunction VirtualTileRows({ items, renderTile, label, role, height, overscan, estimatedTileHeight }: Readonly<{\n items: readonly ImageChoiceItem[]; renderTile: (item: ImageChoiceItem, index: number) => ReactNode; label: string;\n role: \"radiogroup\" | \"group\"; height: number; overscan: number; estimatedTileHeight: number;\n}>) {\n const host = useRef<HTMLDivElement>(null);\n const [width, setWidth] = useState(0);\n const [measuredTileHeight, setMeasuredTileHeight] = useState(0);\n const [scrollTop, setScrollTop] = useState(0);\n const [focusedRow, setFocusedRow] = useState<number | null>(null);\n useEffect(() => {\n if (!host.current) return;\n // Measure the slot itself (content box: inside the inset and the scrollbar); sheets and sidebars change it.\n const observer = new ResizeObserver(([entry]) => setWidth(entry?.contentRect.width ?? 0));\n observer.observe(host.current);\n return () => observer.disconnect();\n }, []);\n useLayoutEffect(() => {\n // Every row is one tile high (the caption block is reserved), so any mounted row gives the real stride.\n const rendered = host.current?.querySelector<HTMLElement>(\".hjm-image-choice__row\")?.offsetHeight ?? 0;\n if (rendered > 0 && rendered !== measuredTileHeight) setMeasuredTileHeight(rendered);\n });\n // Preserve empty columns so a short list fills the same slots as a full row (Diairy, 2026-10-27).\n const columns = width > 0 ? resolveImageChoiceColumns(width) : 0;\n const stride = (measuredTileHeight || estimatedTileHeight) + imageChoiceRecipe.gap;\n const rowCount = columns ? Math.ceil(items.length / columns) : 0;\n const range = resolveVirtualWindow(rowCount, stride, height, scrollTop, overscan);\n // Keep the focused tile's row mounted when scrolling moves it out of the window, so focus is not dropped to body.\n const rows = Array.from({ length: range.end - range.start }, (_, i) => range.start + i);\n if (focusedRow !== null && focusedRow < rowCount && !rows.includes(focusedRow)) rows.push(focusedRow);\n rows.sort((first, second) => first - second); // DOM order is the tab order\n return <div ref={host} className=\"hjm-image-choice__viewport\" role={role} aria-label={label} style={{ height }}\n onScroll={event => setScrollTop(event.currentTarget.scrollTop)}>\n {columns ? <div style={{ position: \"relative\", height: Math.max(0, range.totalHeight - imageChoiceRecipe.gap) }}>\n {rows.map(row => <div key={row} className=\"hjm-image-choice__row\" style={{ top: row * stride, display: \"grid\", gridTemplateColumns: `repeat(${columns}, ${resolveImageChoiceOuterSize()}px)`, justifyContent: \"space-between\" }}\n onFocusCapture={() => setFocusedRow(row)} onBlurCapture={event => { if (!event.currentTarget.contains(event.relatedTarget)) setFocusedRow(current => current === row ? null : current); }}>\n {items.slice(row * columns, row * columns + columns).map((item, offset) => renderTile(item, row * columns + offset))}\n </div>)}\n </div> : null}\n </div>;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"overlays.d.ts","sourceRoot":"","sources":["../src/overlays.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAwB,KAAK,gBAAgB,EAAE,MAAM,yCAAyC,CAAC;AACtG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4CAA4C,CAAC;AAIjF,OAAO,EAKL,KAAK,2BAA2B,EAChC,KAAK,kBAAkB,EAExB,MAAM,iDAAiD,CAAC;AACzD,OAAO,EAML,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,sBAAsB,EAC5B,MAAM,0CAA0C,CAAC;AAClD,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,wBAAwB,EAC7B,KAAK,gBAAgB,EACtB,MAAM,4CAA4C,CAAC;AACpD,OAAO,EAGL,WAAW,EAGX,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACrB,KAAK,YAAY,EAClB,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,mCAAmC,CAAC;AAC/E,OAAO,EAUL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAKf,OAAO,EAOL,KAAK,cAAc,EACnB,KAAK,SAAS,EACd,KAAK,cAAc,EACpB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAEtE,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,MAAM,sBAAsB,GAC9B,SAAS,GACT,cAAc,GACd,QAAQ,GACR,SAAS,CAAC;AAGd,MAAM,MAAM,WAAW,GAAG,cAAc,CAAC,QAAQ,CAAC;IAAE,MAAM,EAAE,sBAAsB,CAAA;CAAE,CAAC,CAAC,GACpF,QAAQ,CAAC;IACP,KAAK,EAAE,SAAS,CAAC;IACjB,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB;;;;OAIG;IACH,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,gGAAgG;IAChG,YAAY,CAAC,EAAE,cAAc,CAAC;IAC9B,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,sDAAsD;IACtD,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACtD;;;;OAIG;IACH,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC,cAAc,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACrD;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC;QAAE,MAAM,EAAE,OAAO,CAAC,sBAAsB,EAAE,SAAS,CAAC,GAAG,cAAc,CAAA;KAAE,CAAC,KAAK,IAAI,CAAC;IACxH,mFAAmF;IACnF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,WAAW,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC,CAAC;AAEL,eAAO,MAAM,MAAM,wGAsKjB,CAAC;AAGH,MAAM,MAAM,gBAAgB,GAAG,cAAc,CAC3C,QAAQ,CAAC;IAAE,MAAM,EAAE,2BAA2B,CAAA;CAAE,CAAC,CAClD,GACC,QAAQ,CAAC;IACP,OAAO,EAAE,kBAAkB,CAAC;IAC5B,oFAAoF;IACpF,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,cAAc,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACrD,mFAAmF;IACnF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,WAAW,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC,CAAC;AAEL,eAAO,MAAM,WAAW,6GA4KvB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,OAAO,GAAG,KAAK,CAAC;AAExD,MAAM,MAAM,SAAS,GAAG,MAAM,OAAO,WAAW,CAAC,KAAK,CAAC;AAGvD,MAAM,MAAM,UAAU,GAAG,cAAc,CAAC,sBAAsB,CAAC,GAC7D,QAAQ,CAAC;IACP,KAAK,EAAE,SAAS,CAAC;IACjB,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B;;;;;OAKG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB;;;;OAIG;IACH,OAAO,CAAC,EAAE,SAAS,WAAW,EAAE,CAAC;IACjC,YAAY,CAAC,EAAE,WAAW,CAAC;IAC3B,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,CAAC;IAC/C,+EAA+E;IAC/E,YAAY,CAAC,EAAE,QAAQ,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9D,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,aAAa,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAC5C,6GAA6G;IAC7G,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,sDAAsD;IACtD,UAAU,EAAE,MAAM,CAAC;IACnB,8FAA8F;IAC9F,eAAe,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACtD;;;OAGG;IACH,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC,cAAc,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACrD,6EAA6E;IAC7E,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC;QAAE,MAAM,EAAE,kBAAkB,CAAA;KAAE,CAAC,KAAK,IAAI,CAAC;IAC/E,mFAAmF;IACnF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,WAAW,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uGAAuG;IACvG,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC,CAAC;AAEL,eAAO,MAAM,KAAK,uGA8RhB,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,SAAS,CAAC,wBAAwB,CAAC,GAC5D,QAAQ,CAAC;IACP,OAAO,EAAE,cAAc,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,WAAW,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gGAAgG;IAChG,WAAW,CAAC,EAAE,uBAAuB,CAAC;CACvC,CAAC,CAAC;AAEL,eAAO,MAAM,OAAO,0GA2OlB,CAAC;AAEH,MAAM,MAAM,QAAQ,GAAG,QAAQ,CAAC;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,SAAS,CAAC;IACjB,2DAA2D;IAC3D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC,CAAC;AAEH,MAAM,MAAM,WAAW,GAAG,IAAI,CAAC,qBAAqB,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,GAC5E,QAAQ,CAAC;IAAE,KAAK,EAAE,SAAS,QAAQ,EAAE,CAAA;CAAE,CAAC,CAAC;AAE3C,MAAM,MAAM,oBAAoB,GAC5B,SAAS,GACT,WAAW,GACX,QAAQ,GACR,SAAS,GACT,KAAK,GACL,cAAc,CAAC;AAEnB,MAAM,MAAM,cAAc,GACtB,QAAQ,CAAC;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,GAC5B,QAAQ,CAAC;IACP,MAAM,EAAE,SAAS,GAAG,aAAa,GAAG,OAAO,GAAG,OAAO,CAAC;IACtD,OAAO,EAAE,SAAS,CAAC;CACpB,CAAC,CAAC;AAEP,KAAK,mBAAmB,GAAG,QAAQ,CAAC;IAClC,aAAa,CAAC,EAAE,QAAQ,CAAC;IACzB,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,YAAY,CAAC,EAAE,KAAK,CAAC;IACrB,aAAa,CAAC,EAAE,KAAK,CAAC;CACvB,CAAC,CAAC;AAEH,KAAK,mBAAmB,GACpB,QAAQ,CAAC;IACP,aAAa,EAAE,QAAQ,CAAC;IACxB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,YAAY,CAAC,EAAE,KAAK,CAAC;IACrB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC,CAAC,GACF,QAAQ,CAAC;IACP,aAAa,EAAE,QAAQ,CAAC;IACxB,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC,CAAC,CAAC;AAEP,KAAK,qBAAqB,GACtB,QAAQ,CAAC;IACP,aAAa,EAAE,UAAU,CAAC;IAC1B,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC3B,YAAY,CAAC,EAAE,KAAK,CAAC;IACrB,aAAa,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;CACjD,CAAC,GACF,QAAQ,CAAC;IACP,aAAa,EAAE,UAAU,CAAC;IAC1B,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,YAAY,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACnC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC;CACtD,CAAC,CAAC;AAEP,KAAK,eAAe,GAChB,QAAQ,CAAC;IACP,KAAK,EAAE,SAAS,QAAQ,EAAE,CAAC;IAC3B,QAAQ,CAAC,EAAE,KAAK,CAAC;CAClB,CAAC,GACF,QAAQ,CAAC;IACP,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,QAAQ,EAAE,SAAS,WAAW,EAAE,CAAC;CAClC,CAAC,CAAC;AAEP,KAAK,aAAa,GAAG,QAAQ,CAAC;IAC5B,OAAO,EAAE,cAAc,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC,yEAAyE;IACzE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sEAAsE;IACtE,gBAAgB,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;IAChD,2EAA2E;IAC3E,KAAK,CAAC,EAAE,OAAO,GAAG,KAAK,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,yDAAyD;IACzD,oBAAoB,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,eAAe,CAAC,EAAE,WAAW,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2GAA2G;IAC3G,WAAW,CAAC,EAAE,uBAAuB,CAAC;CACvC,CAAC,GAAG,eAAe,CAAC;AAErB,MAAM,MAAM,SAAS,GAAG,SAAS,CAAC,QAAQ,CAAC;IAAE,MAAM,EAAE,oBAAoB,CAAA;CAAE,CAAC,CAAC,GAC3E,aAAa,GACb,CAAC,mBAAmB,GAAG,mBAAmB,GAAG,qBAAqB,CAAC,CAAC;AA8CtE,eAAO,MAAM,IAAI,sGAqZf,CAAC;AAEH,MAAM,MAAM,gCAAgC,GAAG,aAAa,GAAG,QAAQ,CAAC;AAGxE,MAAM,MAAM,qBAAqB,GAAG,QAAQ,CAAC;IAC3C,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC;QAAE,MAAM,EAAE,gCAAgC,CAAA;KAAE,CAAC,KAAK,IAAI,CAAC;IACtG,qDAAqD;IACrD,KAAK,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,SAAS,EAAE,MAAM,CAAC;IAClB,mFAAmF;IACnF,QAAQ,EAAE,SAAS,CAAC;IACpB,kEAAkE;IAClE,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,qEAAqE;IACrE,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,mCAAmC;IACnC,eAAe,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACtD,cAAc,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACrD,mFAAmF;IACnF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,WAAW,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB;UA3BrB,OAAO;kBACC,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC;QAAE,MAAM,EAAE,gCAAgC,CAAA;KAAE,CAAC,KAAK,IAAI;IACrG,qDAAqD;WAC9C,MAAM;IACb,kDAAkD;eACvC,MAAM;IACjB,mFAAmF;cACzE,SAAS;IACnB,kEAAkE;YAC1D,SAAS;IACjB,qEAAqE;aAC5D,SAAS;IAClB,mCAAmC;sBACjB,KAAK,CAAC,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC;qBACpC,KAAK,CAAC,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC;IACpD,mFAAmF;oBACnE,MAAM;sBACJ,WAAW;gBACjB,MAAM;mDA6DlB,CAAC"}
1
+ {"version":3,"file":"overlays.d.ts","sourceRoot":"","sources":["../src/overlays.tsx"],"names":[],"mappings":"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"}
package/dist/overlays.js CHANGED
@@ -202,7 +202,7 @@ export const Sheet = forwardRef(function Sheet({ trigger, title, description, ch
202
202
  });
203
203
  const policy = resolveSheetDismissPolicy(placement, dismissPolicy);
204
204
  const closeVisible = resolveSheetCloseButton(placement, showClose);
205
- // Drag-to-dismiss (2026-10-16 user decision): handle and header follow the pointer downward and a
205
+ // Drag-to-dismiss (2026-10-16 user decision; body touch pull-downs joined 2026-10-27): the sheet follows the pointer downward and a
206
206
  // release past the distance or velocity threshold closes with reason "swipe".
207
207
  const showHandle = (placement === "bottom" || swipeDirection === "down") && policy.dismissible && policy.swipeDismiss;
208
208
  const closeBlocked = busy && !policy.dismissWhileBusy;
@@ -225,6 +225,84 @@ export const Sheet = forwardRef(function Sheet({ trigger, title, description, ch
225
225
  suppressDragClick.current = false;
226
226
  setDragY(0);
227
227
  }, [open, canSwipe]);
228
+ // State, not a ref: the portal mounts the body after this component's first commit.
229
+ const [bodyElement, setBodyElement] = useState(null);
230
+ const requestCloseRef = useRef(() => undefined);
231
+ useEffect(() => {
232
+ // Body drag-to-dismiss (2026-10-27 user decision): a touch pull-down on the body closes like the handle,
233
+ // but only while the body, and any scroller between the finger and it, is already at the top. Otherwise
234
+ // the same pull scrolls the content back first. Touch only: a mouse keeps text selection and uses the handle.
235
+ // Native listeners because React's touch handlers are passive and cannot stop the browser's own pan.
236
+ const body = bodyElement;
237
+ if (!open || !canSwipe || !body)
238
+ return undefined;
239
+ let drag = null;
240
+ const atTop = (target) => {
241
+ for (let node = target instanceof Element ? target : null; node && body.contains(node); node = node.parentElement) {
242
+ if (node.scrollTop > 0)
243
+ return false;
244
+ if (node === body)
245
+ break;
246
+ }
247
+ return body.scrollTop <= 0;
248
+ };
249
+ const onStart = (event) => {
250
+ const touch = event.touches[0];
251
+ const editable = event.target instanceof Element && event.target.closest("input,textarea,select,[contenteditable]:not([contenteditable='false'])");
252
+ drag = event.touches.length !== 1 || !touch || editable || !atTop(event.target)
253
+ ? null
254
+ : { x: touch.clientX, y: touch.clientY, t: event.timeStamp, state: "pending" };
255
+ };
256
+ const onMove = (event) => {
257
+ const touch = event.touches[0];
258
+ if (!drag || !touch || drag.state === "rejected")
259
+ return;
260
+ const dy = touch.clientY - drag.y;
261
+ const dx = Math.abs(touch.clientX - drag.x);
262
+ if (drag.state === "pending") {
263
+ // Upward or sideways movement belongs to scrolling, rails and swipe rows inside the body.
264
+ if (dy < 0 || dx > dy) {
265
+ if (dy < 0 || dx > sheetDragRecipe.activationDistance)
266
+ drag.state = "rejected";
267
+ return;
268
+ }
269
+ // Hold the browser's pan from the first downward move so the pull is still cancelable once it activates.
270
+ if (event.cancelable)
271
+ event.preventDefault();
272
+ if (dy < sheetDragRecipe.activationDistance)
273
+ return;
274
+ drag.state = "dragging";
275
+ }
276
+ if (event.cancelable)
277
+ event.preventDefault();
278
+ setDragY(Math.max(0, dy));
279
+ };
280
+ const onEnd = (event) => {
281
+ const current = drag;
282
+ drag = null;
283
+ if (current?.state !== "dragging")
284
+ return;
285
+ setDragY(0);
286
+ const touch = event.changedTouches[0];
287
+ if (event.type === "touchcancel" || !touch)
288
+ return;
289
+ const dy = touch.clientY - current.y;
290
+ const velocity = dy / Math.max(1, event.timeStamp - current.t);
291
+ const height = contentRef.current?.getBoundingClientRect().height ?? 0;
292
+ if (dy > height * sheetDragRecipe.dismissDistanceRatio || velocity > sheetDragRecipe.dismissVelocity)
293
+ requestCloseRef.current("swipe");
294
+ };
295
+ body.addEventListener("touchstart", onStart, { passive: true });
296
+ body.addEventListener("touchmove", onMove, { passive: false });
297
+ body.addEventListener("touchend", onEnd);
298
+ body.addEventListener("touchcancel", onEnd);
299
+ return () => {
300
+ body.removeEventListener("touchstart", onStart);
301
+ body.removeEventListener("touchmove", onMove);
302
+ body.removeEventListener("touchend", onEnd);
303
+ body.removeEventListener("touchcancel", onEnd);
304
+ };
305
+ }, [open, canSwipe, bodyElement]);
228
306
  const lifecycleRef = useRef(createSheetLifecycle(open));
229
307
  const previousOpenRef = useRef(open);
230
308
  const currentOpenRef = useRef(open);
@@ -261,6 +339,7 @@ export const Sheet = forwardRef(function Sheet({ trigger, title, description, ch
261
339
  });
262
340
  }
263
341
  };
342
+ requestCloseRef.current = requestClose;
264
343
  useEffect(() => {
265
344
  if (open) {
266
345
  lifecycleRef.current.open();
@@ -355,7 +434,7 @@ export const Sheet = forwardRef(function Sheet({ trigger, title, description, ch
355
434
  onDetentChange?.(next);
356
435
  }, children: _jsx("span", { "aria-hidden": "true", className: "hjm-sheet__handle-bar" }) }) })) : showHandle ? (
357
436
  // Keep one close command: an explicit × owns it; otherwise the handle supports keyboard and switch users.
358
- _jsx("div", { className: "hjm-sheet__handle-row", children: closeVisible ? (_jsx("div", { className: "hjm-sheet__handle", "aria-hidden": "true", children: _jsx("span", { className: "hjm-sheet__handle-bar" }) })) : (_jsx("button", { type: "button", className: "hjm-sheet__handle", "aria-label": closeLabel, disabled: closeBlocked, onClick: () => requestClose("close-action"), children: _jsx("span", { "aria-hidden": "true", className: "hjm-sheet__handle-bar" }) })) })) : null, description || (policy.dismissible && closeVisible) ? _jsxs("header", { className: "hjm-sheet__header", children: [description ? _jsx("div", { children: _jsx("p", { id: descriptionId, className: "hjm-sheet__description", children: description }) }) : null, policy.dismissible && closeVisible ? (_jsx("button", { type: "button", className: "hjm-dialog__close", "aria-label": closeLabel, disabled: busy && !policy.dismissWhileBusy, onClick: () => requestClose("close-action"), children: "\u00D7" })) : null] }) : null] }), children ? _jsx("div", { className: "hjm-sheet__body", children: _jsx(ScreenFooterContext.Provider, { value: null, children: _jsx(InitialFocusProvider, { scope: focusScope, children: children }) }) }) : null, footer ? _jsx("footer", { className: "hjm-sheet__footer", children: _jsx(ScreenFooterContext.Provider, { value: { bottomPadding: placement === "bottom" ? "calc(var(--hjm-space-sm) + env(safe-area-inset-bottom, 0px))" : "var(--hjm-space-sm)" }, children: _jsx(InitialFocusProvider, { scope: focusScope, children: footer }) }) }) : null] }) }) })) : null] }));
437
+ _jsx("div", { className: "hjm-sheet__handle-row", children: closeVisible ? (_jsx("div", { className: "hjm-sheet__handle", "aria-hidden": "true", children: _jsx("span", { className: "hjm-sheet__handle-bar" }) })) : (_jsx("button", { type: "button", className: "hjm-sheet__handle", "aria-label": closeLabel, disabled: closeBlocked, onClick: () => requestClose("close-action"), children: _jsx("span", { "aria-hidden": "true", className: "hjm-sheet__handle-bar" }) })) })) : null, description || (policy.dismissible && closeVisible) ? _jsxs("header", { className: "hjm-sheet__header", children: [description ? _jsx("div", { children: _jsx("p", { id: descriptionId, className: "hjm-sheet__description", children: description }) }) : null, policy.dismissible && closeVisible ? (_jsx("button", { type: "button", className: "hjm-dialog__close", "aria-label": closeLabel, disabled: busy && !policy.dismissWhileBusy, onClick: () => requestClose("close-action"), children: "\u00D7" })) : null] }) : null] }), children ? _jsx("div", { ref: setBodyElement, className: "hjm-sheet__body", children: _jsx(ScreenFooterContext.Provider, { value: null, children: _jsx(InitialFocusProvider, { scope: focusScope, children: children }) }) }) : null, footer ? _jsx("footer", { className: "hjm-sheet__footer", children: _jsx(ScreenFooterContext.Provider, { value: { bottomPadding: placement === "bottom" ? "calc(var(--hjm-space-sm) + env(safe-area-inset-bottom, 0px))" : "var(--hjm-space-sm)" }, children: _jsx(InitialFocusProvider, { scope: focusScope, children: footer }) }) }) : null] }) }) })) : null] }));
359
438
  });
360
439
  export const Tooltip = forwardRef(function Tooltip({ trigger, content, placement, align, pointerOpenDelayMs = tooltipBehaviorDefaults.pointerOpenDelayMs, focusOpenDelayMs = tooltipBehaviorDefaults.focusOpenDelayMs, portalContainer, open: openProp, defaultOpen, onOpenChange, className, layoutStyle, }, ref) {
361
440
  const descriptor = resolveTooltipDescriptor({