@evergis/react 4.0.133 → 4.0.134
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/hooks/useFetchImageWithAuth.d.ts +9 -0
- package/dist/components/Dashboard/utils/getResourceUrl.d.ts +10 -0
- package/dist/components/Dashboard/utils/index.d.ts +1 -0
- package/dist/components/Dashboard/utils/isCrossOriginUrl.d.ts +15 -0
- package/dist/index.js +80 -12
- package/dist/index.js.map +1 -1
- package/dist/react.esm.js +80 -13
- package/dist/react.esm.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `src` для картинки: свой файл тянется fetch'ем с токеном и отдаётся blob-адресом, чужой
|
|
3
|
+
* возвращается как есть.
|
|
4
|
+
*
|
|
5
|
+
* Внешний адрес намеренно не фетчится: тег `img` грузит кросс-доменную картинку без всякого
|
|
6
|
+
* CORS, а `fetch` — только если чужой сервер отдал `Access-Control-Allow-Origin`. Заодно
|
|
7
|
+
* картинка остаётся в HTTP-кеше браузера, которого blob-адрес лишён. Ровно так же поступают
|
|
8
|
+
* вложения: `useAttachmentPreviewImages` отдаёт внешней ссылке `src: item.link`.
|
|
9
|
+
*/
|
|
1
10
|
export declare const useFetchImageWithAuth: (url: string | null) => string | null;
|
|
@@ -1 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Приводит значение конфига к адресу картинки, иконки или ссылки.
|
|
3
|
+
*
|
|
4
|
+
* Развилка одна: готовый адрес отдаётся как есть, всё остальное считается идентификатором
|
|
5
|
+
* ресурса и разворачивается в путь к файлу каталога.
|
|
6
|
+
*
|
|
7
|
+
* Проверка идёт по схеме, а не по префиксу `http`: иначе `data:`-URI, `blob:`, `mailto:` и
|
|
8
|
+
* `//cdn…` превращались бы в `/sp/resources/file/data:…` — путь к несуществующему файлу,
|
|
9
|
+
* который дальше по течению неотличим от обычного внутреннего адреса и молча даёт 404.
|
|
10
|
+
*/
|
|
1
11
|
export declare const getResourceUrl: (url?: string) => string;
|
|
@@ -60,6 +60,7 @@ export * from './getTotalFromAttributes';
|
|
|
60
60
|
export * from './getTotalFromRelatedFeatures';
|
|
61
61
|
export * from './getRootElementId';
|
|
62
62
|
export * from './hasContainerBgImage';
|
|
63
|
+
export * from './isCrossOriginUrl';
|
|
63
64
|
export * from './isEmptyElementValue';
|
|
64
65
|
export * from './isEmptyValue';
|
|
65
66
|
export * from './isHiddenEmptyValue';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ведёт ли адрес на ЧУЖОЙ origin — сервер, который ничего не знает ни про наш токен, ни про
|
|
3
|
+
* наши заголовки.
|
|
4
|
+
*
|
|
5
|
+
* Относительный адрес (`/sp/resources/file/<id>`) всегда свой. Абсолютный сравнивается с
|
|
6
|
+
* `location.origin`, поэтому `https://<наш хост>/sp/...` тоже считается своим: его файлы
|
|
7
|
+
* закрыты авторизацией ровно так же, как у относительного пути.
|
|
8
|
+
*
|
|
9
|
+
* Проверка НЕ сводится к `^https?://`, как `isExternal` у вложений: там достаточно отличить
|
|
10
|
+
* ссылку от файла каталога, а здесь решается, отправлять ли на адрес `Authorization`.
|
|
11
|
+
*
|
|
12
|
+
* Разобрать адрес не удалось — считаем его своим: пусть с ним разбирается обычный путь
|
|
13
|
+
* загрузки, а не эта проверка.
|
|
14
|
+
*/
|
|
15
|
+
export declare const isCrossOriginUrl: (url?: string | null) => boolean;
|
package/dist/index.js
CHANGED
|
@@ -7226,6 +7226,31 @@ const useEditGroupAttributes = ({ elementConfig, type }) => {
|
|
|
7226
7226
|
return { filteredAttributes, filteredControls };
|
|
7227
7227
|
};
|
|
7228
7228
|
|
|
7229
|
+
/**
|
|
7230
|
+
* Ведёт ли адрес на ЧУЖОЙ origin — сервер, который ничего не знает ни про наш токен, ни про
|
|
7231
|
+
* наши заголовки.
|
|
7232
|
+
*
|
|
7233
|
+
* Относительный адрес (`/sp/resources/file/<id>`) всегда свой. Абсолютный сравнивается с
|
|
7234
|
+
* `location.origin`, поэтому `https://<наш хост>/sp/...` тоже считается своим: его файлы
|
|
7235
|
+
* закрыты авторизацией ровно так же, как у относительного пути.
|
|
7236
|
+
*
|
|
7237
|
+
* Проверка НЕ сводится к `^https?://`, как `isExternal` у вложений: там достаточно отличить
|
|
7238
|
+
* ссылку от файла каталога, а здесь решается, отправлять ли на адрес `Authorization`.
|
|
7239
|
+
*
|
|
7240
|
+
* Разобрать адрес не удалось — считаем его своим: пусть с ним разбирается обычный путь
|
|
7241
|
+
* загрузки, а не эта проверка.
|
|
7242
|
+
*/
|
|
7243
|
+
const isCrossOriginUrl = (url) => {
|
|
7244
|
+
if (!url)
|
|
7245
|
+
return false;
|
|
7246
|
+
try {
|
|
7247
|
+
return new URL(url, window.location.href).origin !== window.location.origin;
|
|
7248
|
+
}
|
|
7249
|
+
catch {
|
|
7250
|
+
return false;
|
|
7251
|
+
}
|
|
7252
|
+
};
|
|
7253
|
+
|
|
7229
7254
|
const useFetchWithAuth = (url, transform, cleanup) => {
|
|
7230
7255
|
const [data, setData] = React.useState(null);
|
|
7231
7256
|
const loadingRef = React.useRef(false);
|
|
@@ -7237,7 +7262,12 @@ const useFetchWithAuth = (url, transform, cleanup) => {
|
|
|
7237
7262
|
if (!url || loadingRef.current)
|
|
7238
7263
|
return;
|
|
7239
7264
|
loadingRef.current = true;
|
|
7240
|
-
|
|
7265
|
+
/*
|
|
7266
|
+
Токен уходит только на свой сервер. Чужому он не нужен и опасен — а вдобавок кастомный
|
|
7267
|
+
заголовок переводит кросс-доменный запрос в preflight-режим, который сторонние хранилища
|
|
7268
|
+
картинок не обслуживают: ответ на OPTIONS без Access-Control-Allow-Headers роняет весь fetch.
|
|
7269
|
+
*/
|
|
7270
|
+
const token = isCrossOriginUrl(url) ? null : window.localStorage.getItem(api.STORAGE_TOKEN_KEY);
|
|
7241
7271
|
fetch(url, {
|
|
7242
7272
|
headers: token ? { Authorization: `Bearer ${token}` } : {},
|
|
7243
7273
|
})
|
|
@@ -7266,7 +7296,20 @@ const useFetchWithAuth = (url, transform, cleanup) => {
|
|
|
7266
7296
|
};
|
|
7267
7297
|
|
|
7268
7298
|
const toObjectUrl = (res) => res.blob().then(blob => URL.createObjectURL(blob));
|
|
7269
|
-
|
|
7299
|
+
/**
|
|
7300
|
+
* `src` для картинки: свой файл тянется fetch'ем с токеном и отдаётся blob-адресом, чужой
|
|
7301
|
+
* возвращается как есть.
|
|
7302
|
+
*
|
|
7303
|
+
* Внешний адрес намеренно не фетчится: тег `img` грузит кросс-доменную картинку без всякого
|
|
7304
|
+
* CORS, а `fetch` — только если чужой сервер отдал `Access-Control-Allow-Origin`. Заодно
|
|
7305
|
+
* картинка остаётся в HTTP-кеше браузера, которого blob-адрес лишён. Ровно так же поступают
|
|
7306
|
+
* вложения: `useAttachmentPreviewImages` отдаёт внешней ссылке `src: item.link`.
|
|
7307
|
+
*/
|
|
7308
|
+
const useFetchImageWithAuth = (url) => {
|
|
7309
|
+
const isExternal = React.useMemo(() => isCrossOriginUrl(url), [url]);
|
|
7310
|
+
const blobUrl = useFetchWithAuth(isExternal ? null : url, toObjectUrl, URL.revokeObjectURL);
|
|
7311
|
+
return isExternal ? url : blobUrl;
|
|
7312
|
+
};
|
|
7270
7313
|
|
|
7271
7314
|
const HEIGHT_OFFSET = 20;
|
|
7272
7315
|
const FILL_OPACITY = 0.28;
|
|
@@ -16373,21 +16416,27 @@ const BarChartFilter = ({ type, config, elementConfig, filter }) => {
|
|
|
16373
16416
|
const DEFAULT_FROM_DATA = { minFromData: null, maxFromData: null };
|
|
16374
16417
|
const RangeDateFilter = ({ type, filter }) => {
|
|
16375
16418
|
const { t } = useGlobalContext();
|
|
16376
|
-
const { changeFilters, dataSources } = useWidgetContext(type);
|
|
16419
|
+
const { changeFilters, dataSources, filters } = useWidgetContext(type);
|
|
16377
16420
|
const { currentPage } = useWidgetPage(type);
|
|
16378
|
-
const [value, setValue] = React.useState([undefined, undefined]);
|
|
16379
|
-
const isInit = React.useRef(false);
|
|
16380
|
-
const isDefaultApplied = React.useRef(false);
|
|
16381
16421
|
const updateDebounce = useDebouncedCallback(FILTER_UPDATE_DEBOUNCE);
|
|
16382
16422
|
const { filters: configFilters } = currentPage;
|
|
16383
16423
|
const { filterName, label, minValue, maxValue, withTime } = filter.options;
|
|
16424
|
+
// Локальный стейт — копия значения из общего стора фильтров, поэтому при монтировании поднимаем
|
|
16425
|
+
// выбор оттуда. Контейнер фильтров размонтируется на время перезапроса источников (их стор
|
|
16426
|
+
// сбрасывается целиком, например, на смену геометрической выборки), и без этого выбранный
|
|
16427
|
+
// диапазон терялся бы, а следом его затирал дефолт.
|
|
16428
|
+
const [value, setValue] = React.useState(() => [getDate(filters[filterName]?.min)?.toISOString(), getDate(filters[filterName]?.max)?.toISOString()]);
|
|
16429
|
+
const isInit = React.useRef(false);
|
|
16430
|
+
// Ref фиксирует только первое значение выражения: если на момент монтирования выбор уже лежит
|
|
16431
|
+
// в сторе, дефолт считается применённым — перемонтирование не должно его затирать.
|
|
16432
|
+
const isDefaultApplied = React.useRef(!lodash.isNil(value[0]) || !lodash.isNil(value[1]));
|
|
16384
16433
|
const configFilter = React.useMemo(() => getConfigFilter(filterName, configFilters), [configFilters, filterName]);
|
|
16385
16434
|
const dataSource = React.useMemo(() => getDataSource(configFilter?.relatedDataSource, dataSources), [configFilter?.relatedDataSource, dataSources]);
|
|
16386
16435
|
const { minFromData, maxFromData } = React.useMemo(() => dataSource?.features?.reduce((prev, { properties }) => {
|
|
16387
16436
|
const date = getDate(properties[configFilter?.attributeValue]);
|
|
16388
16437
|
return {
|
|
16389
16438
|
minFromData: !prev.minFromData ? date : date < prev.minFromData ? date : prev.minFromData,
|
|
16390
|
-
maxFromData: !prev.maxFromData ? date : date > prev.maxFromData ? date : prev.maxFromData
|
|
16439
|
+
maxFromData: !prev.maxFromData ? date : date > prev.maxFromData ? date : prev.maxFromData,
|
|
16391
16440
|
};
|
|
16392
16441
|
}, DEFAULT_FROM_DATA) || DEFAULT_FROM_DATA, [configFilter?.attributeValue, dataSource?.features]);
|
|
16393
16442
|
const minDate = React.useMemo(() => minValue || minFromData?.toISOString(), [minFromData, minValue]);
|
|
@@ -16408,8 +16457,8 @@ const RangeDateFilter = ({ type, filter }) => {
|
|
|
16408
16457
|
[filterName]: {
|
|
16409
16458
|
value: [from, to],
|
|
16410
16459
|
min: from,
|
|
16411
|
-
max: to
|
|
16412
|
-
}
|
|
16460
|
+
max: to,
|
|
16461
|
+
},
|
|
16413
16462
|
}, configFilter?.resetFilters);
|
|
16414
16463
|
}, [changeFilters, configFilter?.resetFilters, defaultRange, filterName]);
|
|
16415
16464
|
const onChange = React.useCallback((newValue) => {
|
|
@@ -16436,8 +16485,8 @@ const RangeDateFilter = ({ type, filter }) => {
|
|
|
16436
16485
|
[filterName]: {
|
|
16437
16486
|
value,
|
|
16438
16487
|
min: value[0],
|
|
16439
|
-
max: value[1]
|
|
16440
|
-
}
|
|
16488
|
+
max: value[1],
|
|
16489
|
+
},
|
|
16441
16490
|
}, configFilter?.resetFilters);
|
|
16442
16491
|
});
|
|
16443
16492
|
}, [value]); // eslint-disable-line
|
|
@@ -17186,8 +17235,26 @@ const getFormattedAttributes = (t, data, attributes, config) => {
|
|
|
17186
17235
|
: attributes;
|
|
17187
17236
|
};
|
|
17188
17237
|
|
|
17238
|
+
/**
|
|
17239
|
+
* Адрес, который уже готов к употреблению: со схемой (`https:`, `data:`, `blob:`, `mailto:`),
|
|
17240
|
+
* протокол-относительный (`//cdn…`) или абсолютный путь (`/sp/…`).
|
|
17241
|
+
*
|
|
17242
|
+
* Идентификатор ресурса — GUID, схемы у него нет и с `/` он не начинается, поэтому под условие
|
|
17243
|
+
* не попадает и по-прежнему разворачивается в путь к файлу каталога.
|
|
17244
|
+
*/
|
|
17245
|
+
const READY_URL = /^(?:[a-z][a-z\d+\-.]*:|\/)/i;
|
|
17246
|
+
/**
|
|
17247
|
+
* Приводит значение конфига к адресу картинки, иконки или ссылки.
|
|
17248
|
+
*
|
|
17249
|
+
* Развилка одна: готовый адрес отдаётся как есть, всё остальное считается идентификатором
|
|
17250
|
+
* ресурса и разворачивается в путь к файлу каталога.
|
|
17251
|
+
*
|
|
17252
|
+
* Проверка идёт по схеме, а не по префиксу `http`: иначе `data:`-URI, `blob:`, `mailto:` и
|
|
17253
|
+
* `//cdn…` превращались бы в `/sp/resources/file/data:…` — путь к несуществующему файлу,
|
|
17254
|
+
* который дальше по течению неотличим от обычного внутреннего адреса и молча даёт 404.
|
|
17255
|
+
*/
|
|
17189
17256
|
const getResourceUrl = (url) => {
|
|
17190
|
-
return url ? (
|
|
17257
|
+
return url ? (READY_URL.test(url) ? url : `/sp/resources/file/${url}`) : "";
|
|
17191
17258
|
};
|
|
17192
17259
|
|
|
17193
17260
|
/**
|
|
@@ -18728,6 +18795,7 @@ exports.getTrackSizes = getTrackSizes;
|
|
|
18728
18795
|
exports.getWrapperSizeStyle = getWrapperSizeStyle;
|
|
18729
18796
|
exports.hasContainerBgImage = hasContainerBgImage;
|
|
18730
18797
|
exports.hexToRgba = hexToRgba;
|
|
18798
|
+
exports.isCrossOriginUrl = isCrossOriginUrl;
|
|
18731
18799
|
exports.isEmptyElementValue = isEmptyElementValue;
|
|
18732
18800
|
exports.isEmptyValue = isEmptyValue;
|
|
18733
18801
|
exports.isFeaturesFilterValue = isFeaturesFilterValue;
|