feat(client): localize composer hints and rework input command interaction
Unify the /plan claimed hint with the plan placeholder through a locale namespace, localize slash menu group titles, replace the PermissionSelect native select with the Menu primitive, add a goal pause verb chain, clamp anchored popups to the viewport with scroll-into-view and outside-dismiss, and fix onPasteUpgrade insertedRange to account for the chip trailing gap.
This commit is contained in:
38
packages/client/ui-primitives/src/useAnchoredMaxHeight.ts
Normal file
38
packages/client/ui-primitives/src/useAnchoredMaxHeight.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Viewport-fit hook for bottom-anchored overlays (slash menu, popupSelect):
|
||||
* the element's bottom edge is laid out independent of its height, so it
|
||||
* grows upward and only the top edge can collide with the viewport — clamp
|
||||
* the design cap to the space between that edge and the viewport top.
|
||||
*/
|
||||
import { useLayoutEffect, useState } from 'react'
|
||||
import type { RefObject } from 'react'
|
||||
|
||||
/** Safe distance kept between the overlay and the viewport top edge (mirrors the Menu portal margin). */
|
||||
const MARGIN = 12
|
||||
|
||||
/**
|
||||
* Clamp a bottom-anchored overlay's max-height to the viewport.
|
||||
* @param ref - the overlay element; a null current (overlay closed) skips measuring.
|
||||
* @param cap - design max-height in px (the clamp never exceeds it).
|
||||
* @param signal - re-measure trigger: pass the overlay's render state so anchor
|
||||
* moves (composer growth) re-fit; resize/scroll re-fit while mounted.
|
||||
* @returns the max-height to apply inline, in px.
|
||||
*/
|
||||
export function useAnchoredMaxHeight(ref: RefObject<HTMLElement>, cap: number, signal: unknown): number {
|
||||
const [maxHeight, setMaxHeight] = useState(cap)
|
||||
useLayoutEffect(() => {
|
||||
const el = ref.current
|
||||
if (el === null) return
|
||||
const fit = () => {
|
||||
setMaxHeight(Math.min(cap, Math.max(0, el.getBoundingClientRect().bottom - MARGIN)))
|
||||
}
|
||||
fit()
|
||||
window.addEventListener('resize', fit)
|
||||
window.addEventListener('scroll', fit, true)
|
||||
return () => {
|
||||
window.removeEventListener('resize', fit)
|
||||
window.removeEventListener('scroll', fit, true)
|
||||
}
|
||||
}, [ref, cap, signal])
|
||||
return maxHeight
|
||||
}
|
||||
Reference in New Issue
Block a user