diff --git a/apps/catalog/src/CatalogApp.tsx b/apps/catalog/src/CatalogApp.tsx index 891cf56..ca05cdf 100644 --- a/apps/catalog/src/CatalogApp.tsx +++ b/apps/catalog/src/CatalogApp.tsx @@ -216,7 +216,7 @@ const iconGroups: Array<{ title: string; note: string; icons: IconName[] }> = [ { title: "Состояние и доступ", note: "Вся платформа", - icons: ["check", "alert", "activity", "lock", "key", "shield", "circle", "eye", "eye-off"], + icons: ["check", "alert", "info", "activity", "lock", "key", "shield", "circle", "eye", "eye-off"], }, { title: "Сущности", @@ -257,6 +257,7 @@ const iconLabels: Record = { grid: "Обзор", image: "Изображение", inbox: "Уведомления", + info: "Информация", key: "Ключ", layers: "Слои", list: "Список задач", @@ -1294,6 +1295,8 @@ export function CatalogApp() { <> `${value}%`} onChange={setBrightness} /> +
`${value.toFixed(1).replace('.', ',')} м`} formatLimit={(value) => value.toFixed(1).replace('.', ',')} onChange={(value) => setBrightness((value + 1.2) / 52.7 * 100)} />
+
`${value.toFixed(1).replace('.', ',')} м`} formatLimit={(value) => value.toFixed(1).replace('.', ',')} onChange={(value) => setBrightness((value + 1.2) / 52.7 * 100)} />
`${value}%`} onChange={setGlowDistance} /> ), @@ -1345,6 +1348,14 @@ export function CatalogApp() { ({ ...option, action: { + label: `Удалить ${option.label}`, icon: , tone: 'danger' as const, + disabled: option.value === 'active', + onAction: () => setConfirmOpen(true), + } }))} /> + onChange(Number(event.target.value))} /> + {label} · {displayValue} + + + {formatLimit(max)} + {formatLimit(min)} + + ); + return (
{ description?: string; icon?: ReactNode; disabled?: boolean; + action?: { label: string; icon: ReactNode; disabled?: boolean; tone?: "neutral" | "danger"; onAction: () => void }; } export type SelectVariant = "integrated" | "split" | "inline"; @@ -52,7 +54,11 @@ export function Select({ const inspectorSplitRequired = useContext(InspectorSelectPolicyContext); const resolvedVariant: SelectVariant = inspectorSplitRequired ? "split" : variant; const [query, setQuery] = useState(""); + const triggerButton = useRef(null); const selected = options.find((option) => option.value === value) ?? options[0]; + // Row actions are siblings, never nested interactive listbox options. + const withActions = options.some((option) => option.action); + const popupRole = withActions ? "dialog" : "listbox"; const visibleOptions = useMemo(() => { const normalized = query.trim().toLocaleLowerCase(); if (!normalized) return options; @@ -66,10 +72,15 @@ export function Select({ minWidth={minMenuWidth} width={menuWidth} disabled={disabled} - surfaceRole="listbox" + surfaceRole={popupRole} surfaceClassName={cn(resolvedVariant === "split" && "nodedc-select__menu", menuClassName)} trigger={({ open, toggle, setAnchorRef, setTriggerRef, surfaceId }) => { + const handleClick = (event: MouseEvent) => { + triggerButton.current = event.currentTarget; + toggle(); + }; const handleKeyDown = (event: KeyboardEvent) => { + triggerButton.current = event.currentTarget; if (event.key === "ArrowDown" || event.key === "Enter" || event.key === " ") { event.preventDefault(); if (!open) toggle(); @@ -88,11 +99,11 @@ export function Select({ type="button" className="nodedc-select__toggle" aria-label={label} - aria-haspopup="listbox" + aria-haspopup={popupRole} aria-controls={surfaceId} aria-expanded={open} disabled={disabled} - onClick={toggle} + onClick={handleClick} onKeyDown={handleKeyDown} >