@evergis/react 4.0.159 → 4.0.162
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/Dashboard/componentTypes.d.ts +6 -6
- package/dist/components/Dashboard/constants.d.ts +25 -3
- package/dist/components/Dashboard/containers/FiltersContainer/utils/getFilterWidth.d.ts +2 -3
- package/dist/components/Dashboard/elements/ElementControl/hooks/useFirstNonEmptyValue.d.ts +6 -0
- package/dist/components/Dashboard/elements/ElementModal/hooks/useModalContent.d.ts +2 -2
- package/dist/components/Dashboard/elements/ElementTable/utils/tableBox.d.ts +2 -2
- package/dist/components/Dashboard/hooks/index.d.ts +2 -0
- package/dist/components/Dashboard/hooks/useConfigStringSources.d.ts +8 -0
- package/dist/components/Dashboard/hooks/useDashboardFilterStore.d.ts +9 -0
- package/dist/components/Dashboard/hooks/useEqualTileWidth.d.ts +1 -1
- package/dist/components/Dashboard/hooks/useWidgetPage.d.ts +3 -0
- package/dist/components/Dashboard/hooks/useWrapperSize.d.ts +3 -3
- package/dist/components/Dashboard/styled/containers.d.ts +5 -6
- package/dist/components/Dashboard/styled/index.d.ts +2 -3
- package/dist/components/Dashboard/types.d.ts +33 -2
- package/dist/components/Dashboard/utils/applyQueryFilters.d.ts +4 -3
- package/dist/components/Dashboard/utils/chartAxis.d.ts +1 -1
- package/dist/components/Dashboard/utils/configString/formatConditionString.d.ts +18 -0
- package/dist/components/Dashboard/utils/configString/formatters.d.ts +16 -0
- package/dist/components/Dashboard/utils/configString/getFilterTokenValue.d.ts +13 -0
- package/dist/components/Dashboard/utils/configString/index.d.ts +10 -0
- package/dist/components/Dashboard/utils/configString/isDashboardReference.d.ts +2 -0
- package/dist/components/Dashboard/utils/configString/referencesFilters.d.ts +11 -0
- package/dist/components/Dashboard/utils/configString/resolveConfigToken.d.ts +3 -0
- package/dist/components/Dashboard/utils/configString/resolveElementStrings.d.ts +8 -0
- package/dist/components/Dashboard/utils/configString/resolveFilterDefaults.d.ts +8 -0
- package/dist/components/Dashboard/utils/configString/tokenizeConfigString.d.ts +5 -0
- package/dist/components/Dashboard/utils/configString/types.d.ts +78 -0
- package/dist/components/Dashboard/utils/formatDataSourceCondition.d.ts +16 -23
- package/dist/components/Dashboard/utils/getChangedFilterNames.d.ts +8 -0
- package/dist/components/Dashboard/utils/getPageFilters.d.ts +3 -0
- package/dist/components/Dashboard/utils/getRenderElement.d.ts +1 -1
- package/dist/components/Dashboard/utils/index.d.ts +4 -0
- package/dist/components/Dashboard/utils/selectUpdatingDataSources.d.ts +21 -0
- package/dist/index.js +900 -559
- package/dist/index.js.map +1 -1
- package/dist/react.esm.js +879 -560
- package/dist/react.esm.js.map +1 -1
- package/package.json +11 -4
package/dist/react.esm.js
CHANGED
|
@@ -4,7 +4,7 @@ import { jsx, jsxs, Fragment as Fragment$1 } from 'react/jsx-runtime';
|
|
|
4
4
|
import { isValidElement, Fragment, createContext, memo, useRef, useState, useCallback, useEffect, useContext, useMemo, createElement, useLayoutEffect, forwardRef } from 'react';
|
|
5
5
|
import { barChartClassNames, lineChartClassNames, BarChart as BarChart$1, LineChart, PieChart } from '@evergis/charts';
|
|
6
6
|
import { AttributeType, AttributeIconType, generateId, STORAGE_TOKEN_KEY, parseJwt, STORAGE_REFRESH_TOKEN_KEY, RemoteTaskStatus, AttributeConfigurationType, LayerServiceType, OgcGeometryType, StringSubType } from '@evergis/api';
|
|
7
|
-
import { isNil, isEqual, uniqueId, uniqBy, isEmpty, unescape } from 'lodash';
|
|
7
|
+
import { isNil, isUndefined, isEqual, uniqueId, uniqBy, isEmpty, unescape } from 'lodash';
|
|
8
8
|
import { ColorScale, Color as Color$1 } from '@evergis/color';
|
|
9
9
|
import { isValid, format, parseJSON, parseISO, toDate } from 'date-fns';
|
|
10
10
|
import { ru, enUS } from 'date-fns/locale';
|
|
@@ -12,9 +12,9 @@ import { stringify } from 'wkt';
|
|
|
12
12
|
import { toMercator, bbox } from '@turf/turf';
|
|
13
13
|
import { v4 } from 'uuid';
|
|
14
14
|
import { HubConnectionBuilder, HttpTransportType, LogLevel } from '@microsoft/signalr';
|
|
15
|
+
import { returnFound } from 'find-and';
|
|
15
16
|
import MapboxDraw from '@mapbox/mapbox-gl-draw';
|
|
16
17
|
import { Swiper, SwiperSlide } from 'swiper/react';
|
|
17
|
-
import { returnFound } from 'find-and';
|
|
18
18
|
import ReactMarkdown from 'react-markdown';
|
|
19
19
|
import rehypeRaw from 'rehype-raw';
|
|
20
20
|
import rehypeSanitize, { defaultSchema } from 'rehype-sanitize';
|
|
@@ -2102,7 +2102,7 @@ function tweenValue(transition, name, value) {
|
|
|
2102
2102
|
};
|
|
2103
2103
|
}
|
|
2104
2104
|
|
|
2105
|
-
function interpolate(a, b) {
|
|
2105
|
+
function interpolate$1(a, b) {
|
|
2106
2106
|
var c;
|
|
2107
2107
|
return (typeof b === "number" ? interpolateNumber
|
|
2108
2108
|
: b instanceof color ? interpolateRgb
|
|
@@ -2177,7 +2177,7 @@ function attrFunctionNS(fullname, interpolate, value) {
|
|
|
2177
2177
|
}
|
|
2178
2178
|
|
|
2179
2179
|
function transition_attr(name, value) {
|
|
2180
|
-
var fullname = namespace(name), i = fullname === "transform" ? interpolateTransformSvg : interpolate;
|
|
2180
|
+
var fullname = namespace(name), i = fullname === "transform" ? interpolateTransformSvg : interpolate$1;
|
|
2181
2181
|
return this.attrTween(name, typeof value === "function"
|
|
2182
2182
|
? (fullname.local ? attrFunctionNS : attrFunction)(fullname, i, tweenValue(this, "attr." + name, value))
|
|
2183
2183
|
: value == null ? (fullname.local ? attrRemoveNS : attrRemove)(fullname)
|
|
@@ -2485,7 +2485,7 @@ function styleMaybeRemove(id, name) {
|
|
|
2485
2485
|
}
|
|
2486
2486
|
|
|
2487
2487
|
function transition_style(name, value, priority) {
|
|
2488
|
-
var i = (name += "") === "transform" ? interpolateTransformCss : interpolate;
|
|
2488
|
+
var i = (name += "") === "transform" ? interpolateTransformCss : interpolate$1;
|
|
2489
2489
|
return value == null ? this
|
|
2490
2490
|
.styleTween(name, styleNull(name, i))
|
|
2491
2491
|
.on("end.style." + name, styleRemove(name))
|
|
@@ -3426,9 +3426,42 @@ const PROJECT_PROPS = [PROJECT_ALIAS_PROP, PROJECT_NAME_PROP, ""];
|
|
|
3426
3426
|
const PROVIDER_PREFIX = "$";
|
|
3427
3427
|
var ProviderPrefix;
|
|
3428
3428
|
(function (ProviderPrefix) {
|
|
3429
|
+
/** `$card:<слой>:<поле>` — атрибут объекта карточки. */
|
|
3429
3430
|
ProviderPrefix["Card"] = "card";
|
|
3430
|
-
|
|
3431
|
+
/**
|
|
3432
|
+
* Ближайший дашборд, в ряд к `%` своего виджета: `$dashboard:<фильтр>[.свойство]` — его фильтр,
|
|
3433
|
+
* `$dashboard:<датасорс>:<поле>` — поле первой строки его датасорса.
|
|
3434
|
+
*/
|
|
3435
|
+
ProviderPrefix["Dashboard"] = "dashboard";
|
|
3431
3436
|
})(ProviderPrefix || (ProviderPrefix = {}));
|
|
3437
|
+
/**
|
|
3438
|
+
* Имя слоя или датасорса в подстановке: буквы, цифры, `_`, `.` и `-`. Более широкий класс захватил бы
|
|
3439
|
+
* соседний токен: `$dashboard:a.min–$dashboard:a.max` читался бы как датасорс `a.min–$dashboard`.
|
|
3440
|
+
*/
|
|
3441
|
+
const CONFIG_SOURCE_NAME = "[\\w.-]+";
|
|
3442
|
+
/**
|
|
3443
|
+
* Все подстановки строки конфига одной грамматикой, по группам:
|
|
3444
|
+
* 1–2 `$dashboard:<датасорс>:<поле>`, 3–4 `$dashboard:<фильтр>[.свойство]`, 5–6 `$card:<слой>:<поле>`,
|
|
3445
|
+
* 7–8 `%<фильтр>[.свойство]`, 9 `{<атрибут>}`. Форма датасорса проверяется раньше формы фильтра:
|
|
3446
|
+
* их различает второе двоеточие. Имя фильтра — `\w+` целиком, поэтому `%name` не цепляет начало
|
|
3447
|
+
* `%name2`, а точка в конце предложения («за %year.») не считается свойством.
|
|
3448
|
+
*/
|
|
3449
|
+
const CONFIG_TOKEN_REGEXP = new RegExp([
|
|
3450
|
+
`\\${PROVIDER_PREFIX}${ProviderPrefix.Dashboard}:(${CONFIG_SOURCE_NAME}):(\\w+)`,
|
|
3451
|
+
`\\${PROVIDER_PREFIX}${ProviderPrefix.Dashboard}:(\\w+)(?:\\.(\\w+))?`,
|
|
3452
|
+
`\\${PROVIDER_PREFIX}${ProviderPrefix.Card}:(${CONFIG_SOURCE_NAME}):(\\w+)`,
|
|
3453
|
+
`${FILTER_PREFIX}(\\w+)(?:\\.(\\w+))?`,
|
|
3454
|
+
"\\{([^{}\\s]+)\\}",
|
|
3455
|
+
].join("|"), "g");
|
|
3456
|
+
/** Свойства границ диапазона: `%name.min` / `%name.max`. */
|
|
3457
|
+
const RANGE_PROPS = ["min", "max"];
|
|
3458
|
+
/** Разделитель элементов массива, подставленного в текст UI. */
|
|
3459
|
+
const CONFIG_TEXT_ARRAY_SEPARATOR = ", ";
|
|
3460
|
+
/**
|
|
3461
|
+
* Подписи в `options` элемента, которые проходят через подстановки строк конфига. Список явный:
|
|
3462
|
+
* служебные строки (`attributeName`, `chartId`, `relatedDataSource`, ...) подстановкам не подлежат.
|
|
3463
|
+
*/
|
|
3464
|
+
const CONFIG_STRING_OPTION_KEYS = ["label", "placeholder", "title"];
|
|
3432
3465
|
const FILTERED_VALUE_OPACITY = 28;
|
|
3433
3466
|
const DEFAULT_ATTRIBUTE_NAME = "name";
|
|
3434
3467
|
const DEFAULT_DATA_SOURCE_LIMIT = 100;
|
|
@@ -3483,8 +3516,8 @@ const BASE_CONTAINER_STYLE = {
|
|
|
3483
3516
|
/**
|
|
3484
3517
|
* Маркер тела контейнера внутри `ContainerRoot` — содержимое без заголовка.
|
|
3485
3518
|
*
|
|
3486
|
-
* Нужен, чтобы находить тело по DOM снаружи: `#<id контейнера>`
|
|
3487
|
-
*
|
|
3519
|
+
* Нужен, чтобы находить тело по DOM снаружи: `#<id контейнера>` указывает на корень, то есть на
|
|
3520
|
+
* пару «заголовок + тело».
|
|
3488
3521
|
*/
|
|
3489
3522
|
const CONTAINER_BODY_ATTRIBUTE = "data-container-body";
|
|
3490
3523
|
/**
|
|
@@ -3871,6 +3904,8 @@ const addDataSources = (dashboardConfiguration, pageIndex, layerNames) => {
|
|
|
3871
3904
|
return newConfig.children;
|
|
3872
3905
|
};
|
|
3873
3906
|
|
|
3907
|
+
const isEmptyValue = (value) => value === "" || value === null || value === undefined;
|
|
3908
|
+
|
|
3874
3909
|
const transparentizeColor = (originalColor, percent) => {
|
|
3875
3910
|
const color = new Color$1(originalColor);
|
|
3876
3911
|
color.a = Math.round((255 / 100) * percent);
|
|
@@ -4210,7 +4245,7 @@ const formatDateValue = (stringFormat, dateValue) => {
|
|
|
4210
4245
|
const timeOption = getValueFromOptions(timeOptions, stringFormat.format);
|
|
4211
4246
|
const timeOptionValue = timeOption && (isRuLocale ? timeOption.value : `${timeOption.value} aa`);
|
|
4212
4247
|
const timeFormat = (timeOptionValue && format(value, showTimeZone ? `${timeOptionValue} zz` : timeOptionValue)) || "";
|
|
4213
|
-
return dateFormat ?
|
|
4248
|
+
return dateFormat ? [dateFormat, timeFormat].filter(Boolean).join(" ") : "";
|
|
4214
4249
|
};
|
|
4215
4250
|
const appendUnitsLabel = (value, unitsLabel, noUnits) => (unitsLabel && !noUnits ? `${value} ${unitsLabel}` : value)?.toString() || "";
|
|
4216
4251
|
const formatNumberValue = (stringFormat, value, type, noUnits = false) => {
|
|
@@ -4231,8 +4266,8 @@ const formatNumberValue = (stringFormat, value, type, noUnits = false) => {
|
|
|
4231
4266
|
if (typeof compactValue === "string" && compactValue) {
|
|
4232
4267
|
return appendUnitsLabel(compactValue, unitsLabel, noUnits);
|
|
4233
4268
|
}
|
|
4234
|
-
if (hasRounding && (!isIntValue || !isDefaultScaling)) {
|
|
4235
|
-
currentValue =
|
|
4269
|
+
if (hasRounding && (!isIntValue || !isDefaultScaling) && !isNil(currentValue)) {
|
|
4270
|
+
currentValue = Number(currentValue).toFixed(rounding);
|
|
4236
4271
|
}
|
|
4237
4272
|
if (splitDigitGroup) {
|
|
4238
4273
|
currentValue = numberWithSpaces(currentValue);
|
|
@@ -4353,6 +4388,49 @@ const metersPerPixel = (zoom, lat) => {
|
|
|
4353
4388
|
return (156543.03 * Math.cos(lat * (Math.PI / 180))) / 2 ** zoom;
|
|
4354
4389
|
};
|
|
4355
4390
|
|
|
4391
|
+
const checkIsDateFormat = (value) => value.startsWith("#'") && value.endsWith("'");
|
|
4392
|
+
const checkIfNeedQuotes = (value, attributeType) => {
|
|
4393
|
+
return attributeType
|
|
4394
|
+
? !NUMERIC_ATTRIBUTE_TYPES.includes(attributeType)
|
|
4395
|
+
: typeof value === "number"
|
|
4396
|
+
? false
|
|
4397
|
+
: typeof value === "string" && !isNumeric(value) && !checkIsDateFormat(value);
|
|
4398
|
+
};
|
|
4399
|
+
const formatConditionValue = ({ value, attributeType, defaultValue, checkQuotes = true, isSetParams = false, }) => {
|
|
4400
|
+
if (isEmptyValue(value))
|
|
4401
|
+
return "";
|
|
4402
|
+
const formattedValue = Array.isArray(defaultValue) && !Array.isArray(value) ? [value] : value;
|
|
4403
|
+
const isArray = Array.isArray(formattedValue);
|
|
4404
|
+
let stringValue = formattedValue.toString();
|
|
4405
|
+
if (isArray) {
|
|
4406
|
+
return formattedValue.length && !formattedValue.every(item => !item)
|
|
4407
|
+
? `[${formattedValue
|
|
4408
|
+
.map(item => (checkQuotes && checkIfNeedQuotes(item, attributeType) ? `'${item}'` : item))
|
|
4409
|
+
.join(",")}]`
|
|
4410
|
+
: "";
|
|
4411
|
+
}
|
|
4412
|
+
if (isSetParams && checkIsDateFormat(stringValue)) {
|
|
4413
|
+
stringValue = stringValue.slice(2, -1);
|
|
4414
|
+
}
|
|
4415
|
+
return checkQuotes && checkIfNeedQuotes(stringValue, attributeType) ? `'${stringValue}'` : stringValue;
|
|
4416
|
+
};
|
|
4417
|
+
|
|
4418
|
+
/**
|
|
4419
|
+
* Значение системной подстановки текущего проекта. Голый `%project` и `%project.name` дают
|
|
4420
|
+
* системное имя, `%project.alias` — алиас с фолбэком на имя: пустой алиас не должен уводить
|
|
4421
|
+
* запрос в пустое значение. Чужое свойство (`%project.foo`) не наше — вернём `undefined`,
|
|
4422
|
+
* и плейсхолдер останется нетронутым, как у незаполненного фильтра.
|
|
4423
|
+
*/
|
|
4424
|
+
const getProjectValue = ({ prop, projectName, projectAlias, }) => {
|
|
4425
|
+
if (prop && prop !== PROJECT_NAME_PROP && prop !== PROJECT_ALIAS_PROP) {
|
|
4426
|
+
return undefined;
|
|
4427
|
+
}
|
|
4428
|
+
if (prop === PROJECT_ALIAS_PROP) {
|
|
4429
|
+
return projectAlias || projectName || undefined;
|
|
4430
|
+
}
|
|
4431
|
+
return projectName || undefined;
|
|
4432
|
+
};
|
|
4433
|
+
|
|
4356
4434
|
/** Ключ уровня в значении tree-фильтра: `l1`, `l2`, ... */
|
|
4357
4435
|
const LEVEL_KEY = /^l\d+$/;
|
|
4358
4436
|
/**
|
|
@@ -4402,22 +4480,6 @@ const getDataSourceFilterValue = ({ filterName, filterProp, attributeAlias, data
|
|
|
4402
4480
|
return feature?.properties?.[filterProp];
|
|
4403
4481
|
};
|
|
4404
4482
|
|
|
4405
|
-
/**
|
|
4406
|
-
* Значение системной подстановки текущего проекта. Голый `%project` и `%project.name` дают
|
|
4407
|
-
* системное имя, `%project.alias` — алиас с фолбэком на имя: пустой алиас не должен уводить
|
|
4408
|
-
* запрос в пустое значение. Чужое свойство (`%project.foo`) не наше — вернём `undefined`,
|
|
4409
|
-
* и плейсхолдер останется нетронутым, как у незаполненного фильтра.
|
|
4410
|
-
*/
|
|
4411
|
-
const getProjectValue = ({ prop, projectName, projectAlias, }) => {
|
|
4412
|
-
if (prop && prop !== PROJECT_NAME_PROP && prop !== PROJECT_ALIAS_PROP) {
|
|
4413
|
-
return undefined;
|
|
4414
|
-
}
|
|
4415
|
-
if (prop === PROJECT_ALIAS_PROP) {
|
|
4416
|
-
return projectAlias || projectName || undefined;
|
|
4417
|
-
}
|
|
4418
|
-
return projectName || undefined;
|
|
4419
|
-
};
|
|
4420
|
-
|
|
4421
4483
|
const getSelectedFilterValue = (filterName, selectedFilters, defaultValue) => {
|
|
4422
4484
|
return ((!isNil(selectedFilters?.[filterName]?.value) &&
|
|
4423
4485
|
Array.isArray(defaultValue) &&
|
|
@@ -4426,124 +4488,370 @@ const getSelectedFilterValue = (filterName, selectedFilters, defaultValue) => {
|
|
|
4426
4488
|
: selectedFilters?.[filterName]?.value) ?? defaultValue);
|
|
4427
4489
|
};
|
|
4428
4490
|
|
|
4429
|
-
const
|
|
4491
|
+
const known = (value, defaultValue) => ({ known: true, value, defaultValue });
|
|
4492
|
+
const getRangeBound = (prop, defaultValue, scalarDefault) => {
|
|
4493
|
+
if (Array.isArray(defaultValue)) {
|
|
4494
|
+
return defaultValue[prop === "min" ? 0 : 1];
|
|
4495
|
+
}
|
|
4496
|
+
return scalarDefault ? defaultValue : undefined;
|
|
4497
|
+
};
|
|
4498
|
+
/** `.prop` — свойство строки `relatedDataSource`, выбранной фильтром. */
|
|
4499
|
+
const getRelatedValue = ({ name, prop, store, configFilter, defaultValue }) => getDataSourceFilterValue({
|
|
4500
|
+
filterName: name,
|
|
4501
|
+
filterProp: prop,
|
|
4502
|
+
selectedFilters: store.selectedFilters ?? {},
|
|
4503
|
+
attributeAlias: configFilter?.attributeAlias,
|
|
4504
|
+
dataSource: getDataSource(configFilter?.relatedDataSource, store.dataSources),
|
|
4505
|
+
}) ?? defaultValue;
|
|
4506
|
+
const getTreeLevel = (value, prop) => {
|
|
4507
|
+
const level = value[prop];
|
|
4508
|
+
return isNil(level) || !level.length ? undefined : level;
|
|
4509
|
+
};
|
|
4510
|
+
/** Параметры запроса и текст UI: пустое (`null`/`undefined`) значение падает в дефолт. */
|
|
4511
|
+
const getValueModeToken = (context) => {
|
|
4512
|
+
const { name, prop, store, configFilter, selected, defaultValue } = context;
|
|
4513
|
+
if (RANGE_PROPS.includes(prop)) {
|
|
4514
|
+
return known(selected?.[prop] ?? getRangeBound(prop, defaultValue, true));
|
|
4515
|
+
}
|
|
4516
|
+
if (prop) {
|
|
4517
|
+
return isTreeFilterValue(selected?.value, configFilter)
|
|
4518
|
+
? known(getTreeLevel(selected.value, prop))
|
|
4519
|
+
: known(getRelatedValue(context));
|
|
4520
|
+
}
|
|
4521
|
+
return known(getSelectedFilterValue(name, store.selectedFilters, defaultValue), defaultValue);
|
|
4522
|
+
};
|
|
4523
|
+
/** Условие слоя: выбранное пустое значение не подставляется, объектные значения — только уровнями tree. */
|
|
4524
|
+
const getConditionModeToken = (context) => {
|
|
4525
|
+
const { prop, configFilter, selected, defaultValue } = context;
|
|
4526
|
+
const rawValue = selected !== undefined ? selected.value : defaultValue;
|
|
4527
|
+
if (configFilter?.valueType === "features" || isFeaturesFilterValue(rawValue)) {
|
|
4528
|
+
return { known: false };
|
|
4529
|
+
}
|
|
4530
|
+
if (isTreeFilterValue(rawValue, configFilter)) {
|
|
4531
|
+
return prop ? known(getTreeLevel(rawValue, prop)) : { known: false };
|
|
4532
|
+
}
|
|
4533
|
+
const hasValue = selected !== undefined && !!selected.value?.toString().length;
|
|
4534
|
+
if (RANGE_PROPS.includes(prop)) {
|
|
4535
|
+
return known(hasValue ? selected[prop] : getRangeBound(prop, defaultValue, false));
|
|
4536
|
+
}
|
|
4537
|
+
if (prop) {
|
|
4538
|
+
return known(getRelatedValue(context));
|
|
4539
|
+
}
|
|
4540
|
+
return known(hasValue ? selected.value : selected === undefined ? defaultValue : "", defaultValue);
|
|
4541
|
+
};
|
|
4542
|
+
/**
|
|
4543
|
+
* Значение токена фильтра (`%name[.prop]`, `$dashboard:name[.prop]`) из переданного стора.
|
|
4544
|
+
* Фильтр, о котором стор ничего не знает, — `known: false`: токен остаётся в строке как есть.
|
|
4545
|
+
*/
|
|
4546
|
+
const getFilterTokenValue = (params) => {
|
|
4547
|
+
const { name, store, mode } = params;
|
|
4548
|
+
const configFilter = getConfigFilter(name, store.configFilters);
|
|
4549
|
+
const selected = store.selectedFilters?.[name];
|
|
4550
|
+
if (!configFilter && (mode === "condition" || selected === undefined)) {
|
|
4551
|
+
return { known: false };
|
|
4552
|
+
}
|
|
4553
|
+
const context = { ...params, configFilter, selected, defaultValue: configFilter?.defaultValue };
|
|
4554
|
+
return mode === "condition" ? getConditionModeToken(context) : getValueModeToken(context);
|
|
4555
|
+
};
|
|
4556
|
+
|
|
4557
|
+
const UNKNOWN = { known: false };
|
|
4558
|
+
/** Стор `$dashboard:`. Без отдельного стора строка живёт на самом дашборде — это стор виджета. */
|
|
4559
|
+
const getDashboardStore = ({ dashboard, widget }) => dashboard ?? widget ?? {};
|
|
4560
|
+
const isValidGeometry = (geometry) => !!geometry && !geometry.includes("()") && geometry.endsWith(")");
|
|
4561
|
+
/**
|
|
4562
|
+
* Системные подстановки `%geometry`, `%extent`, `%zoom`, `%project[.name|.alias]` — значения из
|
|
4563
|
+
* `GlobalContext`, а не из фильтров. Пока значения нет, `geometry` / `extent` / `zoom` уступают
|
|
4564
|
+
* одноимённому фильтру; имя `project` зарезервировано целиком.
|
|
4565
|
+
*/
|
|
4566
|
+
const resolveSystemToken = ({ name, prop }, sources) => {
|
|
4567
|
+
const { geometry, extent, zoomLevel, projectName, projectAlias } = sources;
|
|
4568
|
+
if (name === GEOMETRY_FILTER_NAME && !prop && isValidGeometry(geometry)) {
|
|
4569
|
+
return { known: true, value: geometry, system: "quoted" };
|
|
4570
|
+
}
|
|
4571
|
+
if (name === EXTENT_FILTER_NAME && !prop && extent) {
|
|
4572
|
+
return { known: true, value: extent, system: "quoted" };
|
|
4573
|
+
}
|
|
4574
|
+
if (name === ZOOM_FILTER_NAME && !prop && !isNil(zoomLevel)) {
|
|
4575
|
+
return { known: true, value: zoomLevel, system: "number" };
|
|
4576
|
+
}
|
|
4577
|
+
if (name === PROJECT_FILTER_NAME) {
|
|
4578
|
+
const value = getProjectValue({ prop, projectName, projectAlias });
|
|
4579
|
+
return isNil(value) ? UNKNOWN : { known: true, value, system: "quoted" };
|
|
4580
|
+
}
|
|
4581
|
+
return null;
|
|
4582
|
+
};
|
|
4583
|
+
const resolveFilterToken = (token, sources, mode) => {
|
|
4584
|
+
const { scope, name, prop } = token;
|
|
4585
|
+
if (scope === "widget") {
|
|
4586
|
+
const system = resolveSystemToken(token, sources);
|
|
4587
|
+
if (system)
|
|
4588
|
+
return system;
|
|
4589
|
+
}
|
|
4590
|
+
const store = scope === "dashboard" ? getDashboardStore(sources) : sources.widget ?? {};
|
|
4591
|
+
return getFilterTokenValue({ name, prop, store, mode });
|
|
4592
|
+
};
|
|
4593
|
+
/** `$card:<слой>:<поле>` — атрибут объекта карточки, если карточка открыта на этом слое. */
|
|
4594
|
+
const resolveCardToken = ({ layerName, fieldName }, { attributes, layerInfo }) => {
|
|
4595
|
+
if (!attributes || layerInfo?.name !== layerName)
|
|
4596
|
+
return UNKNOWN;
|
|
4597
|
+
const attribute = attributes.find(({ attributeName }) => attributeName === fieldName);
|
|
4598
|
+
return { known: true, value: attribute?.value ?? null, attributeType: attribute?.type };
|
|
4599
|
+
};
|
|
4600
|
+
/**
|
|
4601
|
+
* `$dashboard:<датасорс>:<поле>` — поле первой строки датасорса дашборда (по имени, затем по
|
|
4602
|
+
* `layerName`). Пока датасорс не загружен, значение пустое: токен не должен мелькать в тексте.
|
|
4603
|
+
*/
|
|
4604
|
+
const resolveDataSourceToken = ({ dataSourceName, fieldName }, sources) => {
|
|
4605
|
+
const { dataSources } = getDashboardStore(sources);
|
|
4606
|
+
const dataSource = dataSources?.find(({ name }) => name === dataSourceName) ??
|
|
4607
|
+
dataSources?.find(({ layerName }) => layerName === dataSourceName);
|
|
4608
|
+
return { known: true, value: dataSource?.features?.[0]?.properties?.[fieldName] ?? null };
|
|
4609
|
+
};
|
|
4610
|
+
/** `{name}` — атрибут объекта; пустой атрибут даёт пустую строку, незнакомое имя остаётся как есть. */
|
|
4611
|
+
const resolveAttributeToken = ({ name }, { attributes }) => {
|
|
4612
|
+
const attribute = attributes?.find(({ attributeName }) => attributeName === name);
|
|
4613
|
+
return attribute ? { known: true, value: attribute.value ?? "", attributeType: attribute.type } : UNKNOWN;
|
|
4614
|
+
};
|
|
4615
|
+
/** Сырое значение токена строки конфига — без форматирования под параметр, условие или текст. */
|
|
4616
|
+
const resolveConfigToken = (token, sources, mode) => {
|
|
4617
|
+
if (token.kind === "filter")
|
|
4618
|
+
return resolveFilterToken(token, sources, mode);
|
|
4619
|
+
if (token.kind === "card")
|
|
4620
|
+
return resolveCardToken(token, sources);
|
|
4621
|
+
if (token.kind === "dataSource")
|
|
4622
|
+
return resolveDataSourceToken(token, sources);
|
|
4623
|
+
return resolveAttributeToken(token, sources);
|
|
4624
|
+
};
|
|
4625
|
+
|
|
4626
|
+
const toToken = ([raw, ...groups]) => {
|
|
4627
|
+
const [dataSourceName, dataSourceField, dashboardFilter, dashboardProp, cardLayer, cardField, filterName, filterProp, attributeName,] = groups;
|
|
4628
|
+
if (dataSourceName) {
|
|
4629
|
+
return { kind: "dataSource", dataSourceName, fieldName: dataSourceField, raw };
|
|
4630
|
+
}
|
|
4631
|
+
if (dashboardFilter) {
|
|
4632
|
+
return { kind: "filter", scope: "dashboard", name: dashboardFilter, prop: dashboardProp, raw };
|
|
4633
|
+
}
|
|
4634
|
+
if (cardLayer) {
|
|
4635
|
+
return { kind: "card", layerName: cardLayer, fieldName: cardField, raw };
|
|
4636
|
+
}
|
|
4637
|
+
if (filterName) {
|
|
4638
|
+
return { kind: "filter", scope: "widget", name: filterName, prop: filterProp, raw };
|
|
4639
|
+
}
|
|
4640
|
+
return { kind: "attribute", name: attributeName, raw };
|
|
4641
|
+
};
|
|
4642
|
+
/** Разбирает строку конфига на литералы и токены подстановок — в исходном порядке. */
|
|
4643
|
+
const tokenizeConfigString = (value) => {
|
|
4644
|
+
const segments = [];
|
|
4645
|
+
let lastIndex = 0;
|
|
4646
|
+
for (const match of value.matchAll(CONFIG_TOKEN_REGEXP)) {
|
|
4647
|
+
if (match.index > lastIndex) {
|
|
4648
|
+
segments.push(value.slice(lastIndex, match.index));
|
|
4649
|
+
}
|
|
4650
|
+
segments.push(toToken(match));
|
|
4651
|
+
lastIndex = match.index + match[0].length;
|
|
4652
|
+
}
|
|
4653
|
+
if (lastIndex < value.length) {
|
|
4654
|
+
segments.push(value.slice(lastIndex));
|
|
4655
|
+
}
|
|
4656
|
+
return segments;
|
|
4657
|
+
};
|
|
4658
|
+
/** Строка целиком — один токен: тогда значение уходит как есть, с типом (число, массив). */
|
|
4659
|
+
const getSingleToken = (segments) => segments.length === 1 && typeof segments[0] !== "string" ? segments[0] : null;
|
|
4660
|
+
|
|
4661
|
+
/** Склейка сегментов: токен заменяется своим форматом, незнакомый — остаётся как написан. */
|
|
4662
|
+
const joinConfigSegments = (segments, formatToken) => segments.map(segment => (typeof segment === "string" ? segment : formatToken(segment))).join("");
|
|
4663
|
+
const hasTokens = (segments) => segments.some(segment => typeof segment !== "string");
|
|
4664
|
+
const toParameterText = (value) => (isNil(value) ? "" : String(value));
|
|
4665
|
+
const toUiText = (value) => {
|
|
4666
|
+
if (isNil(value) || isObject(value) && !(value instanceof Date))
|
|
4667
|
+
return "";
|
|
4668
|
+
if (Array.isArray(value)) {
|
|
4669
|
+
return value
|
|
4670
|
+
.map(toUiText)
|
|
4671
|
+
.filter(item => item !== "")
|
|
4672
|
+
.join(CONFIG_TEXT_ARRAY_SEPARATOR);
|
|
4673
|
+
}
|
|
4674
|
+
return String(value);
|
|
4675
|
+
};
|
|
4676
|
+
const interpolate = (segments, sources, toText) => joinConfigSegments(segments, token => {
|
|
4677
|
+
const resolved = resolveConfigToken(token, sources, "value");
|
|
4678
|
+
return resolved.known ? toText(resolved.value) : token.raw;
|
|
4679
|
+
});
|
|
4680
|
+
/**
|
|
4681
|
+
* Значение параметра запроса. Строка-токен целиком уходит сырым значением с типом, а пустой или
|
|
4682
|
+
* незнакомый фильтр не уходит вовсе (`undefined`). Токены внутри строки интерполируются текстом.
|
|
4683
|
+
*/
|
|
4684
|
+
const resolveQueryParameter = (value, sources) => {
|
|
4685
|
+
if (typeof value !== "string")
|
|
4686
|
+
return value;
|
|
4687
|
+
const segments = tokenizeConfigString(value);
|
|
4688
|
+
const token = getSingleToken(segments);
|
|
4689
|
+
if (token) {
|
|
4690
|
+
const resolved = resolveConfigToken(token, sources, "value");
|
|
4691
|
+
// Незнакомое имя в фигурных скобках — не подстановка, а литерал (JSON, шаблон сервиса).
|
|
4692
|
+
if (!resolved.known && token.kind === "attribute")
|
|
4693
|
+
return value;
|
|
4694
|
+
return resolved.known && !isNil(resolved.value) ? resolved.value : undefined;
|
|
4695
|
+
}
|
|
4696
|
+
return hasTokens(segments) ? interpolate(segments, sources, toParameterText) : value;
|
|
4697
|
+
};
|
|
4698
|
+
/**
|
|
4699
|
+
* Строка конфига для UI (значение элемента, дефолт, подпись). Строка-токен целиком отдаёт сырое
|
|
4700
|
+
* значение (число, массив — например, дефолт мультиселекта); пустое — пустая строка. Внутри текста
|
|
4701
|
+
* массив склеивается через `", "`. Незнакомый токен (опечатка, литерал `50%off`) остаётся как написан.
|
|
4702
|
+
*/
|
|
4703
|
+
const resolveConfigString = (value, sources) => {
|
|
4704
|
+
if (typeof value !== "string" || !value)
|
|
4705
|
+
return value;
|
|
4706
|
+
const segments = tokenizeConfigString(value);
|
|
4707
|
+
const token = getSingleToken(segments);
|
|
4708
|
+
if (token) {
|
|
4709
|
+
const resolved = resolveConfigToken(token, sources, "value");
|
|
4710
|
+
if (!resolved.known)
|
|
4711
|
+
return value;
|
|
4712
|
+
return isNil(resolved.value) || isObject(resolved.value) && !(resolved.value instanceof Date) ? "" : resolved.value;
|
|
4713
|
+
}
|
|
4714
|
+
return hasTokens(segments) ? interpolate(segments, sources, toUiText) : value;
|
|
4715
|
+
};
|
|
4716
|
+
|
|
4717
|
+
const SET_PARAMS_REGEXP = /\$\([^)]+\)/g;
|
|
4718
|
+
const CONDITION_SPLITTER = " AND ";
|
|
4719
|
+
const formatResolvedToken = (resolved, isSetParams) => {
|
|
4720
|
+
if (!resolved.known)
|
|
4721
|
+
return "";
|
|
4722
|
+
const { value, system, attributeType, defaultValue } = resolved;
|
|
4723
|
+
if (system === "quoted")
|
|
4724
|
+
return `'${value}'`;
|
|
4725
|
+
if (system === "number")
|
|
4726
|
+
return String(value);
|
|
4727
|
+
return formatConditionValue({
|
|
4728
|
+
value: (attributeType ? value?.toString() : value),
|
|
4729
|
+
attributeType,
|
|
4730
|
+
defaultValue,
|
|
4731
|
+
isSetParams,
|
|
4732
|
+
});
|
|
4733
|
+
};
|
|
4734
|
+
/**
|
|
4735
|
+
* Eql-параметры слоя подставляются по голому имени. Имя внутри `$(имя=` прячется на время замены,
|
|
4736
|
+
* чтобы не задеть объявление set-параметра.
|
|
4737
|
+
*/
|
|
4738
|
+
const applyEqlParameters = (section, { eqlParameters, layerParams }) => {
|
|
4739
|
+
if (!eqlParameters || !layerParams)
|
|
4740
|
+
return section;
|
|
4741
|
+
return Object.keys(eqlParameters).reduce((result, param) => result
|
|
4742
|
+
.replace(new RegExp(`\\$\\(${param}=`), "***")
|
|
4743
|
+
.replace(new RegExp(param), formatConditionValue({ value: layerParams[param] }))
|
|
4744
|
+
.replace(/\*\*\*/, `$(${param}=`), section);
|
|
4745
|
+
};
|
|
4746
|
+
/**
|
|
4747
|
+
* Одна секция условия: все токены в формате условия. Пустое значение фильтра оставляет токен как
|
|
4748
|
+
* есть; пустой атрибут объекта (`{name}`) заменяется пустой строкой.
|
|
4749
|
+
*/
|
|
4750
|
+
const formatConditionSection = (section, sources, { isSetParams = false, ...eql } = {}) => {
|
|
4751
|
+
const formatted = joinConfigSegments(tokenizeConfigString(section), (token) => {
|
|
4752
|
+
const resolved = resolveConfigToken(token, sources, "condition");
|
|
4753
|
+
const value = formatResolvedToken(resolved, isSetParams);
|
|
4754
|
+
return value || (token.kind === "attribute" && resolved.known ? "" : token.raw);
|
|
4755
|
+
});
|
|
4756
|
+
return applyEqlParameters(formatted, eql);
|
|
4757
|
+
};
|
|
4758
|
+
/**
|
|
4759
|
+
* Условие слоя: ведущие set-параметры `$(имя=…)` форматируются отдельно (даты без `#'…'`),
|
|
4760
|
+
* остаток — по секциям `AND`.
|
|
4761
|
+
*/
|
|
4762
|
+
const formatConditionString = (condition, sources, eql = {}) => {
|
|
4763
|
+
const setParams = condition.match(SET_PARAMS_REGEXP) ?? [];
|
|
4764
|
+
const setParamsSection = setParams.map(section => formatConditionSection(section, sources, { ...eql, isSetParams: true }));
|
|
4765
|
+
const conditionSection = condition
|
|
4766
|
+
.substring(setParams.join("").length)
|
|
4767
|
+
.split(CONDITION_SPLITTER)
|
|
4768
|
+
.map(section => formatConditionSection(section, sources, eql));
|
|
4769
|
+
if (!setParamsSection.length)
|
|
4770
|
+
return conditionSection.join(CONDITION_SPLITTER);
|
|
4771
|
+
return conditionSection.length
|
|
4772
|
+
? [setParamsSection.join(""), conditionSection.join(CONDITION_SPLITTER)].join(" ")
|
|
4773
|
+
: setParamsSection.join("");
|
|
4774
|
+
};
|
|
4775
|
+
|
|
4776
|
+
const DASHBOARD_TOKEN_PREFIX = `${PROVIDER_PREFIX}${ProviderPrefix.Dashboard}:`;
|
|
4777
|
+
/** Строка ссылается на фильтр дашборда (`$dashboard:`). */
|
|
4778
|
+
const isDashboardReference = (value) => typeof value === "string" && value.includes(DASHBOARD_TOKEN_PREFIX);
|
|
4779
|
+
|
|
4780
|
+
/**
|
|
4781
|
+
* Строка конфига ссылается на один из фильтров: `%name[.prop]` — на фильтр виджета,
|
|
4782
|
+
* `$dashboard:name[.prop]` — на фильтр дашборда. На самом дашборде (`hasDashboardStore = false`)
|
|
4783
|
+
* `$dashboard:` — те же фильтры виджета.
|
|
4784
|
+
*/
|
|
4785
|
+
const referencesFilters = ({ value, widgetNames, dashboardNames, hasDashboardStore, }) => typeof value === "string" &&
|
|
4786
|
+
tokenizeConfigString(value).some(segment => {
|
|
4787
|
+
if (typeof segment === "string" || segment.kind !== "filter")
|
|
4788
|
+
return false;
|
|
4789
|
+
const names = segment.scope === "dashboard" && hasDashboardStore ? dashboardNames : widgetNames;
|
|
4790
|
+
return names.includes(segment.name);
|
|
4791
|
+
});
|
|
4792
|
+
|
|
4793
|
+
/**
|
|
4794
|
+
* Узел конфига с подставленными строками: `value`, `defaultValue` и подписи из
|
|
4795
|
+
* {@link CONFIG_STRING_OPTION_KEYS}. Без подстановок узел возвращается тем же объектом —
|
|
4796
|
+
* мемоизированные контейнеры не перерисовываются зря.
|
|
4797
|
+
*/
|
|
4798
|
+
const resolveElementStrings = (element, sources) => {
|
|
4799
|
+
if (!element || !sources)
|
|
4800
|
+
return element;
|
|
4801
|
+
const value = resolveConfigString(element.value, sources);
|
|
4802
|
+
const defaultValue = resolveConfigString(element.defaultValue, sources);
|
|
4803
|
+
const changedOptions = CONFIG_STRING_OPTION_KEYS.reduce((result, key) => {
|
|
4804
|
+
const option = element.options?.[key];
|
|
4805
|
+
const resolved = resolveConfigString(option, sources);
|
|
4806
|
+
return resolved === option ? result : { ...result, [key]: resolved };
|
|
4807
|
+
}, {});
|
|
4808
|
+
const hasChangedOptions = Object.keys(changedOptions).length > 0;
|
|
4809
|
+
if (value === element.value && defaultValue === element.defaultValue && !hasChangedOptions) {
|
|
4810
|
+
return element;
|
|
4811
|
+
}
|
|
4812
|
+
return {
|
|
4813
|
+
...element,
|
|
4814
|
+
value,
|
|
4815
|
+
defaultValue,
|
|
4816
|
+
...(hasChangedOptions && { options: { ...element.options, ...changedOptions } }),
|
|
4817
|
+
};
|
|
4818
|
+
};
|
|
4819
|
+
|
|
4820
|
+
/**
|
|
4821
|
+
* Дефолты фильтров виджета из фильтров дашборда: `defaultValue: "$dashboard:year"`. Резолвится
|
|
4822
|
+
* только `$dashboard:` — ссылки на собственные фильтры (`%name`) дали бы цикл и остаются как есть.
|
|
4823
|
+
* Дефолт живой: пока фильтр виджета не выбран, он следует за дашбордом, ручной выбор его перекрывает.
|
|
4824
|
+
*/
|
|
4825
|
+
const resolveFilterDefaults = (configFilters, dashboard) => {
|
|
4826
|
+
if (!dashboard || !configFilters?.some(({ defaultValue }) => isDashboardReference(defaultValue))) {
|
|
4827
|
+
return configFilters;
|
|
4828
|
+
}
|
|
4829
|
+
return configFilters.map(filter => {
|
|
4830
|
+
if (!isDashboardReference(filter.defaultValue))
|
|
4831
|
+
return filter;
|
|
4832
|
+
const defaultValue = resolveQueryParameter(filter.defaultValue, { widget: {}, dashboard });
|
|
4833
|
+
return defaultValue === filter.defaultValue ? filter : { ...filter, defaultValue: defaultValue };
|
|
4834
|
+
});
|
|
4835
|
+
};
|
|
4836
|
+
|
|
4837
|
+
const applyQueryFilters = ({ parameters: configParameters, filters: configFilters, selectedFilters, dashboard, geometry, extent, zoomLevel, projectName, projectAlias, attributes, layerInfo, dataSources, }) => {
|
|
4430
4838
|
if (!configParameters) {
|
|
4431
4839
|
return {};
|
|
4432
4840
|
}
|
|
4841
|
+
const sources = {
|
|
4842
|
+
widget: { selectedFilters, configFilters, dataSources },
|
|
4843
|
+
dashboard,
|
|
4844
|
+
geometry,
|
|
4845
|
+
extent,
|
|
4846
|
+
zoomLevel,
|
|
4847
|
+
projectName,
|
|
4848
|
+
projectAlias,
|
|
4849
|
+
attributes,
|
|
4850
|
+
layerInfo,
|
|
4851
|
+
};
|
|
4433
4852
|
return Object.keys(configParameters).reduce((result, key) => {
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
const [provider, layerName, fieldName] = raw.split(":");
|
|
4437
|
-
if (provider === ProviderPrefix.Card && fieldName && layerInfo.name === layerName && attributes) {
|
|
4438
|
-
const attribute = attributes.find(({ attributeName }) => attributeName === fieldName);
|
|
4439
|
-
const cardValue = attribute?.value ?? null;
|
|
4440
|
-
if (isNil(cardValue))
|
|
4441
|
-
return result;
|
|
4442
|
-
return { ...result, [key]: cardValue };
|
|
4443
|
-
}
|
|
4444
|
-
if (provider === ProviderPrefix.Left && layerName && fieldName && projectDataSources?.length) {
|
|
4445
|
-
const dataSource = projectDataSources.find(({ layerName: name }) => name === layerName);
|
|
4446
|
-
const leftValue = dataSource?.features?.[0]?.properties?.[fieldName] ?? null;
|
|
4447
|
-
if (isNil(leftValue))
|
|
4448
|
-
return result;
|
|
4449
|
-
return { ...result, [key]: leftValue };
|
|
4450
|
-
}
|
|
4451
|
-
return result;
|
|
4452
|
-
}
|
|
4453
|
-
if (typeof configParameters[key] === "string" && configParameters[key].includes("{") && attributes?.length) {
|
|
4454
|
-
const exactAttr = attributes.find(({ attributeName }) => configParameters[key] === `{${attributeName}}`);
|
|
4455
|
-
if (exactAttr) {
|
|
4456
|
-
return { ...result, [key]: exactAttr.value ?? "" };
|
|
4457
|
-
}
|
|
4458
|
-
let interpolated = configParameters[key];
|
|
4459
|
-
attributes.forEach(({ attributeName, value: attrValue }) => {
|
|
4460
|
-
interpolated = interpolated.replace(new RegExp(`\\{${attributeName}\\}`, "g"), attrValue?.toString() ?? "");
|
|
4461
|
-
});
|
|
4462
|
-
return { ...result, [key]: interpolated };
|
|
4463
|
-
}
|
|
4464
|
-
if (typeof configParameters[key] !== "string" || !configParameters[key].startsWith("%")) {
|
|
4465
|
-
return {
|
|
4466
|
-
...result,
|
|
4467
|
-
[key]: configParameters[key],
|
|
4468
|
-
};
|
|
4469
|
-
}
|
|
4470
|
-
const filterFullName = configParameters[key].replace("%", "");
|
|
4471
|
-
const [filterName, filterProp] = filterFullName.includes(".") ? filterFullName.split(".") : [filterFullName, null];
|
|
4472
|
-
const configFilter = getConfigFilter(filterName, configFilters);
|
|
4473
|
-
const { defaultValue, relatedDataSource, attributeAlias } = configFilter || {};
|
|
4474
|
-
if (filterName === GEOMETRY_FILTER_NAME && geometry && !geometry.includes("()") && geometry.endsWith(")")) {
|
|
4475
|
-
return {
|
|
4476
|
-
...result,
|
|
4477
|
-
[key]: geometry,
|
|
4478
|
-
};
|
|
4479
|
-
}
|
|
4480
|
-
// Системные фильтры вида карты: значение приходит из `GlobalContext`, а не из `SelectedFilters`,
|
|
4481
|
-
// поэтому резолвятся до веток `.min` / `.max` / `.property` — как `geometry` выше.
|
|
4482
|
-
if (filterName === EXTENT_FILTER_NAME && extent) {
|
|
4483
|
-
return {
|
|
4484
|
-
...result,
|
|
4485
|
-
[key]: extent,
|
|
4486
|
-
};
|
|
4487
|
-
}
|
|
4488
|
-
// Единственная нестроковая системная подстановка: уровень зума уходит числом.
|
|
4489
|
-
if (filterName === ZOOM_FILTER_NAME && !isNil(zoomLevel)) {
|
|
4490
|
-
return {
|
|
4491
|
-
...result,
|
|
4492
|
-
[key]: zoomLevel,
|
|
4493
|
-
};
|
|
4494
|
-
}
|
|
4495
|
-
// Текущий проект: значение приходит из `GlobalContext`, а не из `SelectedFilters`. Резолвим
|
|
4496
|
-
// до веток `.min` / `.max` / `.property`, как экстент и зум, — имя `project` зарезервировано.
|
|
4497
|
-
if (filterName === PROJECT_FILTER_NAME) {
|
|
4498
|
-
const projectValue = getProjectValue({ prop: filterProp, projectName, projectAlias });
|
|
4499
|
-
if (isNil(projectValue)) {
|
|
4500
|
-
return result;
|
|
4501
|
-
}
|
|
4502
|
-
return {
|
|
4503
|
-
...result,
|
|
4504
|
-
[key]: projectValue,
|
|
4505
|
-
};
|
|
4506
|
-
}
|
|
4507
|
-
if (configParameters[key].endsWith(".max")) {
|
|
4508
|
-
return {
|
|
4509
|
-
...result,
|
|
4510
|
-
[key]: selectedFilters?.[filterName]?.max ?? (Array.isArray(defaultValue) ? defaultValue[1] : defaultValue),
|
|
4511
|
-
};
|
|
4512
|
-
}
|
|
4513
|
-
if (configParameters[key].endsWith(".min")) {
|
|
4514
|
-
return {
|
|
4515
|
-
...result,
|
|
4516
|
-
[key]: selectedFilters?.[filterName]?.min ?? (Array.isArray(defaultValue) ? defaultValue[0] : defaultValue),
|
|
4517
|
-
};
|
|
4518
|
-
}
|
|
4519
|
-
if (configParameters[key].includes(".")) {
|
|
4520
|
-
const selectedValue = selectedFilters?.[filterName]?.value;
|
|
4521
|
-
if (isTreeFilterValue(selectedValue, configFilter)) {
|
|
4522
|
-
const levelValue = filterProp ? selectedValue[filterProp] : undefined;
|
|
4523
|
-
if (isNil(levelValue) || !levelValue.length) {
|
|
4524
|
-
return result;
|
|
4525
|
-
}
|
|
4526
|
-
return { ...result, [key]: levelValue };
|
|
4527
|
-
}
|
|
4528
|
-
return {
|
|
4529
|
-
...result,
|
|
4530
|
-
[key]: getDataSourceFilterValue({
|
|
4531
|
-
filterName,
|
|
4532
|
-
filterProp,
|
|
4533
|
-
selectedFilters,
|
|
4534
|
-
attributeAlias,
|
|
4535
|
-
dataSource: getDataSource(relatedDataSource, dataSources),
|
|
4536
|
-
}) ?? defaultValue,
|
|
4537
|
-
};
|
|
4538
|
-
}
|
|
4539
|
-
const value = getSelectedFilterValue(filterName, selectedFilters, defaultValue);
|
|
4540
|
-
if (isNil(value)) {
|
|
4541
|
-
return result;
|
|
4542
|
-
}
|
|
4543
|
-
return {
|
|
4544
|
-
...result,
|
|
4545
|
-
[key]: value,
|
|
4546
|
-
};
|
|
4853
|
+
const value = resolveQueryParameter(configParameters[key], sources);
|
|
4854
|
+
return isUndefined(value) ? result : { ...result, [key]: value };
|
|
4547
4855
|
}, {});
|
|
4548
4856
|
};
|
|
4549
4857
|
|
|
@@ -4598,35 +4906,6 @@ const createConfigPage = (props) => {
|
|
|
4598
4906
|
};
|
|
4599
4907
|
};
|
|
4600
4908
|
|
|
4601
|
-
const isEmptyValue = (value) => value === "" || value === null || value === undefined;
|
|
4602
|
-
|
|
4603
|
-
const checkIsDateFormat = (value) => value.startsWith("#'") && value.endsWith("'");
|
|
4604
|
-
const checkIfNeedQuotes = (value, attributeType) => {
|
|
4605
|
-
return attributeType
|
|
4606
|
-
? !NUMERIC_ATTRIBUTE_TYPES.includes(attributeType)
|
|
4607
|
-
: typeof value === "number"
|
|
4608
|
-
? false
|
|
4609
|
-
: typeof value === "string" && !isNumeric(value) && !checkIsDateFormat(value);
|
|
4610
|
-
};
|
|
4611
|
-
const formatConditionValue = ({ value, attributeType, defaultValue, checkQuotes = true, isSetParams = false, }) => {
|
|
4612
|
-
if (isEmptyValue(value))
|
|
4613
|
-
return "";
|
|
4614
|
-
const formattedValue = Array.isArray(defaultValue) && !Array.isArray(value) ? [value] : value;
|
|
4615
|
-
const isArray = Array.isArray(formattedValue);
|
|
4616
|
-
let stringValue = formattedValue.toString();
|
|
4617
|
-
if (isArray) {
|
|
4618
|
-
return formattedValue.length && !formattedValue.every(item => !item)
|
|
4619
|
-
? `[${formattedValue
|
|
4620
|
-
.map(item => (checkQuotes && checkIfNeedQuotes(item, attributeType) ? `'${item}'` : item))
|
|
4621
|
-
.join(",")}]`
|
|
4622
|
-
: "";
|
|
4623
|
-
}
|
|
4624
|
-
if (isSetParams && checkIsDateFormat(stringValue)) {
|
|
4625
|
-
stringValue = stringValue.slice(2, -1);
|
|
4626
|
-
}
|
|
4627
|
-
return checkQuotes && checkIfNeedQuotes(stringValue, attributeType) ? `'${stringValue}'` : stringValue;
|
|
4628
|
-
};
|
|
4629
|
-
|
|
4630
4909
|
/**
|
|
4631
4910
|
* Подставляет плейсхолдеры уровней `%name.l{N}` значениями id соответствующего уровня
|
|
4632
4911
|
* (формат `[id1,id2,...]` для оператора IN). Уровни, отсутствующие в значении (или пустые),
|
|
@@ -4674,10 +4953,10 @@ const isDevEnvironment = (() => {
|
|
|
4674
4953
|
}
|
|
4675
4954
|
})();
|
|
4676
4955
|
/**
|
|
4677
|
-
* Один раз на источник сообщает, что конфиг
|
|
4956
|
+
* Один раз на источник сообщает, что конфиг задаёт ось устаревшей плоской формой.
|
|
4678
4957
|
*
|
|
4679
|
-
*
|
|
4680
|
-
*
|
|
4958
|
+
* Предупреждение показывает, какие конфиги ещё держатся на плоской форме, — без него её поддержку
|
|
4959
|
+
* нельзя убрать, не проверив все конфиги вручную.
|
|
4681
4960
|
*/
|
|
4682
4961
|
const warnLegacyAxis = ({ dataSourceName, attributeName }) => {
|
|
4683
4962
|
if (!isDevEnvironment)
|
|
@@ -4717,7 +4996,7 @@ const resolveAxis = (source) => {
|
|
|
4717
4996
|
* Источник питает ось значений.
|
|
4718
4997
|
*
|
|
4719
4998
|
* Отсутствие `axis.type` означает `"y"`: конфиг без явного типа — самый частый случай,
|
|
4720
|
-
* и
|
|
4999
|
+
* и такой источник не должен выпадать из графика.
|
|
4721
5000
|
*/
|
|
4722
5001
|
const isValueAxis = (source) => (resolveAxis(source).type ?? "y") === "y";
|
|
4723
5002
|
/** Сторона оси; отсутствие `axis.side` означает `"left"`. */
|
|
@@ -4782,161 +5061,40 @@ const fetchQueryDescription = (api, { ds, query, parameters }) => {
|
|
|
4782
5061
|
return request;
|
|
4783
5062
|
};
|
|
4784
5063
|
|
|
4785
|
-
const
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
|
|
4789
|
-
|
|
4790
|
-
|
|
4791
|
-
|
|
4792
|
-
|
|
4793
|
-
|
|
4794
|
-
|
|
4795
|
-
|
|
4796
|
-
|
|
4797
|
-
const filterName = `${FILTER_PREFIX}${name}`;
|
|
4798
|
-
// Пока фильтр не применён, границы `%name.min` / `%name.max` берём из range-дефолта конфига
|
|
4799
|
-
// (`defaultValue: [from, to]`) — иначе в условие ушёл бы весь массив вместо своей границы.
|
|
4800
|
-
// Проверка плейсхолдеров обязательна: у array-фильтра массивный дефолт подставляется целиком.
|
|
4801
|
-
const rangeDefault = !hasFilter &&
|
|
4802
|
-
Array.isArray(defaultValue) &&
|
|
4803
|
-
(condition.includes(`${filterName}.min`) || condition.includes(`${filterName}.max`))
|
|
4804
|
-
? defaultValue
|
|
4805
|
-
: null;
|
|
4806
|
-
const min = formatConditionValue({
|
|
4807
|
-
value: hasFilter ? filters[name].min : rangeDefault?.[0] ?? null,
|
|
4808
|
-
checkQuotes: !isSingle,
|
|
4809
|
-
isSetParams,
|
|
4810
|
-
});
|
|
4811
|
-
const max = formatConditionValue({
|
|
4812
|
-
value: hasFilter ? filters[name].max : rangeDefault?.[1] ?? null,
|
|
4813
|
-
checkQuotes: !isSingle,
|
|
4814
|
-
isSetParams,
|
|
4815
|
-
});
|
|
4816
|
-
const value = formatConditionValue({
|
|
4817
|
-
value: hasFilter ? filters[name].value : filters[name] === undefined ? defaultValue : "",
|
|
4818
|
-
defaultValue,
|
|
4819
|
-
checkQuotes: !isSingle,
|
|
4820
|
-
isSetParams,
|
|
4821
|
-
});
|
|
4822
|
-
const hasMin = min !== "" && min !== null && min !== undefined;
|
|
4823
|
-
const hasMax = max !== "" && max !== null && max !== undefined;
|
|
4824
|
-
let result = condition;
|
|
4825
|
-
if (hasMin || hasMax) {
|
|
4826
|
-
if (hasMin) {
|
|
4827
|
-
result = result.replace(new RegExp(`${filterName}\\.min`), min);
|
|
4828
|
-
}
|
|
4829
|
-
if (hasMax) {
|
|
4830
|
-
result = result.replace(new RegExp(`${filterName}\\.max`), max);
|
|
4831
|
-
}
|
|
4832
|
-
}
|
|
4833
|
-
else if (value) {
|
|
4834
|
-
result = result.replace(new RegExp(filterName), value);
|
|
4835
|
-
}
|
|
4836
|
-
return isSingle && isNumeric(result) ? Number(result) : result;
|
|
4837
|
-
};
|
|
4838
|
-
const applyVarsToCondition = ({ section, configFilters, filters, attributes, layerParams, eqlParameters, geometry, extent, zoomLevel, projectName, projectAlias, isSetParams, }) => {
|
|
5064
|
+
const toSources = ({ configFilters, filters, dataSources, dashboard, attributes, geometry, extent, zoomLevel, projectName, projectAlias, }) => ({
|
|
5065
|
+
widget: { selectedFilters: filters, configFilters, dataSources },
|
|
5066
|
+
dashboard,
|
|
5067
|
+
attributes,
|
|
5068
|
+
geometry,
|
|
5069
|
+
extent,
|
|
5070
|
+
zoomLevel,
|
|
5071
|
+
projectName,
|
|
5072
|
+
projectAlias,
|
|
5073
|
+
});
|
|
5074
|
+
/** Подстановки в секции условия (строке или массиве секций) — без разбора set-параметров. */
|
|
5075
|
+
const applyVarsToCondition = ({ section, isSetParams, ...params }) => {
|
|
4839
5076
|
if (!section?.length)
|
|
4840
5077
|
return [];
|
|
4841
|
-
const
|
|
4842
|
-
const
|
|
4843
|
-
|
|
4844
|
-
|
|
4845
|
-
result[index] = result[index].replace(new RegExp("%geometry"), `'${geometry}'`);
|
|
4846
|
-
}
|
|
4847
|
-
// Системные фильтры вида карты подставляются ДО цикла по `configFilters`: одноимённый
|
|
4848
|
-
// пользовательский фильтр не должен перехватывать `%extent` / `%zoom`. Lookahead `(?![\w.])`
|
|
4849
|
-
// оставляет фильтрам составные формы (`%zoomLevel`, `%extent_id`, `%zoom.min`) — та же
|
|
4850
|
-
// граница, что уже разводит `%name` и `%name2.l1` в `getUpdatingDataSources`.
|
|
4851
|
-
if (extent) {
|
|
4852
|
-
result[index] = result[index].replace(new RegExp(`${FILTER_PREFIX}${EXTENT_FILTER_NAME}(?![\\w.])`, "g"), `'${extent}'`);
|
|
4853
|
-
}
|
|
4854
|
-
// Зум — число, кавычки не ставим.
|
|
4855
|
-
if (!isNil(zoomLevel)) {
|
|
4856
|
-
result[index] = result[index].replace(new RegExp(`${FILTER_PREFIX}${ZOOM_FILTER_NAME}(?![\\w.])`, "g"), String(zoomLevel));
|
|
4857
|
-
}
|
|
4858
|
-
// Текущий проект — строковая подстановка, поэтому в кавычках, как `%geometry`. Свойства
|
|
4859
|
-
// заменяются раньше голого плейсхолдера, а граница `(?![\w.])` оставляет фильтрам составные
|
|
4860
|
-
// формы (`%project_id`) и не трогает чужое свойство (`%project.foo`).
|
|
4861
|
-
PROJECT_PROPS.forEach(prop => {
|
|
4862
|
-
const projectValue = getProjectValue({ prop, projectName, projectAlias });
|
|
4863
|
-
if (isNil(projectValue))
|
|
4864
|
-
return;
|
|
4865
|
-
result[index] = result[index].replace(new RegExp(`${FILTER_PREFIX}${PROJECT_FILTER_NAME}${prop ? `\\.${prop}` : ""}(?![\\w.])`, "g"), `'${projectValue}'`);
|
|
4866
|
-
});
|
|
4867
|
-
if (configFilters?.length) {
|
|
4868
|
-
configFilters.forEach(filter => {
|
|
4869
|
-
result[index] = applyFiltersToCondition({
|
|
4870
|
-
filters,
|
|
4871
|
-
condition: result[index],
|
|
4872
|
-
name: filter.name,
|
|
4873
|
-
defaultValue: filter.defaultValue,
|
|
4874
|
-
configFilter: filter,
|
|
4875
|
-
isSetParams,
|
|
4876
|
-
isSingle: isSingle && !Array.isArray(filter.defaultValue),
|
|
4877
|
-
}).toString();
|
|
4878
|
-
});
|
|
4879
|
-
}
|
|
4880
|
-
if (attributes?.length) {
|
|
4881
|
-
attributes.forEach(({ attributeName, value, type: attributeType }) => {
|
|
4882
|
-
result[index] = result[index].replace(new RegExp(`{${attributeName}}`), formatConditionValue({ value: value?.toString(), attributeType, checkQuotes: !isSingle }));
|
|
4883
|
-
});
|
|
4884
|
-
}
|
|
4885
|
-
if (eqlParameters && layerParams) {
|
|
4886
|
-
Object.keys(eqlParameters).forEach(param => {
|
|
4887
|
-
result[index] = result[index].replace(new RegExp(`\\$\\(${param}=`), "***");
|
|
4888
|
-
result[index] = result[index].replace(new RegExp(param), formatConditionValue({ value: layerParams[param], checkQuotes: !isSingle }));
|
|
4889
|
-
result[index] = result[index].replace(new RegExp("\\*\\*\\*"), `$(${param}=`);
|
|
4890
|
-
});
|
|
4891
|
-
}
|
|
4892
|
-
});
|
|
4893
|
-
return isSingle ? result?.[0] : result;
|
|
4894
|
-
};
|
|
4895
|
-
const formatSingleCondition = (condition, { configFilters, filters, attributes, eqlParameters, layerParams, geometry, extent, zoomLevel, projectName, projectAlias, }) => {
|
|
4896
|
-
const setParams = condition.match(new RegExp("\\$\\([^)]+\\)", "g"));
|
|
4897
|
-
const setParamsSection = applyVarsToCondition({
|
|
4898
|
-
section: setParams,
|
|
4899
|
-
configFilters,
|
|
4900
|
-
filters,
|
|
4901
|
-
attributes,
|
|
4902
|
-
eqlParameters,
|
|
4903
|
-
layerParams,
|
|
4904
|
-
geometry,
|
|
4905
|
-
extent,
|
|
4906
|
-
zoomLevel,
|
|
4907
|
-
projectName,
|
|
4908
|
-
projectAlias,
|
|
4909
|
-
isSetParams: true,
|
|
4910
|
-
});
|
|
4911
|
-
const splitter = " AND ";
|
|
4912
|
-
const setParamsLength = setParams?.join("").length || 0;
|
|
4913
|
-
const conditionSection = applyVarsToCondition({
|
|
4914
|
-
section: condition.substring(setParamsLength).split(splitter),
|
|
4915
|
-
configFilters,
|
|
4916
|
-
filters,
|
|
4917
|
-
attributes,
|
|
4918
|
-
eqlParameters,
|
|
4919
|
-
layerParams,
|
|
4920
|
-
geometry,
|
|
4921
|
-
extent,
|
|
4922
|
-
zoomLevel,
|
|
4923
|
-
projectName,
|
|
4924
|
-
projectAlias,
|
|
4925
|
-
});
|
|
4926
|
-
return setParamsSection?.length && conditionSection.length
|
|
4927
|
-
? [setParamsSection.join(""), conditionSection.join(splitter)].join(" ")
|
|
4928
|
-
: setParamsSection?.length
|
|
4929
|
-
? setParamsSection.join("")
|
|
4930
|
-
: conditionSection.join(splitter);
|
|
5078
|
+
const sources = toSources(params);
|
|
5079
|
+
const { eqlParameters, layerParams } = params;
|
|
5080
|
+
const format = (item) => formatConditionSection(item, sources, { eqlParameters, layerParams, isSetParams });
|
|
5081
|
+
return (typeof section === "string" ? format(section) : section.map(format));
|
|
4931
5082
|
};
|
|
5083
|
+
/**
|
|
5084
|
+
* Условие слоя с подстановками строк конфига: фильтры (`%filter`, `$dashboard:filter`), атрибуты
|
|
5085
|
+
* объекта, системные значения карты и проекта, eql-параметры слоя. Пустое значение фильтра
|
|
5086
|
+
* оставляет плейсхолдер нетронутым.
|
|
5087
|
+
*/
|
|
4932
5088
|
const formatDataSourceCondition = ({ condition, ...rest }) => {
|
|
5089
|
+
const sources = toSources(rest);
|
|
5090
|
+
const eql = { eqlParameters: rest.eqlParameters, layerParams: rest.layerParams };
|
|
4933
5091
|
if (Array.isArray(condition)) {
|
|
4934
|
-
return condition.map(item =>
|
|
5092
|
+
return condition.map(item => formatConditionString(item, sources, eql));
|
|
4935
5093
|
}
|
|
4936
5094
|
if (!condition) {
|
|
4937
5095
|
return "";
|
|
4938
5096
|
}
|
|
4939
|
-
return
|
|
5097
|
+
return formatConditionString(condition, sources, eql);
|
|
4940
5098
|
};
|
|
4941
5099
|
|
|
4942
5100
|
const DashboardChipsContainer = styled(Flex).withConfig({ displayName: "DashboardChipsContainer", componentId: "sc-pzlzu7" }) `
|
|
@@ -5107,10 +5265,9 @@ const noMarginMixin = css `
|
|
|
5107
5265
|
/**
|
|
5108
5266
|
* Обёртка значения элемента.
|
|
5109
5267
|
*
|
|
5110
|
-
* Контейнеры, владеющие своим корнем (`ROOT_OWNING_TEMPLATES`), сюда
|
|
5111
|
-
*
|
|
5112
|
-
* `$sizeTransparent` поэтому работает только на
|
|
5113
|
-
* на `useContainerRoot`.
|
|
5268
|
+
* Контейнеры, владеющие своим корнем (`ROOT_OWNING_TEMPLATES`), сюда не попадают — обёртка
|
|
5269
|
+
* продублировала бы их `data-id`, `data-templatename` и `style` вторым узлом DOM.
|
|
5270
|
+
* `$sizeTransparent` поэтому работает только на контейнерах без `useContainerRoot`.
|
|
5114
5271
|
*/
|
|
5115
5272
|
const ElementValueWrapper = styled.div.withConfig({ displayName: "ElementValueWrapper", componentId: "sc-x3ra9l" }) `
|
|
5116
5273
|
transition: background-color ${transition.toggle};
|
|
@@ -5139,8 +5296,8 @@ const ElementValueWrapper = styled.div.withConfig({ displayName: "ElementValueWr
|
|
|
5139
5296
|
* а доля из `options.width` достаётся только телу. Поэтому `id`, `data-templatename`, авторский
|
|
5140
5297
|
* `style` и `$sizeCss` живут здесь, а не на теле — см. `useContainerRoot`.
|
|
5141
5298
|
*
|
|
5142
|
-
* Всегда flex-колонка:
|
|
5143
|
-
*
|
|
5299
|
+
* Всегда flex-колонка: в блочной обёртке вертикальные отступы между заголовком и телом
|
|
5300
|
+
* схлопывались бы.
|
|
5144
5301
|
*/
|
|
5145
5302
|
const ContainerRoot = styled(Flex).withConfig({ displayName: "ContainerRoot", componentId: "sc-1m2pft5" }) `
|
|
5146
5303
|
flex-direction: column;
|
|
@@ -5608,6 +5765,34 @@ const getChartFilterName = (relatedDataSources) => {
|
|
|
5608
5765
|
return axes?.[0]?.filterName;
|
|
5609
5766
|
};
|
|
5610
5767
|
|
|
5768
|
+
const isValueChanged = (value, prevValue, configFilter) => {
|
|
5769
|
+
const isObjectValue = isTreeFilterValue(value, configFilter) ||
|
|
5770
|
+
isTreeFilterValue(prevValue, configFilter) ||
|
|
5771
|
+
isFeaturesFilterValue(value) ||
|
|
5772
|
+
isFeaturesFilterValue(prevValue);
|
|
5773
|
+
if (isObjectValue) {
|
|
5774
|
+
return JSON.stringify(value) !== JSON.stringify(prevValue);
|
|
5775
|
+
}
|
|
5776
|
+
if (Array.isArray(value)) {
|
|
5777
|
+
return (value.length !== prevValue?.length ||
|
|
5778
|
+
value.some((item, index) => item !== prevValue?.[index]));
|
|
5779
|
+
}
|
|
5780
|
+
return value !== prevValue;
|
|
5781
|
+
};
|
|
5782
|
+
/** Имена фильтров, чьё выбранное значение изменилось с прошлого снимка. */
|
|
5783
|
+
const getChangedFilterNames = (filters, prevFilters, configFilters) => filters
|
|
5784
|
+
? Object.keys(filters).filter(key => isValueChanged(filters[key].value, prevFilters[key]?.value, getConfigFilter(key, configFilters)))
|
|
5785
|
+
: [];
|
|
5786
|
+
/**
|
|
5787
|
+
* Имена фильтров, чей дефолт изменился (`defaultValue: "$dashboard:year"` при смене фильтра дашборда),
|
|
5788
|
+
* пока пользователь не выбрал своё значение, — для потребителей это то же, что смена значения.
|
|
5789
|
+
*/
|
|
5790
|
+
const getChangedDefaultNames = (configFilters, prevConfigFilters, filters) => (configFilters ?? [])
|
|
5791
|
+
.filter(({ name, defaultValue }) => filters?.[name] === undefined &&
|
|
5792
|
+
prevConfigFilters !== undefined &&
|
|
5793
|
+
!isEqual(defaultValue, getConfigFilter(name, prevConfigFilters)?.defaultValue))
|
|
5794
|
+
.map(({ name }) => name);
|
|
5795
|
+
|
|
5611
5796
|
function getValueIndex(items, attributes) {
|
|
5612
5797
|
return items?.findIndex(({ name }) => name.toString() === attributes.value?.toString());
|
|
5613
5798
|
}
|
|
@@ -6252,29 +6437,227 @@ const useAutoCompleteControl = (items) => {
|
|
|
6252
6437
|
.map(item => ({ text: String(item), value: String(item) })), [items]);
|
|
6253
6438
|
const onChange = useCallback((newValue) => setValue(newValue), []);
|
|
6254
6439
|
return {
|
|
6255
|
-
value,
|
|
6256
|
-
setValue,
|
|
6257
|
-
onChange,
|
|
6258
|
-
options: staticOptions.length ? staticOptions : options,
|
|
6259
|
-
setOptions,
|
|
6440
|
+
value,
|
|
6441
|
+
setValue,
|
|
6442
|
+
onChange,
|
|
6443
|
+
options: staticOptions.length ? staticOptions : options,
|
|
6444
|
+
setOptions,
|
|
6445
|
+
};
|
|
6446
|
+
};
|
|
6447
|
+
|
|
6448
|
+
/**
|
|
6449
|
+
* Пропсы хоста фонового слоя для корней, которые не берут пропсы из `useWrapperSize`
|
|
6450
|
+
* (ставят `id`/`style` руками — `Title`, `Icon`, `Tabs`, `AddFeature`, `ExportPdf`, …).
|
|
6451
|
+
*
|
|
6452
|
+
* Контейнеры на `useContainerRoot` этот хук напрямую не зовут: `useWrapperSize` вызывает его
|
|
6453
|
+
* сам и кладёт результат в `root` — так признаки хоста считаются в одном месте.
|
|
6454
|
+
*/
|
|
6455
|
+
const useBgImageHost = (elementConfig) => {
|
|
6456
|
+
const hasBgImage = useMemo(() => hasContainerBgImage(elementConfig), [elementConfig]);
|
|
6457
|
+
const innerPadding = !!elementConfig?.options?.innerPadding;
|
|
6458
|
+
return useMemo(() => ({ $hasBgImage: hasBgImage, $innerPadding: innerPadding }), [hasBgImage, innerPadding]);
|
|
6459
|
+
};
|
|
6460
|
+
|
|
6461
|
+
const getPagesFromConfig = (config) => config?.children?.[0]?.children;
|
|
6462
|
+
|
|
6463
|
+
/** Фильтры страницы `pageIndex` (с 1) вместе с общими фильтрами конфига; общие перекрывают одноимённые. */
|
|
6464
|
+
const getPageFilters = (config, pageIndex) => {
|
|
6465
|
+
const page = getPagesFromConfig(config)?.[pageIndex - 1];
|
|
6466
|
+
return [
|
|
6467
|
+
...(page?.filters?.filter(({ name }) => !config?.filters?.some(item => item.name === name)) || []),
|
|
6468
|
+
...(config?.filters || []),
|
|
6469
|
+
];
|
|
6470
|
+
};
|
|
6471
|
+
|
|
6472
|
+
/**
|
|
6473
|
+
* Generic-параметр `T` сейчас идёт как намерение (под какой виджет). Дальнейшее сужение
|
|
6474
|
+
* `config` под конкретный виджет — отдельная задача.
|
|
6475
|
+
*/
|
|
6476
|
+
const useWidgetConfig = (type = WidgetType.Dashboard) => {
|
|
6477
|
+
const { config: configProp, containerIds, projectInfo, layerInfo, isEditing } = useWidgetContext(type);
|
|
6478
|
+
const config = useMemo(() => {
|
|
6479
|
+
if (configProp) {
|
|
6480
|
+
return configProp;
|
|
6481
|
+
}
|
|
6482
|
+
if (isEditing) {
|
|
6483
|
+
return type === WidgetType.Dashboard
|
|
6484
|
+
? (projectInfo?.content?.editConfiguration || {})
|
|
6485
|
+
: (layerInfo?.configuration?.editConfiguration || {});
|
|
6486
|
+
}
|
|
6487
|
+
const currentConfig = type === WidgetType.Dashboard
|
|
6488
|
+
? (projectInfo?.content?.dashboardConfiguration || {})
|
|
6489
|
+
: (layerInfo?.configuration?.cardConfiguration || {});
|
|
6490
|
+
if (containerIds?.length) {
|
|
6491
|
+
const elements = containerIds.map(id => returnFound(currentConfig, { id })).filter(Boolean);
|
|
6492
|
+
if (!elements.length)
|
|
6493
|
+
return currentConfig;
|
|
6494
|
+
const newConfig = JSON.parse(JSON.stringify(currentConfig));
|
|
6495
|
+
const pages = getPagesFromConfig(newConfig);
|
|
6496
|
+
if (!pages?.[0]?.children)
|
|
6497
|
+
return currentConfig;
|
|
6498
|
+
pages[0].children = elements;
|
|
6499
|
+
return newConfig;
|
|
6500
|
+
}
|
|
6501
|
+
return currentConfig;
|
|
6502
|
+
}, [configProp, containerIds, layerInfo?.configuration?.cardConfiguration, projectInfo?.content?.dashboardConfiguration, type, isEditing]);
|
|
6503
|
+
const pages = useMemo(() => (getPagesFromConfig(config) || []), [config]);
|
|
6504
|
+
return {
|
|
6505
|
+
config,
|
|
6506
|
+
pages,
|
|
6507
|
+
};
|
|
6508
|
+
};
|
|
6509
|
+
|
|
6510
|
+
/**
|
|
6511
|
+
* Фильтры ближайшего дашборда для `$dashboard:` в строках конфига виджета `type`. На самом
|
|
6512
|
+
* дашборде стор не нужен (`undefined`): `$dashboard:name` там равен `%name`.
|
|
6513
|
+
*
|
|
6514
|
+
* Страница дашборда собирается напрямую из конфига, а не через `useWidgetPage`: тот сам зовёт этот хук.
|
|
6515
|
+
*/
|
|
6516
|
+
const useDashboardFilterStore = (type) => {
|
|
6517
|
+
const { filters, dataSources, pageIndex } = useWidgetContext(WidgetType.Dashboard);
|
|
6518
|
+
const { config } = useWidgetConfig(WidgetType.Dashboard);
|
|
6519
|
+
const isDashboard = type === WidgetType.Dashboard;
|
|
6520
|
+
const configFilters = useMemo(() => (isDashboard ? undefined : getPageFilters(config, pageIndex)), [config, isDashboard, pageIndex]);
|
|
6521
|
+
return useMemo(() => (isDashboard ? undefined : { selectedFilters: filters, configFilters, dataSources }), [configFilters, dataSources, filters, isDashboard]);
|
|
6522
|
+
};
|
|
6523
|
+
|
|
6524
|
+
/**
|
|
6525
|
+
* Generic-параметр `T` — намерение под виджет. Дальнейшая параметризация — отдельная задача.
|
|
6526
|
+
*
|
|
6527
|
+
* Дефолты фильтров не-дашборда (карточки) с `$dashboard:` приходят уже разрезолвленными из фильтров
|
|
6528
|
+
* дашборда. Страница дашборда отдаётся как есть: её `currentPage` редактор пишет обратно в конфиг.
|
|
6529
|
+
*/
|
|
6530
|
+
const useWidgetPage = (type = WidgetType.Dashboard) => {
|
|
6531
|
+
const { t } = useGlobalContext();
|
|
6532
|
+
const { pageIndex, changePage, projectInfo, updateProject } = useWidgetContext(type);
|
|
6533
|
+
const { config } = useWidgetConfig(type);
|
|
6534
|
+
const dashboardFilterStore = useDashboardFilterStore(type);
|
|
6535
|
+
const pageFilters = useMemo(() => getPageFilters(config, pageIndex), [config, pageIndex]);
|
|
6536
|
+
// Стор дашборда меняется на каждый его фильтр: без ссылок на `$dashboard:` страница от него не зависит.
|
|
6537
|
+
const defaultsStore = useMemo(() => (pageFilters.some(({ defaultValue }) => isDashboardReference(defaultValue)) ? dashboardFilterStore : undefined), [dashboardFilterStore, pageFilters]);
|
|
6538
|
+
const resolvedFilters = useMemo(() => resolveFilterDefaults(pageFilters, defaultsStore), [defaultsStore, pageFilters]);
|
|
6539
|
+
const filtersRef = useRef(resolvedFilters);
|
|
6540
|
+
// Стор дашборда пересобирается и на загрузку его датасорсов — страницу меняют только новые дефолты.
|
|
6541
|
+
if (!isEqual(filtersRef.current, resolvedFilters)) {
|
|
6542
|
+
filtersRef.current = resolvedFilters;
|
|
6543
|
+
}
|
|
6544
|
+
const filters = filtersRef.current;
|
|
6545
|
+
const currentPage = useMemo(() => {
|
|
6546
|
+
const page = getPagesFromConfig(config)?.[pageIndex - 1];
|
|
6547
|
+
return {
|
|
6548
|
+
...(page || {}),
|
|
6549
|
+
header: page?.header || config.header,
|
|
6550
|
+
dataSources: [
|
|
6551
|
+
...(page?.dataSources?.filter(({ name }) => !config.dataSources?.some(item => item.name === name)) || []),
|
|
6552
|
+
...(config.dataSources || []),
|
|
6553
|
+
],
|
|
6554
|
+
filters,
|
|
6555
|
+
layers: page?.layers?.map((layer) => ({
|
|
6556
|
+
...layer,
|
|
6557
|
+
isVisible: layer.isVisible ?? true,
|
|
6558
|
+
})) || [],
|
|
6559
|
+
};
|
|
6560
|
+
}, [config, pageIndex, filters]);
|
|
6561
|
+
const updateConfigPage = useCallback((newConfig) => {
|
|
6562
|
+
const newProjectInfo = JSON.parse(JSON.stringify(projectInfo));
|
|
6563
|
+
const pages = getPagesFromProjectInfo(newProjectInfo);
|
|
6564
|
+
if (!pages?.[pageIndex - 1])
|
|
6565
|
+
return;
|
|
6566
|
+
pages[pageIndex - 1] = { ...pages[pageIndex - 1], ...newConfig };
|
|
6567
|
+
updateProject(newProjectInfo);
|
|
6568
|
+
}, [projectInfo, pageIndex, updateProject]);
|
|
6569
|
+
const addConfigPage = useCallback(() => {
|
|
6570
|
+
const newProjectInfo = JSON.parse(JSON.stringify(projectInfo));
|
|
6571
|
+
const pages = getPagesFromProjectInfo(newProjectInfo);
|
|
6572
|
+
if (!pages)
|
|
6573
|
+
return;
|
|
6574
|
+
pages.push(createConfigPage({ pages, defaultTitle: t("page", { ns: "dashboard", defaultValue: "Страница" }) }));
|
|
6575
|
+
updateProject(newProjectInfo);
|
|
6576
|
+
changePage(pages.length);
|
|
6577
|
+
}, [changePage, projectInfo, t, updateProject]);
|
|
6578
|
+
const deleteConfigPage = useCallback((index) => {
|
|
6579
|
+
const newProjectInfo = JSON.parse(JSON.stringify(projectInfo));
|
|
6580
|
+
const pages = getPagesFromProjectInfo(newProjectInfo);
|
|
6581
|
+
if (!pages?.[index] || pages.length === 1)
|
|
6582
|
+
return;
|
|
6583
|
+
pages.splice(index, 1);
|
|
6584
|
+
updateProject(newProjectInfo);
|
|
6585
|
+
if (!pages[pageIndex]) {
|
|
6586
|
+
changePage(pageIndex > 1 ? pageIndex - 1 : 1);
|
|
6587
|
+
}
|
|
6588
|
+
}, [changePage, pageIndex, projectInfo, updateProject]);
|
|
6589
|
+
const updateConfigLayer = useCallback((name, newProps) => {
|
|
6590
|
+
const newProjectInfo = JSON.parse(JSON.stringify(projectInfo));
|
|
6591
|
+
const pages = getPagesFromProjectInfo(newProjectInfo);
|
|
6592
|
+
const page = pages?.[pageIndex - 1];
|
|
6593
|
+
const index = page?.layers?.findIndex(layer => layer.name === name) ?? -1;
|
|
6594
|
+
if (index === -1)
|
|
6595
|
+
return;
|
|
6596
|
+
page.layers[index] = { ...page.layers[index], ...newProps };
|
|
6597
|
+
updateProject(newProjectInfo);
|
|
6598
|
+
}, [projectInfo, pageIndex, updateProject]);
|
|
6599
|
+
const updateConfigLayers = useCallback((props) => {
|
|
6600
|
+
const newProjectInfo = JSON.parse(JSON.stringify(projectInfo));
|
|
6601
|
+
const pages = getPagesFromProjectInfo(newProjectInfo);
|
|
6602
|
+
const page = pages?.[pageIndex - 1];
|
|
6603
|
+
if (!page?.layers?.length)
|
|
6604
|
+
return;
|
|
6605
|
+
page.layers = page.layers.map(layer => ({
|
|
6606
|
+
...layer,
|
|
6607
|
+
...(props[layer.name] || {}),
|
|
6608
|
+
}));
|
|
6609
|
+
updateProject(newProjectInfo);
|
|
6610
|
+
}, [projectInfo, pageIndex, updateProject]);
|
|
6611
|
+
return {
|
|
6612
|
+
pageIndex,
|
|
6613
|
+
currentPage,
|
|
6614
|
+
changePage,
|
|
6615
|
+
updateConfigPage,
|
|
6616
|
+
addConfigPage,
|
|
6617
|
+
deleteConfigPage,
|
|
6618
|
+
updateConfigLayer,
|
|
6619
|
+
updateConfigLayers,
|
|
6260
6620
|
};
|
|
6261
6621
|
};
|
|
6262
6622
|
|
|
6263
6623
|
/**
|
|
6264
|
-
*
|
|
6265
|
-
*
|
|
6266
|
-
*
|
|
6267
|
-
* Контейнеры на `useContainerRoot` этот хук напрямую не зовут: `useWrapperSize` вызывает его
|
|
6268
|
-
* сам и кладёт результат в `root` — так признаки хоста считаются в одном месте.
|
|
6624
|
+
* Все источники подстановок строк конфига для виджета `type`: его фильтры (`%`), фильтры и
|
|
6625
|
+
* датасорсы дашборда (`$dashboard:`), атрибуты объекта, вид карты и проект.
|
|
6626
|
+
* Атрибуты — объекта виджета; строки `DataSource` подменяют их своими при рендере.
|
|
6269
6627
|
*/
|
|
6270
|
-
const
|
|
6271
|
-
const
|
|
6272
|
-
const
|
|
6273
|
-
|
|
6628
|
+
const useConfigStringSources = (type) => {
|
|
6629
|
+
const { ewktGeometry, ewktExtent, zoomLevel, projectName, projectAlias } = useGlobalContext();
|
|
6630
|
+
const { filters, dataSources, attributes, layerInfo } = useWidgetContext(type);
|
|
6631
|
+
const { currentPage } = useWidgetPage(type);
|
|
6632
|
+
const dashboard = useDashboardFilterStore(type);
|
|
6633
|
+
return useMemo(() => ({
|
|
6634
|
+
widget: { selectedFilters: filters, configFilters: currentPage?.filters, dataSources },
|
|
6635
|
+
dashboard,
|
|
6636
|
+
attributes,
|
|
6637
|
+
layerInfo,
|
|
6638
|
+
geometry: ewktGeometry,
|
|
6639
|
+
extent: ewktExtent,
|
|
6640
|
+
zoomLevel,
|
|
6641
|
+
projectName,
|
|
6642
|
+
projectAlias,
|
|
6643
|
+
}), [
|
|
6644
|
+
attributes,
|
|
6645
|
+
currentPage?.filters,
|
|
6646
|
+
dashboard,
|
|
6647
|
+
dataSources,
|
|
6648
|
+
ewktExtent,
|
|
6649
|
+
ewktGeometry,
|
|
6650
|
+
filters,
|
|
6651
|
+
layerInfo,
|
|
6652
|
+
projectAlias,
|
|
6653
|
+
projectName,
|
|
6654
|
+
zoomLevel,
|
|
6655
|
+
]);
|
|
6274
6656
|
};
|
|
6275
6657
|
|
|
6276
6658
|
const useRenderContainerItem = (type, renderElement) => {
|
|
6277
6659
|
const { config, layerInfo, selectedTabId, attributes } = useWidgetContext(type);
|
|
6660
|
+
const configStringSources = useConfigStringSources(type);
|
|
6278
6661
|
return useCallback((elementConfig, attribute) => {
|
|
6279
6662
|
const { id, options, style, children } = elementConfig || {};
|
|
6280
6663
|
const { hideEmpty, innerTemplateStyle } = options || {};
|
|
@@ -6307,6 +6690,7 @@ const useRenderContainerItem = (type, renderElement) => {
|
|
|
6307
6690
|
attributes,
|
|
6308
6691
|
layerInfo,
|
|
6309
6692
|
type,
|
|
6693
|
+
configStringSources,
|
|
6310
6694
|
})
|
|
6311
6695
|
: renderElement;
|
|
6312
6696
|
const value = render({ id: "value" });
|
|
@@ -6319,7 +6703,7 @@ const useRenderContainerItem = (type, renderElement) => {
|
|
|
6319
6703
|
hasUnits,
|
|
6320
6704
|
render,
|
|
6321
6705
|
};
|
|
6322
|
-
}, [config, layerInfo, selectedTabId, attributes, type, renderElement]);
|
|
6706
|
+
}, [config, layerInfo, selectedTabId, attributes, type, renderElement, configStringSources]);
|
|
6323
6707
|
};
|
|
6324
6708
|
|
|
6325
6709
|
const customModes = MapboxDraw.modes;
|
|
@@ -7069,8 +7453,8 @@ const useContainerAttributes = ({ elementConfig, type, renderElement }) => {
|
|
|
7069
7453
|
* Размеры уходят styled-пропом (класс), а не inline-стилем, поэтому перебиваются снаружи
|
|
7070
7454
|
* без `!important`. Авторский `style` остаётся inline — у него приоритет по замыслу автора конфига.
|
|
7071
7455
|
*
|
|
7072
|
-
* `data-id` и `$noMargin`
|
|
7073
|
-
* на второй узел поверх
|
|
7456
|
+
* `data-id` и `$noMargin` живут на самом корне: внешняя обёртка `ElementValueWrapper` повесила бы
|
|
7457
|
+
* их на второй узел поверх него, дублируя стили, поэтому для таких контейнеров она не создаётся
|
|
7074
7458
|
* (см. `ROOT_OWNING_TEMPLATES`).
|
|
7075
7459
|
*
|
|
7076
7460
|
* Пропсы хоста фонового слоя (`$hasBgImage`, `$innerPadding`) подмешиваются здесь, а не в каждом
|
|
@@ -7483,132 +7867,6 @@ const getModalsDataSources = (config, modalIds) => uniqBy((config?.modals ?? [])
|
|
|
7483
7867
|
.filter(({ id }) => !modalIds || modalIds.includes(id))
|
|
7484
7868
|
.flatMap(({ dataSources }) => dataSources ?? []), "name");
|
|
7485
7869
|
|
|
7486
|
-
/**
|
|
7487
|
-
* Generic-параметр `T` сейчас идёт как намерение (под какой виджет). Дальнейшее сужение
|
|
7488
|
-
* `config` под конкретный виджет — отдельная задача.
|
|
7489
|
-
*/
|
|
7490
|
-
const useWidgetConfig = (type = WidgetType.Dashboard) => {
|
|
7491
|
-
const { config: configProp, containerIds, projectInfo, layerInfo, isEditing } = useWidgetContext(type);
|
|
7492
|
-
const config = useMemo(() => {
|
|
7493
|
-
if (configProp) {
|
|
7494
|
-
return configProp;
|
|
7495
|
-
}
|
|
7496
|
-
if (isEditing) {
|
|
7497
|
-
return type === WidgetType.Dashboard
|
|
7498
|
-
? (projectInfo?.content?.editConfiguration || {})
|
|
7499
|
-
: (layerInfo?.configuration?.editConfiguration || {});
|
|
7500
|
-
}
|
|
7501
|
-
const currentConfig = type === WidgetType.Dashboard
|
|
7502
|
-
? (projectInfo?.content?.dashboardConfiguration || {})
|
|
7503
|
-
: (layerInfo?.configuration?.cardConfiguration || {});
|
|
7504
|
-
if (containerIds?.length) {
|
|
7505
|
-
const elements = containerIds.map(id => returnFound(currentConfig, { id })).filter(Boolean);
|
|
7506
|
-
if (!elements.length)
|
|
7507
|
-
return currentConfig;
|
|
7508
|
-
const newConfig = JSON.parse(JSON.stringify(currentConfig));
|
|
7509
|
-
const pages = getPagesFromConfig(newConfig);
|
|
7510
|
-
if (!pages?.[0]?.children)
|
|
7511
|
-
return currentConfig;
|
|
7512
|
-
pages[0].children = elements;
|
|
7513
|
-
return newConfig;
|
|
7514
|
-
}
|
|
7515
|
-
return currentConfig;
|
|
7516
|
-
}, [configProp, containerIds, layerInfo?.configuration?.cardConfiguration, projectInfo?.content?.dashboardConfiguration, type, isEditing]);
|
|
7517
|
-
const pages = useMemo(() => (getPagesFromConfig(config) || []), [config]);
|
|
7518
|
-
return {
|
|
7519
|
-
config,
|
|
7520
|
-
pages,
|
|
7521
|
-
};
|
|
7522
|
-
};
|
|
7523
|
-
|
|
7524
|
-
/**
|
|
7525
|
-
* Generic-параметр `T` — намерение под виджет. Дальнейшая параметризация — отдельная задача.
|
|
7526
|
-
*/
|
|
7527
|
-
const useWidgetPage = (type = WidgetType.Dashboard) => {
|
|
7528
|
-
const { t } = useGlobalContext();
|
|
7529
|
-
const { pageIndex, changePage, projectInfo, updateProject } = useWidgetContext(type);
|
|
7530
|
-
const { config } = useWidgetConfig(type);
|
|
7531
|
-
const currentPage = useMemo(() => {
|
|
7532
|
-
const page = getPagesFromConfig(config)?.[pageIndex - 1];
|
|
7533
|
-
return {
|
|
7534
|
-
...(page || {}),
|
|
7535
|
-
header: page?.header || config.header,
|
|
7536
|
-
dataSources: [
|
|
7537
|
-
...(page?.dataSources?.filter(({ name }) => !config.dataSources?.some(item => item.name === name)) || []),
|
|
7538
|
-
...(config.dataSources || [])
|
|
7539
|
-
],
|
|
7540
|
-
filters: [
|
|
7541
|
-
...(page?.filters?.filter(({ name }) => !config.filters?.some(item => item.name === name)) || []),
|
|
7542
|
-
...(config.filters || [])
|
|
7543
|
-
],
|
|
7544
|
-
layers: page?.layers?.map((layer) => ({
|
|
7545
|
-
...layer,
|
|
7546
|
-
isVisible: layer.isVisible ?? true
|
|
7547
|
-
})) || []
|
|
7548
|
-
};
|
|
7549
|
-
}, [config, pageIndex]);
|
|
7550
|
-
const updateConfigPage = useCallback((newConfig) => {
|
|
7551
|
-
const newProjectInfo = JSON.parse(JSON.stringify(projectInfo));
|
|
7552
|
-
const pages = getPagesFromProjectInfo(newProjectInfo);
|
|
7553
|
-
if (!pages?.[pageIndex - 1])
|
|
7554
|
-
return;
|
|
7555
|
-
pages[pageIndex - 1] = { ...pages[pageIndex - 1], ...newConfig };
|
|
7556
|
-
updateProject(newProjectInfo);
|
|
7557
|
-
}, [projectInfo, pageIndex, updateProject]);
|
|
7558
|
-
const addConfigPage = useCallback(() => {
|
|
7559
|
-
const newProjectInfo = JSON.parse(JSON.stringify(projectInfo));
|
|
7560
|
-
const pages = getPagesFromProjectInfo(newProjectInfo);
|
|
7561
|
-
if (!pages)
|
|
7562
|
-
return;
|
|
7563
|
-
pages.push(createConfigPage({ pages, defaultTitle: t("page", { ns: "dashboard", defaultValue: "Страница" }) }));
|
|
7564
|
-
updateProject(newProjectInfo);
|
|
7565
|
-
changePage(pages.length);
|
|
7566
|
-
}, [changePage, projectInfo, t, updateProject]);
|
|
7567
|
-
const deleteConfigPage = useCallback((index) => {
|
|
7568
|
-
const newProjectInfo = JSON.parse(JSON.stringify(projectInfo));
|
|
7569
|
-
const pages = getPagesFromProjectInfo(newProjectInfo);
|
|
7570
|
-
if (!pages?.[index] || pages.length === 1)
|
|
7571
|
-
return;
|
|
7572
|
-
pages.splice(index, 1);
|
|
7573
|
-
updateProject(newProjectInfo);
|
|
7574
|
-
if (!pages[pageIndex]) {
|
|
7575
|
-
changePage(pageIndex > 1 ? pageIndex - 1 : 1);
|
|
7576
|
-
}
|
|
7577
|
-
}, [changePage, pageIndex, projectInfo, updateProject]);
|
|
7578
|
-
const updateConfigLayer = useCallback((name, newProps) => {
|
|
7579
|
-
const newProjectInfo = JSON.parse(JSON.stringify(projectInfo));
|
|
7580
|
-
const pages = getPagesFromProjectInfo(newProjectInfo);
|
|
7581
|
-
const page = pages?.[pageIndex - 1];
|
|
7582
|
-
const index = page?.layers?.findIndex(layer => layer.name === name) ?? -1;
|
|
7583
|
-
if (index === -1)
|
|
7584
|
-
return;
|
|
7585
|
-
page.layers[index] = { ...page.layers[index], ...newProps };
|
|
7586
|
-
updateProject(newProjectInfo);
|
|
7587
|
-
}, [projectInfo, pageIndex, updateProject]);
|
|
7588
|
-
const updateConfigLayers = useCallback((props) => {
|
|
7589
|
-
const newProjectInfo = JSON.parse(JSON.stringify(projectInfo));
|
|
7590
|
-
const pages = getPagesFromProjectInfo(newProjectInfo);
|
|
7591
|
-
const page = pages?.[pageIndex - 1];
|
|
7592
|
-
if (!page?.layers?.length)
|
|
7593
|
-
return;
|
|
7594
|
-
page.layers = page.layers.map(layer => ({
|
|
7595
|
-
...layer,
|
|
7596
|
-
...(props[layer.name] || {}),
|
|
7597
|
-
}));
|
|
7598
|
-
updateProject(newProjectInfo);
|
|
7599
|
-
}, [projectInfo, pageIndex, updateProject]);
|
|
7600
|
-
return {
|
|
7601
|
-
pageIndex,
|
|
7602
|
-
currentPage,
|
|
7603
|
-
changePage,
|
|
7604
|
-
updateConfigPage,
|
|
7605
|
-
addConfigPage,
|
|
7606
|
-
deleteConfigPage,
|
|
7607
|
-
updateConfigLayer,
|
|
7608
|
-
updateConfigLayers,
|
|
7609
|
-
};
|
|
7610
|
-
};
|
|
7611
|
-
|
|
7612
7870
|
/**
|
|
7613
7871
|
* Конфиги всех источников, доступных контейнерам виджета: страница с корнем и все модалки.
|
|
7614
7872
|
* Одноимённый источник модалки перекрывается страничным. Только для поиска по имени — в конфиг
|
|
@@ -7709,6 +7967,7 @@ const useHeaderRender = (elementConfig, type = WidgetType.Dashboard) => {
|
|
|
7709
7967
|
const { layerInfo, attributes, expandedContainers, selectedTabId, setSelectedTabId, } = useWidgetContext(type);
|
|
7710
7968
|
const { config } = useWidgetConfig(type);
|
|
7711
7969
|
const { pageIndex } = useWidgetPage(type);
|
|
7970
|
+
const configStringSources = useConfigStringSources(type);
|
|
7712
7971
|
return useMemo(() => getRenderElement({
|
|
7713
7972
|
config,
|
|
7714
7973
|
elementConfig,
|
|
@@ -7718,8 +7977,10 @@ const useHeaderRender = (elementConfig, type = WidgetType.Dashboard) => {
|
|
|
7718
7977
|
selectedTabId,
|
|
7719
7978
|
setSelectedTabId,
|
|
7720
7979
|
pageIndex,
|
|
7980
|
+
configStringSources,
|
|
7721
7981
|
}), [
|
|
7722
7982
|
config,
|
|
7983
|
+
configStringSources,
|
|
7723
7984
|
elementConfig,
|
|
7724
7985
|
attributes,
|
|
7725
7986
|
layerInfo,
|
|
@@ -7777,9 +8038,11 @@ const useDataSourceLoading = (type) => {
|
|
|
7777
8038
|
const useDataSources = ({ type: widgetType, config, attributes, filters, layerParams, eqlParameters, }) => {
|
|
7778
8039
|
const { ewktGeometry, ewktExtent, zoomLevel, projectName, projectAlias, api } = useGlobalContext();
|
|
7779
8040
|
const { dataSources, layerInfo } = useWidgetContext(widgetType);
|
|
7780
|
-
const
|
|
8041
|
+
const dashboard = useDashboardFilterStore(widgetType ?? WidgetType.Dashboard);
|
|
7781
8042
|
const { filters: configFilters, dataSources: configDataSources } = config || {};
|
|
7782
8043
|
const prevFilters = useRef({});
|
|
8044
|
+
const prevDashboardFilters = useRef(dashboard?.selectedFilters ?? {});
|
|
8045
|
+
const prevConfigFilters = useRef(configFilters);
|
|
7783
8046
|
const inFlightRequests = useRef(new Map());
|
|
7784
8047
|
const getDataSourcePromises = useCallback(async ({ ds, query, parameters, layerName, limit, condition, url, resourceId, fileName, methodName, }, newFilters, offset = 0) => {
|
|
7785
8048
|
// Дедуп in-flight по сигнатуре резолвленного запроса: если идентичный
|
|
@@ -7809,7 +8072,7 @@ const useDataSources = ({ type: widgetType, config, attributes, filters, layerPa
|
|
|
7809
8072
|
projectAlias,
|
|
7810
8073
|
layerInfo,
|
|
7811
8074
|
dataSources,
|
|
7812
|
-
|
|
8075
|
+
dashboard,
|
|
7813
8076
|
});
|
|
7814
8077
|
if (resourceId) {
|
|
7815
8078
|
const newSignature = JSON.stringify(["python", resourceId, newParams, methodName, fileName]);
|
|
@@ -7880,6 +8143,8 @@ const useDataSources = ({ type: widgetType, config, attributes, filters, layerPa
|
|
|
7880
8143
|
condition,
|
|
7881
8144
|
configFilters,
|
|
7882
8145
|
filters: selectedFilters,
|
|
8146
|
+
dataSources,
|
|
8147
|
+
dashboard,
|
|
7883
8148
|
geometry: ewktGeometry,
|
|
7884
8149
|
extent: ewktExtent,
|
|
7885
8150
|
zoomLevel,
|
|
@@ -7914,50 +8179,34 @@ const useDataSources = ({ type: widgetType, config, attributes, filters, layerPa
|
|
|
7914
8179
|
attributes,
|
|
7915
8180
|
layerParams,
|
|
7916
8181
|
layerInfo,
|
|
7917
|
-
projectDataSources,
|
|
7918
8182
|
eqlParameters,
|
|
8183
|
+
dashboard,
|
|
7919
8184
|
]);
|
|
8185
|
+
/**
|
|
8186
|
+
* Датасорсы, которые нужно перезапросить: их параметры ссылаются на изменившийся фильтр виджета
|
|
8187
|
+
* (`%name`, в том числе через дефолт `$dashboard:`) или дашборда (`$dashboard:name`).
|
|
8188
|
+
* Датасорсы слоя без `query` перезапрашиваются на любое изменение.
|
|
8189
|
+
*/
|
|
7920
8190
|
const getUpdatingDataSources = useCallback(() => {
|
|
7921
|
-
const
|
|
7922
|
-
|
|
7923
|
-
|
|
7924
|
-
|
|
7925
|
-
|
|
7926
|
-
|
|
7927
|
-
|
|
7928
|
-
|
|
7929
|
-
|
|
7930
|
-
|
|
7931
|
-
|
|
7932
|
-
|
|
7933
|
-
if (Array.isArray(value)) {
|
|
7934
|
-
return (value.length !== prevValue?.length ||
|
|
7935
|
-
value.some((item, index) => item !== prevValue?.[index]));
|
|
7936
|
-
}
|
|
7937
|
-
return value !== prevValue;
|
|
7938
|
-
})
|
|
7939
|
-
: [];
|
|
7940
|
-
if (!diffFilterNames.length)
|
|
7941
|
-
return;
|
|
7942
|
-
const filteredDataSources = configDataSources?.filter(({ parameters = {}, query }) => isNil(query) ||
|
|
7943
|
-
diffFilterNames.some(diffFilterName => {
|
|
7944
|
-
// Плейсхолдер параметра может ссылаться на фильтр целиком (`%name`)
|
|
7945
|
-
// либо на его свойство — уровень tree-фильтра или границу range
|
|
7946
|
-
// (`%name.lN`, `%name.min`, `%name.max`). Граница `.` обязательна,
|
|
7947
|
-
// иначе `%name` ложно матчит `%name2.l1`.
|
|
7948
|
-
const placeholder = `${FILTER_PREFIX}${diffFilterName}`;
|
|
7949
|
-
return Object.keys(parameters).some(paramName => {
|
|
7950
|
-
const paramValue = parameters[paramName];
|
|
7951
|
-
return (typeof paramValue === "string" &&
|
|
7952
|
-
(paramValue === placeholder || paramValue.startsWith(`${placeholder}.`)));
|
|
7953
|
-
});
|
|
7954
|
-
}));
|
|
7955
|
-
if (!filteredDataSources?.length) {
|
|
8191
|
+
const filteredDataSources = selectUpdatingDataSources({
|
|
8192
|
+
configDataSources,
|
|
8193
|
+
filters,
|
|
8194
|
+
configFilters,
|
|
8195
|
+
dashboard,
|
|
8196
|
+
prev: {
|
|
8197
|
+
filters: prevFilters.current,
|
|
8198
|
+
configFilters: prevConfigFilters.current,
|
|
8199
|
+
dashboardFilters: prevDashboardFilters.current,
|
|
8200
|
+
},
|
|
8201
|
+
});
|
|
8202
|
+
if (!filteredDataSources) {
|
|
7956
8203
|
return;
|
|
7957
8204
|
}
|
|
7958
8205
|
prevFilters.current = JSON.parse(JSON.stringify(filters));
|
|
8206
|
+
prevDashboardFilters.current = JSON.parse(JSON.stringify(dashboard?.selectedFilters ?? {}));
|
|
8207
|
+
prevConfigFilters.current = configFilters;
|
|
7959
8208
|
return filteredDataSources;
|
|
7960
|
-
}, [configDataSources, configFilters, filters]);
|
|
8209
|
+
}, [configDataSources, configFilters, dashboard, filters]);
|
|
7961
8210
|
const getUpdatedDataSources = useCallback((responses, currentDataSources, otherDataSources) => {
|
|
7962
8211
|
const newDataSources = JSON.parse(JSON.stringify([...currentDataSources, ...otherDataSources]));
|
|
7963
8212
|
responses.forEach((response, index) => {
|
|
@@ -8299,8 +8548,9 @@ const useExportPdf = (id, margin = 20) => {
|
|
|
8299
8548
|
const useSavePrototypeBuilder = () => {
|
|
8300
8549
|
const { ewktGeometry } = useGlobalContext();
|
|
8301
8550
|
const { layerInfo, attributes, dataSources, filters: selectedFilters, } = useWidgetContext(WidgetType.FeatureCard);
|
|
8302
|
-
const { projectInfo
|
|
8551
|
+
const { projectInfo } = useWidgetContext(WidgetType.Dashboard);
|
|
8303
8552
|
const { currentPage } = useWidgetPage(WidgetType.FeatureCard);
|
|
8553
|
+
const dashboard = useDashboardFilterStore(WidgetType.FeatureCard);
|
|
8304
8554
|
return useCallback((hook, input) => {
|
|
8305
8555
|
const resolvedParameters = applyQueryFilters({
|
|
8306
8556
|
parameters: hook.parameters ?? {},
|
|
@@ -8310,7 +8560,7 @@ const useSavePrototypeBuilder = () => {
|
|
|
8310
8560
|
attributes,
|
|
8311
8561
|
layerInfo,
|
|
8312
8562
|
dataSources,
|
|
8313
|
-
|
|
8563
|
+
dashboard,
|
|
8314
8564
|
});
|
|
8315
8565
|
const scriptParameters = {
|
|
8316
8566
|
projectName: projectInfo?.name,
|
|
@@ -8343,10 +8593,10 @@ const useSavePrototypeBuilder = () => {
|
|
|
8343
8593
|
}, [
|
|
8344
8594
|
attributes,
|
|
8345
8595
|
currentPage?.filters,
|
|
8596
|
+
dashboard,
|
|
8346
8597
|
dataSources,
|
|
8347
8598
|
ewktGeometry,
|
|
8348
8599
|
layerInfo,
|
|
8349
|
-
projectDataSources,
|
|
8350
8600
|
projectInfo?.name,
|
|
8351
8601
|
selectedFilters,
|
|
8352
8602
|
]);
|
|
@@ -8568,6 +8818,7 @@ const useRenderElement = (type = WidgetType.Dashboard, elementConfig) => {
|
|
|
8568
8818
|
const { selectedTabId, setSelectedTabId, expandedContainers, layerInfo, attributes } = useWidgetContext(type);
|
|
8569
8819
|
const { config } = useWidgetConfig(type);
|
|
8570
8820
|
const { pageIndex } = useWidgetPage(type);
|
|
8821
|
+
const configStringSources = useConfigStringSources(type);
|
|
8571
8822
|
return useMemo(() => getRenderElement({
|
|
8572
8823
|
t,
|
|
8573
8824
|
type,
|
|
@@ -8579,9 +8830,11 @@ const useRenderElement = (type = WidgetType.Dashboard, elementConfig) => {
|
|
|
8579
8830
|
pageIndex,
|
|
8580
8831
|
selectedTabId,
|
|
8581
8832
|
setSelectedTabId,
|
|
8833
|
+
configStringSources,
|
|
8582
8834
|
}), [
|
|
8583
8835
|
attributes,
|
|
8584
8836
|
config,
|
|
8837
|
+
configStringSources,
|
|
8585
8838
|
elementConfig,
|
|
8586
8839
|
expandedContainers,
|
|
8587
8840
|
layerInfo,
|
|
@@ -9085,9 +9338,8 @@ const LinkDialogContent = styled.div.withConfig({ displayName: "LinkDialogConten
|
|
|
9085
9338
|
* Заглушка недоступного изображения.
|
|
9086
9339
|
*
|
|
9087
9340
|
* Масштаб значка считается от размера самого превью: `font-size` контейнера приравнен к нему, а
|
|
9088
|
-
* бокс значка и его глиф заданы одной долей от этого размера.
|
|
9089
|
-
*
|
|
9090
|
-
* бокса и съезжал относительно квадрата.
|
|
9341
|
+
* бокс значка и его глиф заданы одной долей от этого размера. Фиксированный глиф в мелком превью
|
|
9342
|
+
* (`1.5rem` в списке) вышел бы крупнее своего бокса и съехал относительно квадрата.
|
|
9091
9343
|
*/
|
|
9092
9344
|
const ImagePreviewError = styled.div.withConfig({ displayName: "ImagePreviewError", componentId: "sc-7cwrhm" }) `
|
|
9093
9345
|
display: flex;
|
|
@@ -9659,6 +9911,7 @@ const DataSourceInnerContainer = memo(({ config, elementConfig, feature, maxValu
|
|
|
9659
9911
|
valueElement?.attributeName,
|
|
9660
9912
|
]);
|
|
9661
9913
|
const { hasAnyFilter, isFiltered, onFilter } = useWidgetFilters(type, filterName, data);
|
|
9914
|
+
const configStringSources = useConfigStringSources(type);
|
|
9662
9915
|
const render = useMemo(() => getRenderElement({
|
|
9663
9916
|
config,
|
|
9664
9917
|
elementConfig,
|
|
@@ -9669,7 +9922,19 @@ const DataSourceInnerContainer = memo(({ config, elementConfig, feature, maxValu
|
|
|
9669
9922
|
setSelectedTabId,
|
|
9670
9923
|
pageIndex,
|
|
9671
9924
|
type,
|
|
9672
|
-
|
|
9925
|
+
configStringSources,
|
|
9926
|
+
}), [
|
|
9927
|
+
config,
|
|
9928
|
+
elementConfig,
|
|
9929
|
+
attributes,
|
|
9930
|
+
layerInfo,
|
|
9931
|
+
expandedContainers,
|
|
9932
|
+
selectedTabId,
|
|
9933
|
+
setSelectedTabId,
|
|
9934
|
+
pageIndex,
|
|
9935
|
+
type,
|
|
9936
|
+
configStringSources,
|
|
9937
|
+
]);
|
|
9673
9938
|
if (!InnerContainer) {
|
|
9674
9939
|
return null;
|
|
9675
9940
|
}
|
|
@@ -10022,8 +10287,8 @@ const TreeFilterContainer = styled.div.withConfig({ displayName: "TreeFilterCont
|
|
|
10022
10287
|
}
|
|
10023
10288
|
`;
|
|
10024
10289
|
const TextFilterContainer = styled.div.withConfig({ displayName: "TextFilterContainer", componentId: "sc-1cbkxpd" }) `
|
|
10025
|
-
/* Ширину задаёт контейнер
|
|
10026
|
-
|
|
10290
|
+
/* Ширину задаёт контейнер фильтров: своя фиксированная ширина сделала бы текстовый фильтр
|
|
10291
|
+
уже соседних. */
|
|
10027
10292
|
width: 100%;
|
|
10028
10293
|
|
|
10029
10294
|
${MultiSelectContainer} {
|
|
@@ -11247,6 +11512,7 @@ const PagesContainer = memo(({ type = WidgetType.Dashboard, noBorders }) => {
|
|
|
11247
11512
|
const { config } = useWidgetConfig(type);
|
|
11248
11513
|
const { pageIndex, currentPage } = useWidgetPage(type);
|
|
11249
11514
|
const { selectedTabId, setSelectedTabId, expandedContainers, attributes, onContainerChange } = useWidgetContext(type);
|
|
11515
|
+
const configStringSources = useConfigStringSources(type);
|
|
11250
11516
|
const { options } = config || {};
|
|
11251
11517
|
const { column, width } = options || {};
|
|
11252
11518
|
const isColumn = column === undefined || column;
|
|
@@ -11264,12 +11530,13 @@ const PagesContainer = memo(({ type = WidgetType.Dashboard, noBorders }) => {
|
|
|
11264
11530
|
selectedTabId,
|
|
11265
11531
|
setSelectedTabId,
|
|
11266
11532
|
onChange: onContainerChange,
|
|
11533
|
+
configStringSources,
|
|
11267
11534
|
}), [
|
|
11268
11535
|
attributes,
|
|
11269
11536
|
config,
|
|
11537
|
+
configStringSources,
|
|
11270
11538
|
currentPage,
|
|
11271
11539
|
expandedContainers,
|
|
11272
|
-
getRenderElement,
|
|
11273
11540
|
onContainerChange,
|
|
11274
11541
|
pageIndex,
|
|
11275
11542
|
selectedTabId,
|
|
@@ -11369,8 +11636,8 @@ const TINTED_BACKGROUND_PERCENT = 6;
|
|
|
11369
11636
|
/** Тонированный фон из цвета-источника; без цвета — `undefined`, чтобы уступить следующему в приоритете. */
|
|
11370
11637
|
const tintedBackground = (color) => color ? transparentizeColor(color, TINTED_BACKGROUND_PERCENT) : undefined;
|
|
11371
11638
|
/**
|
|
11372
|
-
* Отступ между элементами плитки: заданный в конфиге `innerGap` (px) либо
|
|
11373
|
-
*
|
|
11639
|
+
* Отступ между элементами плитки: заданный в конфиге `innerGap` (px) либо собственный отступ
|
|
11640
|
+
* элемента по умолчанию, если опция не задана.
|
|
11374
11641
|
*/
|
|
11375
11642
|
const innerGapOr = (fallback) => ({ $innerGap }) => ($innerGap == null ? fallback : `${$innerGap}px`);
|
|
11376
11643
|
/**
|
|
@@ -11484,7 +11751,7 @@ const RoundedBackgroundContainerWrapper = styled(Flex).withConfig({ displayName:
|
|
|
11484
11751
|
text-align: left;
|
|
11485
11752
|
}
|
|
11486
11753
|
|
|
11487
|
-
/* Зазор наружу даёт обёртка иконки, здесь —
|
|
11754
|
+
/* Зазор наружу даёт обёртка иконки, здесь — отступ внутри её бокса. */
|
|
11488
11755
|
span[kind] {
|
|
11489
11756
|
margin-right: 0.5rem;
|
|
11490
11757
|
}
|
|
@@ -11504,7 +11771,7 @@ const RoundedBackgroundContainerWrapper = styled(Flex).withConfig({ displayName:
|
|
|
11504
11771
|
|
|
11505
11772
|
${ContainerUnits} {
|
|
11506
11773
|
margin-left: ${({ $inlineUnits, $innerGap }) => ($inlineUnits ? innerGapOr("0.25rem")({ $innerGap }) : 0)};
|
|
11507
|
-
/* Единицы под значением:
|
|
11774
|
+
/* Единицы под значением: без innerGap зазора нет. */
|
|
11508
11775
|
margin-top: ${({ $inlineUnits, $innerGap }) => (!$inlineUnits && $innerGap != null ? `${$innerGap}px` : 0)};
|
|
11509
11776
|
}
|
|
11510
11777
|
|
|
@@ -11547,17 +11814,17 @@ const RoundedBackgroundContainer = memo(({ type, elementConfig, feature, renderE
|
|
|
11547
11814
|
const color = useMemo(() => attributes?.find(({ attributeName }) => attributeName === colorAttribute)?.value || fontColor, [attributes, colorAttribute, fontColor]);
|
|
11548
11815
|
/**
|
|
11549
11816
|
* Цвет иконки — только источник фона: сам глиф красит ElementIcon/ElementSvg своим `fontColor`.
|
|
11550
|
-
*
|
|
11817
|
+
* `style.color` читается фолбэком — тем же порядком, что и в редакторе.
|
|
11551
11818
|
*/
|
|
11552
11819
|
const iconColor = useMemo(() => iconElement?.options?.fontColor || iconElement?.style?.color, [iconElement]);
|
|
11553
11820
|
/**
|
|
11554
11821
|
* Отступ иконки задаётся на её обёртке `ContainerIconWrapper`: сам элемент `icon` рендерится через
|
|
11555
11822
|
* ElementIcon/ElementSvg со своими styled-компонентами, до него из стилей плитки не дотянуться.
|
|
11556
11823
|
*
|
|
11557
|
-
* В `big` иконка и текст стоят в строку — зазор горизонтальный (
|
|
11558
|
-
* В колоночной плитке зазор
|
|
11824
|
+
* В `big` иконка и текст стоят в строку — зазор горизонтальный (по умолчанию 0.5rem).
|
|
11825
|
+
* В колоночной плитке зазор вертикальный и по умолчанию нулевой: `mb` появляется только
|
|
11559
11826
|
* с заданным `innerGap`. `mr` там на вертикаль не влияет, но участвует в горизонтальном
|
|
11560
|
-
* центрировании, поэтому
|
|
11827
|
+
* центрировании, поэтому от `innerGap` не зависит — иначе он сдвигал бы иконку вбок.
|
|
11561
11828
|
*/
|
|
11562
11829
|
const renderIcon = useMemo(() => {
|
|
11563
11830
|
if (!iconElement)
|
|
@@ -11603,6 +11870,7 @@ const SlideshowContainer = memo(({ config, elementConfig, type }) => {
|
|
|
11603
11870
|
elementConfig,
|
|
11604
11871
|
dataSources,
|
|
11605
11872
|
});
|
|
11873
|
+
const configStringSources = useConfigStringSources(type);
|
|
11606
11874
|
const { id, options } = elementConfig || {};
|
|
11607
11875
|
const { expandable, expanded } = options || {};
|
|
11608
11876
|
const isVisible = isVisibleContainer(id, expandable, expanded, expandedContainers);
|
|
@@ -11617,9 +11885,11 @@ const SlideshowContainer = memo(({ config, elementConfig, type }) => {
|
|
|
11617
11885
|
setSelectedTabId,
|
|
11618
11886
|
pageIndex,
|
|
11619
11887
|
type,
|
|
11888
|
+
configStringSources,
|
|
11620
11889
|
}), [
|
|
11621
11890
|
getRenderElement,
|
|
11622
11891
|
config,
|
|
11892
|
+
configStringSources,
|
|
11623
11893
|
elementConfig,
|
|
11624
11894
|
attributes,
|
|
11625
11895
|
layerInfo,
|
|
@@ -11859,7 +12129,7 @@ const useStructuredDataDraft = ({ schema, dataSource, hasDataSource, filterValue
|
|
|
11859
12129
|
}, []);
|
|
11860
12130
|
const reset = useCallback(() => setRows(baseline.current), []);
|
|
11861
12131
|
const commitSaved = useCallback((next) => {
|
|
11862
|
-
// Сохранённое состояние — наше: значение фильтра
|
|
12132
|
+
// Сохранённое состояние — наше: значение фильтра записали мы, и вернувшееся из него
|
|
11863
12133
|
// обновление не должно пересоздавать ключи уже показанных строк.
|
|
11864
12134
|
hydrated.current = true;
|
|
11865
12135
|
applyBaseline(next);
|
|
@@ -13222,8 +13492,8 @@ const useVoteContainer = (elementConfig) => {
|
|
|
13222
13492
|
const VoteContainer = memo(({ type, elementConfig, isVisible, renderElement }) => {
|
|
13223
13493
|
const { root, body } = useContainerRoot({ elementConfig });
|
|
13224
13494
|
const { configured, ...vote } = useVoteContainer(elementConfig);
|
|
13225
|
-
// Без имени атрибута или хотя бы одного источника контейнеру не с чем
|
|
13226
|
-
//
|
|
13495
|
+
// Без имени атрибута или хотя бы одного источника контейнеру не с чем работать. Ошибка вместо
|
|
13496
|
+
// пустоты: молчаливый `null` выдавал бы опечатку в конфиге за «блока просто нет».
|
|
13227
13497
|
if (!configured) {
|
|
13228
13498
|
return jsx(DataSourceError, { name: elementConfig?.templateName ?? ContainerTemplate.Vote });
|
|
13229
13499
|
}
|
|
@@ -14069,8 +14339,8 @@ const useTaskNotifications = ({ enabled, suppressed, status, lastMessage, openLo
|
|
|
14069
14339
|
const TaskContainer = memo(({ type, elementConfig, renderElement }) => {
|
|
14070
14340
|
const { t, ewktGeometry } = useGlobalContext();
|
|
14071
14341
|
const { dataSources, filters: selectedFilters, attributes, layerInfo, changeFilters, } = useWidgetContext(type);
|
|
14072
|
-
const { dataSources: projectDataSources } = useWidgetContext(WidgetType.Dashboard);
|
|
14073
14342
|
const { currentPage } = useWidgetPage(type);
|
|
14343
|
+
const dashboard = useDashboardFilterStore(type);
|
|
14074
14344
|
const { taskId, runTask, stopTask, status, openLog, loading, isLogDialogOpen, closeLog, log, lastMessage, result, } = usePythonTask();
|
|
14075
14345
|
const { options } = elementConfig || {};
|
|
14076
14346
|
const { title, relatedResources, center, icon, statusColors, responseFilters, useNotifications, } = options || {};
|
|
@@ -14109,11 +14379,11 @@ const TaskContainer = memo(({ type, elementConfig, renderElement }) => {
|
|
|
14109
14379
|
attributes,
|
|
14110
14380
|
layerInfo,
|
|
14111
14381
|
dataSources,
|
|
14112
|
-
|
|
14382
|
+
dashboard,
|
|
14113
14383
|
});
|
|
14114
14384
|
return runTask({ resourceId, parameters: newParams, script, fileName, methodName });
|
|
14115
14385
|
}));
|
|
14116
|
-
}, [attributes, currentPage.filters, dataSources, ewktGeometry, layerInfo,
|
|
14386
|
+
}, [attributes, currentPage.filters, dashboard, dataSources, ewktGeometry, layerInfo, relatedResources, runTask, selectedFilters, stopTask, taskId]);
|
|
14117
14387
|
return (jsxs(ContainerRoot, { ...root, children: [jsx(ContainerBackground, { elementConfig: elementConfig, renderElement: renderElement }), jsx(ExpandableTitle, { elementConfig: elementConfig, type: type, renderElement: renderElement }), jsxs(TaskContainerWrapper, { ...body, justifyContent: center ? "center" : "flex-start", children: [jsx(StatusWaitingButton, { title: title || t("run", { ns: "dashboard", defaultValue: "Запуск" }), icon: icon, status: status, statusColors: statusColors, isWaiting: loading || !!taskId, isDisabled: !relatedResources?.length, onClick: onClick }), !!(log || taskId) && (jsxs(Fragment$1, { children: [jsx(IconButton, { kind: "info", onClick: openLog }), jsx(LogDialog, { logs: log, status: status, statusColors: statusColors, isOpen: isLogDialogOpen, onClose: onCloseLog, onMinimize: useNotifications ? onMinimizeLog : undefined })] }))] })] }));
|
|
14118
14388
|
});
|
|
14119
14389
|
|
|
@@ -14515,7 +14785,7 @@ const EditAttachmentContainer = memo(({ type, elementConfig, renderElement }) =>
|
|
|
14515
14785
|
return (jsxs(AttachmentsContainer, { id: id, style: { ...BASE_CONTAINER_STYLE, ...style }, ...bgHost, children: [jsx(ContainerBackground, { elementConfig: elementConfig, renderElement: renderElement }), jsx(AttachmentsHeader, { alias: renderElement?.({ id: "alias" }), count: items.length, viewMode: viewMode, onChangeViewMode: setViewMode }), jsx(AttachmentsContent, { children: viewMode === "grid" ? (jsx(AttachmentsGrid, { items: visibleItems, isEdit: true, onPreview: handlePreview, onDelete: handleDelete })) : (jsx(AttachmentsList, { items: visibleItems, isEdit: true, onPreview: handlePreview, onDelete: handleDelete })) }), hasMore && !showMore && (jsx(ShowMoreButton, { hiddenCount: hiddenCount, onClick: handleShowMore })), jsx(AddButton, { accept: fileExtensions, onSelectFiles: uploading ? () => undefined : handleUpload, onSelectFromCatalog: handleSelectFromCatalog, onSelectFromLink: handleOpenLinkDialog }), jsx(AttachmentLinkDialog, { isOpen: isLinkDialogOpen, onClose: handleCloseLinkDialog, onSubmit: handleAddByLink }), previewIndex !== null && (jsx(Preview, { images: previewImages, initialIndex: previewIndex, isOpen: previewIndex !== null, onClose: handleClosePreview, onDownload: handleDownload }, previewIndex))] }));
|
|
14516
14786
|
});
|
|
14517
14787
|
|
|
14518
|
-
// `ProgressContainer` и `RoundedBackgroundContainer`
|
|
14788
|
+
// `ProgressContainer` и `RoundedBackgroundContainer` принимают `InnerContainerProps`
|
|
14519
14789
|
// (см. `DataSourceInnerContainer/types.ts`). `<Name>ContainerProps` ⊆ `InnerContainerProps`
|
|
14520
14790
|
// по структуре — каст оправдан, расхождение только в generic-параметре `propTypes`.
|
|
14521
14791
|
const ProgressContainerTyped = ProgressContainer;
|
|
@@ -15554,10 +15824,25 @@ const ElementChips = memo(({ type, elementConfig }) => {
|
|
|
15554
15824
|
return (jsx(DashboardChipsContainer, { style: style, children: tags.map(tag => (jsx(DashboardChip$1, { text: tag.text, "$bgColor": tag.color || colorAttr?.value || bgColor, "$fontColor": fontColor, "$fontSize": fontSize, "$isDefault": !tag.color && !colorAttr?.value }, tag.text))) }));
|
|
15555
15825
|
});
|
|
15556
15826
|
|
|
15827
|
+
/**
|
|
15828
|
+
* Первое непустое значение за жизнь компонента. Дефолт контрола с подстановкой
|
|
15829
|
+
* (`$dashboard:scenario`) фиксируется при открытии карточки: пока фильтры дашборда грузятся, он ещё
|
|
15830
|
+
* пуст, а после — смена фильтра дашборда не должна затирать выбор.
|
|
15831
|
+
*/
|
|
15832
|
+
const useFirstNonEmptyValue = (value) => {
|
|
15833
|
+
const firstValue = useRef(value);
|
|
15834
|
+
if (isEmptyValue(firstValue.current) && !isEmptyValue(value)) {
|
|
15835
|
+
firstValue.current = value;
|
|
15836
|
+
}
|
|
15837
|
+
return firstValue.current;
|
|
15838
|
+
};
|
|
15839
|
+
|
|
15557
15840
|
const ElementControl = ({ elementConfig }) => {
|
|
15558
15841
|
const { t } = useGlobalContext();
|
|
15559
15842
|
const { attributes, layerInfo, dataSources, controls, changeControls } = useWidgetContext(WidgetType.FeatureCard);
|
|
15560
15843
|
const { options, attributeName, defaultValue } = elementConfig || {};
|
|
15844
|
+
// Дефолт фиксируется при открытии карточки и в `controls` не пишется — только показ.
|
|
15845
|
+
const initialDefaultValue = useFirstNonEmptyValue(defaultValue);
|
|
15561
15846
|
const { relatedDataSource, label, width, control, placeholder = t("selectValue", {
|
|
15562
15847
|
ns: "dashboard",
|
|
15563
15848
|
defaultValue: "Выберите значение",
|
|
@@ -15587,7 +15872,7 @@ const ElementControl = ({ elementConfig }) => {
|
|
|
15587
15872
|
}, [control?.targetAttributeName, layerInfo?.configuration]);
|
|
15588
15873
|
const currentValue = controls?.[control?.targetAttributeName] ??
|
|
15589
15874
|
attribute?.value ??
|
|
15590
|
-
|
|
15875
|
+
initialDefaultValue;
|
|
15591
15876
|
const handleChange = useCallback(([option]) => {
|
|
15592
15877
|
if (control?.targetAttributeName && changeControls) {
|
|
15593
15878
|
changeControls({
|
|
@@ -17323,8 +17608,8 @@ const getNextSort = (current, attributeName) => {
|
|
|
17323
17608
|
* Css собственного бокса таблицы: размер из `options` представления плюс прокрутка того,
|
|
17324
17609
|
* что в этот размер не влез.
|
|
17325
17610
|
*
|
|
17326
|
-
* Размеров нет — нет и бокса: таблица занимает столько, сколько просит содержимое, а
|
|
17327
|
-
*
|
|
17611
|
+
* Размеров нет — нет и бокса: таблица занимает столько, сколько просит содержимое, а лишнее
|
|
17612
|
+
* уходит в прокрутку области представления контейнера.
|
|
17328
17613
|
*
|
|
17329
17614
|
* Проценты работают только по ширине. Между областью представления и таблицей стоит блочная
|
|
17330
17615
|
* обёртка элемента (`ElementValueWrapper`) с высотой `auto`, и `height: "100%"` в ней не
|
|
@@ -17594,8 +17879,8 @@ const getDataFromRelatedFeatures = ({ t, config, filters, relatedConfig, dataSou
|
|
|
17594
17879
|
}
|
|
17595
17880
|
// Источник чарта отличается от источника фильтра наличием осей: BarChartFilter собирает
|
|
17596
17881
|
// relatedConfig вручную и оси не задаёт, потому что ему нужна исходная палитра, а не растянутая
|
|
17597
|
-
// под число элементов. Гейт смотрит и на устаревший chartAxis — иначе у
|
|
17598
|
-
// молча поехали бы цвета столбцов и долей.
|
|
17882
|
+
// под число элементов. Гейт смотрит и на устаревший chartAxis — иначе у конфигов с плоской
|
|
17883
|
+
// формой оси молча поехали бы цвета столбцов и долей.
|
|
17599
17884
|
const gradientArray = hasAxisConfig(relatedConfig)
|
|
17600
17885
|
? stretchPalette(colors, data.length)
|
|
17601
17886
|
: colors;
|
|
@@ -17622,7 +17907,7 @@ const getDataFromRelatedFeatures = ({ t, config, filters, relatedConfig, dataSou
|
|
|
17622
17907
|
min: filter?.attributeMin
|
|
17623
17908
|
? feature.properties[filter.attributeMin]
|
|
17624
17909
|
: null,
|
|
17625
|
-
max: filter?.
|
|
17910
|
+
max: filter?.attributeMax
|
|
17626
17911
|
? feature.properties[filter.attributeMax]
|
|
17627
17912
|
: null,
|
|
17628
17913
|
},
|
|
@@ -17992,7 +18277,7 @@ const isContainerRef = (element) => "containerId" in element &&
|
|
|
17992
18277
|
!("templateName" in element) &&
|
|
17993
18278
|
!("type" in element) &&
|
|
17994
18279
|
!("children" in element);
|
|
17995
|
-
const getRenderElement = ({ t, config, elementConfig, attributes = [], layerInfo, selectedTabId, setSelectedTabId, pageIndex, expandedContainers, onChange, type = WidgetType.FeatureCard, }) => (props) => {
|
|
18280
|
+
const getRenderElement = ({ t, config, elementConfig, attributes = [], layerInfo, selectedTabId, setSelectedTabId, pageIndex, expandedContainers, onChange, type = WidgetType.FeatureCard, configStringSources, }) => (props) => {
|
|
17996
18281
|
const { id, index, wrap = true } = props;
|
|
17997
18282
|
const currentConfig = elementConfig || config;
|
|
17998
18283
|
let element = id
|
|
@@ -18009,6 +18294,12 @@ const getRenderElement = ({ t, config, elementConfig, attributes = [], layerInfo
|
|
|
18009
18294
|
container = container[0];
|
|
18010
18295
|
element = container || undefined;
|
|
18011
18296
|
}
|
|
18297
|
+
// Строки узла — от атрибутов этого рендера: у строк `DataSource` они свои, не карточки.
|
|
18298
|
+
element = resolveElementStrings(element, configStringSources && {
|
|
18299
|
+
...configStringSources,
|
|
18300
|
+
attributes: attributes.length ? attributes : configStringSources.attributes,
|
|
18301
|
+
layerInfo: layerInfo ?? configStringSources.layerInfo,
|
|
18302
|
+
});
|
|
18012
18303
|
const { options, children, templateName } = element || {};
|
|
18013
18304
|
const { tabId, hideEmpty, expandable, expanded, innerTemplateName } = options || {};
|
|
18014
18305
|
if (isNotValidSelectedTab(tabId, selectedTabId))
|
|
@@ -18025,6 +18316,7 @@ const getRenderElement = ({ t, config, elementConfig, attributes = [], layerInfo
|
|
|
18025
18316
|
pageIndex,
|
|
18026
18317
|
onChange,
|
|
18027
18318
|
type,
|
|
18319
|
+
configStringSources,
|
|
18028
18320
|
});
|
|
18029
18321
|
// Ставится внутри `getDefaultContainer` — единственного места, которое знает по построению,
|
|
18030
18322
|
// что значение оказалось контейнером. Коллбэк вызывается синхронно, читаем строго после.
|
|
@@ -18071,7 +18363,12 @@ const getRenderElement = ({ t, config, elementConfig, attributes = [], layerInfo
|
|
|
18071
18363
|
const useModalContent = (type, modalId) => {
|
|
18072
18364
|
const { config } = useWidgetConfig(type);
|
|
18073
18365
|
const { expandedContainers, attributes } = useWidgetContext(type);
|
|
18074
|
-
const
|
|
18366
|
+
const configStringSources = useConfigStringSources(type);
|
|
18367
|
+
// Заголовок модалки (`options.title`) — тоже строка конфига с подстановками.
|
|
18368
|
+
const modalConfig = useMemo(() => {
|
|
18369
|
+
const modal = (config?.modals ?? []).find(({ id }) => id === modalId) ?? null;
|
|
18370
|
+
return resolveElementStrings(modal, configStringSources);
|
|
18371
|
+
}, [config?.modals, configStringSources, modalId]);
|
|
18075
18372
|
const modalContent = useMemo(() => (modalConfig?.children ?? []), [modalConfig]);
|
|
18076
18373
|
const modalSlots = useMemo(() => modalContent.filter(({ id }) => MODAL_SLOT_IDS.includes(id)), [modalContent]);
|
|
18077
18374
|
const renderElement = useMemo(() => getRenderElement({
|
|
@@ -18080,14 +18377,16 @@ const useModalContent = (type, modalId) => {
|
|
|
18080
18377
|
elementConfig: { children: modalContent },
|
|
18081
18378
|
attributes,
|
|
18082
18379
|
expandedContainers,
|
|
18083
|
-
|
|
18380
|
+
configStringSources,
|
|
18381
|
+
}), [type, config, modalContent, attributes, expandedContainers, configStringSources]);
|
|
18084
18382
|
const renderSlot = useMemo(() => getRenderElement({
|
|
18085
18383
|
type,
|
|
18086
18384
|
config,
|
|
18087
18385
|
elementConfig: { children: modalSlots },
|
|
18088
18386
|
attributes,
|
|
18089
18387
|
expandedContainers,
|
|
18090
|
-
|
|
18388
|
+
configStringSources,
|
|
18389
|
+
}), [type, config, modalSlots, attributes, expandedContainers, configStringSources]);
|
|
18091
18390
|
const hasTitleIcon = useMemo(() => modalSlots.some(({ id }) => id === MODAL_TITLE_ICON_SLOT_ID), [modalSlots]);
|
|
18092
18391
|
const hasBgImage = useMemo(() => hasContainerBgImage({ children: modalSlots }), [modalSlots]);
|
|
18093
18392
|
return { modalConfig, modalContent, renderElement, renderSlot, hasTitleIcon, hasBgImage };
|
|
@@ -18258,9 +18557,8 @@ const getFeatureCardHeader = (templateName) => {
|
|
|
18258
18557
|
/**
|
|
18259
18558
|
* Ширина фильтра из конфига. Без явного значения фильтр занимает всю ширину контейнера.
|
|
18260
18559
|
*
|
|
18261
|
-
*
|
|
18262
|
-
*
|
|
18263
|
-
* панели фильтры вставали «лесенкой» — и тем заметнее, чем шире контейнер.
|
|
18560
|
+
* Фолбэк общий для всех типов фильтров: свои пиксельные фолбэки у разных типов выстраивали бы
|
|
18561
|
+
* фильтры одной панели «лесенкой» — и тем заметнее, чем шире контейнер.
|
|
18264
18562
|
*/
|
|
18265
18563
|
const getFilterWidth = (width) => toCssSize(width) ?? "100%";
|
|
18266
18564
|
|
|
@@ -18747,7 +19045,7 @@ const RangeDateFilter = ({ type, filter }) => {
|
|
|
18747
19045
|
const maxDate = useMemo(() => maxValue || maxFromData?.toISOString(), [maxFromData, maxValue]);
|
|
18748
19046
|
// Диапазон по умолчанию: `defaultValue: [from, to]` из конфига фильтра — начальное значение
|
|
18749
19047
|
// и точка сброса. Границы календаря (min/max) при этом не меняются; незаданная граница
|
|
18750
|
-
// дефолта падает на них же — как
|
|
19048
|
+
// дефолта падает на них же — как и без `defaultValue`.
|
|
18751
19049
|
const defaultRange = useMemo(() => {
|
|
18752
19050
|
const [from, to] = (Array.isArray(configFilter?.defaultValue) ? configFilter.defaultValue : []);
|
|
18753
19051
|
return [getDate(from)?.toISOString() ?? minDate, getDate(to)?.toISOString() ?? maxDate];
|
|
@@ -19393,21 +19691,23 @@ const getFilterValue = ({ selectedFilters, configFilters, filterName, newValue,
|
|
|
19393
19691
|
/** Условие источника задаётся строкой или массивом строк — приводим к массиву для единообразного обхода. */
|
|
19394
19692
|
const toConditionsArray = (value) => Array.isArray(value) ? value : value ? [value] : [];
|
|
19395
19693
|
|
|
19396
|
-
|
|
19397
|
-
|
|
19398
|
-
|
|
19399
|
-
|
|
19694
|
+
/**
|
|
19695
|
+
* Строка ссылается на вид карты — `%extent` / `%zoom` целиком или внутри строки. Граница та же,
|
|
19696
|
+
* что у резолвера строк конфига: составные формы (`%zoomLevel`, `%extent_id`, `%zoom.min`)
|
|
19697
|
+
* принадлежат пользовательским фильтрам, не карте.
|
|
19698
|
+
*/
|
|
19699
|
+
const hasMapViewPlaceholder = (value) => typeof value === "string" &&
|
|
19700
|
+
tokenizeConfigString(value).some(segment => typeof segment !== "string" &&
|
|
19701
|
+
segment.kind === "filter" &&
|
|
19702
|
+
segment.scope === "widget" &&
|
|
19703
|
+
!segment.prop &&
|
|
19704
|
+
MAP_VIEW_FILTER_NAMES.includes(segment.name));
|
|
19400
19705
|
/**
|
|
19401
19706
|
* Источники, чей запрос зависит от текущего вида карты — в `parameters` или `condition` у них
|
|
19402
19707
|
* есть `%extent` / `%zoom`. Перезапрашивают при движении карты только их: иначе каждый пан
|
|
19403
19708
|
* дёргал бы все запросы страницы.
|
|
19404
19709
|
*/
|
|
19405
|
-
const getMapViewDataSources = (dataSources) => dataSources?.filter(({ parameters, condition }) => {
|
|
19406
|
-
const hasInParameters = Object.values(parameters ?? {}).some(value => typeof value === "string" && MAP_VIEW_PLACEHOLDERS.includes(value));
|
|
19407
|
-
if (hasInParameters)
|
|
19408
|
-
return true;
|
|
19409
|
-
return toConditionsArray(condition).some(hasMapViewPlaceholder);
|
|
19410
|
-
}) ?? [];
|
|
19710
|
+
const getMapViewDataSources = (dataSources) => dataSources?.filter(({ parameters, condition }) => Object.values(parameters ?? {}).some(hasMapViewPlaceholder) || toConditionsArray(condition).some(hasMapViewPlaceholder)) ?? [];
|
|
19411
19711
|
|
|
19412
19712
|
const getFormattedAttributes = (t, data, attributes, config) => {
|
|
19413
19713
|
const showOtherItems = config?.options?.otherItems < data?.length;
|
|
@@ -19528,8 +19828,6 @@ const getLayerInfo = (layer) => {
|
|
|
19528
19828
|
|
|
19529
19829
|
const getLayerInfoFromDataSources = (layerInfos, dataSources, relatedDataSource) => layerInfos?.find(({ name }) => name === dataSources?.find(item => item.name === relatedDataSource)?.layerName);
|
|
19530
19830
|
|
|
19531
|
-
const getPagesFromConfig = (config) => config?.children?.[0]?.children;
|
|
19532
|
-
|
|
19533
19831
|
const getPagesFromProjectInfo = (projectInfo) => getPagesFromConfig(projectInfo?.content?.dashboardConfiguration);
|
|
19534
19832
|
|
|
19535
19833
|
const getRelatedAttribute = (layerInfo, sourceAttributeName, relatedLayerName) => {
|
|
@@ -19647,6 +19945,27 @@ const removeDataSource = (dashboardConfiguration, name, pageIndex) => {
|
|
|
19647
19945
|
return newConfig;
|
|
19648
19946
|
};
|
|
19649
19947
|
|
|
19948
|
+
/**
|
|
19949
|
+
* Датасорсы, которые нужно перезапросить после изменения фильтров: их параметры ссылаются на
|
|
19950
|
+
* изменившийся фильтр виджета (`%name`, в том числе через дефолт `$dashboard:`) или дашборда
|
|
19951
|
+
* (`$dashboard:name`). Датасорсы слоя без `query` перезапрашиваются на любое изменение.
|
|
19952
|
+
* Нечего перезапрашивать — `undefined`.
|
|
19953
|
+
*/
|
|
19954
|
+
const selectUpdatingDataSources = ({ configDataSources, filters, configFilters, dashboard, prev, }) => {
|
|
19955
|
+
const widgetNames = [
|
|
19956
|
+
...getChangedFilterNames(filters, prev.filters ?? {}, configFilters),
|
|
19957
|
+
...getChangedDefaultNames(configFilters, prev.configFilters, filters),
|
|
19958
|
+
];
|
|
19959
|
+
const dashboardNames = dashboard
|
|
19960
|
+
? getChangedFilterNames(dashboard.selectedFilters, prev.dashboardFilters ?? {}, dashboard.configFilters)
|
|
19961
|
+
: [];
|
|
19962
|
+
if (!widgetNames.length && !dashboardNames.length)
|
|
19963
|
+
return undefined;
|
|
19964
|
+
const dataSources = configDataSources?.filter(({ parameters = {}, query }) => isNil(query) ||
|
|
19965
|
+
Object.values(parameters).some(value => referencesFilters({ value, widgetNames, dashboardNames, hasDashboardStore: !!dashboard })));
|
|
19966
|
+
return dataSources?.length ? dataSources : undefined;
|
|
19967
|
+
};
|
|
19968
|
+
|
|
19650
19969
|
const updateDataSource = (dashboardConfiguration, name, pageIndex, data) => {
|
|
19651
19970
|
const newConfig = JSON.parse(JSON.stringify(dashboardConfiguration));
|
|
19652
19971
|
if (newConfig.dataSources?.length) {
|
|
@@ -20933,5 +21252,5 @@ const DEFAULT_HEATMAP_STYLE = {
|
|
|
20933
21252
|
],
|
|
20934
21253
|
};
|
|
20935
21254
|
|
|
20936
|
-
export { ALIGNMENTS, ALIGN_ITEMS, ATTRIBUTE_ICON_ELEMENT_TYPES, AXIS_TITLE_GAP, AXIS_TITLE_LINE_HEIGHT, AXIS_TITLE_TOP_GRID_GAP, AXIS_TITLE_TOP_HEIGHT, AddButtonRow, AddFeatureButton, AddFeatureContainer, AlertIconContainer, AttachmentContainer, AttributeGalleryContainer, AttributeLabel, BASE_CONTAINER_STYLE, BG_IMAGE_SLOT_ID, BaseMapTheme, CHART_AXIS_ALIGN_JUSTIFY, CHART_TYPES, COMPACT_FRACTION_DIGITS, CONFIG_PAGES_ID, CONFIG_PAGE_ID, CONTAINERS_GROUP_DEFAULTS, CONTAINER_BODY_ATTRIBUTE, CONTAINER_BODY_FILL_STYLE, CameraContainer, Chart, ChartContainer, ChartLegend, ChartLoading, Container, ContainerBackground, ContainerChildren, ContainerLoading, ContainerRoot, ContainerTemplate, ContainerWrapper, ContainersGroupContainer, DASHBOARD_OVERLAY_Z_INDEX, DEFAULT_ATTRIBUTE_NAME, DEFAULT_BARCHART_RADIUS, DEFAULT_BASE_MAP, DEFAULT_BLUR, DEFAULT_CHART_ANGLE, DEFAULT_CHART_HEIGHT, DEFAULT_CHART_WIDTH, DEFAULT_CIRCLE_RADIUS, DEFAULT_CIRCLE_STROKE_WIDTH, DEFAULT_CIRCLE_STYLE, DEFAULT_COLOR, DEFAULT_DASHBOARD_CONFIG, DEFAULT_DATA_SOURCE_LIMIT, DEFAULT_DROPDOWN_WIDTH, DEFAULT_FILL_EXTRUSION_BASE, DEFAULT_FILL_EXTRUSION_HEIGHT, DEFAULT_FILL_EXTRUSION_STYLE, DEFAULT_FILL_EXTRUSION_VERTICAL_GRADIENT, DEFAULT_FILL_STYLE, DEFAULT_FILTER_PADDING, DEFAULT_GRID_GAP, DEFAULT_HEATMAP_COLOR, DEFAULT_HEATMAP_INTENSITY, DEFAULT_HEATMAP_RADIUS, DEFAULT_HEATMAP_STYLE, DEFAULT_HEATMAP_WEIGHT, DEFAULT_ICON_ANCHOR, DEFAULT_ICON_OVERLAP, DEFAULT_ICON_PADDING, DEFAULT_ICON_ROTATE, DEFAULT_ICON_SIZE, DEFAULT_ID_ATTRIBUTE_NAME, DEFAULT_LAT, DEFAULT_LINE_CAP, DEFAULT_LINE_JOIN, DEFAULT_LINE_STYLE, DEFAULT_LINE_WIDTH, DEFAULT_LNG, DEFAULT_OPACITY, DEFAULT_PAGES_CONFIG, DEFAULT_PIECHART_RADIUS, DEFAULT_SYMBOL_COLOR, DEFAULT_SYMBOL_HALO_COLOR, DEFAULT_SYMBOL_PLACEMENT, DEFAULT_SYMBOL_SPACING, DEFAULT_SYMBOL_STYLE, DEFAULT_TEXT_ANCHOR, DEFAULT_TEXT_JUSTIFY, DEFAULT_TEXT_SIZE, DEFAULT_TEXT_TRANSFORM, DEFAULT_TRACK_FR, DEFAULT_TRANSLATE, DEFAULT_ZOOM, DRAG_THRESHOLD_PX, Dashboard, DashboardCheckbox, DashboardChip, DashboardContent, DashboardContext, DashboardDefaultHeader, DashboardHeader, DashboardLoading, DashboardPlaceholder, DashboardPlaceholderWrap, DashboardProvider, DashboardWrapper, DataSourceContainer, DataSourceError, DataSourceErrorContainer, DataSourceInnerContainer, DataSourceProgressContainer, DateFormat, DefaultAttributesContainer, DefaultHeaderContainer, DefaultHeaderWrapper, DividerContainer, EMPTY_DATA_SOURCE_LAYER_INFO, EXTENT_FILTER_NAME, EditGeometryType, ElementButton, ElementCamera, ElementChart, ElementChips, ElementControl, ElementIcon, ElementImage, ElementLegend, ElementLink, ElementMarkdown, ElementSlideshow, ElementSvg, ElementTable, ElementTooltip, ElementValueWrapper, ExpandableTitle, FEATURE_CARD_DEFAULT_COLORS, FEATURE_CARD_OTHER_COLOR, FILL_SIZE, FILTERED_VALUE_OPACITY, FILTER_PREFIX, FR_PRECISION, FeatureCardBackgroundHeader, FeatureCardButtons, FeatureCardContext, FeatureCardDefaultHeader, FeatureCardHeader, FeatureCardProvider, FeatureCardSlideshowHeader, FeatureCardTitle, FeatureControls, FeatureTitleContainer, FiltersContainer, GEOMETRY_ATTRIBUTE, GEOMETRY_FILTER_NAME, GRID_AUTO_FILL_DEFAULTS, GRID_CELL_ATTR, GRID_CELL_ID_PREFIX, GRID_DRAGGING_ATTR, GRID_DRAG_SOURCE_ATTR, GRID_DROP_TARGET_ATTR, GRID_FILL_DEFAULTS, GRID_ROW_ID_PREFIX, GlobalContext, GlobalProvider, GridRowContainer, HANDLE_BLEED_PX, Header, HeaderContainer, HeaderFontColorMixin, HeaderFrontView, HeaderTemplate, HeaderTitleContainer, HiddenTitleItems, IconContainer, ImageContainer, LEFT_PANEL_HEADER_HEIGHT, LINE_CHART_X_LABELS_BLANK, LINE_CHART_Y_TICKS, Layer, LayerDescription, LayerGroupList, LayerIcon, LayerIconContainer, LayerListContainer, LayerTree, LayersContainer, LayersListWrapper, LinearProgressContainer, LogTerminal, LogoContainer, MAP_VIEW_FILTER_NAMES, MAX_CHART_WIDTH, MAX_TRACKS, MIN_TRACK_PX, MIN_TRACK_RATIO, MODAL_HEADER_BG_POSITIONS, Map$1 as Map, MapContext, MapProvider, NON_TRACK_SLOT_IDS, NO_CELL_DRAG_SELECTOR, NO_CONTENT_VALUE, NUMERIC_ATTRIBUTE_TYPES, NoLiveSnapshotContainer, OBJECT_FITS, OVERFLOWS, OneColumnContainer, PIE_CHART_TOOLTIP_STYLE, POLL_SUBTASK_INTERVAL_MS, POLL_SUBTASK_TIMEOUT_MS, PROJECT_ALIAS_PROP, PROJECT_FILTER_NAME, PROJECT_NAME_PROP, PROJECT_PROPS, PROVIDER_PREFIX, PageNavigator, PageTitle, PageTitleContainer, PagesContainer, Pagination, PresentationHeader, PresentationHeaderButtons, PresentationHeaderTools, PresentationPanelContainer, PresentationPanelWrapper, PresentationWrapper, ProgressContainer, ProviderPrefix, QUERY_DESCRIPTION_CACHE_TTL, RESIZE_HANDLE_ATTR, ResizeHandle, RoundedBackgroundContainer, SAVE_HOOK_RESULT_DURATION, SERVER_NOTIFICATION_EVENT, STACK_BAR_TOTAL_HEIGHT, ScalingFactor, ServerNotificationsContext, ServerNotificationsProvider, SlideshowContainer, SmallPreviewContainer$1 as SmallPreviewContainer, SmallPreviewControl, SmallPreviewCounter, SmallPreviewImages, SmallPreviewLeft, SmallPreviewRight, StackBar, StructuredDataContainer, SvgImage, TILE_ALIGNMENTS, TIME_ZONE_FORMAT, TITLE_SLOT_IDS, TabsContainer, TextTrim, ThemeName, TitleContainer, TopContainer, TopContainerButtons, TwoColumnContainer, UploadContainer, VIEW_MODES, VoteContainer, WidgetType, X_AXIS_TITLE_GAP, X_AXIS_TITLE_LEGEND_GAP, Y_AXIS_LABEL_OVERHANG, ZOOM_FILTER_NAME, addDataSource, addDataSources, adjustColor, applyFiltersToCondition, applyQueryFilters, applyTreeFilterToCondition, applyVarsToCondition, asAttributeName, asChartId, asContainerId, asDataSourceName, asFilterName, asLayerName, asModalId, asResourceId, asTabId, buildGridTemplate, buildTrackTemplate, checkEqualOrIncludes, checkIsLoading, collectConfigIds, colorToHex, containsNodeId, createConfigLayer, createConfigPage, createGridCell, createGridIdFactory, createGridRow, createNewPageId, createSaveNotificationId, dateOptions, debounce, decimalOpacityToHex, enrichStyleItemsWithIds, enrichStyleModelsWithIds, eqlParametersToPayload, fetchQueryDescription, findAttributeInExpression, findCellContext, formatArea, formatAttributeValue, formatChartRelatedValue, formatConditionValue, formatDataSourceCondition, formatDate$1 as formatDate, formatElementValue, formatLength, formatNumber, formatPolygonMeasure, geometryToEwkt, getActualExtrusionHeight, getAttributeByName, getAttributeConfigurationByName, getAttributeIconElement, getAttributeIconUrl, getAttributeValue, getAttributesConfiguration, getAverageTrackSize, getAxisSide, getChartAxes, getChartFilterName, getChartMarkers, getConfigFilter, getContainerComponent, getDashboardHeader, getDataFromAttributes, getDataFromRelatedFeatures, getDataSource, getDataSourceFilterValue, getDataSourceLayerInfo, getDate, getDefaultConfig, getDisplayTemplateNameFromAttribute, getElementValue, getFeatureAttributes, getFeatureCardHeader, getFilterComponent, getFilterSelectedItems, getFilterValue, getFormattedAttributes, getGradientColors, getImageUrl, getLayerClientStyle, getLayerInfo, getLayerInfoAttribute, getLayerInfoFromDataSources, getLayoutChildren, getMapViewDataSources, getModalsDataSources, getPagesFromConfig, getPagesFromProjectInfo, getProjectValue, getProxyService, getRelatedAttribute, getRenderElement, getResourceUrl, getRootElementId, getSelectedFilterValue, getShownItemsLimit, getSlideshowImages, getStyleAttributes, getSvgUrl, getTemplateNameFromAttribute, getThemeByName, getTotalFromAttributes, getTotalFromRelatedFeatures, getTrackSize, getTrackSizeKey, getTrackSizes, getWrapperSizeStyle, hasAxisConfig, hasContainerBgImage, hexToRgba, isCrossOriginUrl, isEmptyElementValue, isEmptyValue, isFeaturesFilterValue, isFillSize, isFrSize, isGridNode, isHiddenEmptyValue, isHookActive, isLayerService, isNotValidSelectedTab, isNumeric, isObject, isProxyService, isRightAxis, isRootOwningContainer, isTreeFilterValue, isValueAxis, isVisibleContainer, mapNodeById, mergeAttributeConfigurations, metersPerPixel, noMarginMixin, numberOptions, parseFrValue, parseIconNames, parseIconNamesFromClientStyle, pieChartTooltipFromAttributes, pieChartTooltipFromRelatedFeatures, pointOptions, removeDataSource, removeTracks, replaceNodesByIds, resolveAxis, rgbToHex, roundFr, roundTotalSum, setLayoutChildren, sizeCssMixin, sliceShownOtherItems, stretchPalette, timeOptions, toConditionsArray, toCssSize, toFrSize, toPxNumber, toRenderableValue, tooltipNameFromAttributes, tooltipValueFromAttributes, tooltipValueFromRelatedFeatures, transparentizeColor, updateDataSource, useAfterSave, useAppHeight, useAttachmentDownload, useAttachmentItems, useAttachmentPreviewImages, useAttachmentsView, useAutoCompleteControl, useBeforeSave, useBgImageHost, useChartAxisAttributes, useChartAxisTickFormat, useChartAxisTitles, useChartChange, useChartData, useConfigDataSources, useContainerAttributes, useContainerRoot, useCurrentPageLayers, useCustomFeatureSelect, useDashboardHeader, useDataSourceLoading, useDataSources, useDebouncedCallback, useDiffPage, useEditGroupAttributes, useEqualTileWidth, useExpandableContainers, useExportPdf, useFeatureSaveHooks, useFetchImageWithAuth, useFetchWithAuth, useGetConfigLayer, useGlobalContext, useHeaderRender, useHideIfEmptyDataSource, useLayerHiddenAttributes, useLayerParams, useMapContext, useMapDraw, useMapImages, useMaxZoomTo, useProjectDashboardInit, usePythonSandbox, usePythonTask, useRedrawLayer, useRelatedDataSourceAttributes, useRemoteTask, useRenderElement, useResizeBox, useResizeDrag, useSavePrototypeBuilder, useServerNotificationsContext, useShownOtherItems, useToggle, useUpdateDataSource, useVisibleProjectItems, useWidgetConfig, useWidgetContext, useWidgetFilters, useWidgetPage, useWindowResize, useWrapperSize, useZoomToFeatures, useZoomToPoint, withTrackSize };
|
|
21255
|
+
export { ALIGNMENTS, ALIGN_ITEMS, ATTRIBUTE_ICON_ELEMENT_TYPES, AXIS_TITLE_GAP, AXIS_TITLE_LINE_HEIGHT, AXIS_TITLE_TOP_GRID_GAP, AXIS_TITLE_TOP_HEIGHT, AddButtonRow, AddFeatureButton, AddFeatureContainer, AlertIconContainer, AttachmentContainer, AttributeGalleryContainer, AttributeLabel, BASE_CONTAINER_STYLE, BG_IMAGE_SLOT_ID, BaseMapTheme, CHART_AXIS_ALIGN_JUSTIFY, CHART_TYPES, COMPACT_FRACTION_DIGITS, CONFIG_PAGES_ID, CONFIG_PAGE_ID, CONFIG_STRING_OPTION_KEYS, CONFIG_TEXT_ARRAY_SEPARATOR, CONFIG_TOKEN_REGEXP, CONTAINERS_GROUP_DEFAULTS, CONTAINER_BODY_ATTRIBUTE, CONTAINER_BODY_FILL_STYLE, CameraContainer, Chart, ChartContainer, ChartLegend, ChartLoading, Container, ContainerBackground, ContainerChildren, ContainerLoading, ContainerRoot, ContainerTemplate, ContainerWrapper, ContainersGroupContainer, DASHBOARD_OVERLAY_Z_INDEX, DEFAULT_ATTRIBUTE_NAME, DEFAULT_BARCHART_RADIUS, DEFAULT_BASE_MAP, DEFAULT_BLUR, DEFAULT_CHART_ANGLE, DEFAULT_CHART_HEIGHT, DEFAULT_CHART_WIDTH, DEFAULT_CIRCLE_RADIUS, DEFAULT_CIRCLE_STROKE_WIDTH, DEFAULT_CIRCLE_STYLE, DEFAULT_COLOR, DEFAULT_DASHBOARD_CONFIG, DEFAULT_DATA_SOURCE_LIMIT, DEFAULT_DROPDOWN_WIDTH, DEFAULT_FILL_EXTRUSION_BASE, DEFAULT_FILL_EXTRUSION_HEIGHT, DEFAULT_FILL_EXTRUSION_STYLE, DEFAULT_FILL_EXTRUSION_VERTICAL_GRADIENT, DEFAULT_FILL_STYLE, DEFAULT_FILTER_PADDING, DEFAULT_GRID_GAP, DEFAULT_HEATMAP_COLOR, DEFAULT_HEATMAP_INTENSITY, DEFAULT_HEATMAP_RADIUS, DEFAULT_HEATMAP_STYLE, DEFAULT_HEATMAP_WEIGHT, DEFAULT_ICON_ANCHOR, DEFAULT_ICON_OVERLAP, DEFAULT_ICON_PADDING, DEFAULT_ICON_ROTATE, DEFAULT_ICON_SIZE, DEFAULT_ID_ATTRIBUTE_NAME, DEFAULT_LAT, DEFAULT_LINE_CAP, DEFAULT_LINE_JOIN, DEFAULT_LINE_STYLE, DEFAULT_LINE_WIDTH, DEFAULT_LNG, DEFAULT_OPACITY, DEFAULT_PAGES_CONFIG, DEFAULT_PIECHART_RADIUS, DEFAULT_SYMBOL_COLOR, DEFAULT_SYMBOL_HALO_COLOR, DEFAULT_SYMBOL_PLACEMENT, DEFAULT_SYMBOL_SPACING, DEFAULT_SYMBOL_STYLE, DEFAULT_TEXT_ANCHOR, DEFAULT_TEXT_JUSTIFY, DEFAULT_TEXT_SIZE, DEFAULT_TEXT_TRANSFORM, DEFAULT_TRACK_FR, DEFAULT_TRANSLATE, DEFAULT_ZOOM, DRAG_THRESHOLD_PX, Dashboard, DashboardCheckbox, DashboardChip, DashboardContent, DashboardContext, DashboardDefaultHeader, DashboardHeader, DashboardLoading, DashboardPlaceholder, DashboardPlaceholderWrap, DashboardProvider, DashboardWrapper, DataSourceContainer, DataSourceError, DataSourceErrorContainer, DataSourceInnerContainer, DataSourceProgressContainer, DateFormat, DefaultAttributesContainer, DefaultHeaderContainer, DefaultHeaderWrapper, DividerContainer, EMPTY_DATA_SOURCE_LAYER_INFO, EXTENT_FILTER_NAME, EditGeometryType, ElementButton, ElementCamera, ElementChart, ElementChips, ElementControl, ElementIcon, ElementImage, ElementLegend, ElementLink, ElementMarkdown, ElementSlideshow, ElementSvg, ElementTable, ElementTooltip, ElementValueWrapper, ExpandableTitle, FEATURE_CARD_DEFAULT_COLORS, FEATURE_CARD_OTHER_COLOR, FILL_SIZE, FILTERED_VALUE_OPACITY, FILTER_PREFIX, FR_PRECISION, FeatureCardBackgroundHeader, FeatureCardButtons, FeatureCardContext, FeatureCardDefaultHeader, FeatureCardHeader, FeatureCardProvider, FeatureCardSlideshowHeader, FeatureCardTitle, FeatureControls, FeatureTitleContainer, FiltersContainer, GEOMETRY_ATTRIBUTE, GEOMETRY_FILTER_NAME, GRID_AUTO_FILL_DEFAULTS, GRID_CELL_ATTR, GRID_CELL_ID_PREFIX, GRID_DRAGGING_ATTR, GRID_DRAG_SOURCE_ATTR, GRID_DROP_TARGET_ATTR, GRID_FILL_DEFAULTS, GRID_ROW_ID_PREFIX, GlobalContext, GlobalProvider, GridRowContainer, HANDLE_BLEED_PX, Header, HeaderContainer, HeaderFontColorMixin, HeaderFrontView, HeaderTemplate, HeaderTitleContainer, HiddenTitleItems, IconContainer, ImageContainer, LEFT_PANEL_HEADER_HEIGHT, LINE_CHART_X_LABELS_BLANK, LINE_CHART_Y_TICKS, Layer, LayerDescription, LayerGroupList, LayerIcon, LayerIconContainer, LayerListContainer, LayerTree, LayersContainer, LayersListWrapper, LinearProgressContainer, LogTerminal, LogoContainer, MAP_VIEW_FILTER_NAMES, MAX_CHART_WIDTH, MAX_TRACKS, MIN_TRACK_PX, MIN_TRACK_RATIO, MODAL_HEADER_BG_POSITIONS, Map$1 as Map, MapContext, MapProvider, NON_TRACK_SLOT_IDS, NO_CELL_DRAG_SELECTOR, NO_CONTENT_VALUE, NUMERIC_ATTRIBUTE_TYPES, NoLiveSnapshotContainer, OBJECT_FITS, OVERFLOWS, OneColumnContainer, PIE_CHART_TOOLTIP_STYLE, POLL_SUBTASK_INTERVAL_MS, POLL_SUBTASK_TIMEOUT_MS, PROJECT_ALIAS_PROP, PROJECT_FILTER_NAME, PROJECT_NAME_PROP, PROJECT_PROPS, PROVIDER_PREFIX, PageNavigator, PageTitle, PageTitleContainer, PagesContainer, Pagination, PresentationHeader, PresentationHeaderButtons, PresentationHeaderTools, PresentationPanelContainer, PresentationPanelWrapper, PresentationWrapper, ProgressContainer, ProviderPrefix, QUERY_DESCRIPTION_CACHE_TTL, RANGE_PROPS, RESIZE_HANDLE_ATTR, ResizeHandle, RoundedBackgroundContainer, SAVE_HOOK_RESULT_DURATION, SERVER_NOTIFICATION_EVENT, STACK_BAR_TOTAL_HEIGHT, ScalingFactor, ServerNotificationsContext, ServerNotificationsProvider, SlideshowContainer, SmallPreviewContainer$1 as SmallPreviewContainer, SmallPreviewControl, SmallPreviewCounter, SmallPreviewImages, SmallPreviewLeft, SmallPreviewRight, StackBar, StructuredDataContainer, SvgImage, TILE_ALIGNMENTS, TIME_ZONE_FORMAT, TITLE_SLOT_IDS, TabsContainer, TextTrim, ThemeName, TitleContainer, TopContainer, TopContainerButtons, TwoColumnContainer, UploadContainer, VIEW_MODES, VoteContainer, WidgetType, X_AXIS_TITLE_GAP, X_AXIS_TITLE_LEGEND_GAP, Y_AXIS_LABEL_OVERHANG, ZOOM_FILTER_NAME, addDataSource, addDataSources, adjustColor, applyQueryFilters, applyTreeFilterToCondition, applyVarsToCondition, asAttributeName, asChartId, asContainerId, asDataSourceName, asFilterName, asLayerName, asModalId, asResourceId, asTabId, buildGridTemplate, buildTrackTemplate, checkEqualOrIncludes, checkIsLoading, collectConfigIds, colorToHex, containsNodeId, createConfigLayer, createConfigPage, createGridCell, createGridIdFactory, createGridRow, createNewPageId, createSaveNotificationId, dateOptions, debounce, decimalOpacityToHex, enrichStyleItemsWithIds, enrichStyleModelsWithIds, eqlParametersToPayload, fetchQueryDescription, findAttributeInExpression, findCellContext, formatArea, formatAttributeValue, formatChartRelatedValue, formatConditionSection, formatConditionString, formatConditionValue, formatDataSourceCondition, formatDate$1 as formatDate, formatElementValue, formatLength, formatNumber, formatPolygonMeasure, geometryToEwkt, getActualExtrusionHeight, getAttributeByName, getAttributeConfigurationByName, getAttributeIconElement, getAttributeIconUrl, getAttributeValue, getAttributesConfiguration, getAverageTrackSize, getAxisSide, getChangedDefaultNames, getChangedFilterNames, getChartAxes, getChartFilterName, getChartMarkers, getConfigFilter, getContainerComponent, getDashboardHeader, getDataFromAttributes, getDataFromRelatedFeatures, getDataSource, getDataSourceFilterValue, getDataSourceLayerInfo, getDate, getDefaultConfig, getDisplayTemplateNameFromAttribute, getElementValue, getFeatureAttributes, getFeatureCardHeader, getFilterComponent, getFilterSelectedItems, getFilterTokenValue, getFilterValue, getFormattedAttributes, getGradientColors, getImageUrl, getLayerClientStyle, getLayerInfo, getLayerInfoAttribute, getLayerInfoFromDataSources, getLayoutChildren, getMapViewDataSources, getModalsDataSources, getPageFilters, getPagesFromConfig, getPagesFromProjectInfo, getProjectValue, getProxyService, getRelatedAttribute, getRenderElement, getResourceUrl, getRootElementId, getSelectedFilterValue, getShownItemsLimit, getSingleToken, getSlideshowImages, getStyleAttributes, getSvgUrl, getTemplateNameFromAttribute, getThemeByName, getTotalFromAttributes, getTotalFromRelatedFeatures, getTrackSize, getTrackSizeKey, getTrackSizes, getWrapperSizeStyle, hasAxisConfig, hasContainerBgImage, hexToRgba, isCrossOriginUrl, isDashboardReference, isEmptyElementValue, isEmptyValue, isFeaturesFilterValue, isFillSize, isFrSize, isGridNode, isHiddenEmptyValue, isHookActive, isLayerService, isNotValidSelectedTab, isNumeric, isObject, isProxyService, isRightAxis, isRootOwningContainer, isTreeFilterValue, isValueAxis, isVisibleContainer, joinConfigSegments, mapNodeById, mergeAttributeConfigurations, metersPerPixel, noMarginMixin, numberOptions, parseFrValue, parseIconNames, parseIconNamesFromClientStyle, pieChartTooltipFromAttributes, pieChartTooltipFromRelatedFeatures, pointOptions, referencesFilters, removeDataSource, removeTracks, replaceNodesByIds, resolveAxis, resolveConfigString, resolveConfigToken, resolveElementStrings, resolveFilterDefaults, resolveQueryParameter, rgbToHex, roundFr, roundTotalSum, selectUpdatingDataSources, setLayoutChildren, sizeCssMixin, sliceShownOtherItems, stretchPalette, timeOptions, toConditionsArray, toCssSize, toFrSize, toPxNumber, toRenderableValue, tokenizeConfigString, tooltipNameFromAttributes, tooltipValueFromAttributes, tooltipValueFromRelatedFeatures, transparentizeColor, updateDataSource, useAfterSave, useAppHeight, useAttachmentDownload, useAttachmentItems, useAttachmentPreviewImages, useAttachmentsView, useAutoCompleteControl, useBeforeSave, useBgImageHost, useChartAxisAttributes, useChartAxisTickFormat, useChartAxisTitles, useChartChange, useChartData, useConfigDataSources, useConfigStringSources, useContainerAttributes, useContainerRoot, useCurrentPageLayers, useCustomFeatureSelect, useDashboardFilterStore, useDashboardHeader, useDataSourceLoading, useDataSources, useDebouncedCallback, useDiffPage, useEditGroupAttributes, useEqualTileWidth, useExpandableContainers, useExportPdf, useFeatureSaveHooks, useFetchImageWithAuth, useFetchWithAuth, useGetConfigLayer, useGlobalContext, useHeaderRender, useHideIfEmptyDataSource, useLayerHiddenAttributes, useLayerParams, useMapContext, useMapDraw, useMapImages, useMaxZoomTo, useProjectDashboardInit, usePythonSandbox, usePythonTask, useRedrawLayer, useRelatedDataSourceAttributes, useRemoteTask, useRenderElement, useResizeBox, useResizeDrag, useSavePrototypeBuilder, useServerNotificationsContext, useShownOtherItems, useToggle, useUpdateDataSource, useVisibleProjectItems, useWidgetConfig, useWidgetContext, useWidgetFilters, useWidgetPage, useWindowResize, useWrapperSize, useZoomToFeatures, useZoomToPoint, withTrackSize };
|
|
20937
21256
|
//# sourceMappingURL=react.esm.js.map
|