@evergis/react 4.0.167 → 4.0.169
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/actions/types.d.ts +2 -0
- package/dist/components/Dashboard/componentTypes.d.ts +3 -4
- package/dist/components/Dashboard/components/ContainerButton/index.d.ts +4 -0
- package/dist/components/Dashboard/containers/VoteContainer/components/VoteScreen.d.ts +2 -2
- package/dist/components/Dashboard/containers/VoteContainer/types.d.ts +10 -3
- package/dist/components/Dashboard/types.d.ts +2 -0
- package/dist/components/Dashboard/utils/getButtonSlot.d.ts +4 -0
- package/dist/dashboardActions/index.js +6 -0
- package/dist/dashboardActions/index.js.map +1 -1
- package/dist/hooks/task/runtime/getPythonTaskConfiguration.d.ts +2 -0
- package/dist/index.js +76 -37
- package/dist/index.js.map +1 -1
- package/dist/react.esm.js +77 -38
- package/dist/react.esm.js.map +1 -1
- package/package.json +2 -2
|
@@ -164,9 +164,8 @@ export interface ElementUploaderProps extends Omit<ContainerProps, "elementConfi
|
|
|
164
164
|
*/
|
|
165
165
|
export type ContainerBoxOptions = Pick<ConfigOptions, "width" | "height" | "overflow">;
|
|
166
166
|
export type AddFeatureContainerOptions = Record<string, never>;
|
|
167
|
-
export type AddFeatureButtonOptions = Pick<ConfigOptions, "
|
|
168
|
-
export interface AddFeatureButtonChild extends Omit<
|
|
169
|
-
type?: "button";
|
|
167
|
+
export type AddFeatureButtonOptions = Pick<ConfigOptions, "layerName" | "geometryType"> & ElementButtonOptions;
|
|
168
|
+
export interface AddFeatureButtonChild extends Omit<ElementButtonConfig, "options"> {
|
|
170
169
|
options?: AddFeatureButtonOptions;
|
|
171
170
|
}
|
|
172
171
|
export interface AddFeatureContainerConfig extends Omit<ConfigContainerChild, "options" | "templateName" | "children"> {
|
|
@@ -356,7 +355,7 @@ export interface EditAttachmentContainerConfig extends Omit<ConfigContainerChild
|
|
|
356
355
|
export interface EditAttachmentContainerProps extends Omit<ContainerProps, "elementConfig"> {
|
|
357
356
|
elementConfig?: EditAttachmentContainerConfig;
|
|
358
357
|
}
|
|
359
|
-
export type ExportPdfContainerOptions =
|
|
358
|
+
export type ExportPdfContainerOptions = Record<string, never>;
|
|
360
359
|
export interface ExportPdfContainerConfig extends Omit<ConfigContainerChild, "options" | "templateName"> {
|
|
361
360
|
templateName?: ContainerTemplate.ExportPdf;
|
|
362
361
|
options?: ExportPdfContainerOptions;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { FC } from 'react';
|
|
2
|
-
import { VoteScreenProps } from '../types';
|
|
2
|
+
import { VoteButtonProps, VoteScreenProps } from '../types';
|
|
3
3
|
/**
|
|
4
4
|
* Тело контейнера: выбирает экран по вью-модели голосования.
|
|
5
5
|
*
|
|
6
6
|
* Живёт отдельно от `VoteContainer`, потому что тот отвечает за корень контейнера — размеры,
|
|
7
7
|
* фон и заголовок — и не должен раздуваться ветвлением по состояниям.
|
|
8
8
|
*/
|
|
9
|
-
export declare const VoteScreen: FC<VoteScreenProps>;
|
|
9
|
+
export declare const VoteScreen: FC<VoteScreenProps & VoteButtonProps>;
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import { VoteContainerOptions } from '../../componentTypes';
|
|
1
|
+
import { ElementButtonConfig, VoteContainerOptions } from '../../componentTypes';
|
|
2
|
+
import { WidgetType } from '../../types';
|
|
3
|
+
export type VoteButtonSlotId = "submitButton" | "createButton" | "saveButton" | "deleteButton";
|
|
4
|
+
export type VoteButtonConfigs = Partial<Record<VoteButtonSlotId, ElementButtonConfig>>;
|
|
5
|
+
export interface VoteButtonProps {
|
|
6
|
+
type?: WidgetType;
|
|
7
|
+
buttonConfigs: VoteButtonConfigs;
|
|
8
|
+
}
|
|
2
9
|
/** Экран, который показывает контейнер голосования. */
|
|
3
10
|
export type VoteScreen = "loading" | "create" | "voting" | "voted" | "unauthenticated";
|
|
4
11
|
export interface VoteCategory {
|
|
@@ -40,7 +47,7 @@ export interface VoteFormVariant {
|
|
|
40
47
|
* к данным отношения не имеют, а `Required<VoteContainerOptions>` потребовал бы и их.
|
|
41
48
|
*/
|
|
42
49
|
export type VoteDataSources = Required<Pick<VoteContainerOptions, "categoryDataSource" | "questionDataSource" | "variantDataSource" | "answerDataSource">>;
|
|
43
|
-
export interface VoteCreateFormProps {
|
|
50
|
+
export interface VoteCreateFormProps extends VoteButtonProps {
|
|
44
51
|
categories: VoteCategory[];
|
|
45
52
|
initialValues?: VoteFormValues;
|
|
46
53
|
isEditing: boolean;
|
|
@@ -50,7 +57,7 @@ export interface VoteCreateFormProps {
|
|
|
50
57
|
onDelete?: () => void;
|
|
51
58
|
onCancel?: () => void;
|
|
52
59
|
}
|
|
53
|
-
export interface VoteResultsProps {
|
|
60
|
+
export interface VoteResultsProps extends VoteButtonProps {
|
|
54
61
|
question: VoteQuestion;
|
|
55
62
|
category?: VoteCategory;
|
|
56
63
|
results: VoteVariantResult[];
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { ElementButtonConfig } from '../componentTypes';
|
|
2
|
+
import { ConfigContainerChild } from '../types';
|
|
3
|
+
/** Слот кнопки существует только при совпадении id и явном type: "button". */
|
|
4
|
+
export declare const getButtonSlot: (children: ConfigContainerChild[] | undefined, id: string) => ElementButtonConfig | undefined;
|
|
@@ -355,6 +355,12 @@ function getActionOptionsError(type, value) {
|
|
|
355
355
|
return "Options must be an object";
|
|
356
356
|
if (type === "runTask" && (typeof value.taskName !== "string" || !value.taskName))
|
|
357
357
|
return "runTask requires taskName";
|
|
358
|
+
if (type === "runTask") {
|
|
359
|
+
for (const field of ["fileName", "methodName"]) {
|
|
360
|
+
if (value[field] !== undefined && (typeof value[field] !== "string" || !value[field].trim()))
|
|
361
|
+
return `runTask ${field} must be a non-empty string`;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
358
364
|
if (type === "openModal" && (typeof value.modalId !== "string" || !value.modalId))
|
|
359
365
|
return "openModal requires modalId";
|
|
360
366
|
if (type === "openUrl" && (typeof value.url !== "string" || !value.url))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { PythonExecutionDependencies } from './types';
|
|
2
|
+
export declare const getPythonTaskConfiguration: (resourceId: string, manager: Pick<PythonExecutionDependencies["manager"], "getTaskResource">, hasExplicitTarget: boolean) => Promise<import('@evergis/api').TaskConfigurationDc>;
|
package/dist/index.js
CHANGED
|
@@ -5154,7 +5154,7 @@ const FeatureControls = styled(uilibGl.Flex).withConfig({ displayName: "FeatureC
|
|
|
5154
5154
|
const FiltersContainerWrapper = styled(uilibGl.Flex).withConfig({ displayName: "FiltersContainerWrapper", componentId: "sc-1a6ru74" }) `
|
|
5155
5155
|
flex-direction: column;
|
|
5156
5156
|
padding: ${({ $padding }) => $padding ?? `${DEFAULT_FILTER_PADDING}px`};
|
|
5157
|
-
background
|
|
5157
|
+
background: ${({ $bgColor, $fontColor, theme: { palette } }) => $bgColor || ($fontColor ? transparentizeColor($fontColor, 6) : palette.element)};
|
|
5158
5158
|
border-radius: ${({ theme: { borderRadius } }) => borderRadius.medium};
|
|
5159
5159
|
|
|
5160
5160
|
${uilibGl.DropdownField}, input {
|
|
@@ -6469,6 +6469,12 @@ function getActionOptionsError(type, value) {
|
|
|
6469
6469
|
return "Options must be an object";
|
|
6470
6470
|
if (type === "runTask" && (typeof value.taskName !== "string" || !value.taskName))
|
|
6471
6471
|
return "runTask requires taskName";
|
|
6472
|
+
if (type === "runTask") {
|
|
6473
|
+
for (const field of ["fileName", "methodName"]) {
|
|
6474
|
+
if (value[field] !== undefined && (typeof value[field] !== "string" || !value[field].trim()))
|
|
6475
|
+
return `runTask ${field} must be a non-empty string`;
|
|
6476
|
+
}
|
|
6477
|
+
}
|
|
6472
6478
|
if (type === "openModal" && (typeof value.modalId !== "string" || !value.modalId))
|
|
6473
6479
|
return "openModal requires modalId";
|
|
6474
6480
|
if (type === "openUrl" && (typeof value.url !== "string" || !value.url))
|
|
@@ -8948,7 +8954,7 @@ const POLL_SUBTASK_INTERVAL_MS = 500;
|
|
|
8948
8954
|
const POLL_SUBTASK_TIMEOUT_MS = 15_000;
|
|
8949
8955
|
|
|
8950
8956
|
const StatusBadge = styled(uilibGl.Chip).withConfig({ displayName: "StatusBadge", componentId: "sc-1xmfl10" }) `
|
|
8951
|
-
background
|
|
8957
|
+
background: ${({ bgColor }) => bgColor};
|
|
8952
8958
|
color: ${({ theme }) => theme.palette.iconContrast};
|
|
8953
8959
|
`;
|
|
8954
8960
|
|
|
@@ -9037,6 +9043,22 @@ class PythonExecutionError extends Error {
|
|
|
9037
9043
|
const RUN_ID_RADIX = 36;
|
|
9038
9044
|
const TASK_OBSERVATION_TIMEOUT_MS = 86_400_000;
|
|
9039
9045
|
|
|
9046
|
+
const BAD_REQUEST_STATUS = 400;
|
|
9047
|
+
const getPythonTaskConfiguration = async (resourceId, manager, hasExplicitTarget) => {
|
|
9048
|
+
try {
|
|
9049
|
+
return await manager.getTaskResource({ id: resourceId });
|
|
9050
|
+
}
|
|
9051
|
+
catch (error) {
|
|
9052
|
+
if (!hasExplicitTarget || !(error instanceof api.HTTPError) || error.response.status !== BAD_REQUEST_STATUS)
|
|
9053
|
+
throw error;
|
|
9054
|
+
// Clone the response so a propagated error retains its body for the caller.
|
|
9055
|
+
const body = await error.response.clone().text().catch(() => "");
|
|
9056
|
+
if (!body.includes("Python importMethod is required"))
|
|
9057
|
+
throw error;
|
|
9058
|
+
return undefined;
|
|
9059
|
+
}
|
|
9060
|
+
};
|
|
9061
|
+
|
|
9040
9062
|
const resolvePythonTaskDefinition = async (request, manager) => {
|
|
9041
9063
|
const task = request.taskName ? request.tasks?.find(({ name }) => name === request.taskName) : undefined;
|
|
9042
9064
|
if (request.taskName && !task)
|
|
@@ -9044,20 +9066,23 @@ const resolvePythonTaskDefinition = async (request, manager) => {
|
|
|
9044
9066
|
const resourceId = task?.resourceId ?? request.resourceId;
|
|
9045
9067
|
if (!resourceId)
|
|
9046
9068
|
throw new Error(`Task resourceId is missing: ${request.taskName ?? ""}`);
|
|
9047
|
-
const
|
|
9048
|
-
|
|
9069
|
+
const fileName = request.fileName ?? task?.fileName;
|
|
9070
|
+
const methodName = request.methodName ?? task?.methodName;
|
|
9071
|
+
const hasExplicitTarget = typeof fileName === "string" && !!fileName.trim() && typeof methodName === "string" && !!methodName.trim();
|
|
9072
|
+
const configuration = await getPythonTaskConfiguration(resourceId, manager, hasExplicitTarget);
|
|
9073
|
+
if (configuration && configuration.taskResourceSubType !== api.TaskResourceSubType.PythonTask && !["pythonService", "PythonTask"].includes(configuration.taskType)) {
|
|
9049
9074
|
throw new Error(`Task ${request.taskName ?? resourceId} is not a Python task`);
|
|
9050
9075
|
}
|
|
9051
|
-
const schema = configuration
|
|
9076
|
+
const schema = configuration?.defaultConfiguration ?? {};
|
|
9052
9077
|
const defaults = buildDefaultForSchema(new SchemaResolver(schema), schema);
|
|
9053
|
-
const parameters = [defaults, configuration
|
|
9078
|
+
const parameters = [defaults, configuration?.userConfiguration, task?.params, request.parameters]
|
|
9054
9079
|
.filter(isObject)
|
|
9055
9080
|
.reduce((base, override) => mergeActionParameters(base, override), {});
|
|
9056
9081
|
return {
|
|
9057
9082
|
resourceId,
|
|
9058
9083
|
parameters,
|
|
9059
|
-
fileName:
|
|
9060
|
-
methodName:
|
|
9084
|
+
fileName: fileName ?? schema.fileName,
|
|
9085
|
+
methodName: methodName ?? schema.methodName,
|
|
9061
9086
|
script: request.script,
|
|
9062
9087
|
};
|
|
9063
9088
|
};
|
|
@@ -11293,7 +11318,7 @@ const FilterChip = styled.div.withConfig({ displayName: "FilterChip", componentI
|
|
|
11293
11318
|
padding: 0.3125rem 0.5rem;
|
|
11294
11319
|
height: 1.5rem;
|
|
11295
11320
|
border-radius: 0.25rem;
|
|
11296
|
-
background
|
|
11321
|
+
background: ${({ $isActive, $bgColor, theme }) => $bgColor
|
|
11297
11322
|
? $bgColor
|
|
11298
11323
|
: $isActive
|
|
11299
11324
|
? theme.palette?.primary
|
|
@@ -11313,7 +11338,7 @@ const FilterChip = styled.div.withConfig({ displayName: "FilterChip", componentI
|
|
|
11313
11338
|
box-sizing: border-box;
|
|
11314
11339
|
|
|
11315
11340
|
&:hover {
|
|
11316
|
-
background
|
|
11341
|
+
background: ${({ $isActive, $bgColor, theme }) => $isActive
|
|
11317
11342
|
? $bgColor || 'inherit'
|
|
11318
11343
|
: $bgColor || theme.palette?.elementDark};
|
|
11319
11344
|
}
|
|
@@ -14558,12 +14583,21 @@ const ContainerBackground = React.memo(({ elementConfig, renderElement }) => {
|
|
|
14558
14583
|
return (jsxRuntime.jsx(ContainerBackgroundLayer, { "$outflow": elementConfig?.options?.outflow, children: renderElement({ id: BG_IMAGE_SLOT_ID, wrap: false }) }));
|
|
14559
14584
|
});
|
|
14560
14585
|
|
|
14561
|
-
|
|
14586
|
+
/** Кнопка нативного действия контейнера со стандартными подстановками дочернего конфига. */
|
|
14587
|
+
const ContainerButton = React.memo(({ type = exports.WidgetType.Dashboard, elementConfig, ...props }) => {
|
|
14588
|
+
const sources = useConfigStringSources(type);
|
|
14589
|
+
const node = useActionNode();
|
|
14590
|
+
const attributes = props.attributes ?? node.attributes ?? sources.attributes;
|
|
14591
|
+
const resolvedConfig = React.useMemo(() => resolveElementStrings(elementConfig, { ...sources, attributes }), [elementConfig, sources, attributes]);
|
|
14592
|
+
return jsxRuntime.jsx(ElementButton, { ...props, type: type, attributes: attributes, elementConfig: resolvedConfig });
|
|
14593
|
+
});
|
|
14594
|
+
|
|
14595
|
+
const AddFeatureContainer = React.memo(({ type, elementConfig, renderElement }) => {
|
|
14562
14596
|
const { id, children, style } = elementConfig || {};
|
|
14563
14597
|
const bgHost = useBgImageHost(elementConfig);
|
|
14564
|
-
|
|
14565
|
-
|
|
14566
|
-
.map((
|
|
14598
|
+
const handleAddFeature = React.useCallback(() => undefined, []);
|
|
14599
|
+
return (jsxRuntime.jsxs(Container, { id: id, style: style, ...bgHost, children: [jsxRuntime.jsx(ContainerBackground, { elementConfig: elementConfig, renderElement: renderElement }), children?.filter(({ type: childType }) => childType === "button")
|
|
14600
|
+
.map((button, index) => (jsxRuntime.jsx(ContainerButton, { type: type, elementConfig: button, onClick: handleAddFeature }, button.id ?? index)))] }));
|
|
14567
14601
|
});
|
|
14568
14602
|
|
|
14569
14603
|
/**
|
|
@@ -16497,7 +16531,7 @@ const RoundedBackgroundContainerWrapper = styled(uilibGl.Flex).withConfig({ disp
|
|
|
16497
16531
|
* иначе фон тонируется цветом иконки, а без неё — цветом текста (colorAttribute/fontColor);
|
|
16498
16532
|
* если не задан ни один цвет, остаётся токен темы.
|
|
16499
16533
|
*/
|
|
16500
|
-
background
|
|
16534
|
+
background: ${({ $bgColor, $iconColor, $color, theme: { palette } }) => $bgColor || tintedBackground($iconColor || $color) || palette.element};
|
|
16501
16535
|
border-radius: ${({ theme: { borderRadius } }) => borderRadius.large};
|
|
16502
16536
|
flex-wrap: nowrap;
|
|
16503
16537
|
|
|
@@ -16568,11 +16602,6 @@ const RoundedBackgroundContainerWrapper = styled(uilibGl.Flex).withConfig({ disp
|
|
|
16568
16602
|
> * {
|
|
16569
16603
|
text-align: left;
|
|
16570
16604
|
}
|
|
16571
|
-
|
|
16572
|
-
/* Зазор наружу даёт обёртка иконки, здесь — отступ внутри её бокса. */
|
|
16573
|
-
span[kind] {
|
|
16574
|
-
margin-right: 0.5rem;
|
|
16575
|
-
}
|
|
16576
16605
|
|
|
16577
16606
|
${ContainerValue} {
|
|
16578
16607
|
width: auto;
|
|
@@ -17286,7 +17315,7 @@ const TabContainer = styled.a.withConfig({ displayName: "TabContainer", componen
|
|
|
17286
17315
|
justify-content: center;
|
|
17287
17316
|
flex-wrap: nowrap;
|
|
17288
17317
|
padding: ${({ onlyIcon }) => (onlyIcon ? 1 : 0.5)}rem 1rem;
|
|
17289
|
-
background
|
|
17318
|
+
background: ${({ active, bgColor, theme: { palette } }) => active ? bgColor || palette.primary : palette.element};
|
|
17290
17319
|
border-radius: ${({ column, radius, theme: { borderRadius } }) => radius ? `${radius}px` : column ? borderRadius.medium : borderRadius.xLarge};
|
|
17291
17320
|
text-decoration: none;
|
|
17292
17321
|
|
|
@@ -17752,22 +17781,22 @@ const useVoteForm = (initialValues) => {
|
|
|
17752
17781
|
};
|
|
17753
17782
|
};
|
|
17754
17783
|
|
|
17755
|
-
const VoteCreateForm = ({ categories, initialValues, isEditing, canDelete, submitting, onSubmit, onDelete, onCancel, }) => {
|
|
17784
|
+
const VoteCreateForm = ({ type, buttonConfigs, categories, initialValues, isEditing, canDelete, submitting, onSubmit, onDelete, onCancel, }) => {
|
|
17756
17785
|
const { t } = useGlobalContext();
|
|
17757
17786
|
const form = useVoteForm(initialValues);
|
|
17758
17787
|
const { categoryId, setCategoryId, text, setText, multiSelect, setMultiSelect, variants, addVariant, removeVariant, draft, setDraft, isValid, values, } = form;
|
|
17759
17788
|
const categoryOptions = React.useMemo(() => categories.map(({ id, name }) => ({ text: name, value: id })), [categories]);
|
|
17789
|
+
const submitButton = React.useMemo(() => isEditing ? buttonConfigs.saveButton : buttonConfigs.createButton, [buttonConfigs, isEditing]);
|
|
17790
|
+
const handleSubmit = React.useCallback(() => onSubmit(values), [onSubmit, values]);
|
|
17760
17791
|
return (jsxRuntime.jsxs(VoteWrapper, { children: [!isEditing && jsxRuntime.jsx(VoteHeading, { children: t("vote.newVote", { ns: VOTE_NS, defaultValue: "Новое голосование" }) }), jsxRuntime.jsx(uilibGl.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 ?? "")) }), jsxRuntime.jsx(uilibGl.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) }), jsxRuntime.jsx(VoteSectionLabel, { children: t("vote.variants", { ns: VOTE_NS, defaultValue: "Варианты ответов" }) }), !!variants.length && (jsxRuntime.jsx(VoteChipList, { children: variants.map(({ text: variantText }, index) => (jsxRuntime.jsxs(VoteChip, { children: [jsxRuntime.jsx(VoteVariantText, { children: variantText }), jsxRuntime.jsx(uilibGl.IconButton, { kind: "close", onClick: () => removeVariant(index) })] }, `${variantText}:${index}`))) })), jsxRuntime.jsx(uilibGl.Input, { width: "100%", placeholder: t("vote.variantPlaceholder", { ns: VOTE_NS, defaultValue: "Введите вариант и нажмите Enter" }), value: draft, onChange: ({ target }) => setDraft(target.value), onKeyDown: event => {
|
|
17761
17792
|
if (event.key === "Enter") {
|
|
17762
17793
|
event.preventDefault();
|
|
17763
17794
|
addVariant();
|
|
17764
17795
|
}
|
|
17765
|
-
} }), jsxRuntime.jsxs(uilibGl.Flex, { alignItems: "center", justifyContent: "space-between", children: [jsxRuntime.jsx(VoteSectionLabel, { children: t("vote.multiSelect", { ns: VOTE_NS, defaultValue: "Множественный выбор" }) }), jsxRuntime.jsx(uilibGl.Switch, { checked: multiSelect, onChange: () => setMultiSelect(prev => !prev) })] }), jsxRuntime.jsx(
|
|
17766
|
-
? t("vote.save", { ns: VOTE_NS, defaultValue: "Сохранить" })
|
|
17767
|
-
: t("vote.create", { ns: VOTE_NS, defaultValue: "Создать голосование" }) }), !isValid && (jsxRuntime.jsx(VoteHint, { children: t("vote.formHint", {
|
|
17796
|
+
} }), jsxRuntime.jsxs(uilibGl.Flex, { alignItems: "center", justifyContent: "space-between", children: [jsxRuntime.jsx(VoteSectionLabel, { children: t("vote.multiSelect", { ns: VOTE_NS, defaultValue: "Множественный выбор" }) }), jsxRuntime.jsx(uilibGl.Switch, { checked: multiSelect, onChange: () => setMultiSelect(prev => !prev) })] }), submitButton && (jsxRuntime.jsx(ContainerButton, { type: type, elementConfig: submitButton, disabled: !isValid || submitting, onClick: handleSubmit })), !isValid && (jsxRuntime.jsx(VoteHint, { children: t("vote.formHint", {
|
|
17768
17797
|
ns: VOTE_NS,
|
|
17769
17798
|
defaultValue: "Нужны категория, вопрос и минимум два варианта ответа",
|
|
17770
|
-
}) })), isEditing && (jsxRuntime.jsxs(uilibGl.Flex, { alignItems: "center", justifyContent: "space-between", children: [onCancel && (jsxRuntime.jsx(VoteEditLink, { onClick: onCancel, children: t("vote.cancel", { ns: VOTE_NS, defaultValue: "Отмена" }) })), canDelete && onDelete && (jsxRuntime.jsx(
|
|
17799
|
+
}) })), isEditing && (jsxRuntime.jsxs(uilibGl.Flex, { alignItems: "center", justifyContent: "space-between", children: [onCancel && (jsxRuntime.jsx(VoteEditLink, { onClick: onCancel, children: t("vote.cancel", { ns: VOTE_NS, defaultValue: "Отмена" }) })), canDelete && onDelete && buttonConfigs.deleteButton && (jsxRuntime.jsx(ContainerButton, { type: type, elementConfig: buttonConfigs.deleteButton, disabled: submitting, onClick: onDelete }))] }))] }));
|
|
17771
17800
|
};
|
|
17772
17801
|
|
|
17773
17802
|
/** Блок «Поделиться в соцсетях» — пока только вёрстка, без рабочих ссылок. */
|
|
@@ -17776,7 +17805,7 @@ const VoteShare = () => {
|
|
|
17776
17805
|
return (jsxRuntime.jsxs(VoteShareBlock, { children: [jsxRuntime.jsx(VoteSectionLabel, { children: t("vote.share", { ns: VOTE_NS, defaultValue: "Поделиться в соцсетях" }) }), jsxRuntime.jsx(VoteShareChips, { children: VOTE_SOCIALS.map(name => (jsxRuntime.jsx(VoteShareChip, { children: name }, name))) })] }));
|
|
17777
17806
|
};
|
|
17778
17807
|
|
|
17779
|
-
const VoteResults = ({ question, category, results, screen, isOwner, hasAnswers, submitting, onSubmit, onEdit, }) => {
|
|
17808
|
+
const VoteResults = ({ type, buttonConfigs, question, category, results, screen, isOwner, hasAnswers, submitting, onSubmit, onEdit, }) => {
|
|
17780
17809
|
const { t } = useGlobalContext();
|
|
17781
17810
|
const [selected, setSelected] = React.useState([]);
|
|
17782
17811
|
const showControls = screen === "voting";
|
|
@@ -17790,8 +17819,9 @@ const VoteResults = ({ question, category, results, screen, isOwner, hasAnswers,
|
|
|
17790
17819
|
});
|
|
17791
17820
|
}, [question.multiSelect]);
|
|
17792
17821
|
const canSubmit = React.useMemo(() => selected.length > 0 && !submitting, [selected.length, submitting]);
|
|
17822
|
+
const handleSubmit = React.useCallback(() => onSubmit(selected), [onSubmit, selected]);
|
|
17793
17823
|
return (jsxRuntime.jsxs(VoteWrapper, { children: [category && jsxRuntime.jsx(VoteCategoryLabel, { children: category.name }), jsxRuntime.jsx(VoteQuestionText, { children: question.text }), jsxRuntime.jsx(VoteVariantList, { column: true, value: selected[0] ?? "", children: results.map(({ id, text, count, percent }) => (jsxRuntime.jsxs(VoteVariantRow, { children: [jsxRuntime.jsxs(VoteVariantHead, { children: [showControls &&
|
|
17794
|
-
(question.multiSelect ? (jsxRuntime.jsx(uilibGl.Checkbox, { checked: selected.includes(id), onChange: () => toggle(id) })) : (jsxRuntime.jsx(uilibGl.Radio, { value: id, checked: selected.includes(id), onChange: () => toggle(id) }))), jsxRuntime.jsx(VoteVariantText, { children: text }), jsxRuntime.jsx(VoteVariantCount, { children: count })] }), jsxRuntime.jsx(uilibGl.LinearProgress, { done: percent })] }, id))) }), screen === "unauthenticated" && (jsxRuntime.jsx(VoteHint, { children: t("vote.authHint", { ns: VOTE_NS, defaultValue: "Чтобы проголосовать, пожалуйста, авторизуйтесь" }) })), screen === "voted" && (jsxRuntime.jsxs(VoteAcceptedBadge, { children: [jsxRuntime.jsx(uilibGl.Icon, { kind: "success" }), t("vote.accepted", { ns: VOTE_NS, defaultValue: "Ваш ответ принят" })] })), showControls && (jsxRuntime.jsx(
|
|
17824
|
+
(question.multiSelect ? (jsxRuntime.jsx(uilibGl.Checkbox, { checked: selected.includes(id), onChange: () => toggle(id) })) : (jsxRuntime.jsx(uilibGl.Radio, { value: id, checked: selected.includes(id), onChange: () => toggle(id) }))), jsxRuntime.jsx(VoteVariantText, { children: text }), jsxRuntime.jsx(VoteVariantCount, { children: count })] }), jsxRuntime.jsx(uilibGl.LinearProgress, { done: percent })] }, id))) }), screen === "unauthenticated" && (jsxRuntime.jsx(VoteHint, { children: t("vote.authHint", { ns: VOTE_NS, defaultValue: "Чтобы проголосовать, пожалуйста, авторизуйтесь" }) })), screen === "voted" && (jsxRuntime.jsxs(VoteAcceptedBadge, { children: [jsxRuntime.jsx(uilibGl.Icon, { kind: "success" }), t("vote.accepted", { ns: VOTE_NS, defaultValue: "Ваш ответ принят" })] })), showControls && buttonConfigs.submitButton && (jsxRuntime.jsx(ContainerButton, { type: type, elementConfig: buttonConfigs.submitButton, disabled: !canSubmit, onClick: handleSubmit })), canEdit && onEdit && (jsxRuntime.jsx(uilibGl.Flex, { children: jsxRuntime.jsx(VoteEditLink, { onClick: onEdit, children: t("vote.edit", { ns: VOTE_NS, defaultValue: "Редактировать" }) }) })), showShare && jsxRuntime.jsx(VoteShare, {})] }));
|
|
17795
17825
|
};
|
|
17796
17826
|
|
|
17797
17827
|
/**
|
|
@@ -17800,7 +17830,7 @@ const VoteResults = ({ question, category, results, screen, isOwner, hasAnswers,
|
|
|
17800
17830
|
* Живёт отдельно от `VoteContainer`, потому что тот отвечает за корень контейнера — размеры,
|
|
17801
17831
|
* фон и заголовок — и не должен раздуваться ветвлением по состояниям.
|
|
17802
17832
|
*/
|
|
17803
|
-
const VoteScreen = ({ screen, categories, question, category, results, totalVotes, isOwner, hasAnswers, isEditing, setIsEditing, editInitialValues, submitting, createVote, submitAnswer, editVote, deleteVote, }) => {
|
|
17833
|
+
const VoteScreen = ({ type, buttonConfigs, screen, categories, question, category, results, totalVotes, isOwner, hasAnswers, isEditing, setIsEditing, editInitialValues, submitting, createVote, submitAnswer, editVote, deleteVote, }) => {
|
|
17804
17834
|
const handleCreate = React.useCallback((values) => void createVote(values), [createVote]);
|
|
17805
17835
|
const handleEdit = React.useCallback((values) => void editVote(values).then(() => setIsEditing(false)), [editVote, setIsEditing]);
|
|
17806
17836
|
const handleDelete = React.useCallback(() => void deleteVote().then(() => setIsEditing(false)), [deleteVote, setIsEditing]);
|
|
@@ -17810,14 +17840,14 @@ const VoteScreen = ({ screen, categories, question, category, results, totalVote
|
|
|
17810
17840
|
if (screen === "loading")
|
|
17811
17841
|
return jsxRuntime.jsx(ContainerLoading, {});
|
|
17812
17842
|
if (isEditing && question) {
|
|
17813
|
-
return (jsxRuntime.jsx(VoteCreateForm, { categories: categories, initialValues: editInitialValues, isEditing: true, canDelete: !hasAnswers, submitting: submitting, onSubmit: handleEdit, onDelete: handleDelete, onCancel: handleCancel }));
|
|
17843
|
+
return (jsxRuntime.jsx(VoteCreateForm, { type: type, buttonConfigs: buttonConfigs, categories: categories, initialValues: editInitialValues, isEditing: true, canDelete: !hasAnswers, submitting: submitting, onSubmit: handleEdit, onDelete: handleDelete, onCancel: handleCancel }));
|
|
17814
17844
|
}
|
|
17815
17845
|
if (screen === "create") {
|
|
17816
|
-
return (jsxRuntime.jsx(VoteCreateForm, { categories: categories, isEditing: false, canDelete: false, submitting: submitting, onSubmit: handleCreate }));
|
|
17846
|
+
return (jsxRuntime.jsx(VoteCreateForm, { type: type, buttonConfigs: buttonConfigs, categories: categories, isEditing: false, canDelete: false, submitting: submitting, onSubmit: handleCreate }));
|
|
17817
17847
|
}
|
|
17818
17848
|
if (!question)
|
|
17819
17849
|
return null;
|
|
17820
|
-
return (jsxRuntime.jsx(VoteResults, { question: question, category: category, results: results, totalVotes: totalVotes, screen: screen, isOwner: isOwner, hasAnswers: hasAnswers, submitting: submitting, onSubmit: handleSubmitAnswer, onEdit: handleEditOpen }));
|
|
17850
|
+
return (jsxRuntime.jsx(VoteResults, { type: type, buttonConfigs: buttonConfigs, question: question, category: category, results: results, totalVotes: totalVotes, screen: screen, isOwner: isOwner, hasAnswers: hasAnswers, submitting: submitting, onSubmit: handleSubmitAnswer, onEdit: handleEditOpen }));
|
|
17821
17851
|
};
|
|
17822
17852
|
|
|
17823
17853
|
/** Приводит значение атрибута к строке-идентификатору (id приходят числами/строками). */
|
|
@@ -18244,6 +18274,9 @@ const useVoteContainer = (elementConfig) => {
|
|
|
18244
18274
|
};
|
|
18245
18275
|
};
|
|
18246
18276
|
|
|
18277
|
+
/** Слот кнопки существует только при совпадении id и явном type: "button". */
|
|
18278
|
+
const getButtonSlot = (children, id) => children?.find((child) => child.id === id && child.type === "button");
|
|
18279
|
+
|
|
18247
18280
|
/**
|
|
18248
18281
|
* Контейнер «Голосование»: привязывает опрос к объекту карты через `attributeName`
|
|
18249
18282
|
* и показывает один из экранов — создание, голосование, после голосования
|
|
@@ -18255,6 +18288,13 @@ const useVoteContainer = (elementConfig) => {
|
|
|
18255
18288
|
const VoteContainer = React.memo(({ type, elementConfig, isVisible, renderElement }) => {
|
|
18256
18289
|
const { root, body } = useContainerRoot({ elementConfig });
|
|
18257
18290
|
const { configured, ...vote } = useVoteContainer(elementConfig);
|
|
18291
|
+
const children = elementConfig?.children;
|
|
18292
|
+
const buttonConfigs = React.useMemo(() => ({
|
|
18293
|
+
submitButton: getButtonSlot(children, "submitButton"),
|
|
18294
|
+
createButton: getButtonSlot(children, "createButton"),
|
|
18295
|
+
saveButton: getButtonSlot(children, "saveButton"),
|
|
18296
|
+
deleteButton: getButtonSlot(children, "deleteButton"),
|
|
18297
|
+
}), [children]);
|
|
18258
18298
|
// Без имени атрибута или хотя бы одного источника контейнеру не с чем работать. Ошибка вместо
|
|
18259
18299
|
// пустоты: молчаливый `null` выдавал бы опечатку в конфиге за «блока просто нет».
|
|
18260
18300
|
if (!configured) {
|
|
@@ -18264,7 +18304,7 @@ const VoteContainer = React.memo(({ type, elementConfig, isVisible, renderElemen
|
|
|
18264
18304
|
// в карточке в этом случае тоже не нужен, поэтому не рисуем и корень.
|
|
18265
18305
|
if (!vote.screen)
|
|
18266
18306
|
return null;
|
|
18267
|
-
return (jsxRuntime.jsxs(ContainerRoot, { ...root, children: [jsxRuntime.jsx(ContainerBackground, { elementConfig: elementConfig, renderElement: renderElement }), jsxRuntime.jsx(ExpandableTitle, { elementConfig: elementConfig, type: type, renderElement: renderElement }), isVisible && (jsxRuntime.jsx(Container, { ...body, isColumn: true, children: jsxRuntime.jsx(VoteScreen, { ...vote, screen: vote.screen }) }))] }));
|
|
18307
|
+
return (jsxRuntime.jsxs(ContainerRoot, { ...root, children: [jsxRuntime.jsx(ContainerBackground, { elementConfig: elementConfig, renderElement: renderElement }), jsxRuntime.jsx(ExpandableTitle, { elementConfig: elementConfig, type: type, renderElement: renderElement }), isVisible && (jsxRuntime.jsx(Container, { ...body, isColumn: true, children: jsxRuntime.jsx(VoteScreen, { ...vote, screen: vote.screen, type: type, buttonConfigs: buttonConfigs }) }))] }));
|
|
18268
18308
|
});
|
|
18269
18309
|
|
|
18270
18310
|
const ExpandableTitle = React.memo(({ elementConfig, type, renderElement }) => {
|
|
@@ -18880,12 +18920,11 @@ const ContainersGroupContainer = React.memo(props => {
|
|
|
18880
18920
|
});
|
|
18881
18921
|
|
|
18882
18922
|
const ExportPdfContainer = React.memo(({ type, elementConfig, renderElement }) => {
|
|
18883
|
-
const { t } = useGlobalContext();
|
|
18884
18923
|
const { loading, onExport } = useExportPdf(getRootElementId(type));
|
|
18885
|
-
const { id, style,
|
|
18886
|
-
const
|
|
18924
|
+
const { id, style, children } = elementConfig || {};
|
|
18925
|
+
const buttonConfig = React.useMemo(() => getButtonSlot(children, "button"), [children]);
|
|
18887
18926
|
const bgHost = useBgImageHost(elementConfig);
|
|
18888
|
-
return (jsxRuntime.jsxs(Container, { id: id, style: style, ...bgHost, children: [jsxRuntime.jsx(ContainerBackground, { elementConfig: elementConfig, renderElement: renderElement }), jsxRuntime.jsx(
|
|
18927
|
+
return (jsxRuntime.jsxs(Container, { id: id, style: style, ...bgHost, children: [jsxRuntime.jsx(ContainerBackground, { elementConfig: elementConfig, renderElement: renderElement }), buttonConfig && jsxRuntime.jsx(ContainerButton, { type: type, elementConfig: buttonConfig, disabled: loading, onClick: onExport })] }));
|
|
18889
18928
|
});
|
|
18890
18929
|
|
|
18891
18930
|
exports.ThemeName = void 0;
|