@evergis/react 4.0.74 → 4.0.78

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -4677,33 +4677,6 @@ const createConfigPage = (props) => {
4677
4677
  };
4678
4678
  };
4679
4679
 
4680
- const getAttributesConfiguration = (layer) => {
4681
- const layerAttributeConfiguration = layer?.configuration?.attributesConfiguration ?? {};
4682
- const { geometryAttribute, idAttribute } = layerAttributeConfiguration;
4683
- const emptyLayerDefinition = {
4684
- attributes: [],
4685
- geometryAttribute: geometryAttribute ?? GEOMETRY_ATTRIBUTE,
4686
- idAttribute: idAttribute ?? DEFAULT_ID_ATTRIBUTE_NAME,
4687
- titleAttribute: "",
4688
- };
4689
- if (!isLayerService(layer)) {
4690
- return emptyLayerDefinition;
4691
- }
4692
- return {
4693
- ...emptyLayerDefinition,
4694
- ...layerAttributeConfiguration,
4695
- attributes: layerAttributeConfiguration?.attributes ||
4696
- emptyLayerDefinition.attributes,
4697
- };
4698
- };
4699
-
4700
- const formatChartRelatedValue = (t, value, layerInfo, relatedAttributes) => {
4701
- const layerDefinition = getAttributesConfiguration(layerInfo);
4702
- const relatedAxis = relatedAttributes.find(({ chartAxis }) => chartAxis === "y");
4703
- const attribute = layerDefinition.attributes[relatedAxis.attributeName];
4704
- return attribute ? formatAttributeValue({ t, type: attribute.type, value, stringFormat: attribute.stringFormat }) : value;
4705
- };
4706
-
4707
4680
  const isEmptyValue = (value) => value === "" || value === null || value === undefined;
4708
4681
 
4709
4682
  const checkIsDateFormat = (value) => value.startsWith("#'") && value.endsWith("'");
@@ -4733,7 +4706,59 @@ const formatConditionValue = ({ value, attributeType, defaultValue, checkQuotes
4733
4706
  return checkQuotes && checkIfNeedQuotes(stringValue, attributeType) ? `'${stringValue}'` : stringValue;
4734
4707
  };
4735
4708
 
4709
+ /**
4710
+ * Объектное значение иерархического фильтра "tree" («уровень → массив id»),
4711
+ * в отличие от скалярных/массивных значений остальных фильтров.
4712
+ */
4713
+ const isTreeFilterValue = (value) => typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date);
4714
+ /**
4715
+ * Подставляет плейсхолдеры уровней `%name.l{N}` значениями id соответствующего уровня
4716
+ * (формат `[id1,id2,...]` для оператора IN). Уровни, отсутствующие в значении (или пустые),
4717
+ * не подставляются — плейсхолдер остаётся нетронутым, как и любой пустой `%`-плейсхолдер.
4718
+ */
4719
+ const applyTreeFilterToCondition = (condition, name, value, isSingle) => {
4720
+ const filterName = `${FILTER_PREFIX}${name}`;
4721
+ return Object.keys(value).reduce((result, level) => {
4722
+ const formatted = formatConditionValue({ value: value[level], checkQuotes: !isSingle });
4723
+ if (!formatted) {
4724
+ return result;
4725
+ }
4726
+ return result.replace(new RegExp(`${filterName}\\.${level}\\b`, "g"), formatted);
4727
+ }, condition);
4728
+ };
4729
+
4730
+ const getAttributesConfiguration = (layer) => {
4731
+ const layerAttributeConfiguration = layer?.configuration?.attributesConfiguration ?? {};
4732
+ const { geometryAttribute, idAttribute } = layerAttributeConfiguration;
4733
+ const emptyLayerDefinition = {
4734
+ attributes: [],
4735
+ geometryAttribute: geometryAttribute ?? GEOMETRY_ATTRIBUTE,
4736
+ idAttribute: idAttribute ?? DEFAULT_ID_ATTRIBUTE_NAME,
4737
+ titleAttribute: "",
4738
+ };
4739
+ if (!isLayerService(layer)) {
4740
+ return emptyLayerDefinition;
4741
+ }
4742
+ return {
4743
+ ...emptyLayerDefinition,
4744
+ ...layerAttributeConfiguration,
4745
+ attributes: layerAttributeConfiguration?.attributes ||
4746
+ emptyLayerDefinition.attributes,
4747
+ };
4748
+ };
4749
+
4750
+ const formatChartRelatedValue = (t, value, layerInfo, relatedAttributes) => {
4751
+ const layerDefinition = getAttributesConfiguration(layerInfo);
4752
+ const relatedAxis = relatedAttributes.find(({ chartAxis }) => chartAxis === "y");
4753
+ const attribute = layerDefinition.attributes[relatedAxis.attributeName];
4754
+ return attribute ? formatAttributeValue({ t, type: attribute.type, value, stringFormat: attribute.stringFormat }) : value;
4755
+ };
4756
+
4736
4757
  const applyFiltersToCondition = ({ condition, name, defaultValue, filters, isSingle, isSetParams, }) => {
4758
+ const rawValue = filters[name] !== undefined ? filters[name].value : defaultValue;
4759
+ if (isTreeFilterValue(rawValue)) {
4760
+ return applyTreeFilterToCondition(condition, name, rawValue, isSingle);
4761
+ }
4737
4762
  const hasFilter = filters[name] !== undefined && !!filters[name].value?.toString().length;
4738
4763
  const min = formatConditionValue({
4739
4764
  value: hasFilter ? filters[name].min : null,
@@ -11343,7 +11368,7 @@ const ChipsFilter = ({ type, filter, elementConfig, }) => {
11343
11368
  icon: chipIcon,
11344
11369
  };
11345
11370
  });
11346
- }, [configFilter, dataSources, colorAttribute, iconAttribute, theme]);
11371
+ }, [variants, configFilter, dataSources, iconAttribute, colorAttribute, theme]);
11347
11372
  const isChipActive = React.useCallback((chipValue) => {
11348
11373
  const currentValue = filters?.[filterName]?.value;
11349
11374
  if (currentValue === undefined) {
@@ -11419,6 +11444,373 @@ function getIconFromAttribute(option, iconAttribute) {
11419
11444
  return getResourceUrl(iconValue);
11420
11445
  }
11421
11446
 
11447
+ const MAX_ANCESTOR_DEPTH = 50;
11448
+ const buildLevelOneCondition = (attributeLevel) => `${attributeLevel} = 1`;
11449
+ const buildChildrenCondition = (attributeParentName, parentName) => `${attributeParentName} = ${formatConditionValue({ value: parentName })}`;
11450
+ const buildSearchCondition = (attributeAlias, query) => `${attributeAlias} LIKE '%${query.replace(/'/g, "''")}%'`;
11451
+ /** Запрос пачки записей по их именам (`attributeName`) — для достройки предков. */
11452
+ const buildNamesCondition = (attributeName, names) => `${attributeName} in ${formatConditionValue({ value: names })}`;
11453
+ /** Запрос пачки записей по их значениям (`attributeValue`) — для восстановления `defaultValue`. */
11454
+ const buildValuesCondition = (attributeValue, values) => `${attributeValue} in ${formatConditionValue({ value: values })}`;
11455
+ /** Группирует выбранные id по уровню в объектное значение фильтра `{ l{N}: [ids] }`. */
11456
+ const buildFilterValue = (selected) => {
11457
+ const result = {};
11458
+ selected.forEach((level, id) => {
11459
+ const key = `l${level}`;
11460
+ if (!result[key]) {
11461
+ result[key] = [];
11462
+ }
11463
+ result[key].push(id);
11464
+ });
11465
+ return result;
11466
+ };
11467
+ /** Разбирает объектное значение фильтра в карту `id → level`. */
11468
+ const parseFilterValue = (value) => {
11469
+ const result = new Map();
11470
+ if (!value) {
11471
+ return result;
11472
+ }
11473
+ Object.keys(value).forEach(levelKey => {
11474
+ const level = Number(levelKey.replace(/^l/, ""));
11475
+ value[levelKey]?.forEach(id => result.set(id, level));
11476
+ });
11477
+ return result;
11478
+ };
11479
+ /** Стабильная сериализация выбора для сравнения (порядок не важен). */
11480
+ const serializeSelection = (selected) => [...selected.entries()]
11481
+ .map(([id, level]) => `${id}:${level}`)
11482
+ .sort()
11483
+ .join(",");
11484
+ /**
11485
+ * Дозапрашивает цепочку предков вверх по `parentName` уровень за уровнем (батч `attributeName in
11486
+ * (...)`), пока не достигнет уровня 1. Связь идёт по именам (`name`/`parentName`), т.к. `node.id`
11487
+ * хранит значение (`attributeValue`). Возвращает исходные узлы вместе с предками.
11488
+ */
11489
+ const collectAncestors = async (nodes, loadByNames) => {
11490
+ const byName = new Map();
11491
+ nodes.forEach(node => byName.set(node.data.name, node));
11492
+ let frontier = nodes;
11493
+ for (let depth = 0; depth < MAX_ANCESTOR_DEPTH; depth++) {
11494
+ const missingParents = new Set();
11495
+ frontier.forEach(node => {
11496
+ const { parentName, level } = node.data ?? {};
11497
+ if (parentName != null && level > 1 && !byName.has(parentName)) {
11498
+ missingParents.add(parentName);
11499
+ }
11500
+ });
11501
+ if (!missingParents.size) {
11502
+ break;
11503
+ }
11504
+ const parents = await loadByNames([...missingParents]);
11505
+ if (!parents.length) {
11506
+ break;
11507
+ }
11508
+ parents.forEach(parent => byName.set(parent.data.name, parent));
11509
+ frontier = parents;
11510
+ }
11511
+ return [...byName.values()];
11512
+ };
11513
+ /**
11514
+ * Собирает плоский набор узлов в дерево по связи `parentName` ↔ `name`. Узлы с подгруженными
11515
+ * потомками раскрываются (`expanded` хранит `node.id` = значение); листья-папки без потомков
11516
+ * получают `children = null` (ленивая подгрузка).
11517
+ */
11518
+ const assembleTree = (nodes) => {
11519
+ const nodesByName = new Map();
11520
+ nodes.forEach(node => nodesByName.set(node.data.name, node));
11521
+ const childrenMap = new Map();
11522
+ nodesByName.forEach(node => {
11523
+ const parentName = node.data?.parentName;
11524
+ if (parentName != null && nodesByName.has(parentName)) {
11525
+ const siblings = childrenMap.get(parentName) ?? [];
11526
+ siblings.push(node);
11527
+ childrenMap.set(parentName, siblings);
11528
+ }
11529
+ });
11530
+ const roots = [];
11531
+ const expanded = [];
11532
+ nodesByName.forEach(node => {
11533
+ const { name, parentName } = node.data;
11534
+ const kids = childrenMap.get(name);
11535
+ if (kids?.length) {
11536
+ node.children = kids;
11537
+ expanded.push(node.id);
11538
+ }
11539
+ else if (node.isLeaf) {
11540
+ node.children = null;
11541
+ }
11542
+ if (parentName == null || !nodesByName.has(parentName)) {
11543
+ roots.push(node);
11544
+ }
11545
+ });
11546
+ return { roots, expanded };
11547
+ };
11548
+
11549
+ const useTreeFilterData = (type, filterName) => {
11550
+ const { api } = useGlobalContext();
11551
+ const { currentPage } = useWidgetPage(type);
11552
+ const configFilter = React.useMemo(() => getConfigFilter(filterName, currentPage?.filters), [filterName, currentPage?.filters]);
11553
+ const { relatedDataSource, attributeName = DEFAULT_ATTRIBUTE_NAME, attributeValue, attributeAlias = DEFAULT_ATTRIBUTE_NAME, attributeParentName, attributeLevel, attributeHasChildren, limit = DEFAULT_DATA_SOURCE_LIMIT, } = configFilter ?? {};
11554
+ const valueAttribute = attributeValue ?? attributeName;
11555
+ const layerName = React.useMemo(() => currentPage?.dataSources?.find(({ name }) => name === relatedDataSource)?.layerName, [currentPage?.dataSources, relatedDataSource]);
11556
+ const mapNode = React.useCallback(({ properties }) => {
11557
+ const hasChildren = attributeHasChildren ? !!properties[attributeHasChildren] : false;
11558
+ const data = {
11559
+ name: properties[attributeName],
11560
+ parentName: attributeParentName ? properties[attributeParentName] ?? null : null,
11561
+ level: attributeLevel ? Number(properties[attributeLevel]) : 1,
11562
+ };
11563
+ return {
11564
+ id: properties[valueAttribute],
11565
+ text: String(properties[attributeAlias] ?? ""),
11566
+ isLeaf: hasChildren,
11567
+ children: hasChildren ? null : undefined,
11568
+ data,
11569
+ };
11570
+ }, [valueAttribute, attributeName, attributeAlias, attributeParentName, attributeLevel, attributeHasChildren]);
11571
+ const runQuery = React.useCallback(async (condition) => {
11572
+ if (!layerName) {
11573
+ return [];
11574
+ }
11575
+ const response = await api.layers.getFeatures(layerName, { withGeom: false, limit, conditions: [condition] });
11576
+ return response.features ?? [];
11577
+ }, [api, layerName, limit]);
11578
+ const loadLevelOne = React.useCallback(async () => {
11579
+ if (!attributeLevel) {
11580
+ return [];
11581
+ }
11582
+ const features = await runQuery(buildLevelOneCondition(attributeLevel));
11583
+ return features.map(mapNode);
11584
+ }, [attributeLevel, runQuery, mapNode]);
11585
+ const loadChildren = React.useCallback(async (item) => {
11586
+ if (!attributeParentName) {
11587
+ return { children: [] };
11588
+ }
11589
+ const features = await runQuery(buildChildrenCondition(attributeParentName, item.data.name));
11590
+ return { children: features.map(mapNode) };
11591
+ }, [attributeParentName, runQuery, mapNode]);
11592
+ const searchNodes = React.useCallback(async (query) => {
11593
+ const features = await runQuery(buildSearchCondition(attributeAlias, query));
11594
+ return features.map(mapNode);
11595
+ }, [attributeAlias, runQuery, mapNode]);
11596
+ const loadByNames = React.useCallback(async (names) => {
11597
+ if (!names.length) {
11598
+ return [];
11599
+ }
11600
+ const features = await runQuery(buildNamesCondition(attributeName, names));
11601
+ return features.map(mapNode);
11602
+ }, [attributeName, runQuery, mapNode]);
11603
+ const loadByValues = React.useCallback(async (values) => {
11604
+ if (!values.length) {
11605
+ return [];
11606
+ }
11607
+ const features = await runQuery(buildValuesCondition(valueAttribute, values));
11608
+ return features.map(mapNode);
11609
+ }, [valueAttribute, runQuery, mapNode]);
11610
+ return { configFilter, layerName, loadLevelOne, loadChildren, searchNodes, loadByNames, loadByValues };
11611
+ };
11612
+
11613
+ const SEARCH_MIN_LENGTH = 3;
11614
+ const SEARCH_DEBOUNCE = 300;
11615
+ const useTreeFilterSearch = ({ searchNodes, loadByNames }) => {
11616
+ const [searchValue, setSearchValue] = React.useState("");
11617
+ const [searchData, setSearchData] = React.useState([]);
11618
+ const [searchExpanded, setSearchExpanded] = React.useState([]);
11619
+ const [searching, setSearching] = React.useState(false);
11620
+ const timer = React.useRef();
11621
+ const requestId = React.useRef(0);
11622
+ const isSearchActive = searchValue.trim().length >= SEARCH_MIN_LENGTH;
11623
+ const runSearch = React.useCallback(async (query) => {
11624
+ const id = ++requestId.current;
11625
+ setSearching(true);
11626
+ try {
11627
+ const results = await searchNodes(query);
11628
+ const withAncestors = await collectAncestors(results, loadByNames);
11629
+ const { roots, expanded } = assembleTree(withAncestors);
11630
+ if (id !== requestId.current) {
11631
+ return;
11632
+ }
11633
+ setSearchData(roots);
11634
+ setSearchExpanded(expanded);
11635
+ }
11636
+ finally {
11637
+ if (id === requestId.current) {
11638
+ setSearching(false);
11639
+ }
11640
+ }
11641
+ }, [searchNodes, loadByNames]);
11642
+ const clearSearchState = React.useCallback(() => {
11643
+ requestId.current++;
11644
+ setSearchData([]);
11645
+ setSearchExpanded([]);
11646
+ setSearching(false);
11647
+ }, []);
11648
+ const onSearchChange = React.useCallback((value) => {
11649
+ setSearchValue(value);
11650
+ if (timer.current) {
11651
+ clearTimeout(timer.current);
11652
+ }
11653
+ const query = value.trim();
11654
+ if (query.length < SEARCH_MIN_LENGTH) {
11655
+ clearSearchState();
11656
+ return;
11657
+ }
11658
+ timer.current = setTimeout(() => runSearch(query), SEARCH_DEBOUNCE);
11659
+ }, [runSearch, clearSearchState]);
11660
+ const resetSearch = React.useCallback(() => {
11661
+ if (timer.current) {
11662
+ clearTimeout(timer.current);
11663
+ }
11664
+ setSearchValue("");
11665
+ clearSearchState();
11666
+ }, [clearSearchState]);
11667
+ return {
11668
+ searchValue,
11669
+ isSearchActive,
11670
+ searching,
11671
+ searchData,
11672
+ setSearchData,
11673
+ searchExpanded,
11674
+ setSearchExpanded,
11675
+ onSearchChange,
11676
+ resetSearch,
11677
+ };
11678
+ };
11679
+
11680
+ const useTreeFilterSelection = (type, filterName, configFilter) => {
11681
+ const { filters, changeFilters } = useWidgetContext(type);
11682
+ const [selected, setSelected] = React.useState(() => parseFilterValue(configFilter?.defaultValue));
11683
+ const [selectedLabel, setSelectedLabel] = React.useState();
11684
+ const selectedIds = React.useMemo(() => new Set(selected.keys()), [selected]);
11685
+ /** Уже применённый (в redux) выбор — база для `dirty`. */
11686
+ const committed = React.useMemo(() => parseFilterValue((filters[filterName]?.value ?? configFilter?.defaultValue)), [filters, filterName, configFilter?.defaultValue]);
11687
+ const dirty = React.useMemo(() => serializeSelection(selected) !== serializeSelection(committed), [selected, committed]);
11688
+ const commit = React.useCallback((next) => {
11689
+ setSelected(next);
11690
+ changeFilters({ [filterName]: { value: buildFilterValue(next) } }, configFilter?.resetFilters);
11691
+ }, [changeFilters, filterName, configFilter?.resetFilters]);
11692
+ const onToggleSelect = React.useCallback((item, multiSelect) => {
11693
+ const level = item.data?.level ?? 1;
11694
+ const next = new Map(selected);
11695
+ if (next.has(item.id)) {
11696
+ next.delete(item.id);
11697
+ if (!multiSelect) {
11698
+ setSelectedLabel(undefined);
11699
+ }
11700
+ }
11701
+ else {
11702
+ if (!multiSelect) {
11703
+ next.clear();
11704
+ setSelectedLabel(item.text);
11705
+ }
11706
+ next.set(item.id, level);
11707
+ }
11708
+ // Мультивыбор не коммитит в redux — сохранение по кнопке (onSave). Single — сразу.
11709
+ if (multiSelect) {
11710
+ setSelected(next);
11711
+ }
11712
+ else {
11713
+ commit(next);
11714
+ }
11715
+ }, [selected, commit]);
11716
+ const onSave = React.useCallback(() => {
11717
+ changeFilters({ [filterName]: { value: buildFilterValue(selected) } }, configFilter?.resetFilters);
11718
+ }, [changeFilters, filterName, selected, configFilter?.resetFilters]);
11719
+ const onReset = React.useCallback(() => {
11720
+ setSelectedLabel(undefined);
11721
+ commit(new Map());
11722
+ }, [commit]);
11723
+ const initRef = React.useRef(false);
11724
+ React.useEffect(() => {
11725
+ if (initRef.current) {
11726
+ return;
11727
+ }
11728
+ initRef.current = true;
11729
+ if (selected.size && filters[filterName] === undefined) {
11730
+ changeFilters({ [filterName]: { value: buildFilterValue(selected) } });
11731
+ }
11732
+ }, [filters, filterName, changeFilters, selected]);
11733
+ return { selected, selectedIds, selectedLabel, dirty, onToggleSelect, onReset, onSave };
11734
+ };
11735
+
11736
+ const useTreeFilterTree = ({ selected, loadLevelOne, loadByValues, loadByNames }) => {
11737
+ const [treeData, setTreeData] = React.useState([]);
11738
+ const [treeExpanded, setTreeExpanded] = React.useState([]);
11739
+ const [loading, setLoading] = React.useState(false);
11740
+ const loadedRef = React.useRef(false);
11741
+ const selectedRef = React.useRef(selected);
11742
+ selectedRef.current = selected;
11743
+ const onOpen = React.useCallback(async () => {
11744
+ if (loadedRef.current) {
11745
+ return;
11746
+ }
11747
+ loadedRef.current = true;
11748
+ setLoading(true);
11749
+ try {
11750
+ const roots = await loadLevelOne();
11751
+ const preselected = [...selectedRef.current.keys()];
11752
+ if (!preselected.length) {
11753
+ setTreeData(roots);
11754
+ return;
11755
+ }
11756
+ const selectedNodes = await loadByValues(preselected);
11757
+ const withAncestors = await collectAncestors([...roots, ...selectedNodes], loadByNames);
11758
+ const { roots: assembled, expanded } = assembleTree(withAncestors);
11759
+ setTreeData(assembled);
11760
+ setTreeExpanded(expanded);
11761
+ }
11762
+ finally {
11763
+ setLoading(false);
11764
+ }
11765
+ }, [loadLevelOne, loadByValues, loadByNames]);
11766
+ return { treeData, setTreeData, treeExpanded, setTreeExpanded, loading, onOpen };
11767
+ };
11768
+
11769
+ const useTreeFilter = (type, filter) => {
11770
+ const { filterName, multiSelect, placeholder, width } = filter.options ?? {};
11771
+ const { configFilter, loadLevelOne, loadChildren, searchNodes, loadByNames, loadByValues } = useTreeFilterData(type, filterName ?? "");
11772
+ const { selected, selectedIds, selectedLabel, dirty, onToggleSelect: toggleSelect, onReset, onSave, } = useTreeFilterSelection(type, filterName ?? "", configFilter);
11773
+ const { treeData, setTreeData, treeExpanded, setTreeExpanded, loading, onOpen } = useTreeFilterTree({
11774
+ selected,
11775
+ loadLevelOne,
11776
+ loadByValues,
11777
+ loadByNames,
11778
+ });
11779
+ const search = useTreeFilterSearch({ searchNodes, loadByNames });
11780
+ const onToggleSelect = React.useCallback(item => toggleSelect(item, multiSelect), [toggleSelect, multiSelect]);
11781
+ const onClose = React.useCallback(() => search.resetSearch(), [search]);
11782
+ const view = React.useMemo(() => search.isSearchActive
11783
+ ? { data: search.searchData, setData: search.setSearchData, expanded: search.searchExpanded, setExpanded: search.setSearchExpanded }
11784
+ : { data: treeData, setData: setTreeData, expanded: treeExpanded, setExpanded: setTreeExpanded }, [search.isSearchActive, search.searchData, search.setSearchData, search.searchExpanded, search.setSearchExpanded, treeData, setTreeData, treeExpanded, setTreeExpanded]);
11785
+ return {
11786
+ width: width ? `${width}px` : undefined,
11787
+ placeholder,
11788
+ multiSelect,
11789
+ loading: loading || search.searching,
11790
+ data: view.data,
11791
+ setData: view.setData,
11792
+ expanded: view.expanded,
11793
+ setExpanded: view.setExpanded,
11794
+ onLoadChildren: loadChildren,
11795
+ selectedIds,
11796
+ selectedCount: selectedIds.size,
11797
+ selectedLabel,
11798
+ searchValue: search.searchValue,
11799
+ onSearchChange: search.onSearchChange,
11800
+ onOpen,
11801
+ onClose,
11802
+ onToggleSelect,
11803
+ onReset,
11804
+ onSave,
11805
+ dirty,
11806
+ };
11807
+ };
11808
+
11809
+ const TreeFilter = ({ type, filter }) => {
11810
+ const { width, placeholder, multiSelect, loading, data, setData, expanded, setExpanded, onLoadChildren, selectedIds, selectedCount, selectedLabel, searchValue, onSearchChange, onOpen, onClose, onToggleSelect, onReset, onSave, dirty, } = useTreeFilter(type, filter);
11811
+ return (jsxRuntime.jsx(uilibGl.TreeDropdown, { zIndex: 100, width: width, placeholder: placeholder, multiSelect: multiSelect, loading: loading, data: data, setData: setData, expanded: expanded, setExpanded: setExpanded, onLoadChildren: onLoadChildren, selectedIds: selectedIds, selectedCount: selectedCount, selectedLabel: selectedLabel, searchValue: searchValue, onSearchChange: onSearchChange, onOpen: onOpen, onClose: onClose, onToggleSelect: onToggleSelect, onReset: onReset, onSave: onSave, dirty: dirty }));
11812
+ };
11813
+
11422
11814
  const getFilterComponent = (filterType) => {
11423
11815
  switch (filterType) {
11424
11816
  case "checkbox":
@@ -11433,6 +11825,8 @@ const getFilterComponent = (filterType) => {
11433
11825
  return TextFilter;
11434
11826
  case "chips":
11435
11827
  return ChipsFilter;
11828
+ case "tree":
11829
+ return TreeFilter;
11436
11830
  case "dropdown":
11437
11831
  default:
11438
11832
  return DropdownFilter;
@@ -12405,11 +12799,18 @@ const useDataSources = ({ type: widgetType, config, attributes, filters, layerPa
12405
12799
  ]);
12406
12800
  const getUpdatingDataSources = React.useCallback(() => {
12407
12801
  const diffFilterNames = filters
12408
- ? Object.keys(filters).filter(key => Array.isArray(filters[key].value)
12409
- ? filters[key].value.length !==
12410
- prevFilters.current[key]?.value?.length ||
12411
- filters[key].value.some((item, index) => item !== prevFilters.current[key]?.value?.[index])
12412
- : filters[key].value !== prevFilters.current[key]?.value)
12802
+ ? Object.keys(filters).filter(key => {
12803
+ const { value } = filters[key];
12804
+ const prevValue = prevFilters.current[key]?.value;
12805
+ if (isTreeFilterValue(value) || isTreeFilterValue(prevValue)) {
12806
+ return JSON.stringify(value) !== JSON.stringify(prevValue);
12807
+ }
12808
+ if (Array.isArray(value)) {
12809
+ return (value.length !== prevValue?.length ||
12810
+ value.some((item, index) => item !== prevValue?.[index]));
12811
+ }
12812
+ return value !== prevValue;
12813
+ })
12413
12814
  : [];
12414
12815
  if (!diffFilterNames.length)
12415
12816
  return;
@@ -14109,6 +14510,7 @@ exports.addDataSources = addDataSources;
14109
14510
  exports.adjustColor = adjustColor;
14110
14511
  exports.applyFiltersToCondition = applyFiltersToCondition;
14111
14512
  exports.applyQueryFilters = applyQueryFilters;
14513
+ exports.applyTreeFilterToCondition = applyTreeFilterToCondition;
14112
14514
  exports.applyVarsToCondition = applyVarsToCondition;
14113
14515
  exports.asAttributeName = asAttributeName;
14114
14516
  exports.asChartId = asChartId;
@@ -14193,6 +14595,7 @@ exports.isNotValidSelectedTab = isNotValidSelectedTab;
14193
14595
  exports.isNumeric = isNumeric;
14194
14596
  exports.isObject = isObject;
14195
14597
  exports.isProxyService = isProxyService;
14598
+ exports.isTreeFilterValue = isTreeFilterValue;
14196
14599
  exports.isVisibleContainer = isVisibleContainer;
14197
14600
  exports.metersPerPixel = metersPerPixel;
14198
14601
  exports.numberOptions = numberOptions;