@topvisor/ui 1.4.0-TopGroupSelector.9 → 1.4.0
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/package.json +1 -1
- package/project/project.amd.js +1 -1
- package/project/project.amd.js.map +1 -1
- package/project/project.js +208 -211
- package/project/project.js.map +1 -1
- package/src/components/project/groupSelector/groupSelector.vue.d.ts +5 -0
- package/src/components/project/groupSelector/groups/types.d.ts +4 -0
- package/src/components/project/groupSelector/types.d.ts +4 -0
- package/utils/string.amd.js +1 -1
- package/utils/string.amd.js.map +1 -1
- package/utils/string.js +49 -49
- package/utils/string.js.map +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project.amd.js","sources":["../../src/components/project/competitorSelector/competitorSelector.vue","../../src/components/project/competitorSelector/composables.ts","../../src/components/project/regionSelector/utils/consts.ts","../../src/components/project/regionSelector/utils/utils.ts","../../src/components/project/regionSelector/composables/selectSearcher.ts","../../src/components/project/regionSelector/composables/selectRegion.ts","../../src/components/project/regionSelector/composables/compare.ts","../../src/components/project/regionSelector/composables/selectorRegion.ts","../../src/components/project/regionSelector/regionSelector.vue","../../src/components/project/groupSelector/folders/utils.ts","../../src/core/utils/composables/useWatch.ts","../../src/components/project/groupSelector/folders/folders.vue","../../src/components/project/groupSelector/groups/utils.ts","../../src/components/project/groupSelector/groups/groups.vue","../../src/components/project/groupSelector/groupSelector.vue","../../src/components/project/tagSelector/utils/utils.ts","../../src/components/project/tagSelector/utils/el.ts","../../src/components/project/tagSelector/tagsDefaults.ts","../../src/components/project/tagSelector/tagIcon/tagIcon.vue","../../src/components/project/tagSelector/popupListItem/tagPopupListItem.vue","../../src/components/project/tagSelector/popupOpener/popupOpener.vue","../../src/components/project/tagSelector/tagSelector.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from 'vue';\nimport type { Props } from './types';\nimport Core from '@/core/core/core';\nimport Button from '@/components/forms/button/button.vue';\nimport Popup from '@/components/popup/popup/popup.vue';\nimport ListItem from '@/components/popup/popup/listItem.vue';\nimport Menu from '@/components/formsExt/menu/menu.vue';\nimport { useI18n } from '@/core/plugins/i18n';\n\nconst props = withDefaults(defineProps<Props>(), {\n\tshowSelectAllItem: true,\n});\nconst model = defineModel<Props['modelValue']>();\n\nconst selectAllItem = computed(() => {\n\tif (props.showSelectAllItem) {\n\t\treturn {\n\t\t\ticon: '',\n\t\t\ttitle: useI18n().Common.Select_all,\n\t\t\tvalue: 'all',\n\t\t\tcontent: '',\n\t\t};\n\t}\n});\n</script>\n\n<template>\n\t<div class=\"top-competitorSelector\">\n\t\t<Popup v-if=\"Core.state.isMobile\">\n\t\t\t<template #opener>\n\t\t\t\t<Button\n\t\t\t\t\tclass=\"top-competitorSelector_opener\"\n\t\t\t\t\tcolor=\"theme\"\n\t\t\t\t\ticon=\"\"\n\t\t\t\t\ticon2=\"\"\n\t\t\t\t>\n\t\t\t\t\t{{ items.find((item) => item.value === model?.[0])?.content }}\n\t\t\t\t</Button>\n\t\t\t</template>\n\n\t\t\t<template #contentList>\n\t\t\t\t<ListItem\n\t\t\t\t\tv-for=\"(item) in items\"\n\t\t\t\t\t:class=\"{\n\t\t\t\t\t\t'top-active': model?.includes(item.value)\n\t\t\t\t\t}\"\n\t\t\t\t\t:data-top-icon=\"item.icon\"\n\t\t\t\t\t:title=\"item.title\"\n\t\t\t\t\t@click=\"() => model = [item.value]\"\n\t\t\t\t>\n\t\t\t\t\t<span class=\"top-ellipsis1\">\n\t\t\t\t\t\t{{ item.content }}\n\t\t\t\t\t</span>\n\t\t\t\t</ListItem>\n\t\t\t</template>\n\t\t</Popup>\n\n\t\t<Menu\n\t\t\tv-else\n\t\t\tv-model=\"model\"\n\t\t\t:items=\"items\"\n\t\t\t:isMultiple=\"true\"\n\t\t\tstyling=\"bar\"\n\t\t\t:canBeEmptyMultiple=\"false\"\n\t\t\t:selectAllItem=\"selectAllItem\"\n\t\t/>\n\t</div>\n</template>\n\n<style>\n.top-competitorSelector_opener.top-button {\n\twidth: 100%;\n}\n</style>\n","import type { MaybeRefOrGetter } from 'vue';\nimport { computed, toValue } from 'vue';\nimport type { Competitor, Item } from '@/components/project/competitorSelector/types';\n\nexport const useItemsFromCompetitors = (competitors: MaybeRefOrGetter<Competitor[]>, projectId: MaybeRefOrGetter<number>) => {\n\treturn computed(() => {\n\t\tconst activeCompetitors = toValue(competitors).filter(competitor => competitor.on >= 0 || competitor.id === projectId);\n\n\t\tconst items: Item[] = activeCompetitors.map(competitor => {\n\t\t\treturn {\n\t\t\t\tvalue: competitor.id,\n\t\t\t\ttitle: competitor.url + ` [${competitor.id}]`,\n\t\t\t\ticon: competitor.id === toValue(projectId) ? '' : '',\n\t\t\t\tcontent: competitor.name,\n\t\t\t};\n\t\t});\n\n\t\treturn items;\n\t});\n};\n","import type { Region, SearcherIndexed } from '../types';\nimport { useI18n } from '@/core/plugins/i18n';\n\n/**\n * Ключ ПС - для выбора нескольких ПС или регионов\n */\nexport const searhcerCompareKey = -1;\n\n/**\n * Ключ ПС или индекса Региона, используется для ПС или Региона, не настроенных в проекте\n *\n * Также, используется для API с возможностью не указывать регион, см. `props.autoRegion`\n */\nexport const dummyIndex = -2;\n\n/**\n * Регион - без региона\n */\nexport const globalRegionIndex = -1;\n\nexport const searchersNames = {\n\t0: 'Yandex',\n\t1: 'Google',\n\t4: 'YouTube',\n\t5: 'Bing',\n\t7: 'Seznam',\n\t8: 'AppStore',\n\t9: 'GoogleStore',\n\t20: 'Yandex.com',\n\t21: 'Yandex.com.tr',\n};\n\nexport const regionUndefined: Region = {\n\tkey: dummyIndex,\n\tname: '--',\n\tindex: dummyIndex,\n};\n\nexport const searcherUndefined: SearcherIndexed = {\n\tkey: dummyIndex,\n\tname: '--',\n\tregions: [regionUndefined],\n\tregionByIndex: new Map([[dummyIndex, regionUndefined]]),\n};\n\nconst regionAuto: Region = {\n\tkey: dummyIndex,\n\tname: 'Autoselect',\n\tindex: dummyIndex,\n};\n\nconst searcherAuto: SearcherIndexed = {\n\tkey: dummyIndex,\n\tname: 'Autoselect',\n\tregions: [regionAuto],\n\tregionByIndex: new Map([[dummyIndex, regionAuto]]),\n};\n\nconst regionGlobal: Region = {\n\tcountryCode: '00',\n\tdepth: 1,\n\tdevice: 0,\n\tkey: globalRegionIndex,\n\tindex: globalRegionIndex,\n\tlang: 'ru',\n\tname: 'Without region',\n};\n\nexport const getRegionAuto = () => {\n\tregionAuto.name = useI18n().Common.Autoselect!;\n\n\treturn regionAuto;\n};\n\nexport const getSearcherAuto = () => {\n\tgetRegionAuto();\n\n\tsearcherAuto.name = useI18n().Common.Autoselect!;\n\tconsole.log(searcherAuto);\n\n\treturn searcherAuto;\n};\n\nexport const getRegionGlobal = () => {\n\tregionGlobal.name = useI18n().Keywords.Without_region!;\n\n\treturn regionGlobal;\n};\n","import type { Region, Searcher, SearcherByKey, SearcherIndexed } from '../types';\nimport { dummyIndex, getRegionGlobal, getSearcherAuto, searchersNames, searcherUndefined } from './consts';\nimport { useAsyncTopDialog } from '@/components/dialog/dialog/composables/utils';\n\n/**\n * Генерация Map ПС с Map регионов из массива searchers\n * - ключ для ПС - searcherKey\n * - ключ для региона - regionIndex\n *\n * Если поисковиков в списке нет, в список будет добавлен searcherUndefined\n *\n * Если регионов в списке нет, в список будет добавлен regionUndefined\n *\n * @param forFrequency - получить массив для просмотра частоты (или ставок)\n * @param autoRegion - добавить элемент с ключем -2, который в API заменяется на нужный ключ в зависимости от контекста\n * @param searchers - поисковики с регионами проекта, см.: get/projects_2/projects, show_searchers_and_regions = 1\n */\nexport const genSearcherByKey = (\n\tforFrequency: boolean = false,\n\tautoRegion: boolean = false,\n\tsearchers: Searcher[] = [],\n) => {\n\tlet searcherByKey: SearcherByKey;\n\n\tif (forFrequency) {\n\t\tsearcherByKey = genSearchersMapVolume(searchers);\n\t} else {\n\t\tsearcherByKey = genSearchersMapCommon(searchers);\n\t}\n\n\tif (autoRegion) searcherByKey.set(dummyIndex, getSearcherAuto());\n\tif (!searcherByKey.size) searcherByKey.set(dummyIndex, searcherUndefined);\n\n\treturn searcherByKey;\n};\n\n/**\n * Список ПС с регионами\n *\n * @param searchers\n * @param onlyEnabledRegions\n * @param forceSearchersKeys - ключи ПС, которые нееобходимо вывести в любом случае\n * @param forFrequency - для частоты будут выводиться только Яндекс, Google и Mail\n */\nconst genSearchersMapCommon = (\n\tsearchers: Searcher[],\n\tonlyEnabledRegions: boolean = true,\n\tforceSearchersKeys: Searcher['key'][] = [],\n\tforFrequency: boolean = false,\n) => {\n\tconst searcherByKey: Map<Searcher['key'], SearcherIndexed> = new Map();\n\n\t// настроенные ПС\n\tsearchers.forEach((searcher) => {\n\t\tif (!searcher.enabled) return;\n\t\tif (forFrequency && typeof searcher.key === 'number' && searcher.key > 1) return;\n\n\t\tconst searcherI = { ...searcher } as SearcherIndexed;\n\t\tsearcherI.regionByIndex = new Map();\n\n\t\tif (searcher.regions) {\n\t\t\tsearcher.regions.forEach((region: Region) => {\n\t\t\t\tif (onlyEnabledRegions && !region.enabled) return;\n\n\t\t\t\tconst regionI = { ...region };\n\t\t\t\tsearcherI.regionByIndex.set(regionI.index, regionI);\n\t\t\t});\n\t\t}\n\n\t\tif (!searcherI.regionByIndex.size && !forceSearchersKeys.length) {\n\t\t\t// searcherI.regionByIndex.set(regionUndefined.index, regionUndefined);\n\t\t}\n\n\t\tif (\n\t\t\t// режим вывода ПС без регионов\n\t\t\t!searcher.regions ||\n\n\t\t\t// есть включенные регионы\n\t\t\tsearcherI.regionByIndex.size ||\n\n\t\t\t// запрошен вывод конкретных ПС\n\t\t\tforceSearchersKeys.length\n\t\t) {\n\t\t\tif (typeof searcherI.key === 'number') searcherByKey.set(searcherI.key, searcherI);\n\t\t}\n\t});\n\n\t// дополнительные ПС\n\tforceSearchersKeys.forEach((searcherKey) => {\n\t\tif (searcherByKey.has(searcherKey)) return;\n\n\t\tconst searcherI: SearcherIndexed = {\n\t\t\tkey: searcherKey,\n\t\t\tname: searchersNames[searcherKey],\n\t\t\tregions: [],\n\t\t\tregionByIndex: new Map(),\n\t\t};\n\n\t\tsearcherByKey.set(searcherI.key, searcherI);\n\t});\n\n\treturn searcherByKey;\n};\n\n/**\n * Список ПС с регионами для проверки частоты (Яндекс, Google, Mail если добавлен в проект)\n *\n * @param searchers\n */\nconst genSearchersMapVolume = (searchers: Searcher[]) => {\n\tconst serarcherByKey = genSearchersMapCommon(searchers, false, [0, 1], true);\n\n\t// в Mail нет регионов\n\tif (serarcherByKey.has(2)) {\n\t\tconst searcherMail = serarcherByKey.get(2);\n\t\tif (searcherMail) searcherMail.regionByIndex = new Map();\n\t}\n\n\t// добавление глобального региона\n\tserarcherByKey.forEach((searcher) => {\n\t\tif (!searcher.regionByIndex) return;\n\n\t\tconst region = { ...getRegionGlobal() };\n\t\tsearcher.regionByIndex.set(region.index, region);\n\t});\n\n\treturn serarcherByKey;\n};\n\n/**\n * Поиск региона по заданному критерию из списка поисковиков проекта\n *\n * @param forFrequency - поиск региона для просмотра частоты или ставок\n * @param searchRegion - объект с параметрами региона, который следует найти\n * @param searchers\n */\nexport const findRegion = (forFrequency: boolean, searchRegion: Partial<Region>, searchers: Searcher[] = []) => {\n\tconst searcherByKey = genSearcherByKey(forFrequency, false, searchers);\n\n\tlet findedRegion: Region | undefined;\n\n\t// поиск ПС\n\tsearcherByKey.forEach((searcher) => {\n\t\tif (searchRegion.searcher_key !== undefined && searchRegion.searcher_key != searcher.key) return;\n\t\tif (!searcher.regions) return;\n\n\t\t// поиск региона\n\t\tsearcher.regions.forEach((region) => {\n\t\t\tif (findedRegion) return;\n\n\t\t\tif (searchRegion.key !== undefined && searchRegion.key != region.key) return;\n\t\t\tif (searchRegion.index !== undefined && searchRegion.index != region.index) return;\n\n\t\t\tif (!forFrequency) {\n\t\t\t\tif (searchRegion.lang !== undefined && searchRegion.lang != region.lang) return;\n\t\t\t\tif (searchRegion.device !== undefined && searchRegion.device != region.device) return;\n\t\t\t}\n\n\t\t\tregion.searcher_key = searcher.key;\n\t\t\tfindedRegion = region;\n\n\t\t\treturn false;\n\t\t});\n\n\t\tif (findedRegion) return false;\n\t});\n\n\treturn findedRegion;\n};\n\n/**\n * Открыть диалоговое окно с выбором нескольких регионов\n *\n * @see import('../dialog_regionSelectorRegions/types').Props\n */\nexport const dialogRegionSelector = useAsyncTopDialog(() => {\n\treturn import('@/components/project/regionSelector/dialog_regionSelectorRegions/dialog_regionSelectorRegions.vue');\n});\n","import { computed, type ComputedRef, ref } from 'vue';\nimport { useI18n } from '@/core/plugins/i18n';\nimport type { Props, SearcherByKey } from '../types';\nimport type { Option, Options } from '@/components/forms/select/types';\nimport { dummyIndex, searhcerCompareKey } from '../utils/consts';\nimport { getSearcherGIcon } from '@/core/utils/searchers';\n\n/**\n * Создание и управление реактивными переменными для выбора ПС\n *\n * @param props - входные props компонента\n * @param mapSearchers - Map ПС (реактивная)\n */\nexport const useSelectSearcher = (\n\tprops: Props,\n\tmapSearchers: ComputedRef<SearcherByKey>,\n) => {\n\tconst i18n = useI18n();\n\n\t/**\n\t * Ключ выбранной поисковой системы\n\t */\n\tconst searcherKey = ref(mapSearchers.value.keys().next().value ?? dummyIndex);\n\n\t/**\n\t * Коллекция опций для выбора ПС\n\t */\n\tconst optionBySearcherKey = computed(() => {\n\t\tconst res: Options = new Map();\n\t\tmapSearchers.value.forEach((searcher) => {\n\t\t\tlet option: Option = {\n\t\t\t\tvalue: searcher.key,\n\t\t\t\ttitle: searcher.name,\n\t\t\t};\n\n\t\t\tif (props.addSearcherIcon) option.icon = getSearcherGIcon(searcher.key);\n\n\t\t\tres.set(searcher.key, option);\n\t\t});\n\n\t\t// добавить режим сравнения, если есть хотя бы одна ПС с одним регионом\n\t\tif (props.addCompare && !res.has(dummyIndex)) {\n\t\t\tconst dummyOption: Option = {\n\t\t\t\tvalue: '',\n\t\t\t\ttitle: '--------------------',\n\t\t\t\tdisabled: true,\n\t\t\t};\n\t\t\tres.set(dummyOption.value, dummyOption);\n\n\t\t\tconst compareOption: Option = {\n\t\t\t\tvalue: searhcerCompareKey,\n\t\t\t\ttitle: i18n.Common.Compare!,\n\t\t\t};\n\t\t\tres.set(compareOption.value, compareOption);\n\t\t}\n\n\t\treturn res;\n\t});\n\n\treturn {\n\t\tsearcherKey,\n\t\toptionBySearcherKey,\n\t};\n};\n","import { computed, type ComputedRef, ref, watch } from 'vue';\nimport { useI18n } from '@/core/plugins/i18n';\nimport { getDeviceGIcon, getLangLabel } from '@/core/utils/searchers';\nimport type { Option } from '@/components/forms/select/types';\nimport { dummyIndex } from '../utils/consts';\nimport type { Props, Region, SearcherIndexed } from '../types';\n\n/**\n * Создание и управление реактивными переменными для выбора региона\n *\n * @param props - входные props компонента\n * @param activeSearcherIndexed - объект активной поисковой системы (реактивная)\n */\nexport const useSelectRegion = (props: Props, activeSearcherIndexed: ComputedRef<SearcherIndexed>) => {\n\tconst i18n = useI18n();\n\n\t/**\n\t * Индекс выбранного региона\n\t */\n\tconst regionIndex = ref(dummyIndex);\n\n\tif (props.modelValue.length === 1) {\n\t\tregionIndex.value = props.modelValue[0];\n\t}\n\n\tif (regionIndex.value === dummyIndex) {\n\t\tif (props.forFrequency) {\n\t\t\t// в качетсве ключа используется region.key, а не region.index\n\t\t\tregionIndex.value = activeSearcherIndexed.value?.regionByIndex.values().next().value?.key ?? dummyIndex;\n\t\t} else {\n\t\t\tregionIndex.value = activeSearcherIndexed.value?.regionByIndex.keys().next().value ?? dummyIndex;\n\t\t}\n\t}\n\n\t/**\n\t * Коллекция опций для выбора региона\n\t */\n\tconst optionByRegionIndex = computed(() => {\n\t\tconst options = new Map<number, Option>();\n\n\t\tactiveSearcherIndexed.value.regionByIndex?.forEach((region) => {\n\t\t\tlet regionLabel = region.name;\n\n\t\t\t// на частоту в текущей версии устройство и язык не влияют\n\t\t\t// обратите внимание, в качетсве ключа используется region.key, а не region.index\n\t\t\tif (props.forFrequency) {\n\t\t\t\tconst option: Option = {\n\t\t\t\t\tvalue: region.key,\n\t\t\t\t\ttitle: regionLabel,\n\t\t\t\t};\n\t\t\t\tif (!options.has(region.key)) options.set(region.key, option);\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (region.device) {\n\t\t\t\tregionLabel += ' (' + i18n.Common['Device_' + region.device] + ')';\n\t\t\t}\n\n\t\t\tconst langLabel = getLangLabel(activeSearcherIndexed.value.key || 0, region.lang ?? '');\n\t\t\tif (langLabel) regionLabel += ' / ' + langLabel;\n\n\t\t\tconst option: Option = {\n\t\t\t\tvalue: region.index,\n\t\t\t\ttitle: regionLabel,\n\t\t\t\ticon: region.device ? getDeviceGIcon(region.device) : undefined,\n\t\t\t};\n\n\t\t\toptions.set(region.index, option);\n\t\t});\n\n\t\treturn options;\n\t});\n\n\t/**\n\t * Выбор максимально похожего региона в новой коллекции регионов\n\t */\n\twatch(optionByRegionIndex, (optionByRegionIndex, oldOptionByRegionIndex) => {\n\t\tif (props.onlySearcher || regionIndex.value !== undefined && optionByRegionIndex.get(regionIndex.value)) {\n\t\t\treturn;\n\t\t}\n\n\t\tlet newRegionIndex = optionByRegionIndex.keys().next().value as Region['index'];\n\t\tif (regionIndex.value === dummyIndex || newRegionIndex === dummyIndex) {\n\t\t\tregionIndex.value = newRegionIndex;\n\n\t\t\treturn;\n\t\t}\n\n\t\tlet regionName = (oldOptionByRegionIndex?.get(regionIndex.value) as Option)?.title || '';\n\t\tlet regionMatchLevel = -1;\n\t\tfor (const [index, option] of optionByRegionIndex.entries()) {\n\t\t\tconst title = (option as Option).title;\n\n\t\t\tif (typeof title !== 'string' || typeof index === 'string') {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// полное совпадение\n\t\t\tif (title === regionName) {\n\t\t\t\tnewRegionIndex = index;\n\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tconst regexpDevice = new RegExp(` \\\\((${i18n.Common['Device_1']}|${i18n.Common['Device_2']})\\\\)`);\n\t\t\tlet regionNameCompare = regionName;\n\t\t\tlet regionMatchLevelI = 3;\n\n\t\t\t// без устройства\n\t\t\tif (title.indexOf(regionNameCompare) === -1) {\n\t\t\t\tregionNameCompare = regionName.replace(/^[^a-zа-я]/i, '').replace(regexpDevice, '');\n\n\t\t\t\tregionMatchLevelI--;\n\t\t\t}\n\n\t\t\t// без языка\n\t\t\tif (title.indexOf(regionNameCompare) === -1) {\n\t\t\t\tregionNameCompare = regionName.replace(/ \\/.*/, '');\n\n\t\t\t\tregionMatchLevelI--;\n\t\t\t}\n\n\t\t\t// без устройства и без языка\n\t\t\tif (title.indexOf(regionNameCompare) === -1) {\n\t\t\t\tregionNameCompare = regionName.replace(/ \\/.*/, '');\n\t\t\t\tregionNameCompare = regionNameCompare.replace(/^[^a-zа-я]/i, '').replace(regexpDevice, '');\n\n\t\t\t\tregionMatchLevelI--;\n\t\t\t}\n\n\t\t\tif (title.indexOf(regionNameCompare) === -1) continue;\n\t\t\tif (regionMatchLevelI <= regionMatchLevel) continue;\n\n\t\t\tregionMatchLevel = regionMatchLevelI;\n\t\t\tnewRegionIndex = index;\n\t\t}\n\n\t\tregionIndex.value = newRegionIndex;\n\t});\n\n\treturn {\n\t\tregionIndex,\n\t\toptionByRegionIndex,\n\t};\n};\n","import { type ComputedRef, ref, watch } from 'vue';\nimport type { Props, SearcherByKey } from '../types';\n\n/**\n * Создание и управление реактивными переменными для сравнения\n *\n * @param props - входные props компонента\n * @param searcherByKey - Map ПС (реактивная)\n * @param allRegionsIndexes - Все доступные индексы регионов (реактивная)\n */\nexport const useCompare = (props: Props, searcherByKey: ComputedRef<SearcherByKey>, allRegionsIndexes: ComputedRef<Set<number>>) => {\n\t/**\n\t * Индексы регионов/ПС в сравнение\n\t */\n\tconst regionsIndexes = ref<number[]>([]);\n\n\t/**\n\t * Загрузка индексов регионов для сравнения\n\t *\n\t * Если в modelValue передано несколько регионов, они будут установлены как выбранные для сравнения\n\t */\n\tconst compareLoad = () => {\n\t\tif (props.onlySearcher && searcherByKey.value) {\n\t\t\tregionsIndexes.value = Array.from(searcherByKey.value.keys());\n\n\t\t\treturn;\n\t\t}\n\n\t\tlet regionsIndexesSaved: Props['modelValue'] = [];\n\n\t\tif (props.modelValue.length > 1) {\n\t\t\tregionsIndexesSaved = [...props.modelValue];\n\t\t} else {\n\t\t\ttry {\n\t\t\t\t// загрузить индексы регионов, если они были сохранены\n\t\t\t\tregionsIndexesSaved = JSON.parse(\n\t\t\t\t\tlocalStorage.getItem('ui:project:regionSelector' + props.projectId + ':regionsIndexes') as string,\n\t\t\t\t) ?? [];\n\t\t\t} catch (e) {\n\n\t\t\t}\n\t\t}\n\n\t\t// убрать из сравнения регионы, которых нет в props.searchers\n\t\tif (regionsIndexesSaved.length) {\n\t\t\tregionsIndexesSaved = regionsIndexesSaved.filter((regionIndex) => {\n\t\t\t\treturn allRegionsIndexes.value.has(regionIndex);\n\t\t\t});\n\t\t}\n\n\t\t// если не сохранено ни одного региона, выбрать все регионы проекта\n\t\tif (!regionsIndexesSaved.length) {\n\t\t\tregionsIndexesSaved = Array.from(allRegionsIndexes.value);\n\t\t}\n\n\t\tregionsIndexes.value = [...regionsIndexesSaved];\n\t};\n\n\t/**\n\t * Сохранение выбранных регионов\n\t */\n\tconst compareSave = () => {\n\t\tif (regionsIndexes.value.length) {\n\t\t\tlocalStorage.setItem('ui:project:regionSelector:' + props.projectId + ':regionsIndexes', JSON.stringify(regionsIndexes.value));\n\t\t} else {\n\t\t\tlocalStorage.removeItem('ui:project:regionSelector:' + props.projectId + ':regionsIndexes');\n\t\t}\n\t};\n\n\twatch(regionsIndexes, () => {\n\t\tcompareSave();\n\t});\n\n\tif (props.addCompare) {\n\t\t// if (props.compareRegionsIndexes?.length) {\n\t\t// \tcompareRegionsIndexes.value = [...props.compareRegionsIndexes];\n\t\t// } else {\n\t\t// \tcompareLoad();\n\t\t// }\n\n\t\tcompareLoad();\n\t}\n\n\treturn {\n\t\tregionsIndexes,\n\t};\n};\n","import { computed, watch } from 'vue';\nimport { findRegion, genSearcherByKey } from '../utils/utils';\nimport { dummyIndex, globalRegionIndex, searcherUndefined, searhcerCompareKey } from '../utils/consts';\nimport type { Props, Region, SearcherIndexed } from '../types';\nimport { useSelectSearcher } from './selectSearcher';\nimport { useSelectRegion } from './selectRegion';\nimport { useCompare } from './compare';\n\n/**\n * Создание и управления рективными переменными компонента\n *\n * @param props - входные props компонента\n */\nexport const useSelectorRegion = (props: Props) => {\n\tconst searcherByKey = computed(() => {\n\t\treturn genSearcherByKey(props.forFrequency, props.autoRegion, props.searchers);\n\t});\n\n\tconst activeSearcherIndexed = computed(() => {\n\t\treturn searcherByKey.value.get(selectSearcher.searcherKey.value) || searcherUndefined;\n\t});\n\n\t/**\n\t * Все индексы регионов из searcherByKey\n\t *\n\t * Активность регионов определяется в searcherByKey\n\t */\n\tconst allRegionsIndexes = computed(() => {\n\t\tconst regionsIndexes = new Set<number>();\n\n\t\tsearcherByKey.value.forEach((searcher) => {\n\t\t\tsearcher.regionByIndex.forEach((region) => {\n\t\t\t\tif (region.index === globalRegionIndex) return;\n\t\t\t\tif (region.index === dummyIndex) return;\n\n\t\t\t\tregionsIndexes.add(region.index);\n\t\t\t});\n\t\t});\n\n\t\treturn regionsIndexes;\n\t});\n\n\tconst selectSearcher = useSelectSearcher(props, searcherByKey);\n\tconst selectRegion = useSelectRegion(props, activeSearcherIndexed);\n\tconst compare = useCompare(props, searcherByKey, allRegionsIndexes);\n\n\t// контроль за внешним изменением списка ПС\n\twatch(searcherByKey, () => {\n\t\t// возможные значения для сравнения регионов/пс\n\t\tif (props.onlySearcher) {\n\t\t\tcompare.regionsIndexes.value = Array.from(searcherByKey.value.keys());\n\t\t} else {\n\t\t\tcompare.regionsIndexes.value = compare.regionsIndexes.value.filter(regionIndex => {\n\t\t\t\treturn allRegionsIndexes.value.has(regionIndex);\n\t\t\t});\n\t\t}\n\n\t\tif (selectSearcher.searcherKey.value === searhcerCompareKey) return;\n\n\t\tlet newSearcherKey = searcherByKey.value.keys().next().value;\n\n\t\tsearcherByKey.value.forEach((searcher) => {\n\t\t\t// определить выбранную ПС\n\t\t\tif (props.onlySearcher && searcher.key === selectSearcher.searcherKey.value) {\n\t\t\t\tnewSearcherKey = selectSearcher.searcherKey.value;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\tselectRegion.regionIndex.value &&\n\t\t\t\tsearcher.regionByIndex?.has(selectRegion.regionIndex.value)\n\t\t\t) {\n\t\t\t\tnewSearcherKey = searcher.key;\n\t\t\t}\n\n\t\t\t// выбрать первую ПС с выбранным регионом\n\t\t\tif (!props.onlySearcher) {\n\t\t\t\tlet regionsNewSearcher: SearcherIndexed['regionByIndex'] | undefined;\n\n\t\t\t\tif (newSearcherKey !== undefined) {\n\t\t\t\t\tregionsNewSearcher = searcherByKey.value.get(newSearcherKey)?.regionByIndex;\n\t\t\t\t}\n\n\t\t\t\tconst regionsCurrentSearcher = searcherByKey.value.get(searcher.key)?.regionByIndex;\n\t\t\t\tif (\n\t\t\t\t\tregionsNewSearcher?.has(dummyIndex) &&\n\t\t\t\t\t!regionsCurrentSearcher?.has(dummyIndex)\n\t\t\t\t) {\n\t\t\t\t\tnewSearcherKey = searcher.key;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t// if (props.addCompare && !selectSearcher.optionBySearcherKey.value.has(dummyIndex)) {\n\t\t// \tif (!selectRegion.regionIndex.value && compare.regionsIndexes.value?.length) newSearcherKey = searhcerCompareKey;\n\t\t// }\n\n\t\t// if (props.onlySearcher && selectRegion.regionIndex.value !== dummyIndex) newSearcherKey = selectRegion.regionIndex.value;\n\n\t\tif (newSearcherKey !== undefined) {\n\t\t\tselectSearcher.searcherKey.value = newSearcherKey;\n\t\t}\n\n\t\tif (\n\t\t\tselectRegion.regionIndex.value !== undefined &&\n\t\t\t!(activeSearcherIndexed.value?.regionByIndex)?.has(selectRegion.regionIndex.value)\n\t\t) {\n\t\t\tselectRegion.regionIndex.value = activeSearcherIndexed.value?.regions?.keys().next().value as number;\n\t\t}\n\t}, { immediate: true });\n\n\tconst getSearcherKey = () => {\n\t\tif (selectSearcher.searcherKey.value === searhcerCompareKey || selectSearcher.searcherKey.value === dummyIndex) return;\n\n\t\treturn selectSearcher.searcherKey.value;\n\t};\n\n\tconst getRegionIndex = () => {\n\t\tif (props.onlySearcher) return;\n\n\t\tif (selectRegion.regionIndex.value === dummyIndex) return;\n\n\t\tlet res: Region['index'] | undefined = selectRegion.regionIndex.value;\n\n\t\t// в качестве ключа используется region.key\n\t\tif (props.forFrequency) {\n\t\t\tconst regionKey = selectRegion.regionIndex.value;\n\t\t\tconst region = findRegion(props.forFrequency, { searcher_key: getSearcherKey(), key: regionKey } as Region, props.searchers);\n\n\t\t\tres = region?.index;\n\t\t}\n\n\t\treturn res;\n\t};\n\n\t/**\n\t * Получить выбранную ПС\n\t */\n\tconst getSearcher = () => {\n\t\tconst searcherKey = getSearcherKey();\n\t\tif (searcherKey === undefined) return;\n\n\t\treturn searcherByKey.value.get(searcherKey);\n\t};\n\n\t/**\n\t * Получить выбранный регион\n\t */\n\tconst getRegion = () => {\n\t\tconst regionIndex = getRegionIndex();\n\t\tif (regionIndex === undefined) return;\n\n\t\treturn getSearcher()?.regionByIndex?.get(regionIndex);\n\t};\n\n\treturn {\n\t\tselectSearcher,\n\t\tselectRegion,\n\t\tcompare,\n\n\t\tsearcherByKey,\n\t\tallRegionsIndexes,\n\n\t\tgetSearcher,\n\t\tgetRegion,\n\t};\n};\n","<script setup lang=\"ts\">\nimport { watch } from 'vue';\nimport type { Props, Region } from './types';\nimport Select from '@/components/forms/select/select.vue';\nimport Button from '@/components/forms/button/button.vue';\nimport { useSelectorRegion } from './composables/selectorRegion';\nimport { dummyIndex, globalRegionIndex, searhcerCompareKey } from './utils/consts';\nimport { dialogRegionSelector } from '@/components/project/regionSelector/utils/utils';\n\nconst props = withDefaults(defineProps<Props>(), {\n\tsearchers: () => [],\n\t// compareRegionsIndexes: () => [],\n\taddSearcherIcon: true,\n\taddRegionIcon: true,\n\taddChanger: true,\n});\n\nconst model = defineModel<Props['modelValue']>({ required: true });\nconst modelSingle = defineModel<Props['modelValueSingle']>('modelValueSingle');\n\nconst {\n\tselectSearcher,\n\tselectRegion,\n\tcompare,\n\n\tsearcherByKey,\n\tallRegionsIndexes,\n\n\tgetSearcher,\n\tgetRegion,\n} = useSelectorRegion(props);\n\nconst onClickCompare = () => {\n\tconst regions: Region[] = [];\n\n\tsearcherByKey.value.forEach((searcher) => {\n\t\tif (!searcher.enabled) return;\n\n\t\tsearcher.regions.forEach((region) => {\n\t\t\tif (!region.enabled) return;\n\n\t\t\tregions.push(region);\n\t\t});\n\t});\n\n\tdialogRegionSelector.open('regions', {\n\t\tregions,\n\t\tregionsIndexes: compare.regionsIndexes.value,\n\t\t'@update:regionsIndexes': (regionsIndexes: number[]) => compare.regionsIndexes.value = regionsIndexes,\n\t});\n};\n\nwatch([selectRegion.regionIndex, selectSearcher.searcherKey, compare.regionsIndexes], () => {\n\tif (selectSearcher.searcherKey.value === searhcerCompareKey && compare.regionsIndexes.value.length) {\n\t\tif (JSON.stringify(model.value) === JSON.stringify(compare.regionsIndexes.value)) {\n\t\t\treturn;\n\t\t}\n\n\t\tmodel.value = [...compare.regionsIndexes.value];\n\t} else {\n\t\tif (props.onlySearcher) {\n\t\t\tmodel.value = [selectSearcher.searcherKey.value];\n\n\t\t\tif (selectSearcher.searcherKey.value === dummyIndex && !props.autoRegion) model.value.length = 0;\n\t\t} else {\n\t\t\tmodel.value = [selectRegion.regionIndex.value];\n\n\t\t\t// не добавлять несуществующий регион в результаты\n\t\t\tif (selectRegion.regionIndex.value === dummyIndex && !props.autoRegion) model.value.length = 0;\n\t\t}\n\t}\n\n\t// регионов нет\n\tif (!props.onlySearcher && !allRegionsIndexes.value.size) {\n\t\tselectSearcher.searcherKey.value = dummyIndex;\n\t}\n});\n\nif (modelSingle.value) {\n\twatch(modelSingle, () => {\n\t\tif (modelSingle.value) {\n\t\t\tmodel.value = [modelSingle.value];\n\t\t}\n\t}, { immediate: true });\n}\n\nwatch(model, () => {\n\tif (model.value[0]) {\n\t\tmodelSingle.value = model.value[0];\n\t}\n\n\t// проверка входных данных v-model на корректность\n\tif (props.onlySearcher) {\n\t\tif (\n\t\t\t!model.value.length ||\n\t\t\tmodel.value.length === 1 && !searcherByKey.value.has(model.value[0]) ||\n\t\t\tmodel.value.length === 1 && model.value[0] === dummyIndex && !props.autoRegion\n\t\t) {\n\t\t\tlet defaultKey: number | undefined = searcherByKey.value.keys().next().value;\n\n\t\t\t// запретить устанавливать несуществующий регион\n\t\t\tif (defaultKey === dummyIndex && !props.autoRegion) {\n\t\t\t\tdefaultKey = undefined;\n\n\t\t\t\tmodelSingle.value = dummyIndex;\n\t\t\t}\n\n\t\t\tif (defaultKey !== undefined) {\n\t\t\t\tmodel.value = [defaultKey];\n\t\t\t}else{\n\t\t\t\tmodel.value.length = 0;\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\tif (\n\t\t\tmodel.value.length > 1 &&\n\t\t\tJSON.stringify(model.value) !== JSON.stringify(compare.regionsIndexes.value)\n\t\t) {\n\t\t\tmodel.value = [...compare.regionsIndexes.value];\n\n\t\t\treturn;\n\t\t}\n\t} else {\n\t\tlet newModel = [...new Set(model.value)];\n\n\t\tlet defaultIndex = searcherByKey.value.values().next().value?.regionByIndex?.keys().next().value;\n\t\tif (props.forFrequency) {\n\t\t\tdefaultIndex = searcherByKey.value.values().next().value?.regionByIndex?.values().next().value?.key;\n\t\t}\n\n\t\t// запретить устанавливать несуществующий регион\n\t\tif (defaultIndex === dummyIndex && !props.autoRegion) {\n\t\t\tdefaultIndex = undefined;\n\n\t\t\tmodelSingle.value = dummyIndex;\n\t\t}\n\n\t\tif (!newModel.length) {\n\t\t\tif (defaultIndex !== undefined) {\n\t\t\t\tnewModel.push(defaultIndex);\n\t\t\t}\n\t\t} else if (newModel.length === 1) {\n\t\t\tlet all = allRegionsIndexes.value;\n\n\t\t\tif (props.forFrequency) {\n\t\t\t\tall = new Set();\n\t\t\t\tsearcherByKey.value.forEach((searcher) => {\n\t\t\t\t\tsearcher.regionByIndex.forEach((region) => {\n\t\t\t\t\t\tif (region.index === globalRegionIndex) return;\n\t\t\t\t\t\tif (region.index === dummyIndex) return;\n\n\t\t\t\t\t\tall.add(region.key);\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// убрано, так как dummyIndex запрещено выбирать в model\n\t\t\t// if (!all.has(newModel[0]) && newModel[0] !== dummyIndex) {\n\t\t\tif (!all.has(newModel[0])) {\n\t\t\t\tnewModel = [];\n\t\t\t\tif (defaultIndex !== undefined) {\n\t\t\t\t\tnewModel.push(defaultIndex);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tnewModel = newModel.filter(index => allRegionsIndexes.value.has(index));\n\t\t\tif (!newModel.length && defaultIndex !== undefined) {\n\t\t\t\tnewModel.push(defaultIndex);\n\t\t\t}\n\t\t}\n\n\t\tif (JSON.stringify(model.value) !== JSON.stringify(newModel)) {\n\t\t\tmodel.value = newModel;\n\n\t\t\treturn;\n\t\t}\n\t}\n\n\t// входные данные v-model совпадают с внутренними значениями\n\tif (\n\t\tmodel.value.length === 1 &&\n\t\tmodel.value[0] === (props.onlySearcher ? selectSearcher.searcherKey.value : selectRegion.regionIndex.value)\n\t) {\n\t\treturn;\n\t}\n\n\t// if (\n\t// \tmodel.value.length > 1 &&\n\t// \tselectorSearcher.searcherKey.value === searhcerCompareKey &&\n\t// \tJSON.stringify(model.value) === JSON.stringify(selectorCompare.regionsIndexes.value)\n\t// ) {\n\t// \treturn;\n\t// }\n\n\t// обновление regionIndex, searcherKey, selectorCompare.regionsIndexes\n\tif (props.onlySearcher) {\n\t\t// if (!model.value.length) {\n\t\t// \tselectorSearcher.searcherKey.value = dummyIndex;\n\t\t//\n\t\t// \treturn;\n\t\t// }\n\n\t\tif (model.value.length === 1) {\n\t\t\tselectSearcher.searcherKey.value = model.value[0];\n\n\t\t\treturn;\n\t\t}\n\n\t\tselectSearcher.searcherKey.value = searhcerCompareKey;\n\n\t\treturn;\n\t} else {\n\t\tif (!model.value.length) {\n\t\t\t// selectSearcher.searcherKey.value = dummyIndex;\n\t\t\t// selectRegion.regionIndex.value = dummyIndex;\n\n\t\t\treturn;\n\t\t}\n\n\t\tif (model.value.length === 1 && selectSearcher.searcherKey.value !== searhcerCompareKey) {\n\t\t\tselectRegion.regionIndex.value = model.value[0];\n\n\t\t\tlet newSearherKey: number | undefined;\n\t\t\tfor (const searcher of searcherByKey.value.values()) {\n\t\t\t\tfor (const region of searcher.regionByIndex.values()) {\n\t\t\t\t\tconst currentRegionIndex = props.forFrequency ? region.key : region.index;\n\t\t\t\t\tif (currentRegionIndex === selectRegion.regionIndex.value) {\n\t\t\t\t\t\tnewSearherKey = searcher.key;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (newSearherKey !== undefined) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (newSearherKey !== undefined) {\n\t\t\t\tselectSearcher.searcherKey.value = newSearherKey;\n\t\t\t}\n\t\t} else {\n\t\t\tselectSearcher.searcherKey.value = searhcerCompareKey;\n\t\t\tcompare.regionsIndexes.value = [...model.value];\n\t\t}\n\t}\n}, { immediate: true });\n\ndefineExpose({\n\tgetSearcher,\n\tgetRegion,\n});\n</script>\n\n<template>\n\t<div\n\t\t:class=\"{\n\t\t\t'top-selectorRegion': true,\n\t\t\t'top-selectorRegion-onlySearcher': onlySearcher,\n\t\t}\"\n\t>\n\t\t<Select\n\t\t\t:options=\"selectSearcher.optionBySearcherKey.value\"\n\t\t\tv-model=\"selectSearcher.searcherKey.value\"\n\t\t\tname=\"searcher_key\"\n\t\t\t:addChanger=\"addChanger\"\n\t\t/>\n\n\t\t<Select\n\t\t\tv-if=\"!onlySearcher && selectSearcher.searcherKey.value !== searhcerCompareKey\"\n\t\t\tclass=\"top-select-region\"\n\t\t\t:options=\"selectRegion.optionByRegionIndex.value\"\n\t\t\tv-model=\"selectRegion.regionIndex.value\"\n\t\t\t:name=\"forFrequency ? 'region_key' : 'region_index'\"\n\t\t\t:addChanger=\"addChanger\"\n\t\t\t:data-top-icon=\"addRegionIcon ? '' : undefined\"\n\t\t/>\n\n\t\t<Button\n\t\t\tv-if=\"addCompare && !onlySearcher && selectSearcher.searcherKey.value === searhcerCompareKey\"\n\t\t\tname=\"compare\"\n\t\t\t@click=\"onClickCompare\"\n\t\t\t:data-count-compare-regions-indexes=\"compare.regionsIndexes.value.length\"\n\t\t>\n\t\t\t{{ $i18n.Common.Selected_regions }}\n\t\t</Button>\n\t</div>\n</template>\n\n<style>\n@import \"./styles/searcherColors.css\";\n\n.top-selectorRegion {\n\twidth: 340px;\n\tdisplay: inline-flex;\n\tvertical-align: middle;\n}\n\n.top-selectorRegion > .top-select {\n\tflex-grow: 1;\n}\n\n.top-selectorRegion > .top-select:focus-within {\n\tz-index: 4;\n}\n\n.top-selectorRegion > .top-select > .top-select_select:hover,\n.top-selectorRegion > .top-select > .top-select_select.top-error {\n\tz-index: 1;\n}\n\n.top-selectorRegion > .top-select-searcher_key {\n\twidth: 140px;\n\tmax-width: 160px;\n\tmargin-right: -1px;\n}\n\n.top-selectorRegion > .top-select-region > select {\n\tborder-top-left-radius: 0;\n\tborder-bottom-left-radius: 0;\n}\n\n.top-selectorRegion > .top-select-region {\n\t--top-icon-size: 20px;\n\t--top-icon-color: var(--color-text-primary);\n\n\t--top-icon2-size: 20px;\n\t--top-icon2-color: var(--color-text-primary);\n}\n\n.top-selectorRegion > .top-select-region[data-top-icon][data-top-icon2] {\n\t--top-icon2-size: 16px;\n}\n\n.top-selectorRegion > .top-select-region[data-top-icon][data-top-icon2]:after {\n\ttext-indent: -4px;\n}\n\n.top-selectorRegion > [name=\"compare\"] {\n\tborder-top-left-radius: 0;\n\tborder-bottom-left-radius: 0;\n\tflex-grow: 1;\n}\n\n.top-selectorRegion > [name=\"compare\"]:after {\n\tcontent: \"(\" attr(data-count-compare-regions-indexes) \")\";\n\tmargin: 0 0 0 6px;\n}\n\n.top-selectorRegion:not(.top-selectorRegion-onlySearcher) > .top-select-searcher_key > select {\n\tborder-top-right-radius: 0;\n\tborder-bottom-right-radius: 0;\n\tmargin-right: 0;\n}\n\n.top-selectorRegion:not(.top-selectorRegion-onlySearcher) > .top-select-searcher_key[data-value=\"-1\"] > select {\n\tborder-right: none;\n}\n\n.top-selectorRegion-onlySearcher {\n\twidth: 140px;\n}\n</style>\n","// Генерация плоского массива объектов папок с заполнением childsIds и счетчиков\nimport type { Folder, FolderTree } from './types';\nimport type { Props } from '../types';\nimport { useI18n } from '@/core/app';\n\nexport const folderDefault: Folder = {\n\tid: 0,\n\tname: '/',\n\tpath: '/',\n};\n\n/**\n * Сгенерировать объект корневой папки для компонента\n *\n * Содержит измененное имя\n *\n * Необходимо предустанавливать для минимизации `rerender`\n */\nexport const genRootFolder = (folders: FolderTree | undefined, useSelectAll: boolean) => {\n\tlet rootFolder = folders?.['root'][0] ?? folderDefault;\n\trootFolder = { ...rootFolder };\n\n\trootFolder.name = genRootFolderName(useSelectAll);\n\n\treturn rootFolder;\n};\n\nfunction genRootFolderName(useSelectAll: boolean) {\n\tif (useSelectAll) {\n\t\treturn useI18n()?.Common.All_folders;\n\t} else {\n\t\treturn '/ (' + useI18n()?.Keywords.Root_folder + ')';\n\t}\n}\n\n/**\n * Сгенерировать плоский список папок\n *\n * В `folders` должен передаваться список всех папок, дозагрузка не предусмотрена\n */\nexport const genFlat = (\n\tfolderById: NonNullable<Props['folders']>,\n\tuseSelectAll: boolean,\n\tresultFolders = new Map<Folder['id'], Folder>(),\n\tparentFolder: Folder = { id: 'root' } as unknown as Folder,\n\tdepth = 0,\n) => {\n\tparentFolder.childsIds = [];\n\n\tif (parentFolder.id) {\n\t\tparentFolder.countAllGroupsActive = parentFolder.count_groups_active;\n\t}\n\n\tif (!folderById[parentFolder.id]) {\n\t\treturn resultFolders;\n\t}\n\n\tfolderById[parentFolder.id].forEach((folder) => {\n\t\tfolder = { ...folder };\n\n\t\tparentFolder.childsIds!.push(folder.id);\n\n\t\tif (folder.id === 0) folder.name = genRootFolderName(useSelectAll);\n\n\t\tconst prefix = depth > 1 ? '-'.repeat(depth - 1) + ' ' : '';\n\t\tif (prefix && !folder.name.startsWith(prefix)) folder.name = prefix + folder.name;\n\n\t\tresultFolders.set(folder.id, folder);\n\n\t\tgenFlat(folderById, useSelectAll, resultFolders, folder, depth + 1);\n\n\t\tparentFolder.childsIds = parentFolder.childsIds!.concat(folder.childsIds!);\n\n\t\tif (parentFolder.id && folder.count_groups_active) {\n\t\t\tparentFolder.countAllGroupsActive! += folder.count_groups_active;\n\t\t}\n\t});\n\n\treturn resultFolders;\n};\n\n/**\n * Создать API запрос на получение групп\n */\nexport const genApiGetFolders = (client: Api.Client<Api.TV.Paths, true>) => {\n\treturn client.gen('/get/keywords_2/folders/', ['id', 'parent_id', 'name', 'path']).changeParams({ orders: ['ord_path'] as any, limit: 100 });\n};\n","import { watch, type WatchOptions, type WatchSource, type WatchStopHandle } from 'vue';\n\ntype UnwrapSource<T> = T extends WatchSource<infer V>\n\t? V\n\t: T extends object ? T : never;\n\ntype Changes<T> = Partial<{\n\t[K in keyof T]: {\n\t\told: UnwrapSource<T[K]> | undefined;\n\t\tnew: UnwrapSource<T[K]>\n\t}\n}>;\n\n/**\n * useWatch — расширенный `watch` с измененным callback\n * - принимает объект с реактивными свойствами, точно такими же, как при указании в массиве в стандартном `watch`\n * - callback возвращает изменившиеся свойства в объекте `changes` с `{old, new}`\n *\n * Если у вас есть несколько `watch`, которые меняют одно состояние, то могут возникнуть проблемы:\n * - рост длины цепочек `watch`, когда один `watch` приводит к срабатыванию другого\n * - лишние вызовы при согласованных состояниях, когда разные `watch` устанавливают одно и то же значение\n * - гонка при несогласованных состояниях, когда разные `watch` в разные тики при этом одновременно могут устанавливать разные состояния\n *\n * Если у вас такой случай и его нельзя упростить, то используйте `useWatch`, в остальных случаях используйте только стандартный `watch`\n *\n * @example\n *\n * ```js\n *\n * useWatch({ a: ref(5), b: ref('word') }, (changes) => {\n * \tconsole.log(changes.a?.new, changes.a?.old);\n * \tconsole.log(changes.b?.new, changes.b?.old);\n * });\n * ```\n */\nexport function useWatch<T extends Record<string, WatchSource>>(\n\tsourcesObj: T,\n\tcb: (\n\t\tchanges: Changes<T>,\n\t\tonCleanup: (fn: () => void) => void,\n\t) => void | Promise<void>,\n\toptions?: WatchOptions,\n): WatchStopHandle {\n\tconst keys = Object.keys(sourcesObj) as (keyof T)[];\n\tconst sources = keys.map(k => sourcesObj[k]);\n\n\treturn watch(sources, (newValues, oldValues, onCleanup) => {\n\t\tconst changed = {} as Changes<T>;\n\n\t\tnewValues.forEach((val, i) => {\n\t\t\tif (!Object.is(val, oldValues[i])) {\n\t\t\t\tconst k = keys[i];\n\n\t\t\t\tchanged[k] = {\n\t\t\t\t\told: oldValues[i],\n\t\t\t\t\tnew: val,\n\t\t\t\t} as any;\n\t\t\t}\n\t\t});\n\n\t\tif (Object.keys(changed).length) cb(changed, onCleanup);\n\t}, options);\n}\n","<script setup lang=\"ts\">\nimport { computed, ref, watch } from 'vue';\nimport Selector2 from '@/components/formsExt/selector2/selector2.vue';\nimport type { Props } from './types';\nimport { folderDefault, genApiGetFolders, genFlat } from './utils';\nimport { apiSetSearchParamsFilter } from '@/components/formsExt/formsExt';\nimport { useWatch } from '@/core/utils/composables/useWatch';\n\nconst props = withDefaults(defineProps<Props>(), {\n\taddIcon: true,\n});\n\nconst modelFolderId = defineModel<Props['folderId']>('folderId', { required: true });\nconst modelFolder = defineModel<NonNullable<Props['folder']>>('folder', { default: folderDefault });\n\nconst apiGet = props.client && !props.folders ? genApiGetFolders(props.client) : undefined;\n\nconst refSelector = ref<InstanceType<typeof Selector2> | null>(null);\n\n// Настройка API\nwatch(() => props.projectId, () => {\n\tapiGet?.changeParams({\n\t\tproject_id: props.projectId,\n\t});\n\n\tapiGet?.setOptions({\n\t\tcheckFingerprint: 'TopGroupSelectorFolders:' + props.projectId,\n\t});\n\n\trefSelector.value?.resetCache();\n}, { immediate: true });\n\n/**\n * Плоский список папок\n */\nconst foldersFlat = computed(() => {\n\treturn genFlat(props.folders ?? { root: [folderDefault] }, props.canSelectAll);\n});\n\n/**\n * Проверка входного значения `modelFolderId` и изменение `modelFolder`\n *\n * Определяем существует ли папка:\n * - Если существует: меняем папку на указанную\n * - Если не существует: устанавливаем Корневую папку\n *\n * При загрузке групп по API проверка выполняется в асинхронном режиме\n */\nuseWatch({\n\tmodelFolder,\n\tmodelFolderId,\n}, async (changes) => {\n\tif (modelFolderId.value === modelFolder.value.id) {\n\t\treturn;\n\t}\n\n\t// изменение только `modelFolder`\n\tif (changes.modelFolder && !changes.modelFolderId) {\n\t\tmodelFolderId.value = modelFolder.value.id;\n\n\t\treturn;\n\t}\n\n\tif (changes.modelFolderId) {\n\t\tconst folderSelected = foldersFlat.value.get(props.folderId);\n\t\tif (folderSelected) {\n\t\t\tmodelFolder.value = folderSelected;\n\t\t\tmodelFolder.value = folderSelected;\n\t\t}\n\t}\n\n\tlet folderSelected = foldersFlat.value.get(props.folderId);\n\tif (!folderSelected) folderSelected = folderDefault;\n\n\tmodelFolder.value = folderSelected;\n\tmodelFolderId.value = folderSelected.id;\n\n\t// /**\n\t// * Для не api\n\t// */\n\t// if (props.folders) {\n\t// \tmodelFolder.value = folderSelected;\n\t// \tmodelFolderId.value = folderSelected.id;\n\t// }\n\t//\n\t// /**\n\t// * Для api проверка не делается\n\t// */\n\t// if (!props.folders) {\n\t// \tmodelFolder.value = folderSelected;\n\t// \tmodelFolderId.value = folderSelected.id;\n\t// }\n}, { immediate: true });\n\nwatch(modelFolder, () => {\n\tmodelFolderId.value = modelFolder.value.id;\n});\n</script>\n\n<template>\n\t<Selector2\n\t\tclass=\"top-groupSelector_folder\"\n\t\tv-model=\"modelFolder\"\n\t\t:items=\"folders ? [...foldersFlat.values()] : undefined\"\n\t\tsearch-type=\"inline\"\n\t\t:icon=\"addIcon ? '' : undefined\"\n\t\t:api=\"folders ? undefined : apiGet\"\n\t\t:apiSetSearchParams=\"(...args) => apiSetSearchParamsFilter(...args, 'name')\"\n\t\t:addChanger\n\t\tuseCache\n\t/>\n</template>\n","import type { Folder } from '../folders/types';\nimport type { Group } from './types';\nimport { useI18n } from '@/core/plugins/i18n';\nimport { folderDefault } from '../folders/utils';\nimport { ITEM_ID_ALL } from '@/components/formsExt/selector2/utils';\n\n/**\n * Элемент - группа ненайдена\n *\n * Указывается несуществующий id\n */\nexport const groupNone: Group = {\n\tid: -1,\n\tname: '--',\n\tfolder_id: folderDefault.id,\n\tfolder_path: folderDefault.path,\n};\n\n/**\n * Элемент для выбора всех групп\n */\nexport const groupAll: Group = {\n\tid: ITEM_ID_ALL,\n\tname: 'All groups',\n\tfolder_id: folderDefault.id,\n\tfolder_path: folderDefault.path,\n};\n\n/**\n * Элемент - `Все группы`\n */\nexport const genGroupAll = () => {\n\tgroupAll.name = useI18n()?.Common.All_groups;\n\n\treturn groupAll;\n};\n\n/**\n * Элемент - `Выберите группу`\n */\nexport const genGroupPlaceholder = () => {\n\tgroupAll.name = useI18n()?.Keywords.Choose_group;\n\n\treturn groupAll;\n};\n\n/**\n * Сгенерировать список групп для TopSelector\n */\nexport const genItems = (groups: Group[], on?: boolean, folderForFilter?: Folder) => {\n\tgroups = [...groups];\n\n\t// фильтр по активности\n\tif (on !== undefined) {\n\t\tgroups = groups.filter((group) => {\n\t\t\treturn group.on == Number(on);\n\t\t});\n\t}\n\n\t// фильтр по папке\n\tif (folderForFilter && folderForFilter.id && folderForFilter.childsIds) {\n\t\tgroups = groups.filter((group) => {\n\t\t\treturn (\n\t\t\t\tgroup.folder_id === folderForFilter.id ||\n\t\t\t\tfolderForFilter.childsIds!.includes(group.folder_id)\n\t\t\t);\n\t\t});\n\t}\n\n\treturn groups;\n};\n\n/**\n * Создать API запрос на получение групп\n */\nexport const genApiGetGroups = (client: Api.Client<Api.TV.Paths, true>) => {\n\treturn client.gen('/get/keywords_2/groups/', ['id', 'name', 'folder_id', 'folder_path']).changeParams({ folder_id_depth: true, limit: 100 });\n};\n\n/**\n * Создать API запрос на получение групп\n */\nexport const genApiAddGroup = (client: Api.Client<Api.TV.Paths, true>) => {\n\treturn client.gen('/add/keywords_2/groups/');\n};\n","<script setup lang=\"ts\">\nimport { reactive, ref } from 'vue';\nimport { useWatch } from '@/core/utils/composables/useWatch';\nimport Selector2 from '@/components/formsExt/selector2/selector2.vue';\nimport type { Item as SelectorItem } from '@/components/formsExt/selector2/types';\nimport { apiSetSearchParamsFilter, ITEM_ID_ALL, ITEM_ID_NEW } from '@/components/formsExt/formsExt';\nimport type { Emits, Group, Props } from './types';\nimport { genApiAddGroup, genApiGetGroups, genGroupAll, genGroupPlaceholder, genItems, groupNone } from './utils';\nimport { genFieldFilter } from '@/api/api/index';\nimport { folderDefault } from '../folders/utils';\n\nconst props = withDefaults(defineProps<Props>(), {\n\taddIcon: true,\n\taddChanger: true,\n\tautoselect: 'first',\n\ton: undefined,\n});\n\nconst emits = defineEmits<Emits>();\n\nconst modelGroupId = defineModel<Props['groupId']>('groupId', { required: true });\nconst modelGroup = defineModel<Props['group']>('group', { required: true });\n\nconst apiGet = props.client && !props.groups ? genApiGetGroups(props.client) : undefined;\nconst apiAdd = props.client ? genApiAddGroup(props.client) : undefined;\n\nconst refSelector = ref<InstanceType<typeof Selector2> | null>(null);\n\n/**\n * Группы выбранной папки\n */\nconst groupsBySelectedFolder = ref<Group[] | undefined>(undefined);\n\nconst filterByFolder = genFieldFilter('folder_id', 'EQUALS', [props.folder?.id ?? 0]);\n\napiGet?.changeParams({\n\tproject_id: props.projectId,\n\tfilters: [\n\t\tfilterByFolder,\n\t],\n});\n\nif (props.on !== undefined) {\n\tapiGet?.params.filters?.push(genFieldFilter('on', 'EQUALS', [props.on]));\n}\n\n/**\n * Синхронная установка группы и id группы\n *\n * Id рекомендуется вместе с группой\n * - для согласованной смены состояния\n * - для уменьшения цепочки `watch` синхронизации состояний `modelGroupId` и `modelGroup`\n *\n * Пример несогласованности: если передать `modelGroup` и значение `modelGroupId`, которое ему не соответсвует, то попытка установить ту же самоую группу в `modelGroup` не приведет к смене `modelGroupId`\n */\nconst setGroup = (group: Props['group']) => {\n\tmodelGroup.value = group;\n\tmodelGroupId.value = group.id;\n};\n\n// Настройка API, обработка смены папки и выбор всех групп\nuseWatch({\n\tprojectId: () => props.projectId,\n\tfolderId: () => props.folder?.id,\n\tcanSelectAll: () => props.canSelectAll,\n}, (changes) => {\n\t// Настройка API\n\tif (changes.projectId) {\n\t\tapiGet?.changeParams({\n\t\t\tproject_id: props.projectId,\n\t\t});\n\n\t\tapiGet?.setOptions({\n\t\t\tcheckFingerprint: 'TopGroupSelectorGroups:' + props.projectId,\n\t\t});\n\t}\n\n\t// Обработка смены папки\n\tif (changes.folderId) {\n\t\tfilterByFolder.values = [\n\t\t\tprops.folder?.id ?? 0,\n\t\t];\n\n\t\t// необходимо установить группы выбранной папки\n\t\tif (props.groups) {\n\t\t\tgroupsBySelectedFolder.value = genItems(props.groups, props.on, props.folder);\n\t\t}\n\t}\n\n\trefSelector.value?.resetCache();\n\n\t/**\n\t * При смене папки автоматически выбирать нужную группу с учетом `props.autoselect`\n\t *\n\t * Новая группа, при смене папки не меняется\n\t */\n\tif (\n\t\t(changes.folderId?.old !== undefined || changes.canSelectAll?.old !== undefined) &&\n\t\tmodelGroupId.value !== ITEM_ID_NEW\n\t) {\n\t\tlet group: Props['group'] | undefined;\n\n\t\tif (props.autoselect === 'first') {\n\t\t\tgroup = groupsBySelectedFolder.value?.[0];\n\n\t\t\tif (props.canSelectAll) {\n\t\t\t\tgroup = reactive(genGroupAll());\n\t\t\t}\n\t\t}\n\n\t\tif (props.autoselect === 'placeholder' || !group) {\n\t\t\tgroup = genGroupPlaceholder();\n\t\t}\n\n\t\tsetGroup(group);\n\t}\n}, { immediate: true });\n\n/**\n * Проверка входного значения `modelGroupId` и изменение `modelGroup`\n *\n * Определяем существует ли группа:\n * - Если существует: меняем группу на указанную\n * - Если не существует: устанавливаем \"Нет группы\"\n *\n * При загрузке групп по API проверка выполняется в асинхронном режиме\n */\nuseWatch({\n\tmodelGroup,\n\tmodelGroupId,\n}, async (changes) => {\n\tif (modelGroupId.value === modelGroup.value.id) {\n\t\treturn;\n\t}\n\n\t// изменение только `modelGroup`\n\tif (changes.modelGroup && !changes.modelGroupId) {\n\t\tmodelGroupId.value = modelGroup.value.id;\n\n\t\treturn;\n\t}\n\n\tif (props.canAdd && modelGroupId.value === ITEM_ID_NEW) {\n\t\treturn;\n\t}\n\n\t/**\n\t * Для не api\n\t */\n\tif (props.groups) {\n\t\tlet groupSelected = groupsBySelectedFolder.value?.find((group) => group.id === modelGroupId.value);\n\n\t\tif (!groupSelected && props.canSelectAll) groupSelected = genGroupAll();\n\n\t\tif (groupSelected) {\n\t\t\tsetGroup(groupSelected);\n\t\t} else {\n\t\t\tconst firstGroup = groupsBySelectedFolder.value?.[0];\n\t\t\tif (firstGroup && !modelGroupId.value) {\n\t\t\t\t// группа не была указана, инициализация компонента\n\t\t\t\tsetGroup(firstGroup);\n\t\t\t} else {\n\t\t\t\tsetGroup(groupNone);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Для api\n\t */\n\tif (props.client && !props.groups && (modelGroupId.value || modelGroupId.value === ITEM_ID_ALL && !props.canSelectAll)) {\n\t\tconst api = genApiGetGroups(props.client).changeParams({\n\t\t\tproject_id: props.projectId,\n\t\t\tid: modelGroupId.value,\n\t\t\tfilters: [\n\t\t\t\tfilterByFolder,\n\t\t\t],\n\t\t});\n\n\t\t/**\n\t\t * @remarks При выборе группы \"Все группы\" или при добавлении группы отмена запроса не происходит\n\t\t */\n\t\tapi?.setOptions({\n\t\t\tcheckFingerprint: 'TopGroupSelectorFindGroup:' + props.projectId,\n\t\t});\n\n\t\tconst res = await api.call();\n\n\t\t// при отмене запроса, смену группы не выполняем\n\t\tif (!res.errors?.length && !res.result) return;\n\n\t\tif (!res.errors && res.result[0]) {\n\t\t\tsetGroup(res.result[0] as Group);\n\t\t} else {\n\t\t\tsetGroup(groupNone);\n\t\t}\n\t}\n}, { immediate: true });\n\nconst addGroups = async (group: SelectorItem) => {\n\t// просто выбор произвольной группы, без реального добавления\n\tif (props.canAdd !== 'api') return;\n\n\t// при добавлении группы `props.client` обязателен\n\tif (!apiAdd) return;\n\n\tif (import.meta.env.STORYBOOK) {\n\t\tapiAdd.changeParams({\n\t\t\t// @ts-ignore Для storybook\n\t\t\t__sbIsUseAPI: !props.groups,\n\t\t});\n\t}\n\n\tconst modelGroupOld = modelGroup.value;\n\n\t// Для api\n\tconst res = await apiAdd.changeParams({\n\t\tproject_id: props.projectId,\n\t\tnames: [group.name],\n\t\tto_id: props.folder?.id ?? folderDefault.id,\n\t\tto_type: 'in_folder_last',\n\t}).call();\n\n\tif (res.result) {\n\t\tsetGroup(res.result as unknown as Group);\n\n\t\temits('addGroup', res.result as unknown as Group);\n\t} else {\n\t\t// вернуть предыдущий выбор в случае ошибки добавления группы\n\t\tsetGroup(modelGroupOld);\n\t}\n\n\trefSelector.value?.resetCache(true);\n};\n</script>\n\n<template>\n\t<Selector2\n\t\tref=\"refSelector\"\n\t\tclass=\"top-groupSelector_group\"\n\t\tv-model=\"modelGroup\"\n\t\t:items=\"groupsBySelectedFolder ?? groups\"\n\t\tsearchType=\"inline\"\n\t\t:icon=\"addIcon ? '' : undefined\"\n\t\t:api=\"groups ? undefined : apiGet\"\n\t\t:apiSetSearchParams=\"(...args) => apiSetSearchParamsFilter(...args, 'name')\"\n\t\t:appendSearchToResult=\"!!canAdd\"\n\t\t:useAllItem=\"canSelectAll ? genGroupAll().name : false\"\n\t\t:addChanger\n\t\tuseCache\n\t\t@appendItem=\"addGroups\"\n\t>\n\t\t<template #item=\"{ item }\">\n\t\t\t<div class=\"top-groupSelector_groupItem\">\n\t\t\t\t<div\n\t\t\t\t\tclass=\"top-comment\"\n\t\t\t\t\tv-if=\"item.id === ITEM_ID_NEW\"\n\t\t\t\t>\n\t\t\t\t\t{{ $i18n.Common.Add }}:\n\t\t\t\t</div>\n\n\t\t\t\t<span>{{ item.name }}</span>\n\n\t\t\t\t<span v-if=\"item.folder_path\" class=\"top-groupSelector_groupItemFolderPath\">{{ item.folder_path }}</span>\n\t\t\t</div>\n\t\t</template>\n\t</Selector2>\n</template>\n\n<style>\n.top-groupSelector_groupItem {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: var(--top-padding-1);\n}\n\n.top-groupSelector_groupItemFolderPath {\n\twidth: 100%;\n\tcolor: var(--color-text-4);\n\tfont-size: 12px;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { type ModelRef, ref } from 'vue';\nimport type { Emits, Props } from './types';\nimport type { Folder } from './folders/types';\nimport type { Group } from './groups/types';\nimport Folders from './folders/folders.vue';\nimport Groups from './groups/groups.vue';\nimport { folderDefault, genFlat, genRootFolder } from './folders/utils';\nimport { genGroupAll, genGroupPlaceholder, groupNone } from './groups/utils';\n\nconst props = withDefaults(defineProps<Props>(), {\n\tcanSelectAll: false,\n\tautoselect: 'first',\n\taddChanger: true,\n\tshowFolders: true,\n\tshowGroups: true,\n\taddIcon: true,\n});\n\nconst modelFolderId = defineModel<Props['folderId']>('folderId', { required: true });\n\nconst modelGroupId = defineModel<Props['groupId']>('groupId', { required: true });\nconst modelGroup = defineModel<Props['group']>('group') as ModelRef<NonNullable<Props['group']>>; // значение по умолчанию задается ниже\n\nconst emit = defineEmits<Emits>();\n\n/**\n * Выбранная папка\n *\n * Спекулятивное предсказание выбранной папки\n *\n * Если папка не определена, она ставится в `undefined`, чтобы при последующем определении не фиксировались события смены папки, см. `groups.vue`\n */\nconst folder = ref<Folder | undefined>(genRootFolder(props.folders, props.canSelectAllGroups));\nconst foldersFlat = genFlat(props.folders ?? { root: [folderDefault] }, props.canSelectAllGroups);\nfolder.value = foldersFlat.get(modelFolderId.value);\n\n/**\n * Спекулятивное предсказание выбранной группы, чтобы не было лишней перерисовки\n *\n * Точная проверка происходит в `groups.vue`\n */\nlet groupInitial = props.groups?.find((group) => group.id === modelGroupId.value);\nif (!modelGroupId.value) groupInitial = props.groups?.[0];\nif (props.canSelectAllGroups && !groupInitial) {\n\tgroupInitial = genGroupAll();\n}\nif (props.canSelectAllGroups && !groupInitial) {\n\tgroupInitial = genGroupAll();\n}\nif (props.autoselect === 'placeholder' || !groupInitial) {\n\tgroupInitial = genGroupPlaceholder();\n}\ngroupInitial ??= groupNone;\n\nmodelGroup.value ??= groupInitial;\n\nconst onAddGroup = (group: Group) => {\n\t// если загрузка групп производится по api, то добавлять новые группы в `props.groups` не нужно\n\tlet groups: Group[] | undefined;\n\n\tif (props.groups && group) {\n\t\tgroups = [...props.groups, group];\n\t}\n\n\temit('update:groups', groups);\n};\n</script>\n\n<template>\n\t<div class=\"top-groupSelector\">\n\t\t<Folders\n\t\t\tv-if=\"showFolders\"\n\t\t\tv-model:folderId=\"modelFolderId\"\n\t\t\tv-model:folder=\"folder\"\n\t\t\t:projectId\n\t\t\t:folders\n\t\t\t:canSelectAll=\"canSelectAllGroups\"\n\t\t\t:addChanger\n\t\t\t:addIcon\n\t\t\t:client\n\t\t/>\n\n\t\t<Groups\n\t\t\tv-if=\"showGroups\"\n\t\t\tv-model:groupId=\"modelGroupId\"\n\t\t\tv-model:group=\"modelGroup\"\n\t\t\t:projectId\n\t\t\t:folder\n\t\t\t:groups\n\t\t\t:on\n\t\t\t:canAdd=\"canAddGroup\"\n\t\t\t:canSelectAll=\"canSelectAllGroups\"\n\t\t\t:autoselect\n\t\t\t:addChanger\n\t\t\t:addIcon\n\t\t\t:client\n\t\t\t@addGroup=\"onAddGroup\"\n\t\t/>\n\t</div>\n</template>\n\n<style>\n.top-groupSelector {\n\tdisplay: inline-flex;\n\tgap: var(--top-padding-1);\n}\n</style>\n","import type { Props as TagSelectorOpenerProps } from '../popupOpener/types';\nimport type { Tag, TagIdExclude } from '../types';\n\n/**\n * Полуичить значение исключения тега tagId\n */\nexport const genTagIdExcluded = (tagId: Tag['id']): TagIdExclude => {\n\treturn '-' + tagId as TagIdExclude;\n};\n\n/**\n * Вернуть чистый tagId без exclude флага\n */\nexport const genTagIdClear = (tagId: Tag['id'] | TagIdExclude) => {\n\tif (tagId[0] === '-') return tagId.substring(1) as Tag['id'];\n\n\treturn tagId as Tag['id'];\n};\n\n/**\n * Получить инфомрацию о теге по его id\n */\nexport const getTagById = (tagId: Tag['id'] | TagIdExclude, tags: TagSelectorOpenerProps['tags']) => {\n\ttagId = genTagIdClear(tagId);\n\n\tconst tag = tags.find(tag => tag.id === tagId);\n\tif (!tag) return;\n\n\treturn tag;\n};\n","import { isRef, type Ref, ref, unref, watch } from 'vue';\nimport { useI18n } from '@/core/plugins/i18n';\nimport { storage } from '@/core/utils/dom';\nimport TopPopupWorker from '@/components/popup/lib/worker';\nimport type { OpenerProps as PopupOpenerProps } from '@/components/popup/popup/opener/types';\nimport type { Props as TagSelectorOpenerProps, TagSelectorTarget } from '../popupOpener/types';\nimport type { Props as TagIconProps } from '../tagIcon/types';\nimport { genTagIdClear, getTagById } from './utils';\n\n/**\n * Сгенерировать элемент для открытия popup с выбором тегов\n *\n * Альтернатива TopTagSelectorPopupOpener для работы вне vue\n *\n * Требует монтирования компонента TopTagSelector\n */\nexport const genElPopupOpener = (\n\tprops: TagSelectorOpenerProps & { modelValue: TagSelectorOpenerProps['modelValue'] | Ref<TagSelectorOpenerProps['modelValue']> },\n\tpropsPopup?: Partial<PopupOpenerProps>,\n\thtmlSetterSeveral?: string,\n): HTMLElement => {\n\tif (!propsPopup) propsPopup = { id: props.id };\n\n\t// Значения по умолчанию см. в: `@/components/popup/popup/opener/opener.vue`\n\tpropsPopup.id = props.id;\n\tpropsPopup.pos ??= '3';\n\tpropsPopup.notch ??= true;\n\tpropsPopup.posBy ??= 'fixed';\n\n\tconst el = TopPopupWorker.genElPopupOpener('div', propsPopup as PopupOpenerProps);\n\n\tel.classList.add('top-tagSelector');\n\tif (props.useTopButton) el.classList.add('top-tagSelector-useTopButton', 'top-button', 'top-color_theme', 'top-as-selector');\n\tif (!props.useTopButton) el.classList.add('top-tagSelector-custom');\n\tif (props.mode === 'filter') el.classList.add('top-tagSelector-filter');\n\tif (props.mode === 'setter' && !props.filters) el.classList.add('top-tagSelector-setter_single');\n\tif (props.mode === 'setter' && props.filters) el.classList.add('top-tagSelector-setter_several');\n\n\t// прокcи клик: lazy connect\n\tel.onclick = (e) => {\n\t\te.preventDefault();\n\t\te.stopPropagation();\n\n\t\tel.onclick = null;\n\n\t\tconst model = ref(props.modelValue);\n\n\t\tconst target: TagSelectorTarget = {\n\t\t\tmodel,\n\t\t\tmode: props.mode,\n\t\t\ttargetId: props.targetId,\n\t\t\tfilters: props.filters,\n\t\t\tpayload: props.payload,\n\t\t};\n\n\t\tstorage(el, 'topTagSelectorTarget', target);\n\n\t\tdelete el.dataset.topPopupDisabled;\n\n\t\tif (!isRef(props.modelValue)) {\n\t\t\twatch(model, () => {\n\t\t\t\tprops.modelValue = model.value;\n\n\t\t\t\telPopupOpenerRender(el, props, htmlSetterSeveral);\n\t\t\t});\n\t\t}\n\n\t\t// выполнение клика\n\t\tel.click();\n\t};\n\n\tif (isRef(props.modelValue)) {\n\t\twatch(props.modelValue, () => elPopupOpenerRender(el, props, htmlSetterSeveral));\n\t} else {\n\t\tstorage(el, 'topTagSelectorRender', (modelValue: TagSelectorOpenerProps['modelValue']) => {\n\t\t\tprops.modelValue = modelValue;\n\n\t\t\tconst target = storage(el, 'topTagSelectorTarget');\n\t\t\tif (target) target.model.value = modelValue;\n\n\t\t\telPopupOpenerRender(el, props, htmlSetterSeveral);\n\t\t});\n\t}\n\n\telPopupOpenerRender(el, props, htmlSetterSeveral);\n\n\treturn el;\n};\n\n/**\n * Вызвать перерисовку элемента, который был создан через genElPopupOpener()\n *\n * Работет только для элементов не с реактивным modelValue\n */\nexport const renderElPopupOpener = (el: HTMLElement, modelValue: TagSelectorOpenerProps['modelValue']) => {\n\tstorage(el, 'topTagSelectorRender')?.(modelValue);\n};\n\n/**\n * Отрисовать TopTagSelecorPopupOpener\n */\nconst elPopupOpenerRender = (el, props: TagSelectorOpenerProps, htmlSetterSeveral?: string) => {\n\tconst tagsIds = unref(props.modelValue);\n\n\tel.classList.toggle('top-tagSelector-selectedOne', !tagsIds.length || tagsIds.length === 1);\n\tel.classList.toggle('top-tagSelector-toTwoLine', tagsIds.length > 5);\n\n\tif (props.mode === 'setter' && props.filters) {\n\t\tel.innerHTML = `<div>${htmlSetterSeveral}</div>`;\n\n\t\treturn;\n\t}\n\n\tel.innerHTML = '';\n\n\tif (!tagsIds.length && props.mode === 'filter') {\n\t\tconst elTagIcon = genElTagIcon({\n\t\t\tid: 'all',\n\t\t\tcolorId: '',\n\t\t\tname: useI18n().Common?.All_tags ?? '',\n\t\t\tstate: '',\n\t\t});\n\n\t\tel.append(elTagIcon);\n\t}\n\n\ttagsIds.forEach(tagId => {\n\t\tconst elTagIcon = genElTagIcon({\n\t\t\tid: genTagIdClear(tagId),\n\t\t\tcolorId: getTagById(tagId, props.tags)?.color_id ?? '',\n\t\t\tname: getTagById(tagId, props.tags)?.name ?? '',\n\t\t\tstate: genTagIdClear(tagId) === tagId ? 'selected' : 'excluded',\n\t\t});\n\n\t\tel.append(elTagIcon);\n\t});\n};\n\n/**\n * Сгенерировтаь элемент для открытия popup с выбором тегов\n *\n * Альтернатива TopTagSelecorTagIcon для работы вне vue\n */\nconst genElTagIcon = (props: TagIconProps) => {\n\tconst el = document.createElement('div');\n\n\tel.classList.add('top-tagSelector_tagIcon');\n\tel.classList.toggle('top-tagSelector-active', !!props.state);\n\tel.classList.toggle('top-tagSelector-excluded', props.state === 'excluded');\n\n\tel.dataset.tag_id = props.id;\n\tel.dataset.tag_color_id = props.colorId;\n\tel.title = props.name;\n\n\treturn el;\n};\n","import type { Tag } from './types';\n\nexport default [\n\t{\n\t\tid: '1',\n\t\tname: 'Without Tag',\n\t\tcolor_id: '1',\n\t},\n\t{\n\t\tid: '2',\n\t\tname: 'Red',\n\t\tcolor_id: '2',\n\t},\n\t{\n\t\tid: '3',\n\t\tname: 'Orange',\n\t\tcolor_id: '3',\n\t},\n\t{\n\t\tid: '4',\n\t\tname: 'Yellow',\n\t\tcolor_id: '4',\n\t},\n\t{\n\t\tid: '5',\n\t\tname: 'Blue',\n\t\tcolor_id: '5',\n\t},\n\t{\n\t\tid: '6',\n\t\tname: 'Purple',\n\t\tcolor_id: '6',\n\t},\n\t{\n\t\tid: '7',\n\t\tname: 'Green',\n\t\tcolor_id: '7',\n\t},\n\t{\n\t\tid: '8',\n\t\tname: 'Magenta',\n\t\tcolor_id: '8',\n\t},\n\t{\n\t\tid: '9',\n\t\tname: 'Dark blue',\n\t\tcolor_id: '9',\n\t},\n\t{\n\t\tid: '10',\n\t\tname: 'Turquoise',\n\t\tcolor_id: '10',\n\t},\n] as Tag[];\n","<script setup lang=\"ts\">\nimport type { Props } from './types';\n\ndefineProps<Props>();\n</script>\n\n<template>\n\t<div\n\t\t:class=\"{\n\t\t\t'top-tagSelector_tagIcon': true,\n\t\t\t'top-tagSelector-active': !!state,\n\t\t\t'top-tagSelector-excluded': state === 'excluded',\n\t\t}\"\n\t\t:data-tag_id=\"id\"\n\t\t:data-tag_color_id=\"colorId\"\n\t\t:title=\"name\"\n\t></div>\n</template>\n\n<style>\n[data-tag_id] { display: inline-flex; gap: var(--top-gap-2); align-items: center; }\n\n[data-tag_id=\"\"] { border-color: #bdc3c7 !important; color: #55555F !important; }\n[data-tag_color_id=\"1\"] { border-color: var(--color-tag-1) !important; color: #55555F !important; }\n[data-tag_color_id=\"2\"] { border-color: var(--color-tag-2) !important; color: var(--color-tag-2) !important; }\n[data-tag_color_id=\"3\"] { border-color: var(--color-tag-3) !important; color: var(--color-tag-3) !important; }\n[data-tag_color_id=\"4\"] { border-color: var(--color-tag-4) !important; color: var(--color-tag-4) !important; }\n[data-tag_color_id=\"5\"] { border-color: var(--color-tag-5) !important; color: var(--color-tag-5) !important; }\n[data-tag_color_id=\"6\"] { border-color: var(--color-tag-6) !important; color: var(--color-tag-6) !important; }\n[data-tag_color_id=\"7\"] { border-color: var(--color-tag-7) !important; color: var(--color-tag-7) !important; }\n[data-tag_color_id=\"8\"] { border-color: var(--color-tag-8) !important; color: var(--color-tag-8) !important; }\n[data-tag_color_id=\"9\"] { border-color: var(--color-tag-9) !important; color: var(--color-tag-9) !important; }\n[data-tag_color_id=\"10\"] { border-color: var(--color-tag-10) !important; color: var(--color-tag-10) !important; }\n\n[data-tag_id=\"\"].top-tagSelector-active { background: #bdc3c7 !important; }\n[data-tag_color_id=\"1\"].top-tagSelector-active { background: var(--color-tag-1) !important; }\n[data-tag_color_id=\"2\"].top-tagSelector-active { background: var(--color-tag-2) !important; }\n[data-tag_color_id=\"3\"].top-tagSelector-active { background: var(--color-tag-3) !important; }\n[data-tag_color_id=\"4\"].top-tagSelector-active { background: var(--color-tag-4) !important; }\n[data-tag_color_id=\"5\"].top-tagSelector-active { background: var(--color-tag-5) !important; }\n[data-tag_color_id=\"6\"].top-tagSelector-active { background: var(--color-tag-6) !important; }\n[data-tag_color_id=\"7\"].top-tagSelector-active { background: var(--color-tag-7) !important; }\n[data-tag_color_id=\"8\"].top-tagSelector-active { background: var(--color-tag-8) !important; }\n[data-tag_color_id=\"9\"].top-tagSelector-active { background: var(--color-tag-9) !important; }\n[data-tag_color_id=\"10\"].top-tagSelector-active { background: var(--color-tag-10) !important; }\n\n[data-tag_id=\"all\"] {\n\tborder-color: transparent !important; background: none !important;\n}\n\n[data-tag_id=\"all\"]:before {\n\tcontent: \"\";\n\tcolor: var(--color-tag-all) !important;\n\tfont-size: 26px;\n\tmin-width: calc(100% + 4px);\n\theight: calc(100% + 4px);\n\tmargin-top: -18px;\n\tmargin-left: -8px;\n}\n\n/* в кнопке выбора тэгов и в popup */\n.top-tagSelector-useTopButton [data-tag_id],\n.top-tagSelector_popup [data-tag_id] {\n\t--top-tag-selector-size: 10px;\n\n\tbox-sizing: content-box;\n\tborder-radius: 100%; border: 2px solid var(--color-layout-front-1); background: var(--color-layout-front-1);\n\twidth: var(--top-tag-selector-size);\n\theight: var(--top-tag-selector-size);\n\tfont-size: calc(var(--top-tag-selector-size) + 4px);\n\t/*font-family: inherit !important;*/\n}\n\n/* исключение тегов */\n.top-body-press_ctrl .top-tagSelector_tagListItem-canExclude:hover [data-tag_id],\n[data-tag_id].top-tagSelector-excluded { position: relative; }\n\n.top-body-press_ctrl .top-tagSelector_tagListItem-canExclude:hover [data-tag_id]:before,\n[data-tag_id].top-tagSelector-excluded:before {\n\tcontent: \"\";\n\twidth: calc(var(--top-tag-selector-size) * 2); height: 1px; background: inherit;\n\ttransform: rotate(-45deg); filter: brightness(80%);\n\tdisplay: block;\n\tposition: absolute; top: calc(var(--top-tag-selector-size) / 2); left: calc(0px - var(--top-tag-selector-size) / 2);\n}\n.top-body-press_ctrl .top-tagSelector_tagListItem-canExclude:hover [data-tag_id]:not([data-tag_id=\"all\"]):not(.top-tagSelector-active):before { background: #AAA; }\n[data-tag_id].top-tagSelector-excluded:after,\n[data-tag_id].top-tagSelector-excluded ~ .top-tagSelector_tagListItemName { text-decoration: line-through; }\n\n/* popup */\n.top-popup_listItem.top-tagSelector-active { background: var(--top-popup-background-color-active) !important; }\n\n.top-popup_listItem:hover [data-tag_id=\"all\"]:before { border-color: var(--top-popup-background-color-hover); }\n.top-popup_listItem.top-tagSelector-active [data-tag_id=\"all\"]:before,\n.top-popup_listItem.top-active [data-tag_id=\"all\"]:before { border-color: var(--top-popup-background-color-active); }\n</style>\n","<script setup lang=\"ts\">\nimport { computed, nextTick, ref } from 'vue';\nimport type { Emits, Props } from './types';\nimport TopTagIcon from '../tagIcon/tagIcon.vue';\nimport TopPopupListItem from '@/components/popup/popup/listItem.vue';\n\nconst props = defineProps<Props>();\nconst emit = defineEmits<Emits>();\n\nconst name = defineModel<Props['name']>('name', {\n\trequired: true,\n});\n\nconst elName = ref<HTMLElement | null>(null);\n\n/**\n * @todo Удалить через пол года после выхода версии firefox с поддержкой contenteditable plaintext-only, включая бета версии\n */\nconst firefoxProps = computed(() => {\n\tif (navigator.userAgent.indexOf('Firefox') != -1) {\n\n\t\treturn {\n\t\t\tcontenteditable: inEdit.value,\n\t\t\tonpaste: (event: Event) => event.preventDefault(),\n\t\t};\n\t}\n\n\treturn {};\n});\n\n/**\n * Включен режим редактирвоания\n */\nconst inEdit = ref(false);\n\n/**\n * Включить режим редактирование\n */\nconst turnToEdit = async () => {\n\tinEdit.value = true;\n\n\tawait nextTick();\n\n\telName.value?.focus();\n};\n\n/**\n * Применить редактирование\n */\nconst editCommit = () => {\n\tconst name = elName.value?.innerText;\n\tif (!name) return editCancel();\n\n\tif (elName.value) elName.value.innerText = name;\n\n\tinEdit.value = false;\n\n\temit('update:name', name);\n};\n\n/**\n * Отменить редактирование\n */\nconst editCancel = async () => {\n\tif (elName.value) elName.value.innerText = props.name;\n\n\tinEdit.value = false;\n};\n\n/**\n * Применить выбор\n */\nconst changeSelect = (e: MouseEvent) => {\n\t// в режиме редактирования выбор не применяется\n\tif (inEdit.value) return;\n\n\t// тег запрещено выбирать\n\tif (props.disabled) return;\n\n\tlet toState: Props['state'] = 'selected';\n\n\tif (props.canExclude) {\n\t\tif (e.ctrlKey || e.metaKey) {\n\t\t\ttoState = 'excluded';\n\t\t}\n\t}\n\n\tif (props.state == toState) {\n\t\ttoState = '';\n\t}\n\n\tif (toState === '') emit('unselect');\n\tif (toState === 'selected') emit('select');\n\tif (toState === 'excluded') emit('exclude');\n};\n</script>\n\n<template>\n\t<TopPopupListItem\n\t\t:class=\"{\n\t\t\t'top-tagSelector_tagListItem': true,\n\t\t\t'top-tagSelector_tagListItem-inEdit': inEdit,\n\t\t\t'top-tagSelector_tagListItem-disabled': disabled,\n\t\t\t'top-tagSelector_tagListItem-canExclude': canExclude,\n\t\t\t'top-tagSelector-active': !!state,\n\t\t\t'top-tagSelector-excluded': state === 'excluded'\n\t\t}\"\n\t\t@click.stop=\"changeSelect\"\n\t>\n\t\t<TopTagIcon\n\t\t\t:id\n\t\t\t:name\n\t\t\t:colorId\n\t\t\t:state\n\t\t/>\n\n\t\t<span\n\t\t\tref=\"elName\"\n\t\t\tclass=\"top-tagSelector_tagListItemName\"\n\t\t\t:contenteditable=\"inEdit ? 'plaintext-only' : false\"\n\t\t\t:=\"firefoxProps\"\n\t\t\t@keydown.enter.stop=\"editCommit\"\n\t\t\t@keydown.esc.stop=\"editCancel\"\n\t\t>\n\t\t\t{{ name }}\n\t\t</span>\n\n\t\t<template v-if=\"editable\">\n\t\t\t<span\n\t\t\t\tv-if=\"!inEdit\"\n\t\t\t\tdata-top-icon=\"\"\n\t\t\t\tclass=\"top-tagSelector_edit\"\n\t\t\t\t@click=\"turnToEdit\"\n\t\t\t></span>\n\n\t\t\t<span\n\t\t\t\tv-else\n\t\t\t\tdata-top-icon=\"\"\n\t\t\t\tclass=\"top-tagSelector_edit\"\n\t\t\t\t@click.stop=\"editCommit\"\n\t\t\t></span>\n\t\t</template>\n\t</TopPopupListItem>\n</template>\n\n<style>\n.top-tagSelector_tagListItem.top-popup_listItem {\n\tpadding-top: var(--top-padding-2) !important;\n\tpadding-bottom: var(--top-padding-2) !important;\n\tgap: 10px;\n}\n\n.top-tagSelector_tagListItemName {\n\tborder-radius: var(--top-radius-1);\n\twidth: 120px;\n\tpadding: 4px 2px;\n\tmargin: -4px -2px;\n\ttext-overflow: ellipsis;\n\toverflow: hidden;\n\tflex-grow: 1;\n}\n\n/* редактирование */\n.top-tagSelector_edit {\n\t--top-icon-size: 18px;\n\t--top-icon-color: var(--color-text-3);\n\n\twidth: 20px;\n\tflex-grow: 0;\n}\n\n.top-tagSelector_edit:before {\n\tline-height: 0;\n}\n\n.top-tagSelector_edit { opacity: 0; }\n.top-tagSelector_edit:hover {\n\t--top-icon-color: var(--color-text-primary);\n}\n\n.top-tagSelector_tagListItem:hover .top-tagSelector_edit,\n.top-tagSelector_tagListItem-inEdit .top-tagSelector_edit {\n\tcursor: pointer;\n\topacity: 1;\n}\n\n.top-tagSelector_tagListItem-inEdit {\n\tbackground: var(--color-layout-front-3) !important;\n\toutline: 1px dashed var(--color-line-2);\n\toutline-offset: -1px;\n}\n\n.top-tagSelector_tagListItem-inEdit .top-tagSelector_tagListItemName {\n\tcursor: text;\n\tbackground: var(--color-layout-front-1);\n}\n\n/* disabled */\n.top-tagSelector_tagListItem-disabled {\n\tcursor: not-allowed !important;\n\topacity: 0.85;\n}\n.top-tagSelector_tagListItem-disabled:hover {\n\tbackground: inherit;\n}\n.top-tagSelector_tagListItem-disabled [data-tag_id] {\n\topacity: 0.6;\n}\n</style>\n","top-forms-focusable top-size_s top-button-withoutText top-tagSelector-useTopButton\n\n<script setup lang=\"ts\">\nimport type { Props, TagSelectorTarget } from './types';\nimport TopPopupOpener from '@/components/popup/popup/opener/opener.vue';\nimport TopButton from '@/components/forms/button/button.vue';\nimport { genTagIdClear, getTagById } from '../utils/utils';\nimport TopTagIcon from '../tagIcon/tagIcon.vue';\n\ndefineOptions({\n\tinheritAttrs: false,\n});\n\nconst props = defineProps<Props>();\n\nconst model = defineModel<Props['modelValue']>({\n\trequired: true,\n});\n\nconst component = props.useTopButton ? TopButton : 'div';\nconst componentSlotName = props.useTopButton ? 'html' : 'default';\n\nconst topTagSelectorTarget: TagSelectorTarget = {\n\tmodel,\n\tmode: props.mode,\n\ttargetId: props.targetId,\n\tfilters: props.filters,\n\tpayload: props.payload,\n};\n</script>\n\n<template>\n\t<TopPopupOpener :id>\n\t\t<component\n\t\t\t:is=\"component\"\n\t\t\t:class=\"{\n\t\t\t\t'top-tagSelector': true,\n\t\t\t\t'top-tagSelector-useTopButton': props.useTopButton,\n\t\t\t\t'top-tagSelector-custom': !props.useTopButton,\n\t\t\t\t'top-as-selector': props.useTopButton,\n\t\t\t\t'top-tagSelector-filter': props.mode === 'filter',\n\t\t\t\t'top-tagSelector-setter_single': props.mode === 'setter' && !filters,\n\t\t\t\t'top-tagSelector-setter_several': props.mode === 'setter' && filters,\n\t\t\t\t'top-tagSelector-selectedOne': !model.length || model.length === 1,\n\t\t\t\t'top-tagSelector-toTwoLine': model.length > 5,\n\t\t\t}\"\n\t\t\tcolor=\"theme\"\n\t\t\t:styling\n\t\t\tv-top-data:topTagSelectorTarget=\"topTagSelectorTarget\"\n\t\t\t:=$attrs\n\t\t>\n\t\t\t<template #[componentSlotName]>\n\t\t\t\t<TopTagIcon\n\t\t\t\t\tv-if=\"!model.length && mode === 'filter'\"\n\t\t\t\t\tid=\"all\"\n\t\t\t\t\tcolorId=\"\"\n\t\t\t\t\t:name=\"$i18n.Common.All_tags ?? ''\"\n\t\t\t\t\tstate=\"\"\n\t\t\t\t/>\n\n\t\t\t\t<!-- Массовое редактирование -->\n\t\t\t\t<div v-if=\"mode === 'setter' && filters\">\n\t\t\t\t\t<slot></slot>\n\t\t\t\t</div>\n\n\t\t\t\t<!-- Список тегов -->\n\t\t\t\t<TopTagIcon\n\t\t\t\t\tv-else\n\t\t\t\t\tv-for=\"tagId in model\"\n\t\t\t\t\t:id=\"genTagIdClear(tagId)\"\n\t\t\t\t\t:colorId=\"getTagById(tagId, tags)?.color_id ?? ''\"\n\t\t\t\t\t:name=\"getTagById(tagId, tags)?.name ?? ''\"\n\t\t\t\t\t:state=\"genTagIdClear(tagId) === tagId ? 'selected' : 'excluded'\"\n\t\t\t\t/>\n\t\t\t</template>\n\t\t</component>\n\t</TopPopupOpener>\n</template>\n\n<style>\n.top-tagSelector {\n\tcursor: pointer;\n\tmin-width: 0;\n\twhite-space: nowrap;\n\tjustify-content: left;\n}\n\n.top-tagSelector-useTopButton {\n\twidth: 112px;\n\tgap: var(--top-gap-1);\n\tvertical-align: middle;\n}\n\n.top-tagSelector.top-button.top-as-selector {\n\twidth: calc(112px + 8px);\n}\n\n/* все теги в фильтре */\n.top-tagSelector-useTopButton [data-tag_id=\"all\"]:before {\n\tmargin-right: 6px;\n}\n\n/* фильтр по одному тегу */\n.top-tagSelector-useTopButton.top-tagSelector-selectedOne [data-tag_id]:after {\n\tcontent: attr(title);\n\tmargin-left: 20px;\n\tmin-width: 74px;\n\ttext-align: left;\n\tline-height: 1.2;\n\ttext-overflow: ellipsis;\n\toverflow: hidden;\n}\n\n.top-tagSelector-useTopButton.top-tagSelector-selectedOne [data-tag_id=\"all\"]:after {\n\tmargin-left: 0;\n}\n\n/* фильтр по многим тегам */\n.top-button.top-tagSelector-useTopButton.top-tagSelector-toTwoLine {\n\tflex-wrap: wrap;\n\talign-content: center;\n}\n\n.top-button.top-tagSelector-useTopButton.top-tagSelector-toTwoLine [data-tag_id] {\n\t--top-tag-selector-size: 8px;\n}\n\n.top-button.top-tagSelector-useTopButton.top-tagSelector-toTwoLine [data-tag_id]:nth-child(5) {\n\tmargin-right: 5px;\n}\n\n/* установка тегов */\n.top-tagSelector-useTopButton.top-tagSelector-setter_several,\n.top-tagSelector-useTopButton.top-tagSelector-setter_several.top-as-selector{\n\twidth: auto;\n}\n\n.top-tagSelector-custom {\n\tposition: relative;\n\tdisplay: flex !important; flex-direction: column; flex-wrap: wrap; align-items: stretch !important;\n}\n\n.top-tagSelector-custom [data-tag_id] {\n\twidth: auto; min-height: 20%; min-width: 50%;\n\tflex: 1 1 auto;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, reactive, ref, shallowRef, watch } from 'vue';\n\nimport Core from '@/core/core/core';\nimport { useI18n } from '@/core/plugins/i18n';\nimport { storage } from '@/core/utils/dom';\nimport { debounce } from '@/core/utils/lodash';\n\nimport type { Option } from '@/components/forms/select/types';\nimport TopSelect from '@/components/forms/select/select.vue';\n\nimport TopButton from '@/components/forms/button/button.vue';\n\nimport type { PopupEvent } from '@/components/popup/popup/types';\nimport TopPopup from '../../popup/popup/popup.vue';\nimport TopPopupListItem from '../../popup/popup/listItem.vue';\n\nimport type { Emits, FiltersAction, Props, Tag } from './types';\nimport tagsDefaults from './tagsDefaults';\nimport { genTagIdExcluded } from './utils/utils';\n\nimport type { Props as TagListItemProps } from './popupListItem/types';\nimport TopTagListItem from './popupListItem/tagPopupListItem.vue';\n\nimport type { TagSelectorTarget } from './popupOpener/types';\nimport TopTagSelectorPopupOpener from './popupOpener/popupOpener.vue';\n\nconst i18n = useI18n();\n\nconst props = withDefaults(defineProps<Props>(), {\n\ttagsMax: 10,\n\trequiredForSetter: true,\n\temitDelay: 500,\n\tuseTopButton: true,\n});\n\n/**\n * Выбранные теги кнопки в этом компоненте\n *\n * Редактирвоание ведется только через `editTarget.model`\n */\nconst model = defineModel<Props['modelValue']>({\n\trequired: true,\n});\n\n/**\n * Список тегов\n */\nconst tags = defineModel<Tag[]>('tags', {\n\tdefault: reactive(tagsDefaults),\n});\n\nconst emit = defineEmits<Emits>();\n\nconst emitDebounce: typeof emit = debounce((name, e) => {\n\temit(name, e);\n}, props.emitDelay);\n\n// требуется сразу указать один тег\nif (props.singleMode && !model.value.length) {\n\tmodel.value = [tags.value[0].id];\n}\n\nconst id = props.id ?? 'top-popup-id-' + Math.random() + '';\n\nconst filtersAction = ref<FiltersAction>('add');\n\n/**\n * Сгенерировать опции для выбора дейсвтия массого редактирования\n */\nconst genFiltersActionOptions = () => {\n\tconst tagsText = ' ' + i18n.Common.Tags?.toLowerCase();\n\n\tconst res = new Map<FiltersAction, Option>();\n\tres.set('add', { value: 'add', title: i18n.Common.Add + tagsText });\n\tres.set('replace', { value: 'replace', title: i18n.Common.Replace + tagsText });\n\tres.set('delete', { value: 'delete', title: i18n.Common.Delete + tagsText });\n\n\treturn res;\n};\n\n/**\n * Целевой объект редактирования тегов\n *\n * Определяется кнопкой, которая открыла popup выбора тегов\n */\nlet target = shallowRef<TagSelectorTarget>({\n\tmodel,\n\tmode: 'filter',\n\ttargetId: undefined,\n\tfilters: undefined,\n\tpayload: undefined,\n});\n\nwatch(model, () => {\n\temitDebounce('selector', model.value);\n});\n\n/**\n * Можно ли выбрать еще теги\n */\nconst disabled = computed(() => {\n\tif (target.value.mode !== 'setter') return;\n\n\tif (!props.maxTagsForSetter) return;\n\n\t// массовая смена тегов, количество тегов в результате узнать невозможно\n\tif (target.value.filters) return;\n\n\treturn target.value.model.value.length >= props.maxTagsForSetter;\n});\n\nconst genTargetTagState = (tagId: Tag['id'] | 'all'): TagListItemProps['state'] => {\n\tif (tagId !== 'all') {\n\t\tif (target.value.model.value.includes(tagId)) return 'selected';\n\n\t\tif (target.value.model.value.includes(genTagIdExcluded(tagId))) return 'excluded';\n\t}\n\n\t// Все теги, ни один из тегов не выбран\n\tif (tagId === 'all' && !target.value.model.value.length) return 'selected';\n\n\treturn '';\n};\n\nconst updateTargetModel = (tagId: Tag['id'], action: 'unselect' | 'select' | 'exclude') => {\n\tconst tagIdExcluded = genTagIdExcluded(tagId);\n\n\tlet tagsIds = target.value.model.value.filter(modelTagId => modelTagId !== tagId && modelTagId !== tagIdExcluded);\n\n\tif (action === 'select') tagsIds.push(tagId);\n\tif (action === 'exclude') tagsIds.push(tagIdExcluded);\n\n\t// режим редактирвоания тегов одного объекта с требованием указать хотя бы один тег\n\tif (target.value.mode === 'setter' && target.value.targetId !== undefined && props.requiredForSetter) {\n\t\t// нельзя снимать выделение со всех тегов\n\t\tif (!tagsIds.length) {\n\t\t\ttagsIds.push('1');\n\t\t}\n\n\t\t// при первом выборе сразу снять тег \"Без тега\"\n\t\tif (tagsIds.length === 2 && target.value.model.value.length === 1 && target.value.model.value[0] === '1') {\n\t\t\ttagsIds = tagsIds.filter((tagId) => tagId !== '1');\n\t\t}\n\t}\n\n\t// режим выбора одного тега\n\tif (props.singleMode && !target.value.filters) {\n\t\t// нужно указать хотя бы один тег\n\t\tif (!tagsIds.length) {\n\t\t\ttagsIds = target.value.model.value;\n\t\t}\n\n\t\tif (tagsIds.length > 1) {\n\t\t\ttagsIds = [tagsIds[tagsIds.length - 1]];\n\t\t}\n\t}\n\n\t// всегда выводить теги в порядке, указанном в настройках tags\n\ttagsIds.sort((a, b) => {\n\t\tif (!props.tags) return 0;\n\n\t\tconst aIndex = props.tags.findIndex((tag) => tag.id === a);\n\t\tconst bIndex = props.tags.findIndex((tag) => tag.id === b);\n\n\t\treturn aIndex - bIndex;\n\t});\n\n\ttarget.value.model.value = tagsIds;\n\n\tif (target.value.mode === 'setter' && target.value.targetId !== undefined) {\n\t\temitDebounce('setter', {\n\t\t\ttagsIds: tagsIds as Tag['id'][],\n\t\t\ttargetId: target.value.targetId,\n\t\t\tpayload: target.value.payload,\n\t\t});\n\t}\n};\n\nconst classString = computed(() => {\n\tlet res = 'top-tagSelector_popup';\n\n\tif (target.value.mode === 'filter') res += ' top-tagSelector_popup-filter';\n\tif (target.value.mode === 'setter') res += ' top-tagSelector_popup-setter';\n\n\treturn res;\n});\n\n/**\n * Добавить тег\n */\nconst addTag = () => {\n\tconst tagName = prompt('', 'New tag');\n\tif (!tagName || tagName === 'New tag') return;\n\n\tconst tagId = tags.value.length + 1;\n\n\ttags.value.push({\n\t\tid: String(tagId) as Tag['id'],\n\t\tname: tagName,\n\t\tcolor_id: String((tagId - 1) % 10 + 1) as Tag['color_id'],\n\t});\n\n\temit('tagsChanged', tags.value);\n};\n\nconst onOpen = (popupEvent: PopupEvent) => {\n\t// popup открыт другой кнопкой c другим modelValue, для редактирвоания объектов\n\ttarget.value = storage(popupEvent.elPopupOpener, 'topTagSelectorTarget');\n\tif (!target.value) throw new Error('Open popup TopTagSelector required v-data:topTagSelectorTarget');\n\n\t// при начале массовой установки тегов сбросить состояние формы\n\tif (target.value.filters) {\n\t\tfiltersAction.value = 'add';\n\t\ttarget.value.model.value = [];\n\t}\n\n\tif (!Core.$?.ui['sortable']) {\n\t\tconsole.info('Для работы сортировки требуется глобальная загрузка jQuery UI Sortable');\n\n\t\treturn;\n\t}\n\n\tif (!Core.state.isMobile && !Core.state.isMobileUA && tags.value) {\n\t\t$(popupEvent.elPopup).sortable({\n\t\t\titems: 'li:has([data-tag_id]:not([data-tag_id=\"all\"]))',\n\n\t\t\t/**\n\t\t\t * @todo Удалить `[contenteditable=\"true\"]` через пол года после выхода версии firefox с поддержкой contenteditable plaintext-only, включая бета версии\n\t\t\t */\n\t\t\tcancel: '[contenteditable=\"plaintext-only\"], [contenteditable=\"true\"]',\n\n\t\t\tdistance: 10,\n\t\t\tstop: function (_e, ui) {\n\t\t\t\tif (!tags.value) return;\n\n\t\t\t\tconst $tags = $(ui.item).parent().find('[data-tag_id]');\n\n\t\t\t\tconst tagsSorted: Tag['id'][] = [];\n\t\t\t\t$tags.each((_index, elTag) => {\n\t\t\t\t\tif (!tags.value) return;\n\n\t\t\t\t\tconst tagId = $(elTag).attr('data-tag_id') as Tag['id'];\n\t\t\t\t\ttagsSorted.push(tagId);\n\t\t\t\t});\n\n\t\t\t\ttags.value.sort((tagIdA, tagIdB) => {\n\t\t\t\t\tconst a = tagsSorted.findIndex(tagSorted => tagSorted === tagIdA.id);\n\t\t\t\t\tconst b = tagsSorted.findIndex(tagSorted => tagSorted === tagIdB.id);\n\n\t\t\t\t\treturn a - b;\n\t\t\t\t});\n\n\t\t\t\temitDebounce('tagsChanged', tags.value);\n\t\t\t},\n\t\t});\n\t}\n};\n\nconst onClose = (popupEvent: PopupEvent) => {\n\tif (!Core.$?.ui['sortable']) return;\n\n\tif ($(popupEvent.elPopup).data('ui-sortable')) {\n\t\t$(popupEvent.elPopup).sortable('destroy');\n\t}\n};\n</script>\n\n<template>\n\t<TopTagSelectorPopupOpener\n\t\tv-model=\"model\"\n\t\t:id\n\t\t:tags\n\t\t:styling\n\t\tmode=\"filter\"\n\t\t:useTopButton\n\t/>\n\n\t<TopPopup\n\t\t:id\n\t\t:class=\"classString\"\n\t\t@open=\"onOpen($event)\"\n\t\t@close=\"onClose($event)\"\n\t\t:transition-duration=\"50\"\n\t>\n\t\t<!-- Массовое редактирование-->\n\t\t<template #header v-if=\"target.mode === 'setter' && target.filters\">\n\t\t\t<TopSelect\n\t\t\t\tv-model=\"filtersAction\"\n\t\t\t\t:options=\"genFiltersActionOptions()\"\n\t\t\t/>\n\t\t</template>\n\n\t\t<template #footer v-if=\"target.mode === 'setter' && target.filters\">\n\t\t\t<TopButton color=\"theme\">\n\t\t\t\t{{ $i18n.Common.Cancel }}\n\t\t\t</TopButton>\n\n\t\t\t<TopButton\n\t\t\t\t@click=\"emitDebounce('setter', {\n\t\t\t\t\ttagsIds: target.model.value as Tag['id'][],\n\t\t\t\t\tfilters: target.filters,\n\t\t\t\t\tfiltersAction,\n\t\t\t\t\tpayload: target.payload,\n\t\t\t\t})\"\n\t\t\t>\n\t\t\t\t{{ filtersAction === 'add' ? $i18n.Common['Add'] : '' }}\n\t\t\t\t{{ filtersAction === 'replace' ? $i18n.Common['Replace'] : '' }}\n\t\t\t\t{{ filtersAction === 'delete' ? $i18n.Common['Delete'] : '' }}\n\t\t\t</TopButton>\n\t\t</template>\n\n\t\t<template #contentList>\n\t\t\t<TopTagListItem\n\t\t\t\tv-if=\"target.mode === 'filter' && !singleMode\"\n\t\t\t\tid=\"all\"\n\t\t\t\tcolorId=\"\"\n\t\t\t\t:name=\"$i18n.Common.All_tags ?? ''\"\n\t\t\t\t:state=\"target.model.value.length ? '' : 'selected'\"\n\t\t\t\t@select=\"target.model.value = []\"\n\t\t\t/>\n\n\t\t\t<TopTagListItem\n\t\t\t\tv-for=\"tag in tags\"\n\t\t\t\t:key=\"tag.id\"\n\t\t\t\t:id=\"tag.id\"\n\t\t\t\t:colorId=\"tag.color_id\"\n\t\t\t\t:name=\"tag.name\"\n\t\t\t\t:state=\"genTargetTagState(tag.id)\"\n\t\t\t\t:canExclude=\"target.mode === 'filter' && !singleMode\"\n\t\t\t\t:editable=\"tagsEditable\"\n\t\t\t\t:disabled=\"disabled && genTargetTagState(tag.id) === ''\"\n\t\t\t\t@unselect=\"updateTargetModel(tag.id, 'unselect')\"\n\t\t\t\t@select=\"updateTargetModel(tag.id, 'select')\"\n\t\t\t\t@exclude=\"updateTargetModel(tag.id, 'exclude')\"\n\t\t\t\t@update:name=\"tag.name = $event as string, emitDebounce('tagsChanged', tags);\"\n\t\t\t/>\n\n\t\t\t<TopPopupListItem\n\t\t\t\tv-if=\"tagsEditable && tags.length < tagsMax && tags.length < 20\"\n\t\t\t\tdata-top-icon=\"\"\n\t\t\t\t@click.stop=\"addTag\"\n\t\t\t>\n\t\t\t\t{{ $i18n.Common.Add }}\n\t\t\t</TopPopupListItem>\n\t\t</template>\n\t</TopPopup>\n</template>\n\n<style>\n.top-tagSelector_popup .top-popup {\n\tuser-select: none;\n\tmin-width: 220px !important;\n\ttext-align: left;\n}\n\n.top-tagSelector_popup .top-popup_header .top-select{\n\tflex-grow: 1;\n}\n</style>\n"],"names":["selectAllItem","vue","props","forms","__props","item","model","_hoisted_2$1","useItemsFromCompetitors","competitors","projectId","competitor","searchersNames","regionUndefined","dummyIndex","searcherUndefined","regionAuto","searcherAuto","regionGlobal","getRegionAuto","genSearcherByKey","forFrequency","autoRegion","searchers","searcherByKey","genSearchersMapVolume","genSearchersMapCommon","onlyEnabledRegions","forceSearchersKeys","searcher","searcherI","region","regionI","searcherKey","serarcherByKey","getRegionGlobal","findRegion","searchRegion","findedRegion","dialogRegionSelector","utils","useSelectSearcher","mapSearchers","i18n","optionBySearcherKey","res","option","utils_searchers","useSelectRegion","activeSearcherIndexed","regionIndex","optionByRegionIndex","options","regionLabel","langLabel","optionByRegionIndex2","newRegionIndex","regionName","oldOptionByRegionIndex","index","title","regionNameCompare","regexpDevice","regionMatchLevelI","regionMatchLevel","regionsIndexes","regionsIndexesSaved","compareSave","compareLoad","useSelectorRegion","selectSearcher","allRegionsIndexes","globalRegionIndex","compare","newSearcherKey","selectRegion","regionsNewSearcher","regionsCurrentSearcher","searhcerCompareKey","regionKey","getSearcherKey","getSearcher","getRegionIndex","regions","modelSingle","defaultKey","newModel","defaultIndex","all","newSearherKey","__expose","_ctx","folderDefault","genRootFolder","folders","useSelectAll","rootFolder","genRootFolderName","parentFolder","folder","prefix","depth","genFlat","folderById","resultFolders","genApiGetFolders","client","useWatch","sourcesObj","cb","sources","keys","k","val","oldValues","i","changed","onCleanup","apiGet","refSelector","foldersFlat","modelFolder","changes","folderSelected2","folderSelected","modelFolderId","args","policy_vue_vue_type_style_index_0_lang","groupNone","groupAll","groups","on","group","folderForFilter","genApiGetGroups","genApiAddGroup","apiAdd","groupsBySelectedFolder","filterByFolder","field","setGroup","modelGroup","modelGroupId","genItems","genGroupAll","groupSelected","firstGroup","api","addGroups","modelGroupOld","emits","_hoisted_1$4","_hoisted_2","groupInitial","onAddGroup","emit","genTagIdExcluded","tagId","genTagIdClear","getTagById","tags","tag","tag2","genElPopupOpener","propsPopup","htmlSetterSeveral","el","e","utils_dom","target","elPopupOpenerRender","modelValue","renderElPopupOpener","tagsIds","elTagIcon","genElTagIcon","tagsDefaults","_hoisted_1$2","elName","firefoxProps","inEdit","event","name2","editCancel","changeSelect","toState","name","editCommit","turnToEdit","component","_sfc_main$3","id","filtersAction","tagsText","emitDebounce","disabled","genTargetTagState","updateTargetModel","action","tagIdExcluded","tagId2","aIndex","a","bIndex","b","classString","tagName","onOpen","popupEvent","_e","ui","$tags","tagsSorted","tagSorted","tagIdA","tagIdB","onClose","_cache","$event"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"project.amd.js","sources":["../../src/components/project/competitorSelector/competitorSelector.vue","../../src/components/project/competitorSelector/composables.ts","../../src/components/project/regionSelector/utils/consts.ts","../../src/components/project/regionSelector/utils/utils.ts","../../src/components/project/regionSelector/composables/selectSearcher.ts","../../src/components/project/regionSelector/composables/selectRegion.ts","../../src/components/project/regionSelector/composables/compare.ts","../../src/components/project/regionSelector/composables/selectorRegion.ts","../../src/components/project/regionSelector/regionSelector.vue","../../src/components/project/groupSelector/folders/utils.ts","../../src/core/utils/composables/useWatch.ts","../../src/components/project/groupSelector/folders/folders.vue","../../src/components/project/groupSelector/groups/utils.ts","../../src/components/project/groupSelector/groups/groups.vue","../../src/components/project/groupSelector/groupSelector.vue","../../src/components/project/tagSelector/utils/utils.ts","../../src/components/project/tagSelector/utils/el.ts","../../src/components/project/tagSelector/tagsDefaults.ts","../../src/components/project/tagSelector/tagIcon/tagIcon.vue","../../src/components/project/tagSelector/popupListItem/tagPopupListItem.vue","../../src/components/project/tagSelector/popupOpener/popupOpener.vue","../../src/components/project/tagSelector/tagSelector.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from 'vue';\nimport type { Props } from './types';\nimport Core from '@/core/core/core';\nimport Button from '@/components/forms/button/button.vue';\nimport Popup from '@/components/popup/popup/popup.vue';\nimport ListItem from '@/components/popup/popup/listItem.vue';\nimport Menu from '@/components/formsExt/menu/menu.vue';\nimport { useI18n } from '@/core/plugins/i18n';\n\nconst props = withDefaults(defineProps<Props>(), {\n\tshowSelectAllItem: true,\n});\nconst model = defineModel<Props['modelValue']>();\n\nconst selectAllItem = computed(() => {\n\tif (props.showSelectAllItem) {\n\t\treturn {\n\t\t\ticon: '',\n\t\t\ttitle: useI18n().Common.Select_all,\n\t\t\tvalue: 'all',\n\t\t\tcontent: '',\n\t\t};\n\t}\n});\n</script>\n\n<template>\n\t<div class=\"top-competitorSelector\">\n\t\t<Popup v-if=\"Core.state.isMobile\">\n\t\t\t<template #opener>\n\t\t\t\t<Button\n\t\t\t\t\tclass=\"top-competitorSelector_opener\"\n\t\t\t\t\tcolor=\"theme\"\n\t\t\t\t\ticon=\"\"\n\t\t\t\t\ticon2=\"\"\n\t\t\t\t>\n\t\t\t\t\t{{ items.find((item) => item.value === model?.[0])?.content }}\n\t\t\t\t</Button>\n\t\t\t</template>\n\n\t\t\t<template #contentList>\n\t\t\t\t<ListItem\n\t\t\t\t\tv-for=\"(item) in items\"\n\t\t\t\t\t:class=\"{\n\t\t\t\t\t\t'top-active': model?.includes(item.value)\n\t\t\t\t\t}\"\n\t\t\t\t\t:data-top-icon=\"item.icon\"\n\t\t\t\t\t:title=\"item.title\"\n\t\t\t\t\t@click=\"() => model = [item.value]\"\n\t\t\t\t>\n\t\t\t\t\t<span class=\"top-ellipsis1\">\n\t\t\t\t\t\t{{ item.content }}\n\t\t\t\t\t</span>\n\t\t\t\t</ListItem>\n\t\t\t</template>\n\t\t</Popup>\n\n\t\t<Menu\n\t\t\tv-else\n\t\t\tv-model=\"model\"\n\t\t\t:items=\"items\"\n\t\t\t:isMultiple=\"true\"\n\t\t\tstyling=\"bar\"\n\t\t\t:canBeEmptyMultiple=\"false\"\n\t\t\t:selectAllItem=\"selectAllItem\"\n\t\t/>\n\t</div>\n</template>\n\n<style>\n.top-competitorSelector_opener.top-button {\n\twidth: 100%;\n}\n</style>\n","import type { MaybeRefOrGetter } from 'vue';\nimport { computed, toValue } from 'vue';\nimport type { Competitor, Item } from '@/components/project/competitorSelector/types';\n\nexport const useItemsFromCompetitors = (competitors: MaybeRefOrGetter<Competitor[]>, projectId: MaybeRefOrGetter<number>) => {\n\treturn computed(() => {\n\t\tconst activeCompetitors = toValue(competitors).filter(competitor => competitor.on >= 0 || competitor.id === projectId);\n\n\t\tconst items: Item[] = activeCompetitors.map(competitor => {\n\t\t\treturn {\n\t\t\t\tvalue: competitor.id,\n\t\t\t\ttitle: competitor.url + ` [${competitor.id}]`,\n\t\t\t\ticon: competitor.id === toValue(projectId) ? '' : '',\n\t\t\t\tcontent: competitor.name,\n\t\t\t};\n\t\t});\n\n\t\treturn items;\n\t});\n};\n","import type { Region, SearcherIndexed } from '../types';\nimport { useI18n } from '@/core/plugins/i18n';\n\n/**\n * Ключ ПС - для выбора нескольких ПС или регионов\n */\nexport const searhcerCompareKey = -1;\n\n/**\n * Ключ ПС или индекса Региона, используется для ПС или Региона, не настроенных в проекте\n *\n * Также, используется для API с возможностью не указывать регион, см. `props.autoRegion`\n */\nexport const dummyIndex = -2;\n\n/**\n * Регион - без региона\n */\nexport const globalRegionIndex = -1;\n\nexport const searchersNames = {\n\t0: 'Yandex',\n\t1: 'Google',\n\t4: 'YouTube',\n\t5: 'Bing',\n\t7: 'Seznam',\n\t8: 'AppStore',\n\t9: 'GoogleStore',\n\t20: 'Yandex.com',\n\t21: 'Yandex.com.tr',\n};\n\nexport const regionUndefined: Region = {\n\tkey: dummyIndex,\n\tname: '--',\n\tindex: dummyIndex,\n};\n\nexport const searcherUndefined: SearcherIndexed = {\n\tkey: dummyIndex,\n\tname: '--',\n\tregions: [regionUndefined],\n\tregionByIndex: new Map([[dummyIndex, regionUndefined]]),\n};\n\nconst regionAuto: Region = {\n\tkey: dummyIndex,\n\tname: 'Autoselect',\n\tindex: dummyIndex,\n};\n\nconst searcherAuto: SearcherIndexed = {\n\tkey: dummyIndex,\n\tname: 'Autoselect',\n\tregions: [regionAuto],\n\tregionByIndex: new Map([[dummyIndex, regionAuto]]),\n};\n\nconst regionGlobal: Region = {\n\tcountryCode: '00',\n\tdepth: 1,\n\tdevice: 0,\n\tkey: globalRegionIndex,\n\tindex: globalRegionIndex,\n\tlang: 'ru',\n\tname: 'Without region',\n};\n\nexport const getRegionAuto = () => {\n\tregionAuto.name = useI18n().Common.Autoselect!;\n\n\treturn regionAuto;\n};\n\nexport const getSearcherAuto = () => {\n\tgetRegionAuto();\n\n\tsearcherAuto.name = useI18n().Common.Autoselect!;\n\tconsole.log(searcherAuto);\n\n\treturn searcherAuto;\n};\n\nexport const getRegionGlobal = () => {\n\tregionGlobal.name = useI18n().Keywords.Without_region!;\n\n\treturn regionGlobal;\n};\n","import type { Region, Searcher, SearcherByKey, SearcherIndexed } from '../types';\nimport { dummyIndex, getRegionGlobal, getSearcherAuto, searchersNames, searcherUndefined } from './consts';\nimport { useAsyncTopDialog } from '@/components/dialog/dialog/composables/utils';\n\n/**\n * Генерация Map ПС с Map регионов из массива searchers\n * - ключ для ПС - searcherKey\n * - ключ для региона - regionIndex\n *\n * Если поисковиков в списке нет, в список будет добавлен searcherUndefined\n *\n * Если регионов в списке нет, в список будет добавлен regionUndefined\n *\n * @param forFrequency - получить массив для просмотра частоты (или ставок)\n * @param autoRegion - добавить элемент с ключем -2, который в API заменяется на нужный ключ в зависимости от контекста\n * @param searchers - поисковики с регионами проекта, см.: get/projects_2/projects, show_searchers_and_regions = 1\n */\nexport const genSearcherByKey = (\n\tforFrequency: boolean = false,\n\tautoRegion: boolean = false,\n\tsearchers: Searcher[] = [],\n) => {\n\tlet searcherByKey: SearcherByKey;\n\n\tif (forFrequency) {\n\t\tsearcherByKey = genSearchersMapVolume(searchers);\n\t} else {\n\t\tsearcherByKey = genSearchersMapCommon(searchers);\n\t}\n\n\tif (autoRegion) searcherByKey.set(dummyIndex, getSearcherAuto());\n\tif (!searcherByKey.size) searcherByKey.set(dummyIndex, searcherUndefined);\n\n\treturn searcherByKey;\n};\n\n/**\n * Список ПС с регионами\n *\n * @param searchers\n * @param onlyEnabledRegions\n * @param forceSearchersKeys - ключи ПС, которые нееобходимо вывести в любом случае\n * @param forFrequency - для частоты будут выводиться только Яндекс, Google и Mail\n */\nconst genSearchersMapCommon = (\n\tsearchers: Searcher[],\n\tonlyEnabledRegions: boolean = true,\n\tforceSearchersKeys: Searcher['key'][] = [],\n\tforFrequency: boolean = false,\n) => {\n\tconst searcherByKey: Map<Searcher['key'], SearcherIndexed> = new Map();\n\n\t// настроенные ПС\n\tsearchers.forEach((searcher) => {\n\t\tif (!searcher.enabled) return;\n\t\tif (forFrequency && typeof searcher.key === 'number' && searcher.key > 1) return;\n\n\t\tconst searcherI = { ...searcher } as SearcherIndexed;\n\t\tsearcherI.regionByIndex = new Map();\n\n\t\tif (searcher.regions) {\n\t\t\tsearcher.regions.forEach((region: Region) => {\n\t\t\t\tif (onlyEnabledRegions && !region.enabled) return;\n\n\t\t\t\tconst regionI = { ...region };\n\t\t\t\tsearcherI.regionByIndex.set(regionI.index, regionI);\n\t\t\t});\n\t\t}\n\n\t\tif (!searcherI.regionByIndex.size && !forceSearchersKeys.length) {\n\t\t\t// searcherI.regionByIndex.set(regionUndefined.index, regionUndefined);\n\t\t}\n\n\t\tif (\n\t\t\t// режим вывода ПС без регионов\n\t\t\t!searcher.regions ||\n\n\t\t\t// есть включенные регионы\n\t\t\tsearcherI.regionByIndex.size ||\n\n\t\t\t// запрошен вывод конкретных ПС\n\t\t\tforceSearchersKeys.length\n\t\t) {\n\t\t\tif (typeof searcherI.key === 'number') searcherByKey.set(searcherI.key, searcherI);\n\t\t}\n\t});\n\n\t// дополнительные ПС\n\tforceSearchersKeys.forEach((searcherKey) => {\n\t\tif (searcherByKey.has(searcherKey)) return;\n\n\t\tconst searcherI: SearcherIndexed = {\n\t\t\tkey: searcherKey,\n\t\t\tname: searchersNames[searcherKey],\n\t\t\tregions: [],\n\t\t\tregionByIndex: new Map(),\n\t\t};\n\n\t\tsearcherByKey.set(searcherI.key, searcherI);\n\t});\n\n\treturn searcherByKey;\n};\n\n/**\n * Список ПС с регионами для проверки частоты (Яндекс, Google, Mail если добавлен в проект)\n *\n * @param searchers\n */\nconst genSearchersMapVolume = (searchers: Searcher[]) => {\n\tconst serarcherByKey = genSearchersMapCommon(searchers, false, [0, 1], true);\n\n\t// в Mail нет регионов\n\tif (serarcherByKey.has(2)) {\n\t\tconst searcherMail = serarcherByKey.get(2);\n\t\tif (searcherMail) searcherMail.regionByIndex = new Map();\n\t}\n\n\t// добавление глобального региона\n\tserarcherByKey.forEach((searcher) => {\n\t\tif (!searcher.regionByIndex) return;\n\n\t\tconst region = { ...getRegionGlobal() };\n\t\tsearcher.regionByIndex.set(region.index, region);\n\t});\n\n\treturn serarcherByKey;\n};\n\n/**\n * Поиск региона по заданному критерию из списка поисковиков проекта\n *\n * @param forFrequency - поиск региона для просмотра частоты или ставок\n * @param searchRegion - объект с параметрами региона, который следует найти\n * @param searchers\n */\nexport const findRegion = (forFrequency: boolean, searchRegion: Partial<Region>, searchers: Searcher[] = []) => {\n\tconst searcherByKey = genSearcherByKey(forFrequency, false, searchers);\n\n\tlet findedRegion: Region | undefined;\n\n\t// поиск ПС\n\tsearcherByKey.forEach((searcher) => {\n\t\tif (searchRegion.searcher_key !== undefined && searchRegion.searcher_key != searcher.key) return;\n\t\tif (!searcher.regions) return;\n\n\t\t// поиск региона\n\t\tsearcher.regions.forEach((region) => {\n\t\t\tif (findedRegion) return;\n\n\t\t\tif (searchRegion.key !== undefined && searchRegion.key != region.key) return;\n\t\t\tif (searchRegion.index !== undefined && searchRegion.index != region.index) return;\n\n\t\t\tif (!forFrequency) {\n\t\t\t\tif (searchRegion.lang !== undefined && searchRegion.lang != region.lang) return;\n\t\t\t\tif (searchRegion.device !== undefined && searchRegion.device != region.device) return;\n\t\t\t}\n\n\t\t\tregion.searcher_key = searcher.key;\n\t\t\tfindedRegion = region;\n\n\t\t\treturn false;\n\t\t});\n\n\t\tif (findedRegion) return false;\n\t});\n\n\treturn findedRegion;\n};\n\n/**\n * Открыть диалоговое окно с выбором нескольких регионов\n *\n * @see import('../dialog_regionSelectorRegions/types').Props\n */\nexport const dialogRegionSelector = useAsyncTopDialog(() => {\n\treturn import('@/components/project/regionSelector/dialog_regionSelectorRegions/dialog_regionSelectorRegions.vue');\n});\n","import { computed, type ComputedRef, ref } from 'vue';\nimport { useI18n } from '@/core/plugins/i18n';\nimport type { Props, SearcherByKey } from '../types';\nimport type { Option, Options } from '@/components/forms/select/types';\nimport { dummyIndex, searhcerCompareKey } from '../utils/consts';\nimport { getSearcherGIcon } from '@/core/utils/searchers';\n\n/**\n * Создание и управление реактивными переменными для выбора ПС\n *\n * @param props - входные props компонента\n * @param mapSearchers - Map ПС (реактивная)\n */\nexport const useSelectSearcher = (\n\tprops: Props,\n\tmapSearchers: ComputedRef<SearcherByKey>,\n) => {\n\tconst i18n = useI18n();\n\n\t/**\n\t * Ключ выбранной поисковой системы\n\t */\n\tconst searcherKey = ref(mapSearchers.value.keys().next().value ?? dummyIndex);\n\n\t/**\n\t * Коллекция опций для выбора ПС\n\t */\n\tconst optionBySearcherKey = computed(() => {\n\t\tconst res: Options = new Map();\n\t\tmapSearchers.value.forEach((searcher) => {\n\t\t\tlet option: Option = {\n\t\t\t\tvalue: searcher.key,\n\t\t\t\ttitle: searcher.name,\n\t\t\t};\n\n\t\t\tif (props.addSearcherIcon) option.icon = getSearcherGIcon(searcher.key);\n\n\t\t\tres.set(searcher.key, option);\n\t\t});\n\n\t\t// добавить режим сравнения, если есть хотя бы одна ПС с одним регионом\n\t\tif (props.addCompare && !res.has(dummyIndex)) {\n\t\t\tconst dummyOption: Option = {\n\t\t\t\tvalue: '',\n\t\t\t\ttitle: '--------------------',\n\t\t\t\tdisabled: true,\n\t\t\t};\n\t\t\tres.set(dummyOption.value, dummyOption);\n\n\t\t\tconst compareOption: Option = {\n\t\t\t\tvalue: searhcerCompareKey,\n\t\t\t\ttitle: i18n.Common.Compare!,\n\t\t\t};\n\t\t\tres.set(compareOption.value, compareOption);\n\t\t}\n\n\t\treturn res;\n\t});\n\n\treturn {\n\t\tsearcherKey,\n\t\toptionBySearcherKey,\n\t};\n};\n","import { computed, type ComputedRef, ref, watch } from 'vue';\nimport { useI18n } from '@/core/plugins/i18n';\nimport { getDeviceGIcon, getLangLabel } from '@/core/utils/searchers';\nimport type { Option } from '@/components/forms/select/types';\nimport { dummyIndex } from '../utils/consts';\nimport type { Props, Region, SearcherIndexed } from '../types';\n\n/**\n * Создание и управление реактивными переменными для выбора региона\n *\n * @param props - входные props компонента\n * @param activeSearcherIndexed - объект активной поисковой системы (реактивная)\n */\nexport const useSelectRegion = (props: Props, activeSearcherIndexed: ComputedRef<SearcherIndexed>) => {\n\tconst i18n = useI18n();\n\n\t/**\n\t * Индекс выбранного региона\n\t */\n\tconst regionIndex = ref(dummyIndex);\n\n\tif (props.modelValue.length === 1) {\n\t\tregionIndex.value = props.modelValue[0];\n\t}\n\n\tif (regionIndex.value === dummyIndex) {\n\t\tif (props.forFrequency) {\n\t\t\t// в качетсве ключа используется region.key, а не region.index\n\t\t\tregionIndex.value = activeSearcherIndexed.value?.regionByIndex.values().next().value?.key ?? dummyIndex;\n\t\t} else {\n\t\t\tregionIndex.value = activeSearcherIndexed.value?.regionByIndex.keys().next().value ?? dummyIndex;\n\t\t}\n\t}\n\n\t/**\n\t * Коллекция опций для выбора региона\n\t */\n\tconst optionByRegionIndex = computed(() => {\n\t\tconst options = new Map<number, Option>();\n\n\t\tactiveSearcherIndexed.value.regionByIndex?.forEach((region) => {\n\t\t\tlet regionLabel = region.name;\n\n\t\t\t// на частоту в текущей версии устройство и язык не влияют\n\t\t\t// обратите внимание, в качетсве ключа используется region.key, а не region.index\n\t\t\tif (props.forFrequency) {\n\t\t\t\tconst option: Option = {\n\t\t\t\t\tvalue: region.key,\n\t\t\t\t\ttitle: regionLabel,\n\t\t\t\t};\n\t\t\t\tif (!options.has(region.key)) options.set(region.key, option);\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (region.device) {\n\t\t\t\tregionLabel += ' (' + i18n.Common['Device_' + region.device] + ')';\n\t\t\t}\n\n\t\t\tconst langLabel = getLangLabel(activeSearcherIndexed.value.key || 0, region.lang ?? '');\n\t\t\tif (langLabel) regionLabel += ' / ' + langLabel;\n\n\t\t\tconst option: Option = {\n\t\t\t\tvalue: region.index,\n\t\t\t\ttitle: regionLabel,\n\t\t\t\ticon: region.device ? getDeviceGIcon(region.device) : undefined,\n\t\t\t};\n\n\t\t\toptions.set(region.index, option);\n\t\t});\n\n\t\treturn options;\n\t});\n\n\t/**\n\t * Выбор максимально похожего региона в новой коллекции регионов\n\t */\n\twatch(optionByRegionIndex, (optionByRegionIndex, oldOptionByRegionIndex) => {\n\t\tif (props.onlySearcher || regionIndex.value !== undefined && optionByRegionIndex.get(regionIndex.value)) {\n\t\t\treturn;\n\t\t}\n\n\t\tlet newRegionIndex = optionByRegionIndex.keys().next().value as Region['index'];\n\t\tif (regionIndex.value === dummyIndex || newRegionIndex === dummyIndex) {\n\t\t\tregionIndex.value = newRegionIndex;\n\n\t\t\treturn;\n\t\t}\n\n\t\tlet regionName = (oldOptionByRegionIndex?.get(regionIndex.value) as Option)?.title || '';\n\t\tlet regionMatchLevel = -1;\n\t\tfor (const [index, option] of optionByRegionIndex.entries()) {\n\t\t\tconst title = (option as Option).title;\n\n\t\t\tif (typeof title !== 'string' || typeof index === 'string') {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// полное совпадение\n\t\t\tif (title === regionName) {\n\t\t\t\tnewRegionIndex = index;\n\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tconst regexpDevice = new RegExp(` \\\\((${i18n.Common['Device_1']}|${i18n.Common['Device_2']})\\\\)`);\n\t\t\tlet regionNameCompare = regionName;\n\t\t\tlet regionMatchLevelI = 3;\n\n\t\t\t// без устройства\n\t\t\tif (title.indexOf(regionNameCompare) === -1) {\n\t\t\t\tregionNameCompare = regionName.replace(/^[^a-zа-я]/i, '').replace(regexpDevice, '');\n\n\t\t\t\tregionMatchLevelI--;\n\t\t\t}\n\n\t\t\t// без языка\n\t\t\tif (title.indexOf(regionNameCompare) === -1) {\n\t\t\t\tregionNameCompare = regionName.replace(/ \\/.*/, '');\n\n\t\t\t\tregionMatchLevelI--;\n\t\t\t}\n\n\t\t\t// без устройства и без языка\n\t\t\tif (title.indexOf(regionNameCompare) === -1) {\n\t\t\t\tregionNameCompare = regionName.replace(/ \\/.*/, '');\n\t\t\t\tregionNameCompare = regionNameCompare.replace(/^[^a-zа-я]/i, '').replace(regexpDevice, '');\n\n\t\t\t\tregionMatchLevelI--;\n\t\t\t}\n\n\t\t\tif (title.indexOf(regionNameCompare) === -1) continue;\n\t\t\tif (regionMatchLevelI <= regionMatchLevel) continue;\n\n\t\t\tregionMatchLevel = regionMatchLevelI;\n\t\t\tnewRegionIndex = index;\n\t\t}\n\n\t\tregionIndex.value = newRegionIndex;\n\t});\n\n\treturn {\n\t\tregionIndex,\n\t\toptionByRegionIndex,\n\t};\n};\n","import { type ComputedRef, ref, watch } from 'vue';\nimport type { Props, SearcherByKey } from '../types';\n\n/**\n * Создание и управление реактивными переменными для сравнения\n *\n * @param props - входные props компонента\n * @param searcherByKey - Map ПС (реактивная)\n * @param allRegionsIndexes - Все доступные индексы регионов (реактивная)\n */\nexport const useCompare = (props: Props, searcherByKey: ComputedRef<SearcherByKey>, allRegionsIndexes: ComputedRef<Set<number>>) => {\n\t/**\n\t * Индексы регионов/ПС в сравнение\n\t */\n\tconst regionsIndexes = ref<number[]>([]);\n\n\t/**\n\t * Загрузка индексов регионов для сравнения\n\t *\n\t * Если в modelValue передано несколько регионов, они будут установлены как выбранные для сравнения\n\t */\n\tconst compareLoad = () => {\n\t\tif (props.onlySearcher && searcherByKey.value) {\n\t\t\tregionsIndexes.value = Array.from(searcherByKey.value.keys());\n\n\t\t\treturn;\n\t\t}\n\n\t\tlet regionsIndexesSaved: Props['modelValue'] = [];\n\n\t\tif (props.modelValue.length > 1) {\n\t\t\tregionsIndexesSaved = [...props.modelValue];\n\t\t} else {\n\t\t\ttry {\n\t\t\t\t// загрузить индексы регионов, если они были сохранены\n\t\t\t\tregionsIndexesSaved = JSON.parse(\n\t\t\t\t\tlocalStorage.getItem('ui:project:regionSelector' + props.projectId + ':regionsIndexes') as string,\n\t\t\t\t) ?? [];\n\t\t\t} catch (e) {\n\n\t\t\t}\n\t\t}\n\n\t\t// убрать из сравнения регионы, которых нет в props.searchers\n\t\tif (regionsIndexesSaved.length) {\n\t\t\tregionsIndexesSaved = regionsIndexesSaved.filter((regionIndex) => {\n\t\t\t\treturn allRegionsIndexes.value.has(regionIndex);\n\t\t\t});\n\t\t}\n\n\t\t// если не сохранено ни одного региона, выбрать все регионы проекта\n\t\tif (!regionsIndexesSaved.length) {\n\t\t\tregionsIndexesSaved = Array.from(allRegionsIndexes.value);\n\t\t}\n\n\t\tregionsIndexes.value = [...regionsIndexesSaved];\n\t};\n\n\t/**\n\t * Сохранение выбранных регионов\n\t */\n\tconst compareSave = () => {\n\t\tif (regionsIndexes.value.length) {\n\t\t\tlocalStorage.setItem('ui:project:regionSelector:' + props.projectId + ':regionsIndexes', JSON.stringify(regionsIndexes.value));\n\t\t} else {\n\t\t\tlocalStorage.removeItem('ui:project:regionSelector:' + props.projectId + ':regionsIndexes');\n\t\t}\n\t};\n\n\twatch(regionsIndexes, () => {\n\t\tcompareSave();\n\t});\n\n\tif (props.addCompare) {\n\t\t// if (props.compareRegionsIndexes?.length) {\n\t\t// \tcompareRegionsIndexes.value = [...props.compareRegionsIndexes];\n\t\t// } else {\n\t\t// \tcompareLoad();\n\t\t// }\n\n\t\tcompareLoad();\n\t}\n\n\treturn {\n\t\tregionsIndexes,\n\t};\n};\n","import { computed, watch } from 'vue';\nimport { findRegion, genSearcherByKey } from '../utils/utils';\nimport { dummyIndex, globalRegionIndex, searcherUndefined, searhcerCompareKey } from '../utils/consts';\nimport type { Props, Region, SearcherIndexed } from '../types';\nimport { useSelectSearcher } from './selectSearcher';\nimport { useSelectRegion } from './selectRegion';\nimport { useCompare } from './compare';\n\n/**\n * Создание и управления рективными переменными компонента\n *\n * @param props - входные props компонента\n */\nexport const useSelectorRegion = (props: Props) => {\n\tconst searcherByKey = computed(() => {\n\t\treturn genSearcherByKey(props.forFrequency, props.autoRegion, props.searchers);\n\t});\n\n\tconst activeSearcherIndexed = computed(() => {\n\t\treturn searcherByKey.value.get(selectSearcher.searcherKey.value) || searcherUndefined;\n\t});\n\n\t/**\n\t * Все индексы регионов из searcherByKey\n\t *\n\t * Активность регионов определяется в searcherByKey\n\t */\n\tconst allRegionsIndexes = computed(() => {\n\t\tconst regionsIndexes = new Set<number>();\n\n\t\tsearcherByKey.value.forEach((searcher) => {\n\t\t\tsearcher.regionByIndex.forEach((region) => {\n\t\t\t\tif (region.index === globalRegionIndex) return;\n\t\t\t\tif (region.index === dummyIndex) return;\n\n\t\t\t\tregionsIndexes.add(region.index);\n\t\t\t});\n\t\t});\n\n\t\treturn regionsIndexes;\n\t});\n\n\tconst selectSearcher = useSelectSearcher(props, searcherByKey);\n\tconst selectRegion = useSelectRegion(props, activeSearcherIndexed);\n\tconst compare = useCompare(props, searcherByKey, allRegionsIndexes);\n\n\t// контроль за внешним изменением списка ПС\n\twatch(searcherByKey, () => {\n\t\t// возможные значения для сравнения регионов/пс\n\t\tif (props.onlySearcher) {\n\t\t\tcompare.regionsIndexes.value = Array.from(searcherByKey.value.keys());\n\t\t} else {\n\t\t\tcompare.regionsIndexes.value = compare.regionsIndexes.value.filter(regionIndex => {\n\t\t\t\treturn allRegionsIndexes.value.has(regionIndex);\n\t\t\t});\n\t\t}\n\n\t\tif (selectSearcher.searcherKey.value === searhcerCompareKey) return;\n\n\t\tlet newSearcherKey = searcherByKey.value.keys().next().value;\n\n\t\tsearcherByKey.value.forEach((searcher) => {\n\t\t\t// определить выбранную ПС\n\t\t\tif (props.onlySearcher && searcher.key === selectSearcher.searcherKey.value) {\n\t\t\t\tnewSearcherKey = selectSearcher.searcherKey.value;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\tselectRegion.regionIndex.value &&\n\t\t\t\tsearcher.regionByIndex?.has(selectRegion.regionIndex.value)\n\t\t\t) {\n\t\t\t\tnewSearcherKey = searcher.key;\n\t\t\t}\n\n\t\t\t// выбрать первую ПС с выбранным регионом\n\t\t\tif (!props.onlySearcher) {\n\t\t\t\tlet regionsNewSearcher: SearcherIndexed['regionByIndex'] | undefined;\n\n\t\t\t\tif (newSearcherKey !== undefined) {\n\t\t\t\t\tregionsNewSearcher = searcherByKey.value.get(newSearcherKey)?.regionByIndex;\n\t\t\t\t}\n\n\t\t\t\tconst regionsCurrentSearcher = searcherByKey.value.get(searcher.key)?.regionByIndex;\n\t\t\t\tif (\n\t\t\t\t\tregionsNewSearcher?.has(dummyIndex) &&\n\t\t\t\t\t!regionsCurrentSearcher?.has(dummyIndex)\n\t\t\t\t) {\n\t\t\t\t\tnewSearcherKey = searcher.key;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t// if (props.addCompare && !selectSearcher.optionBySearcherKey.value.has(dummyIndex)) {\n\t\t// \tif (!selectRegion.regionIndex.value && compare.regionsIndexes.value?.length) newSearcherKey = searhcerCompareKey;\n\t\t// }\n\n\t\t// if (props.onlySearcher && selectRegion.regionIndex.value !== dummyIndex) newSearcherKey = selectRegion.regionIndex.value;\n\n\t\tif (newSearcherKey !== undefined) {\n\t\t\tselectSearcher.searcherKey.value = newSearcherKey;\n\t\t}\n\n\t\tif (\n\t\t\tselectRegion.regionIndex.value !== undefined &&\n\t\t\t!(activeSearcherIndexed.value?.regionByIndex)?.has(selectRegion.regionIndex.value)\n\t\t) {\n\t\t\tselectRegion.regionIndex.value = activeSearcherIndexed.value?.regions?.keys().next().value as number;\n\t\t}\n\t}, { immediate: true });\n\n\tconst getSearcherKey = () => {\n\t\tif (selectSearcher.searcherKey.value === searhcerCompareKey || selectSearcher.searcherKey.value === dummyIndex) return;\n\n\t\treturn selectSearcher.searcherKey.value;\n\t};\n\n\tconst getRegionIndex = () => {\n\t\tif (props.onlySearcher) return;\n\n\t\tif (selectRegion.regionIndex.value === dummyIndex) return;\n\n\t\tlet res: Region['index'] | undefined = selectRegion.regionIndex.value;\n\n\t\t// в качестве ключа используется region.key\n\t\tif (props.forFrequency) {\n\t\t\tconst regionKey = selectRegion.regionIndex.value;\n\t\t\tconst region = findRegion(props.forFrequency, { searcher_key: getSearcherKey(), key: regionKey } as Region, props.searchers);\n\n\t\t\tres = region?.index;\n\t\t}\n\n\t\treturn res;\n\t};\n\n\t/**\n\t * Получить выбранную ПС\n\t */\n\tconst getSearcher = () => {\n\t\tconst searcherKey = getSearcherKey();\n\t\tif (searcherKey === undefined) return;\n\n\t\treturn searcherByKey.value.get(searcherKey);\n\t};\n\n\t/**\n\t * Получить выбранный регион\n\t */\n\tconst getRegion = () => {\n\t\tconst regionIndex = getRegionIndex();\n\t\tif (regionIndex === undefined) return;\n\n\t\treturn getSearcher()?.regionByIndex?.get(regionIndex);\n\t};\n\n\treturn {\n\t\tselectSearcher,\n\t\tselectRegion,\n\t\tcompare,\n\n\t\tsearcherByKey,\n\t\tallRegionsIndexes,\n\n\t\tgetSearcher,\n\t\tgetRegion,\n\t};\n};\n","<script setup lang=\"ts\">\nimport { watch } from 'vue';\nimport type { Props, Region } from './types';\nimport Select from '@/components/forms/select/select.vue';\nimport Button from '@/components/forms/button/button.vue';\nimport { useSelectorRegion } from './composables/selectorRegion';\nimport { dummyIndex, globalRegionIndex, searhcerCompareKey } from './utils/consts';\nimport { dialogRegionSelector } from '@/components/project/regionSelector/utils/utils';\n\nconst props = withDefaults(defineProps<Props>(), {\n\tsearchers: () => [],\n\t// compareRegionsIndexes: () => [],\n\taddSearcherIcon: true,\n\taddRegionIcon: true,\n\taddChanger: true,\n});\n\nconst model = defineModel<Props['modelValue']>({ required: true });\nconst modelSingle = defineModel<Props['modelValueSingle']>('modelValueSingle');\n\nconst {\n\tselectSearcher,\n\tselectRegion,\n\tcompare,\n\n\tsearcherByKey,\n\tallRegionsIndexes,\n\n\tgetSearcher,\n\tgetRegion,\n} = useSelectorRegion(props);\n\nconst onClickCompare = () => {\n\tconst regions: Region[] = [];\n\n\tsearcherByKey.value.forEach((searcher) => {\n\t\tif (!searcher.enabled) return;\n\n\t\tsearcher.regions.forEach((region) => {\n\t\t\tif (!region.enabled) return;\n\n\t\t\tregions.push(region);\n\t\t});\n\t});\n\n\tdialogRegionSelector.open('regions', {\n\t\tregions,\n\t\tregionsIndexes: compare.regionsIndexes.value,\n\t\t'@update:regionsIndexes': (regionsIndexes: number[]) => compare.regionsIndexes.value = regionsIndexes,\n\t});\n};\n\nwatch([selectRegion.regionIndex, selectSearcher.searcherKey, compare.regionsIndexes], () => {\n\tif (selectSearcher.searcherKey.value === searhcerCompareKey && compare.regionsIndexes.value.length) {\n\t\tif (JSON.stringify(model.value) === JSON.stringify(compare.regionsIndexes.value)) {\n\t\t\treturn;\n\t\t}\n\n\t\tmodel.value = [...compare.regionsIndexes.value];\n\t} else {\n\t\tif (props.onlySearcher) {\n\t\t\tmodel.value = [selectSearcher.searcherKey.value];\n\n\t\t\tif (selectSearcher.searcherKey.value === dummyIndex && !props.autoRegion) model.value.length = 0;\n\t\t} else {\n\t\t\tmodel.value = [selectRegion.regionIndex.value];\n\n\t\t\t// не добавлять несуществующий регион в результаты\n\t\t\tif (selectRegion.regionIndex.value === dummyIndex && !props.autoRegion) model.value.length = 0;\n\t\t}\n\t}\n\n\t// регионов нет\n\tif (!props.onlySearcher && !allRegionsIndexes.value.size) {\n\t\tselectSearcher.searcherKey.value = dummyIndex;\n\t}\n});\n\nif (modelSingle.value) {\n\twatch(modelSingle, () => {\n\t\tif (modelSingle.value) {\n\t\t\tmodel.value = [modelSingle.value];\n\t\t}\n\t}, { immediate: true });\n}\n\nwatch(model, () => {\n\tif (model.value[0]) {\n\t\tmodelSingle.value = model.value[0];\n\t}\n\n\t// проверка входных данных v-model на корректность\n\tif (props.onlySearcher) {\n\t\tif (\n\t\t\t!model.value.length ||\n\t\t\tmodel.value.length === 1 && !searcherByKey.value.has(model.value[0]) ||\n\t\t\tmodel.value.length === 1 && model.value[0] === dummyIndex && !props.autoRegion\n\t\t) {\n\t\t\tlet defaultKey: number | undefined = searcherByKey.value.keys().next().value;\n\n\t\t\t// запретить устанавливать несуществующий регион\n\t\t\tif (defaultKey === dummyIndex && !props.autoRegion) {\n\t\t\t\tdefaultKey = undefined;\n\n\t\t\t\tmodelSingle.value = dummyIndex;\n\t\t\t}\n\n\t\t\tif (defaultKey !== undefined) {\n\t\t\t\tmodel.value = [defaultKey];\n\t\t\t}else{\n\t\t\t\tmodel.value.length = 0;\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\tif (\n\t\t\tmodel.value.length > 1 &&\n\t\t\tJSON.stringify(model.value) !== JSON.stringify(compare.regionsIndexes.value)\n\t\t) {\n\t\t\tmodel.value = [...compare.regionsIndexes.value];\n\n\t\t\treturn;\n\t\t}\n\t} else {\n\t\tlet newModel = [...new Set(model.value)];\n\n\t\tlet defaultIndex = searcherByKey.value.values().next().value?.regionByIndex?.keys().next().value;\n\t\tif (props.forFrequency) {\n\t\t\tdefaultIndex = searcherByKey.value.values().next().value?.regionByIndex?.values().next().value?.key;\n\t\t}\n\n\t\t// запретить устанавливать несуществующий регион\n\t\tif (defaultIndex === dummyIndex && !props.autoRegion) {\n\t\t\tdefaultIndex = undefined;\n\n\t\t\tmodelSingle.value = dummyIndex;\n\t\t}\n\n\t\tif (!newModel.length) {\n\t\t\tif (defaultIndex !== undefined) {\n\t\t\t\tnewModel.push(defaultIndex);\n\t\t\t}\n\t\t} else if (newModel.length === 1) {\n\t\t\tlet all = allRegionsIndexes.value;\n\n\t\t\tif (props.forFrequency) {\n\t\t\t\tall = new Set();\n\t\t\t\tsearcherByKey.value.forEach((searcher) => {\n\t\t\t\t\tsearcher.regionByIndex.forEach((region) => {\n\t\t\t\t\t\tif (region.index === globalRegionIndex) return;\n\t\t\t\t\t\tif (region.index === dummyIndex) return;\n\n\t\t\t\t\t\tall.add(region.key);\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// убрано, так как dummyIndex запрещено выбирать в model\n\t\t\t// if (!all.has(newModel[0]) && newModel[0] !== dummyIndex) {\n\t\t\tif (!all.has(newModel[0])) {\n\t\t\t\tnewModel = [];\n\t\t\t\tif (defaultIndex !== undefined) {\n\t\t\t\t\tnewModel.push(defaultIndex);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tnewModel = newModel.filter(index => allRegionsIndexes.value.has(index));\n\t\t\tif (!newModel.length && defaultIndex !== undefined) {\n\t\t\t\tnewModel.push(defaultIndex);\n\t\t\t}\n\t\t}\n\n\t\tif (JSON.stringify(model.value) !== JSON.stringify(newModel)) {\n\t\t\tmodel.value = newModel;\n\n\t\t\treturn;\n\t\t}\n\t}\n\n\t// входные данные v-model совпадают с внутренними значениями\n\tif (\n\t\tmodel.value.length === 1 &&\n\t\tmodel.value[0] === (props.onlySearcher ? selectSearcher.searcherKey.value : selectRegion.regionIndex.value)\n\t) {\n\t\treturn;\n\t}\n\n\t// if (\n\t// \tmodel.value.length > 1 &&\n\t// \tselectorSearcher.searcherKey.value === searhcerCompareKey &&\n\t// \tJSON.stringify(model.value) === JSON.stringify(selectorCompare.regionsIndexes.value)\n\t// ) {\n\t// \treturn;\n\t// }\n\n\t// обновление regionIndex, searcherKey, selectorCompare.regionsIndexes\n\tif (props.onlySearcher) {\n\t\t// if (!model.value.length) {\n\t\t// \tselectorSearcher.searcherKey.value = dummyIndex;\n\t\t//\n\t\t// \treturn;\n\t\t// }\n\n\t\tif (model.value.length === 1) {\n\t\t\tselectSearcher.searcherKey.value = model.value[0];\n\n\t\t\treturn;\n\t\t}\n\n\t\tselectSearcher.searcherKey.value = searhcerCompareKey;\n\n\t\treturn;\n\t} else {\n\t\tif (!model.value.length) {\n\t\t\t// selectSearcher.searcherKey.value = dummyIndex;\n\t\t\t// selectRegion.regionIndex.value = dummyIndex;\n\n\t\t\treturn;\n\t\t}\n\n\t\tif (model.value.length === 1 && selectSearcher.searcherKey.value !== searhcerCompareKey) {\n\t\t\tselectRegion.regionIndex.value = model.value[0];\n\n\t\t\tlet newSearherKey: number | undefined;\n\t\t\tfor (const searcher of searcherByKey.value.values()) {\n\t\t\t\tfor (const region of searcher.regionByIndex.values()) {\n\t\t\t\t\tconst currentRegionIndex = props.forFrequency ? region.key : region.index;\n\t\t\t\t\tif (currentRegionIndex === selectRegion.regionIndex.value) {\n\t\t\t\t\t\tnewSearherKey = searcher.key;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (newSearherKey !== undefined) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (newSearherKey !== undefined) {\n\t\t\t\tselectSearcher.searcherKey.value = newSearherKey;\n\t\t\t}\n\t\t} else {\n\t\t\tselectSearcher.searcherKey.value = searhcerCompareKey;\n\t\t\tcompare.regionsIndexes.value = [...model.value];\n\t\t}\n\t}\n}, { immediate: true });\n\ndefineExpose({\n\tgetSearcher,\n\tgetRegion,\n});\n</script>\n\n<template>\n\t<div\n\t\t:class=\"{\n\t\t\t'top-selectorRegion': true,\n\t\t\t'top-selectorRegion-onlySearcher': onlySearcher,\n\t\t}\"\n\t>\n\t\t<Select\n\t\t\t:options=\"selectSearcher.optionBySearcherKey.value\"\n\t\t\tv-model=\"selectSearcher.searcherKey.value\"\n\t\t\tname=\"searcher_key\"\n\t\t\t:addChanger=\"addChanger\"\n\t\t/>\n\n\t\t<Select\n\t\t\tv-if=\"!onlySearcher && selectSearcher.searcherKey.value !== searhcerCompareKey\"\n\t\t\tclass=\"top-select-region\"\n\t\t\t:options=\"selectRegion.optionByRegionIndex.value\"\n\t\t\tv-model=\"selectRegion.regionIndex.value\"\n\t\t\t:name=\"forFrequency ? 'region_key' : 'region_index'\"\n\t\t\t:addChanger=\"addChanger\"\n\t\t\t:data-top-icon=\"addRegionIcon ? '' : undefined\"\n\t\t/>\n\n\t\t<Button\n\t\t\tv-if=\"addCompare && !onlySearcher && selectSearcher.searcherKey.value === searhcerCompareKey\"\n\t\t\tname=\"compare\"\n\t\t\t@click=\"onClickCompare\"\n\t\t\t:data-count-compare-regions-indexes=\"compare.regionsIndexes.value.length\"\n\t\t>\n\t\t\t{{ $i18n.Common.Selected_regions }}\n\t\t</Button>\n\t</div>\n</template>\n\n<style>\n@import \"./styles/searcherColors.css\";\n\n.top-selectorRegion {\n\twidth: 340px;\n\tdisplay: inline-flex;\n\tvertical-align: middle;\n}\n\n.top-selectorRegion > .top-select {\n\tflex-grow: 1;\n}\n\n.top-selectorRegion > .top-select:focus-within {\n\tz-index: 4;\n}\n\n.top-selectorRegion > .top-select > .top-select_select:hover,\n.top-selectorRegion > .top-select > .top-select_select.top-error {\n\tz-index: 1;\n}\n\n.top-selectorRegion > .top-select-searcher_key {\n\twidth: 140px;\n\tmax-width: 160px;\n\tmargin-right: -1px;\n}\n\n.top-selectorRegion > .top-select-region > select {\n\tborder-top-left-radius: 0;\n\tborder-bottom-left-radius: 0;\n}\n\n.top-selectorRegion > .top-select-region {\n\t--top-icon-size: 20px;\n\t--top-icon-color: var(--color-text-primary);\n\n\t--top-icon2-size: 20px;\n\t--top-icon2-color: var(--color-text-primary);\n}\n\n.top-selectorRegion > .top-select-region[data-top-icon][data-top-icon2] {\n\t--top-icon2-size: 16px;\n}\n\n.top-selectorRegion > .top-select-region[data-top-icon][data-top-icon2]:after {\n\ttext-indent: -4px;\n}\n\n.top-selectorRegion > [name=\"compare\"] {\n\tborder-top-left-radius: 0;\n\tborder-bottom-left-radius: 0;\n\tflex-grow: 1;\n}\n\n.top-selectorRegion > [name=\"compare\"]:after {\n\tcontent: \"(\" attr(data-count-compare-regions-indexes) \")\";\n\tmargin: 0 0 0 6px;\n}\n\n.top-selectorRegion:not(.top-selectorRegion-onlySearcher) > .top-select-searcher_key > select {\n\tborder-top-right-radius: 0;\n\tborder-bottom-right-radius: 0;\n\tmargin-right: 0;\n}\n\n.top-selectorRegion:not(.top-selectorRegion-onlySearcher) > .top-select-searcher_key[data-value=\"-1\"] > select {\n\tborder-right: none;\n}\n\n.top-selectorRegion-onlySearcher {\n\twidth: 140px;\n}\n</style>\n","// Генерация плоского массива объектов папок с заполнением childsIds и счетчиков\nimport type { Folder, FolderTree } from './types';\nimport type { Props } from '../types';\nimport { useI18n } from '@/core/app';\n\nexport const folderDefault: Folder = {\n\tid: 0,\n\tname: '/',\n\tpath: '/',\n};\n\n/**\n * Сгенерировать объект корневой папки для компонента\n *\n * Содержит измененное имя\n *\n * Необходимо предустанавливать для минимизации `rerender`\n */\nexport const genRootFolder = (folders: FolderTree | undefined, useSelectAll: boolean) => {\n\tlet rootFolder = folders?.['root'][0] ?? folderDefault;\n\trootFolder = { ...rootFolder };\n\n\trootFolder.name = genRootFolderName(useSelectAll);\n\n\treturn rootFolder;\n};\n\nfunction genRootFolderName(useSelectAll: boolean) {\n\tif (useSelectAll) {\n\t\treturn useI18n()?.Common.All_folders;\n\t} else {\n\t\treturn '/ (' + useI18n()?.Keywords.Root_folder + ')';\n\t}\n}\n\n/**\n * Сгенерировать плоский список папок\n *\n * В `folders` должен передаваться список всех папок, дозагрузка не предусмотрена\n */\nexport const genFlat = (\n\tfolderById: NonNullable<Props['folders']>,\n\tuseSelectAll: boolean,\n\tresultFolders = new Map<Folder['id'], Folder>(),\n\tparentFolder: Folder = { id: 'root' } as unknown as Folder,\n\tdepth = 0,\n) => {\n\tparentFolder.childsIds = [];\n\n\tif (parentFolder.id) {\n\t\tparentFolder.countAllGroupsActive = parentFolder.count_groups_active;\n\t}\n\n\tif (!folderById[parentFolder.id]) {\n\t\treturn resultFolders;\n\t}\n\n\tfolderById[parentFolder.id].forEach((folder) => {\n\t\tfolder = { ...folder };\n\n\t\tparentFolder.childsIds!.push(folder.id);\n\n\t\tif (folder.id === 0) folder.name = genRootFolderName(useSelectAll);\n\n\t\tconst prefix = depth > 1 ? '-'.repeat(depth - 1) + ' ' : '';\n\t\tif (prefix && !folder.name.startsWith(prefix)) folder.name = prefix + folder.name;\n\n\t\tresultFolders.set(folder.id, folder);\n\n\t\tgenFlat(folderById, useSelectAll, resultFolders, folder, depth + 1);\n\n\t\tparentFolder.childsIds = parentFolder.childsIds!.concat(folder.childsIds!);\n\n\t\tif (parentFolder.id && folder.count_groups_active) {\n\t\t\tparentFolder.countAllGroupsActive! += folder.count_groups_active;\n\t\t}\n\t});\n\n\treturn resultFolders;\n};\n\n/**\n * Создать API запрос на получение групп\n */\nexport const genApiGetFolders = (client: Api.Client<Api.TV.Paths, true>) => {\n\treturn client.gen('/get/keywords_2/folders/', ['id', 'parent_id', 'name', 'path']).changeParams({ orders: ['ord_path'] as any, limit: 100 });\n};\n","import { watch, type WatchOptions, type WatchSource, type WatchStopHandle } from 'vue';\n\ntype UnwrapSource<T> = T extends WatchSource<infer V>\n\t? V\n\t: T extends object ? T : never;\n\ntype Changes<T> = Partial<{\n\t[K in keyof T]: {\n\t\told: UnwrapSource<T[K]> | undefined;\n\t\tnew: UnwrapSource<T[K]>\n\t}\n}>;\n\n/**\n * useWatch — расширенный `watch` с измененным callback\n * - принимает объект с реактивными свойствами, точно такими же, как при указании в массиве в стандартном `watch`\n * - callback возвращает изменившиеся свойства в объекте `changes` с `{old, new}`\n *\n * Если у вас есть несколько `watch`, которые меняют одно состояние, то могут возникнуть проблемы:\n * - рост длины цепочек `watch`, когда один `watch` приводит к срабатыванию другого\n * - лишние вызовы при согласованных состояниях, когда разные `watch` устанавливают одно и то же значение\n * - гонка при несогласованных состояниях, когда разные `watch` в разные тики при этом одновременно могут устанавливать разные состояния\n *\n * Если у вас такой случай и его нельзя упростить, то используйте `useWatch`, в остальных случаях используйте только стандартный `watch`\n *\n * @example\n *\n * ```js\n *\n * useWatch({ a: ref(5), b: ref('word') }, (changes) => {\n * \tconsole.log(changes.a?.new, changes.a?.old);\n * \tconsole.log(changes.b?.new, changes.b?.old);\n * });\n * ```\n */\nexport function useWatch<T extends Record<string, WatchSource>>(\n\tsourcesObj: T,\n\tcb: (\n\t\tchanges: Changes<T>,\n\t\tonCleanup: (fn: () => void) => void,\n\t) => void | Promise<void>,\n\toptions?: WatchOptions,\n): WatchStopHandle {\n\tconst keys = Object.keys(sourcesObj) as (keyof T)[];\n\tconst sources = keys.map(k => sourcesObj[k]);\n\n\treturn watch(sources, (newValues, oldValues, onCleanup) => {\n\t\tconst changed = {} as Changes<T>;\n\n\t\tnewValues.forEach((val, i) => {\n\t\t\tif (!Object.is(val, oldValues[i])) {\n\t\t\t\tconst k = keys[i];\n\n\t\t\t\tchanged[k] = {\n\t\t\t\t\told: oldValues[i],\n\t\t\t\t\tnew: val,\n\t\t\t\t} as any;\n\t\t\t}\n\t\t});\n\n\t\tif (Object.keys(changed).length) cb(changed, onCleanup);\n\t}, options);\n}\n","<script setup lang=\"ts\">\nimport { computed, ref, watch } from 'vue';\nimport Selector2 from '@/components/formsExt/selector2/selector2.vue';\nimport type { Props } from './types';\nimport { folderDefault, genApiGetFolders, genFlat } from './utils';\nimport { apiSetSearchParamsFilter } from '@/components/formsExt/formsExt';\nimport { useWatch } from '@/core/utils/composables/useWatch';\n\nconst props = withDefaults(defineProps<Props>(), {\n\taddIcon: true,\n});\n\nconst modelFolderId = defineModel<Props['folderId']>('folderId', { required: true });\nconst modelFolder = defineModel<NonNullable<Props['folder']>>('folder', { default: folderDefault });\n\nconst apiGet = props.client && !props.folders ? genApiGetFolders(props.client) : undefined;\n\nconst refSelector = ref<InstanceType<typeof Selector2> | null>(null);\n\n// Настройка API\nwatch(() => props.projectId, () => {\n\tapiGet?.changeParams({\n\t\tproject_id: props.projectId,\n\t});\n\n\tapiGet?.setOptions({\n\t\tcheckFingerprint: 'TopGroupSelectorFolders:' + props.projectId,\n\t});\n\n\trefSelector.value?.resetCache();\n}, { immediate: true });\n\n/**\n * Плоский список папок\n */\nconst foldersFlat = computed(() => {\n\treturn genFlat(props.folders ?? { root: [folderDefault] }, props.canSelectAll);\n});\n\n/**\n * Проверка входного значения `modelFolderId` и изменение `modelFolder`\n *\n * Определяем существует ли папка:\n * - Если существует: меняем папку на указанную\n * - Если не существует: устанавливаем Корневую папку\n *\n * При загрузке групп по API проверка выполняется в асинхронном режиме\n */\nuseWatch({\n\tmodelFolder,\n\tmodelFolderId,\n}, async (changes) => {\n\tif (modelFolderId.value === modelFolder.value.id) {\n\t\treturn;\n\t}\n\n\t// изменение только `modelFolder`\n\tif (changes.modelFolder && !changes.modelFolderId) {\n\t\tmodelFolderId.value = modelFolder.value.id;\n\n\t\treturn;\n\t}\n\n\tlet folderSelected = foldersFlat.value.get(props.folderId);\n\tif (!folderSelected) folderSelected = folderDefault;\n\n\tmodelFolder.value = folderSelected;\n\tmodelFolderId.value = folderSelected.id;\n\n\t// /**\n\t// * Для не api\n\t// */\n\t// if (props.folders) {\n\t// \tmodelFolder.value = folderSelected;\n\t// \tmodelFolderId.value = folderSelected.id;\n\t// }\n\t//\n\t// /**\n\t// * Для api проверка не делается\n\t// */\n\t// if (!props.folders) {\n\t// \tmodelFolder.value = folderSelected;\n\t// \tmodelFolderId.value = folderSelected.id;\n\t// }\n}, { immediate: true });\n</script>\n\n<template>\n\t<Selector2\n\t\tclass=\"top-groupSelector_folder\"\n\t\tv-model=\"modelFolder\"\n\t\t:items=\"folders ? [...foldersFlat.values()] : undefined\"\n\t\tsearch-type=\"inline\"\n\t\t:icon=\"addIcon ? '' : undefined\"\n\t\t:api=\"folders ? undefined : apiGet\"\n\t\t:apiSetSearchParams=\"(...args) => apiSetSearchParamsFilter(...args, 'name')\"\n\t\t:addChanger\n\t\tuseCache\n\t/>\n</template>\n","import type { Folder } from '../folders/types';\nimport type { Group } from './types';\nimport { useI18n } from '@/core/plugins/i18n';\nimport { folderDefault } from '../folders/utils';\nimport { ITEM_ID_ALL } from '@/components/formsExt/selector2/utils';\n\n/**\n * Элемент - группа ненайдена\n *\n * Указывается несуществующий id\n */\nexport const groupNone: Group = {\n\tid: -1,\n\tname: '--',\n\tfolder_id: folderDefault.id,\n\tfolder_path: folderDefault.path,\n};\n\n/**\n * Элемент для выбора всех групп\n */\nexport const groupAll: Group = {\n\tid: ITEM_ID_ALL,\n\tname: 'All groups',\n\tfolder_id: folderDefault.id,\n\tfolder_path: folderDefault.path,\n};\n\n/**\n * Элемент - `Все группы`\n */\nexport const genGroupAll = () => {\n\tgroupAll.name = useI18n()?.Common.All_groups;\n\n\treturn groupAll;\n};\n\n/**\n * Элемент - `Выберите группу`\n */\nexport const genGroupPlaceholder = () => {\n\tgroupAll.name = useI18n()?.Keywords.Choose_group;\n\n\treturn groupAll;\n};\n\n/**\n * Сгенерировать список групп для TopSelector\n */\nexport const genItems = (groups: Group[], on?: boolean, folderForFilter?: Folder) => {\n\tgroups = [...groups];\n\n\t// фильтр по активности\n\tif (on !== undefined) {\n\t\tgroups = groups.filter((group) => {\n\t\t\treturn group.on == Number(on);\n\t\t});\n\t}\n\n\t// фильтр по папке\n\tif (folderForFilter && folderForFilter.id && folderForFilter.childsIds) {\n\t\tgroups = groups.filter((group) => {\n\t\t\treturn (\n\t\t\t\tgroup.folder_id === folderForFilter.id ||\n\t\t\t\tfolderForFilter.childsIds!.includes(group.folder_id)\n\t\t\t);\n\t\t});\n\t}\n\n\treturn groups;\n};\n\n/**\n * Создать API запрос на получение групп\n */\nexport const genApiGetGroups = (client: Api.Client<Api.TV.Paths, true>) => {\n\treturn client.gen('/get/keywords_2/groups/', ['id', 'name', 'folder_id', 'folder_path']).changeParams({ folder_id_depth: true, limit: 100 });\n};\n\n/**\n * Создать API запрос на получение групп\n */\nexport const genApiAddGroup = (client: Api.Client<Api.TV.Paths, true>) => {\n\treturn client.gen('/add/keywords_2/groups/');\n};\n","<script setup lang=\"ts\">\nimport { reactive, ref, watch } from 'vue';\nimport { useWatch } from '@/core/utils/composables/useWatch';\nimport Selector2 from '@/components/formsExt/selector2/selector2.vue';\nimport type { Item as SelectorItem } from '@/components/formsExt/selector2/types';\nimport { apiSetSearchParamsFilter, ITEM_ID_ALL, ITEM_ID_NEW } from '@/components/formsExt/formsExt';\nimport type { Emits, Group, Props } from './types';\nimport { genApiAddGroup, genApiGetGroups, genGroupAll, genGroupPlaceholder, genItems, groupNone } from './utils';\nimport { genFieldFilter } from '@/api/api/index';\nimport { folderDefault } from '../folders/utils';\n\nconst props = withDefaults(defineProps<Props>(), {\n\ton: undefined,\n\tautoselect: 'first',\n\taddChanger: true,\n\taddIcon: true,\n});\n\nconst emits = defineEmits<Emits>();\n\nconst modelGroupId = defineModel<Props['groupId']>('groupId', { required: true });\nconst modelGroup = defineModel<Props['group']>('group', { required: true });\n\nconst apiGet = props.client && !props.groups ? genApiGetGroups(props.client) : undefined;\nconst apiAdd = props.client ? genApiAddGroup(props.client) : undefined;\n\nconst refSelector = ref<InstanceType<typeof Selector2> | null>(null);\n\n/**\n * Группы выбранной папки\n */\nconst groupsBySelectedFolder = ref<Group[] | undefined>(undefined);\n\nconst filterByFolder = genFieldFilter('folder_id', 'EQUALS', [props.folder?.id ?? 0]);\n\napiGet?.changeParams({\n\tproject_id: props.projectId,\n\tfolder_id_depth: true,\n\tfilters: [\n\t\tfilterByFolder,\n\t],\n});\n\nif (props.on !== undefined) {\n\tapiGet?.params.filters?.push(genFieldFilter('on', 'EQUALS', [Number(props.on)]));\n}\n\n/**\n * Синхронная установка группы и id группы\n *\n * Id рекомендуется вместе с группой\n * - для согласованной смены состояния\n * - для уменьшения цепочки `watch` синхронизации состояний `modelGroupId` и `modelGroup`\n *\n * Пример несогласованности: если передать `modelGroup` и значение `modelGroupId`, которое ему не соответсвует, то попытка установить ту же самоую группу в `modelGroup` не приведет к смене `modelGroupId`\n */\nconst setGroup = (group: Props['group']) => {\n\tmodelGroup.value = group;\n\tmodelGroupId.value = group.id;\n};\n\n// Настройка API, обработка смены папки и выбор всех групп\nuseWatch({\n\tprojectId: () => props.projectId,\n\tfolderId: () => props.folder?.id,\n\tcanSelectAll: () => props.canSelectAll,\n}, (changes) => {\n\t// Настройка API\n\tif (changes.projectId) {\n\t\tapiGet?.changeParams({\n\t\t\tproject_id: props.projectId,\n\t\t});\n\n\t\tapiGet?.setOptions({\n\t\t\tcheckFingerprint: 'TopGroupSelectorGroups:' + props.projectId,\n\t\t});\n\t}\n\n\t// Обработка смены папки\n\tif (changes.folderId) {\n\t\tfilterByFolder.values = [\n\t\t\tprops.folder?.id ?? 0,\n\t\t];\n\n\t\t// необходимо установить группы выбранной папки\n\t\tif (props.groups) {\n\t\t\tgroupsBySelectedFolder.value = genItems(props.groups, props.on, props.folder);\n\t\t}\n\t}\n\n\trefSelector.value?.resetCache();\n\n\t/**\n\t * При смене папки автоматически выбирать нужную группу с учетом `props.autoselect`\n\t *\n\t * Новая группа, при смене папки не меняется\n\t */\n\tif (\n\t\t(changes.folderId?.old !== undefined || changes.canSelectAll?.old !== undefined) &&\n\t\tmodelGroupId.value !== ITEM_ID_NEW\n\t) {\n\t\tlet group: Props['group'] | undefined;\n\n\t\tif (props.autoselect === 'first') {\n\t\t\tgroup = groupsBySelectedFolder.value?.[0];\n\n\t\t\tif (props.canSelectAll) {\n\t\t\t\tgroup = reactive(genGroupAll());\n\t\t\t}\n\t\t}\n\n\t\tif (props.autoselect === 'placeholder' || !group) {\n\t\t\tgroup = genGroupPlaceholder();\n\t\t}\n\n\t\tsetGroup(group);\n\t}\n}, { immediate: true });\n\n/**\n * Проверка входного значения `modelGroupId` и изменение `modelGroup`\n *\n * Определяем существует ли группа:\n * - Если существует: меняем группу на указанную\n * - Если не существует: устанавливаем \"Нет группы\"\n *\n * При загрузке групп по API проверка выполняется в асинхронном режиме\n */\nuseWatch({\n\tmodelGroup,\n\tmodelGroupId,\n}, async (changes) => {\n\tif (modelGroupId.value === modelGroup.value.id) {\n\t\treturn;\n\t}\n\n\t// изменение только `modelGroup`\n\tif (changes.modelGroup && !changes.modelGroupId) {\n\t\tmodelGroupId.value = modelGroup.value.id;\n\n\t\treturn;\n\t}\n\n\tif (props.canAdd && modelGroupId.value === ITEM_ID_NEW) {\n\t\treturn;\n\t}\n\n\t/**\n\t * Для не api\n\t */\n\tif (props.groups) {\n\t\tlet groupSelected = groupsBySelectedFolder.value?.find((group) => group.id === modelGroupId.value);\n\n\t\tif (!groupSelected && props.canSelectAll) groupSelected = genGroupAll();\n\n\t\tif (groupSelected) {\n\t\t\tsetGroup(groupSelected);\n\t\t} else {\n\t\t\tconst firstGroup = groupsBySelectedFolder.value?.[0];\n\t\t\tif (firstGroup && !modelGroupId.value) {\n\t\t\t\t// группа не была указана, инициализация компонента\n\t\t\t\tsetGroup(firstGroup);\n\t\t\t} else {\n\t\t\t\tsetGroup(groupNone);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Для api\n\t */\n\tif (props.client && !props.groups && (modelGroupId.value || modelGroupId.value === ITEM_ID_ALL && !props.canSelectAll)) {\n\t\tconst api = genApiGetGroups(props.client).changeParams({\n\t\t\tproject_id: props.projectId,\n\t\t\tid: modelGroupId.value,\n\t\t\tfilters: [\n\t\t\t\tfilterByFolder,\n\t\t\t],\n\t\t});\n\n\t\t/**\n\t\t * @remarks При выборе группы \"Все группы\" или при добавлении группы отмена запроса не происходит\n\t\t */\n\t\tapi?.setOptions({\n\t\t\tcheckFingerprint: 'TopGroupSelectorFindGroup:' + props.projectId,\n\t\t});\n\n\t\tconst res = await api.call();\n\n\t\t// при отмене запроса, смену группы не выполняем\n\t\tif (!res.errors?.length && !res.result) return;\n\n\t\tif (!res.errors && res.result[0]) {\n\t\t\tsetGroup(res.result[0] as Group);\n\t\t} else {\n\t\t\tsetGroup(groupNone);\n\t\t}\n\t}\n}, { immediate: true });\n\nconst addGroups = async (group: SelectorItem) => {\n\t// просто выбор произвольной группы, без реального добавления\n\tif (props.canAdd !== 'api') return;\n\n\t// при добавлении группы `props.client` обязателен\n\tif (!apiAdd) return;\n\n\tif (import.meta.env.STORYBOOK) {\n\t\tapiAdd.changeParams({\n\t\t\t// @ts-ignore Для storybook\n\t\t\t__sbIsUseAPI: !props.groups,\n\t\t});\n\t}\n\n\tconst modelGroupOld = modelGroup.value;\n\n\t// Для api\n\tconst res = await apiAdd.changeParams({\n\t\tproject_id: props.projectId,\n\t\tnames: [group.name],\n\t\tto_id: props.folder?.id ?? folderDefault.id,\n\t\tto_type: 'in_folder_last',\n\t}).call();\n\n\tif (res.result) {\n\t\tsetGroup(res.result as unknown as Group);\n\n\t\temits('addGroup', res.result as unknown as Group);\n\t} else {\n\t\t// вернуть предыдущий выбор в случае ошибки добавления группы\n\t\tsetGroup(modelGroupOld);\n\t}\n\n\trefSelector.value?.resetCache(true);\n};\n</script>\n\n<template>\n\t<Selector2\n\t\tref=\"refSelector\"\n\t\tclass=\"top-groupSelector_group\"\n\t\tv-model=\"modelGroup\"\n\t\t:items=\"groupsBySelectedFolder ?? groups\"\n\t\tsearchType=\"inline\"\n\t\t:icon=\"addIcon ? '' : undefined\"\n\t\t:api=\"groups ? undefined : apiGet\"\n\t\t:apiSetSearchParams=\"(...args) => apiSetSearchParamsFilter(...args, 'name')\"\n\t\t:appendSearchToResult=\"!!canAdd\"\n\t\t:useAllItem=\"canSelectAll ? genGroupAll().name : false\"\n\t\t:addChanger\n\t\tuseCache\n\t\t@appendItem=\"addGroups\"\n\t>\n\t\t<template #item=\"{ item }\">\n\t\t\t<div class=\"top-groupSelector_groupItem\">\n\t\t\t\t<div\n\t\t\t\t\tclass=\"top-comment\"\n\t\t\t\t\tv-if=\"item.id === ITEM_ID_NEW\"\n\t\t\t\t>\n\t\t\t\t\t{{ $i18n.Common.Add }}:\n\t\t\t\t</div>\n\n\t\t\t\t<span>{{ item.name }}</span>\n\n\t\t\t\t<span v-if=\"item.folder_path && showPath\" class=\"top-groupSelector_groupItemFolderPath\">\n\t\t\t\t\t{{ item.folder_path }}\n\t\t\t\t</span>\n\t\t\t</div>\n\t\t</template>\n\t</Selector2>\n</template>\n\n<style>\n.top-groupSelector_groupItem {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: var(--top-padding-1);\n}\n\n.top-groupSelector_groupItemFolderPath {\n\twidth: 100%;\n\tcolor: var(--color-text-4);\n\tfont-size: 12px;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { type ModelRef } from 'vue';\nimport type { Emits, Props } from './types';\nimport type { Folder } from './folders/types';\nimport type { Group } from './groups/types';\nimport Folders from './folders/folders.vue';\nimport Groups from './groups/groups.vue';\nimport { folderDefault, genFlat, genRootFolder } from './folders/utils';\nimport { genGroupAll, genGroupPlaceholder, groupNone } from './groups/utils';\n\nconst props = withDefaults(defineProps<Props>(), {\n\ton: undefined,\n\tcanSelectAll: false,\n\tautoselect: 'first',\n\taddChanger: true,\n\tshowFolders: true,\n\tshowGroups: true,\n\taddIcon: true,\n});\n\nconst modelFolderId = defineModel<Props['folderId']>('folderId', { required: true });\nconst modelFolder = defineModel<Props['folder']>('folder');\n\nconst modelGroupId = defineModel<Props['groupId']>('groupId', { required: true });\nconst modelGroup = defineModel<Props['group']>('group', { default: groupNone }) as ModelRef<NonNullable<Props['group']>>;\n\nconst emit = defineEmits<Emits>();\n\n/**\n * Выбранная папка\n *\n * Спекулятивное предсказание выбранной папки\n *\n * Если папка не определена, она ставится в `undefined`, чтобы при последующем определении не фиксировались события смены папки, см. `groups.vue`\n */\nconst foldersFlat = genFlat(props.folders ?? { root: [folderDefault] }, props.canSelectAllGroups);\nmodelFolder.value = foldersFlat.get(modelFolderId.value);\n\n/**\n * Спекулятивное предсказание выбранной группы, чтобы не было лишней перерисовки\n *\n * Точная проверка происходит в `groups.vue`\n */\nlet groupInitial = props.groups?.find((group) => group.id === modelGroupId.value);\nif (!modelGroupId.value) groupInitial = props.groups?.[0];\nif (props.canSelectAllGroups && !groupInitial) {\n\tgroupInitial = genGroupAll();\n}\nif (props.canSelectAllGroups && !groupInitial) {\n\tgroupInitial = genGroupAll();\n}\nif (props.autoselect === 'placeholder' || !groupInitial) {\n\tgroupInitial = genGroupPlaceholder();\n}\ngroupInitial ??= groupNone;\n\nmodelGroup.value = groupInitial;\n\nconst onAddGroup = (group: Group) => {\n\t// если загрузка групп производится по api, то добавлять новые группы в `props.groups` не нужно\n\tlet groups: Group[] | undefined;\n\n\tif (props.groups && group) {\n\t\tgroups = [...props.groups, group];\n\t}\n\n\temit('update:groups', groups);\n};\n</script>\n\n<template>\n\t<div class=\"top-groupSelector\">\n\t\t<Folders\n\t\t\tv-if=\"showFolders\"\n\t\t\tv-model:folderId=\"modelFolderId\"\n\t\t\tv-model:folder=\"modelFolder\"\n\t\t\t:projectId\n\t\t\t:folders\n\t\t\t:canSelectAll=\"canSelectAllGroups\"\n\t\t\t:addChanger\n\t\t\t:addIcon\n\t\t\t:client\n\t\t/>\n\n\t\t<Groups\n\t\t\tv-if=\"showGroups\"\n\t\t\tv-model:groupId=\"modelGroupId\"\n\t\t\tv-model:group=\"modelGroup\"\n\t\t\t:projectId\n\t\t\t:folder=\"modelFolder\"\n\t\t\t:groups\n\t\t\t:on\n\t\t\t:canAdd=\"canAddGroup\"\n\t\t\t:canSelectAll=\"canSelectAllGroups\"\n\t\t\t:autoselect\n\t\t\t:addChanger\n\t\t\t:showPath=\"showFolders && !!folders?.[0]\"\n\t\t\t:addIcon\n\t\t\t:client\n\t\t\t@addGroup=\"onAddGroup\"\n\t\t/>\n\t</div>\n</template>\n\n<style>\n.top-groupSelector {\n\tdisplay: inline-flex;\n\tgap: var(--top-padding-1);\n}\n</style>\n","import type { Props as TagSelectorOpenerProps } from '../popupOpener/types';\nimport type { Tag, TagIdExclude } from '../types';\n\n/**\n * Полуичить значение исключения тега tagId\n */\nexport const genTagIdExcluded = (tagId: Tag['id']): TagIdExclude => {\n\treturn '-' + tagId as TagIdExclude;\n};\n\n/**\n * Вернуть чистый tagId без exclude флага\n */\nexport const genTagIdClear = (tagId: Tag['id'] | TagIdExclude) => {\n\tif (tagId[0] === '-') return tagId.substring(1) as Tag['id'];\n\n\treturn tagId as Tag['id'];\n};\n\n/**\n * Получить инфомрацию о теге по его id\n */\nexport const getTagById = (tagId: Tag['id'] | TagIdExclude, tags: TagSelectorOpenerProps['tags']) => {\n\ttagId = genTagIdClear(tagId);\n\n\tconst tag = tags.find(tag => tag.id === tagId);\n\tif (!tag) return;\n\n\treturn tag;\n};\n","import { isRef, type Ref, ref, unref, watch } from 'vue';\nimport { useI18n } from '@/core/plugins/i18n';\nimport { storage } from '@/core/utils/dom';\nimport TopPopupWorker from '@/components/popup/lib/worker';\nimport type { OpenerProps as PopupOpenerProps } from '@/components/popup/popup/opener/types';\nimport type { Props as TagSelectorOpenerProps, TagSelectorTarget } from '../popupOpener/types';\nimport type { Props as TagIconProps } from '../tagIcon/types';\nimport { genTagIdClear, getTagById } from './utils';\n\n/**\n * Сгенерировать элемент для открытия popup с выбором тегов\n *\n * Альтернатива TopTagSelectorPopupOpener для работы вне vue\n *\n * Требует монтирования компонента TopTagSelector\n */\nexport const genElPopupOpener = (\n\tprops: TagSelectorOpenerProps & { modelValue: TagSelectorOpenerProps['modelValue'] | Ref<TagSelectorOpenerProps['modelValue']> },\n\tpropsPopup?: Partial<PopupOpenerProps>,\n\thtmlSetterSeveral?: string,\n): HTMLElement => {\n\tif (!propsPopup) propsPopup = { id: props.id };\n\n\t// Значения по умолчанию см. в: `@/components/popup/popup/opener/opener.vue`\n\tpropsPopup.id = props.id;\n\tpropsPopup.pos ??= '3';\n\tpropsPopup.notch ??= true;\n\tpropsPopup.posBy ??= 'fixed';\n\n\tconst el = TopPopupWorker.genElPopupOpener('div', propsPopup as PopupOpenerProps);\n\n\tel.classList.add('top-tagSelector');\n\tif (props.useTopButton) el.classList.add('top-tagSelector-useTopButton', 'top-button', 'top-color_theme', 'top-as-selector');\n\tif (!props.useTopButton) el.classList.add('top-tagSelector-custom');\n\tif (props.mode === 'filter') el.classList.add('top-tagSelector-filter');\n\tif (props.mode === 'setter' && !props.filters) el.classList.add('top-tagSelector-setter_single');\n\tif (props.mode === 'setter' && props.filters) el.classList.add('top-tagSelector-setter_several');\n\n\t// прокcи клик: lazy connect\n\tel.onclick = (e) => {\n\t\te.preventDefault();\n\t\te.stopPropagation();\n\n\t\tel.onclick = null;\n\n\t\tconst model = ref(props.modelValue);\n\n\t\tconst target: TagSelectorTarget = {\n\t\t\tmodel,\n\t\t\tmode: props.mode,\n\t\t\ttargetId: props.targetId,\n\t\t\tfilters: props.filters,\n\t\t\tpayload: props.payload,\n\t\t};\n\n\t\tstorage(el, 'topTagSelectorTarget', target);\n\n\t\tdelete el.dataset.topPopupDisabled;\n\n\t\tif (!isRef(props.modelValue)) {\n\t\t\twatch(model, () => {\n\t\t\t\tprops.modelValue = model.value;\n\n\t\t\t\telPopupOpenerRender(el, props, htmlSetterSeveral);\n\t\t\t});\n\t\t}\n\n\t\t// выполнение клика\n\t\tel.click();\n\t};\n\n\tif (isRef(props.modelValue)) {\n\t\twatch(props.modelValue, () => elPopupOpenerRender(el, props, htmlSetterSeveral));\n\t} else {\n\t\tstorage(el, 'topTagSelectorRender', (modelValue: TagSelectorOpenerProps['modelValue']) => {\n\t\t\tprops.modelValue = modelValue;\n\n\t\t\tconst target = storage(el, 'topTagSelectorTarget');\n\t\t\tif (target) target.model.value = modelValue;\n\n\t\t\telPopupOpenerRender(el, props, htmlSetterSeveral);\n\t\t});\n\t}\n\n\telPopupOpenerRender(el, props, htmlSetterSeveral);\n\n\treturn el;\n};\n\n/**\n * Вызвать перерисовку элемента, который был создан через genElPopupOpener()\n *\n * Работет только для элементов не с реактивным modelValue\n */\nexport const renderElPopupOpener = (el: HTMLElement, modelValue: TagSelectorOpenerProps['modelValue']) => {\n\tstorage(el, 'topTagSelectorRender')?.(modelValue);\n};\n\n/**\n * Отрисовать TopTagSelecorPopupOpener\n */\nconst elPopupOpenerRender = (el, props: TagSelectorOpenerProps, htmlSetterSeveral?: string) => {\n\tconst tagsIds = unref(props.modelValue);\n\n\tel.classList.toggle('top-tagSelector-selectedOne', !tagsIds.length || tagsIds.length === 1);\n\tel.classList.toggle('top-tagSelector-toTwoLine', tagsIds.length > 5);\n\n\tif (props.mode === 'setter' && props.filters) {\n\t\tel.innerHTML = `<div>${htmlSetterSeveral}</div>`;\n\n\t\treturn;\n\t}\n\n\tel.innerHTML = '';\n\n\tif (!tagsIds.length && props.mode === 'filter') {\n\t\tconst elTagIcon = genElTagIcon({\n\t\t\tid: 'all',\n\t\t\tcolorId: '',\n\t\t\tname: useI18n().Common?.All_tags ?? '',\n\t\t\tstate: '',\n\t\t});\n\n\t\tel.append(elTagIcon);\n\t}\n\n\ttagsIds.forEach(tagId => {\n\t\tconst elTagIcon = genElTagIcon({\n\t\t\tid: genTagIdClear(tagId),\n\t\t\tcolorId: getTagById(tagId, props.tags)?.color_id ?? '',\n\t\t\tname: getTagById(tagId, props.tags)?.name ?? '',\n\t\t\tstate: genTagIdClear(tagId) === tagId ? 'selected' : 'excluded',\n\t\t});\n\n\t\tel.append(elTagIcon);\n\t});\n};\n\n/**\n * Сгенерировтаь элемент для открытия popup с выбором тегов\n *\n * Альтернатива TopTagSelecorTagIcon для работы вне vue\n */\nconst genElTagIcon = (props: TagIconProps) => {\n\tconst el = document.createElement('div');\n\n\tel.classList.add('top-tagSelector_tagIcon');\n\tel.classList.toggle('top-tagSelector-active', !!props.state);\n\tel.classList.toggle('top-tagSelector-excluded', props.state === 'excluded');\n\n\tel.dataset.tag_id = props.id;\n\tel.dataset.tag_color_id = props.colorId;\n\tel.title = props.name;\n\n\treturn el;\n};\n","import type { Tag } from './types';\n\nexport default [\n\t{\n\t\tid: '1',\n\t\tname: 'Without Tag',\n\t\tcolor_id: '1',\n\t},\n\t{\n\t\tid: '2',\n\t\tname: 'Red',\n\t\tcolor_id: '2',\n\t},\n\t{\n\t\tid: '3',\n\t\tname: 'Orange',\n\t\tcolor_id: '3',\n\t},\n\t{\n\t\tid: '4',\n\t\tname: 'Yellow',\n\t\tcolor_id: '4',\n\t},\n\t{\n\t\tid: '5',\n\t\tname: 'Blue',\n\t\tcolor_id: '5',\n\t},\n\t{\n\t\tid: '6',\n\t\tname: 'Purple',\n\t\tcolor_id: '6',\n\t},\n\t{\n\t\tid: '7',\n\t\tname: 'Green',\n\t\tcolor_id: '7',\n\t},\n\t{\n\t\tid: '8',\n\t\tname: 'Magenta',\n\t\tcolor_id: '8',\n\t},\n\t{\n\t\tid: '9',\n\t\tname: 'Dark blue',\n\t\tcolor_id: '9',\n\t},\n\t{\n\t\tid: '10',\n\t\tname: 'Turquoise',\n\t\tcolor_id: '10',\n\t},\n] as Tag[];\n","<script setup lang=\"ts\">\nimport type { Props } from './types';\n\ndefineProps<Props>();\n</script>\n\n<template>\n\t<div\n\t\t:class=\"{\n\t\t\t'top-tagSelector_tagIcon': true,\n\t\t\t'top-tagSelector-active': !!state,\n\t\t\t'top-tagSelector-excluded': state === 'excluded',\n\t\t}\"\n\t\t:data-tag_id=\"id\"\n\t\t:data-tag_color_id=\"colorId\"\n\t\t:title=\"name\"\n\t></div>\n</template>\n\n<style>\n[data-tag_id] { display: inline-flex; gap: var(--top-gap-2); align-items: center; }\n\n[data-tag_id=\"\"] { border-color: #bdc3c7 !important; color: #55555F !important; }\n[data-tag_color_id=\"1\"] { border-color: var(--color-tag-1) !important; color: #55555F !important; }\n[data-tag_color_id=\"2\"] { border-color: var(--color-tag-2) !important; color: var(--color-tag-2) !important; }\n[data-tag_color_id=\"3\"] { border-color: var(--color-tag-3) !important; color: var(--color-tag-3) !important; }\n[data-tag_color_id=\"4\"] { border-color: var(--color-tag-4) !important; color: var(--color-tag-4) !important; }\n[data-tag_color_id=\"5\"] { border-color: var(--color-tag-5) !important; color: var(--color-tag-5) !important; }\n[data-tag_color_id=\"6\"] { border-color: var(--color-tag-6) !important; color: var(--color-tag-6) !important; }\n[data-tag_color_id=\"7\"] { border-color: var(--color-tag-7) !important; color: var(--color-tag-7) !important; }\n[data-tag_color_id=\"8\"] { border-color: var(--color-tag-8) !important; color: var(--color-tag-8) !important; }\n[data-tag_color_id=\"9\"] { border-color: var(--color-tag-9) !important; color: var(--color-tag-9) !important; }\n[data-tag_color_id=\"10\"] { border-color: var(--color-tag-10) !important; color: var(--color-tag-10) !important; }\n\n[data-tag_id=\"\"].top-tagSelector-active { background: #bdc3c7 !important; }\n[data-tag_color_id=\"1\"].top-tagSelector-active { background: var(--color-tag-1) !important; }\n[data-tag_color_id=\"2\"].top-tagSelector-active { background: var(--color-tag-2) !important; }\n[data-tag_color_id=\"3\"].top-tagSelector-active { background: var(--color-tag-3) !important; }\n[data-tag_color_id=\"4\"].top-tagSelector-active { background: var(--color-tag-4) !important; }\n[data-tag_color_id=\"5\"].top-tagSelector-active { background: var(--color-tag-5) !important; }\n[data-tag_color_id=\"6\"].top-tagSelector-active { background: var(--color-tag-6) !important; }\n[data-tag_color_id=\"7\"].top-tagSelector-active { background: var(--color-tag-7) !important; }\n[data-tag_color_id=\"8\"].top-tagSelector-active { background: var(--color-tag-8) !important; }\n[data-tag_color_id=\"9\"].top-tagSelector-active { background: var(--color-tag-9) !important; }\n[data-tag_color_id=\"10\"].top-tagSelector-active { background: var(--color-tag-10) !important; }\n\n[data-tag_id=\"all\"] {\n\tborder-color: transparent !important; background: none !important;\n}\n\n[data-tag_id=\"all\"]:before {\n\tcontent: \"\";\n\tcolor: var(--color-tag-all) !important;\n\tfont-size: 26px;\n\tmin-width: calc(100% + 4px);\n\theight: calc(100% + 4px);\n\tmargin-top: -18px;\n\tmargin-left: -8px;\n}\n\n/* в кнопке выбора тэгов и в popup */\n.top-tagSelector-useTopButton [data-tag_id],\n.top-tagSelector_popup [data-tag_id] {\n\t--top-tag-selector-size: 10px;\n\n\tbox-sizing: content-box;\n\tborder-radius: 100%; border: 2px solid var(--color-layout-front-1); background: var(--color-layout-front-1);\n\twidth: var(--top-tag-selector-size);\n\theight: var(--top-tag-selector-size);\n\tfont-size: calc(var(--top-tag-selector-size) + 4px);\n\t/*font-family: inherit !important;*/\n}\n\n/* исключение тегов */\n.top-body-press_ctrl .top-tagSelector_tagListItem-canExclude:hover [data-tag_id],\n[data-tag_id].top-tagSelector-excluded { position: relative; }\n\n.top-body-press_ctrl .top-tagSelector_tagListItem-canExclude:hover [data-tag_id]:before,\n[data-tag_id].top-tagSelector-excluded:before {\n\tcontent: \"\";\n\twidth: calc(var(--top-tag-selector-size) * 2); height: 1px; background: inherit;\n\ttransform: rotate(-45deg); filter: brightness(80%);\n\tdisplay: block;\n\tposition: absolute; top: calc(var(--top-tag-selector-size) / 2); left: calc(0px - var(--top-tag-selector-size) / 2);\n}\n.top-body-press_ctrl .top-tagSelector_tagListItem-canExclude:hover [data-tag_id]:not([data-tag_id=\"all\"]):not(.top-tagSelector-active):before { background: #AAA; }\n[data-tag_id].top-tagSelector-excluded:after,\n[data-tag_id].top-tagSelector-excluded ~ .top-tagSelector_tagListItemName { text-decoration: line-through; }\n\n/* popup */\n.top-popup_listItem.top-tagSelector-active { background: var(--top-popup-background-color-active) !important; }\n\n.top-popup_listItem:hover [data-tag_id=\"all\"]:before { border-color: var(--top-popup-background-color-hover); }\n.top-popup_listItem.top-tagSelector-active [data-tag_id=\"all\"]:before,\n.top-popup_listItem.top-active [data-tag_id=\"all\"]:before { border-color: var(--top-popup-background-color-active); }\n</style>\n","<script setup lang=\"ts\">\nimport { computed, nextTick, ref } from 'vue';\nimport type { Emits, Props } from './types';\nimport TopTagIcon from '../tagIcon/tagIcon.vue';\nimport TopPopupListItem from '@/components/popup/popup/listItem.vue';\n\nconst props = defineProps<Props>();\nconst emit = defineEmits<Emits>();\n\nconst name = defineModel<Props['name']>('name', {\n\trequired: true,\n});\n\nconst elName = ref<HTMLElement | null>(null);\n\n/**\n * @todo Удалить через пол года после выхода версии firefox с поддержкой contenteditable plaintext-only, включая бета версии\n */\nconst firefoxProps = computed(() => {\n\tif (navigator.userAgent.indexOf('Firefox') != -1) {\n\n\t\treturn {\n\t\t\tcontenteditable: inEdit.value,\n\t\t\tonpaste: (event: Event) => event.preventDefault(),\n\t\t};\n\t}\n\n\treturn {};\n});\n\n/**\n * Включен режим редактирвоания\n */\nconst inEdit = ref(false);\n\n/**\n * Включить режим редактирование\n */\nconst turnToEdit = async () => {\n\tinEdit.value = true;\n\n\tawait nextTick();\n\n\telName.value?.focus();\n};\n\n/**\n * Применить редактирование\n */\nconst editCommit = () => {\n\tconst name = elName.value?.innerText;\n\tif (!name) return editCancel();\n\n\tif (elName.value) elName.value.innerText = name;\n\n\tinEdit.value = false;\n\n\temit('update:name', name);\n};\n\n/**\n * Отменить редактирование\n */\nconst editCancel = async () => {\n\tif (elName.value) elName.value.innerText = props.name;\n\n\tinEdit.value = false;\n};\n\n/**\n * Применить выбор\n */\nconst changeSelect = (e: MouseEvent) => {\n\t// в режиме редактирования выбор не применяется\n\tif (inEdit.value) return;\n\n\t// тег запрещено выбирать\n\tif (props.disabled) return;\n\n\tlet toState: Props['state'] = 'selected';\n\n\tif (props.canExclude) {\n\t\tif (e.ctrlKey || e.metaKey) {\n\t\t\ttoState = 'excluded';\n\t\t}\n\t}\n\n\tif (props.state == toState) {\n\t\ttoState = '';\n\t}\n\n\tif (toState === '') emit('unselect');\n\tif (toState === 'selected') emit('select');\n\tif (toState === 'excluded') emit('exclude');\n};\n</script>\n\n<template>\n\t<TopPopupListItem\n\t\t:class=\"{\n\t\t\t'top-tagSelector_tagListItem': true,\n\t\t\t'top-tagSelector_tagListItem-inEdit': inEdit,\n\t\t\t'top-tagSelector_tagListItem-disabled': disabled,\n\t\t\t'top-tagSelector_tagListItem-canExclude': canExclude,\n\t\t\t'top-tagSelector-active': !!state,\n\t\t\t'top-tagSelector-excluded': state === 'excluded'\n\t\t}\"\n\t\t@click.stop=\"changeSelect\"\n\t>\n\t\t<TopTagIcon\n\t\t\t:id\n\t\t\t:name\n\t\t\t:colorId\n\t\t\t:state\n\t\t/>\n\n\t\t<span\n\t\t\tref=\"elName\"\n\t\t\tclass=\"top-tagSelector_tagListItemName\"\n\t\t\t:contenteditable=\"inEdit ? 'plaintext-only' : false\"\n\t\t\t:=\"firefoxProps\"\n\t\t\t@keydown.enter.stop=\"editCommit\"\n\t\t\t@keydown.esc.stop=\"editCancel\"\n\t\t>\n\t\t\t{{ name }}\n\t\t</span>\n\n\t\t<template v-if=\"editable\">\n\t\t\t<span\n\t\t\t\tv-if=\"!inEdit\"\n\t\t\t\tdata-top-icon=\"\"\n\t\t\t\tclass=\"top-tagSelector_edit\"\n\t\t\t\t@click=\"turnToEdit\"\n\t\t\t></span>\n\n\t\t\t<span\n\t\t\t\tv-else\n\t\t\t\tdata-top-icon=\"\"\n\t\t\t\tclass=\"top-tagSelector_edit\"\n\t\t\t\t@click.stop=\"editCommit\"\n\t\t\t></span>\n\t\t</template>\n\t</TopPopupListItem>\n</template>\n\n<style>\n.top-tagSelector_tagListItem.top-popup_listItem {\n\tpadding-top: var(--top-padding-2) !important;\n\tpadding-bottom: var(--top-padding-2) !important;\n\tgap: 10px;\n}\n\n.top-tagSelector_tagListItemName {\n\tborder-radius: var(--top-radius-1);\n\twidth: 120px;\n\tpadding: 4px 2px;\n\tmargin: -4px -2px;\n\ttext-overflow: ellipsis;\n\toverflow: hidden;\n\tflex-grow: 1;\n}\n\n/* редактирование */\n.top-tagSelector_edit {\n\t--top-icon-size: 18px;\n\t--top-icon-color: var(--color-text-3);\n\n\twidth: 20px;\n\tflex-grow: 0;\n}\n\n.top-tagSelector_edit:before {\n\tline-height: 0;\n}\n\n.top-tagSelector_edit { opacity: 0; }\n.top-tagSelector_edit:hover {\n\t--top-icon-color: var(--color-text-primary);\n}\n\n.top-tagSelector_tagListItem:hover .top-tagSelector_edit,\n.top-tagSelector_tagListItem-inEdit .top-tagSelector_edit {\n\tcursor: pointer;\n\topacity: 1;\n}\n\n.top-tagSelector_tagListItem-inEdit {\n\tbackground: var(--color-layout-front-3) !important;\n\toutline: 1px dashed var(--color-line-2);\n\toutline-offset: -1px;\n}\n\n.top-tagSelector_tagListItem-inEdit .top-tagSelector_tagListItemName {\n\tcursor: text;\n\tbackground: var(--color-layout-front-1);\n}\n\n/* disabled */\n.top-tagSelector_tagListItem-disabled {\n\tcursor: not-allowed !important;\n\topacity: 0.85;\n}\n.top-tagSelector_tagListItem-disabled:hover {\n\tbackground: inherit;\n}\n.top-tagSelector_tagListItem-disabled [data-tag_id] {\n\topacity: 0.6;\n}\n</style>\n","top-forms-focusable top-size_s top-button-withoutText top-tagSelector-useTopButton\n\n<script setup lang=\"ts\">\nimport type { Props, TagSelectorTarget } from './types';\nimport TopPopupOpener from '@/components/popup/popup/opener/opener.vue';\nimport TopButton from '@/components/forms/button/button.vue';\nimport { genTagIdClear, getTagById } from '../utils/utils';\nimport TopTagIcon from '../tagIcon/tagIcon.vue';\n\ndefineOptions({\n\tinheritAttrs: false,\n});\n\nconst props = defineProps<Props>();\n\nconst model = defineModel<Props['modelValue']>({\n\trequired: true,\n});\n\nconst component = props.useTopButton ? TopButton : 'div';\nconst componentSlotName = props.useTopButton ? 'html' : 'default';\n\nconst topTagSelectorTarget: TagSelectorTarget = {\n\tmodel,\n\tmode: props.mode,\n\ttargetId: props.targetId,\n\tfilters: props.filters,\n\tpayload: props.payload,\n};\n</script>\n\n<template>\n\t<TopPopupOpener :id>\n\t\t<component\n\t\t\t:is=\"component\"\n\t\t\t:class=\"{\n\t\t\t\t'top-tagSelector': true,\n\t\t\t\t'top-tagSelector-useTopButton': props.useTopButton,\n\t\t\t\t'top-tagSelector-custom': !props.useTopButton,\n\t\t\t\t'top-as-selector': props.useTopButton,\n\t\t\t\t'top-tagSelector-filter': props.mode === 'filter',\n\t\t\t\t'top-tagSelector-setter_single': props.mode === 'setter' && !filters,\n\t\t\t\t'top-tagSelector-setter_several': props.mode === 'setter' && filters,\n\t\t\t\t'top-tagSelector-selectedOne': !model.length || model.length === 1,\n\t\t\t\t'top-tagSelector-toTwoLine': model.length > 5,\n\t\t\t}\"\n\t\t\tcolor=\"theme\"\n\t\t\t:styling\n\t\t\tv-top-data:topTagSelectorTarget=\"topTagSelectorTarget\"\n\t\t\t:=$attrs\n\t\t>\n\t\t\t<template #[componentSlotName]>\n\t\t\t\t<TopTagIcon\n\t\t\t\t\tv-if=\"!model.length && mode === 'filter'\"\n\t\t\t\t\tid=\"all\"\n\t\t\t\t\tcolorId=\"\"\n\t\t\t\t\t:name=\"$i18n.Common.All_tags ?? ''\"\n\t\t\t\t\tstate=\"\"\n\t\t\t\t/>\n\n\t\t\t\t<!-- Массовое редактирование -->\n\t\t\t\t<div v-if=\"mode === 'setter' && filters\">\n\t\t\t\t\t<slot></slot>\n\t\t\t\t</div>\n\n\t\t\t\t<!-- Список тегов -->\n\t\t\t\t<TopTagIcon\n\t\t\t\t\tv-else\n\t\t\t\t\tv-for=\"tagId in model\"\n\t\t\t\t\t:id=\"genTagIdClear(tagId)\"\n\t\t\t\t\t:colorId=\"getTagById(tagId, tags)?.color_id ?? ''\"\n\t\t\t\t\t:name=\"getTagById(tagId, tags)?.name ?? ''\"\n\t\t\t\t\t:state=\"genTagIdClear(tagId) === tagId ? 'selected' : 'excluded'\"\n\t\t\t\t/>\n\t\t\t</template>\n\t\t</component>\n\t</TopPopupOpener>\n</template>\n\n<style>\n.top-tagSelector {\n\tcursor: pointer;\n\tmin-width: 0;\n\twhite-space: nowrap;\n\tjustify-content: left;\n}\n\n.top-tagSelector-useTopButton {\n\twidth: 112px;\n\tgap: var(--top-gap-1);\n\tvertical-align: middle;\n}\n\n.top-tagSelector.top-button.top-as-selector {\n\twidth: calc(112px + 8px);\n}\n\n/* все теги в фильтре */\n.top-tagSelector-useTopButton [data-tag_id=\"all\"]:before {\n\tmargin-right: 6px;\n}\n\n/* фильтр по одному тегу */\n.top-tagSelector-useTopButton.top-tagSelector-selectedOne [data-tag_id]:after {\n\tcontent: attr(title);\n\tmargin-left: 20px;\n\tmin-width: 74px;\n\ttext-align: left;\n\tline-height: 1.2;\n\ttext-overflow: ellipsis;\n\toverflow: hidden;\n}\n\n.top-tagSelector-useTopButton.top-tagSelector-selectedOne [data-tag_id=\"all\"]:after {\n\tmargin-left: 0;\n}\n\n/* фильтр по многим тегам */\n.top-button.top-tagSelector-useTopButton.top-tagSelector-toTwoLine {\n\tflex-wrap: wrap;\n\talign-content: center;\n}\n\n.top-button.top-tagSelector-useTopButton.top-tagSelector-toTwoLine [data-tag_id] {\n\t--top-tag-selector-size: 8px;\n}\n\n.top-button.top-tagSelector-useTopButton.top-tagSelector-toTwoLine [data-tag_id]:nth-child(5) {\n\tmargin-right: 5px;\n}\n\n/* установка тегов */\n.top-tagSelector-useTopButton.top-tagSelector-setter_several,\n.top-tagSelector-useTopButton.top-tagSelector-setter_several.top-as-selector{\n\twidth: auto;\n}\n\n.top-tagSelector-custom {\n\tposition: relative;\n\tdisplay: flex !important; flex-direction: column; flex-wrap: wrap; align-items: stretch !important;\n}\n\n.top-tagSelector-custom [data-tag_id] {\n\twidth: auto; min-height: 20%; min-width: 50%;\n\tflex: 1 1 auto;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, reactive, ref, shallowRef, watch } from 'vue';\n\nimport Core from '@/core/core/core';\nimport { useI18n } from '@/core/plugins/i18n';\nimport { storage } from '@/core/utils/dom';\nimport { debounce } from '@/core/utils/lodash';\n\nimport type { Option } from '@/components/forms/select/types';\nimport TopSelect from '@/components/forms/select/select.vue';\n\nimport TopButton from '@/components/forms/button/button.vue';\n\nimport type { PopupEvent } from '@/components/popup/popup/types';\nimport TopPopup from '../../popup/popup/popup.vue';\nimport TopPopupListItem from '../../popup/popup/listItem.vue';\n\nimport type { Emits, FiltersAction, Props, Tag } from './types';\nimport tagsDefaults from './tagsDefaults';\nimport { genTagIdExcluded } from './utils/utils';\n\nimport type { Props as TagListItemProps } from './popupListItem/types';\nimport TopTagListItem from './popupListItem/tagPopupListItem.vue';\n\nimport type { TagSelectorTarget } from './popupOpener/types';\nimport TopTagSelectorPopupOpener from './popupOpener/popupOpener.vue';\n\nconst i18n = useI18n();\n\nconst props = withDefaults(defineProps<Props>(), {\n\ttagsMax: 10,\n\trequiredForSetter: true,\n\temitDelay: 500,\n\tuseTopButton: true,\n});\n\n/**\n * Выбранные теги кнопки в этом компоненте\n *\n * Редактирвоание ведется только через `editTarget.model`\n */\nconst model = defineModel<Props['modelValue']>({\n\trequired: true,\n});\n\n/**\n * Список тегов\n */\nconst tags = defineModel<Tag[]>('tags', {\n\tdefault: reactive(tagsDefaults),\n});\n\nconst emit = defineEmits<Emits>();\n\nconst emitDebounce: typeof emit = debounce((name, e) => {\n\temit(name, e);\n}, props.emitDelay);\n\n// требуется сразу указать один тег\nif (props.singleMode && !model.value.length) {\n\tmodel.value = [tags.value[0].id];\n}\n\nconst id = props.id ?? 'top-popup-id-' + Math.random() + '';\n\nconst filtersAction = ref<FiltersAction>('add');\n\n/**\n * Сгенерировать опции для выбора дейсвтия массого редактирования\n */\nconst genFiltersActionOptions = () => {\n\tconst tagsText = ' ' + i18n.Common.Tags?.toLowerCase();\n\n\tconst res = new Map<FiltersAction, Option>();\n\tres.set('add', { value: 'add', title: i18n.Common.Add + tagsText });\n\tres.set('replace', { value: 'replace', title: i18n.Common.Replace + tagsText });\n\tres.set('delete', { value: 'delete', title: i18n.Common.Delete + tagsText });\n\n\treturn res;\n};\n\n/**\n * Целевой объект редактирования тегов\n *\n * Определяется кнопкой, которая открыла popup выбора тегов\n */\nlet target = shallowRef<TagSelectorTarget>({\n\tmodel,\n\tmode: 'filter',\n\ttargetId: undefined,\n\tfilters: undefined,\n\tpayload: undefined,\n});\n\nwatch(model, () => {\n\temitDebounce('selector', model.value);\n});\n\n/**\n * Можно ли выбрать еще теги\n */\nconst disabled = computed(() => {\n\tif (target.value.mode !== 'setter') return;\n\n\tif (!props.maxTagsForSetter) return;\n\n\t// массовая смена тегов, количество тегов в результате узнать невозможно\n\tif (target.value.filters) return;\n\n\treturn target.value.model.value.length >= props.maxTagsForSetter;\n});\n\nconst genTargetTagState = (tagId: Tag['id'] | 'all'): TagListItemProps['state'] => {\n\tif (tagId !== 'all') {\n\t\tif (target.value.model.value.includes(tagId)) return 'selected';\n\n\t\tif (target.value.model.value.includes(genTagIdExcluded(tagId))) return 'excluded';\n\t}\n\n\t// Все теги, ни один из тегов не выбран\n\tif (tagId === 'all' && !target.value.model.value.length) return 'selected';\n\n\treturn '';\n};\n\nconst updateTargetModel = (tagId: Tag['id'], action: 'unselect' | 'select' | 'exclude') => {\n\tconst tagIdExcluded = genTagIdExcluded(tagId);\n\n\tlet tagsIds = target.value.model.value.filter(modelTagId => modelTagId !== tagId && modelTagId !== tagIdExcluded);\n\n\tif (action === 'select') tagsIds.push(tagId);\n\tif (action === 'exclude') tagsIds.push(tagIdExcluded);\n\n\t// режим редактирвоания тегов одного объекта с требованием указать хотя бы один тег\n\tif (target.value.mode === 'setter' && target.value.targetId !== undefined && props.requiredForSetter) {\n\t\t// нельзя снимать выделение со всех тегов\n\t\tif (!tagsIds.length) {\n\t\t\ttagsIds.push('1');\n\t\t}\n\n\t\t// при первом выборе сразу снять тег \"Без тега\"\n\t\tif (tagsIds.length === 2 && target.value.model.value.length === 1 && target.value.model.value[0] === '1') {\n\t\t\ttagsIds = tagsIds.filter((tagId) => tagId !== '1');\n\t\t}\n\t}\n\n\t// режим выбора одного тега\n\tif (props.singleMode && !target.value.filters) {\n\t\t// нужно указать хотя бы один тег\n\t\tif (!tagsIds.length) {\n\t\t\ttagsIds = target.value.model.value;\n\t\t}\n\n\t\tif (tagsIds.length > 1) {\n\t\t\ttagsIds = [tagsIds[tagsIds.length - 1]];\n\t\t}\n\t}\n\n\t// всегда выводить теги в порядке, указанном в настройках tags\n\ttagsIds.sort((a, b) => {\n\t\tif (!props.tags) return 0;\n\n\t\tconst aIndex = props.tags.findIndex((tag) => tag.id === a);\n\t\tconst bIndex = props.tags.findIndex((tag) => tag.id === b);\n\n\t\treturn aIndex - bIndex;\n\t});\n\n\ttarget.value.model.value = tagsIds;\n\n\tif (target.value.mode === 'setter' && target.value.targetId !== undefined) {\n\t\temitDebounce('setter', {\n\t\t\ttagsIds: tagsIds as Tag['id'][],\n\t\t\ttargetId: target.value.targetId,\n\t\t\tpayload: target.value.payload,\n\t\t});\n\t}\n};\n\nconst classString = computed(() => {\n\tlet res = 'top-tagSelector_popup';\n\n\tif (target.value.mode === 'filter') res += ' top-tagSelector_popup-filter';\n\tif (target.value.mode === 'setter') res += ' top-tagSelector_popup-setter';\n\n\treturn res;\n});\n\n/**\n * Добавить тег\n */\nconst addTag = () => {\n\tconst tagName = prompt('', 'New tag');\n\tif (!tagName || tagName === 'New tag') return;\n\n\tconst tagId = tags.value.length + 1;\n\n\ttags.value.push({\n\t\tid: String(tagId) as Tag['id'],\n\t\tname: tagName,\n\t\tcolor_id: String((tagId - 1) % 10 + 1) as Tag['color_id'],\n\t});\n\n\temit('tagsChanged', tags.value);\n};\n\nconst onOpen = (popupEvent: PopupEvent) => {\n\t// popup открыт другой кнопкой c другим modelValue, для редактирвоания объектов\n\ttarget.value = storage(popupEvent.elPopupOpener, 'topTagSelectorTarget');\n\tif (!target.value) throw new Error('Open popup TopTagSelector required v-data:topTagSelectorTarget');\n\n\t// при начале массовой установки тегов сбросить состояние формы\n\tif (target.value.filters) {\n\t\tfiltersAction.value = 'add';\n\t\ttarget.value.model.value = [];\n\t}\n\n\tif (!Core.$?.ui['sortable']) {\n\t\tconsole.info('Для работы сортировки требуется глобальная загрузка jQuery UI Sortable');\n\n\t\treturn;\n\t}\n\n\tif (!Core.state.isMobile && !Core.state.isMobileUA && tags.value) {\n\t\t$(popupEvent.elPopup).sortable({\n\t\t\titems: 'li:has([data-tag_id]:not([data-tag_id=\"all\"]))',\n\n\t\t\t/**\n\t\t\t * @todo Удалить `[contenteditable=\"true\"]` через пол года после выхода версии firefox с поддержкой contenteditable plaintext-only, включая бета версии\n\t\t\t */\n\t\t\tcancel: '[contenteditable=\"plaintext-only\"], [contenteditable=\"true\"]',\n\n\t\t\tdistance: 10,\n\t\t\tstop: function (_e, ui) {\n\t\t\t\tif (!tags.value) return;\n\n\t\t\t\tconst $tags = $(ui.item).parent().find('[data-tag_id]');\n\n\t\t\t\tconst tagsSorted: Tag['id'][] = [];\n\t\t\t\t$tags.each((_index, elTag) => {\n\t\t\t\t\tif (!tags.value) return;\n\n\t\t\t\t\tconst tagId = $(elTag).attr('data-tag_id') as Tag['id'];\n\t\t\t\t\ttagsSorted.push(tagId);\n\t\t\t\t});\n\n\t\t\t\ttags.value.sort((tagIdA, tagIdB) => {\n\t\t\t\t\tconst a = tagsSorted.findIndex(tagSorted => tagSorted === tagIdA.id);\n\t\t\t\t\tconst b = tagsSorted.findIndex(tagSorted => tagSorted === tagIdB.id);\n\n\t\t\t\t\treturn a - b;\n\t\t\t\t});\n\n\t\t\t\temitDebounce('tagsChanged', tags.value);\n\t\t\t},\n\t\t});\n\t}\n};\n\nconst onClose = (popupEvent: PopupEvent) => {\n\tif (!Core.$?.ui['sortable']) return;\n\n\tif ($(popupEvent.elPopup).data('ui-sortable')) {\n\t\t$(popupEvent.elPopup).sortable('destroy');\n\t}\n};\n</script>\n\n<template>\n\t<TopTagSelectorPopupOpener\n\t\tv-model=\"model\"\n\t\t:id\n\t\t:tags\n\t\t:styling\n\t\tmode=\"filter\"\n\t\t:useTopButton\n\t/>\n\n\t<TopPopup\n\t\t:id\n\t\t:class=\"classString\"\n\t\t@open=\"onOpen($event)\"\n\t\t@close=\"onClose($event)\"\n\t\t:transition-duration=\"50\"\n\t>\n\t\t<!-- Массовое редактирование-->\n\t\t<template #header v-if=\"target.mode === 'setter' && target.filters\">\n\t\t\t<TopSelect\n\t\t\t\tv-model=\"filtersAction\"\n\t\t\t\t:options=\"genFiltersActionOptions()\"\n\t\t\t/>\n\t\t</template>\n\n\t\t<template #footer v-if=\"target.mode === 'setter' && target.filters\">\n\t\t\t<TopButton color=\"theme\">\n\t\t\t\t{{ $i18n.Common.Cancel }}\n\t\t\t</TopButton>\n\n\t\t\t<TopButton\n\t\t\t\t@click=\"emitDebounce('setter', {\n\t\t\t\t\ttagsIds: target.model.value as Tag['id'][],\n\t\t\t\t\tfilters: target.filters,\n\t\t\t\t\tfiltersAction,\n\t\t\t\t\tpayload: target.payload,\n\t\t\t\t})\"\n\t\t\t>\n\t\t\t\t{{ filtersAction === 'add' ? $i18n.Common['Add'] : '' }}\n\t\t\t\t{{ filtersAction === 'replace' ? $i18n.Common['Replace'] : '' }}\n\t\t\t\t{{ filtersAction === 'delete' ? $i18n.Common['Delete'] : '' }}\n\t\t\t</TopButton>\n\t\t</template>\n\n\t\t<template #contentList>\n\t\t\t<TopTagListItem\n\t\t\t\tv-if=\"target.mode === 'filter' && !singleMode\"\n\t\t\t\tid=\"all\"\n\t\t\t\tcolorId=\"\"\n\t\t\t\t:name=\"$i18n.Common.All_tags ?? ''\"\n\t\t\t\t:state=\"target.model.value.length ? '' : 'selected'\"\n\t\t\t\t@select=\"target.model.value = []\"\n\t\t\t/>\n\n\t\t\t<TopTagListItem\n\t\t\t\tv-for=\"tag in tags\"\n\t\t\t\t:key=\"tag.id\"\n\t\t\t\t:id=\"tag.id\"\n\t\t\t\t:colorId=\"tag.color_id\"\n\t\t\t\t:name=\"tag.name\"\n\t\t\t\t:state=\"genTargetTagState(tag.id)\"\n\t\t\t\t:canExclude=\"target.mode === 'filter' && !singleMode\"\n\t\t\t\t:editable=\"tagsEditable\"\n\t\t\t\t:disabled=\"disabled && genTargetTagState(tag.id) === ''\"\n\t\t\t\t@unselect=\"updateTargetModel(tag.id, 'unselect')\"\n\t\t\t\t@select=\"updateTargetModel(tag.id, 'select')\"\n\t\t\t\t@exclude=\"updateTargetModel(tag.id, 'exclude')\"\n\t\t\t\t@update:name=\"tag.name = $event as string, emitDebounce('tagsChanged', tags);\"\n\t\t\t/>\n\n\t\t\t<TopPopupListItem\n\t\t\t\tv-if=\"tagsEditable && tags.length < tagsMax && tags.length < 20\"\n\t\t\t\tdata-top-icon=\"\"\n\t\t\t\t@click.stop=\"addTag\"\n\t\t\t>\n\t\t\t\t{{ $i18n.Common.Add }}\n\t\t\t</TopPopupListItem>\n\t\t</template>\n\t</TopPopup>\n</template>\n\n<style>\n.top-tagSelector_popup .top-popup {\n\tuser-select: none;\n\tmin-width: 220px !important;\n\ttext-align: left;\n}\n\n.top-tagSelector_popup .top-popup_header .top-select{\n\tflex-grow: 1;\n}\n</style>\n"],"names":["selectAllItem","vue","props","forms","__props","item","model","_hoisted_2$1","useItemsFromCompetitors","competitors","projectId","competitor","searchersNames","regionUndefined","dummyIndex","searcherUndefined","regionAuto","searcherAuto","regionGlobal","getRegionAuto","genSearcherByKey","forFrequency","autoRegion","searchers","searcherByKey","genSearchersMapVolume","genSearchersMapCommon","onlyEnabledRegions","forceSearchersKeys","searcher","searcherI","region","regionI","searcherKey","serarcherByKey","getRegionGlobal","findRegion","searchRegion","findedRegion","dialogRegionSelector","utils","useSelectSearcher","mapSearchers","i18n","optionBySearcherKey","res","option","utils_searchers","useSelectRegion","activeSearcherIndexed","regionIndex","optionByRegionIndex","options","regionLabel","langLabel","optionByRegionIndex2","newRegionIndex","regionName","oldOptionByRegionIndex","index","title","regionNameCompare","regexpDevice","regionMatchLevelI","regionMatchLevel","regionsIndexes","regionsIndexesSaved","compareSave","compareLoad","useSelectorRegion","selectSearcher","allRegionsIndexes","globalRegionIndex","compare","newSearcherKey","selectRegion","regionsNewSearcher","regionsCurrentSearcher","searhcerCompareKey","regionKey","getSearcherKey","getSearcher","getRegionIndex","regions","modelSingle","defaultKey","newModel","defaultIndex","all","newSearherKey","__expose","_ctx","folderDefault","parentFolder","folder","genRootFolderName","useSelectAll","prefix","depth","genFlat","folderById","resultFolders","genApiGetFolders","client","useWatch","sourcesObj","cb","sources","keys","k","val","oldValues","i","changed","onCleanup","apiGet","refSelector","foldersFlat","modelFolder","folderSelected","modelFolderId","args","policy_vue_vue_type_style_index_0_lang","groupNone","groupAll","groups","on","group","folderForFilter","genApiGetGroups","genApiAddGroup","apiAdd","groupsBySelectedFolder","filterByFolder","field","setGroup","modelGroup","modelGroupId","changes","genItems","genGroupAll","groupSelected","firstGroup","api","addGroups","modelGroupOld","emits","_hoisted_1$4","_hoisted_2","_hoisted_3","groupInitial","onAddGroup","emit","genTagIdExcluded","tagId","genTagIdClear","getTagById","tags","tag","tag2","genElPopupOpener","propsPopup","htmlSetterSeveral","el","e","utils_dom","target","elPopupOpenerRender","modelValue","renderElPopupOpener","tagsIds","elTagIcon","genElTagIcon","tagsDefaults","_hoisted_1$2","elName","firefoxProps","inEdit","event","name2","editCancel","changeSelect","toState","name","editCommit","turnToEdit","component","_sfc_main$3","id","filtersAction","tagsText","emitDebounce","disabled","genTargetTagState","updateTargetModel","action","tagIdExcluded","tagId2","aIndex","a","bIndex","b","classString","tagName","onOpen","popupEvent","_e","ui","$tags","tagsSorted","tagSorted","tagIdA","tagIdB","onClose","_cache","$event"],"mappings":"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"}
|