import { Close, Search } from '@mui/icons-material' import { Box, Button, Checkbox, Dialog, DialogActions, DialogContent, DialogTitle, Divider, IconButton, InputBase, List, ListItemButton, ListItemIcon, ListItemText, Typography, type SxProps, type Theme, } from '@mui/material' import { useId, useState, type JSX } from 'react' import type { LassoLayerFiltersUIProps } from './types' const titleSx = { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 1, p: 2, } satisfies SxProps // One rounded group on the grey "default" background, holding the search row, // a divider, and the scrollable list (Figma node 5960-14780). const groupSx = { borderRadius: ({ spacing }) => spacing(0.5), overflow: 'hidden', backgroundColor: ({ palette }) => palette.background.default, } satisfies SxProps const searchSx = { display: 'flex', alignItems: 'center', gap: 1, height: 32, px: 1.5, color: ({ palette }) => palette.text.secondary, } satisfies SxProps const rowSx = { px: 2, py: 0.5, } satisfies SxProps const checkboxSlotSx = { minWidth: 0, mr: 1, } satisfies SxProps /** * "Layer filters" dialog: choose which map layers the lasso mask clips. The * selection is a draft committed on Apply (Cancel/close discards it). Wire it * to the lasso store via `LassoTools.LayerFilters` in `@carto/ps-react-maps`, * and open it from the drawing-tool menu's "Layer filters" item * (`LassoToolsUI` `onLayerFilters`). */ export function LassoLayerFiltersUI({ open, onClose, layers, onApply, labels, DialogProps, }: LassoLayerFiltersUIProps): JSX.Element { const labelId = useId() const [draft, setDraft] = useState>(() => new Set()) const [search, setSearch] = useState('') // Reset the draft from the committed selection when the dialog opens, by // adjusting state during render (the React-recommended pattern — no effect, // and later `layers` changes don't discard in-progress edits). const [wasOpen, setWasOpen] = useState(false) if (open !== wasOpen) { setWasOpen(open) if (open) { setDraft(new Set(layers.filter((l) => l.checked).map((l) => l.id))) setSearch('') } } const title = labels?.title ?? 'Layer Filters' const description = labels?.description ?? 'Select the layers you want to filter with this polygon' const allSelectedLabel = labels?.allSelected ?? 'All selected' const empty = labels?.empty ?? 'No layers to filter' const allChecked = layers.length > 0 && layers.every((l) => draft.has(l.id)) const someChecked = layers.some((l) => draft.has(l.id)) const toggle = (id: string) => setDraft((prev) => { const next = new Set(prev) if (next.has(id)) next.delete(id) else next.add(id) return next }) const toggleAll = () => setDraft(allChecked ? new Set() : new Set(layers.map((l) => l.id))) const handleApply = () => { // Emit ids in `layers` order (stable, diff-friendly), not Set-insertion order. onApply(layers.filter((l) => draft.has(l.id)).map((l) => l.id)) onClose() } const normalized = search.trim().toLowerCase() const filtered = normalized ? layers.filter((l) => l.label.toLowerCase().includes(normalized)) : layers return ( {title} {description} setSearch(e.target.value)} sx={{ fontSize: 13, color: 'text.primary' }} /> {/* "All selected" master row only makes sense over the full list — hide it while a search is narrowing the results. */} {!normalized && ( <> )} {filtered.length === 0 ? ( {empty} ) : ( filtered.map((layer) => ( toggle(layer.id)} > )) )} ) }