import { getLayersAsFlatArray } from "mobility-toolbox-js/ol"; import { getUid } from "ol"; import { unByKey } from "ol/Observable"; import { memo, useEffect, useMemo, useState } from "preact/compat"; import LayerTree from "../LayerTree"; import { SelectionType } from "../LayerTree/TreeItem"; import { LAYER_TREE_HIDE_PROP, LAYER_TREE_ORDER, LAYER_TREE_TITLE_FUNC_PROP, } from "../utils/constants"; import useLayersConfig from "../utils/hooks/useLayersConfig"; import useMapContext from "../utils/hooks/useMapContext"; import type { Group } from "ol/layer"; import type BaseLayer from "ol/layer/Base"; import type { HTMLAttributes, PreactDOMAttributes } from "preact"; import type { LayerTreeProps } from "../LayerTree/LayerTree"; import type { LayerConfig } from "../utils/hooks/useLayersConfig"; export interface LayerTreeConfig { childItems: LayerTreeConfig[]; id: string; isControlChecked: boolean; layer: BaseLayer; revision: number; selectionType: SelectionType; title: string; } export type LayerTreeMenuProps = { filter?: (layer: BaseLayer) => boolean; order?: string[]; } & HTMLAttributes & Partial & PreactDOMAttributes; const getConfigForLayer = ( layer: BaseLayer, revision: number, layersConfig: Record = {}, ): LayerTreeConfig => { const defaultTitle = layersConfig[layer.get("name")]?.title || ""; const customTitleFunction = layer.get(LAYER_TREE_TITLE_FUNC_PROP); let title = defaultTitle; if (typeof customTitleFunction === "function") { title = customTitleFunction(title); } return { childItems: (layer as Group) ?.getLayers?.() .getArray() .map((subLayer) => { return getConfigForLayer(subLayer, revision, layersConfig); }) || [], id: getUid(layer), isControlChecked: layer.getVisible(), layer, revision, selectionType: SelectionType.CHECKBOX, title, }; }; function LayerTreeMenu({ filter, order = LAYER_TREE_ORDER, ...props }: LayerTreeMenuProps) { const { map } = useMapContext(); const [revision, setRevision] = useState(0); const layersConfig = useLayersConfig(); const layers: LayerTreeConfig[] = useMemo(() => { const config = map ?.getLayers() .getArray() .filter( filter ?? ((layer) => { return ( (layer.get(LAYER_TREE_HIDE_PROP) || layersConfig[layer.get("name")]?.[LAYER_TREE_HIDE_PROP]) !== true ); }), ) .sort((a, b) => { if ( order && order.indexOf(a.get("name")) > order.indexOf(b.get("name")) ) { return 1; } else { return -1; } }) .map((layer) => { return getConfigForLayer(layer, revision, layersConfig); }) || []; return config; }, [filter, layersConfig, map, order, revision]); // Force update of config when a layers`s visibility changes progammatically useEffect(() => { const keys = []; const layerss = getLayersAsFlatArray(map.getLayers().getArray()); layerss.forEach((layer) => { const key = layer.on("change:visible", () => { setRevision((prev) => { return prev + 1; }); }); keys.push(key); }); keys.push( map.on("change:layergroup", () => { setRevision((prev) => { return prev + 1; }); }), map.getLayers().on("add", () => { setRevision((prev) => { return prev + 1; }); }), map.getLayers().on("remove", () => { setRevision((prev) => { return prev + 1; }); }), ); return () => { // Clean up all listeners unByKey(keys); }; }, [map]); return ; } export default memo(LayerTreeMenu);