import { useState } from 'react'
import {
Box,
IconButton,
InputAdornment,
MenuItem,
OutlinedInput,
Slider,
Typography,
} from '@mui/material'
import { Close as CloseIcon } from '@mui/icons-material'
import { Tooltip } from '../../../components/tooltip/tooltip'
import { useLegendId, useLegendShallow, useLegendStore } from '../../stores'
import { useLegendConfig } from '../../provider'
import { useLegendRow } from '../contexts'
import { styles } from './styles'
const clampPercent = (value: number): number =>
Math.min(100, Math.max(0, Math.round(value)))
/**
* Opacity menu item — toggles the row's inline bar; self-hides when hidden or no `opacityControl`.
*
* @experimental This API is new and may change in a future release.
*/
function OpacityItem() {
const id = useLegendId()
const row = useLegendRow()
const layer = useLegendShallow(id, (s) =>
row ? s.layers[row.layerId] : undefined,
)
const setCollapsed = useLegendShallow(id, (s) => s.setCollapsed)
const { labels } = useLegendConfig()
if (!row || !layer?.visible || !layer.opacityControl) return null
const toggle = () => {
const next = !row.opacityOpen
row.setOpacityOpen(next)
if (next && layer.collapsed) setCollapsed(row.layerId, false)
}
return (
)
}
/**
* Inline opacity bar (first body child of `Legend.Row`). Slider commits on release; input commits live.
*
* @experimental This API is new and may change in a future release.
*/
function OpacityInline() {
const id = useLegendId()
const row = useLegendRow()
const layerId = row?.layerId
const opacity = useLegendStore(id, (s) =>
layerId ? (s.layers[layerId]?.opacity ?? 1) : 1,
)
const setOpacity = useLegendStore(id, (s) => s.setOpacity)
const { labels } = useLegendConfig()
const [dragPercent, setDragPercent] = useState(null)
const [draft, setDraft] = useState(null)
if (!row || !layerId || !row.opacityOpen) return null
const percent = dragPercent ?? clampPercent(opacity * 100)
const commitPercent = (value: number): number => {
const clamped = clampPercent(value)
setOpacity(layerId, clamped / 100)
return clamped
}
const handleInputChange = (raw: string) => {
setDraft(raw)
if (raw.trim() === '') return
const parsed = Number(raw)
if (!Number.isFinite(parsed)) return
const clamped = commitPercent(parsed)
if (clamped !== parsed) setDraft(String(clamped))
}
const handleClose = () => {
row.setOpacityOpen(false)
setDraft(null)
setDragPercent(null)
}
return (
{labels.opacity}
{
setDragPercent(value as number)
setDraft(null)
}}
onChangeCommitted={(_, value) => {
commitPercent(value as number)
setDragPercent(null)
}}
aria-label={labels.opacity}
sx={styles.slider}
/>
handleInputChange(event.target.value)}
onBlur={() => setDraft(null)}
onKeyDown={(event) => {
if (event.key === 'Enter') (event.target as HTMLElement).blur()
}}
endAdornment={%}
inputProps={{ inputMode: 'numeric', 'aria-label': labels.opacity }}
sx={styles.input}
/>
)
}
/**
* Compound opacity control: `Item` in the row menu, `Inline` in the body.
*
* @experimental This API is new and may change in a future release.
*/
export const LegendOpacity = {
Item: OpacityItem,
Inline: OpacityInline,
}