@evergis/react 4.0.127 → 4.0.129

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/react.esm.js CHANGED
@@ -7926,7 +7926,7 @@ const useRelatedDataSourceAttributes = ({ type = WidgetType.Dashboard, elementCo
7926
7926
  const layerInfo = useMemo(() => getDataSourceLayerInfo({ layerInfos, configDataSource, fetchedDataSource: dataSource }) ||
7927
7927
  EMPTY_DATA_SOURCE_LAYER_INFO, [configDataSource, dataSource, layerInfos]);
7928
7928
  const attributes = useMemo(() => getFeatureAttributes(feature, layerInfo, dataSource), [dataSource, feature, layerInfo]);
7929
- return { layerInfo, attributes, dataSource };
7929
+ return { layerInfo, attributes, dataSource, configDataSource };
7930
7930
  };
7931
7931
 
7932
7932
  const useRenderElement = (type = WidgetType.Dashboard, elementConfig) => {
@@ -11174,11 +11174,16 @@ const getSourceAttributes = (attributesConfiguration) => {
11174
11174
  return (attributes ?? []).filter(({ attributeName }) => attributeName !== geometryAttribute);
11175
11175
  };
11176
11176
  /**
11177
- * Конфиг может только ЗАПРЕТИТЬ правку, но не разрешить её там, где источник её не допускает:
11178
- * id-атрибут и вычисляемые поля сервер всё равно не примет.
11177
+ * Ограничения источника действуют там, где правка в него и уезжает: id-атрибут и вычисляемое
11178
+ * поле сервер всё равно не примет, поэтому конфиг может такую ячейку только ЗАПРЕТИТЬ, но не
11179
+ * разрешить.
11180
+ *
11181
+ * Источник без записи (EQL-запрос, python-скрипт, внешний url) их не накладывает вовсе: его
11182
+ * строки уходят только в фильтр, и «нередактируемо» там значит лишь «нельзя записать обратно».
11183
+ * Решает конфиг — по умолчанию правка разрешена.
11179
11184
  */
11180
- const resolveIsEditable = (description, source, idAttribute) => {
11181
- const sourceEditable = source
11185
+ const resolveIsEditable = ({ description, source, idAttribute, canWriteToSource }) => {
11186
+ const sourceEditable = source && canWriteToSource
11182
11187
  ? source.isEditable !== false &&
11183
11188
  source.attributeConfigurationType !== AttributeConfigurationType.Calculated &&
11184
11189
  source.attributeName !== idAttribute
@@ -11192,7 +11197,8 @@ const resolveStringFormat = (description, source) => {
11192
11197
  }
11193
11198
  return { ...source?.stringFormat, ...description?.stringFormat };
11194
11199
  };
11195
- const toSchemaAttribute = (description, source, idAttribute) => {
11200
+ const toSchemaAttribute = (params) => {
11201
+ const { description, source } = params;
11196
11202
  const attributeName = description?.attributeName ?? source?.attributeName;
11197
11203
  return {
11198
11204
  attributeName,
@@ -11200,7 +11206,7 @@ const toSchemaAttribute = (description, source, idAttribute) => {
11200
11206
  type: source?.type ?? description?.type ?? DEFAULT_ATTRIBUTE_TYPE,
11201
11207
  alias: description?.alias ?? source?.alias ?? attributeName,
11202
11208
  description: description?.description ?? source?.description,
11203
- isEditable: resolveIsEditable(description, source, idAttribute),
11209
+ isEditable: resolveIsEditable(params),
11204
11210
  stringFormat: resolveStringFormat(description, source),
11205
11211
  };
11206
11212
  };
@@ -11211,15 +11217,20 @@ const toSchemaAttribute = (description, source, idAttribute) => {
11211
11217
  * `alias`, `stringFormat` и `isEditable`, тогда как типы приходят от источника. Без описания
11212
11218
  * берутся все атрибуты источника. Без источника описание — единственная схема, и оно обязательно.
11213
11219
  */
11214
- const buildStructuredDataSchema = ({ attributesDescription, attributesConfiguration, }) => {
11220
+ const buildStructuredDataSchema = ({ attributesDescription, attributesConfiguration, canWriteToSource, }) => {
11215
11221
  const sourceAttributes = getSourceAttributes(attributesConfiguration);
11216
11222
  const { idAttribute } = attributesConfiguration || {};
11217
11223
  if (attributesDescription?.length) {
11218
11224
  return attributesDescription
11219
11225
  .filter(({ attributeName }) => !!attributeName)
11220
- .map(description => toSchemaAttribute(description, sourceAttributes.find(({ attributeName }) => attributeName === description.attributeName), idAttribute));
11226
+ .map(description => toSchemaAttribute({
11227
+ description,
11228
+ source: sourceAttributes.find(({ attributeName }) => attributeName === description.attributeName),
11229
+ idAttribute,
11230
+ canWriteToSource,
11231
+ }));
11221
11232
  }
11222
- return sourceAttributes.map(source => toSchemaAttribute(undefined, source, idAttribute));
11233
+ return sourceAttributes.map(source => toSchemaAttribute({ source, idAttribute, canWriteToSource }));
11223
11234
  };
11224
11235
 
11225
11236
  /**
@@ -11227,14 +11238,22 @@ const buildStructuredDataSchema = ({ attributesDescription, attributesConfigurat
11227
11238
  *
11228
11239
  * Источник резолвится через общий `useRelatedDataSourceAttributes`: он собирает layerInfo
11229
11240
  * реального слоя, а для EQL/python-источников — синтетический из атрибутов ответа.
11241
+ *
11242
+ * `layerName` берётся из конфига страницы, а не из загруженного источника: от него зависит,
11243
+ * какие ячейки редактируемы, и до окончания загрузки схема не должна меняться.
11230
11244
  */
11231
11245
  const useStructuredDataSchema = (type, elementConfig) => {
11232
11246
  const { dataSources } = useWidgetContext(type);
11233
11247
  const { attributesDescription, relatedDataSource } = elementConfig?.options || {};
11234
- const { layerInfo, dataSource } = useRelatedDataSourceAttributes({ type, elementConfig, dataSources });
11248
+ const { layerInfo, dataSource, configDataSource } = useRelatedDataSourceAttributes({
11249
+ type,
11250
+ elementConfig,
11251
+ dataSources,
11252
+ });
11253
+ const layerName = configDataSource?.layerName;
11235
11254
  const attributesConfiguration = useMemo(() => (relatedDataSource ? getAttributesConfiguration(layerInfo) : undefined), [layerInfo, relatedDataSource]);
11236
- const schema = useMemo(() => buildStructuredDataSchema({ attributesDescription, attributesConfiguration }), [attributesDescription, attributesConfiguration]);
11237
- return { schema, dataSource };
11255
+ const schema = useMemo(() => buildStructuredDataSchema({ attributesDescription, attributesConfiguration, canWriteToSource: !!layerName }), [attributesDescription, attributesConfiguration, layerName]);
11256
+ return { schema, dataSource, layerName };
11238
11257
  };
11239
11258
 
11240
11259
  /**
@@ -11243,14 +11262,14 @@ const useStructuredDataSchema = (type, elementConfig) => {
11243
11262
  * Правку открывает `options.editMode`: не задан или `false` — таблица целиком на чтение, без
11244
11263
  * редакторов ячеек, добавления, удаления и кнопок сохранения.
11245
11264
  *
11246
- * Источник без `layerName` (EQL, python-скрипт, внешний url) записи не поддерживает контейнер
11247
- * деградирует в «ручную структуру поверх данных источника»: строки правятся и уезжают в фильтр,
11248
- * но добавление и удаление строк недоступны.
11265
+ * Вид источника на права правки не влияет от него зависит только адресат «Сохранить». Слой
11266
+ * принимает запись через features-API, источник без `layerName` (EQL, python-скрипт, внешний
11267
+ * url) не принимает ничего, и правка такой таблицы уходит только в фильтр.
11249
11268
  */
11250
11269
  const useStructuredData = (type, elementConfig) => {
11251
11270
  const { filters } = useWidgetContext(type);
11252
11271
  const { relatedDataSource, filterName, editMode } = elementConfig?.options || {};
11253
- const { schema, dataSource } = useStructuredDataSchema(type, elementConfig);
11272
+ const { schema, dataSource, layerName } = useStructuredDataSchema(type, elementConfig);
11254
11273
  const hasDataSource = !!relatedDataSource;
11255
11274
  const filterValue = filterName ? filters?.[filterName]?.value : undefined;
11256
11275
  const { rows, visibleRows, dirty, changeCell, addRow, deleteRow, reset, commitSaved } = useStructuredDataDraft({
@@ -11262,14 +11281,13 @@ const useStructuredData = (type, elementConfig) => {
11262
11281
  const { save, saving } = useStructuredDataSave({
11263
11282
  type,
11264
11283
  schema,
11265
- layerName: dataSource?.layerName,
11284
+ layerName,
11266
11285
  filterName,
11267
11286
  });
11268
11287
  const [deletingKey, setDeletingKey] = useState(null);
11269
11288
  const loading = hasDataSource && (!dataSource || dataSource.features === undefined);
11270
11289
  const hasError = hasDataSource && dataSource?.features === null;
11271
11290
  const canEdit = !!editMode;
11272
- const canEditRows = canEdit && (!hasDataSource || !!dataSource?.layerName);
11273
11291
  const onSave = useCallback(async () => {
11274
11292
  const savedRows = await save(rows);
11275
11293
  if (savedRows) {
@@ -11278,7 +11296,9 @@ const useStructuredData = (type, elementConfig) => {
11278
11296
  }, [commitSaved, rows, save]);
11279
11297
  const onRowDelete = useCallback((key) => setDeletingKey(key), []);
11280
11298
  const onDeleteCancel = useCallback(() => setDeletingKey(null), []);
11281
- const isDeletingSourceRow = useMemo(() => !!rows.find(({ key }) => key === deletingKey)?.featureId, [deletingKey, rows]);
11299
+ // Про необратимость предупреждаем только там, где удаление и правда дойдёт до источника:
11300
+ // у строки источника без записи id есть, но удаление уберёт её лишь из значения фильтра.
11301
+ const isDeletingSourceRow = useMemo(() => !!layerName && !!rows.find(({ key }) => key === deletingKey)?.featureId, [deletingKey, layerName, rows]);
11282
11302
  const onDeleteConfirm = useCallback(() => {
11283
11303
  if (deletingKey) {
11284
11304
  deleteRow(deletingKey);
@@ -11290,10 +11310,9 @@ const useStructuredData = (type, elementConfig) => {
11290
11310
  rows: visibleRows,
11291
11311
  loading,
11292
11312
  canEdit,
11293
- canDeleteRows: canEditRows,
11294
11313
  onCellChange: changeCell,
11295
11314
  onRowDelete,
11296
- }), [canEdit, canEditRows, changeCell, loading, onRowDelete, schema, visibleRows]);
11315
+ }), [canEdit, changeCell, loading, onRowDelete, schema, visibleRows]);
11297
11316
  return {
11298
11317
  contextValue,
11299
11318
  dirty,
@@ -11301,7 +11320,6 @@ const useStructuredData = (type, elementConfig) => {
11301
11320
  loading,
11302
11321
  hasError,
11303
11322
  canEdit,
11304
- canAddRows: canEditRows,
11305
11323
  isDeleteConfirmOpen: !!deletingKey,
11306
11324
  isDeletingSourceRow,
11307
11325
  onAddRow: addRow,
@@ -11322,11 +11340,11 @@ const useStructuredData = (type, elementConfig) => {
11322
11340
  const StructuredDataContainer = memo(({ type, elementConfig, isVisible, renderElement }) => {
11323
11341
  const { t } = useGlobalContext();
11324
11342
  const { root, body } = useContainerRoot({ elementConfig });
11325
- const { contextValue, dirty, saving, hasError, canEdit, canAddRows, isDeleteConfirmOpen, isDeletingSourceRow, onAddRow, onCancel, onSave, onDeleteConfirm, onDeleteCancel, } = useStructuredData(type, elementConfig);
11343
+ const { contextValue, dirty, saving, hasError, canEdit, isDeleteConfirmOpen, isDeletingSourceRow, onAddRow, onCancel, onSave, onDeleteConfirm, onDeleteCancel, } = useStructuredData(type, elementConfig);
11326
11344
  if (hasError) {
11327
11345
  return jsx(DataSourceError, { name: elementConfig?.templateName });
11328
11346
  }
11329
- return (jsxs(ContainerRoot, { ...root, children: [jsx(ExpandableTitle, { elementConfig: elementConfig, type: type, renderElement: renderElement }), isVisible && (jsxs(Container, { ...body, isColumn: true, children: [jsx(StructuredDataView, { children: jsx(StructuredDataContext.Provider, { value: contextValue, children: renderElement({ id: "data" }) }) }), canEdit && (jsxs(StructuredDataToolbar, { children: [jsx(StructuredDataActions, { children: canAddRows && (jsx(IconButton, { kind: "plus", primary: true, disabled: saving, onClick: onAddRow, children: t("structuredData.addRow", { ns: "dashboard", defaultValue: "Добавить строку" }) })) }), dirty && (jsxs(StructuredDataActions, { children: [jsx(FlatButton, { disabled: saving, onClick: onCancel, children: t("actions.cancel", { ns: "common", defaultValue: "Отменить" }) }), jsx(RaisedButton, { primary: true, disabled: saving, onClick: onSave, children: t("actions.save", { ns: "common", defaultValue: "Сохранить" }) })] }))] }))] })), jsx(DeleteRowDialog, { isOpen: isDeleteConfirmOpen, isSourceRow: isDeletingSourceRow, onConfirm: onDeleteConfirm, onCancel: onDeleteCancel })] }));
11347
+ return (jsxs(ContainerRoot, { ...root, children: [jsx(ExpandableTitle, { elementConfig: elementConfig, type: type, renderElement: renderElement }), isVisible && (jsxs(Container, { ...body, isColumn: true, children: [jsx(StructuredDataView, { children: jsx(StructuredDataContext.Provider, { value: contextValue, children: renderElement({ id: "data" }) }) }), canEdit && (jsxs(StructuredDataToolbar, { children: [jsx(StructuredDataActions, { children: jsx(IconButton, { kind: "plus", primary: true, disabled: saving, onClick: onAddRow, children: t("structuredData.addRow", { ns: "dashboard", defaultValue: "Добавить строку" }) }) }), dirty && (jsxs(StructuredDataActions, { children: [jsx(FlatButton, { disabled: saving, onClick: onCancel, children: t("actions.cancel", { ns: "common", defaultValue: "Отменить" }) }), jsx(RaisedButton, { primary: true, disabled: saving, onClick: onSave, children: t("actions.save", { ns: "common", defaultValue: "Сохранить" }) })] }))] }))] })), jsx(DeleteRowDialog, { isOpen: isDeleteConfirmOpen, isSourceRow: isDeletingSourceRow, onConfirm: onDeleteConfirm, onCancel: onDeleteCancel })] }));
11330
11348
  });
11331
11349
 
11332
11350
  const TabAnchor = styled.div.withConfig({ displayName: "TabAnchor", componentId: "sc-emqf31" }) `
@@ -14282,12 +14300,16 @@ const TableWrapper = styled.table.withConfig({ displayName: "TableWrapper", comp
14282
14300
  border-collapse: collapse;
14283
14301
  font-size: 0.875rem;
14284
14302
  `;
14285
- /** Шапка липнет к верху области прокрутки — при длинной таблице заголовки остаются видны. */
14303
+ /**
14304
+ * Шапка липнет к верху области прокрутки — при длинной таблице заголовки остаются видны.
14305
+ *
14306
+ * Собственного фона у шапки нет: цвет из палитры не следовал за подложкой контейнера
14307
+ * и выделялся белым прямоугольником на любом фоне, отличном от базового.
14308
+ */
14286
14309
  const TableHead = styled.thead.withConfig({ displayName: "TableHead", componentId: "sc-pa89d3" }) `
14287
14310
  position: sticky;
14288
14311
  top: 0;
14289
14312
  z-index: 1;
14290
- background: ${({ theme }) => theme.palette.background};
14291
14313
  `;
14292
14314
  const TableHeadCell = styled.th.withConfig({ displayName: "TableHeadCell", componentId: "sc-1f151xj" }) `
14293
14315
  padding: 0.375rem 0.5rem;
@@ -14601,7 +14623,7 @@ const ElementTable = memo(({ elementConfig }) => {
14601
14623
  defaultValue: "Схема данных не задана",
14602
14624
  }) }));
14603
14625
  }
14604
- return (jsxs(TableWrapper, { children: [jsx(TableHead, { children: jsxs("tr", { children: [columns.map(({ attributeName, alias, description }) => (jsx(TableHeadCell, { title: description || alias, "$sortable": sortEnabled, onClick: sortEnabled ? () => onSortToggle(attributeName) : undefined, children: jsxs(TableHeadContent, { children: [alias, sort?.attributeName === attributeName && (jsx(Icon, { kind: sort.direction === "asc" ? "sorting_asc" : "sorting_des" }))] }) }, attributeName))), context.canDeleteRows && jsx(TableHeadCell, {})] }) }), jsx("tbody", { children: rows.map(row => (jsxs(TableRow, { children: [columns.map(attribute => (jsx(TableCellWrapper, { children: jsx(TableCell, { attribute: attribute, row: row, canEdit: context.canEdit, onChange: context.onCellChange }) }, attribute.attributeName))), context.canDeleteRows && (jsx(TableActionsCell, { children: jsx(IconButton, { kind: "delete", title: t("actions.delete", { ns: "common", defaultValue: "Удалить" }), onClick: () => context.onRowDelete(row.key) }) }))] }, row.key))) })] }));
14626
+ return (jsxs(TableWrapper, { children: [jsx(TableHead, { children: jsxs("tr", { children: [columns.map(({ attributeName, alias, description }) => (jsx(TableHeadCell, { title: description || alias, "$sortable": sortEnabled, onClick: sortEnabled ? () => onSortToggle(attributeName) : undefined, children: jsxs(TableHeadContent, { children: [alias, sort?.attributeName === attributeName && (jsx(Icon, { kind: sort.direction === "asc" ? "sorting_asc" : "sorting_des" }))] }) }, attributeName))), context.canEdit && jsx(TableHeadCell, {})] }) }), jsx("tbody", { children: rows.map(row => (jsxs(TableRow, { children: [columns.map(attribute => (jsx(TableCellWrapper, { children: jsx(TableCell, { attribute: attribute, row: row, canEdit: context.canEdit, onChange: context.onCellChange }) }, attribute.attributeName))), context.canEdit && (jsx(TableActionsCell, { children: jsx(IconButton, { kind: "delete", title: t("actions.delete", { ns: "common", defaultValue: "Удалить" }), onClick: () => context.onRowDelete(row.key) }) }))] }, row.key))) })] }));
14605
14627
  });
14606
14628
 
14607
14629
  const TooltipIcon = styled(Icon).withConfig({ displayName: "TooltipIcon", componentId: "sc-1lkxudm" }) `
@@ -17548,7 +17570,9 @@ const RasterLayer = ({ layer, tileUrl, visible, beforeId, }) => {
17548
17570
  const VectorLayer = ({ layer, tileUrl, visible, beforeId, getLayerTempStyle, }) => {
17549
17571
  const layerConfiguration = layer?.configuration;
17550
17572
  const clientStyle = layerConfiguration?.clientStyle;
17551
- const currentStyle = useMemo(() => getLayerTempStyle(layer.name) ?? clientStyle, [clientStyle, getLayerTempStyle, layer.name]);
17573
+ const currentStyle = useMemo(() => !isNil(getLayerTempStyle?.(layer.name)) && !isEmpty(getLayerTempStyle?.(layer.name))
17574
+ ? getLayerTempStyle(layer.name)
17575
+ : clientStyle, [clientStyle, getLayerTempStyle, layer.name]);
17552
17576
  const idAttribute = layerConfiguration?.attributesConfiguration?.idAttribute;
17553
17577
  const tiles = useMemo(() => [tileUrl], [tileUrl]);
17554
17578
  // Собираем конфигурации иконок для загрузки из clientStyle
@@ -17561,8 +17585,19 @@ const VectorLayer = ({ layer, tileUrl, visible, beforeId, getLayerTempStyle, })
17561
17585
  return [];
17562
17586
  }
17563
17587
  return iconNames.map(name => {
17564
- return currentStyle.icons?.find((icon) => icon.name === name) ?? null;
17565
- }).filter(Boolean);
17588
+ const customIcon = currentStyle.icons?.find((icon) => icon.name === name);
17589
+ if (customIcon) {
17590
+ return {
17591
+ name: customIcon.name,
17592
+ url: customIcon.url,
17593
+ sdf: customIcon.sdf,
17594
+ size: customIcon.size,
17595
+ pixelRatio: customIcon.pixelRatio,
17596
+ };
17597
+ }
17598
+ // Fallback - иконка не найдена в конфиге, пропускаем
17599
+ return null;
17600
+ }).filter((config) => config !== null);
17566
17601
  }, [currentStyle]);
17567
17602
  useMapImages({ images: iconConfigs });
17568
17603
  const renderClientStyle = useCallback(() => {