@evergis/react 4.0.104 → 4.0.105

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -3402,6 +3402,11 @@ const asResourceId = (value) => value;
3402
3402
  const CHART_TYPES = ["bar", "line", "pie", "stack"];
3403
3403
  /** Выравнивание текста/блоков. */
3404
3404
  const ALIGNMENTS = ["left", "center", "right"];
3405
+ /**
3406
+ * Выравнивание плиток контейнера DataSource+RoundedBackground: к базовым `left/center/right`
3407
+ * добавлен режим `stretch` — плитки строки растягиваются на всю ширину (ТЗ 9.4/9.5).
3408
+ */
3409
+ const TILE_ALIGNMENTS = ["left", "center", "right", "stretch"];
3405
3410
  /** Выравнивание детей контейнера по поперечной оси (CSS `align-items`). */
3406
3411
  const ALIGN_ITEMS = ["flex-start", "center", "flex-end", "stretch", "baseline"];
3407
3412
  /** Масштабирование изображения внутри своего бокса (CSS `object-fit`). */
@@ -7219,6 +7224,56 @@ const useDiffPage = (type) => {
7219
7224
  return React.useMemo(() => isDiffPage, [isDiffPage]);
7220
7225
  };
7221
7226
 
7227
+ /**
7228
+ * Уравнивает ширину всех плиток контейнера по самой широкой.
7229
+ *
7230
+ * Натуральная ширина каждой плитки измеряется через временное inline `width: max-content`
7231
+ * (снимает применённое ограничение `--tile-width`, поэтому замер устойчив к динамике данных и не
7232
+ * зацикливает ResizeObserver — итоговый максимум совпадает с уже применённым). Возвращаемое число
7233
+ * контейнер прокидывает в CSS-переменную `--tile-width`, которую читает каждая плитка.
7234
+ *
7235
+ * `enabled: false` (режим «Растянуть», «В столбец» или старые конфиги без `columns`) полностью
7236
+ * отключает измерение — ширина берётся из grid-ячейки.
7237
+ */
7238
+ const useEqualTileWidth = (enabled, itemsCount) => {
7239
+ const ref = React.useRef(null);
7240
+ const [width, setWidth] = React.useState();
7241
+ const measure = React.useCallback(() => {
7242
+ const node = ref.current;
7243
+ if (!node)
7244
+ return;
7245
+ const tiles = Array.from(node.querySelectorAll("[data-tile]"));
7246
+ if (!tiles.length)
7247
+ return;
7248
+ tiles.forEach(tile => {
7249
+ tile.style.width = "max-content";
7250
+ });
7251
+ let max = 0;
7252
+ tiles.forEach(tile => {
7253
+ max = Math.max(max, tile.getBoundingClientRect().width);
7254
+ });
7255
+ tiles.forEach(tile => {
7256
+ tile.style.width = "";
7257
+ });
7258
+ const next = Math.ceil(max);
7259
+ setWidth(previous => (max && previous !== next ? next : previous));
7260
+ }, []);
7261
+ React.useEffect(() => {
7262
+ if (!enabled) {
7263
+ setWidth(undefined);
7264
+ return;
7265
+ }
7266
+ const node = ref.current;
7267
+ if (!node)
7268
+ return;
7269
+ measure();
7270
+ const observer = new ResizeObserver(() => measure());
7271
+ observer.observe(node);
7272
+ return () => observer.disconnect();
7273
+ }, [enabled, itemsCount, measure]);
7274
+ return [ref, enabled ? width : undefined];
7275
+ };
7276
+
7222
7277
  const SAVE_HOOK_RESULT_DURATION = 4000;
7223
7278
  const NOTIFICATION_ID_RADIX = 36;
7224
7279
  const NOTIFICATION_ID_LENGTH = 8;
@@ -8686,33 +8741,70 @@ const IconContainer = React.memo(({ elementConfig, renderElement }) => {
8686
8741
  return (jsxRuntime.jsxs(IconContainerWrapper, { id: id, style: style, children: [jsxRuntime.jsxs(IconContainerHeaderWrapper, { children: [jsxRuntime.jsxs(IconContainerHeader, { children: [renderElement({ id: "icon" }), jsxRuntime.jsx(IconContainerTitle, { children: renderElement({ id: "alias" }) })] }), renderElement({ id: "link" })] }), jsxRuntime.jsx(IconContainerText, { children: renderElement({ id: "text" }) })] }));
8687
8742
  });
8688
8743
 
8689
- /**
8690
- * Обёртка записей источника.
8691
- *
8692
- * В ряду (`column: false`) записи переносятся на новую строку. Без переноса они дрались бы за
8693
- * одну строку: обёртка каждой записи — flex-элемент и ужимается, а внутренний шаблон (плашка
8694
- * RoundedBackground имеет жёсткие 9rem) — нет, поэтому записи наезжали бы друг на друга.
8695
- *
8696
- * `flex-wrap` намеренно не вынесен в row-ветку самого `Container`: там дети — карточки
8697
- * контейнеров шириной 100%, и перенос уложил бы их по одной на строку, сломав ряд у
8698
- * ContainersGroup.
8699
- */
8744
+ /** Дефолтный внутренний отступ между плитками, px (ТЗ §11). */
8745
+ const DEFAULT_TILE_GAP = 8;
8746
+ /** Раскладка ряда плиток (grid `justify-content`) для не-`stretch` выравнивания. */
8747
+ const ROW_JUSTIFY = {
8748
+ left: "start",
8749
+ center: "center",
8750
+ right: "end",
8751
+ stretch: "stretch",
8752
+ };
8753
+ /** Горизонтальное положение плитки в режиме «В столбец» (CSS `align-items`). */
8754
+ const COLUMN_ALIGN_ITEMS = {
8755
+ left: "flex-start",
8756
+ center: "center",
8757
+ right: "flex-end",
8758
+ stretch: "stretch",
8759
+ };
8760
+
8700
8761
  const DataSourceContainerWrapper = styled(Container).withConfig({ displayName: "DataSourceContainerWrapper", componentId: "sc-1xnd27m" }) `
8701
- ${({ $isRow }) => $isRow &&
8702
- styled.css `
8703
- flex-wrap: wrap;
8704
- justify-content: flex-start;
8705
- column-gap: 0.5rem;
8706
- `};
8762
+ ${({ $tileWidth }) => $tileWidth != null && `--tile-width: ${$tileWidth}px;`}
8763
+
8764
+ ${({ $isRow, $columns, $gap, $align, $stretch }) => {
8765
+ if ($isRow) {
8766
+ if ($columns) {
8767
+ return styled.css `
8768
+ display: grid;
8769
+ grid-template-columns: repeat(${$columns}, ${$stretch ? "minmax(0, 1fr)" : "auto"});
8770
+ justify-content: ${$stretch ? "stretch" : ROW_JUSTIFY[$align ?? "left"]};
8771
+ align-items: stretch;
8772
+ gap: ${$gap ?? DEFAULT_TILE_GAP}px;
8773
+ `;
8774
+ }
8775
+ return styled.css `
8776
+ flex-wrap: wrap;
8777
+ justify-content: flex-start;
8778
+ gap: ${$gap != null ? `${$gap}px` : "0.5rem"};
8779
+ `;
8780
+ }
8781
+ return styled.css `
8782
+ align-items: ${$stretch ? "stretch" : COLUMN_ALIGN_ITEMS[$align ?? "left"]};
8783
+
8784
+ > * {
8785
+ width: ${$stretch ? "100%" : "auto"};
8786
+ }
8787
+
8788
+ /* Вертикальный отступ между плитками в столбце — настраиваемый gap (ТЗ §7): переопределяем
8789
+ фикс. margin-bottom 0.5rem, который навешивает детям база Container(isColumn). */
8790
+ && > *:not(:last-child) {
8791
+ margin-bottom: ${$gap != null ? `${$gap}px` : `${DEFAULT_TILE_GAP}px`};
8792
+ }
8793
+ `;
8794
+ }};
8707
8795
  `;
8708
8796
 
8709
8797
  const DataSourceContainer = React.memo(({ config, elementConfig, type, innerComponent, renderElement }) => {
8798
+ const { t } = useGlobalContext();
8710
8799
  const { dataSources, expandedContainers } = useWidgetContext(type);
8711
8800
  const { id, options } = elementConfig || {};
8712
- const { column = true, relatedDataSource, expandable, expanded } = options || {};
8801
+ const { column = true, relatedDataSource, expandable, expanded, columns, gap, align } = options || {};
8713
8802
  const isVisible = isVisibleContainer(id, expandable, expanded, expandedContainers);
8803
+ const { sliceItems, checkIsSliced, showMore, onShowMore } = useShownOtherItems(options);
8714
8804
  const dataSource = React.useMemo(() => dataSources?.find(({ name }) => name === relatedDataSource), [dataSources, relatedDataSource]);
8715
8805
  const isLoading = !dataSource?.features;
8806
+ const stretch = align === "stretch";
8807
+ const [tilesRef, tileWidth] = useEqualTileWidth(!column && !!columns && !stretch, dataSource?.features?.length ?? 0);
8716
8808
  const defaults = React.useMemo(() => ({ height: isLoading ? "3rem" : "auto" }), [isLoading]);
8717
8809
  const root = useWrapperSize({ elementConfig, defaults });
8718
8810
  if (!relatedDataSource)
@@ -8720,7 +8812,9 @@ const DataSourceContainer = React.memo(({ config, elementConfig, type, innerComp
8720
8812
  if (dataSource && !dataSource.features) {
8721
8813
  return jsxRuntime.jsx(DataSourceError, { name: elementConfig.templateName });
8722
8814
  }
8723
- return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(ExpandableTitle, { elementConfig: elementConfig, type: type, renderElement: renderElement }), !isVisible ? null : dataSource ? (jsxRuntime.jsx(DataSourceContainerWrapper, { ...root, isColumn: column, "$isRow": !column, children: dataSource.features?.map((feature, index) => (jsxRuntime.jsx(DataSourceInnerContainer, { index: index, type: type, config: config, feature: feature, elementConfig: elementConfig, innerComponent: innerComponent }, index))) })) : (jsxRuntime.jsx(ContainerLoading, {}))] }));
8815
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(ExpandableTitle, { elementConfig: elementConfig, type: type, renderElement: renderElement }), !isVisible ? null : dataSource ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(DataSourceContainerWrapper, { ...root, ref: tilesRef, isColumn: column, "$isRow": !column, "$columns": columns, "$gap": gap, "$align": align, "$stretch": stretch, "$tileWidth": tileWidth, children: sliceItems(dataSource.features)?.map((feature, index) => (jsxRuntime.jsx(DataSourceInnerContainer, { index: index, type: type, config: config, feature: feature, elementConfig: elementConfig, innerComponent: innerComponent }, index))) }), checkIsSliced(dataSource.features) && (jsxRuntime.jsx(ContainerToggler, { toggled: showMore, onClick: onShowMore, children: showMore
8816
+ ? t("hide", { ns: "dashboard", defaultValue: "Свернуть" })
8817
+ : t("showAll", { ns: "dashboard", defaultValue: "Показать все" }) }))] })) : (jsxRuntime.jsx(ContainerLoading, {}))] }));
8724
8818
  });
8725
8819
 
8726
8820
  const SvgContainerColorMixin = styled.css `
@@ -8743,11 +8837,11 @@ const SvgContainer = styled.div.withConfig({ displayName: "SvgContainer", compon
8743
8837
  }
8744
8838
  `;
8745
8839
 
8746
- /** Выравнивание заголовка по горизонтали — в терминах flex-раскладки. */
8747
8840
  const ALIGN_TO_JUSTIFY = {
8748
8841
  left: "flex-start",
8749
8842
  center: "center",
8750
8843
  right: "flex-end",
8844
+ stretch: "flex-start",
8751
8845
  };
8752
8846
  const ContainerIconTitle = styled(uilibGl.Flex).withConfig({ displayName: "ContainerIconTitle", componentId: "sc-1jfnkqo" }) `
8753
8847
  /* inline-flex, а не блочный flex во всю ширину: иначе text-align родителя игнорируется. */
@@ -9025,16 +9119,14 @@ const ContainerIconValue = styled(uilibGl.Flex).withConfig({ displayName: "Conta
9025
9119
  const RoundedBackgroundContainerWrapper = styled(uilibGl.Flex).withConfig({ displayName: "RoundedBackgroundContainerWrapper", componentId: "sc-a1l8h3" }) `
9026
9120
  position: relative;
9027
9121
  flex-direction: ${({ $bigIcon }) => ($bigIcon ? "row" : "column")};
9028
- width: 9rem;
9122
+ width: ${({ $fillWidth }) => ($fillWidth ? "var(--tile-width, 100%)" : "9rem")};
9123
+ height: 100%;
9124
+ box-sizing: border-box;
9029
9125
  padding: 0.75rem 0.75rem 0.5rem;
9030
9126
  background-color: ${({ theme: { palette } }) => palette.element};
9031
9127
  border-radius: ${({ theme: { borderRadius } }) => borderRadius.large};
9032
9128
  flex-wrap: nowrap;
9033
9129
 
9034
- && {
9035
- margin-bottom: 0.5rem;
9036
- }
9037
-
9038
9130
  ${({ $center }) => $center &&
9039
9131
  styled.css `
9040
9132
  align-items: center;
@@ -9051,7 +9143,7 @@ const RoundedBackgroundContainerWrapper = styled(uilibGl.Flex).withConfig({ disp
9051
9143
  styled.css `
9052
9144
  background-color: ${transparentizeColor($color, 6)};
9053
9145
 
9054
- * {
9146
+ ${ContainerValue}, ${ContainerUnits}, ${ContainerAlias} {
9055
9147
  color: ${$color};
9056
9148
  fill: ${$color};
9057
9149
  }
@@ -9111,11 +9203,13 @@ const RoundedBackgroundContainerWrapper = styled(uilibGl.Flex).withConfig({ disp
9111
9203
  }
9112
9204
 
9113
9205
  ${ContainerAlias} {
9114
- margin-top: 0.25rem;
9206
+ margin-top: auto;
9207
+ padding-top: 0.25rem;
9115
9208
  }
9116
9209
  `;
9117
9210
 
9118
9211
  const ALIAS_DEFAULT_MAX_LENGTH = 28;
9212
+ const DESCRIPTION_DEFAULT_MAX_LINES = 2;
9119
9213
  const RoundedBackgroundContainer = React.memo(({ type, elementConfig, feature, renderElement, }) => {
9120
9214
  const { t } = useGlobalContext();
9121
9215
  const { dataSources, attributes: ctxAttributes } = useWidgetContext(type);
@@ -9127,7 +9221,7 @@ const RoundedBackgroundContainer = React.memo(({ type, elementConfig, feature, r
9127
9221
  });
9128
9222
  const attributes = lodash.isEmpty(dsAttributes) ? ctxAttributes : dsAttributes;
9129
9223
  const { id, options, style, children } = elementConfig || {};
9130
- const { maxLength, wordBreak, center, fontColor, innerTemplateStyle, inlineUnits, big, bigIcon, hideEmpty, colorAttribute } = options || {};
9224
+ const { maxLength, maxLines, wordBreak, center, fontColor, innerTemplateStyle, inlineUnits, big, bigIcon, hideEmpty, colorAttribute, columns } = options || {};
9131
9225
  const iconElement = children?.find(item => item.id === "icon");
9132
9226
  const aliasElement = children?.find(item => item.id === "alias");
9133
9227
  const unitsElement = children?.find(item => item.id === "units");
@@ -9146,11 +9240,35 @@ const RoundedBackgroundContainer = React.memo(({ type, elementConfig, feature, r
9146
9240
  return null;
9147
9241
  return (jsxRuntime.jsx(uilibGl.FlexSpan, { width: iconElement.options?.width || "1rem", alignItems: "center", mr: "0.5rem", children: renderElement({ id: "icon", wrap: false }) }));
9148
9242
  }, [iconElement, renderElement]);
9149
- const renderAlias = React.useMemo(() => (jsxRuntime.jsx(ContainerAlias, { style: aliasElement?.style, children: jsxRuntime.jsx(TextTrim, { maxLength: maxLength || ALIAS_DEFAULT_MAX_LENGTH, wordBreak: wordBreak, children: renderElement({ id: "alias", wrap: false }) }) })), [aliasElement?.style, maxLength, renderElement, wordBreak]);
9150
- const renderValue = React.useMemo(() => lodash.isNil(value) ? null : (jsxRuntime.jsxs(ContainerValue, { style: valueElement?.style, big: true, children: [value, !!unitsElement && (jsxRuntime.jsx(ContainerUnits, { style: unitsElement?.style, children: renderElement({ id: "units" }) }))] })), [valueElement?.style, value, unitsElement, renderElement]);
9243
+ const aliasAttribute = React.useMemo(() => aliasElement && !aliasElement.type && aliasElement.attributeName
9244
+ ? attributes.find(({ attributeName }) => attributeName === aliasElement.attributeName)
9245
+ : undefined, [attributes, aliasElement]);
9246
+ const aliasContent = React.useMemo(() => aliasAttribute
9247
+ ? formatAttributeValue({
9248
+ t,
9249
+ type: aliasAttribute.type,
9250
+ value: aliasAttribute.value,
9251
+ stringFormat: aliasAttribute.stringFormat,
9252
+ noUnits: true,
9253
+ })
9254
+ : renderElement({ id: "alias", wrap: false }), [aliasAttribute, t, renderElement]);
9255
+ const renderAlias = React.useMemo(() => (jsxRuntime.jsx(ContainerAlias, { style: aliasElement?.style, children: jsxRuntime.jsx(TextTrim, { maxLength: maxLength || ALIAS_DEFAULT_MAX_LENGTH, maxLines: maxLines ?? (columns ? DESCRIPTION_DEFAULT_MAX_LINES : undefined), wordBreak: wordBreak, children: aliasContent }) })), [aliasElement?.style, maxLength, maxLines, columns, aliasContent, wordBreak]);
9256
+ const unitsAttribute = React.useMemo(() => unitsElement && !unitsElement.type && unitsElement.attributeName
9257
+ ? attributes.find(({ attributeName }) => attributeName === unitsElement.attributeName)
9258
+ : undefined, [attributes, unitsElement]);
9259
+ const unitsContent = React.useMemo(() => unitsAttribute
9260
+ ? formatAttributeValue({
9261
+ t,
9262
+ type: unitsAttribute.type,
9263
+ value: unitsAttribute.value,
9264
+ stringFormat: unitsAttribute.stringFormat,
9265
+ noUnits: true,
9266
+ })
9267
+ : renderElement({ id: "units" }), [unitsAttribute, t, renderElement]);
9268
+ const renderValue = React.useMemo(() => lodash.isNil(value) ? null : (jsxRuntime.jsxs(ContainerValue, { style: valueElement?.style, big: true, children: [value, !!unitsElement && (jsxRuntime.jsx(ContainerUnits, { style: unitsElement?.style, children: unitsContent }))] })), [valueElement?.style, value, unitsElement, unitsContent]);
9151
9269
  if (lodash.isNil(value) && hideEmpty)
9152
9270
  return null;
9153
- return (jsxRuntime.jsxs(RoundedBackgroundContainerWrapper, { id: id, style: innerTemplateStyle || style, "$center": center, "$color": color, "$inlineUnits": inlineUnits, "$big": big, "$bigIcon": bigIcon, children: [jsxRuntime.jsxs(ContainerIconValue, { children: [renderIcon, big ? renderAlias : renderValue] }), big ? renderValue : renderAlias] }));
9271
+ return (jsxRuntime.jsxs(RoundedBackgroundContainerWrapper, { id: id, "data-tile": true, style: innerTemplateStyle || style, "$center": center !== false, "$color": color, "$inlineUnits": inlineUnits, "$big": big, "$bigIcon": bigIcon, "$fillWidth": !!columns, children: [jsxRuntime.jsxs(ContainerIconValue, { children: [renderIcon, big ? renderAlias : renderValue] }), big ? renderValue : renderAlias] }));
9154
9272
  });
9155
9273
 
9156
9274
  const AddFeatureContainer = React.memo(({ elementConfig }) => {
@@ -9425,23 +9543,16 @@ exports.EditGeometryType = void 0;
9425
9543
 
9426
9544
  exports.StyligClassificationTypes = void 0;
9427
9545
  (function (StyligClassificationTypes) {
9428
- StyligClassificationTypes["FillColor"] = "fill";
9429
- StyligClassificationTypes["CircleColor"] = "circle";
9430
- StyligClassificationTypes["LineColor"] = "line";
9431
- StyligClassificationTypes["CircleStroke"] = "circle-stroke";
9432
- StyligClassificationTypes["PolygonStroke"] = "polygon-stroke";
9546
+ StyligClassificationTypes["FillColor"] = "fill-color";
9547
+ StyligClassificationTypes["FillExtrusionColor"] = "fill-extrusion-color";
9548
+ StyligClassificationTypes["CircleColor"] = "circle-color";
9549
+ StyligClassificationTypes["LineColor"] = "line-color";
9550
+ StyligClassificationTypes["CircleStrokeColor"] = "circle-stroke-color";
9433
9551
  StyligClassificationTypes["CircleRadius"] = "circle-radius";
9434
9552
  StyligClassificationTypes["CircleStrokeWidth"] = "circle-stroke-width";
9435
9553
  StyligClassificationTypes["LineWidth"] = "line-width";
9436
- StyligClassificationTypes["PolygonStrokeWidth"] = "polygon-stroke-width";
9437
9554
  StyligClassificationTypes["CircleBlur"] = "circle-blur";
9438
9555
  StyligClassificationTypes["LineBlur"] = "line-blur";
9439
- StyligClassificationTypes["PolygonStrokeBlur"] = "polygon-stroke-blur";
9440
- StyligClassificationTypes["FillOpacity"] = "fill-opacity";
9441
- StyligClassificationTypes["CircleOpacity"] = "circle-opacity";
9442
- StyligClassificationTypes["LineOpacity"] = "line-opacity";
9443
- StyligClassificationTypes["PolygonStrokeOpacity"] = "polygon-stroke-opacity";
9444
- StyligClassificationTypes["CircleTranslate"] = "circle-translate";
9445
9556
  })(exports.StyligClassificationTypes || (exports.StyligClassificationTypes = {}));
9446
9557
 
9447
9558
  const StyledButton = styled(uilibGl.FlatButton).withConfig({ displayName: "StyledButton", componentId: "sc-13r9ztd" }) `
@@ -9497,7 +9608,7 @@ const buildFiltersFromResponse = (responseFilters, result) => {
9497
9608
  return Object.keys(filters).length ? filters : null;
9498
9609
  };
9499
9610
 
9500
- const ClampedText = styled.div.withConfig({ displayName: "ClampedText", componentId: "sc-138git7" }) `
9611
+ const ClampedText$1 = styled.div.withConfig({ displayName: "ClampedText", componentId: "sc-138git7" }) `
9501
9612
  display: -webkit-box;
9502
9613
  -webkit-line-clamp: 2;
9503
9614
  -webkit-box-orient: vertical;
@@ -9505,7 +9616,7 @@ const ClampedText = styled.div.withConfig({ displayName: "ClampedText", componen
9505
9616
  text-overflow: ellipsis;
9506
9617
  word-break: break-word;
9507
9618
  `;
9508
- const clampDescription = (text) => React.createElement(ClampedText, null, text);
9619
+ const clampDescription = (text) => React.createElement(ClampedText$1, null, text);
9509
9620
 
9510
9621
  const RUN_ID_RADIX = 36;
9511
9622
  const RUN_ID_LENGTH = 8;
@@ -14420,6 +14531,13 @@ const ExpandableTitle = React.memo(({ elementConfig, type, renderElement }) => {
14420
14531
  const titleElement = children?.find(item => item.id === "title");
14421
14532
  if (!titleElement)
14422
14533
  return null;
14534
+ const titleIconElement = children?.find(item => item.id === "titleIcon");
14535
+ const isLayers = templateName === exports.ContainerTemplate.Layers;
14536
+ const hasTitleText = !!titleElement.value?.toString().trim() || !!titleElement.attributeName;
14537
+ const hasTitleIcon = !!titleIconElement?.value;
14538
+ const hasControls = !!expandable || !!titleElement.options?.downloadById || isLayers;
14539
+ if (!hasTitleText && !hasTitleIcon && !hasControls)
14540
+ return null;
14423
14541
  const isVisible = isVisibleContainer(id, expandable, expanded, expandedContainers);
14424
14542
  return (jsxRuntime.jsx(TitleContainer, { containerId: id, elementConfig: titleElement, templateName: templateName, layerNames: layerNames, fontColor: fontColor, expandable: expandable, expanded: expanded, type: type, isVisible: isVisible, renderElement: renderElement }));
14425
14543
  });
@@ -14745,8 +14863,40 @@ const TextTrimValue = styled.div.withConfig({ displayName: "TextTrimValue", comp
14745
14863
  word-break: ${({ wordBreak }) => wordBreak ?? "break-word"};
14746
14864
  overflow: hidden;
14747
14865
  `;
14866
+ const ClampedText = styled.div.withConfig({ displayName: "ClampedText", componentId: "sc-asq1h5" }) `
14867
+ display: -webkit-box;
14868
+ -webkit-line-clamp: ${({ $maxLines }) => $maxLines};
14869
+ -webkit-box-orient: vertical;
14870
+ overflow: hidden;
14871
+ width: 100%;
14872
+ max-width: 100%;
14873
+ word-break: ${({ wordBreak }) => wordBreak ?? "break-word"};
14874
+ `;
14748
14875
 
14749
- const TextTrim = React.memo(({ maxLength, expandable, lineBreak, wordBreak, children }) => {
14876
+ /**
14877
+ * Обрезка описания по числу строк (line-clamp). Тултип с полным текстом показывается ТОЛЬКО при
14878
+ * реальном переполнении (`scrollHeight > clientHeight`), отслеживаемом через ResizeObserver, —
14879
+ * у необрезанного текста тултипа нет (ТЗ 9.7).
14880
+ */
14881
+ const TextTrimLineClamp = React.memo(({ maxLines, wordBreak, children }) => {
14882
+ const ref = React.useRef(null);
14883
+ const [isOverflow, setIsOverflow] = React.useState(false);
14884
+ React.useEffect(() => {
14885
+ const node = ref.current;
14886
+ if (!node)
14887
+ return;
14888
+ const check = () => setIsOverflow(node.scrollHeight > node.clientHeight);
14889
+ check();
14890
+ const observer = new ResizeObserver(check);
14891
+ observer.observe(node);
14892
+ return () => observer.disconnect();
14893
+ }, [children, maxLines]);
14894
+ const clamped = (jsxRuntime.jsx(ClampedText, { ref: ref, "$maxLines": maxLines, wordBreak: wordBreak, children: children }));
14895
+ if (!isOverflow)
14896
+ return clamped;
14897
+ return (jsxRuntime.jsx(uilibGl.Tooltip, { placement: "top", arrow: true, content: children, children: tooltipRef => jsxRuntime.jsx("div", { ref: tooltipRef, children: clamped }) }));
14898
+ });
14899
+ const TextTrim = React.memo(({ maxLength, maxLines, expandable, lineBreak, wordBreak, children }) => {
14750
14900
  const { t } = useGlobalContext();
14751
14901
  const [expanded, toggleExpanded] = useToggle();
14752
14902
  const text = children?.toString();
@@ -14755,6 +14905,9 @@ const TextTrim = React.memo(({ maxLength, expandable, lineBreak, wordBreak, chil
14755
14905
  return jsxRuntime.jsx(TextTrimValue, { wordBreak: wordBreak, children: value });
14756
14906
  return jsxRuntime.jsx(TextTrimValue, { wordBreak: wordBreak, dangerouslySetInnerHTML: { __html: lodash.unescape(value).split(lineBreak).join("<br />") } });
14757
14907
  }, [lineBreak, wordBreak]);
14908
+ if (maxLines && text?.length) {
14909
+ return jsxRuntime.jsx(TextTrimLineClamp, { maxLines: maxLines, wordBreak: wordBreak, children: text });
14910
+ }
14758
14911
  if (!text?.length || !maxLength || text.length <= maxLength)
14759
14912
  return jsxRuntime.jsx(jsxRuntime.Fragment, { children: formatValue(text) });
14760
14913
  const substring = `${text.substring(0, maxLength)}...`;
@@ -14801,41 +14954,6 @@ const RasterLayer = ({ layer, tileUrl, visible, beforeId, }) => {
14801
14954
  return (jsxRuntime.jsx(MapGL.Source, { id: layer.name, type: "raster", tiles: tiles, children: jsxRuntime.jsx(MapGL.Layer, { id: layer.name, type: "raster", "source-layer": "default", beforeId: beforeId, layout: { visibility: visible ? "visible" : "none" } }) }, `${layer.name}-${tileUrl}`));
14802
14955
  };
14803
14956
 
14804
- const getClientStyleItemPrefixSuffix = (geometryType, itemType) => {
14805
- switch (geometryType) {
14806
- case api.OgcGeometryType.Point:
14807
- case api.OgcGeometryType.MultiPoint:
14808
- switch (itemType) {
14809
- case "symbol":
14810
- return ["point-label-layer-", "-label"];
14811
- default:
14812
- return ["point-layer-", ""];
14813
- }
14814
- case api.OgcGeometryType.LineString:
14815
- case api.OgcGeometryType.MultiLineString:
14816
- switch (itemType) {
14817
- case "symbol":
14818
- return ["polyline-label-layer-", "-label"];
14819
- default:
14820
- return ["polyline-layer-", ""];
14821
- }
14822
- case api.OgcGeometryType.Polygon:
14823
- case api.OgcGeometryType.MultiPolygon:
14824
- switch (itemType) {
14825
- case "line":
14826
- return ["polygon-stroke-layer-", "-stroke"];
14827
- case "fill-extrusion":
14828
- return ["polygon-extrusion-layer-", "-extrusion"];
14829
- case "symbol":
14830
- return ["polygon-label-layer-", "-label"];
14831
- default:
14832
- return ["polygon-layer-", ""];
14833
- }
14834
- default:
14835
- return ["", ""];
14836
- }
14837
- };
14838
-
14839
14957
  const findAttributeInExpression = (expression) => {
14840
14958
  if (Array.isArray(expression) && expression.length === 2 && expression[0] === "get") {
14841
14959
  return [expression[1]];
@@ -14857,6 +14975,14 @@ const getActualExtrusionHeight = (paint) => {
14857
14975
  : paint?.["fill-extrusion-height"];
14858
14976
  };
14859
14977
 
14978
+ const getLayerClientStyle = (layer) => {
14979
+ const clientStyle = layer?.configuration?.clientStyle;
14980
+ return {
14981
+ ...clientStyle,
14982
+ items: clientStyle?.items ?? [],
14983
+ };
14984
+ };
14985
+
14860
14986
  const extractAttributesFromObject = (obj) => {
14861
14987
  if (!obj || lodash.isEmpty(obj)) {
14862
14988
  return [];
@@ -14886,7 +15012,6 @@ const VectorLayer = ({ layer, tileUrl, visible, beforeId, getLayerTempStyle, })
14886
15012
  const layerConfiguration = layer?.configuration;
14887
15013
  const clientStyle = layerConfiguration?.clientStyle;
14888
15014
  const idAttribute = layerConfiguration?.attributesConfiguration?.idAttribute;
14889
- const geometryType = layerConfiguration?.geometryType;
14890
15015
  const tiles = React.useMemo(() => [tileUrl], [tileUrl]);
14891
15016
  // Собираем конфигурации иконок для загрузки из clientStyle
14892
15017
  const iconConfigs = React.useMemo(() => {
@@ -14913,121 +15038,28 @@ const VectorLayer = ({ layer, tileUrl, visible, beforeId, getLayerTempStyle, })
14913
15038
  }).filter((config) => config !== null);
14914
15039
  }, [clientStyle]);
14915
15040
  useMapImages({ images: iconConfigs });
14916
- const renderLayerByGeometryType = React.useCallback(() => {
14917
- const visibility = visible ? "visible" : "none";
14918
- switch (geometryType) {
14919
- case api.OgcGeometryType.Point:
14920
- case api.OgcGeometryType.MultiPoint:
14921
- return (jsxRuntime.jsx(MapGL.Layer, { id: layer.name, type: "circle", "source-layer": "default", beforeId: beforeId, layout: {
14922
- ...getLayerTempStyle?.(layer.name, "circle")?.layout,
14923
- visibility,
14924
- }, paint: {
14925
- ...DEFAULT_CIRCLE_PAINT,
14926
- ...getLayerTempStyle?.(layer.name, "circle")?.paint,
14927
- } }));
14928
- case api.OgcGeometryType.Polygon:
14929
- case api.OgcGeometryType.MultiPolygon:
14930
- return [
14931
- jsxRuntime.jsx(MapGL.Layer, { id: layer.name, type: "fill", "source-layer": "default", beforeId: beforeId, layout: {
14932
- ...getLayerTempStyle?.(layer.name, "fill")?.layout,
14933
- visibility: visible &&
14934
- getLayerTempStyle?.(layer.name, "fill-extrusion")?.settings?.fill?.showBottomSurface !== false &&
14935
- Boolean(getActualExtrusionHeight({
14936
- ...DEFAULT_FILL_EXTRUSION_PAINT,
14937
- ...getLayerTempStyle?.(layer.name, "fill-extrusion")?.paint,
14938
- }))
14939
- ? "visible"
14940
- : "none",
14941
- }, paint: {
14942
- ...DEFAULT_FILL_PAINT,
14943
- ...getLayerTempStyle?.(layer.name, "fill")?.paint,
14944
- } }, `polygon-layer-${layer.name}`),
14945
- jsxRuntime.jsx(MapGL.Layer, { id: `${layer.name}-stroke`, type: "line", "source-layer": "default", beforeId: beforeId, layout: {
14946
- ...getLayerTempStyle?.(layer.name, "line")?.layout,
14947
- visibility: visible &&
14948
- getLayerTempStyle?.(layer.name, "fill-extrusion")?.settings?.fill?.showBottomSurface !== false &&
14949
- Boolean(getActualExtrusionHeight({
14950
- ...DEFAULT_FILL_EXTRUSION_PAINT,
14951
- ...getLayerTempStyle?.(layer.name, "fill-extrusion")?.paint,
14952
- }))
14953
- ? "visible"
14954
- : "none",
14955
- }, paint: {
14956
- ...DEFAULT_LINE_PAINT,
14957
- ...getLayerTempStyle?.(layer.name, "line")?.paint,
14958
- } }, `polygon-stroke-layer-${layer.name}`),
14959
- jsxRuntime.jsx(MapGL.Layer, { id: `${layer.name}-extrusion`, type: "fill-extrusion", "source-layer": "default", beforeId: beforeId, minzoom: 0, maxzoom: 23, layout: {
14960
- ...getLayerTempStyle?.(layer.name, "fill-extrusion")?.layout,
14961
- visibility,
14962
- }, paint: {
14963
- ...DEFAULT_FILL_EXTRUSION_PAINT,
14964
- ...getLayerTempStyle?.(layer.name, "fill-extrusion")?.paint,
14965
- } }, `polygon-extrusion-layer-${layer.name}`),
14966
- ];
14967
- case api.OgcGeometryType.LineString:
14968
- case api.OgcGeometryType.MultiLineString:
14969
- return (jsxRuntime.jsx(MapGL.Layer, { id: layer.name, type: "line", "source-layer": "default", beforeId: beforeId, layout: {
14970
- ...getLayerTempStyle?.(layer.name, "line")?.layout,
14971
- visibility,
14972
- }, paint: {
14973
- ...DEFAULT_LINE_PAINT,
14974
- ...getLayerTempStyle?.(layer.name, "line")?.paint,
14975
- } }));
14976
- default:
14977
- return null;
14978
- }
14979
- }, [
14980
- geometryType,
14981
- layer,
14982
- beforeId,
14983
- visible,
14984
- getLayerTempStyle,
14985
- ]);
14986
15041
  const renderClientStyle = React.useCallback(() => {
14987
- return clientStyle.items.map((mockItem, index) => {
14988
- const [prefix, suffix] = getClientStyleItemPrefixSuffix(geometryType, mockItem.type);
14989
- const isExtrusionItem = mockItem.type === "fill-extrusion";
14990
- const isPolygonPart = clientStyle.items.some(item => item.type === "fill-extrusion") &&
14991
- (mockItem.type === "fill" || mockItem.type === "line");
14992
- const fillExtrusionPaint = isPolygonPart || isExtrusionItem
14993
- ? {
14994
- ...clientStyle.items?.find(item => item.type === "fill-extrusion")?.paint,
14995
- ...getLayerTempStyle?.(layer.name, "fill-extrusion")?.paint,
14996
- }
14997
- : undefined;
14998
- const currentSettings = {
14999
- ...clientStyle?.settings,
15000
- ...getLayerTempStyle?.(layer.name, mockItem.type)?.settings,
15001
- };
15002
- const processedExtrusionHeight = getActualExtrusionHeight(fillExtrusionPaint);
15003
- const hasExtrusion = Boolean(processedExtrusionHeight);
15004
- const visibility = visible &&
15005
- ((isExtrusionItem && hasExtrusion) ||
15006
- (!isPolygonPart && !isExtrusionItem) ||
15007
- (isPolygonPart && (!hasExtrusion || (currentSettings?.fill?.showBottomSurface ?? true))))
15008
- ? "visible"
15009
- : "none";
15010
- return (jsxRuntime.jsx(MapGL.Layer, { id: `${layer.name}${suffix}`, type: mockItem.type, "source-layer": "default", beforeId: beforeId, ...(mockItem.filter && { filter: mockItem.filter }), minzoom: mockItem.minzoom ?? clientStyle?.minzoom ?? 0, maxzoom: mockItem.maxzoom ?? clientStyle?.maxzoom ?? 23, layout: {
15042
+ const items = [...(getLayerTempStyle(layer.name)?.items ?? clientStyle?.items ?? [])];
15043
+ let currentBeforeId = beforeId;
15044
+ return items.map((mockItem, mockItemIndex) => {
15045
+ const visibility = visible ? "visible" : "none";
15046
+ if (mockItemIndex > 0) {
15047
+ currentBeforeId = `${layer.name}-${items[mockItemIndex - 1].id}`;
15048
+ }
15049
+ return (jsxRuntime.jsx(MapGL.Layer, { id: `${layer.name}-${mockItem.id}`, type: mockItem.type, "source-layer": "default", beforeId: currentBeforeId, ...(mockItem.filter && { filter: mockItem.filter }), minzoom: mockItem.minzoom ?? clientStyle?.minzoom ?? 0, maxzoom: mockItem.maxzoom ?? clientStyle?.maxzoom ?? 23, layout: {
15011
15050
  ...mockItem.layout,
15012
- ...getLayerTempStyle?.(layer.name, mockItem.type)?.layout,
15051
+ ...getLayerTempStyle(layer.name)?.items?.find(item => item.id === mockItem.id)?.layout,
15013
15052
  visibility,
15014
15053
  }, paint: {
15015
15054
  ...mockItem.paint,
15016
- ...getLayerTempStyle?.(layer.name, mockItem.type)?.paint,
15017
- } }, `${prefix}${layer.name}-${index}`));
15055
+ ...getLayerTempStyle(layer.name)?.items?.find(item => item.id === mockItem.id)?.paint,
15056
+ } }, `${layer.name}-${mockItem.id}`));
15018
15057
  });
15019
- }, [
15020
- beforeId,
15021
- clientStyle,
15022
- geometryType,
15023
- layer.name,
15024
- visible,
15025
- getLayerTempStyle,
15026
- ]);
15058
+ }, [beforeId, clientStyle, layer.name, visible, getLayerTempStyle]);
15027
15059
  if (!layer) {
15028
15060
  return null;
15029
15061
  }
15030
- return (jsxRuntime.jsx(MapGL.Source, { promoteId: idAttribute, id: layer.name, type: "vector", tiles: tiles, children: clientStyle?.items ? renderClientStyle() : renderLayerByGeometryType() }, `${layer.name}-${tileUrl}`));
15062
+ return (jsxRuntime.jsx(MapGL.Source, { promoteId: idAttribute, id: layer.name, type: "vector", tiles: tiles, children: renderClientStyle() }, `${layer.name}-${tileUrl}`));
15031
15063
  };
15032
15064
 
15033
15065
  const Layer = ({ layer, layerType, visible, beforeId, tileUrl, getLayerTempStyle, onMount = () => { }, }) => {
@@ -15234,6 +15266,7 @@ exports.SmallPreviewLeft = SmallPreviewLeft;
15234
15266
  exports.SmallPreviewRight = SmallPreviewRight;
15235
15267
  exports.StackBar = StackBar;
15236
15268
  exports.SvgImage = SvgImage;
15269
+ exports.TILE_ALIGNMENTS = TILE_ALIGNMENTS;
15237
15270
  exports.TIME_ZONE_FORMAT = TIME_ZONE_FORMAT;
15238
15271
  exports.TabsContainer = TabsContainer;
15239
15272
  exports.TextTrim = TextTrim;
@@ -15309,6 +15342,7 @@ exports.getFilterSelectedItems = getFilterSelectedItems;
15309
15342
  exports.getFilterValue = getFilterValue;
15310
15343
  exports.getFormattedAttributes = getFormattedAttributes;
15311
15344
  exports.getGradientColors = getGradientColors;
15345
+ exports.getLayerClientStyle = getLayerClientStyle;
15312
15346
  exports.getLayerInfo = getLayerInfo;
15313
15347
  exports.getLayerInfoAttribute = getLayerInfoAttribute;
15314
15348
  exports.getLayerInfoFromDataSources = getLayerInfoFromDataSources;
@@ -15381,6 +15415,7 @@ exports.useDataSources = useDataSources;
15381
15415
  exports.useDebouncedCallback = useDebouncedCallback;
15382
15416
  exports.useDiffPage = useDiffPage;
15383
15417
  exports.useEditGroupAttributes = useEditGroupAttributes;
15418
+ exports.useEqualTileWidth = useEqualTileWidth;
15384
15419
  exports.useExpandableContainers = useExpandableContainers;
15385
15420
  exports.useExportPdf = useExportPdf;
15386
15421
  exports.useFeatureSaveHooks = useFeatureSaveHooks;