@evergis/react 4.0.137 → 4.0.138
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/Dashboard/componentTypes.d.ts +10 -1
- package/dist/components/Dashboard/containers/VoteContainer/components/VoteCreateForm.d.ts +3 -0
- package/dist/components/Dashboard/containers/VoteContainer/components/VoteResults.d.ts +3 -0
- package/dist/components/Dashboard/containers/VoteContainer/components/VoteScreen.d.ts +9 -0
- package/dist/components/Dashboard/containers/VoteContainer/components/VoteShare.d.ts +3 -0
- package/dist/components/Dashboard/containers/VoteContainer/constants.d.ts +46 -0
- package/dist/components/Dashboard/containers/VoteContainer/hooks/useVoteActions.d.ts +26 -0
- package/dist/components/Dashboard/containers/VoteContainer/hooks/useVoteContainer.d.ts +25 -0
- package/dist/components/Dashboard/containers/VoteContainer/hooks/useVoteData.d.ts +22 -0
- package/dist/components/Dashboard/containers/VoteContainer/hooks/useVoteForm.d.ts +17 -0
- package/dist/components/Dashboard/containers/VoteContainer/hooks/useVoteUser.d.ts +14 -0
- package/dist/components/Dashboard/containers/VoteContainer/index.d.ts +11 -0
- package/dist/components/Dashboard/containers/VoteContainer/styled.d.ts +31 -0
- package/dist/components/Dashboard/containers/VoteContainer/types.d.ts +88 -0
- package/dist/components/Dashboard/containers/VoteContainer/utils.d.ts +38 -0
- package/dist/components/Dashboard/containers/index.d.ts +1 -0
- package/dist/components/Dashboard/containers/registry.d.ts +1 -0
- package/dist/components/Dashboard/types.d.ts +16 -2
- package/dist/index.js +961 -229
- package/dist/index.js.map +1 -1
- package/dist/react.esm.js +963 -232
- package/dist/react.esm.js.map +1 -1
- package/package.json +2 -2
package/dist/react.esm.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import styled, { createGlobalStyle, css, useTheme } from 'styled-components';
|
|
2
|
-
import { Icon, IconButtonInnerChild, IconButton, Flex, transition, Chip, shadows, Description, Divider, Popup, Menu, IconToggleButton, LinearProgress, Tooltip as Tooltip$1, IconToggle, Preview, LegendToggler, DropdownField, MultiSelectContainer, IconButtonButton, useDragAndDropEffect, FlatButton, DraggableTreeContainer, DraggableTree, FlexSpan, Dialog, DialogTitle, ActionsGroup, DialogContent, DialogActions, RaisedButton, UploaderItemArea, UploaderTitleWrapper,
|
|
2
|
+
import { Icon, IconButtonInnerChild, IconButton, Flex, transition, Chip, shadows, Description, Divider, Popup, Menu, IconToggleButton, LinearProgress, Tooltip as Tooltip$1, IconToggle, Preview, LegendToggler, DropdownField, MultiSelectContainer, IconButtonButton, useDragAndDropEffect, FlatButton, DraggableTreeContainer, DraggableTree, FlexSpan, Dialog, DialogTitle, ActionsGroup, DialogContent, DialogActions, RaisedButton, UploaderItemArea, UploaderTitleWrapper, RadioGroup, Dropdown, Input, Switch, Checkbox, Radio, ThemeProvider, darkTheme, CircularProgress, AutoComplete, Slider, DatePicker, getLocale, H2, Blank, Popover, NumberInput, Uploader, NumberRangeSlider, useAsyncAutocomplete, RangeNumberInput, TreeDropdown, defaultTheme, dateFormat } from '@evergis/uilib-gl';
|
|
3
3
|
import { jsx, jsxs, Fragment as Fragment$1 } from 'react/jsx-runtime';
|
|
4
4
|
import { isValidElement, Fragment, createContext, memo, useRef, useState, useCallback, useEffect, useContext, useMemo, createElement, useLayoutEffect, forwardRef } from 'react';
|
|
5
5
|
import { barChartClassNames, lineChartClassNames, BarChart as BarChart$1, LineChart, PieChart } from '@evergis/charts';
|
|
6
6
|
import { AttributeType, AttributeIconType, generateId, STORAGE_TOKEN_KEY, parseJwt, STORAGE_REFRESH_TOKEN_KEY, RemoteTaskStatus, AttributeConfigurationType, LayerServiceType, OgcGeometryType, StringSubType } from '@evergis/api';
|
|
7
7
|
import { isNil, isEqual, isEmpty, uniqueId, unescape } from 'lodash';
|
|
8
|
-
import { Color as Color$1
|
|
8
|
+
import { ColorScale, Color as Color$1 } from '@evergis/color';
|
|
9
9
|
import { isValid, format, parseJSON, parseISO, toDate } from 'date-fns';
|
|
10
10
|
import { ru, enUS } from 'date-fns/locale';
|
|
11
11
|
import { stringify } from 'wkt';
|
|
@@ -3295,6 +3295,7 @@ var ContainerTemplate;
|
|
|
3295
3295
|
ContainerTemplate["Attachment"] = "Attachment";
|
|
3296
3296
|
ContainerTemplate["Divider"] = "Divider";
|
|
3297
3297
|
ContainerTemplate["StructuredData"] = "StructuredData";
|
|
3298
|
+
ContainerTemplate["Vote"] = "Vote";
|
|
3298
3299
|
})(ContainerTemplate || (ContainerTemplate = {}));
|
|
3299
3300
|
var HeaderTemplate;
|
|
3300
3301
|
(function (HeaderTemplate) {
|
|
@@ -5377,169 +5378,6 @@ const getChartMarkers = (items, markers, dataSources) => {
|
|
|
5377
5378
|
})) || []);
|
|
5378
5379
|
};
|
|
5379
5380
|
|
|
5380
|
-
const ChartLegendContainer = styled(Flex).withConfig({ displayName: "ChartLegendContainer", componentId: "sc-1di1zl9" }) `
|
|
5381
|
-
flex-direction: column;
|
|
5382
|
-
flex-wrap: wrap;
|
|
5383
|
-
justify-content: ${({ twoColumns }) => (twoColumns ? "flex-start" : "center")};
|
|
5384
|
-
`;
|
|
5385
|
-
const ChartLegendItem = styled(Flex).withConfig({ displayName: "ChartLegendItem", componentId: "sc-16m9piw" }) `
|
|
5386
|
-
cursor: ${({ onClick }) => (onClick ? "pointer" : "default")};
|
|
5387
|
-
align-items: center;
|
|
5388
|
-
flex-wrap: nowrap;
|
|
5389
|
-
width: auto;
|
|
5390
|
-
margin-right: 0.375rem;
|
|
5391
|
-
margin-bottom: 0.25rem;
|
|
5392
|
-
line-height: 0.75rem;
|
|
5393
|
-
opacity: ${({ isFiltered, hasAnyFilter }) => (isFiltered ? 1 : hasAnyFilter ? FILTERED_VALUE_OPACITY / 100 : 1)};
|
|
5394
|
-
`;
|
|
5395
|
-
const ChartLegendColor = styled.div.withConfig({ displayName: "ChartLegendColor", componentId: "sc-zxnnio" }) `
|
|
5396
|
-
width: 0.5rem;
|
|
5397
|
-
height: 0.5rem;
|
|
5398
|
-
margin-right: 0.375rem;
|
|
5399
|
-
background-color: ${({ color }) => color};
|
|
5400
|
-
border-radius: ${({ theme: { borderRadius } }) => borderRadius.tiny};
|
|
5401
|
-
`;
|
|
5402
|
-
const ChartLegendName = styled.div.withConfig({ displayName: "ChartLegendName", componentId: "sc-isz576" }) `
|
|
5403
|
-
flex: 1;
|
|
5404
|
-
font-size: ${({ $fontSize }) => $fontSize || "0.625rem"};
|
|
5405
|
-
color: ${({ theme: { palette }, $fontColor }) => $fontColor || palette.textPrimary};
|
|
5406
|
-
`;
|
|
5407
|
-
|
|
5408
|
-
const ContainerAlias = styled(Flex).withConfig({ displayName: "ContainerAlias", componentId: "sc-1ftaxyu" }) `
|
|
5409
|
-
align-items: center;
|
|
5410
|
-
flex-wrap: nowrap;
|
|
5411
|
-
font-size: 0.75rem;
|
|
5412
|
-
color: ${({ theme: { palette } }) => palette.textSecondary};
|
|
5413
|
-
|
|
5414
|
-
&&& {
|
|
5415
|
-
margin-bottom: ${({ hasBottomMargin }) => (hasBottomMargin ? 0.25 : 0)}rem;
|
|
5416
|
-
}
|
|
5417
|
-
|
|
5418
|
-
span[kind] {
|
|
5419
|
-
margin-right: 0.5rem;
|
|
5420
|
-
|
|
5421
|
-
:after {
|
|
5422
|
-
color: ${({ theme: { palette } }) => palette.primary};
|
|
5423
|
-
}
|
|
5424
|
-
}
|
|
5425
|
-
`;
|
|
5426
|
-
const ContainerAliasIcon = styled.div.withConfig({ displayName: "ContainerAliasIcon", componentId: "sc-elezmi" }) `
|
|
5427
|
-
margin-right: 0.5rem;
|
|
5428
|
-
`;
|
|
5429
|
-
const ContainerChart = styled(Flex).withConfig({ displayName: "ContainerChart", componentId: "sc-16sh3wn" }) `
|
|
5430
|
-
justify-content: flex-start;
|
|
5431
|
-
|
|
5432
|
-
> * {
|
|
5433
|
-
display: flex;
|
|
5434
|
-
justify-content: center;
|
|
5435
|
-
width: 100%;
|
|
5436
|
-
}
|
|
5437
|
-
|
|
5438
|
-
/* fill: слот забирает остаток высоты ячейки (под слотами title/alias/legend) и центрирует тело графика. */
|
|
5439
|
-
${({ $fill }) => $fill &&
|
|
5440
|
-
css `
|
|
5441
|
-
flex: 1 1 auto;
|
|
5442
|
-
min-height: 0;
|
|
5443
|
-
align-items: center;
|
|
5444
|
-
justify-content: center;
|
|
5445
|
-
|
|
5446
|
-
> * {
|
|
5447
|
-
height: 100%;
|
|
5448
|
-
}
|
|
5449
|
-
`}
|
|
5450
|
-
`;
|
|
5451
|
-
const ContainerLegend = styled(Flex).withConfig({ displayName: "ContainerLegend", componentId: "sc-z1n1s8" }) ``;
|
|
5452
|
-
const ContainerUnits = styled.div.withConfig({ displayName: "ContainerUnits", componentId: "sc-1vurgy9" }) `
|
|
5453
|
-
margin-left: 0.5rem;
|
|
5454
|
-
white-space: nowrap;
|
|
5455
|
-
font-size: 0.75rem;
|
|
5456
|
-
`;
|
|
5457
|
-
const ContainerValue = styled(Flex).withConfig({ displayName: "ContainerValue", componentId: "sc-aj1rs9" }) `
|
|
5458
|
-
justify-content: flex-end;
|
|
5459
|
-
align-items: center;
|
|
5460
|
-
flex-wrap: nowrap;
|
|
5461
|
-
width: 100%;
|
|
5462
|
-
font-size: ${({ big }) => (big ? 1.5 : 1)}rem;
|
|
5463
|
-
color: ${({ fontColor, theme: { palette } }) => fontColor || palette.textPrimary};
|
|
5464
|
-
|
|
5465
|
-
/* fill: строка со слотом chart+legend забирает остаток высоты Container'а (гейт — только у Chart-контейнера). */
|
|
5466
|
-
${({ $fill }) => $fill &&
|
|
5467
|
-
css `
|
|
5468
|
-
flex: 1 1 auto;
|
|
5469
|
-
min-height: 0;
|
|
5470
|
-
`}
|
|
5471
|
-
|
|
5472
|
-
> * {
|
|
5473
|
-
width: ${({ column }) => (column ? "100%" : "auto")};
|
|
5474
|
-
}
|
|
5475
|
-
|
|
5476
|
-
${ContainerChart}, ${ContainerLegend} {
|
|
5477
|
-
width: ${({ column }) => (column ? "100%" : "50%")};
|
|
5478
|
-
}
|
|
5479
|
-
|
|
5480
|
-
${ContainerLegend} {
|
|
5481
|
-
margin-left: ${({ column }) => (column ? 0 : "1rem")};
|
|
5482
|
-
}
|
|
5483
|
-
|
|
5484
|
-
${ChartLegendContainer} {
|
|
5485
|
-
flex-direction: ${({ column }) => (column ? "row" : "column")};
|
|
5486
|
-
margin-top: ${({ column }) => (column ? "1rem" : 0)};
|
|
5487
|
-
}
|
|
5488
|
-
`;
|
|
5489
|
-
const ColorIconMixin = css `
|
|
5490
|
-
:after {
|
|
5491
|
-
color: ${({ $fontColor }) => $fontColor} !important;
|
|
5492
|
-
}
|
|
5493
|
-
`;
|
|
5494
|
-
const SizeIconMixin = css `
|
|
5495
|
-
:after {
|
|
5496
|
-
font-size: ${({ $fontSize }) => $fontSize}px !important;
|
|
5497
|
-
}
|
|
5498
|
-
`;
|
|
5499
|
-
styled(Icon).withConfig({ displayName: "ContainerIcon", componentId: "sc-1w9w0bq" }) `
|
|
5500
|
-
width: auto;
|
|
5501
|
-
height: auto;
|
|
5502
|
-
margin-bottom: 0.5rem;
|
|
5503
|
-
${({ $fontColor }) => !!$fontColor && ColorIconMixin};
|
|
5504
|
-
${({ $fontSize }) => !!$fontSize && SizeIconMixin};
|
|
5505
|
-
`;
|
|
5506
|
-
const SvgContainerColorMixin$1 = css `
|
|
5507
|
-
path,
|
|
5508
|
-
line,
|
|
5509
|
-
circle {
|
|
5510
|
-
fill: ${({ $fontColor }) => $fontColor};
|
|
5511
|
-
}
|
|
5512
|
-
`;
|
|
5513
|
-
const SvgContainer$1 = styled.div.withConfig({ displayName: "SvgContainer", componentId: "sc-1qs5gmx" }) `
|
|
5514
|
-
&&& {
|
|
5515
|
-
min-width: ${({ $width }) => ($width ? `${$width}px` : "1rem")};
|
|
5516
|
-
max-width: ${({ $width }) => ($width ? `${$width}px` : "1rem")};
|
|
5517
|
-
|
|
5518
|
-
${({ $fontColor }) => !!$fontColor && SvgContainerColorMixin$1};
|
|
5519
|
-
|
|
5520
|
-
> * {
|
|
5521
|
-
min-width: inherit;
|
|
5522
|
-
}
|
|
5523
|
-
}
|
|
5524
|
-
`;
|
|
5525
|
-
const TwoColumnContainerWrapper = styled(Container).withConfig({ displayName: "TwoColumnContainerWrapper", componentId: "sc-1ogv4d3" }) `
|
|
5526
|
-
flex-direction: row;
|
|
5527
|
-
flex-wrap: nowrap;
|
|
5528
|
-
align-items: center;
|
|
5529
|
-
|
|
5530
|
-
> * {
|
|
5531
|
-
flex: 1;
|
|
5532
|
-
}
|
|
5533
|
-
|
|
5534
|
-
> ${ContainerValue} {
|
|
5535
|
-
justify-content: flex-end;
|
|
5536
|
-
|
|
5537
|
-
> * {
|
|
5538
|
-
text-align: right;
|
|
5539
|
-
}
|
|
5540
|
-
}
|
|
5541
|
-
`;
|
|
5542
|
-
|
|
5543
5381
|
const DashboardContext = createContext({});
|
|
5544
5382
|
const DashboardProvider = memo(({ children, ...props }) => {
|
|
5545
5383
|
return jsx(DashboardContext.Provider, { value: props, children: children });
|
|
@@ -8665,61 +8503,6 @@ const useUpdateDataSource = ({ dataSource, config, filters, attributes, layerPar
|
|
|
8665
8503
|
}, [dataSource, getDataSourcePromises, getUpdatedDataSources, dataSources]);
|
|
8666
8504
|
};
|
|
8667
8505
|
|
|
8668
|
-
const useRenderContainer = ({ elementConfig, type, renderElement, renderBody, }) => {
|
|
8669
|
-
const { attributes } = useWidgetContext(type);
|
|
8670
|
-
const { getRenderContainerItem, attributesToRender } = useContainerAttributes({
|
|
8671
|
-
elementConfig,
|
|
8672
|
-
type,
|
|
8673
|
-
renderElement,
|
|
8674
|
-
});
|
|
8675
|
-
const renderContainer = useCallback((attribute) => {
|
|
8676
|
-
const item = getRenderContainerItem(elementConfig, attribute);
|
|
8677
|
-
const itemAttribute = attribute
|
|
8678
|
-
? attributes?.find(({ attributeName }) => attributeName === attribute)
|
|
8679
|
-
: undefined;
|
|
8680
|
-
const overrideTemplateName = getDisplayTemplateNameFromAttribute(itemAttribute);
|
|
8681
|
-
if (overrideTemplateName) {
|
|
8682
|
-
const OverrideContainer = getContainerComponent(overrideTemplateName);
|
|
8683
|
-
if (OverrideContainer) {
|
|
8684
|
-
const itemConfig = {
|
|
8685
|
-
...elementConfig,
|
|
8686
|
-
children: (elementConfig?.children ?? []).map(child => ({
|
|
8687
|
-
...child,
|
|
8688
|
-
attributeName: attribute,
|
|
8689
|
-
})),
|
|
8690
|
-
templateName: overrideTemplateName,
|
|
8691
|
-
};
|
|
8692
|
-
return (jsx(OverrideContainer, { type: type, elementConfig: itemConfig, renderElement: item.render }, attribute));
|
|
8693
|
-
}
|
|
8694
|
-
}
|
|
8695
|
-
if (isEmptyElementValue(item.value) && item.hideEmpty)
|
|
8696
|
-
return null;
|
|
8697
|
-
return renderBody(item, attribute);
|
|
8698
|
-
}, [getRenderContainerItem, elementConfig, attributes, type, renderBody]);
|
|
8699
|
-
return { renderContainer, attributesToRender };
|
|
8700
|
-
};
|
|
8701
|
-
|
|
8702
|
-
const OneColumnContainer = memo(({ type, elementConfig, renderElement }) => {
|
|
8703
|
-
const { width, height, overflow } = elementConfig?.options || {};
|
|
8704
|
-
const templateName = elementConfig?.templateName;
|
|
8705
|
-
const bgHost = useBgImageHost(elementConfig);
|
|
8706
|
-
const renderBody = useCallback(({ id, value, style, hasUnits, render }) => (jsxs(Container, { id: id, isColumn: true, "data-templatename": templateName, style: style, "$sizeCss": getWrapperSizeStyle({
|
|
8707
|
-
style,
|
|
8708
|
-
width,
|
|
8709
|
-
height,
|
|
8710
|
-
overflow,
|
|
8711
|
-
defaults: BASE_CONTAINER_STYLE,
|
|
8712
|
-
defaultWidth: FILL_SIZE,
|
|
8713
|
-
}), ...bgHost, children: [jsx(ContainerBackground, { elementConfig: elementConfig, renderElement: render }), jsxs(ContainerAlias, { hasBottomMargin: true, children: [render({ id: "alias" }), render({ id: "tooltip" }), render({ id: "modal" })] }), jsxs(ContainerValue, { children: [value, hasUnits && (jsx(ContainerUnits, { children: render({ id: "units" }) }))] })] })), [width, height, overflow, templateName, bgHost, elementConfig]);
|
|
8714
|
-
const { renderContainer, attributesToRender } = useRenderContainer({
|
|
8715
|
-
type,
|
|
8716
|
-
elementConfig,
|
|
8717
|
-
renderElement,
|
|
8718
|
-
renderBody,
|
|
8719
|
-
});
|
|
8720
|
-
return attributesToRender ? (jsx(Fragment$1, { children: attributesToRender.map(renderContainer) })) : (renderContainer());
|
|
8721
|
-
});
|
|
8722
|
-
|
|
8723
8506
|
/** Обычная (не сеточная) раскладка группы: дети в колонку или в строку. */
|
|
8724
8507
|
const FlowGroup = memo(({ elementConfig, type, renderElement }) => {
|
|
8725
8508
|
const { expandedContainers } = useWidgetContext(type);
|
|
@@ -9272,18 +9055,181 @@ const AttachmentContainer = memo(({ type, elementConfig, renderElement }) => {
|
|
|
9272
9055
|
}) }, previewIndex))] }) }))] }));
|
|
9273
9056
|
});
|
|
9274
9057
|
|
|
9275
|
-
const
|
|
9276
|
-
|
|
9277
|
-
|
|
9278
|
-
|
|
9279
|
-
|
|
9280
|
-
|
|
9281
|
-
|
|
9282
|
-
|
|
9058
|
+
const ChartLegendContainer = styled(Flex).withConfig({ displayName: "ChartLegendContainer", componentId: "sc-1di1zl9" }) `
|
|
9059
|
+
flex-direction: column;
|
|
9060
|
+
flex-wrap: wrap;
|
|
9061
|
+
justify-content: ${({ twoColumns }) => (twoColumns ? "flex-start" : "center")};
|
|
9062
|
+
`;
|
|
9063
|
+
const ChartLegendItem = styled(Flex).withConfig({ displayName: "ChartLegendItem", componentId: "sc-16m9piw" }) `
|
|
9064
|
+
cursor: ${({ onClick }) => (onClick ? "pointer" : "default")};
|
|
9065
|
+
align-items: center;
|
|
9066
|
+
flex-wrap: nowrap;
|
|
9067
|
+
width: auto;
|
|
9068
|
+
margin-right: 0.375rem;
|
|
9069
|
+
margin-bottom: 0.25rem;
|
|
9070
|
+
line-height: 0.75rem;
|
|
9071
|
+
opacity: ${({ isFiltered, hasAnyFilter }) => (isFiltered ? 1 : hasAnyFilter ? FILTERED_VALUE_OPACITY / 100 : 1)};
|
|
9072
|
+
`;
|
|
9073
|
+
const ChartLegendColor = styled.div.withConfig({ displayName: "ChartLegendColor", componentId: "sc-zxnnio" }) `
|
|
9074
|
+
width: 0.5rem;
|
|
9075
|
+
height: 0.5rem;
|
|
9076
|
+
margin-right: 0.375rem;
|
|
9077
|
+
background-color: ${({ color }) => color};
|
|
9078
|
+
border-radius: ${({ theme: { borderRadius } }) => borderRadius.tiny};
|
|
9079
|
+
`;
|
|
9080
|
+
const ChartLegendName = styled.div.withConfig({ displayName: "ChartLegendName", componentId: "sc-isz576" }) `
|
|
9081
|
+
flex: 1;
|
|
9082
|
+
font-size: ${({ $fontSize }) => $fontSize || "0.625rem"};
|
|
9083
|
+
color: ${({ theme: { palette }, $fontColor }) => $fontColor || palette.textPrimary};
|
|
9084
|
+
`;
|
|
9283
9085
|
|
|
9284
|
-
|
|
9285
|
-
|
|
9286
|
-
|
|
9086
|
+
const ContainerAlias = styled(Flex).withConfig({ displayName: "ContainerAlias", componentId: "sc-1ftaxyu" }) `
|
|
9087
|
+
align-items: center;
|
|
9088
|
+
flex-wrap: nowrap;
|
|
9089
|
+
font-size: 0.75rem;
|
|
9090
|
+
color: ${({ theme: { palette } }) => palette.textSecondary};
|
|
9091
|
+
|
|
9092
|
+
&&& {
|
|
9093
|
+
margin-bottom: ${({ hasBottomMargin }) => (hasBottomMargin ? 0.25 : 0)}rem;
|
|
9094
|
+
}
|
|
9095
|
+
|
|
9096
|
+
span[kind] {
|
|
9097
|
+
margin-right: 0.5rem;
|
|
9098
|
+
|
|
9099
|
+
:after {
|
|
9100
|
+
color: ${({ theme: { palette } }) => palette.primary};
|
|
9101
|
+
}
|
|
9102
|
+
}
|
|
9103
|
+
`;
|
|
9104
|
+
const ContainerAliasIcon = styled.div.withConfig({ displayName: "ContainerAliasIcon", componentId: "sc-elezmi" }) `
|
|
9105
|
+
margin-right: 0.5rem;
|
|
9106
|
+
`;
|
|
9107
|
+
const ContainerChart = styled(Flex).withConfig({ displayName: "ContainerChart", componentId: "sc-16sh3wn" }) `
|
|
9108
|
+
justify-content: flex-start;
|
|
9109
|
+
|
|
9110
|
+
> * {
|
|
9111
|
+
display: flex;
|
|
9112
|
+
justify-content: center;
|
|
9113
|
+
width: 100%;
|
|
9114
|
+
}
|
|
9115
|
+
|
|
9116
|
+
/* fill: слот забирает остаток высоты ячейки (под слотами title/alias/legend) и центрирует тело графика. */
|
|
9117
|
+
${({ $fill }) => $fill &&
|
|
9118
|
+
css `
|
|
9119
|
+
flex: 1 1 auto;
|
|
9120
|
+
min-height: 0;
|
|
9121
|
+
align-items: center;
|
|
9122
|
+
justify-content: center;
|
|
9123
|
+
|
|
9124
|
+
> * {
|
|
9125
|
+
height: 100%;
|
|
9126
|
+
}
|
|
9127
|
+
`}
|
|
9128
|
+
`;
|
|
9129
|
+
const ContainerLegend = styled(Flex).withConfig({ displayName: "ContainerLegend", componentId: "sc-z1n1s8" }) ``;
|
|
9130
|
+
const ContainerUnits = styled.div.withConfig({ displayName: "ContainerUnits", componentId: "sc-1vurgy9" }) `
|
|
9131
|
+
margin-left: 0.5rem;
|
|
9132
|
+
white-space: nowrap;
|
|
9133
|
+
font-size: 0.75rem;
|
|
9134
|
+
`;
|
|
9135
|
+
const ContainerValue = styled(Flex).withConfig({ displayName: "ContainerValue", componentId: "sc-aj1rs9" }) `
|
|
9136
|
+
justify-content: flex-end;
|
|
9137
|
+
align-items: center;
|
|
9138
|
+
flex-wrap: nowrap;
|
|
9139
|
+
width: 100%;
|
|
9140
|
+
font-size: ${({ big }) => (big ? 1.5 : 1)}rem;
|
|
9141
|
+
color: ${({ fontColor, theme: { palette } }) => fontColor || palette.textPrimary};
|
|
9142
|
+
|
|
9143
|
+
/* fill: строка со слотом chart+legend забирает остаток высоты Container'а (гейт — только у Chart-контейнера). */
|
|
9144
|
+
${({ $fill }) => $fill &&
|
|
9145
|
+
css `
|
|
9146
|
+
flex: 1 1 auto;
|
|
9147
|
+
min-height: 0;
|
|
9148
|
+
`}
|
|
9149
|
+
|
|
9150
|
+
> * {
|
|
9151
|
+
width: ${({ column }) => (column ? "100%" : "auto")};
|
|
9152
|
+
}
|
|
9153
|
+
|
|
9154
|
+
${ContainerChart}, ${ContainerLegend} {
|
|
9155
|
+
width: ${({ column }) => (column ? "100%" : "50%")};
|
|
9156
|
+
}
|
|
9157
|
+
|
|
9158
|
+
${ContainerLegend} {
|
|
9159
|
+
margin-left: ${({ column }) => (column ? 0 : "1rem")};
|
|
9160
|
+
}
|
|
9161
|
+
|
|
9162
|
+
${ChartLegendContainer} {
|
|
9163
|
+
flex-direction: ${({ column }) => (column ? "row" : "column")};
|
|
9164
|
+
margin-top: ${({ column }) => (column ? "1rem" : 0)};
|
|
9165
|
+
}
|
|
9166
|
+
`;
|
|
9167
|
+
const ColorIconMixin = css `
|
|
9168
|
+
:after {
|
|
9169
|
+
color: ${({ $fontColor }) => $fontColor} !important;
|
|
9170
|
+
}
|
|
9171
|
+
`;
|
|
9172
|
+
const SizeIconMixin = css `
|
|
9173
|
+
:after {
|
|
9174
|
+
font-size: ${({ $fontSize }) => $fontSize}px !important;
|
|
9175
|
+
}
|
|
9176
|
+
`;
|
|
9177
|
+
styled(Icon).withConfig({ displayName: "ContainerIcon", componentId: "sc-1w9w0bq" }) `
|
|
9178
|
+
width: auto;
|
|
9179
|
+
height: auto;
|
|
9180
|
+
margin-bottom: 0.5rem;
|
|
9181
|
+
${({ $fontColor }) => !!$fontColor && ColorIconMixin};
|
|
9182
|
+
${({ $fontSize }) => !!$fontSize && SizeIconMixin};
|
|
9183
|
+
`;
|
|
9184
|
+
const SvgContainerColorMixin$1 = css `
|
|
9185
|
+
path,
|
|
9186
|
+
line,
|
|
9187
|
+
circle {
|
|
9188
|
+
fill: ${({ $fontColor }) => $fontColor};
|
|
9189
|
+
}
|
|
9190
|
+
`;
|
|
9191
|
+
const SvgContainer$1 = styled.div.withConfig({ displayName: "SvgContainer", componentId: "sc-1qs5gmx" }) `
|
|
9192
|
+
&&& {
|
|
9193
|
+
min-width: ${({ $width }) => ($width ? `${$width}px` : "1rem")};
|
|
9194
|
+
max-width: ${({ $width }) => ($width ? `${$width}px` : "1rem")};
|
|
9195
|
+
|
|
9196
|
+
${({ $fontColor }) => !!$fontColor && SvgContainerColorMixin$1};
|
|
9197
|
+
|
|
9198
|
+
> * {
|
|
9199
|
+
min-width: inherit;
|
|
9200
|
+
}
|
|
9201
|
+
}
|
|
9202
|
+
`;
|
|
9203
|
+
const TwoColumnContainerWrapper = styled(Container).withConfig({ displayName: "TwoColumnContainerWrapper", componentId: "sc-1ogv4d3" }) `
|
|
9204
|
+
flex-direction: row;
|
|
9205
|
+
flex-wrap: nowrap;
|
|
9206
|
+
align-items: center;
|
|
9207
|
+
|
|
9208
|
+
> * {
|
|
9209
|
+
flex: 1;
|
|
9210
|
+
}
|
|
9211
|
+
|
|
9212
|
+
> ${ContainerValue} {
|
|
9213
|
+
justify-content: flex-end;
|
|
9214
|
+
|
|
9215
|
+
> * {
|
|
9216
|
+
text-align: right;
|
|
9217
|
+
}
|
|
9218
|
+
}
|
|
9219
|
+
`;
|
|
9220
|
+
|
|
9221
|
+
const CameraContainer = memo(({ elementConfig, type, renderElement }) => {
|
|
9222
|
+
const { expandedContainers } = useWidgetContext(type);
|
|
9223
|
+
const { id, options } = elementConfig || {};
|
|
9224
|
+
const { expandable, expanded } = options || {};
|
|
9225
|
+
const isVisible = isVisibleContainer(id, expandable, expanded, expandedContainers);
|
|
9226
|
+
const { root, body } = useContainerRoot({ elementConfig });
|
|
9227
|
+
return (jsxs(ContainerRoot, { ...root, children: [jsx(ContainerBackground, { elementConfig: elementConfig, renderElement: renderElement }), jsx(ExpandableTitle, { elementConfig: elementConfig, type: type, renderElement: renderElement }), isVisible && (jsxs(Container, { ...body, children: [jsx(ContainerAlias, { hasBottomMargin: true, children: renderElement({ id: "alias" }) }), jsx(ContainerValue, { children: renderElement({ id: "value", wrap: false }) })] }))] }));
|
|
9228
|
+
});
|
|
9229
|
+
|
|
9230
|
+
/**
|
|
9231
|
+
* Контекст вписывания графика (`options.fill` контейнера Chart).
|
|
9232
|
+
*
|
|
9287
9233
|
* Компонент `Chart` рендерится через `renderElement({ id: "chart" })` и получает только дочерний
|
|
9288
9234
|
* узел `{ id: "chart" }` — опции контейнера до него не доходят. `ChartContainer` оборачивает
|
|
9289
9235
|
* график в провайдер этого контекста, а `Chart` читает значение через `useContext`.
|
|
@@ -10919,6 +10865,61 @@ const LayersContainer = memo(({ type, elementConfig, renderElement }) => {
|
|
|
10919
10865
|
return (jsxs(ContainerRoot, { ...root, children: [jsx(ContainerBackground, { elementConfig: elementConfig, renderElement: renderElement }), jsx(ExpandableTitle, { elementConfig: elementConfig, type: type, renderElement: renderElement }), isVisible && (jsx(LayersContainerWrapper, { ...body, children: jsx(LayerTree, { layers: layers, onlyMainTools: true }) }))] }));
|
|
10920
10866
|
});
|
|
10921
10867
|
|
|
10868
|
+
const useRenderContainer = ({ elementConfig, type, renderElement, renderBody, }) => {
|
|
10869
|
+
const { attributes } = useWidgetContext(type);
|
|
10870
|
+
const { getRenderContainerItem, attributesToRender } = useContainerAttributes({
|
|
10871
|
+
elementConfig,
|
|
10872
|
+
type,
|
|
10873
|
+
renderElement,
|
|
10874
|
+
});
|
|
10875
|
+
const renderContainer = useCallback((attribute) => {
|
|
10876
|
+
const item = getRenderContainerItem(elementConfig, attribute);
|
|
10877
|
+
const itemAttribute = attribute
|
|
10878
|
+
? attributes?.find(({ attributeName }) => attributeName === attribute)
|
|
10879
|
+
: undefined;
|
|
10880
|
+
const overrideTemplateName = getDisplayTemplateNameFromAttribute(itemAttribute);
|
|
10881
|
+
if (overrideTemplateName) {
|
|
10882
|
+
const OverrideContainer = getContainerComponent(overrideTemplateName);
|
|
10883
|
+
if (OverrideContainer) {
|
|
10884
|
+
const itemConfig = {
|
|
10885
|
+
...elementConfig,
|
|
10886
|
+
children: (elementConfig?.children ?? []).map(child => ({
|
|
10887
|
+
...child,
|
|
10888
|
+
attributeName: attribute,
|
|
10889
|
+
})),
|
|
10890
|
+
templateName: overrideTemplateName,
|
|
10891
|
+
};
|
|
10892
|
+
return (jsx(OverrideContainer, { type: type, elementConfig: itemConfig, renderElement: item.render }, attribute));
|
|
10893
|
+
}
|
|
10894
|
+
}
|
|
10895
|
+
if (isEmptyElementValue(item.value) && item.hideEmpty)
|
|
10896
|
+
return null;
|
|
10897
|
+
return renderBody(item, attribute);
|
|
10898
|
+
}, [getRenderContainerItem, elementConfig, attributes, type, renderBody]);
|
|
10899
|
+
return { renderContainer, attributesToRender };
|
|
10900
|
+
};
|
|
10901
|
+
|
|
10902
|
+
const OneColumnContainer = memo(({ type, elementConfig, renderElement }) => {
|
|
10903
|
+
const { width, height, overflow } = elementConfig?.options || {};
|
|
10904
|
+
const templateName = elementConfig?.templateName;
|
|
10905
|
+
const bgHost = useBgImageHost(elementConfig);
|
|
10906
|
+
const renderBody = useCallback(({ id, value, style, hasUnits, render }) => (jsxs(Container, { id: id, isColumn: true, "data-templatename": templateName, style: style, "$sizeCss": getWrapperSizeStyle({
|
|
10907
|
+
style,
|
|
10908
|
+
width,
|
|
10909
|
+
height,
|
|
10910
|
+
overflow,
|
|
10911
|
+
defaults: BASE_CONTAINER_STYLE,
|
|
10912
|
+
defaultWidth: FILL_SIZE,
|
|
10913
|
+
}), ...bgHost, children: [jsx(ContainerBackground, { elementConfig: elementConfig, renderElement: render }), jsxs(ContainerAlias, { hasBottomMargin: true, children: [render({ id: "alias" }), render({ id: "tooltip" }), render({ id: "modal" })] }), jsxs(ContainerValue, { children: [value, hasUnits && (jsx(ContainerUnits, { children: render({ id: "units" }) }))] })] })), [width, height, overflow, templateName, bgHost, elementConfig]);
|
|
10914
|
+
const { renderContainer, attributesToRender } = useRenderContainer({
|
|
10915
|
+
type,
|
|
10916
|
+
elementConfig,
|
|
10917
|
+
renderElement,
|
|
10918
|
+
renderBody,
|
|
10919
|
+
});
|
|
10920
|
+
return attributesToRender ? (jsx(Fragment$1, { children: attributesToRender.map(renderContainer) })) : (renderContainer());
|
|
10921
|
+
});
|
|
10922
|
+
|
|
10922
10923
|
const PagesContainer = memo(({ type = WidgetType.Dashboard, noBorders }) => {
|
|
10923
10924
|
const { config } = useWidgetConfig(type);
|
|
10924
10925
|
const { pageIndex, currentPage } = useWidgetPage(type);
|
|
@@ -12087,6 +12088,734 @@ const UploadContainer = memo(({ type, elementConfig, renderElement }) => {
|
|
|
12087
12088
|
return (jsxs(ContainerRoot, { ...root, children: [jsx(ContainerBackground, { elementConfig: elementConfig, renderElement: renderElement }), jsx(ExpandableTitle, { elementConfig: elementConfig, type: type, renderElement: renderElement }), isVisible && (jsx(UploadContainerWrapper, { ...body, children: renderElement({ id: "uploader", wrap: false }) }))] }));
|
|
12088
12089
|
});
|
|
12089
12090
|
|
|
12091
|
+
/** Зелёный для плашки «Ваш ответ принят». */
|
|
12092
|
+
const ACCEPTED_GREEN = "#2fa84f";
|
|
12093
|
+
const VoteWrapper = styled(Flex).withConfig({ displayName: "VoteWrapper", componentId: "sc-1cvc2lm" }) `
|
|
12094
|
+
flex-direction: column;
|
|
12095
|
+
gap: 0.75rem;
|
|
12096
|
+
width: 100%;
|
|
12097
|
+
color: ${({ theme: { palette } }) => palette.textPrimary};
|
|
12098
|
+
`;
|
|
12099
|
+
const VoteHeading = styled.div.withConfig({ displayName: "VoteHeading", componentId: "sc-rzdt4p" }) `
|
|
12100
|
+
font-size: 1rem;
|
|
12101
|
+
font-weight: 600;
|
|
12102
|
+
color: ${({ theme: { palette } }) => palette.textPrimary};
|
|
12103
|
+
`;
|
|
12104
|
+
const VoteQuestionText = styled.div.withConfig({ displayName: "VoteQuestionText", componentId: "sc-1t4w892" }) `
|
|
12105
|
+
font-size: 0.9375rem;
|
|
12106
|
+
font-weight: 600;
|
|
12107
|
+
color: ${({ theme: { palette } }) => palette.textPrimary};
|
|
12108
|
+
`;
|
|
12109
|
+
const VoteCategoryLabel = styled.div.withConfig({ displayName: "VoteCategoryLabel", componentId: "sc-z1fkgy" }) `
|
|
12110
|
+
font-size: 0.75rem;
|
|
12111
|
+
color: ${({ theme: { palette } }) => palette.textSecondary};
|
|
12112
|
+
`;
|
|
12113
|
+
const VoteSectionLabel = styled.div.withConfig({ displayName: "VoteSectionLabel", componentId: "sc-1xxrikh" }) `
|
|
12114
|
+
font-size: 0.75rem;
|
|
12115
|
+
color: ${({ theme: { palette } }) => palette.textSecondary};
|
|
12116
|
+
`;
|
|
12117
|
+
const VoteChipList = styled(Flex).withConfig({ displayName: "VoteChipList", componentId: "sc-1ahupix" }) `
|
|
12118
|
+
flex-wrap: wrap;
|
|
12119
|
+
gap: 0.375rem;
|
|
12120
|
+
`;
|
|
12121
|
+
const VoteChip = styled(Flex).withConfig({ displayName: "VoteChip", componentId: "sc-x1icg3" }) `
|
|
12122
|
+
align-items: center;
|
|
12123
|
+
gap: 0.25rem;
|
|
12124
|
+
padding: 0.25rem 0.5rem;
|
|
12125
|
+
background-color: ${({ theme: { palette } }) => palette.elementDark};
|
|
12126
|
+
border-radius: 0.25rem;
|
|
12127
|
+
font-size: 0.8125rem;
|
|
12128
|
+
|
|
12129
|
+
:hover {
|
|
12130
|
+
background-color: ${({ theme: { palette } }) => palette.elementDeep};
|
|
12131
|
+
}
|
|
12132
|
+
`;
|
|
12133
|
+
/**
|
|
12134
|
+
* Список вариантов ответа.
|
|
12135
|
+
*
|
|
12136
|
+
* Обёртка — именно `RadioGroup`: `Radio` из uilib читает его legacy-контекст и без него бросает
|
|
12137
|
+
* `Radio must be controlled with RadioGroup context.` — даже когда `checked` и `onChange` заданы
|
|
12138
|
+
* на самом контроле. Группа стоит всегда, а не только в режиме одиночного выбора: лишнего она не
|
|
12139
|
+
* рисует, зато контекст не может пропасть при смене экрана.
|
|
12140
|
+
*
|
|
12141
|
+
* Подпись у `Radio` своя, но текст варианта рисует строка — он делит место со счётчиком и
|
|
12142
|
+
* прогресс-баром. Поэтому пустой узел подписи и отступ кружка до него снимаются.
|
|
12143
|
+
*/
|
|
12144
|
+
const VoteVariantList = styled(RadioGroup).withConfig({ displayName: "VoteVariantList", componentId: "sc-kie7qv" }) `
|
|
12145
|
+
width: 100%;
|
|
12146
|
+
|
|
12147
|
+
label > div {
|
|
12148
|
+
display: none;
|
|
12149
|
+
}
|
|
12150
|
+
|
|
12151
|
+
label > span {
|
|
12152
|
+
margin-right: 0;
|
|
12153
|
+
}
|
|
12154
|
+
`;
|
|
12155
|
+
const VoteVariantRow = styled.div.withConfig({ displayName: "VoteVariantRow", componentId: "sc-hlij7m" }) `
|
|
12156
|
+
display: flex;
|
|
12157
|
+
flex-direction: column;
|
|
12158
|
+
gap: 0.25rem;
|
|
12159
|
+
padding: 0.375rem 0;
|
|
12160
|
+
`;
|
|
12161
|
+
const VoteVariantHead = styled(Flex).withConfig({ displayName: "VoteVariantHead", componentId: "sc-1kbw26l" }) `
|
|
12162
|
+
align-items: center;
|
|
12163
|
+
justify-content: space-between;
|
|
12164
|
+
gap: 0.5rem;
|
|
12165
|
+
font-size: 0.875rem;
|
|
12166
|
+
`;
|
|
12167
|
+
const VoteVariantText = styled.span.withConfig({ displayName: "VoteVariantText", componentId: "sc-1el70dv" }) `
|
|
12168
|
+
flex: 1;
|
|
12169
|
+
min-width: 0;
|
|
12170
|
+
`;
|
|
12171
|
+
const VoteVariantCount = styled.span.withConfig({ displayName: "VoteVariantCount", componentId: "sc-jle8ey" }) `
|
|
12172
|
+
flex-shrink: 0;
|
|
12173
|
+
font-size: 0.8125rem;
|
|
12174
|
+
color: ${({ theme: { palette } }) => palette.textSecondary};
|
|
12175
|
+
`;
|
|
12176
|
+
const VoteAcceptedBadge = styled(Flex).withConfig({ displayName: "VoteAcceptedBadge", componentId: "sc-1utdte8" }) `
|
|
12177
|
+
align-items: center;
|
|
12178
|
+
justify-content: center;
|
|
12179
|
+
gap: 0.5rem;
|
|
12180
|
+
padding: 0.5rem 0.75rem;
|
|
12181
|
+
border-radius: 0.375rem;
|
|
12182
|
+
background-color: ${ACCEPTED_GREEN};
|
|
12183
|
+
color: #ffffff;
|
|
12184
|
+
font-size: 0.875rem;
|
|
12185
|
+
`;
|
|
12186
|
+
const VoteAuthHint = styled.div.withConfig({ displayName: "VoteAuthHint", componentId: "sc-l6fdf1" }) `
|
|
12187
|
+
font-size: 0.8125rem;
|
|
12188
|
+
color: ${({ theme: { palette } }) => palette.textSecondary};
|
|
12189
|
+
`;
|
|
12190
|
+
const VoteEditLink = styled.button.withConfig({ displayName: "VoteEditLink", componentId: "sc-1iedcup" }) `
|
|
12191
|
+
align-self: flex-start;
|
|
12192
|
+
padding: 0;
|
|
12193
|
+
border: none;
|
|
12194
|
+
background: none;
|
|
12195
|
+
cursor: pointer;
|
|
12196
|
+
font-size: 0.8125rem;
|
|
12197
|
+
color: ${({ theme: { palette } }) => palette.primary};
|
|
12198
|
+
|
|
12199
|
+
:hover {
|
|
12200
|
+
text-decoration: underline;
|
|
12201
|
+
}
|
|
12202
|
+
`;
|
|
12203
|
+
const VoteShareBlock = styled(Flex).withConfig({ displayName: "VoteShareBlock", componentId: "sc-a8sttg" }) `
|
|
12204
|
+
flex-direction: column;
|
|
12205
|
+
gap: 0.375rem;
|
|
12206
|
+
margin-top: 0.25rem;
|
|
12207
|
+
`;
|
|
12208
|
+
const VoteShareChips = styled(Flex).withConfig({ displayName: "VoteShareChips", componentId: "sc-1xnysrh" }) `
|
|
12209
|
+
flex-wrap: wrap;
|
|
12210
|
+
gap: 0.375rem;
|
|
12211
|
+
`;
|
|
12212
|
+
const VoteShareChip = styled.div.withConfig({ displayName: "VoteShareChip", componentId: "sc-mrpbcg" }) `
|
|
12213
|
+
padding: 0.25rem 0.625rem;
|
|
12214
|
+
border-radius: 1rem;
|
|
12215
|
+
background-color: ${({ theme: { palette } }) => palette.element};
|
|
12216
|
+
color: ${({ theme: { palette } }) => palette.textSecondary};
|
|
12217
|
+
font-size: 0.75rem;
|
|
12218
|
+
`;
|
|
12219
|
+
styled.div.withConfig({ displayName: "VoteError", componentId: "sc-1gfzwqo" }) `
|
|
12220
|
+
font-size: 0.8125rem;
|
|
12221
|
+
color: ${({ theme: { palette } }) => palette.error};
|
|
12222
|
+
`;
|
|
12223
|
+
|
|
12224
|
+
/** Имена полей таблиц голосования. */
|
|
12225
|
+
const VOTE_FIELDS = {
|
|
12226
|
+
category: {
|
|
12227
|
+
id: "id",
|
|
12228
|
+
name: "name",
|
|
12229
|
+
},
|
|
12230
|
+
question: {
|
|
12231
|
+
id: "id",
|
|
12232
|
+
text: "text",
|
|
12233
|
+
categoryId: "category_id",
|
|
12234
|
+
userName: "user_name",
|
|
12235
|
+
multiSelect: "multi_select",
|
|
12236
|
+
},
|
|
12237
|
+
variant: {
|
|
12238
|
+
id: "id",
|
|
12239
|
+
questionId: "question_id",
|
|
12240
|
+
text: "text",
|
|
12241
|
+
},
|
|
12242
|
+
answer: {
|
|
12243
|
+
id: "id",
|
|
12244
|
+
questionId: "question_id",
|
|
12245
|
+
userName: "user_name",
|
|
12246
|
+
variantId: "variant_id",
|
|
12247
|
+
},
|
|
12248
|
+
};
|
|
12249
|
+
/** Минимальное число вариантов ответа в голосовании. */
|
|
12250
|
+
const MIN_VARIANTS = 2;
|
|
12251
|
+
/** Лимит при чтении списков (вариантов/ответов/категорий). */
|
|
12252
|
+
const VOTE_FETCH_LIMIT = 10000;
|
|
12253
|
+
/** База для перевода доли голосов в проценты. */
|
|
12254
|
+
const PERCENT_BASE = 100;
|
|
12255
|
+
/** Namespace переводов потребляющего приложения (подписи берутся через `t` с defaultValue). */
|
|
12256
|
+
const VOTE_NS = "dashboard";
|
|
12257
|
+
/**
|
|
12258
|
+
* Имя публичного (анонимного) аккаунта портала.
|
|
12259
|
+
*
|
|
12260
|
+
* Под ним сервер отдаёт `getUserInfo()` любому неавторизованному посетителю шаренного проекта,
|
|
12261
|
+
* поэтому «кто уже голосовал» по нему не считается: первый же голос закрыл бы голосование всем
|
|
12262
|
+
* сразу. Такой посетитель для контейнера — неавторизованный, ему доступны только результаты.
|
|
12263
|
+
*
|
|
12264
|
+
* Признака анонима в API нет (`UserInfoDc` — это `username` + `roles`), поэтому имя приходится
|
|
12265
|
+
* знать по конвенции портала: оно же стоит ролью в ACL публичных ресурсов.
|
|
12266
|
+
*/
|
|
12267
|
+
const PUBLIC_USER_NAME = "public_user";
|
|
12268
|
+
/** Социальные сети для блока «Поделиться». */
|
|
12269
|
+
const VOTE_SOCIALS = ["Telegram", "VK"];
|
|
12270
|
+
|
|
12271
|
+
/** Локальное состояние формы создания/редактирования голосования (категория, вопрос, чипсы-варианты). */
|
|
12272
|
+
const useVoteForm = (initialValues) => {
|
|
12273
|
+
const [categoryId, setCategoryId] = useState(initialValues?.categoryId ?? "");
|
|
12274
|
+
const [text, setText] = useState(initialValues?.text ?? "");
|
|
12275
|
+
const [multiSelect, setMultiSelect] = useState(initialValues?.multiSelect ?? false);
|
|
12276
|
+
const [variants, setVariants] = useState(initialValues?.variants ?? []);
|
|
12277
|
+
const [draft, setDraft] = useState("");
|
|
12278
|
+
const addVariant = useCallback(() => {
|
|
12279
|
+
const value = draft.trim();
|
|
12280
|
+
if (!value)
|
|
12281
|
+
return;
|
|
12282
|
+
setVariants(prev => [...prev, { text: value }]);
|
|
12283
|
+
setDraft("");
|
|
12284
|
+
}, [draft]);
|
|
12285
|
+
const removeVariant = useCallback((index) => {
|
|
12286
|
+
setVariants(prev => prev.filter((_, itemIndex) => itemIndex !== index));
|
|
12287
|
+
}, []);
|
|
12288
|
+
const isValid = useMemo(() => Boolean(categoryId && text.trim() && variants.length >= MIN_VARIANTS), [categoryId, text, variants.length]);
|
|
12289
|
+
const values = useMemo(() => ({ categoryId, text: text.trim(), multiSelect, variants }), [categoryId, text, multiSelect, variants]);
|
|
12290
|
+
return {
|
|
12291
|
+
categoryId,
|
|
12292
|
+
setCategoryId,
|
|
12293
|
+
text,
|
|
12294
|
+
setText,
|
|
12295
|
+
multiSelect,
|
|
12296
|
+
setMultiSelect,
|
|
12297
|
+
variants,
|
|
12298
|
+
addVariant,
|
|
12299
|
+
removeVariant,
|
|
12300
|
+
draft,
|
|
12301
|
+
setDraft,
|
|
12302
|
+
isValid,
|
|
12303
|
+
values,
|
|
12304
|
+
};
|
|
12305
|
+
};
|
|
12306
|
+
|
|
12307
|
+
const VoteCreateForm = ({ categories, initialValues, isEditing, canDelete, submitting, onSubmit, onDelete, onCancel, }) => {
|
|
12308
|
+
const { t } = useGlobalContext();
|
|
12309
|
+
const form = useVoteForm(initialValues);
|
|
12310
|
+
const { categoryId, setCategoryId, text, setText, multiSelect, setMultiSelect, variants, addVariant, removeVariant, draft, setDraft, isValid, values, } = form;
|
|
12311
|
+
const categoryOptions = useMemo(() => categories.map(({ id, name }) => ({ text: name, value: id })), [categories]);
|
|
12312
|
+
return (jsxs(VoteWrapper, { children: [!isEditing && jsx(VoteHeading, { children: t("vote.newVote", { ns: VOTE_NS, defaultValue: "Новое голосование" }) }), jsx(Dropdown, { zIndex: 110000, label: t("vote.category", { ns: VOTE_NS, defaultValue: "Категория" }), width: "100%", placeholder: t("vote.categoryPlaceholder", { ns: VOTE_NS, defaultValue: "Выберите категорию" }), options: categoryOptions, value: categoryId, onChange: items => setCategoryId(String(items[0]?.value ?? "")) }), jsx(Input, { label: t("vote.question", { ns: VOTE_NS, defaultValue: "Вопрос" }), width: "100%", placeholder: t("vote.questionPlaceholder", { ns: VOTE_NS, defaultValue: "Текст вопроса" }), value: text, onChange: ({ target }) => setText(target.value) }), jsx(VoteSectionLabel, { children: t("vote.variants", { ns: VOTE_NS, defaultValue: "Варианты ответов" }) }), !!variants.length && (jsx(VoteChipList, { children: variants.map(({ text: variantText }, index) => (jsxs(VoteChip, { children: [jsx(VoteVariantText, { children: variantText }), jsx(IconButton, { kind: "close", onClick: () => removeVariant(index) })] }, `${variantText}:${index}`))) })), jsx(Input, { width: "100%", placeholder: t("vote.variantPlaceholder", { ns: VOTE_NS, defaultValue: "Введите вариант и нажмите Enter" }), value: draft, onChange: ({ target }) => setDraft(target.value), onKeyDown: event => {
|
|
12313
|
+
if (event.key === "Enter") {
|
|
12314
|
+
event.preventDefault();
|
|
12315
|
+
addVariant();
|
|
12316
|
+
}
|
|
12317
|
+
} }), jsxs(Flex, { alignItems: "center", justifyContent: "space-between", children: [jsx(VoteSectionLabel, { children: t("vote.multiSelect", { ns: VOTE_NS, defaultValue: "Множественный выбор" }) }), jsx(Switch, { checked: multiSelect, onChange: () => setMultiSelect(prev => !prev) })] }), jsx(RaisedButton, { primary: true, disabled: !isValid || submitting, onClick: () => onSubmit(values), children: isEditing
|
|
12318
|
+
? t("vote.save", { ns: VOTE_NS, defaultValue: "Сохранить" })
|
|
12319
|
+
: t("vote.create", { ns: VOTE_NS, defaultValue: "Создать голосование" }) }), isEditing && (jsxs(Flex, { alignItems: "center", justifyContent: "space-between", children: [onCancel && (jsx(VoteEditLink, { onClick: onCancel, children: t("vote.cancel", { ns: VOTE_NS, defaultValue: "Отмена" }) })), canDelete && onDelete && (jsx(RaisedButton, { error: true, disabled: submitting, onClick: onDelete, children: t("vote.delete", { ns: VOTE_NS, defaultValue: "Удалить голосование" }) }))] }))] }));
|
|
12320
|
+
};
|
|
12321
|
+
|
|
12322
|
+
/** Блок «Поделиться в соцсетях» — пока только вёрстка, без рабочих ссылок. */
|
|
12323
|
+
const VoteShare = () => {
|
|
12324
|
+
const { t } = useGlobalContext();
|
|
12325
|
+
return (jsxs(VoteShareBlock, { children: [jsx(VoteSectionLabel, { children: t("vote.share", { ns: VOTE_NS, defaultValue: "Поделиться в соцсетях" }) }), jsx(VoteShareChips, { children: VOTE_SOCIALS.map(name => (jsx(VoteShareChip, { children: name }, name))) })] }));
|
|
12326
|
+
};
|
|
12327
|
+
|
|
12328
|
+
const VoteResults = ({ question, category, results, screen, isOwner, hasAnswers, submitting, onSubmit, onEdit, }) => {
|
|
12329
|
+
const { t } = useGlobalContext();
|
|
12330
|
+
const [selected, setSelected] = useState([]);
|
|
12331
|
+
const showControls = screen === "voting";
|
|
12332
|
+
const showShare = screen === "voting" || screen === "voted";
|
|
12333
|
+
const canEdit = screen === "voting" && isOwner && !hasAnswers;
|
|
12334
|
+
const toggle = useCallback((variantId) => {
|
|
12335
|
+
setSelected(prev => {
|
|
12336
|
+
if (!question.multiSelect)
|
|
12337
|
+
return [variantId];
|
|
12338
|
+
return prev.includes(variantId) ? prev.filter(id => id !== variantId) : [...prev, variantId];
|
|
12339
|
+
});
|
|
12340
|
+
}, [question.multiSelect]);
|
|
12341
|
+
const canSubmit = useMemo(() => selected.length > 0 && !submitting, [selected.length, submitting]);
|
|
12342
|
+
return (jsxs(VoteWrapper, { children: [category && jsx(VoteCategoryLabel, { children: category.name }), jsx(VoteQuestionText, { children: question.text }), jsx(VoteVariantList, { column: true, value: selected[0] ?? "", children: results.map(({ id, text, count, percent }) => (jsxs(VoteVariantRow, { children: [jsxs(VoteVariantHead, { children: [showControls &&
|
|
12343
|
+
(question.multiSelect ? (jsx(Checkbox, { checked: selected.includes(id), onChange: () => toggle(id) })) : (jsx(Radio, { value: id, checked: selected.includes(id), onChange: () => toggle(id) }))), jsx(VoteVariantText, { children: text }), jsx(VoteVariantCount, { children: count })] }), jsx(LinearProgress, { done: percent })] }, id))) }), screen === "unauthenticated" && (jsx(VoteAuthHint, { children: t("vote.authHint", { ns: VOTE_NS, defaultValue: "Чтобы проголосовать, пожалуйста, авторизуйтесь" }) })), screen === "voted" && (jsxs(VoteAcceptedBadge, { children: [jsx(Icon, { kind: "success" }), t("vote.accepted", { ns: VOTE_NS, defaultValue: "Ваш ответ принят" })] })), showControls && (jsx(RaisedButton, { primary: true, disabled: !canSubmit, onClick: () => onSubmit(selected), children: t("vote.submit", { ns: VOTE_NS, defaultValue: "Отправить ответ" }) })), canEdit && onEdit && (jsx(Flex, { children: jsx(VoteEditLink, { onClick: onEdit, children: t("vote.edit", { ns: VOTE_NS, defaultValue: "Редактировать" }) }) })), showShare && jsx(VoteShare, {})] }));
|
|
12344
|
+
};
|
|
12345
|
+
|
|
12346
|
+
/**
|
|
12347
|
+
* Тело контейнера: выбирает экран по вью-модели голосования.
|
|
12348
|
+
*
|
|
12349
|
+
* Живёт отдельно от `VoteContainer`, потому что тот отвечает за корень контейнера — размеры,
|
|
12350
|
+
* фон и заголовок — и не должен раздуваться ветвлением по состояниям.
|
|
12351
|
+
*/
|
|
12352
|
+
const VoteScreen = ({ screen, categories, question, category, results, totalVotes, isOwner, hasAnswers, isEditing, setIsEditing, editInitialValues, submitting, createVote, submitAnswer, editVote, deleteVote, }) => {
|
|
12353
|
+
const handleCreate = useCallback((values) => void createVote(values), [createVote]);
|
|
12354
|
+
const handleEdit = useCallback((values) => void editVote(values).then(() => setIsEditing(false)), [editVote, setIsEditing]);
|
|
12355
|
+
const handleDelete = useCallback(() => void deleteVote().then(() => setIsEditing(false)), [deleteVote, setIsEditing]);
|
|
12356
|
+
const handleCancel = useCallback(() => setIsEditing(false), [setIsEditing]);
|
|
12357
|
+
const handleEditOpen = useCallback(() => setIsEditing(true), [setIsEditing]);
|
|
12358
|
+
const handleSubmitAnswer = useCallback((variantIds) => void submitAnswer(variantIds), [submitAnswer]);
|
|
12359
|
+
if (screen === "loading")
|
|
12360
|
+
return jsx(ContainerLoading, {});
|
|
12361
|
+
if (isEditing && question) {
|
|
12362
|
+
return (jsx(VoteCreateForm, { categories: categories, initialValues: editInitialValues, isEditing: true, canDelete: !hasAnswers, submitting: submitting, onSubmit: handleEdit, onDelete: handleDelete, onCancel: handleCancel }));
|
|
12363
|
+
}
|
|
12364
|
+
if (screen === "create") {
|
|
12365
|
+
return (jsx(VoteCreateForm, { categories: categories, isEditing: false, canDelete: false, submitting: submitting, onSubmit: handleCreate }));
|
|
12366
|
+
}
|
|
12367
|
+
if (!question)
|
|
12368
|
+
return null;
|
|
12369
|
+
return (jsx(VoteResults, { question: question, category: category, results: results, totalVotes: totalVotes, screen: screen, isOwner: isOwner, hasAnswers: hasAnswers, submitting: submitting, onSubmit: handleSubmitAnswer, onEdit: handleEditOpen }));
|
|
12370
|
+
};
|
|
12371
|
+
|
|
12372
|
+
/** Приводит значение атрибута к строке-идентификатору (id приходят числами/строками). */
|
|
12373
|
+
const toId = (value) => (value === null || value === undefined ? "" : String(value));
|
|
12374
|
+
/**
|
|
12375
|
+
* Условие равенства по числовому полю для `conditions` слоя: `field == value`.
|
|
12376
|
+
*
|
|
12377
|
+
* Двойное равенство — форма, которой написаны условия во всех конфигах проекта; лексер языка
|
|
12378
|
+
* условий принимает и одиночное `=`, но расходиться с авторскими конфигами незачем.
|
|
12379
|
+
*/
|
|
12380
|
+
const eqCondition = (field, value) => `${field} == ${value}`;
|
|
12381
|
+
const props = (feature) => feature.properties ?? {};
|
|
12382
|
+
const mapCategories = (features) => features.map(feature => ({
|
|
12383
|
+
id: toId(props(feature)[VOTE_FIELDS.category.id] ?? feature.id),
|
|
12384
|
+
name: toId(props(feature)[VOTE_FIELDS.category.name]),
|
|
12385
|
+
}));
|
|
12386
|
+
const mapQuestion = (feature) => {
|
|
12387
|
+
const featureProps = props(feature);
|
|
12388
|
+
return {
|
|
12389
|
+
id: toId(featureProps[VOTE_FIELDS.question.id] ?? feature.id),
|
|
12390
|
+
text: toId(featureProps[VOTE_FIELDS.question.text]),
|
|
12391
|
+
categoryId: toId(featureProps[VOTE_FIELDS.question.categoryId]),
|
|
12392
|
+
userName: toId(featureProps[VOTE_FIELDS.question.userName]),
|
|
12393
|
+
multiSelect: Boolean(featureProps[VOTE_FIELDS.question.multiSelect]),
|
|
12394
|
+
};
|
|
12395
|
+
};
|
|
12396
|
+
const mapVariants = (features) => features.map(feature => ({
|
|
12397
|
+
id: toId(props(feature)[VOTE_FIELDS.variant.id] ?? feature.id),
|
|
12398
|
+
text: toId(props(feature)[VOTE_FIELDS.variant.text]),
|
|
12399
|
+
}));
|
|
12400
|
+
/** Список `variant_id` из ответов конкретного пользователя. */
|
|
12401
|
+
const getUserVariantIds = (answers, userName) => answers
|
|
12402
|
+
.filter(answer => toId(props(answer)[VOTE_FIELDS.answer.userName]) === userName)
|
|
12403
|
+
.map(answer => toId(props(answer)[VOTE_FIELDS.answer.variantId]));
|
|
12404
|
+
/** Id записей-ответов конкретного пользователя (для замены ответа при переголосовании). */
|
|
12405
|
+
const getUserAnswerIds = (answers, userName) => answers
|
|
12406
|
+
.filter(answer => toId(props(answer)[VOTE_FIELDS.answer.userName]) === userName)
|
|
12407
|
+
.map(answer => toId(answer.id))
|
|
12408
|
+
.filter(Boolean);
|
|
12409
|
+
/**
|
|
12410
|
+
* Считает голоса по вариантам. `percent` — доля голосов варианта от общего числа голосов
|
|
12411
|
+
* по голосованию.
|
|
12412
|
+
*/
|
|
12413
|
+
const computeResults = (variants, answers) => {
|
|
12414
|
+
const counts = new Map();
|
|
12415
|
+
answers.forEach(answer => {
|
|
12416
|
+
const variantId = toId(props(answer)[VOTE_FIELDS.answer.variantId]);
|
|
12417
|
+
counts.set(variantId, (counts.get(variantId) ?? 0) + 1);
|
|
12418
|
+
});
|
|
12419
|
+
const totalVotes = answers.length;
|
|
12420
|
+
const results = variants.map(variant => {
|
|
12421
|
+
const count = counts.get(variant.id) ?? 0;
|
|
12422
|
+
return {
|
|
12423
|
+
...variant,
|
|
12424
|
+
count,
|
|
12425
|
+
percent: totalVotes ? Math.round((count / totalVotes) * PERCENT_BASE) : 0,
|
|
12426
|
+
};
|
|
12427
|
+
});
|
|
12428
|
+
return { results, totalVotes };
|
|
12429
|
+
};
|
|
12430
|
+
/**
|
|
12431
|
+
* Сравнивает варианты из формы с существующими в БД и раскладывает на наборы операций:
|
|
12432
|
+
* создать (новые без id), обновить (изменился текст) и удалить (исчезли из формы).
|
|
12433
|
+
*/
|
|
12434
|
+
const diffVariants = (existing, next) => {
|
|
12435
|
+
const nextIds = new Set(next.filter(({ id }) => id).map(({ id }) => id));
|
|
12436
|
+
const existingById = new Map(existing.map(variant => [variant.id, variant]));
|
|
12437
|
+
const toCreate = next.filter(({ id, text }) => !id && text.trim()).map(({ text }) => text.trim());
|
|
12438
|
+
const toUpdate = next
|
|
12439
|
+
.filter(({ id, text }) => id && existingById.get(id) && existingById.get(id)?.text !== text.trim())
|
|
12440
|
+
.map(({ id, text }) => ({ id: id, text: text.trim() }));
|
|
12441
|
+
const toDeleteIds = existing.filter(({ id }) => !nextIds.has(id)).map(({ id }) => id);
|
|
12442
|
+
return { toCreate, toUpdate, toDeleteIds };
|
|
12443
|
+
};
|
|
12444
|
+
|
|
12445
|
+
/**
|
|
12446
|
+
* Операции записи контейнера: создание голосования, отправка ответа, редактирование и удаление.
|
|
12447
|
+
* Все операции доступны только авторизованному пользователю.
|
|
12448
|
+
*/
|
|
12449
|
+
const useVoteActions = ({ dataSources, attributeName, featureId, featureLayerName, username, question, variants, answers, onQuestionIdChange, reload, }) => {
|
|
12450
|
+
const { api } = useGlobalContext();
|
|
12451
|
+
const { questionDataSource, variantDataSource, answerDataSource } = dataSources;
|
|
12452
|
+
const [submitting, setSubmitting] = useState(false);
|
|
12453
|
+
const writeFeatureAttribute = useCallback(async (value) => {
|
|
12454
|
+
if (!api || !featureLayerName || !featureId || !attributeName)
|
|
12455
|
+
return;
|
|
12456
|
+
await api.layers.updateFeature(featureLayerName, [{ id: featureId, attributes: { [attributeName]: value } }]);
|
|
12457
|
+
}, [api, featureLayerName, featureId, attributeName]);
|
|
12458
|
+
const createVote = useCallback(async (values) => {
|
|
12459
|
+
if (!api)
|
|
12460
|
+
return;
|
|
12461
|
+
setSubmitting(true);
|
|
12462
|
+
try {
|
|
12463
|
+
const questionResult = await api.layers.createFeatures({ name: questionDataSource }, [
|
|
12464
|
+
{
|
|
12465
|
+
properties: {
|
|
12466
|
+
[VOTE_FIELDS.question.text]: values.text,
|
|
12467
|
+
[VOTE_FIELDS.question.categoryId]: values.categoryId,
|
|
12468
|
+
[VOTE_FIELDS.question.userName]: username,
|
|
12469
|
+
[VOTE_FIELDS.question.multiSelect]: values.multiSelect,
|
|
12470
|
+
},
|
|
12471
|
+
},
|
|
12472
|
+
]);
|
|
12473
|
+
const newQuestionId = questionResult.createdIds?.[0];
|
|
12474
|
+
if (!newQuestionId)
|
|
12475
|
+
return;
|
|
12476
|
+
await api.layers.createFeatures({ name: variantDataSource }, values.variants
|
|
12477
|
+
.filter(({ text }) => text.trim())
|
|
12478
|
+
.map(({ text }) => ({
|
|
12479
|
+
properties: {
|
|
12480
|
+
[VOTE_FIELDS.variant.questionId]: newQuestionId,
|
|
12481
|
+
[VOTE_FIELDS.variant.text]: text.trim(),
|
|
12482
|
+
},
|
|
12483
|
+
})));
|
|
12484
|
+
await writeFeatureAttribute(newQuestionId);
|
|
12485
|
+
onQuestionIdChange(newQuestionId);
|
|
12486
|
+
reload();
|
|
12487
|
+
}
|
|
12488
|
+
finally {
|
|
12489
|
+
setSubmitting(false);
|
|
12490
|
+
}
|
|
12491
|
+
}, [api, questionDataSource, variantDataSource, username, writeFeatureAttribute, onQuestionIdChange, reload]);
|
|
12492
|
+
const submitAnswer = useCallback(async (variantIds) => {
|
|
12493
|
+
if (!api || !question || !variantIds.length)
|
|
12494
|
+
return;
|
|
12495
|
+
setSubmitting(true);
|
|
12496
|
+
try {
|
|
12497
|
+
// Заложено на будущее (переголосование без UI): сначала снимаем прежние ответы пользователя.
|
|
12498
|
+
const previousAnswerIds = getUserAnswerIds(answers, username);
|
|
12499
|
+
if (previousAnswerIds.length) {
|
|
12500
|
+
await api.layers.deleteFeatures({ name: answerDataSource, ids: previousAnswerIds });
|
|
12501
|
+
}
|
|
12502
|
+
await api.layers.createFeatures({ name: answerDataSource }, variantIds.map(variantId => ({
|
|
12503
|
+
properties: {
|
|
12504
|
+
[VOTE_FIELDS.answer.questionId]: question.id,
|
|
12505
|
+
[VOTE_FIELDS.answer.userName]: username,
|
|
12506
|
+
[VOTE_FIELDS.answer.variantId]: variantId,
|
|
12507
|
+
},
|
|
12508
|
+
})));
|
|
12509
|
+
reload();
|
|
12510
|
+
}
|
|
12511
|
+
finally {
|
|
12512
|
+
setSubmitting(false);
|
|
12513
|
+
}
|
|
12514
|
+
}, [api, question, answers, username, answerDataSource, reload]);
|
|
12515
|
+
const editVote = useCallback(async (values) => {
|
|
12516
|
+
if (!api || !question)
|
|
12517
|
+
return;
|
|
12518
|
+
setSubmitting(true);
|
|
12519
|
+
try {
|
|
12520
|
+
await api.layers.updateFeature(questionDataSource, [
|
|
12521
|
+
{
|
|
12522
|
+
id: question.id,
|
|
12523
|
+
attributes: {
|
|
12524
|
+
[VOTE_FIELDS.question.text]: values.text,
|
|
12525
|
+
[VOTE_FIELDS.question.categoryId]: values.categoryId,
|
|
12526
|
+
[VOTE_FIELDS.question.multiSelect]: values.multiSelect,
|
|
12527
|
+
},
|
|
12528
|
+
},
|
|
12529
|
+
]);
|
|
12530
|
+
const { toCreate, toUpdate, toDeleteIds } = diffVariants(variants, values.variants);
|
|
12531
|
+
if (toCreate.length) {
|
|
12532
|
+
await api.layers.createFeatures({ name: variantDataSource }, toCreate.map(text => ({
|
|
12533
|
+
properties: { [VOTE_FIELDS.variant.questionId]: question.id, [VOTE_FIELDS.variant.text]: text },
|
|
12534
|
+
})));
|
|
12535
|
+
}
|
|
12536
|
+
if (toUpdate.length) {
|
|
12537
|
+
await api.layers.updateFeature(variantDataSource, toUpdate.map(({ id, text }) => ({ id, attributes: { [VOTE_FIELDS.variant.text]: text } })));
|
|
12538
|
+
}
|
|
12539
|
+
if (toDeleteIds.length) {
|
|
12540
|
+
await api.layers.deleteFeatures({ name: variantDataSource, ids: toDeleteIds });
|
|
12541
|
+
}
|
|
12542
|
+
reload();
|
|
12543
|
+
}
|
|
12544
|
+
finally {
|
|
12545
|
+
setSubmitting(false);
|
|
12546
|
+
}
|
|
12547
|
+
}, [api, question, questionDataSource, variantDataSource, variants, reload]);
|
|
12548
|
+
const deleteVote = useCallback(async () => {
|
|
12549
|
+
if (!api || !question)
|
|
12550
|
+
return;
|
|
12551
|
+
setSubmitting(true);
|
|
12552
|
+
try {
|
|
12553
|
+
const variantIds = variants.map(({ id }) => id).filter(Boolean);
|
|
12554
|
+
if (variantIds.length) {
|
|
12555
|
+
await api.layers.deleteFeatures({ name: variantDataSource, ids: variantIds });
|
|
12556
|
+
}
|
|
12557
|
+
await api.layers.deleteFeature({ name: questionDataSource, id: question.id });
|
|
12558
|
+
await writeFeatureAttribute(null);
|
|
12559
|
+
onQuestionIdChange("");
|
|
12560
|
+
reload();
|
|
12561
|
+
}
|
|
12562
|
+
finally {
|
|
12563
|
+
setSubmitting(false);
|
|
12564
|
+
}
|
|
12565
|
+
}, [api, question, variants, variantDataSource, questionDataSource, writeFeatureAttribute, onQuestionIdChange, reload]);
|
|
12566
|
+
return { submitting, createVote, submitAnswer, editVote, deleteVote };
|
|
12567
|
+
};
|
|
12568
|
+
|
|
12569
|
+
/**
|
|
12570
|
+
* Загружает справочник категорий, а по `questionId` — само голосование, его варианты и ответы.
|
|
12571
|
+
* Перечитывается при смене `questionId` или `reloadToken` (после создания/голосования/правки).
|
|
12572
|
+
*/
|
|
12573
|
+
const useVoteData = ({ dataSources, questionId, reloadToken }) => {
|
|
12574
|
+
const { api } = useGlobalContext();
|
|
12575
|
+
const { categoryDataSource, questionDataSource, variantDataSource, answerDataSource } = dataSources;
|
|
12576
|
+
const [categories, setCategories] = useState([]);
|
|
12577
|
+
const [question, setQuestion] = useState(null);
|
|
12578
|
+
const [variants, setVariants] = useState([]);
|
|
12579
|
+
const [answers, setAnswers] = useState([]);
|
|
12580
|
+
const [loading, setLoading] = useState(false);
|
|
12581
|
+
const [error, setError] = useState(false);
|
|
12582
|
+
useEffect(() => {
|
|
12583
|
+
let active = true;
|
|
12584
|
+
if (api && categoryDataSource) {
|
|
12585
|
+
api.layers
|
|
12586
|
+
.getFeatures(categoryDataSource, { withGeom: false, limit: VOTE_FETCH_LIMIT })
|
|
12587
|
+
.then(response => {
|
|
12588
|
+
if (active)
|
|
12589
|
+
setCategories(mapCategories(response.features ?? []));
|
|
12590
|
+
})
|
|
12591
|
+
.catch(() => undefined);
|
|
12592
|
+
}
|
|
12593
|
+
return () => {
|
|
12594
|
+
active = false;
|
|
12595
|
+
};
|
|
12596
|
+
}, [api, categoryDataSource]);
|
|
12597
|
+
useEffect(() => {
|
|
12598
|
+
let active = true;
|
|
12599
|
+
if (!api || !questionId) {
|
|
12600
|
+
setQuestion(null);
|
|
12601
|
+
setVariants([]);
|
|
12602
|
+
setAnswers([]);
|
|
12603
|
+
return () => {
|
|
12604
|
+
active = false;
|
|
12605
|
+
};
|
|
12606
|
+
}
|
|
12607
|
+
setLoading(true);
|
|
12608
|
+
setError(false);
|
|
12609
|
+
Promise.all([
|
|
12610
|
+
api.layers.getFeatures(questionDataSource, {
|
|
12611
|
+
conditions: [eqCondition(VOTE_FIELDS.question.id, questionId)],
|
|
12612
|
+
withGeom: false,
|
|
12613
|
+
limit: 1,
|
|
12614
|
+
}),
|
|
12615
|
+
api.layers.getFeatures(variantDataSource, {
|
|
12616
|
+
conditions: [eqCondition(VOTE_FIELDS.variant.questionId, questionId)],
|
|
12617
|
+
withGeom: false,
|
|
12618
|
+
limit: VOTE_FETCH_LIMIT,
|
|
12619
|
+
}),
|
|
12620
|
+
api.layers.getFeatures(answerDataSource, {
|
|
12621
|
+
conditions: [eqCondition(VOTE_FIELDS.answer.questionId, questionId)],
|
|
12622
|
+
withGeom: false,
|
|
12623
|
+
limit: VOTE_FETCH_LIMIT,
|
|
12624
|
+
}),
|
|
12625
|
+
])
|
|
12626
|
+
.then(([questionResponse, variantResponse, answerResponse]) => {
|
|
12627
|
+
if (!active)
|
|
12628
|
+
return;
|
|
12629
|
+
const questionFeature = questionResponse.features?.[0];
|
|
12630
|
+
setQuestion(questionFeature ? mapQuestion(questionFeature) : null);
|
|
12631
|
+
setVariants(mapVariants(variantResponse.features ?? []));
|
|
12632
|
+
setAnswers(answerResponse.features ?? []);
|
|
12633
|
+
})
|
|
12634
|
+
.catch(() => {
|
|
12635
|
+
if (active)
|
|
12636
|
+
setError(true);
|
|
12637
|
+
})
|
|
12638
|
+
.finally(() => {
|
|
12639
|
+
if (active)
|
|
12640
|
+
setLoading(false);
|
|
12641
|
+
});
|
|
12642
|
+
return () => {
|
|
12643
|
+
active = false;
|
|
12644
|
+
};
|
|
12645
|
+
}, [api, questionId, reloadToken, questionDataSource, variantDataSource, answerDataSource]);
|
|
12646
|
+
const { results, totalVotes } = useMemo(() => computeResults(variants, answers), [variants, answers]);
|
|
12647
|
+
return { categories, question, variants, answers, results, totalVotes, loading, error };
|
|
12648
|
+
};
|
|
12649
|
+
|
|
12650
|
+
/**
|
|
12651
|
+
* Текущий пользователь для контейнера голосования. Авторизация обязательна — неавторизованный
|
|
12652
|
+
* пользователь видит только результаты. Источник — `api.account.getUserInfo()`; ошибка/отсутствие
|
|
12653
|
+
* `username` трактуются как «не авторизован».
|
|
12654
|
+
*
|
|
12655
|
+
* Публичный аккаунт портала (`PUBLIC_USER_NAME` в `username` или в ролях) авторизацией не
|
|
12656
|
+
* считается: под ним ходят ВСЕ анонимные посетители шаренного проекта, поэтому и «мой голос»,
|
|
12657
|
+
* и владение голосованием опознались бы у них общими на всех.
|
|
12658
|
+
*/
|
|
12659
|
+
const useVoteUser = () => {
|
|
12660
|
+
const { api } = useGlobalContext();
|
|
12661
|
+
const [username, setUsername] = useState("");
|
|
12662
|
+
const [roles, setRoles] = useState([]);
|
|
12663
|
+
const [loading, setLoading] = useState(true);
|
|
12664
|
+
useEffect(() => {
|
|
12665
|
+
let active = true;
|
|
12666
|
+
if (!api) {
|
|
12667
|
+
setLoading(false);
|
|
12668
|
+
return () => {
|
|
12669
|
+
active = false;
|
|
12670
|
+
};
|
|
12671
|
+
}
|
|
12672
|
+
api.account
|
|
12673
|
+
.getUserInfo()
|
|
12674
|
+
.then(info => {
|
|
12675
|
+
if (!active)
|
|
12676
|
+
return;
|
|
12677
|
+
setUsername(info?.username ?? "");
|
|
12678
|
+
setRoles(info?.roles ?? []);
|
|
12679
|
+
})
|
|
12680
|
+
.catch(() => {
|
|
12681
|
+
if (!active)
|
|
12682
|
+
return;
|
|
12683
|
+
setUsername("");
|
|
12684
|
+
setRoles([]);
|
|
12685
|
+
})
|
|
12686
|
+
.finally(() => {
|
|
12687
|
+
if (active)
|
|
12688
|
+
setLoading(false);
|
|
12689
|
+
});
|
|
12690
|
+
return () => {
|
|
12691
|
+
active = false;
|
|
12692
|
+
};
|
|
12693
|
+
}, [api]);
|
|
12694
|
+
const isPublic = useMemo(() => username === PUBLIC_USER_NAME || roles.includes(PUBLIC_USER_NAME), [username, roles]);
|
|
12695
|
+
const isAuthenticated = !!username && !isPublic;
|
|
12696
|
+
return { username, isAuthenticated, loading };
|
|
12697
|
+
};
|
|
12698
|
+
|
|
12699
|
+
const normalizeAttributeName = (attributeName) => {
|
|
12700
|
+
if (Array.isArray(attributeName))
|
|
12701
|
+
return attributeName[0] ?? "";
|
|
12702
|
+
return attributeName ?? "";
|
|
12703
|
+
};
|
|
12704
|
+
/**
|
|
12705
|
+
* Вью-модель контейнера голосования: определяет текущий экран и собирает данные/действия.
|
|
12706
|
+
* Привязка к объекту карты — через `elementConfig.attributeName` (хранит `question_id`).
|
|
12707
|
+
*/
|
|
12708
|
+
const useVoteContainer = (elementConfig) => {
|
|
12709
|
+
const { feature, layerInfo } = useWidgetContext(WidgetType.FeatureCard);
|
|
12710
|
+
const options = elementConfig?.options;
|
|
12711
|
+
const attributeName = normalizeAttributeName(elementConfig?.attributeName);
|
|
12712
|
+
const dataSources = useMemo(() => ({
|
|
12713
|
+
categoryDataSource: options?.categoryDataSource ?? "",
|
|
12714
|
+
questionDataSource: options?.questionDataSource ?? "",
|
|
12715
|
+
variantDataSource: options?.variantDataSource ?? "",
|
|
12716
|
+
answerDataSource: options?.answerDataSource ?? "",
|
|
12717
|
+
}), [options]);
|
|
12718
|
+
const configured = useMemo(() => Boolean(attributeName &&
|
|
12719
|
+
dataSources.categoryDataSource &&
|
|
12720
|
+
dataSources.questionDataSource &&
|
|
12721
|
+
dataSources.variantDataSource &&
|
|
12722
|
+
dataSources.answerDataSource), [attributeName, dataSources]);
|
|
12723
|
+
const { username, isAuthenticated, loading: userLoading } = useVoteUser();
|
|
12724
|
+
const featureId = feature?.id;
|
|
12725
|
+
const featureQuestionId = toId(feature?.properties?.[attributeName]);
|
|
12726
|
+
const [overrideQuestionId, setOverrideQuestionId] = useState(null);
|
|
12727
|
+
useEffect(() => {
|
|
12728
|
+
setOverrideQuestionId(null);
|
|
12729
|
+
}, [featureId]);
|
|
12730
|
+
const questionId = overrideQuestionId ?? featureQuestionId;
|
|
12731
|
+
const [reloadToken, setReloadToken] = useState(0);
|
|
12732
|
+
const reload = useCallback(() => setReloadToken(token => token + 1), []);
|
|
12733
|
+
const onQuestionIdChange = useCallback((id) => setOverrideQuestionId(id), []);
|
|
12734
|
+
const { categories, question, variants, answers, results, totalVotes, loading } = useVoteData({
|
|
12735
|
+
dataSources,
|
|
12736
|
+
questionId,
|
|
12737
|
+
reloadToken,
|
|
12738
|
+
});
|
|
12739
|
+
const [isEditing, setIsEditing] = useState(false);
|
|
12740
|
+
const actions = useVoteActions({
|
|
12741
|
+
dataSources,
|
|
12742
|
+
attributeName,
|
|
12743
|
+
featureId,
|
|
12744
|
+
featureLayerName: layerInfo?.name,
|
|
12745
|
+
username,
|
|
12746
|
+
question,
|
|
12747
|
+
variants,
|
|
12748
|
+
answers,
|
|
12749
|
+
onQuestionIdChange,
|
|
12750
|
+
reload,
|
|
12751
|
+
});
|
|
12752
|
+
const hasAnswers = answers.length > 0;
|
|
12753
|
+
const isOwner = isAuthenticated && !!question && question.userName === username;
|
|
12754
|
+
const hasVoted = useMemo(() => getUserVariantIds(answers, username).length > 0, [answers, username]);
|
|
12755
|
+
const category = useMemo(() => categories.find(({ id }) => id === question?.categoryId), [categories, question?.categoryId]);
|
|
12756
|
+
const screen = useMemo(() => {
|
|
12757
|
+
if (!configured)
|
|
12758
|
+
return null;
|
|
12759
|
+
if (userLoading)
|
|
12760
|
+
return "loading";
|
|
12761
|
+
if (!questionId)
|
|
12762
|
+
return isAuthenticated ? "create" : null;
|
|
12763
|
+
if (loading && !question)
|
|
12764
|
+
return "loading";
|
|
12765
|
+
if (!question)
|
|
12766
|
+
return null;
|
|
12767
|
+
if (!isAuthenticated)
|
|
12768
|
+
return "unauthenticated";
|
|
12769
|
+
return hasVoted ? "voted" : "voting";
|
|
12770
|
+
}, [configured, userLoading, questionId, isAuthenticated, loading, question, hasVoted]);
|
|
12771
|
+
const editInitialValues = useMemo(() => question
|
|
12772
|
+
? {
|
|
12773
|
+
categoryId: question.categoryId,
|
|
12774
|
+
text: question.text,
|
|
12775
|
+
multiSelect: question.multiSelect,
|
|
12776
|
+
variants: variants.map(({ id, text }) => ({ id, text })),
|
|
12777
|
+
}
|
|
12778
|
+
: undefined, [question, variants]);
|
|
12779
|
+
return {
|
|
12780
|
+
configured,
|
|
12781
|
+
screen,
|
|
12782
|
+
categories,
|
|
12783
|
+
question,
|
|
12784
|
+
category,
|
|
12785
|
+
results,
|
|
12786
|
+
totalVotes,
|
|
12787
|
+
isOwner,
|
|
12788
|
+
hasAnswers,
|
|
12789
|
+
isEditing,
|
|
12790
|
+
setIsEditing,
|
|
12791
|
+
editInitialValues,
|
|
12792
|
+
...actions,
|
|
12793
|
+
};
|
|
12794
|
+
};
|
|
12795
|
+
|
|
12796
|
+
/**
|
|
12797
|
+
* Контейнер «Голосование»: привязывает опрос к объекту карты через `attributeName`
|
|
12798
|
+
* и показывает один из экранов — создание, голосование, после голосования
|
|
12799
|
+
* или (для неавторизованных) только результаты.
|
|
12800
|
+
*
|
|
12801
|
+
* Корнем владеет сам (`useContainerRoot`), поэтому шаблон записан в `ROOT_OWNING_TEMPLATES`:
|
|
12802
|
+
* размеры, авторский `style`, фон и заголовок живут на одном узле, без внешней обёртки.
|
|
12803
|
+
*/
|
|
12804
|
+
const VoteContainer = memo(({ type, elementConfig, isVisible, renderElement }) => {
|
|
12805
|
+
const { root, body } = useContainerRoot({ elementConfig });
|
|
12806
|
+
const { configured, ...vote } = useVoteContainer(elementConfig);
|
|
12807
|
+
// Без имени атрибута или хотя бы одного источника контейнеру не с чем работать: раньше он
|
|
12808
|
+
// молча возвращал null, и опечатка в конфиге выглядела как «блока просто нет».
|
|
12809
|
+
if (!configured) {
|
|
12810
|
+
return jsx(DataSourceError, { name: elementConfig?.templateName ?? ContainerTemplate.Vote });
|
|
12811
|
+
}
|
|
12812
|
+
// Голосования нет, а создавать его посетитель не вправе — показывать нечего. Пустой заголовок
|
|
12813
|
+
// в карточке в этом случае тоже не нужен, поэтому не рисуем и корень.
|
|
12814
|
+
if (!vote.screen)
|
|
12815
|
+
return null;
|
|
12816
|
+
return (jsxs(ContainerRoot, { ...root, children: [jsx(ContainerBackground, { elementConfig: elementConfig, renderElement: renderElement }), jsx(ExpandableTitle, { elementConfig: elementConfig, type: type, renderElement: renderElement }), isVisible && (jsx(Container, { ...body, isColumn: true, children: jsx(VoteScreen, { ...vote, screen: vote.screen }) }))] }));
|
|
12817
|
+
});
|
|
12818
|
+
|
|
12090
12819
|
const ExpandableTitle = memo(({ elementConfig, type, renderElement }) => {
|
|
12091
12820
|
const { expandedContainers } = useWidgetContext(type);
|
|
12092
12821
|
const { id, options, templateName, children } = elementConfig || {};
|
|
@@ -13417,6 +14146,7 @@ const createContainerComponents = () => ({
|
|
|
13417
14146
|
[ContainerTemplate.EditAttachment]: EditAttachmentContainer,
|
|
13418
14147
|
[ContainerTemplate.Attachment]: AttachmentContainer,
|
|
13419
14148
|
[ContainerTemplate.EditGroup]: EditGroupContainer,
|
|
14149
|
+
[ContainerTemplate.Vote]: VoteContainer,
|
|
13420
14150
|
[ContainerTemplate.ContainersGroup]: ContainersGroupContainer,
|
|
13421
14151
|
[ContainerTemplate.GridRow]: GridRowContainer,
|
|
13422
14152
|
[ContainerTemplate.StructuredData]: StructuredDataContainer,
|
|
@@ -13452,6 +14182,7 @@ const ROOT_OWNING_TEMPLATES = new Set([
|
|
|
13452
14182
|
ContainerTemplate.StructuredData,
|
|
13453
14183
|
ContainerTemplate.Task,
|
|
13454
14184
|
ContainerTemplate.Upload,
|
|
14185
|
+
ContainerTemplate.Vote,
|
|
13455
14186
|
]);
|
|
13456
14187
|
let cachedContainerComponents = null;
|
|
13457
14188
|
const getContainerComponents = () => {
|
|
@@ -18520,5 +19251,5 @@ const DEFAULT_HEATMAP_STYLE = {
|
|
|
18520
19251
|
],
|
|
18521
19252
|
};
|
|
18522
19253
|
|
|
18523
|
-
export { ALIGNMENTS, ALIGN_ITEMS, ATTRIBUTE_ICON_ELEMENT_TYPES, AddButtonRow, AddFeatureButton, AddFeatureContainer, AlertIconContainer, AttachmentContainer, AttributeGalleryContainer, AttributeLabel, BASE_CONTAINER_STYLE, BG_IMAGE_SLOT_ID, BaseMapTheme, CHART_TYPES, COMPACT_FRACTION_DIGITS, CONFIG_PAGES_ID, CONFIG_PAGE_ID, CONTAINERS_GROUP_DEFAULTS, CONTAINER_BODY_ATTRIBUTE, CONTAINER_BODY_FILL_STYLE, CameraContainer, Chart, ChartContainer, ChartLegend, ChartLoading, Container, ContainerBackground, ContainerChildren, ContainerLoading, ContainerRoot, ContainerTemplate, ContainerWrapper, ContainersGroupContainer, DASHBOARD_OVERLAY_Z_INDEX, DEFAULT_ATTRIBUTE_NAME, DEFAULT_BARCHART_RADIUS, DEFAULT_BASE_MAP, DEFAULT_BLUR, DEFAULT_CHART_ANGLE, DEFAULT_CHART_HEIGHT, DEFAULT_CHART_WIDTH, DEFAULT_CIRCLE_RADIUS, DEFAULT_CIRCLE_STROKE_WIDTH, DEFAULT_CIRCLE_STYLE, DEFAULT_COLOR, DEFAULT_DASHBOARD_CONFIG, DEFAULT_DATA_SOURCE_LIMIT, DEFAULT_DROPDOWN_WIDTH, DEFAULT_FILL_EXTRUSION_BASE, DEFAULT_FILL_EXTRUSION_HEIGHT, DEFAULT_FILL_EXTRUSION_STYLE, DEFAULT_FILL_EXTRUSION_VERTICAL_GRADIENT, DEFAULT_FILL_STYLE, DEFAULT_FILTER_PADDING, DEFAULT_GRID_GAP, DEFAULT_HEATMAP_COLOR, DEFAULT_HEATMAP_INTENSITY, DEFAULT_HEATMAP_RADIUS, DEFAULT_HEATMAP_STYLE, DEFAULT_HEATMAP_WEIGHT, DEFAULT_ICON_ANCHOR, DEFAULT_ICON_OVERLAP, DEFAULT_ICON_PADDING, DEFAULT_ICON_ROTATE, DEFAULT_ICON_SIZE, DEFAULT_ID_ATTRIBUTE_NAME, DEFAULT_LAT, DEFAULT_LINE_CAP, DEFAULT_LINE_JOIN, DEFAULT_LINE_STYLE, DEFAULT_LINE_WIDTH, DEFAULT_LNG, DEFAULT_OPACITY, DEFAULT_PAGES_CONFIG, DEFAULT_PIECHART_RADIUS, DEFAULT_SYMBOL_COLOR, DEFAULT_SYMBOL_HALO_COLOR, DEFAULT_SYMBOL_PLACEMENT, DEFAULT_SYMBOL_SPACING, DEFAULT_SYMBOL_STYLE, DEFAULT_TEXT_ANCHOR, DEFAULT_TEXT_JUSTIFY, DEFAULT_TEXT_SIZE, DEFAULT_TEXT_TRANSFORM, DEFAULT_TRACK_FR, DEFAULT_TRANSLATE, DEFAULT_ZOOM, DRAG_THRESHOLD_PX, Dashboard, DashboardCheckbox, DashboardChip, DashboardContent, DashboardContext, DashboardDefaultHeader, DashboardHeader, DashboardLoading, DashboardPlaceholder, DashboardPlaceholderWrap, DashboardProvider, DashboardWrapper, DataSourceContainer, DataSourceError, DataSourceErrorContainer, DataSourceInnerContainer, DataSourceProgressContainer, DateFormat, DefaultAttributesContainer, DefaultHeaderContainer, DefaultHeaderWrapper, DividerContainer, EMPTY_DATA_SOURCE_LAYER_INFO, EXTENT_FILTER_NAME, EditGeometryType, ElementButton, ElementCamera, ElementChart, ElementChips, ElementControl, ElementIcon, ElementImage, ElementLegend, ElementLink, ElementMarkdown, ElementSlideshow, ElementSvg, ElementTable, ElementTooltip, ElementValueWrapper, ExpandableTitle, FEATURE_CARD_DEFAULT_COLORS, FEATURE_CARD_OTHER_COLOR, FILL_SIZE, FILTERED_VALUE_OPACITY, FILTER_PREFIX, FR_PRECISION, FeatureCardBackgroundHeader, FeatureCardButtons, FeatureCardContext, FeatureCardDefaultHeader, FeatureCardHeader, FeatureCardProvider, FeatureCardSlideshowHeader, FeatureCardTitle, FeatureControls, FeatureTitleContainer, FiltersContainer, GEOMETRY_ATTRIBUTE, GEOMETRY_FILTER_NAME, GRID_AUTO_FILL_DEFAULTS, GRID_CELL_ATTR, GRID_CELL_ID_PREFIX, GRID_DRAGGING_ATTR, GRID_DRAG_SOURCE_ATTR, GRID_DROP_TARGET_ATTR, GRID_FILL_DEFAULTS, GRID_HANDLE_ATTR, GRID_HANDLE_PROPS, GRID_ROW_ID_PREFIX, GlobalContext, GlobalProvider, GridRowContainer, HANDLE_BLEED_PX, Header, HeaderContainer, HeaderFontColorMixin, HeaderFrontView, HeaderTemplate, HeaderTitleContainer, HiddenTitleItems, IconContainer, ImageContainer, LEFT_PANEL_HEADER_HEIGHT, Layer, LayerDescription, LayerGroupList, LayerIcon, LayerIconContainer, LayerListContainer, LayerTree, LayersContainer, LayersListWrapper, LinearProgressContainer, LogTerminal, LogoContainer, MAP_VIEW_FILTER_NAMES, MAX_CHART_WIDTH, MAX_TRACKS, MIN_TRACK_PX, MIN_TRACK_RATIO, Map$1 as Map, MapContext, MapProvider, NON_TRACK_SLOT_IDS, NO_CELL_DRAG_SELECTOR, NO_CONTENT_VALUE, NUMERIC_ATTRIBUTE_TYPES, NoLiveSnapshotContainer, OBJECT_FITS, OVERFLOWS, OneColumnContainer, PIE_CHART_TOOLTIP_STYLE, POLL_SUBTASK_INTERVAL_MS, POLL_SUBTASK_TIMEOUT_MS, PROVIDER_PREFIX, PageNavigator, PageTitle, PageTitleContainer, PagesContainer, Pagination, PresentationHeader, PresentationHeaderButtons, PresentationHeaderTools, PresentationPanelContainer, PresentationPanelWrapper, PresentationWrapper, ProgressContainer, ProviderPrefix, QUERY_DESCRIPTION_CACHE_TTL, RoundedBackgroundContainer, SAVE_HOOK_RESULT_DURATION, SERVER_NOTIFICATION_EVENT, STACK_BAR_TOTAL_HEIGHT, ScalingFactor, ServerNotificationsContext, ServerNotificationsProvider, SlideshowContainer, SmallPreviewContainer$1 as SmallPreviewContainer, SmallPreviewControl, SmallPreviewCounter, SmallPreviewImages, SmallPreviewLeft, SmallPreviewRight, StackBar, StructuredDataContainer, SvgImage, TILE_ALIGNMENTS, TIME_ZONE_FORMAT, TITLE_SLOT_IDS, TabsContainer, TextTrim, ThemeName, TitleContainer, TopContainer, TopContainerButtons, TwoColumnContainer, UploadContainer, VIEW_MODES, WidgetType, ZOOM_FILTER_NAME, addDataSource, addDataSources, adjustColor, applyFiltersToCondition, applyQueryFilters, applyTreeFilterToCondition, applyVarsToCondition, asAttributeName, asChartId, asContainerId, asDataSourceName, asFilterName, asLayerName, asModalId, asResourceId, asTabId, buildGridTemplate, buildTrackTemplate, checkEqualOrIncludes, checkIsLoading, collectConfigIds, containsNodeId, createConfigLayer, createConfigPage, createGridCell, createGridIdFactory, createGridRow, createNewPageId, createSaveNotificationId, dateOptions, debounce, decimalOpacityToHex, enrichStyleItemsWithIds, enrichStyleModelsWithIds, eqlParametersToPayload, fetchQueryDescription, findAttributeInExpression, findCellContext, formatArea, formatAttributeValue, formatChartRelatedValue, formatConditionValue, formatDataSourceCondition, formatDate$1 as formatDate, formatElementValue, formatLength, formatNumber, formatPolygonMeasure, geometryToEwkt, getActualExtrusionHeight, getAttributeByName, getAttributeConfigurationByName, getAttributeIconElement, getAttributeIconUrl, getAttributeValue, getAttributesConfiguration, getAverageTrackSize, getChartAxes, getChartFilterName, getChartMarkers, getConfigFilter, getContainerComponent, getDashboardHeader, getDataFromAttributes, getDataFromRelatedFeatures, getDataSource, getDataSourceFilterValue, getDataSourceLayerInfo, getDate, getDefaultConfig, getDisplayTemplateNameFromAttribute, getElementValue, getFeatureAttributes, getFeatureCardHeader, getFilterComponent, getFilterSelectedItems, getFilterValue, getFormattedAttributes, getGradientColors, getImageUrl, getLayerClientStyle, getLayerInfo, getLayerInfoAttribute, getLayerInfoFromDataSources, getLayoutChildren, getMapViewDataSources, getPagesFromConfig, getPagesFromProjectInfo, getProxyService, getRelatedAttribute, getRenderElement, getResourceUrl, getRootElementId, getSelectedFilterValue, getSlideshowImages, getStyleAttributes, getSvgUrl, getTemplateNameFromAttribute, getThemeByName, getTotalFromAttributes, getTotalFromRelatedFeatures, getTrackSize, getTrackSizeKey, getTrackSizes, getWrapperSizeStyle, hasContainerBgImage, hexToRgba, isCrossOriginUrl, isEmptyElementValue, isEmptyValue, isFeaturesFilterValue, isFillSize, isFrSize, isGridNode, isHiddenEmptyValue, isHookActive, isLayerService, isNotValidSelectedTab, isNumeric, isObject, isProxyService, isRootOwningContainer, isTreeFilterValue, isVisibleContainer, mapNodeById, mergeAttributeConfigurations, metersPerPixel, noMarginMixin, numberOptions, parseFrValue, parseIconNames, parseIconNamesFromClientStyle, pieChartTooltipFromAttributes, pieChartTooltipFromRelatedFeatures, pointOptions, removeDataSource, removeTracks, replaceNodesByIds, rgbToHex, roundFr, roundTotalSum, setLayoutChildren, sizeCssMixin, sliceShownOtherItems, stretchPalette, timeOptions, toConditionsArray, toCssSize, toFrSize, toPxNumber, toRenderableValue, tooltipNameFromAttributes, tooltipValueFromAttributes, tooltipValueFromRelatedFeatures, transparentizeColor, updateDataSource, useAfterSave, useAppHeight, useAttachmentDownload, useAttachmentItems, useAttachmentPreviewImages, useAutoCompleteControl, useBeforeSave, useBgImageHost, useChartChange, useChartData, useContainerAttributes, useContainerRoot, useCurrentPageLayers, useCustomFeatureSelect, useDashboardHeader, useDataSourceLoading, useDataSources, useDebouncedCallback, useDiffPage, useEditGroupAttributes, useEqualTileWidth, useExpandableContainers, useExportPdf, useFeatureSaveHooks, useFetchImageWithAuth, useFetchWithAuth, useGetConfigLayer, useGlobalContext, useHeaderRender, useHideIfEmptyDataSource, useIconsFromLayers, useLayerHiddenAttributes, useLayerParams, useMapContext, useMapDraw, useMapImages, useMaxZoomTo, useProjectDashboardInit, usePythonSandbox, usePythonTask, useRedrawLayer, useRelatedDataSourceAttributes, useRemoteTask, useRenderElement, useResizeBox, useSavePrototypeBuilder, useServerNotificationsContext, useShownOtherItems, useToggle, useUpdateDataSource, useVisibleProjectItems, useWidgetConfig, useWidgetContext, useWidgetFilters, useWidgetPage, useWindowResize, useWrapperSize, useZoomToFeatures, useZoomToPoint, withTrackSize };
|
|
19254
|
+
export { ALIGNMENTS, ALIGN_ITEMS, ATTRIBUTE_ICON_ELEMENT_TYPES, AddButtonRow, AddFeatureButton, AddFeatureContainer, AlertIconContainer, AttachmentContainer, AttributeGalleryContainer, AttributeLabel, BASE_CONTAINER_STYLE, BG_IMAGE_SLOT_ID, BaseMapTheme, CHART_TYPES, COMPACT_FRACTION_DIGITS, CONFIG_PAGES_ID, CONFIG_PAGE_ID, CONTAINERS_GROUP_DEFAULTS, CONTAINER_BODY_ATTRIBUTE, CONTAINER_BODY_FILL_STYLE, CameraContainer, Chart, ChartContainer, ChartLegend, ChartLoading, Container, ContainerBackground, ContainerChildren, ContainerLoading, ContainerRoot, ContainerTemplate, ContainerWrapper, ContainersGroupContainer, DASHBOARD_OVERLAY_Z_INDEX, DEFAULT_ATTRIBUTE_NAME, DEFAULT_BARCHART_RADIUS, DEFAULT_BASE_MAP, DEFAULT_BLUR, DEFAULT_CHART_ANGLE, DEFAULT_CHART_HEIGHT, DEFAULT_CHART_WIDTH, DEFAULT_CIRCLE_RADIUS, DEFAULT_CIRCLE_STROKE_WIDTH, DEFAULT_CIRCLE_STYLE, DEFAULT_COLOR, DEFAULT_DASHBOARD_CONFIG, DEFAULT_DATA_SOURCE_LIMIT, DEFAULT_DROPDOWN_WIDTH, DEFAULT_FILL_EXTRUSION_BASE, DEFAULT_FILL_EXTRUSION_HEIGHT, DEFAULT_FILL_EXTRUSION_STYLE, DEFAULT_FILL_EXTRUSION_VERTICAL_GRADIENT, DEFAULT_FILL_STYLE, DEFAULT_FILTER_PADDING, DEFAULT_GRID_GAP, DEFAULT_HEATMAP_COLOR, DEFAULT_HEATMAP_INTENSITY, DEFAULT_HEATMAP_RADIUS, DEFAULT_HEATMAP_STYLE, DEFAULT_HEATMAP_WEIGHT, DEFAULT_ICON_ANCHOR, DEFAULT_ICON_OVERLAP, DEFAULT_ICON_PADDING, DEFAULT_ICON_ROTATE, DEFAULT_ICON_SIZE, DEFAULT_ID_ATTRIBUTE_NAME, DEFAULT_LAT, DEFAULT_LINE_CAP, DEFAULT_LINE_JOIN, DEFAULT_LINE_STYLE, DEFAULT_LINE_WIDTH, DEFAULT_LNG, DEFAULT_OPACITY, DEFAULT_PAGES_CONFIG, DEFAULT_PIECHART_RADIUS, DEFAULT_SYMBOL_COLOR, DEFAULT_SYMBOL_HALO_COLOR, DEFAULT_SYMBOL_PLACEMENT, DEFAULT_SYMBOL_SPACING, DEFAULT_SYMBOL_STYLE, DEFAULT_TEXT_ANCHOR, DEFAULT_TEXT_JUSTIFY, DEFAULT_TEXT_SIZE, DEFAULT_TEXT_TRANSFORM, DEFAULT_TRACK_FR, DEFAULT_TRANSLATE, DEFAULT_ZOOM, DRAG_THRESHOLD_PX, Dashboard, DashboardCheckbox, DashboardChip, DashboardContent, DashboardContext, DashboardDefaultHeader, DashboardHeader, DashboardLoading, DashboardPlaceholder, DashboardPlaceholderWrap, DashboardProvider, DashboardWrapper, DataSourceContainer, DataSourceError, DataSourceErrorContainer, DataSourceInnerContainer, DataSourceProgressContainer, DateFormat, DefaultAttributesContainer, DefaultHeaderContainer, DefaultHeaderWrapper, DividerContainer, EMPTY_DATA_SOURCE_LAYER_INFO, EXTENT_FILTER_NAME, EditGeometryType, ElementButton, ElementCamera, ElementChart, ElementChips, ElementControl, ElementIcon, ElementImage, ElementLegend, ElementLink, ElementMarkdown, ElementSlideshow, ElementSvg, ElementTable, ElementTooltip, ElementValueWrapper, ExpandableTitle, FEATURE_CARD_DEFAULT_COLORS, FEATURE_CARD_OTHER_COLOR, FILL_SIZE, FILTERED_VALUE_OPACITY, FILTER_PREFIX, FR_PRECISION, FeatureCardBackgroundHeader, FeatureCardButtons, FeatureCardContext, FeatureCardDefaultHeader, FeatureCardHeader, FeatureCardProvider, FeatureCardSlideshowHeader, FeatureCardTitle, FeatureControls, FeatureTitleContainer, FiltersContainer, GEOMETRY_ATTRIBUTE, GEOMETRY_FILTER_NAME, GRID_AUTO_FILL_DEFAULTS, GRID_CELL_ATTR, GRID_CELL_ID_PREFIX, GRID_DRAGGING_ATTR, GRID_DRAG_SOURCE_ATTR, GRID_DROP_TARGET_ATTR, GRID_FILL_DEFAULTS, GRID_HANDLE_ATTR, GRID_HANDLE_PROPS, GRID_ROW_ID_PREFIX, GlobalContext, GlobalProvider, GridRowContainer, HANDLE_BLEED_PX, Header, HeaderContainer, HeaderFontColorMixin, HeaderFrontView, HeaderTemplate, HeaderTitleContainer, HiddenTitleItems, IconContainer, ImageContainer, LEFT_PANEL_HEADER_HEIGHT, Layer, LayerDescription, LayerGroupList, LayerIcon, LayerIconContainer, LayerListContainer, LayerTree, LayersContainer, LayersListWrapper, LinearProgressContainer, LogTerminal, LogoContainer, MAP_VIEW_FILTER_NAMES, MAX_CHART_WIDTH, MAX_TRACKS, MIN_TRACK_PX, MIN_TRACK_RATIO, Map$1 as Map, MapContext, MapProvider, NON_TRACK_SLOT_IDS, NO_CELL_DRAG_SELECTOR, NO_CONTENT_VALUE, NUMERIC_ATTRIBUTE_TYPES, NoLiveSnapshotContainer, OBJECT_FITS, OVERFLOWS, OneColumnContainer, PIE_CHART_TOOLTIP_STYLE, POLL_SUBTASK_INTERVAL_MS, POLL_SUBTASK_TIMEOUT_MS, PROVIDER_PREFIX, PageNavigator, PageTitle, PageTitleContainer, PagesContainer, Pagination, PresentationHeader, PresentationHeaderButtons, PresentationHeaderTools, PresentationPanelContainer, PresentationPanelWrapper, PresentationWrapper, ProgressContainer, ProviderPrefix, QUERY_DESCRIPTION_CACHE_TTL, RoundedBackgroundContainer, SAVE_HOOK_RESULT_DURATION, SERVER_NOTIFICATION_EVENT, STACK_BAR_TOTAL_HEIGHT, ScalingFactor, ServerNotificationsContext, ServerNotificationsProvider, SlideshowContainer, SmallPreviewContainer$1 as SmallPreviewContainer, SmallPreviewControl, SmallPreviewCounter, SmallPreviewImages, SmallPreviewLeft, SmallPreviewRight, StackBar, StructuredDataContainer, SvgImage, TILE_ALIGNMENTS, TIME_ZONE_FORMAT, TITLE_SLOT_IDS, TabsContainer, TextTrim, ThemeName, TitleContainer, TopContainer, TopContainerButtons, TwoColumnContainer, UploadContainer, VIEW_MODES, VoteContainer, WidgetType, ZOOM_FILTER_NAME, addDataSource, addDataSources, adjustColor, applyFiltersToCondition, applyQueryFilters, applyTreeFilterToCondition, applyVarsToCondition, asAttributeName, asChartId, asContainerId, asDataSourceName, asFilterName, asLayerName, asModalId, asResourceId, asTabId, buildGridTemplate, buildTrackTemplate, checkEqualOrIncludes, checkIsLoading, collectConfigIds, containsNodeId, createConfigLayer, createConfigPage, createGridCell, createGridIdFactory, createGridRow, createNewPageId, createSaveNotificationId, dateOptions, debounce, decimalOpacityToHex, enrichStyleItemsWithIds, enrichStyleModelsWithIds, eqlParametersToPayload, fetchQueryDescription, findAttributeInExpression, findCellContext, formatArea, formatAttributeValue, formatChartRelatedValue, formatConditionValue, formatDataSourceCondition, formatDate$1 as formatDate, formatElementValue, formatLength, formatNumber, formatPolygonMeasure, geometryToEwkt, getActualExtrusionHeight, getAttributeByName, getAttributeConfigurationByName, getAttributeIconElement, getAttributeIconUrl, getAttributeValue, getAttributesConfiguration, getAverageTrackSize, getChartAxes, getChartFilterName, getChartMarkers, getConfigFilter, getContainerComponent, getDashboardHeader, getDataFromAttributes, getDataFromRelatedFeatures, getDataSource, getDataSourceFilterValue, getDataSourceLayerInfo, getDate, getDefaultConfig, getDisplayTemplateNameFromAttribute, getElementValue, getFeatureAttributes, getFeatureCardHeader, getFilterComponent, getFilterSelectedItems, getFilterValue, getFormattedAttributes, getGradientColors, getImageUrl, getLayerClientStyle, getLayerInfo, getLayerInfoAttribute, getLayerInfoFromDataSources, getLayoutChildren, getMapViewDataSources, getPagesFromConfig, getPagesFromProjectInfo, getProxyService, getRelatedAttribute, getRenderElement, getResourceUrl, getRootElementId, getSelectedFilterValue, getSlideshowImages, getStyleAttributes, getSvgUrl, getTemplateNameFromAttribute, getThemeByName, getTotalFromAttributes, getTotalFromRelatedFeatures, getTrackSize, getTrackSizeKey, getTrackSizes, getWrapperSizeStyle, hasContainerBgImage, hexToRgba, isCrossOriginUrl, isEmptyElementValue, isEmptyValue, isFeaturesFilterValue, isFillSize, isFrSize, isGridNode, isHiddenEmptyValue, isHookActive, isLayerService, isNotValidSelectedTab, isNumeric, isObject, isProxyService, isRootOwningContainer, isTreeFilterValue, isVisibleContainer, mapNodeById, mergeAttributeConfigurations, metersPerPixel, noMarginMixin, numberOptions, parseFrValue, parseIconNames, parseIconNamesFromClientStyle, pieChartTooltipFromAttributes, pieChartTooltipFromRelatedFeatures, pointOptions, removeDataSource, removeTracks, replaceNodesByIds, rgbToHex, roundFr, roundTotalSum, setLayoutChildren, sizeCssMixin, sliceShownOtherItems, stretchPalette, timeOptions, toConditionsArray, toCssSize, toFrSize, toPxNumber, toRenderableValue, tooltipNameFromAttributes, tooltipValueFromAttributes, tooltipValueFromRelatedFeatures, transparentizeColor, updateDataSource, useAfterSave, useAppHeight, useAttachmentDownload, useAttachmentItems, useAttachmentPreviewImages, useAutoCompleteControl, useBeforeSave, useBgImageHost, useChartChange, useChartData, useContainerAttributes, useContainerRoot, useCurrentPageLayers, useCustomFeatureSelect, useDashboardHeader, useDataSourceLoading, useDataSources, useDebouncedCallback, useDiffPage, useEditGroupAttributes, useEqualTileWidth, useExpandableContainers, useExportPdf, useFeatureSaveHooks, useFetchImageWithAuth, useFetchWithAuth, useGetConfigLayer, useGlobalContext, useHeaderRender, useHideIfEmptyDataSource, useIconsFromLayers, useLayerHiddenAttributes, useLayerParams, useMapContext, useMapDraw, useMapImages, useMaxZoomTo, useProjectDashboardInit, usePythonSandbox, usePythonTask, useRedrawLayer, useRelatedDataSourceAttributes, useRemoteTask, useRenderElement, useResizeBox, useSavePrototypeBuilder, useServerNotificationsContext, useShownOtherItems, useToggle, useUpdateDataSource, useVisibleProjectItems, useWidgetConfig, useWidgetContext, useWidgetFilters, useWidgetPage, useWindowResize, useWrapperSize, useZoomToFeatures, useZoomToPoint, withTrackSize };
|
|
18524
19255
|
//# sourceMappingURL=react.esm.js.map
|