@evergis/react 4.0.64 → 4.0.66

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
@@ -5745,6 +5745,49 @@ const usePythonTask = () => {
5745
5745
  };
5746
5746
  };
5747
5747
 
5748
+ const useRemoteTask = ({ onUpdate } = {}) => {
5749
+ const { api } = useGlobalContext();
5750
+ const { addSubscription, connection, unsubscribeById } = useServerNotificationsContext();
5751
+ const runTask = useCallback((prototype) => new Promise((resolve, reject) => {
5752
+ const start = async () => {
5753
+ const resourceId = prototype.subTaskSettings?.[0]?.startParameters?.resourceId;
5754
+ let prototypeId = await api.remoteTaskManager.createTaskPrototype(prototype);
5755
+ prototypeId = prototypeId.replace(/["']+/g, "");
5756
+ const { id: taskId, success } = await api.remoteTaskManager.startTask1(prototypeId);
5757
+ if (!success || !taskId) {
5758
+ reject(new Error("startTask returned no taskId"));
5759
+ return;
5760
+ }
5761
+ const subscriptionId = await addSubscription({
5762
+ tag: "python_task_progress_event",
5763
+ resourceId,
5764
+ });
5765
+ const onNotification = ({ data }) => {
5766
+ if (data?.taskId !== taskId) {
5767
+ return;
5768
+ }
5769
+ onUpdate?.({ status: data.status, log: data.log });
5770
+ const isCompleted = data.status === RemoteTaskStatus.Completed;
5771
+ const isError = data.status === RemoteTaskStatus.Error;
5772
+ if (isCompleted || isError) {
5773
+ connection?.off(SERVER_NOTIFICATION_EVENT.PythonProgressNotification, onNotification);
5774
+ if (subscriptionId) {
5775
+ unsubscribeById(subscriptionId);
5776
+ }
5777
+ resolve({
5778
+ taskId,
5779
+ status: isCompleted ? RemoteTaskStatus.Completed : RemoteTaskStatus.Error,
5780
+ log: data.log,
5781
+ });
5782
+ }
5783
+ };
5784
+ connection?.on(SERVER_NOTIFICATION_EVENT.PythonProgressNotification, onNotification);
5785
+ };
5786
+ start().catch(reject);
5787
+ }), [api, addSubscription, connection, unsubscribeById, onUpdate]);
5788
+ return { runTask };
5789
+ };
5790
+
5748
5791
  const useAppHeight = () => {
5749
5792
  useEffect(() => {
5750
5793
  const setAppHeight = () => {
@@ -6212,6 +6255,8 @@ const getAttributeByName = (name, attributes) => {
6212
6255
  : null;
6213
6256
  };
6214
6257
 
6258
+ const isEmptyElementValue = (value) => value === "" || value === null || value === undefined;
6259
+
6215
6260
  /**
6216
6261
  * Returns a value safe to render as a React child.
6217
6262
  *
@@ -6236,7 +6281,7 @@ const formatElementValue = ({ t, value, elementConfig, attributes, wrap, }) => {
6236
6281
  ? getAttributeByName(attributeName, attributes)
6237
6282
  : null;
6238
6283
  const { fontColor, fontSize, noUnits, tagView, bgColor, withDivider, radius, noMargin, } = options || {};
6239
- const valueOrDefault = value || defaultValue;
6284
+ const valueOrDefault = isEmptyElementValue(value) ? defaultValue : value;
6240
6285
  const resultValue = type === "attributeValue" && attribute?.type && attribute?.stringFormat
6241
6286
  ? formatAttributeValue({
6242
6287
  t,
@@ -6260,14 +6305,14 @@ const getAttributeValue = (element, attributes) => {
6260
6305
  return jsx(DashboardCheckbox, { title: attribute.alias || attribute.attributeName, checked: attribute.value });
6261
6306
  }
6262
6307
  if (Array.isArray(element?.attributeName)) {
6263
- const concatAttributes = element.attributeName.map((name) => attributes?.find(({ attributeName }) => attributeName === name)?.value || "");
6308
+ const concatAttributes = element.attributeName.map((name) => attributes?.find(({ attributeName }) => attributeName === name)?.value ?? "");
6264
6309
  value = concatAttributes.join(separator || ", ");
6265
6310
  }
6266
6311
  else {
6267
6312
  const rawValue = attribute?.value;
6268
- value = rawValue && typeof rawValue === "object"
6313
+ value = rawValue !== null && rawValue !== undefined && typeof rawValue === "object"
6269
6314
  ? JSON.stringify(rawValue)
6270
- : (rawValue || "");
6315
+ : (rawValue ?? "");
6271
6316
  }
6272
6317
  return typeof value === "string" && maxLength && maxLength < value.length ? (jsx(TextTrim, { maxLength: maxLength, wordBreak: wordBreak, expandable: expandable, lineBreak: lineBreak, children: value })) : (value);
6273
6318
  };
@@ -6476,7 +6521,7 @@ const useRenderContainer = ({ elementConfig, type, renderElement, renderBody, })
6476
6521
  return (jsx(OverrideContainer, { type: type, elementConfig: itemConfig, renderElement: item.render }, attribute));
6477
6522
  }
6478
6523
  }
6479
- if (!item.value && item.hideEmpty)
6524
+ if (isEmptyElementValue(item.value) && item.hideEmpty)
6480
6525
  return null;
6481
6526
  return renderBody(item, attribute);
6482
6527
  }, [getRenderContainerItem, elementConfig, attributes, type, renderBody]);
@@ -7645,7 +7690,7 @@ const LogDialog = ({ isOpen, onClose, onMinimize, logs, status, statusColors })
7645
7690
  const getStatusColor = useCallback((currentStatus) => {
7646
7691
  return statusColors?.[currentStatus] || STATUS_COLORS[currentStatus] || STATUS_COLORS[RemoteTaskStatus.Unknown];
7647
7692
  }, [statusColors]);
7648
- return (jsxs(Dialog, { isOpen: isOpen, onCloseRequest: onClose, modal: true, maxWidth: "800px", minWidth: "600px", minHeight: "600px", children: [jsx(DialogTitle, { children: jsxs(Flex, { justifyContent: "space-between", alignItems: "center", children: [jsxs(Flex, { alignItems: "center", children: [jsx(FlexSpan, { marginRight: "1rem", children: t("taskLogs", { ns: "dashboard", defaultValue: "Логи выполнения задачи" }) }), jsx(ThemeProvider, { theme: darkTheme, children: jsx(StatusBadge, { text: getStatusTitle(status), bgColor: getStatusColor(status) }) })] }), jsxs(Flex, { alignItems: "center", width: "auto", children: [onMinimize && jsx(IconButton, { kind: "change_geom", onClick: onMinimize }), jsx(IconButton, { kind: "close", onClick: onClose })] })] }) }), jsx(DialogContent, { children: jsx(Flex, { flexDirection: "column", height: "100%", marginBottom: "2rem", children: jsx(LogTerminal, { log: logs || t("taskLogsEmpty", { ns: "dashboard", defaultValue: "Логи отсутствуют" }) }) }) })] }));
7693
+ return (jsxs(Dialog, { isOpen: isOpen, onCloseRequest: onClose, modal: true, maxWidth: "800px", minWidth: "600px", minHeight: "600px", children: [jsx(DialogTitle, { children: jsxs(Flex, { justifyContent: "space-between", alignItems: "center", children: [jsxs(Flex, { alignItems: "center", children: [jsx(FlexSpan, { marginRight: "1rem", children: t("taskLogs", { ns: "dashboard", defaultValue: "Логи выполнения задачи" }) }), jsx(ThemeProvider, { theme: darkTheme, children: jsx(StatusBadge, { text: getStatusTitle(status), bgColor: getStatusColor(status) }) })] }), jsxs(Flex, { alignItems: "center", width: "auto", children: [onMinimize && jsx(IconButton, { kind: "rollup", onClick: onMinimize }), jsx(IconButton, { kind: "close", onClick: onClose })] })] }) }), jsx(DialogContent, { children: jsx(Flex, { flexDirection: "column", height: "100%", marginBottom: "2rem", children: jsx(LogTerminal, { log: logs || t("taskLogsEmpty", { ns: "dashboard", defaultValue: "Логи отсутствуют" }) }) }) })] }));
7649
7694
  };
7650
7695
 
7651
7696
  var ThemeName;
@@ -9036,115 +9081,6 @@ const FeatureCardDefaultHeader = ({ noFeature }) => {
9036
9081
  return (jsx(DefaultHeaderWrapper, { withPadding: withPadding, height: height, children: jsx(ThemeProvider, { theme: getThemeByName(themeName), children: jsx(Header, { "$overlay": overlay, "$isRow": !column, children: jsxs(HeaderFrontView, { isDefault: !column, children: [jsxs(HeaderContainer, { children: [jsx(HeaderLayerIcon, {}), jsx(HeaderTitle, { noFeature: noFeature })] }), jsx(FeatureCardButtons, {})] }) }) }) }));
9037
9082
  };
9038
9083
 
9039
- const HeaderWrapperMixin = css `
9040
- ${Header} {
9041
- min-height: 5.25rem;
9042
- }
9043
-
9044
- ${HeaderContainer} {
9045
- max-width: 100%;
9046
- width: 100%;
9047
- }
9048
-
9049
- ${FeatureControls} {
9050
- max-width: calc(100% - 2rem);
9051
- width: calc(100% - 2rem);
9052
- margin-top: -0.5rem;
9053
- padding-top: 1rem;
9054
- border-radius: ${({ theme: { borderRadius } }) => borderRadius.medium};
9055
- }
9056
-
9057
- ${({ $fontColor }) => !!$fontColor && HeaderFontColorMixin};
9058
- `;
9059
- const HeaderIcon = styled(Flex) `
9060
- position: absolute;
9061
- top: 0;
9062
- right: 0;
9063
- align-items: center;
9064
- justify-content: center;
9065
- width: 7.625rem;
9066
- height: 100%;
9067
-
9068
- span[kind] {
9069
- width: 4rem;
9070
-
9071
- :after {
9072
- font-size: 4rem;
9073
- color: rgba(255, 255, 255, 0.36);
9074
- }
9075
- }
9076
-
9077
- span[kind]:after,
9078
- path,
9079
- line,
9080
- circle {
9081
- fill: rgba(255, 255, 255, 0.36);
9082
- }
9083
-
9084
- && > * {
9085
- display: flex;
9086
- align-items: center;
9087
- height: 100%;
9088
- }
9089
- `;
9090
- const BigIconHeaderMixin = css `
9091
- ${HeaderIcon} {
9092
- min-width: 14rem;
9093
- right: -3rem;
9094
-
9095
- span[kind]:after {
9096
- font-size: 14rem;
9097
- }
9098
- }
9099
- `;
9100
- const WithPaddingHeaderMixin = css `
9101
- ${Header} {
9102
- width: 100%;
9103
- margin: -0.5rem -0.5rem 0.5rem -0.5rem;
9104
- }
9105
- `;
9106
- const BottomBlurHeaderMixin = css `
9107
- ${Header} {
9108
- margin-bottom: 0;
9109
-
9110
- &::after {
9111
- content: "";
9112
- position: absolute;
9113
- top: 0;
9114
- right: 0;
9115
- bottom: 0;
9116
- left: 0;
9117
- z-index: 11;
9118
- pointer-events: none;
9119
- background: ${({ theme: { palette } }) => palette.background};
9120
- mask-image: linear-gradient(to top, #000 0%, transparent 100%);
9121
- -webkit-mask-image: linear-gradient(to top, #000 0%, transparent 100%);
9122
- }
9123
- }
9124
-
9125
- ${HeaderFrontView} {
9126
- z-index: 12;
9127
- }
9128
- `;
9129
- const BackgroundHeaderWrapper = styled.div `
9130
- ${Header} {
9131
- width: calc(100% + 1rem);
9132
- height: ${({ $height }) => $height ? `${$height}px` : "auto"};
9133
- margin: -1rem -1rem 1rem -1rem;
9134
- border-radius: 0.5rem;
9135
- background: ${({ $bgColor }) => $bgColor || "linear-gradient(96.55deg, #FFFCD3 0%, #B4DC47 100%)"};
9136
- overflow: hidden;
9137
- }
9138
-
9139
- ${HeaderWrapperMixin};
9140
-
9141
- ${({ $bigIcon }) => $bigIcon && BigIconHeaderMixin};
9142
-
9143
- ${({ $withPadding }) => $withPadding && WithPaddingHeaderMixin};
9144
-
9145
- ${({ $bottomBlur }) => $bottomBlur && BottomBlurHeaderMixin};
9146
- `;
9147
-
9148
9084
  const ImageContainerButton = styled(FlatButton) `
9149
9085
  min-height: 1.5rem;
9150
9086
  border-radius: ${({ theme: { borderRadius } }) => borderRadius.large};
@@ -10063,6 +9999,121 @@ const HeaderSlideshow = styled.div `
10063
9999
  }
10064
10000
  `;
10065
10001
 
10002
+ const HeaderWrapperMixin = css `
10003
+ ${Header} {
10004
+ min-height: 5.25rem;
10005
+ }
10006
+
10007
+ ${HeaderContainer} {
10008
+ max-width: 100%;
10009
+ width: 100%;
10010
+ }
10011
+
10012
+ ${FeatureControls} {
10013
+ max-width: calc(100% - 2rem);
10014
+ width: calc(100% - 2rem);
10015
+ margin-top: -0.5rem;
10016
+ padding-top: 1rem;
10017
+ border-radius: ${({ theme: { borderRadius } }) => borderRadius.medium};
10018
+ }
10019
+
10020
+ ${({ $fontColor }) => !!$fontColor && HeaderFontColorMixin};
10021
+ `;
10022
+ const HeaderIcon = styled(Flex) `
10023
+ position: absolute;
10024
+ top: 0;
10025
+ right: 0;
10026
+ align-items: center;
10027
+ justify-content: center;
10028
+ width: 7.625rem;
10029
+ height: 100%;
10030
+
10031
+ span[kind] {
10032
+ width: 4rem;
10033
+
10034
+ :after {
10035
+ font-size: 4rem;
10036
+ color: rgba(255, 255, 255, 0.36);
10037
+ }
10038
+ }
10039
+
10040
+ span[kind]:after,
10041
+ path,
10042
+ line,
10043
+ circle {
10044
+ fill: rgba(255, 255, 255, 0.36);
10045
+ }
10046
+
10047
+ && > * {
10048
+ display: flex;
10049
+ align-items: center;
10050
+ height: 100%;
10051
+ }
10052
+ `;
10053
+ const BigIconHeaderMixin = css `
10054
+ ${HeaderIcon} {
10055
+ min-width: 14rem;
10056
+ right: -3rem;
10057
+
10058
+ span[kind]:after {
10059
+ font-size: 14rem;
10060
+ }
10061
+ }
10062
+ `;
10063
+ const WithPaddingHeaderMixin = css `
10064
+ ${Header} {
10065
+ width: 100%;
10066
+ margin: -0.5rem -0.5rem 0.5rem -0.5rem;
10067
+ }
10068
+ `;
10069
+ const BottomBlurHeaderMixin = css `
10070
+ ${Header} {
10071
+ margin-bottom: 0;
10072
+
10073
+ &::before {
10074
+ -webkit-mask-image: linear-gradient(to bottom, #000 0%, transparent 100%);
10075
+ mask-image: linear-gradient(to bottom, #000 0%, transparent 100%);
10076
+ }
10077
+ }
10078
+
10079
+ ${ImageContainerBg} {
10080
+ -webkit-mask-image: linear-gradient(to bottom, #000 0%, transparent 100%);
10081
+ mask-image: linear-gradient(to bottom, #000 0%, transparent 100%);
10082
+ }
10083
+
10084
+ ${HeaderFrontView} {
10085
+ z-index: 12;
10086
+ }
10087
+ `;
10088
+ const BackgroundHeaderWrapper = styled.div `
10089
+ ${Header} {
10090
+ position: relative;
10091
+ width: calc(100% + 1rem);
10092
+ height: ${({ $height }) => $height ? `${$height}px` : "auto"};
10093
+ margin: -1rem -1rem 1rem -1rem;
10094
+ border-radius: 0.5rem;
10095
+ background: transparent;
10096
+ overflow: hidden;
10097
+
10098
+ &::before {
10099
+ content: "";
10100
+ position: absolute;
10101
+ inset: 0;
10102
+ z-index: 0;
10103
+ pointer-events: none;
10104
+ background: ${({ $bgColor }) => $bgColor || "linear-gradient(96.55deg, #FFFCD3 0%, #B4DC47 100%)"};
10105
+ }
10106
+ }
10107
+
10108
+ ${HeaderWrapperMixin};
10109
+
10110
+ ${({ $bigIcon }) => $bigIcon && BigIconHeaderMixin};
10111
+
10112
+ ${({ $withPadding }) => $withPadding && WithPaddingHeaderMixin};
10113
+
10114
+ ${({ $bottomBlur }) => $bottomBlur && BottomBlurHeaderMixin};
10115
+ `;
10116
+
10066
10117
  const FeatureCardBackgroundHeader = () => {
10067
10118
  const { themeName: pageThemeName } = useGlobalContext();
10068
10119
  const { config } = useWidgetConfig(WidgetType.FeatureCard);
@@ -10318,8 +10369,6 @@ const ModalIcon = styled(IconButton) `
10318
10369
  }
10319
10370
  `;
10320
10371
 
10321
- const isEmptyElementValue = (value) => value === "" || value === null || value === undefined;
10322
-
10323
10372
  const isHiddenEmptyValue = ({ value, children, hideEmpty, renderElement, }) => {
10324
10373
  const valueElement = children?.find(item => item.id === "value");
10325
10374
  const renderedValue = valueElement ? renderElement({ id: "value" }) : null;
@@ -12405,6 +12454,112 @@ const useDiffPage = (type) => {
12405
12454
  return useMemo(() => isDiffPage, [isDiffPage]);
12406
12455
  };
12407
12456
 
12457
+ const SAVE_HOOK_RESULT_DURATION = 4000;
12458
+ const NOTIFICATION_ID_RADIX = 36;
12459
+ const NOTIFICATION_ID_LENGTH = 8;
12460
+ const isHookActive = (hook) => !!hook && (!!hook.resourceId || !!hook.fileName);
12461
+ const createSaveNotificationId = () => `save-hook-${Date.now()}-${Math.random().toString(NOTIFICATION_ID_RADIX).slice(2, NOTIFICATION_ID_LENGTH)}`;
12462
+
12463
+ const useAfterSave = (hook, buildPrototype) => {
12464
+ const { t, notification } = useGlobalContext();
12465
+ const { runTask } = useRemoteTask();
12466
+ return useCallback(async (input) => {
12467
+ if (!isHookActive(hook)) {
12468
+ return;
12469
+ }
12470
+ const notificationId = createSaveNotificationId();
12471
+ notification?.add({
12472
+ id: notificationId,
12473
+ title: t("afterSaveProgress", { ns: "dashboard", defaultValue: "Выполняется действие после сохранения" }),
12474
+ progress: true,
12475
+ duration: Number.MAX_SAFE_INTEGER,
12476
+ });
12477
+ try {
12478
+ const { status, log } = await runTask(buildPrototype(hook, input));
12479
+ if (status === RemoteTaskStatus.Completed) {
12480
+ notification?.update({
12481
+ id: notificationId,
12482
+ title: t("afterSaveSuccess", { ns: "dashboard", defaultValue: "Действие после сохранения выполнено" }),
12483
+ success: true,
12484
+ progress: false,
12485
+ duration: SAVE_HOOK_RESULT_DURATION,
12486
+ });
12487
+ return;
12488
+ }
12489
+ notification?.update({
12490
+ id: notificationId,
12491
+ title: t("afterSaveError", { ns: "dashboard", defaultValue: "Не удалось выполнить действие после сохранения" }),
12492
+ description: log,
12493
+ error: true,
12494
+ progress: false,
12495
+ duration: SAVE_HOOK_RESULT_DURATION,
12496
+ });
12497
+ }
12498
+ catch (error) {
12499
+ const description = error instanceof Error ? error.message : undefined;
12500
+ notification?.update({
12501
+ id: notificationId,
12502
+ title: t("afterSaveError", { ns: "dashboard", defaultValue: "Не удалось выполнить действие после сохранения" }),
12503
+ description,
12504
+ error: true,
12505
+ progress: false,
12506
+ duration: SAVE_HOOK_RESULT_DURATION,
12507
+ });
12508
+ }
12509
+ }, [hook, buildPrototype, runTask, notification, t]);
12510
+ };
12511
+
12512
+ const useBeforeSave = (hook, buildPrototype) => {
12513
+ const { t, notification } = useGlobalContext();
12514
+ const { runTask } = useRemoteTask();
12515
+ return useCallback(async (input) => {
12516
+ if (!isHookActive(hook)) {
12517
+ return true;
12518
+ }
12519
+ const notificationId = createSaveNotificationId();
12520
+ notification?.add({
12521
+ id: notificationId,
12522
+ title: t("beforeSaveProgress", { ns: "dashboard", defaultValue: "Выполняется проверка перед сохранением" }),
12523
+ progress: true,
12524
+ duration: Number.MAX_SAFE_INTEGER,
12525
+ });
12526
+ try {
12527
+ const { status, log } = await runTask(buildPrototype(hook, input));
12528
+ if (status === RemoteTaskStatus.Completed) {
12529
+ notification?.update({
12530
+ id: notificationId,
12531
+ title: t("beforeSaveSuccess", { ns: "dashboard", defaultValue: "Проверка перед сохранением пройдена" }),
12532
+ success: true,
12533
+ progress: false,
12534
+ duration: SAVE_HOOK_RESULT_DURATION,
12535
+ });
12536
+ return true;
12537
+ }
12538
+ notification?.update({
12539
+ id: notificationId,
12540
+ title: t("beforeSaveError", { ns: "dashboard", defaultValue: "Не удалось выполнить проверку перед сохранением" }),
12541
+ description: log,
12542
+ error: true,
12543
+ progress: false,
12544
+ duration: SAVE_HOOK_RESULT_DURATION,
12545
+ });
12546
+ return false;
12547
+ }
12548
+ catch (error) {
12549
+ const description = error instanceof Error ? error.message : undefined;
12550
+ notification?.update({
12551
+ id: notificationId,
12552
+ title: t("beforeSaveError", { ns: "dashboard", defaultValue: "Не удалось выполнить проверку перед сохранением" }),
12553
+ description,
12554
+ error: true,
12555
+ progress: false,
12556
+ duration: SAVE_HOOK_RESULT_DURATION,
12557
+ });
12558
+ return false;
12559
+ }
12560
+ }, [hook, buildPrototype, runTask, notification, t]);
12561
+ };
12562
+
12408
12563
  const useExpandableContainers = () => {
12409
12564
  const [expandedContainers, setExpandedContainers] = useState({});
12410
12565
  const expandContainer = useCallback((id, expanded) => {
@@ -12512,6 +12667,83 @@ const useExportPdf = (id, margin = 20) => {
12512
12667
  return { loading, onExport };
12513
12668
  };
12514
12669
 
12670
+ const useSavePrototypeBuilder = () => {
12671
+ const { ewktGeometry } = useGlobalContext();
12672
+ const { layerInfo, attributes, dataSources, filters: selectedFilters, } = useWidgetContext(WidgetType.FeatureCard);
12673
+ const { projectInfo, dataSources: projectDataSources } = useWidgetContext(WidgetType.Dashboard);
12674
+ const { currentPage } = useWidgetPage(WidgetType.FeatureCard);
12675
+ return useCallback((hook, input) => {
12676
+ const resolvedParameters = applyQueryFilters({
12677
+ parameters: hook.parameters ?? {},
12678
+ filters: currentPage?.filters ?? [],
12679
+ selectedFilters,
12680
+ geometry: ewktGeometry,
12681
+ attributes,
12682
+ layerInfo,
12683
+ dataSources,
12684
+ projectDataSources,
12685
+ });
12686
+ const scriptParameters = {
12687
+ projectName: projectInfo?.name,
12688
+ layerName: layerInfo?.name,
12689
+ featureId: input.featureId,
12690
+ properties: input.changedProperties,
12691
+ ...resolvedParameters,
12692
+ };
12693
+ return {
12694
+ enabled: true,
12695
+ startIfPreviousError: true,
12696
+ startIfPreviousNotFinished: true,
12697
+ subTaskSettings: [
12698
+ {
12699
+ type: "pythonService",
12700
+ startParameters: {
12701
+ resourceId: hook.resourceId,
12702
+ fileName: hook.fileName,
12703
+ methodName: hook.methodName,
12704
+ parameters: scriptParameters,
12705
+ method: "pythonrunner/run",
12706
+ },
12707
+ },
12708
+ ],
12709
+ };
12710
+ }, [
12711
+ attributes,
12712
+ currentPage?.filters,
12713
+ dataSources,
12714
+ ewktGeometry,
12715
+ layerInfo,
12716
+ projectDataSources,
12717
+ projectInfo?.name,
12718
+ selectedFilters,
12719
+ ]);
12720
+ };
12721
+
12722
+ /**
12723
+ * Орchestrator-хук для серверных `beforeSave` / `afterSave` python-скриптов,
12724
+ * описанных в `layerInfo.configuration.editConfiguration.options` слоя.
12725
+ *
12726
+ * - `runBeforeSave(input)` — `Promise<boolean>`, resolves `false` если
12727
+ * серверная проверка не прошла; сохранение должно быть отменено.
12728
+ * - `runAfterSave(input)` — fire-and-forget после успешного save.
12729
+ *
12730
+ * Активируется только если у потребителя в GlobalProvider переданы
12731
+ * `api`, `notification` и `t`, а приложение обёрнуто в
12732
+ * `<ServerNotificationsProvider>` (для SignalR-подписки на прогресс).
12733
+ */
12734
+ const useFeatureSaveHooks = () => {
12735
+ const { layerInfo } = useWidgetContext(WidgetType.FeatureCard);
12736
+ const options = useMemo(() => layerInfo?.configuration
12737
+ ?.editConfiguration?.options, [layerInfo?.configuration]);
12738
+ const buildPrototype = useSavePrototypeBuilder();
12739
+ const runBeforeSave = useBeforeSave(options?.beforeSave, buildPrototype);
12740
+ const runAfterSave = useAfterSave(options?.afterSave, buildPrototype);
12741
+ return {
12742
+ runBeforeSave,
12743
+ runAfterSave,
12744
+ };
12745
+ };
12746
+
12515
12747
  const getMinMaxFromStringValue = (items, value, current, type) => {
12516
12748
  const valueIndex = items.findIndex(item => item.value === (type === "min" ? value.min : value.max));
12517
12749
  const currentIndex = items.findIndex(item => item.value === (type === "min" ? value.min : value.max));
@@ -13650,5 +13882,5 @@ const Map$1 = ({ zIndex, lowerSiblings, upperSiblings, onError, children, ...res
13650
13882
  }, children: children }), upperSiblings] }));
13651
13883
  };
13652
13884
 
13653
- export { ALIGNMENTS, AddFeatureButton, AddFeatureContainer, AlertIconContainer, AttachmentContainer, AttributeGalleryContainer, AttributeLabel, BASE_CONTAINER_STYLE, BaseMapTheme, CHART_TYPES, CONFIG_PAGES_ID, CONFIG_PAGE_ID, CameraContainer, Chart, ChartContainer, ChartLegend, ChartLoading, Container, ContainerChildren, ContainerLoading, ContainerTemplate, ContainerWrapper, ContainersGroupContainer, DEFAULT_ATTRIBUTE_NAME, DEFAULT_BARCHART_RADIUS, DEFAULT_BASE_MAP, DEFAULT_CHART_ANGLE, DEFAULT_CHART_HEIGHT, DEFAULT_CHART_WIDTH, DEFAULT_CIRCLE_PAINT, DEFAULT_DASHBOARD_CONFIG, DEFAULT_DATA_SOURCE_LIMIT, DEFAULT_DROPDOWN_WIDTH, DEFAULT_FILL_EXTRUSION_PAINT, DEFAULT_FILL_PAINT, DEFAULT_FILTER_PADDING, DEFAULT_ID_ATTRIBUTE_NAME, DEFAULT_LAT, DEFAULT_LINE_PAINT, DEFAULT_LNG, DEFAULT_PAGES_CONFIG, DEFAULT_PIECHART_RADIUS, DEFAULT_ZOOM, Dashboard, DashboardCheckbox, DashboardChip, DashboardContent, DashboardContext, DashboardDefaultHeader, DashboardHeader, DashboardLoading, DashboardPlaceholder, DashboardPlaceholderWrap, DashboardProvider, DashboardWrapper, DataSourceContainer, DataSourceError, DataSourceErrorContainer, DataSourceInnerContainer, DataSourceProgressContainer, DateFormat, DefaultAttributesContainer, DefaultHeaderContainer, DefaultHeaderWrapper, DividerContainer, EditGeometryType, ElementButton, ElementCamera, ElementChart, ElementChips, ElementControl, ElementIcon, ElementImage, ElementLegend, ElementLink, ElementMarkdown, ElementSlideshow, ElementSvg, ElementTooltip, ElementValueWrapper, ExpandableTitle, FEATURE_CARD_DEFAULT_COLORS, FEATURE_CARD_OTHER_COLOR, FILTERED_VALUE_OPACITY, FILTER_PREFIX, FeatureCardBackgroundHeader, FeatureCardButtons, FeatureCardContext, FeatureCardDefaultHeader, FeatureCardHeader, FeatureCardProvider, FeatureCardSlideshowHeader, FeatureCardTitle, FeatureControls, FeatureTitleContainer, FiltersContainer, GEOMETRY_ATTRIBUTE, GlobalContext, GlobalProvider, Header, HeaderContainer, HeaderFontColorMixin, HeaderFrontView, HeaderTemplate, HeaderTitleContainer, HiddenTitleItems, IconContainer, ImageContainer, LEFT_PANEL_HEADER_HEIGHT, Layer, LayerDescription, LayerGroup, LayerGroupList, LayerIcon, LayerIconContainer, LayerListContainer, LayerTree, LayersContainer, LayersListWrapper, LinearProgressContainer, LogTerminal, LogoContainer, MAX_CHART_WIDTH, Map$1 as Map, MapContext, MapProvider, NO_CONTENT_VALUE, NUMERIC_ATTRIBUTE_TYPES, NoLiveSnapshotContainer, OneColumnContainer, POLL_SUBTASK_INTERVAL_MS, POLL_SUBTASK_TIMEOUT_MS, PROVIDER_PREFIX, PageNavigator, PageTitle, PageTitleContainer, PagesContainer, Pagination, PresentationHeader, PresentationHeaderButtons, PresentationHeaderTools, PresentationPanelContainer, PresentationPanelWrapper, PresentationWrapper, ProgressContainer, ProviderPrefix, RoundedBackgroundContainer, SERVER_NOTIFICATION_EVENT, STACK_BAR_TOTAL_HEIGHT, ScalingFactor, ServerNotificationsContext, ServerNotificationsProvider, SlideshowContainer, SmallPreviewContainer$1 as SmallPreviewContainer, SmallPreviewControl, SmallPreviewCounter, SmallPreviewImages, SmallPreviewLeft, SmallPreviewRight, StackBar, SvgImage, TIME_ZONE_FORMAT, TabsContainer, TextTrim, ThemeName, TitleContainer, TopContainer, TopContainerButtons, TwoColumnContainer, UploadContainer, VIEW_MODES, WidgetType, addDataSource, addDataSources, adjustColor, applyFiltersToCondition, applyQueryFilters, applyVarsToCondition, asAttributeName, asChartId, asContainerId, asDataSourceName, asFilterName, asLayerName, asModalId, asResourceId, asTabId, checkEqualOrIncludes, checkIsLoading, createConfigLayer, createConfigPage, createNewPageId, createTreeNode, dateOptions, debounce, decimalOpacityToHex, eqlParametersToPayload, findAttributeInExpression, formatArea, formatAttributeValue, formatChartRelatedValue, formatConditionValue, formatDataSourceCondition, formatDate$1 as formatDate, formatElementValue, formatLength, formatNumber, formatPolygonMeasure, getActualExtrusionHeight, getAttributeByName, getAttributeValue, getAttributesConfiguration, getChartAxes, getChartFilterName, getChartMarkers, getConfigFilter, getContainerComponent, getDashboardHeader, getDataFromAttributes, getDataFromRelatedFeatures, getDataSource, getDataSourceFilterValue, getDate, getDefaultConfig, getDisplayTemplateNameFromAttribute, getElementValue, getFeatureAttributes, getFeatureCardHeader, getFilterComponent, getFilterSelectedItems, getFilterValue, getFormattedAttributes, getGradientColors, getLayerInfo, getLayerInfoFromDataSources, getPagesFromConfig, getPagesFromProjectInfo, getProxyService, getRelatedAttribute, getRenderElement, getResourceUrl, getRootElementId, getSelectedFilterValue, getSlideshowImages, getSvgUrl, getTemplateNameFromAttribute, getThemeByName, getTotalFromAttributes, getTotalFromRelatedFeatures, hexToRgba, isEmptyElementValue, isEmptyValue, isHiddenEmptyValue, isLayerService, isNotValidSelectedTab, isNumeric, isObject, isProxyService, isVisibleContainer, metersPerPixel, numberOptions, parseClientStyle, parseIconNames, parseIconNamesFromClientStyle, pieChartTooltipFromAttributes, pieChartTooltipFromRelatedFeatures, pointOptions, removeDataSource, rgbToHex, roundTotalSum, sliceShownOtherItems, timeOptions, toRenderableValue, tooltipNameFromAttributes, tooltipValueFromAttributes, tooltipValueFromRelatedFeatures, transparentizeColor, treeNodesToProjectItems, updateDataSource, useAppHeight, useAttachmentItems, useAttachmentPreviewImages, useAutoCompleteControl, useChartChange, useChartData, useContainerAttributes, useCurrentPageLayers, useCustomFeatureSelect, useDashboardHeader, useDataSources, useDebouncedCallback, useDiffPage, useEditGroupAttributes, useExpandableContainers, useExportPdf, useFetchImageWithAuth, useFetchWithAuth, useGetConfigLayer, useGlobalContext, useHeaderRender, useHideIfEmptyDataSource, useIconsFromLayers, useLayerHiddenAttributes, useLayerParams, useMapContext, useMapDraw, useMapImages, useMaxZoomTo, useProjectDashboardInit, usePythonSandbox, usePythonTask, useRedrawLayer, useRelatedDataSourceAttributes, useRenderElement, useServerNotificationsContext, useShownOtherItems, useToggle, useUpdateDataSource, useVisibleProjectItems, useWidgetConfig, useWidgetContext, useWidgetFilters, useWidgetPage, useWindowResize, useZoomToFeatures, useZoomToPoint };
13885
+ export { ALIGNMENTS, AddFeatureButton, AddFeatureContainer, AlertIconContainer, AttachmentContainer, AttributeGalleryContainer, AttributeLabel, BASE_CONTAINER_STYLE, BaseMapTheme, CHART_TYPES, CONFIG_PAGES_ID, CONFIG_PAGE_ID, CameraContainer, Chart, ChartContainer, ChartLegend, ChartLoading, Container, ContainerChildren, ContainerLoading, ContainerTemplate, ContainerWrapper, ContainersGroupContainer, DEFAULT_ATTRIBUTE_NAME, DEFAULT_BARCHART_RADIUS, DEFAULT_BASE_MAP, DEFAULT_CHART_ANGLE, DEFAULT_CHART_HEIGHT, DEFAULT_CHART_WIDTH, DEFAULT_CIRCLE_PAINT, DEFAULT_DASHBOARD_CONFIG, DEFAULT_DATA_SOURCE_LIMIT, DEFAULT_DROPDOWN_WIDTH, DEFAULT_FILL_EXTRUSION_PAINT, DEFAULT_FILL_PAINT, DEFAULT_FILTER_PADDING, DEFAULT_ID_ATTRIBUTE_NAME, DEFAULT_LAT, DEFAULT_LINE_PAINT, DEFAULT_LNG, DEFAULT_PAGES_CONFIG, DEFAULT_PIECHART_RADIUS, DEFAULT_ZOOM, Dashboard, DashboardCheckbox, DashboardChip, DashboardContent, DashboardContext, DashboardDefaultHeader, DashboardHeader, DashboardLoading, DashboardPlaceholder, DashboardPlaceholderWrap, DashboardProvider, DashboardWrapper, DataSourceContainer, DataSourceError, DataSourceErrorContainer, DataSourceInnerContainer, DataSourceProgressContainer, DateFormat, DefaultAttributesContainer, DefaultHeaderContainer, DefaultHeaderWrapper, DividerContainer, EditGeometryType, ElementButton, ElementCamera, ElementChart, ElementChips, ElementControl, ElementIcon, ElementImage, ElementLegend, ElementLink, ElementMarkdown, ElementSlideshow, ElementSvg, ElementTooltip, ElementValueWrapper, ExpandableTitle, FEATURE_CARD_DEFAULT_COLORS, FEATURE_CARD_OTHER_COLOR, FILTERED_VALUE_OPACITY, FILTER_PREFIX, FeatureCardBackgroundHeader, FeatureCardButtons, FeatureCardContext, FeatureCardDefaultHeader, FeatureCardHeader, FeatureCardProvider, FeatureCardSlideshowHeader, FeatureCardTitle, FeatureControls, FeatureTitleContainer, FiltersContainer, GEOMETRY_ATTRIBUTE, GlobalContext, GlobalProvider, Header, HeaderContainer, HeaderFontColorMixin, HeaderFrontView, HeaderTemplate, HeaderTitleContainer, HiddenTitleItems, IconContainer, ImageContainer, LEFT_PANEL_HEADER_HEIGHT, Layer, LayerDescription, LayerGroup, LayerGroupList, LayerIcon, LayerIconContainer, LayerListContainer, LayerTree, LayersContainer, LayersListWrapper, LinearProgressContainer, LogTerminal, LogoContainer, MAX_CHART_WIDTH, Map$1 as Map, MapContext, MapProvider, NO_CONTENT_VALUE, NUMERIC_ATTRIBUTE_TYPES, NoLiveSnapshotContainer, OneColumnContainer, POLL_SUBTASK_INTERVAL_MS, POLL_SUBTASK_TIMEOUT_MS, PROVIDER_PREFIX, PageNavigator, PageTitle, PageTitleContainer, PagesContainer, Pagination, PresentationHeader, PresentationHeaderButtons, PresentationHeaderTools, PresentationPanelContainer, PresentationPanelWrapper, PresentationWrapper, ProgressContainer, ProviderPrefix, 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, SvgImage, TIME_ZONE_FORMAT, TabsContainer, TextTrim, ThemeName, TitleContainer, TopContainer, TopContainerButtons, TwoColumnContainer, UploadContainer, VIEW_MODES, WidgetType, addDataSource, addDataSources, adjustColor, applyFiltersToCondition, applyQueryFilters, applyVarsToCondition, asAttributeName, asChartId, asContainerId, asDataSourceName, asFilterName, asLayerName, asModalId, asResourceId, asTabId, checkEqualOrIncludes, checkIsLoading, createConfigLayer, createConfigPage, createNewPageId, createSaveNotificationId, createTreeNode, dateOptions, debounce, decimalOpacityToHex, eqlParametersToPayload, findAttributeInExpression, formatArea, formatAttributeValue, formatChartRelatedValue, formatConditionValue, formatDataSourceCondition, formatDate$1 as formatDate, formatElementValue, formatLength, formatNumber, formatPolygonMeasure, getActualExtrusionHeight, getAttributeByName, getAttributeValue, getAttributesConfiguration, getChartAxes, getChartFilterName, getChartMarkers, getConfigFilter, getContainerComponent, getDashboardHeader, getDataFromAttributes, getDataFromRelatedFeatures, getDataSource, getDataSourceFilterValue, getDate, getDefaultConfig, getDisplayTemplateNameFromAttribute, getElementValue, getFeatureAttributes, getFeatureCardHeader, getFilterComponent, getFilterSelectedItems, getFilterValue, getFormattedAttributes, getGradientColors, getLayerInfo, getLayerInfoFromDataSources, getPagesFromConfig, getPagesFromProjectInfo, getProxyService, getRelatedAttribute, getRenderElement, getResourceUrl, getRootElementId, getSelectedFilterValue, getSlideshowImages, getSvgUrl, getTemplateNameFromAttribute, getThemeByName, getTotalFromAttributes, getTotalFromRelatedFeatures, hexToRgba, isEmptyElementValue, isEmptyValue, isHiddenEmptyValue, isHookActive, isLayerService, isNotValidSelectedTab, isNumeric, isObject, isProxyService, isVisibleContainer, metersPerPixel, numberOptions, parseClientStyle, parseIconNames, parseIconNamesFromClientStyle, pieChartTooltipFromAttributes, pieChartTooltipFromRelatedFeatures, pointOptions, removeDataSource, rgbToHex, roundTotalSum, sliceShownOtherItems, timeOptions, toRenderableValue, tooltipNameFromAttributes, tooltipValueFromAttributes, tooltipValueFromRelatedFeatures, transparentizeColor, treeNodesToProjectItems, updateDataSource, useAfterSave, useAppHeight, useAttachmentItems, useAttachmentPreviewImages, useAutoCompleteControl, useBeforeSave, useChartChange, useChartData, useContainerAttributes, useCurrentPageLayers, useCustomFeatureSelect, useDashboardHeader, useDataSources, useDebouncedCallback, useDiffPage, useEditGroupAttributes, useExpandableContainers, useExportPdf, useFeatureSaveHooks, useFetchImageWithAuth, useFetchWithAuth, useGetConfigLayer, useGlobalContext, useHeaderRender, useHideIfEmptyDataSource, useIconsFromLayers, useLayerHiddenAttributes, useLayerParams, useMapContext, useMapDraw, useMapImages, useMaxZoomTo, useProjectDashboardInit, usePythonSandbox, usePythonTask, useRedrawLayer, useRelatedDataSourceAttributes, useRemoteTask, useRenderElement, useSavePrototypeBuilder, useServerNotificationsContext, useShownOtherItems, useToggle, useUpdateDataSource, useVisibleProjectItems, useWidgetConfig, useWidgetContext, useWidgetFilters, useWidgetPage, useWindowResize, useZoomToFeatures, useZoomToPoint };
13654
13886
  //# sourceMappingURL=react.esm.js.map