diff --git a/packages/toolbar/src/components/block-editor.tsx b/packages/toolbar/src/components/block-editor.tsx index e941d7a..137bb74 100644 --- a/packages/toolbar/src/components/block-editor.tsx +++ b/packages/toolbar/src/components/block-editor.tsx @@ -27,6 +27,7 @@ export function BlockEditor({ const widgetType = WIDGET_TYPES[type]; const overrides = widgetType?.fieldOverrides ?? {}; const paramLabels = widgetType?.paramLabels ?? {}; + const paramDescriptions = widgetType?.paramDescriptions ?? {}; const paramKeys = widgetType?.fieldOrder ? widgetType.fieldOrder.filter((key) => { @@ -84,6 +85,7 @@ export function BlockEditor({ { return onParameterChange(key, text); @@ -98,6 +100,7 @@ export function BlockEditor({ { return onParameterChange(key, checked); @@ -172,6 +175,7 @@ export function BlockEditor({ + + + + + + + ); +} diff --git a/packages/toolbar/src/components/fields/object-field.tsx b/packages/toolbar/src/components/fields/object-field.tsx index 2a26030..94b0a44 100644 --- a/packages/toolbar/src/components/fields/object-field.tsx +++ b/packages/toolbar/src/components/fields/object-field.tsx @@ -1,5 +1,6 @@ import { useId, useState } from 'preact/hooks'; +import { InfoTooltip } from './info-tooltip'; import { CollapsibleContent, CollapsibleTrigger } from '../ui/collapsible'; import { Label } from '../ui/label'; import { Switch } from '../ui/switch'; @@ -7,6 +8,7 @@ import { TextField } from './text-field'; type ObjectFieldProps = { label: string; + description?: string; enabled: boolean; value: Record; defaultValue: Record; @@ -18,6 +20,7 @@ type ObjectFieldProps = { export function ObjectField({ label, + description, enabled, value, defaultValue, @@ -32,7 +35,10 @@ export function ObjectField({ return (
- + void; }; -export function SwitchField({ label, checked, onToggle }: SwitchFieldProps) { +export function SwitchField({ + label, + description, + checked, + onToggle, +}: SwitchFieldProps) { const id = useId(); return (
- +
); diff --git a/packages/toolbar/src/components/fields/text-field.tsx b/packages/toolbar/src/components/fields/text-field.tsx index a934bd0..1b11794 100644 --- a/packages/toolbar/src/components/fields/text-field.tsx +++ b/packages/toolbar/src/components/fields/text-field.tsx @@ -1,10 +1,12 @@ import { useId } from 'preact/hooks'; +import { InfoTooltip } from './info-tooltip'; import { Input } from '../ui/input'; import { Label } from '../ui/label'; type TextFieldProps = { label: string; + description?: string; value: string; placeholder?: string; onInput: (value: string) => void; @@ -13,6 +15,7 @@ type TextFieldProps = { export function TextField({ label, + description, value, placeholder, onInput, @@ -22,7 +25,10 @@ export function TextField({ return (
- + (null); + const triggerRef = useRef(null); + const tooltipRef = useRef(null); + + const reposition = useCallback(() => { + const trigger = triggerRef.current; + const tooltip = tooltipRef.current; + if (!trigger || !tooltip) return; + + // Use the Shadow DOM host as the boundary for clamping. Falls back to + // the viewport width if rendered outside a Shadow DOM (e.g., in tests). + const root = trigger.getRootNode(); + const host = root instanceof ShadowRoot ? (root.host as HTMLElement) : null; + const hostRect = host?.getBoundingClientRect(); + const triggerRect = trigger.getBoundingClientRect(); + const tooltipWidth = tooltip.offsetWidth; + const tooltipHeight = tooltip.offsetHeight; + + const gap = 6; + const padding = 8; + const minLeft = (hostRect?.left ?? 0) + padding; + const maxRight = (hostRect?.right ?? window.innerWidth) - padding; + + // Center horizontally on trigger, clamp within host + const idealLeft = + triggerRect.left + triggerRect.width / 2 - tooltipWidth / 2; + const clampedLeft = Math.max( + minLeft, + Math.min(idealLeft, maxRight - tooltipWidth) + ); + + setPosition({ + top: triggerRect.top - tooltipHeight - gap, + left: clampedLeft, + }); + }, []); + + const show = useCallback(() => { + setOpen(true); + requestAnimationFrame(reposition); + }, [reposition]); + + const hide = useCallback(() => { + setOpen(false); + setPosition(null); + }, []); + + return ( + + + {open && ( + + {content} + + )} + + ); +} + +export { Tooltip };