= (props: EntrySelector
isInvalid,
getEntryLink,
includeClickableType,
+ navigationPath,
+ changeNavigationPath,
} = props;
- const dispatch = useDispatch();
- const navigationPath = useSelector(selectNavigationPath);
-
- const handleChangeNavigationPath = (newNavigationPath: string) => {
- dispatch(changeNavigationPath(newNavigationPath));
- };
-
return (
@@ -69,7 +63,7 @@ export const EntrySelector: React.FC = (props: EntrySelector
getEntryLink={getEntryLink}
includeClickableType={includeClickableType}
navigationPath={navigationPath}
- changeNavigationPath={handleChangeNavigationPath}
+ changeNavigationPath={changeNavigationPath}
/>
diff --git a/src/ui/components/ControlComponents/Sections/CommonSettingsSection/ExternalSelectorSettings/ExternalSelectorSettings.tsx b/src/ui/components/ControlComponents/Sections/CommonSettingsSection/ExternalSelectorSettings/ExternalSelectorSettings.tsx
index e7e33d99b1..1bd1ef1bba 100644
--- a/src/ui/components/ControlComponents/Sections/CommonSettingsSection/ExternalSelectorSettings/ExternalSelectorSettings.tsx
+++ b/src/ui/components/ControlComponents/Sections/CommonSettingsSection/ExternalSelectorSettings/ExternalSelectorSettings.tsx
@@ -28,7 +28,10 @@ imm.extend('$auto', (value, object) => {
return object ? update(object, value) : update({}, value);
});
-const ExternalSelectorSettings = () => {
+const ExternalSelectorSettings: React.FC<{
+ navigationPath: string | null;
+ changeNavigationPath: (newNavigationPath: string) => void;
+}> = (props) => {
const dispatch = useDispatch();
const {autoHeight, chartId, title, selectorParameters, validation, selectorParametersGroup} =
useSelector(selectSelectorDialog);
@@ -98,6 +101,8 @@ const ExternalSelectorSettings = () => {
entryId={chartId}
onChange={handleChartIdChange}
includeClickableType={EntryTypeNode.CONTROL_NODE}
+ navigationPath={props.navigationPath}
+ changeNavigationPath={props.changeNavigationPath}
/>
diff --git a/src/ui/components/ControlComponents/Sections/CommonSettingsSection/InputTypeSelector/InputTypeSelector.tsx b/src/ui/components/ControlComponents/Sections/CommonSettingsSection/InputTypeSelector/InputTypeSelector.tsx
index 03ead48dd3..5930f2aab7 100644
--- a/src/ui/components/ControlComponents/Sections/CommonSettingsSection/InputTypeSelector/InputTypeSelector.tsx
+++ b/src/ui/components/ControlComponents/Sections/CommonSettingsSection/InputTypeSelector/InputTypeSelector.tsx
@@ -13,6 +13,7 @@ import {
} from 'shared';
import {SelectOptionWithIcon} from 'ui/components/SelectComponents/components/SelectOptionWithIcon/SelectOptionWithIcon';
import {setSelectorDialogItem} from 'ui/store/actions/controlDialog';
+import {ELEMENT_TYPE} from 'ui/store/constants/controlDialog';
import {
selectIsControlConfigurationDisabled,
selectSelectorControlType,
@@ -20,7 +21,6 @@ import {
} from 'ui/store/selectors/controlDialog';
import type {SelectorElementType} from 'ui/store/typings/controlDialog';
-import {ELEMENT_TYPE} from '../../../../../units/dash/containers/Dialogs/Control/constants';
import {getElementOptions} from '../helpers/input-type-select';
const i18n = I18n.keyset('dash.control-dialog.edit');
diff --git a/src/ui/components/ControlComponents/Sections/CommonSettingsSection/helpers/input-type-select.tsx b/src/ui/components/ControlComponents/Sections/CommonSettingsSection/helpers/input-type-select.tsx
index df7861651d..dee8104b6a 100644
--- a/src/ui/components/ControlComponents/Sections/CommonSettingsSection/helpers/input-type-select.tsx
+++ b/src/ui/components/ControlComponents/Sections/CommonSettingsSection/helpers/input-type-select.tsx
@@ -5,8 +5,7 @@ import type {SelectOption} from '@gravity-ui/uikit';
import {Icon} from '@gravity-ui/uikit';
import {I18n} from 'i18n';
import {DialogControlQa} from 'shared/constants/qa';
-
-import {ELEMENT_TYPE} from '../../../../../units/dash/containers/Dialogs/Control/constants';
+import {ELEMENT_TYPE} from 'ui/store/constants/controlDialog';
const i18n = I18n.keyset('dash.control-dialog.edit');
diff --git a/src/ui/components/ControlComponents/Sections/Date/Acceptable/Acceptable.tsx b/src/ui/components/ControlComponents/Sections/Date/Acceptable/Acceptable.tsx
index 4140769f5f..39d0cba517 100644
--- a/src/ui/components/ControlComponents/Sections/Date/Acceptable/Acceptable.tsx
+++ b/src/ui/components/ControlComponents/Sections/Date/Acceptable/Acceptable.tsx
@@ -4,8 +4,8 @@ import block from 'bem-cn-lite';
import {i18n} from 'i18n';
import moment from 'moment';
import {registry} from 'ui/registry';
+import {DATE_FORMAT} from 'ui/store/constants/controlDialog';
-import {DATE_FORMAT} from '../../../../../units/dash/containers/Dialogs/Control/constants';
import Dialog from '../../../Dialog/Dialog';
import Button from '../../Switchers/Button/Button';
diff --git a/src/ui/components/ControlComponents/Sections/Date/Default/Default.tsx b/src/ui/components/ControlComponents/Sections/Date/Default/Default.tsx
index f6c72c2217..be545a90b7 100644
--- a/src/ui/components/ControlComponents/Sections/Date/Default/Default.tsx
+++ b/src/ui/components/ControlComponents/Sections/Date/Default/Default.tsx
@@ -13,11 +13,8 @@ import {
getParsedRelativeDate,
} from 'shared';
import {RelativeDatesPicker} from 'ui';
+import {DATETIME_FORMAT, DATE_FORMAT} from 'ui/store/constants/controlDialog';
-import {
- DATETIME_FORMAT,
- DATE_FORMAT,
-} from '../../../../../units/dash/containers/Dialogs/Control/constants';
import Dialog from '../../../Dialog/Dialog';
import './Default.scss';
diff --git a/src/ui/components/ControlComponents/Sections/ValueSelector/RequiredValueCheckbox/RequiredValueCheckbox.tsx b/src/ui/components/ControlComponents/Sections/ValueSelector/RequiredValueCheckbox/RequiredValueCheckbox.tsx
index 70af6dbed2..514428983f 100644
--- a/src/ui/components/ControlComponents/Sections/ValueSelector/RequiredValueCheckbox/RequiredValueCheckbox.tsx
+++ b/src/ui/components/ControlComponents/Sections/ValueSelector/RequiredValueCheckbox/RequiredValueCheckbox.tsx
@@ -7,14 +7,13 @@ import {I18n} from 'i18n';
import {useDispatch, useSelector} from 'react-redux';
import {DialogControlQa} from 'shared';
import {setSelectorDialogItem} from 'ui/store/actions/controlDialog';
+import {ELEMENT_TYPE} from 'ui/store/constants/controlDialog';
import {
selectIsControlConfigurationDisabled,
selectSelectorControlType,
selectSelectorRequired,
} from 'ui/store/selectors/controlDialog';
-import {ELEMENT_TYPE} from '../../../../../units/dash/containers/Dialogs/Control/constants';
-
import '../ValueSelector.scss';
const i18n = I18n.keyset('dash.control-dialog.edit');
diff --git a/src/ui/components/ControlComponents/Sections/ValueSelector/ValueSelector.tsx b/src/ui/components/ControlComponents/Sections/ValueSelector/ValueSelector.tsx
index 465d525e80..860ef699c0 100644
--- a/src/ui/components/ControlComponents/Sections/ValueSelector/ValueSelector.tsx
+++ b/src/ui/components/ControlComponents/Sections/ValueSelector/ValueSelector.tsx
@@ -10,6 +10,7 @@ import {FieldWrapper} from 'ui/components/FieldWrapper/FieldWrapper';
import {VIEW_MODES} from 'ui/components/Select/hooks/useSelectRenderFilter/useSelectRenderFilter';
import {registry} from 'ui/registry';
import {setSelectorDialogItem} from 'ui/store/actions/controlDialog';
+import {CheckboxControlValue} from 'ui/store/constants/controlDialog';
import {
selectIsControlConfigurationDisabled,
selectSelectorControlType,
@@ -21,7 +22,6 @@ import {selectWorkbookId} from 'ui/units/workbooks/store/selectors';
import type {FilterValue} from '../../../../../shared/modules';
import {DATASET_FIELD_TYPES, DashTabItemControlSourceType} from '../../../../../shared/types';
-import {CheckboxControlValue} from '../../../../units/dash/containers/Dialogs/Control/constants';
import {getDistinctsByTypedQuery} from '../CommonSettingsSection/ConnectionSettings/helpers/get-distincts-by-typed-query';
import {
DEFAULT_PAGE_SIZE,
diff --git a/src/ui/units/dash/components/TwoColumnDialog/TwoColumnDialog.scss b/src/ui/components/ControlComponents/TwoColumnDialog/TwoColumnDialog.scss
similarity index 100%
rename from src/ui/units/dash/components/TwoColumnDialog/TwoColumnDialog.scss
rename to src/ui/components/ControlComponents/TwoColumnDialog/TwoColumnDialog.scss
diff --git a/src/ui/units/dash/components/TwoColumnDialog/TwoColumnDialog.tsx b/src/ui/components/ControlComponents/TwoColumnDialog/TwoColumnDialog.tsx
similarity index 100%
rename from src/ui/units/dash/components/TwoColumnDialog/TwoColumnDialog.tsx
rename to src/ui/components/ControlComponents/TwoColumnDialog/TwoColumnDialog.tsx
diff --git a/src/ui/components/DialogChartWidget/DialogChartWidget.tsx b/src/ui/components/DialogChartWidget/DialogChartWidget.tsx
index 0e2e985364..71072c27ff 100644
--- a/src/ui/components/DialogChartWidget/DialogChartWidget.tsx
+++ b/src/ui/components/DialogChartWidget/DialogChartWidget.tsx
@@ -30,12 +30,12 @@ import {
updateParamValue,
validateParamTitle,
} from '../../units/dash/components/ParamsSettings/helpers';
-import TwoColumnDialog from '../../units/dash/components/TwoColumnDialog/TwoColumnDialog';
import {PaletteBackground} from '../../units/dash/containers/Dialogs/components/PaletteBackground/PaletteBackground';
import {isEntryTypeWithFiltering} from '../../units/dash/containers/Dialogs/utils';
import {DASH_WIDGET_TYPES, EntryTypeNode} from '../../units/dash/modules/constants';
import type {SetItemDataArgs} from '../../units/dash/store/actions/dashTyped';
import Utils from '../../utils';
+import TwoColumnDialog from '../ControlComponents/TwoColumnDialog/TwoColumnDialog';
import {TabMenu} from './TabMenu/TabMenu';
import type {UpdateState} from './TabMenu/types';
@@ -79,7 +79,14 @@ function Line(props: LineProps) {
type AfterSettingsWidgetCallback = ((selectedWidgetType: WidgetKind) => void) | null;
-export interface DialogChartWidgetProps {
+export interface DialogChartWidgetFeatureProps {
+ withoutSidebar?: boolean;
+
+ enableAutoheight?: boolean;
+ enableBackgroundColor?: boolean;
+ enableFilteringSetting?: boolean;
+}
+export interface DialogChartWidgetProps extends DialogChartWidgetFeatureProps {
openedItemId: string | null;
openedItemData: DashTabItemWidget['data'];
dialogIsVisible: boolean;
@@ -92,12 +99,6 @@ export interface DialogChartWidgetProps {
[key: string]: string;
};
- withoutSidebar?: boolean;
-
- enableAutoheight?: boolean;
- enableBackgroundColor?: boolean;
- enableFilteringSetting?: boolean;
-
changeNavigationPath: (newNavigationPath: string) => void;
closeDialog: () => void;
setItemData: (newItemData: SetItemDataArgs) => void;
diff --git a/src/ui/components/DialogEditItem/DialogEditItem.tsx b/src/ui/components/DialogEditItem/DialogEditItem.tsx
new file mode 100644
index 0000000000..a0105a9440
--- /dev/null
+++ b/src/ui/components/DialogEditItem/DialogEditItem.tsx
@@ -0,0 +1,246 @@
+import React from 'react';
+
+import {useDispatch, useSelector} from 'react-redux';
+import type {
+ DashTabItem,
+ DashTabItemGroupControl,
+ DashTabItemImage,
+ DashTabItemText,
+ DashTabItemTitle,
+ DashTabItemWidget,
+ EntryScope,
+ StringParams,
+ WidgetType,
+} from 'shared';
+import {DashTabItemType} from 'shared';
+import {ITEM_TYPE} from 'ui/constants/dialogs';
+import {useEffectOnce} from 'ui/hooks';
+import {initControlDialog, resetControlDialog} from 'ui/store/actions/controlDialog';
+import {selectOpenedDialogType} from 'ui/store/selectors/controlDialog';
+
+import type {DialogChartWidgetFeatureProps} from '../DialogChartWidget/DialogChartWidget';
+import DialogChartWidget from '../DialogChartWidget/DialogChartWidget';
+import DialogExternalControl from '../DialogExternalControl/DialogExternalControl';
+import {DialogGroupControl} from '../DialogGroupControl/DialogGroupControl';
+import {DialogImageWidget} from '../DialogImageWidget';
+import {DialogTextWidgetWrapper} from '../DialogTextWidget';
+import type {DialogTextWidgetFeatureProps} from '../DialogTextWidget/DialogTextWidget';
+import type {DialogTitleWidgetFeatureProps} from '../DialogTitleWidget/DialogTitleWidget';
+import DialogTitleWidget from '../DialogTitleWidget/DialogTitleWidget';
+
+type DialogEditTitleProps = {
+ type: typeof ITEM_TYPE.TITLE;
+ openedItemData: React.ComponentProps['openedItemData'];
+ setItemData: React.ComponentProps['setItemData'];
+ widgetType: any;
+ widgetsCurrentTab: any;
+ openedItemDefaults: any;
+};
+
+type DialogEditTextProps = {
+ type: typeof ITEM_TYPE.TEXT;
+ openedItemData: React.ComponentProps['openedItemData'];
+ setItemData: React.ComponentProps['setItemData'];
+ widgetType: any;
+ widgetsCurrentTab: any;
+ openedItemDefaults: any;
+};
+
+type DialogEditChartProps = {
+ type: typeof ITEM_TYPE.WIDGET;
+ openedItemData: React.ComponentProps['openedItemData'];
+ widgetType: WidgetType;
+ setItemData: React.ComponentProps['setItemData'];
+ widgetsCurrentTab: {
+ [key: string]: string;
+ };
+ openedItemDefaults: any;
+};
+
+type DialogEditGroupControlProps = {
+ type: typeof ITEM_TYPE.GROUP_CONTROL;
+ openedItemData: React.ComponentProps['openedItemData'];
+ setItemData: React.ComponentProps['setItemData'];
+ widgetType: any;
+ widgetsCurrentTab: any;
+ openedItemDefaults: any;
+};
+
+type DialogEditExternalControlProps = {
+ type: typeof ITEM_TYPE.CONTROL;
+ setItemData: React.ComponentProps['setItemData'];
+ openedItemData: any;
+ widgetType: any;
+ widgetsCurrentTab: any;
+ openedItemDefaults: StringParams;
+};
+
+type DialogEditImageProps = {
+ type: typeof ITEM_TYPE.IMAGE;
+ openedItemData: React.ComponentProps['openedItemData'];
+ setItemData: React.ComponentProps['onApply'];
+ widgetType: any;
+ widgetsCurrentTab: any;
+ openedItemDefaults: any;
+};
+
+export type DialogEditItemFeaturesProp = {
+ [DashTabItemType.Title]?: DialogTitleWidgetFeatureProps;
+ [DashTabItemType.Text]?: DialogTextWidgetFeatureProps;
+ [DashTabItemType.Widget]?: DialogChartWidgetFeatureProps;
+};
+
+export type DialogEditItemProps = {
+ entryId: string | null;
+ scope: EntryScope;
+ openedItemId: string | null;
+ currentTabId: string | null;
+ workbookId: string | null;
+ closeDialog: () => void;
+ navigationPath: string | null;
+ openedItemNamespace: string;
+ changeNavigationPath: (newNavigationPath: string) => void;
+ features?: DialogEditItemFeaturesProp;
+ openedItemData: DashTabItem['data'];
+} & (
+ | DialogEditTitleProps
+ | DialogEditTextProps
+ | DialogEditChartProps
+ | DialogEditGroupControlProps
+ | DialogEditExternalControlProps
+ | DialogEditImageProps
+);
+
+export const isDialogEditItemType = (type: string): type is DashTabItemType =>
+ Object.values(ITEM_TYPE).includes(type as DashTabItemType);
+
+export const DialogEditItem: React.FC = (props) => {
+ const {
+ features,
+ scope,
+ entryId,
+ type,
+ openedItemId,
+ openedItemNamespace,
+ openedItemDefaults,
+ currentTabId,
+ openedItemData,
+ widgetType,
+ widgetsCurrentTab,
+ workbookId,
+ navigationPath,
+ changeNavigationPath,
+ closeDialog,
+ setItemData,
+ } = props;
+
+ const dispatch = useDispatch();
+ const openedDialog = useSelector(selectOpenedDialogType);
+
+ const [isOpenedDialog, setOpenedDialog] = React.useState(false);
+
+ useEffectOnce(() => {
+ dispatch(
+ initControlDialog({
+ type,
+ id: openedItemId,
+ data: openedItemData,
+ defaults: openedItemDefaults,
+ openedItemMeta: {
+ scope,
+ entryId,
+ namespace: openedItemNamespace,
+ currentTabId,
+ workbookId,
+ },
+ }),
+ );
+
+ return () => {
+ dispatch(resetControlDialog());
+ };
+ });
+
+ React.useEffect(() => {
+ setOpenedDialog(Boolean(openedDialog));
+ }, [dispatch, openedDialog]);
+
+ switch (openedDialog) {
+ case ITEM_TYPE.TITLE:
+ return (
+
+ );
+ case ITEM_TYPE.TEXT: {
+ return (
+
+ );
+ }
+ case ITEM_TYPE.WIDGET:
+ return (
+
+ );
+ case ITEM_TYPE.CONTROL:
+ return (
+
+ );
+ case ITEM_TYPE.GROUP_CONTROL:
+ return (
+
+ );
+
+ case ITEM_TYPE.IMAGE:
+ return (
+
+ );
+ default: {
+ return null;
+ }
+ }
+};
diff --git a/src/ui/components/DialogExternalControl/DialogExternalControl.tsx b/src/ui/components/DialogExternalControl/DialogExternalControl.tsx
index db2d4e2782..89ccb32f1f 100644
--- a/src/ui/components/DialogExternalControl/DialogExternalControl.tsx
+++ b/src/ui/components/DialogExternalControl/DialogExternalControl.tsx
@@ -6,7 +6,6 @@ import {I18n} from 'i18n';
import type {DatalensGlobalState} from 'index';
import {Utils} from 'index';
import {connect} from 'react-redux';
-import type {Dispatch} from 'redux';
import {bindActionCreators} from 'redux';
import {ControlQA, DashTabItemControlSourceType, Feature} from 'shared';
import {AppearanceSection} from 'ui/components/ControlComponents/Sections/AppearanceSection/AppearanceSection';
@@ -15,11 +14,16 @@ import {ParametersSection} from 'ui/components/ControlComponents/Sections/Parame
import {SelectorPreview} from 'ui/components/ControlComponents/SelectorPreview/SelectorPreview';
import {SelectorTypeSelect} from 'ui/components/ControlComponents/SelectorTypeSelect/SelectorTypeSelect';
import {SectionWrapper} from 'ui/components/SectionWrapper/SectionWrapper';
+import type {AppDispatch} from 'ui/store';
+import {
+ applyExternalControlDialog,
+ closeExternalControlDialog,
+} from 'ui/store/actions/controlDialog';
import {
selectIsParametersSectionAvailable,
selectSelectorDialog,
} from 'ui/store/selectors/controlDialog';
-import {applyControl2Dialog, closeControl2Dialog} from 'units/dash/store/actions/dashTyped';
+import type {SetItemDataArgs} from 'ui/units/dash/store/actions/dashTyped';
import './DialogExternalControl.scss';
@@ -29,7 +33,13 @@ const dashI18n = I18n.keyset('dash.main.view');
type StateProps = ReturnType;
type DispatchProps = ReturnType;
-type OwnProps = {};
+type OwnProps = {
+ dialogIsVisible: boolean;
+ closeDialog: () => void;
+ setItemData: (newItemData: SetItemDataArgs) => void;
+ navigationPath: string | null;
+ changeNavigationPath: (newNavigationPath: string) => void;
+};
type Props = OwnProps & DispatchProps & StateProps;
@@ -37,17 +47,15 @@ const b = block('dl-dialog-add-control');
class DialogExternalControl extends React.Component {
render() {
- const {isEdit, validation} = this.props;
+ const {isEdit, validation, dialogIsVisible} = this.props;
const textButtonApply = isEdit ? controlI18n('button_save') : controlI18n('button_add');
//TODO: raname 'label_control' after enabling feature flag
- const caption = Utils.isEnabledFeature(Feature.GroupControls)
- ? dashI18n('button_edit-panel-editor-selector')
- : controlI18n('label_control');
+ const caption = dashI18n('button_edit-panel-editor-selector');
return (
-
+
diff --git a/src/ui/components/DialogGroupControl/GroupControlSidebar/GroupControlSidebar.tsx b/src/ui/components/DialogGroupControl/GroupControlSidebar/GroupControlSidebar.tsx
index 1e7b3ab93e..41fc33ffaa 100644
--- a/src/ui/components/DialogGroupControl/GroupControlSidebar/GroupControlSidebar.tsx
+++ b/src/ui/components/DialogGroupControl/GroupControlSidebar/GroupControlSidebar.tsx
@@ -28,7 +28,6 @@ import {selectActiveSelectorIndex, selectSelectorsGroup} from 'ui/store/selector
import type {SelectorDialogState, SelectorsGroupDialogState} from 'ui/store/typings/controlDialog';
import type {CopiedConfigData} from 'ui/units/dash/modules/helpers';
import {isItemPasteAllowed} from 'ui/units/dash/modules/helpers';
-import {copyControlToStorage} from 'ui/units/dash/store/actions/controls/actions';
import {TabMenu} from '../../DialogChartWidget/TabMenu/TabMenu';
import type {TabMenuItemData, UpdateState} from '../../DialogChartWidget/TabMenu/types';
@@ -79,7 +78,9 @@ const handlePasteItems = (pasteConfig: CopiedConfigData | null) => {
return pasteItems as TabMenuItemData[];
};
-export const GroupControlSidebar = () => {
+export const GroupControlSidebar: React.FC<{handleCopyItem: (itemIndex: number) => void}> = (
+ props,
+) => {
const selectorsGroup = useSelector(selectSelectorsGroup);
const activeSelectorIndex = useSelector(selectActiveSelectorIndex);
@@ -182,10 +183,6 @@ export const GroupControlSidebar = () => {
);
};
- const handleCopyItem = (itemIndex: number) => {
- dispatch(copyControlToStorage(itemIndex));
- };
-
const handleUpdateItem = (title: string) => {
dispatch(
setSelectorDialogItem({
@@ -217,7 +214,7 @@ export const GroupControlSidebar = () => {
onPasteItems={handlePasteItems}
canPasteItems={canPasteItems}
addButtonView="outlined"
- onCopyItem={handleCopyItem}
+ onCopyItem={props.handleCopyItem}
onUpdateItem={handleUpdateItem}
/>
diff --git a/src/ui/components/DialogTextWidget/DialogTextWidget.tsx b/src/ui/components/DialogTextWidget/DialogTextWidget.tsx
index f4dfb899cf..2d64199b13 100644
--- a/src/ui/components/DialogTextWidget/DialogTextWidget.tsx
+++ b/src/ui/components/DialogTextWidget/DialogTextWidget.tsx
@@ -14,13 +14,15 @@ import './DialogTextWidget.scss';
const b = block('dialog-text');
-export interface DialogTextWidgetProps {
+export interface DialogTextWidgetFeatureProps {
+ enableAutoheight?: boolean;
+}
+
+export interface DialogTextWidgetProps extends DialogTextWidgetFeatureProps {
openedItemId: string | null;
openedItemData: DashTabItemText['data'];
dialogIsVisible: boolean;
- enableAutoheight?: boolean;
-
closeDialog: () => void;
setItemData: (newItemData: SetItemDataArgs) => void;
}
diff --git a/src/ui/components/DialogTitleWidget/DialogTitleWidget.tsx b/src/ui/components/DialogTitleWidget/DialogTitleWidget.tsx
index 0efc0448e0..7de97b07bc 100644
--- a/src/ui/components/DialogTitleWidget/DialogTitleWidget.tsx
+++ b/src/ui/components/DialogTitleWidget/DialogTitleWidget.tsx
@@ -40,7 +40,11 @@ interface DialogTitleWidgetState {
backgroundColor?: string;
}
-interface DialogTitleWidgetProps {
+export interface DialogTitleWidgetFeatureProps {
+ enableAutoheight?: boolean;
+ enableShowInTOC?: boolean;
+}
+interface DialogTitleWidgetProps extends DialogTitleWidgetFeatureProps {
openedItemId: string | null;
openedItemData: DashTabItemTitle['data'];
dialogIsVisible: boolean;
diff --git a/src/ui/index.ts b/src/ui/index.ts
index 3eb626d51e..35fecb9541 100644
--- a/src/ui/index.ts
+++ b/src/ui/index.ts
@@ -6,7 +6,9 @@ import type {LandingState} from 'store/reducers/landing';
import type {AsideHeaderState} from 'store/typings/asideHeader';
import type {EntryGlobalState} from 'store/typings/entryContent';
import type {UserState} from 'store/typings/user';
+import type {ControlDialogState} from 'ui/store/reducers/controlDialog';
import type {CopyEntriesToWorkbookState} from 'ui/store/reducers/copyEntriesToWorkbook';
+import type {EditHistoryState} from 'ui/store/reducers/editHistory';
import type {MigrationToWorkbookState} from 'ui/store/reducers/migrationToWorkbook';
import type {CollectionsState} from 'units/collections/store/reducers';
import type {ConnectionsReduxState} from 'units/connections/store/typings';
@@ -18,7 +20,6 @@ import type {QLState} from 'units/ql/store/typings';
import type {WizardGlobalState} from 'units/wizard/reducers';
import type {WorkbooksState} from 'units/workbooks/store/reducers';
-import type {EditHistoryState} from './store/reducers/editHistory';
import type {CollectionsNavigationState} from './units/collections-navigation/store/reducers';
export {default as ActionPanel} from './components/ActionPanel/ActionPanel';
@@ -58,6 +59,7 @@ export type DatalensGlobalState = {
dash: DashState;
dataset: DatasetReduxState;
ql: QLState;
+ controlDialog: ControlDialogState;
collections: CollectionsState;
collectionsStructure: CollectionsStructureState;
workbooks: WorkbooksState;
diff --git a/src/ui/libs/DatalensChartkit/components/Control/Items/Items.js b/src/ui/libs/DatalensChartkit/components/Control/Items/Items.js
index fc52b19df6..f11b910926 100644
--- a/src/ui/libs/DatalensChartkit/components/Control/Items/Items.js
+++ b/src/ui/libs/DatalensChartkit/components/Control/Items/Items.js
@@ -7,7 +7,7 @@ import PropTypes from 'prop-types';
import {ControlQA, TitlePlacementOption, resolveIntervalDate, resolveRelativeDate} from 'shared';
import {DL} from 'ui/constants/common';
import {registry} from 'ui/registry';
-import {CheckboxControlValue} from 'ui/units/dash/containers/Dialogs/Control/constants';
+import {CheckboxControlValue} from 'ui/store/constants/controlDialog';
import {MOBILE_SIZE} from 'ui/utils/mobile';
import {YCSelect} from '../../../../../components/common/YCSelect/YCSelect';
diff --git a/src/ui/store/actions/controlDialog.ts b/src/ui/store/actions/controlDialog.ts
index b19c7e8895..a8225a4bc0 100644
--- a/src/ui/store/actions/controlDialog.ts
+++ b/src/ui/store/actions/controlDialog.ts
@@ -1,4 +1,78 @@
+import {DashTabItemType, TitlePlacementOption} from 'shared';
+import type {
+ DashTabItem,
+ DashTabItemControlData,
+ DashTabItemGroupControl,
+ StringParams,
+} from 'shared';
import type {SelectorsGroupDialogState, SetSelectorDialogItemArgs} from '../typings/controlDialog';
+import type {AppDispatch} from '..';
+import {
+ getControlDefaultsForField,
+ getControlValidation,
+ getItemDataSource,
+} from '../utils/controlDialog';
+import isEmpty from 'lodash/isEmpty';
+import {
+ getBeforeCloseDialogItemAction,
+ getExtendedItemDataAction,
+} from 'ui/units/dash/store/actions/helpers';
+import {showToast} from './toaster';
+import {I18n} from 'i18n';
+import type {ControlDialogStateItemMeta} from '../reducers/controlDialog';
+import {getGroupSelectorDialogInitialState} from '../reducers/controlDialog';
+import {DEFAULT_CONTROL_LAYOUT} from 'ui/components/DashKit/constants';
+import {COPIED_WIDGET_STORAGE_KEY} from 'ui/constants';
+import type {ConfigItemGroup} from '@gravity-ui/dashkit/helpers';
+import {DEFAULT_NAMESPACE} from '@gravity-ui/dashkit/helpers';
+import {CONTROLS_PLACEMENT_MODE} from 'ui/constants/dialogs';
+import type {PreparedCopyItemOptions} from '@gravity-ui/dashkit';
+import {getPreparedCopyItemOptions, type CopiedConfigContext} from 'ui/units/dash/modules/helpers';
+import type {SetItemDataArgs} from 'ui/units/dash/store/actions/dashTyped';
+import type {DatalensGlobalState} from 'ui/index';
+import {
+ selectActiveSelectorIndex,
+ selectIsControlSourceTypeHasChanged,
+ selectOpenedItemData,
+ selectOpenedItemId,
+ selectOpenedItemMeta,
+ selectSelectorDialog,
+ selectSelectorsGroup,
+} from '../selectors/controlDialog';
+
+const dialogI18n = I18n.keyset('dash.group-controls-dialog.edit');
+
+export const INIT_DIALOG = Symbol('controlDialog/INIT_DIALOG');
+
+export type InitDialogAction = {
+ type: typeof INIT_DIALOG;
+ payload: {
+ id: string | null;
+ data: DashTabItem['data'];
+ type: DashTabItemType;
+ defaults?: StringParams | null;
+ openedItemMeta: ControlDialogStateItemMeta;
+ };
+};
+
+export const initControlDialog = (payload: InitDialogAction['payload']): InitDialogAction => {
+ return {
+ type: INIT_DIALOG,
+ payload,
+ };
+};
+
+export const RESET_DIALOG = Symbol('controlDialog/RESET_DIALOG');
+
+export type ResetDialogAction = {
+ type: typeof RESET_DIALOG;
+};
+
+export const resetControlDialog = (): ResetDialogAction => {
+ return {
+ type: RESET_DIALOG,
+ };
+};
export const ADD_SELECTOR_TO_GROUP = Symbol('controlDialog/ADD_SELECTOR_TO_GROUP');
@@ -65,3 +139,270 @@ export const setSelectorDialogItem = (
payload,
};
};
+
+export const SET_LAST_USED_DATASET_ID = Symbol('controlDialog/SET_LAST_USED_DATASET_ID');
+export type SetLastUsedDatasetIdAction = {
+ type: typeof SET_LAST_USED_DATASET_ID;
+ payload: string;
+};
+
+export const setLastUsedDatasetId = (datasetId: string): SetLastUsedDatasetIdAction => ({
+ type: SET_LAST_USED_DATASET_ID,
+ payload: datasetId,
+});
+
+export const SET_LAST_USED_CONNECTION_ID = Symbol('controlDialog/SET_LAST_USED_CONNECTION_ID');
+export type SetLastUsedConnectionIdAction = {
+ type: typeof SET_LAST_USED_CONNECTION_ID;
+ payload: string;
+};
+
+export const setLastUsedConnectionId = (connectionId: string): SetLastUsedConnectionIdAction => ({
+ type: SET_LAST_USED_CONNECTION_ID,
+ payload: connectionId,
+});
+
+export const applyGroupControlDialog = ({
+ setItemData,
+ closeDialog,
+}: {
+ closeDialog: () => void;
+ setItemData: (newItemData: SetItemDataArgs) => void;
+}) => {
+ return (dispatch: AppDispatch, getState: () => DatalensGlobalState) => {
+ const state = getState();
+ const selectorsGroup = selectSelectorsGroup(state);
+ const activeSelectorIndex = selectActiveSelectorIndex(state);
+ const openedItemData = selectOpenedItemData(state);
+ const openedItemId = selectOpenedItemId(state);
+
+ let firstInvalidIndex: number | null = null;
+ const groupFieldNames: Record = {};
+ selectorsGroup.group.forEach((groupItem) => {
+ if (groupItem.fieldName) {
+ const itemName = groupItem.title;
+ if (groupFieldNames[groupItem.fieldName] && itemName) {
+ groupFieldNames[groupItem.fieldName].push(itemName);
+ }
+
+ if (!groupFieldNames[groupItem.fieldName] && itemName) {
+ groupFieldNames[groupItem.fieldName] = [itemName];
+ }
+ }
+ });
+
+ const validatedSelectorsGroup = Object.assign({}, selectorsGroup);
+
+ // check validation for every control
+ for (let i = 0; i < validatedSelectorsGroup.group.length; i += 1) {
+ const validation = getControlValidation(
+ validatedSelectorsGroup.group[i],
+ groupFieldNames,
+ );
+
+ if (!isEmpty(validation) && firstInvalidIndex === null) {
+ firstInvalidIndex = i;
+ }
+
+ validatedSelectorsGroup.group[i].validation = validation;
+ }
+
+ if (firstInvalidIndex !== null) {
+ const activeSelectorValidation =
+ validatedSelectorsGroup.group[activeSelectorIndex].validation;
+ dispatch(updateSelectorsGroup(validatedSelectorsGroup));
+
+ if (!isEmpty(activeSelectorValidation)) {
+ dispatch(
+ setSelectorDialogItem({
+ validation: activeSelectorValidation,
+ }),
+ );
+ return;
+ }
+ dispatch(setActiveSelectorIndex({activeSelectorIndex: firstInvalidIndex}));
+ return;
+ }
+
+ const isSingleControl = selectorsGroup.group.length === 1;
+ const autoHeight =
+ !isSingleControl ||
+ selectorsGroup.buttonApply ||
+ selectorsGroup.buttonReset ||
+ selectorsGroup.group[0].titlePlacement === TitlePlacementOption.Top
+ ? selectorsGroup.autoHeight
+ : false;
+ const updateControlsOnChange =
+ !isSingleControl && selectorsGroup.buttonApply
+ ? selectorsGroup.updateControlsOnChange
+ : false;
+
+ const data = {
+ autoHeight,
+ buttonApply: selectorsGroup.buttonApply,
+ buttonReset: selectorsGroup.buttonReset,
+ updateControlsOnChange,
+ group: selectorsGroup.group.map((selector) => {
+ let hasChangedSourceType = false;
+ if (openedItemId) {
+ const configSelectorItem = (
+ openedItemData as DashTabItemGroupControl['data']
+ ).group?.find(({id}) => id === selector.id);
+
+ // we check changing of sourceType only if selector was already saved and it's not the old one
+ hasChangedSourceType = configSelectorItem
+ ? configSelectorItem.sourceType !== selector.sourceType
+ : false;
+ }
+
+ return {
+ id: selector.id,
+ title: selector.title,
+ sourceType: selector.sourceType,
+ source: getItemDataSource(selector) as DashTabItemControlData['source'],
+ placementMode: isSingleControl ? 'auto' : selector.placementMode,
+ width: isSingleControl ? '' : selector.width,
+ defaults: getControlDefaultsForField(selector, hasChangedSourceType),
+ namespace: selector.namespace,
+ };
+ }),
+ };
+
+ const getExtendedItemData = getExtendedItemDataAction();
+ const itemData = dispatch(getExtendedItemData({data}));
+
+ setItemData({...itemData, type: DashTabItemType.GroupControl});
+ closeDialog();
+ };
+};
+
+export const copyControlToStorage = (controlIndex: number) => {
+ return (dispatch: AppDispatch, getState: () => DatalensGlobalState) => {
+ const state = getState();
+ const selectorsGroup = selectSelectorsGroup(state);
+ const activeSelectorIndex = selectActiveSelectorIndex(state);
+ const {
+ workbookId,
+ scope,
+ entryId,
+ namespace,
+ currentTabId: tabId,
+ } = selectOpenedItemMeta(state);
+ const validation = getControlValidation(selectorsGroup.group[controlIndex]);
+
+ if (!scope) {
+ return;
+ }
+
+ if (!isEmpty(validation)) {
+ if (activeSelectorIndex !== controlIndex) {
+ dispatch(setActiveSelectorIndex({activeSelectorIndex: controlIndex}));
+ }
+
+ dispatch(
+ setSelectorDialogItem({
+ validation,
+ }),
+ );
+
+ dispatch(
+ showToast({
+ type: 'danger',
+ title: dialogI18n('label_copy-invalid-control'),
+ }),
+ );
+
+ return;
+ }
+
+ // logic is copied from dashkit
+ const selectorToCopy = selectorsGroup.group[controlIndex];
+
+ const copiedItem = {
+ id: selectorToCopy.id,
+ title: selectorToCopy.title,
+ sourceType: selectorToCopy.sourceType,
+ source: getItemDataSource(selectorToCopy) as DashTabItemControlData['source'],
+ defaults: getControlDefaultsForField(selectorToCopy),
+ namespace: namespace || DEFAULT_NAMESPACE,
+ width: '',
+ placementMode: CONTROLS_PLACEMENT_MODE.AUTO,
+ };
+
+ const options: PreparedCopyItemOptions = {
+ timestamp: Date.now(),
+ data: {
+ ...getGroupSelectorDialogInitialState(),
+ group: [copiedItem as unknown as ConfigItemGroup],
+ },
+ type: DashTabItemType.GroupControl,
+ defaults: copiedItem.defaults,
+ namespace: copiedItem.namespace,
+ layout: DEFAULT_CONTROL_LAYOUT,
+ targetId: selectorToCopy.id,
+ };
+
+ const preparedOptions = getPreparedCopyItemOptions(options, null, {
+ workbookId: workbookId ?? null,
+ fromScope: scope,
+ targetDashTabId: tabId,
+ targetEntryId: entryId,
+ });
+
+ localStorage.setItem(COPIED_WIDGET_STORAGE_KEY, JSON.stringify(preparedOptions));
+ // https://stackoverflow.com/questions/35865481/storage-event-not-firing
+ window.dispatchEvent(new Event('storage'));
+ };
+};
+
+export const applyExternalControlDialog = ({
+ closeDialog,
+ setItemData,
+}: {
+ closeDialog: () => void;
+ setItemData: (newItemData: SetItemDataArgs) => void;
+}) => {
+ return (dispatch: AppDispatch, getState: () => DatalensGlobalState) => {
+ const state = getState();
+ const selectorDialog = selectSelectorDialog(state);
+ const {title, sourceType, autoHeight} = selectorDialog;
+
+ const validation = getControlValidation(selectorDialog);
+
+ if (!isEmpty(validation)) {
+ dispatch(
+ setSelectorDialogItem({
+ validation,
+ }),
+ );
+ return;
+ }
+
+ const hasChangedSourceType = selectIsControlSourceTypeHasChanged(state);
+ const defaults = getControlDefaultsForField(selectorDialog, hasChangedSourceType);
+
+ const data = {
+ title,
+ sourceType,
+ autoHeight,
+ source: getItemDataSource(selectorDialog),
+ };
+ const getExtendedItemData = getExtendedItemDataAction();
+ const itemData = dispatch(getExtendedItemData({data, defaults}));
+
+ setItemData({
+ data: itemData.data,
+ type: DashTabItemType.Control,
+ defaults: itemData.defaults,
+ });
+ closeDialog();
+ };
+};
+
+export const closeExternalControlDialog = ({closeDialog}: {closeDialog: () => void}) => {
+ return (dispatch: AppDispatch) => {
+ const beforeCloseDialogItem = getBeforeCloseDialogItemAction();
+ dispatch(beforeCloseDialogItem());
+ closeDialog();
+ };
+};
diff --git a/src/ui/store/constants/controlDialog.ts b/src/ui/store/constants/controlDialog.ts
index a26f9914a5..edaa165b20 100644
--- a/src/ui/store/constants/controlDialog.ts
+++ b/src/ui/store/constants/controlDialog.ts
@@ -36,3 +36,24 @@ export const DATEPICKER_OPERATIONS = [...BASE_EQUALITY_OPERATIONS, ...BASE_COMPA
export const DATEPICKER_RANGE_OPERATIONS = [
...BASE_DATE_OPERATIONS.map((item) => ({...item, range: true})),
];
+
+export const ELEMENT_TYPE: {
+ SELECT: 'select';
+ DATE: 'date';
+ INPUT: 'input';
+ CHECKBOX: 'checkbox';
+} = {
+ SELECT: 'select',
+ DATE: 'date',
+ INPUT: 'input',
+ CHECKBOX: 'checkbox',
+};
+
+export const DATE_FORMAT = 'DD.MM.YYYY';
+
+export const DATETIME_FORMAT = 'DD.MM.YYYY HH:mm:ss';
+
+export enum CheckboxControlValue {
+ TRUE = 'true',
+ FALSE = 'false',
+}
diff --git a/src/ui/store/reducers/controlDialog.ts b/src/ui/store/reducers/controlDialog.ts
index 8dfcb14f50..03240316ad 100644
--- a/src/ui/store/reducers/controlDialog.ts
+++ b/src/ui/store/reducers/controlDialog.ts
@@ -1,18 +1,64 @@
-import type {DashTabItemControlData, DashTabItemGroupControlData, StringParams} from 'shared';
+import type {
+ DashTabItem,
+ DashTabItemControlData,
+ DashTabItemGroupControlData,
+ EntryScope,
+ StringParams,
+} from 'shared';
import {DashTabItemControlSourceType, DashTabItemType, TitlePlacementOption} from 'shared';
import type {SelectorDialogState, SelectorsGroupDialogState} from '../typings/controlDialog';
import {getRandomKey} from 'ui/libs/DatalensChartkit/helpers/helpers';
-import {ELEMENT_TYPE} from 'units/dash/containers/Dialogs/Control/constants';
import {CONTROLS_PLACEMENT_MODE} from 'ui/constants/dialogs';
import {extractTypedQueryParams} from 'shared/modules/typed-query-api/helpers/parameters';
+import {
+ ADD_SELECTOR_TO_GROUP,
+ INIT_DIALOG,
+ RESET_DIALOG,
+ SET_ACTIVE_SELECTOR_INDEX,
+ SET_SELECTOR_DIALOG_ITEM,
+ UPDATE_SELECTORS_GROUP,
+ SET_LAST_USED_DATASET_ID,
+ SET_LAST_USED_CONNECTION_ID,
+} from '../actions/controlDialog';
+import type {
+ SetLastUsedDatasetIdAction,
+ SetLastUsedConnectionIdAction,
+ InitDialogAction,
+ ResetDialogAction,
+ SetSelectorDialogItemAction,
+ SetActiveSelectorIndexAction,
+ UpdateSelectorsGroupAction,
+ AddSelectorToGroupAction,
+} from '../actions/controlDialog';
+import {getActualUniqueFieldNameValidation, getInitialDefaultValue} from '../utils/controlDialog';
+import {I18n} from 'i18n';
+import {ELEMENT_TYPE} from '../constants/controlDialog';
+
+const i18n = I18n.keyset('dash.store.view');
+
+export type ControlDialogStateItemMeta = {
+ scope: EntryScope;
+ currentTabId: string | null;
+ entryId: string | null;
+ workbookId: string | null;
+ namespace: string | null;
+};
export interface ControlDialogState {
activeSelectorIndex: number;
selectorsGroup: SelectorsGroupDialogState;
selectorDialog: SelectorDialogState;
+ openedDialog: DashTabItemType | null;
+ openedItemId: string | null;
+ openedItemData: DashTabItem['data'] | null;
+ openedItemMeta: ControlDialogStateItemMeta | null;
+
+ lastUsedDatasetId?: string;
+ lastUsedConnectionId?: string;
}
export function getSelectorDialogInitialState(
args: {
+ title?: string | null;
lastUsedDatasetId?: string;
lastUsedConnectionId?: string;
openedDialog?: DashTabItemType.Control | DashTabItemType.GroupControl;
@@ -37,6 +83,7 @@ export function getSelectorDialogInitialState(
required: false,
showHint: false,
draftId: getRandomKey(),
+ ...(args.title ? {title: args.title} : {}),
};
}
@@ -52,16 +99,16 @@ export function getGroupSelectorDialogInitialState(): SelectorsGroupDialogState
export function getSelectorDialogFromData(
data: DashTabItemControlData & {source: {[key: string]: any}},
- defaults: StringParams = {},
+ defaults?: StringParams | null,
): SelectorDialogState {
let selectorParameters;
switch (data.sourceType) {
case DashTabItemControlSourceType.Connection:
- selectorParameters = extractTypedQueryParams(defaults, data.source.fieldName);
+ selectorParameters = extractTypedQueryParams(defaults ?? {}, data.source.fieldName);
break;
case DashTabItemControlSourceType.External:
- selectorParameters = defaults;
+ selectorParameters = defaults ?? {};
break;
default:
selectorParameters = {};
@@ -71,7 +118,7 @@ export function getSelectorDialogFromData(
validation: {},
isManualTitle: true,
- defaults,
+ defaults: defaults ?? {},
title: data.title,
sourceType: data.sourceType,
@@ -112,6 +159,7 @@ export function getSelectorDialogFromData(
export function getSelectorGroupDialogFromData(data: DashTabItemGroupControlData) {
return {
+ updateControlsOnChange: false,
...data,
group: data.group.map((item) => getSelectorDialogFromData(item)),
};
@@ -121,8 +169,238 @@ const getInitialState = (): ControlDialogState => ({
activeSelectorIndex: 0,
selectorsGroup: getGroupSelectorDialogInitialState(),
selectorDialog: getSelectorDialogInitialState(),
+ openedDialog: null,
+ openedItemId: null,
+ openedItemData: null,
+ openedItemMeta: null,
});
-export function controlDialog(state: ControlDialogState = getInitialState()): ControlDialogState {
- return state;
+// eslint-disable-next-line complexity
+export function controlDialog(
+ state: ControlDialogState = getInitialState(),
+ action:
+ | InitDialogAction
+ | ResetDialogAction
+ | SetSelectorDialogItemAction
+ | SetActiveSelectorIndexAction
+ | UpdateSelectorsGroupAction
+ | AddSelectorToGroupAction
+ | SetLastUsedDatasetIdAction
+ | SetLastUsedConnectionIdAction,
+): ControlDialogState {
+ const {type} = action;
+ switch (type) {
+ case INIT_DIALOG: {
+ const payload = action.payload;
+ const {id: openedItemId, type: openedDialog, data, defaults, openedItemMeta} = payload;
+
+ const newState = {
+ ...state,
+ openedItemData: {...data},
+ openedDialog,
+ openedItemId,
+ openedItemMeta,
+ activeSelectorIndex: 0,
+ };
+
+ if (
+ openedItemId === null &&
+ (openedDialog === DashTabItemType.Control ||
+ openedDialog === DashTabItemType.GroupControl)
+ ) {
+ newState.selectorDialog = getSelectorDialogInitialState({
+ title:
+ openedDialog === DashTabItemType.GroupControl
+ ? i18n('label_selector-dialog')
+ : null,
+ lastUsedDatasetId: state.lastUsedDatasetId,
+ lastUsedConnectionId: state.lastUsedConnectionId,
+ openedDialog,
+ });
+ newState.selectorsGroup = {
+ ...getGroupSelectorDialogInitialState(),
+ group: [newState.selectorDialog],
+ };
+ } else if (
+ openedDialog === DashTabItemType.Control &&
+ (data as unknown as DashTabItemControlData).sourceType !== 'external'
+ ) {
+ const selectorDialog = getSelectorDialogFromData(
+ data as unknown as DashTabItemControlData,
+ );
+
+ // migration forward to group
+ newState.openedDialog = DashTabItemType.GroupControl;
+ newState.selectorsGroup = {
+ ...getGroupSelectorDialogInitialState(),
+ group: [selectorDialog],
+ };
+ newState.selectorDialog = selectorDialog;
+ } else if (openedDialog === DashTabItemType.GroupControl) {
+ newState.selectorsGroup = getSelectorGroupDialogFromData(
+ data as unknown as DashTabItemGroupControlData,
+ );
+ newState.selectorDialog = newState.selectorsGroup.group[0];
+ } else if (openedDialog === DashTabItemType.Control) {
+ newState.selectorDialog = getSelectorDialogFromData(
+ data as unknown as DashTabItemControlData,
+ defaults,
+ );
+ }
+
+ return newState;
+ }
+
+ case SET_SELECTOR_DIALOG_ITEM: {
+ const {selectorDialog, selectorsGroup, activeSelectorIndex} = state;
+ const {payload} = action;
+
+ const elementTypeChanged =
+ payload.elementType && selectorDialog.elementType !== payload.elementType;
+ const defaultValue = elementTypeChanged
+ ? getInitialDefaultValue(payload.elementType!)
+ : selectorDialog.defaultValue;
+ const isElementTypeWithoutRequired =
+ elementTypeChanged && payload.elementType === ELEMENT_TYPE.CHECKBOX;
+ const required = isElementTypeWithoutRequired ? false : selectorDialog.required;
+
+ const validation: SelectorDialogState['validation'] = {
+ title:
+ selectorDialog.title === payload.title
+ ? selectorDialog.validation.title
+ : undefined,
+ uniqueFieldName:
+ selectorDialog.fieldName === payload.fieldName
+ ? getActualUniqueFieldNameValidation(
+ selectorsGroup.group,
+ payload.fieldName,
+ selectorDialog.validation.fieldName,
+ )
+ : undefined,
+ fieldName:
+ selectorDialog.fieldName === payload.fieldName
+ ? selectorDialog.validation.fieldName
+ : undefined,
+ datasetFieldId:
+ selectorDialog.datasetFieldId === payload.datasetFieldId
+ ? selectorDialog.validation.datasetFieldId
+ : undefined,
+ defaultValue:
+ !isElementTypeWithoutRequired &&
+ selectorDialog.defaultValue === payload.defaultValue
+ ? selectorDialog.validation.defaultValue
+ : undefined,
+ };
+
+ const newSelectorState = {
+ ...state.selectorDialog,
+ defaultValue,
+ validation,
+ required,
+ ...payload,
+ };
+
+ const newSelectorsGroupState = {
+ ...selectorsGroup,
+ };
+
+ if (state.selectorsGroup.group.length) {
+ newSelectorsGroupState.group = [...selectorsGroup.group];
+ newSelectorsGroupState.group[activeSelectorIndex] = newSelectorState;
+ }
+
+ return {
+ ...state,
+ selectorDialog: newSelectorState,
+ selectorsGroup: newSelectorsGroupState,
+ };
+ }
+
+ case ADD_SELECTOR_TO_GROUP: {
+ const {payload} = action;
+ const newSelector = getSelectorDialogInitialState(
+ state.lastUsedDatasetId
+ ? {
+ lastUsedDatasetId: state.lastUsedDatasetId,
+ }
+ : {},
+ );
+
+ // if current length is 1, the added selector will be the second so we enable autoHeight
+ const autoHeight =
+ state.selectorsGroup.group.length === 1 ? true : state.selectorsGroup.autoHeight;
+
+ return {
+ ...state,
+ selectorsGroup: {
+ ...state.selectorsGroup,
+ group: [...state.selectorsGroup.group, {...newSelector, title: payload.title}],
+ autoHeight,
+ },
+ };
+ }
+
+ case UPDATE_SELECTORS_GROUP: {
+ const {selectorsGroup} = state;
+ const {group, autoHeight, buttonApply, buttonReset, updateControlsOnChange} =
+ action.payload;
+
+ // if the number of selectors has increased from 1 to several, we enable autoHeight
+ const updatedAutoHeight =
+ selectorsGroup.group.length === 1 && group.length > 1 ? true : autoHeight;
+
+ return {
+ ...state,
+ selectorsGroup: {
+ ...selectorsGroup,
+ group,
+ autoHeight: updatedAutoHeight,
+ buttonApply,
+ buttonReset,
+ updateControlsOnChange,
+ },
+ };
+ }
+
+ case SET_ACTIVE_SELECTOR_INDEX: {
+ const newCurrentSelector =
+ state.selectorsGroup.group[action.payload.activeSelectorIndex];
+
+ return {
+ ...state,
+ activeSelectorIndex: action.payload.activeSelectorIndex,
+ selectorDialog: {
+ ...newCurrentSelector,
+ validation: {
+ ...newCurrentSelector.validation,
+ // check if validation with non-unique uniqueFieldName is still valid
+ uniqueFieldName: getActualUniqueFieldNameValidation(
+ state.selectorsGroup.group,
+ newCurrentSelector.fieldName,
+ newCurrentSelector.validation.uniqueFieldName,
+ ),
+ },
+ },
+ };
+ }
+
+ case RESET_DIALOG: {
+ return getInitialState();
+ }
+
+ case SET_LAST_USED_DATASET_ID:
+ return {
+ ...state,
+ lastUsedDatasetId: action.payload,
+ };
+
+ case SET_LAST_USED_CONNECTION_ID:
+ return {
+ ...state,
+ lastUsedConnectionId: action.payload,
+ };
+
+ default:
+ return state;
+ }
}
diff --git a/src/ui/store/reducers/index.tsx b/src/ui/store/reducers/index.tsx
index 26f82de96a..eb6bd7f03a 100644
--- a/src/ui/store/reducers/index.tsx
+++ b/src/ui/store/reducers/index.tsx
@@ -8,8 +8,8 @@ import colorPaletteEditor from 'store/reducers/colorPaletteEditor';
import {collectionsStructure} from 'store/reducers/collectionsStructure';
import {migrationToWorkbook} from 'ui/store/reducers/migrationToWorkbook';
import {copyEntriesToWorkbook} from 'ui/store/reducers/copyEntriesToWorkbook';
-
-import {editHistory} from './editHistory';
+import {editHistory} from 'ui/store/reducers/editHistory';
+import {controlDialog} from 'ui/store/reducers/controlDialog';
export default {
user,
@@ -20,6 +20,7 @@ export default {
entryContent,
colorPaletteEditor,
collectionsStructure,
+ controlDialog,
migrationToWorkbook,
copyEntriesToWorkbook,
editHistory,
diff --git a/src/ui/store/selectors/controlDialog.ts b/src/ui/store/selectors/controlDialog.ts
index d39e861ff1..ac9050cecd 100644
--- a/src/ui/store/selectors/controlDialog.ts
+++ b/src/ui/store/selectors/controlDialog.ts
@@ -2,7 +2,7 @@ import type {Operation} from 'components/DialogFilter/constants';
import {BOOLEAN_OPERATIONS} from 'components/DialogFilter/constants';
import {getAvailableOperations} from 'components/DialogFilter/utils';
import {getFilterOperations} from 'libs/datasetHelper';
-import type {Operations} from 'shared';
+import type {DashTabItemControlData, Operations} from 'shared';
import {DashTabItemControlSourceType, DATASET_FIELD_TYPES} from 'shared';
import type {DatalensGlobalState} from 'ui/index';
import {
@@ -14,38 +14,73 @@ import {
MULTISELECT_OPERATIONS,
SELECTOR_OPERATIONS,
} from '../constants/controlDialog';
+import {createSelector} from 'reselect';
-export const selectSelectorsGroup = (state: DatalensGlobalState) => state.dash.selectorsGroup;
+export const selectOpenedDialogType = (state: DatalensGlobalState) =>
+ state.controlDialog.openedDialog;
+
+export const selectControlDialogState = (state: DatalensGlobalState) => state.controlDialog;
+
+export const selectSelectorsGroup = (state: DatalensGlobalState) =>
+ selectControlDialogState(state).selectorsGroup;
+
+export const selectOpenedItemMeta = (state: DatalensGlobalState) =>
+ selectControlDialogState(state).openedItemMeta || {
+ scope: null,
+ workbookId: null,
+ entryId: null,
+ currentTabId: null,
+ namespace: null,
+ };
+
+export const selectOpenedItemData = (state: DatalensGlobalState) =>
+ selectControlDialogState(state).openedItemData || {};
+
+export const selectOpenedItemId = (state: DatalensGlobalState) =>
+ selectControlDialogState(state).openedItemId;
export const selectActiveSelectorIndex = (state: DatalensGlobalState) =>
- state.dash.activeSelectorIndex || 0;
+ selectControlDialogState(state).activeSelectorIndex || 0;
-export const selectSelectorDialog = (state: DatalensGlobalState) => state.dash.selectorDialog;
+export const selectSelectorDialog = (state: DatalensGlobalState) =>
+ selectControlDialogState(state).selectorDialog;
export const selectSelectorSourceType = (state: DatalensGlobalState) =>
- state.dash.selectorDialog.sourceType;
+ selectControlDialogState(state).selectorDialog.sourceType;
export const selectSelectorControlType = (state: DatalensGlobalState) =>
- state.dash.selectorDialog.elementType;
+ selectControlDialogState(state).selectorDialog.elementType;
export const selectSelectorDefaultValue = (state: DatalensGlobalState) =>
- state.dash.selectorDialog.defaultValue;
+ selectControlDialogState(state).selectorDialog.defaultValue;
export const selectSelectorRequired = (state: DatalensGlobalState) =>
- state.dash.selectorDialog.required;
+ selectControlDialogState(state).selectorDialog.required;
export const selectSelectorValidation = (state: DatalensGlobalState) =>
- state.dash.selectorDialog.validation;
+ selectControlDialogState(state).selectorDialog.validation;
export const getDatasetField = (state: DatalensGlobalState) => {
- const {dataset, datasetFieldId} = state.dash.selectorDialog;
+ const {dataset, datasetFieldId} = selectControlDialogState(state).selectorDialog;
return (dataset?.dataset?.result_schema || dataset?.result_schema || [])?.find(
(item) => item.guid === datasetFieldId,
);
};
+export const selectIsControlSourceTypeHasChanged = createSelector(
+ [selectOpenedItemData, selectSelectorSourceType],
+ (openedItemData, sourceType) => {
+ // New item
+ if (!openedItemData) {
+ return false;
+ }
+
+ return (openedItemData as DashTabItemControlData).sourceType !== sourceType;
+ },
+);
+
export const selectIsControlConfigurationDisabled = (state: DatalensGlobalState) => {
- const selectorDialog = state.dash.selectorDialog;
+ const selectorDialog = selectControlDialogState(state).selectorDialog;
switch (selectorDialog.sourceType) {
case DashTabItemControlSourceType.Dataset:
@@ -58,7 +93,8 @@ export const selectIsControlConfigurationDisabled = (state: DatalensGlobalState)
};
export const selectIsParametersSectionAvailable = (state: DatalensGlobalState): boolean => {
- const {sourceType, connectionId, connectionQueryTypes} = state.dash.selectorDialog;
+ const {sourceType, connectionId, connectionQueryTypes} =
+ selectControlDialogState(state).selectorDialog;
switch (sourceType) {
case DashTabItemControlSourceType.Connection:
@@ -73,7 +109,7 @@ export const selectIsParametersSectionAvailable = (state: DatalensGlobalState):
export const selectAvailableOperationsDict = (
state: DatalensGlobalState,
): Record | undefined => {
- const {dataset, datasetFieldId} = state.dash.selectorDialog;
+ const {dataset, datasetFieldId} = selectControlDialogState(state).selectorDialog;
if (!dataset) {
return undefined;
@@ -104,7 +140,7 @@ export const selectAvailableOperationsDict = (
export const selectInputOperations = (state: DatalensGlobalState) => {
const {multiselectable, isRange, elementType, fieldType, sourceType, datasetFieldId} =
- state.dash.selectorDialog;
+ selectControlDialogState(state).selectorDialog;
if (sourceType !== 'dataset' && elementType === 'checkbox') {
return BOOLEAN_OPERATIONS;
diff --git a/src/ui/store/utils/controlDialog.ts b/src/ui/store/utils/controlDialog.ts
index 31a51e2fe7..e57ec8669b 100644
--- a/src/ui/store/utils/controlDialog.ts
+++ b/src/ui/store/utils/controlDialog.ts
@@ -5,11 +5,13 @@ import type {
ItemDataSource,
SelectorDialogState,
SelectorDialogValidation,
+ SelectorElementType,
} from 'ui/store/typings/controlDialog';
import {validateParamTitleOnlyUnderscore} from 'ui/units/dash/components/ParamsSettings/helpers';
import {addOperationForValue} from 'ui/units/dash/modules/helpers';
-import {ELEMENT_TYPE} from 'units/dash/containers/Dialogs/Control/constants';
+import {CheckboxControlValue} from 'ui/store/constants/controlDialog';
import type {DashTab, DashTabItemGroupControl} from 'shared/types';
+import {ELEMENT_TYPE} from '../constants/controlDialog';
export const getActualUniqueFieldNameValidation = (
group: SelectorDialogState[],
@@ -286,3 +288,12 @@ export const getItemDataSource = (selectorDialog: SelectorDialogState): ItemData
return source;
};
+
+export const getInitialDefaultValue = (elementType: SelectorElementType) => {
+ switch (elementType) {
+ case ELEMENT_TYPE.CHECKBOX:
+ return CheckboxControlValue.FALSE;
+ default:
+ return undefined;
+ }
+};
diff --git a/src/ui/units/dash/containers/Dialogs/Control/constants.tsx b/src/ui/units/dash/containers/Dialogs/Control/constants.tsx
deleted file mode 100644
index f1440cd30d..0000000000
--- a/src/ui/units/dash/containers/Dialogs/Control/constants.tsx
+++ /dev/null
@@ -1,20 +0,0 @@
-export const ELEMENT_TYPE: {
- SELECT: 'select';
- DATE: 'date';
- INPUT: 'input';
- CHECKBOX: 'checkbox';
-} = {
- SELECT: 'select',
- DATE: 'date',
- INPUT: 'input',
- CHECKBOX: 'checkbox',
-};
-
-export enum CheckboxControlValue {
- TRUE = 'true',
- FALSE = 'false',
-}
-
-export const DATE_FORMAT = 'DD.MM.YYYY';
-
-export const DATETIME_FORMAT = 'DD.MM.YYYY HH:mm:ss';
diff --git a/src/ui/units/dash/containers/Dialogs/Dialogs.js b/src/ui/units/dash/containers/Dialogs/Dialogs.js
index 4130c9ca4d..4fe488a428 100644
--- a/src/ui/units/dash/containers/Dialogs/Dialogs.js
+++ b/src/ui/units/dash/containers/Dialogs/Dialogs.js
@@ -1,12 +1,10 @@
import React from 'react';
+import {DEFAULT_NAMESPACE} from '@gravity-ui/dashkit/helpers';
import {useDispatch, useSelector} from 'react-redux';
+import {EntryScope} from 'shared';
import {useEffectOnce} from 'ui';
-import DialogChartWidget from 'ui/components/DialogChartWidget/DialogChartWidget';
-import DialogExternalControl from 'ui/components/DialogExternalControl/DialogExternalControl';
-import {DialogGroupControl} from 'ui/components/DialogGroupControl/DialogGroupControl';
-import {DialogTextWidgetWrapper} from 'ui/components/DialogTextWidget';
-import DialogTitleWidget from 'ui/components/DialogTitleWidget/DialogTitleWidget';
+import {DialogEditItem, isDialogEditItemType} from 'ui/components/DialogEditItem/DialogEditItem';
import {registry} from 'ui/registry';
import {DIALOG_TYPE} from '../../../../constants/dialogs';
@@ -15,7 +13,9 @@ import {closeDialog} from '../../store/actions/dialogs/actions';
import {
selectCurrentTabId,
selectDashWorkbookId,
- selectIsDialogVisible,
+ selectEntryId,
+ selectNavigationPath,
+ selectOpenedItem,
selectOpenedItemData,
selectWidgetsCurrentTab,
} from '../../store/selectors/dashTypedSelectors';
@@ -30,24 +30,16 @@ import Tabs from './Tabs/Tabs';
export function Dialogs() {
const dispatch = useDispatch();
+ const entryId = useSelector(selectEntryId);
const openedDialog = useSelector((state) => state.dash.openedDialog);
const widgetType = useSelector((state) => state.dash.openedItemWidgetType);
const openedItemId = useSelector((state) => state.dash.openedItemId);
- const openedItemData = useSelector((state) => selectOpenedItemData(state));
- const currentTabId = useSelector((state) => selectCurrentTabId(state));
- const workbookId = useSelector((state) => selectDashWorkbookId(state));
- const widgetsCurrentTab = useSelector((state) => selectWidgetsCurrentTab(state));
- const navigationPath = useSelector((state) => state.dash.navigationPath);
-
- const dialogTextIsVisible = useSelector((state) =>
- selectIsDialogVisible(state, DIALOG_TYPE.TEXT),
- );
- const dialogTitleIsVisible = useSelector((state) =>
- selectIsDialogVisible(state, DIALOG_TYPE.TITLE),
- );
- const dialogChartIsVisible = useSelector((state) =>
- selectIsDialogVisible(state, DIALOG_TYPE.WIDGET),
- );
+ const openedItemData = useSelector(selectOpenedItemData);
+ const openedItem = useSelector(selectOpenedItem);
+ const currentTabId = useSelector(selectCurrentTabId);
+ const workbookId = useSelector(selectDashWorkbookId);
+ const widgetsCurrentTab = useSelector(selectWidgetsCurrentTab);
+ const navigationPath = useSelector(selectNavigationPath);
useEffectOnce(() => {
return () => {
@@ -55,60 +47,51 @@ export function Dialogs() {
};
});
+ const setItemDataHandle = React.useCallback(
+ (newItemData) => {
+ dispatch(setItemData(newItemData));
+ },
+ [dispatch],
+ );
+
+ const closeDialogHandle = React.useCallback(() => {
+ dispatch(closeDialog());
+ }, [dispatch]);
+
+ const changeNavigationPathHandle = React.useCallback(
+ (newNavigationPath) => {
+ dispatch(changeNavigationPath(newNavigationPath));
+ },
+ [dispatch],
+ );
+
+ if (openedDialog && isDialogEditItemType(openedDialog)) {
+ return (
+
+ );
+ }
+
switch (openedDialog) {
case DIALOG_TYPE.CONNECTIONS:
return ;
case DIALOG_TYPE.TABS:
return ;
- case DIALOG_TYPE.TITLE:
- return (
- dispatch(closeDialog())}
- setItemData={(newItemData) => {
- dispatch(setItemData(newItemData));
- }}
- />
- );
- case DIALOG_TYPE.TEXT: {
- return (
- dispatch(closeDialog())}
- setItemData={(newItemData) => {
- dispatch(setItemData(newItemData));
- }}
- />
- );
- }
- case DIALOG_TYPE.WIDGET:
- return (
- dispatch(closeDialog())}
- setItemData={(newItemData) => {
- dispatch(setItemData(newItemData));
- }}
- navigationPath={navigationPath}
- changeNavigationPath={(newNavigationPath) => {
- dispatch(changeNavigationPath(newNavigationPath));
- }}
- />
- );
- case DIALOG_TYPE.CONTROL:
- return ;
- case DIALOG_TYPE.GROUP_CONTROL:
- return ;
case DIALOG_TYPE.SETTINGS:
return ;
case DIALOG_TYPE.SELECT_STATE: {
diff --git a/src/ui/units/dash/store/actions/controls/actions.ts b/src/ui/units/dash/store/actions/controls/actions.ts
deleted file mode 100644
index f1a1d5deab..0000000000
--- a/src/ui/units/dash/store/actions/controls/actions.ts
+++ /dev/null
@@ -1,224 +0,0 @@
-import type {PreparedCopyItemOptions} from '@gravity-ui/dashkit';
-import {type ConfigItemGroup, DEFAULT_NAMESPACE} from '@gravity-ui/dashkit/helpers';
-import {I18n} from 'i18n';
-import isEmpty from 'lodash/isEmpty';
-import type {DashTabItemControlData, DashTabItemGroupControlData} from 'shared/types';
-import {DashTabItemType, TitlePlacementOption} from 'shared/types';
-import {DEFAULT_CONTROL_LAYOUT} from 'ui/components/DashKit/constants';
-import {COPIED_WIDGET_STORAGE_KEY, type DatalensGlobalState} from 'ui/index';
-import type {AppDispatch} from 'ui/store';
-import {
- setActiveSelectorIndex,
- setSelectorDialogItem,
- updateSelectorsGroup,
-} from 'ui/store/actions/controlDialog';
-import {showToast} from 'ui/store/actions/toaster';
-import {getGroupSelectorDialogInitialState} from 'ui/store/reducers/controlDialog';
-import {
- getControlDefaultsForField,
- getControlValidation,
- getItemDataSource,
-} from 'ui/store/utils/controlDialog';
-import type {CopiedConfigContext} from 'ui/units/dash/modules/helpers';
-import {getPreparedCopyItemOptions} from 'ui/units/dash/modules/helpers';
-
-import {CONTROLS_PLACEMENT_MODE} from '../../../../../constants/dialogs';
-import {
- selectCurrentTabId,
- selectOpenedItem,
- selectOpenedItemData,
-} from '../../selectors/dashTypedSelectors';
-import {setItemData} from '../dashTyped';
-import {closeDialog as closeDashDialog} from '../dialogs/actions';
-import {getExtendedItemDataAction} from '../helpers';
-
-const dialogI18n = I18n.keyset('dash.group-controls-dialog.edit');
-
-export const copyControlToStorage = (controlIndex: number) => {
- return (dispatch: AppDispatch, getState: () => DatalensGlobalState) => {
- const state = getState();
- const {
- selectorsGroup,
- activeSelectorIndex,
- entry: {workbookId, scope, entryId},
- } = state.dash;
- const openedItem = selectOpenedItem(state);
- const tabId = selectCurrentTabId(state);
-
- const validation = getControlValidation(selectorsGroup.group[controlIndex]);
-
- if (!isEmpty(validation)) {
- if (activeSelectorIndex !== controlIndex) {
- dispatch(setActiveSelectorIndex({activeSelectorIndex: controlIndex}));
- }
-
- dispatch(
- setSelectorDialogItem({
- validation,
- }),
- );
-
- dispatch(
- showToast({
- type: 'danger',
- title: dialogI18n('label_copy-invalid-control'),
- }),
- );
-
- return;
- }
-
- // logic is copied from dashkit
- const selectorToCopy = selectorsGroup.group[controlIndex];
-
- const copiedItem = {
- id: selectorToCopy.id,
- title: selectorToCopy.title,
- sourceType: selectorToCopy.sourceType,
- source: getItemDataSource(selectorToCopy) as DashTabItemControlData['source'],
- defaults: getControlDefaultsForField(selectorToCopy),
- namespace: openedItem?.namespace || DEFAULT_NAMESPACE,
- width: '',
- placementMode: CONTROLS_PLACEMENT_MODE.AUTO,
- };
-
- const options: PreparedCopyItemOptions = {
- timestamp: Date.now(),
- data: {
- ...getGroupSelectorDialogInitialState(),
- group: [copiedItem as unknown as ConfigItemGroup],
- },
- type: DashTabItemType.GroupControl,
- defaults: copiedItem.defaults,
- namespace: copiedItem.namespace,
- layout: DEFAULT_CONTROL_LAYOUT,
- targetId: selectorToCopy.id,
- };
-
- const preparedOptions = getPreparedCopyItemOptions(options, null, {
- workbookId: workbookId ?? null,
- fromScope: scope,
- targetDashTabId: tabId,
- targetEntryId: entryId,
- });
-
- localStorage.setItem(COPIED_WIDGET_STORAGE_KEY, JSON.stringify(preparedOptions));
- // https://stackoverflow.com/questions/35865481/storage-event-not-firing
- window.dispatchEvent(new Event('storage'));
- };
-};
-
-export const applyGroupControlDialog = () => {
- return (dispatch: AppDispatch, getState: () => DatalensGlobalState) => {
- const state = getState();
- const {selectorsGroup, openedItemId, activeSelectorIndex} = state.dash;
-
- let firstInvalidIndex: number | null = null;
- const groupFieldNames: Record = {};
- selectorsGroup.group.forEach((groupItem) => {
- if (groupItem.fieldName) {
- const itemName = groupItem.title;
- if (groupFieldNames[groupItem.fieldName] && itemName) {
- groupFieldNames[groupItem.fieldName].push(itemName);
- }
-
- if (!groupFieldNames[groupItem.fieldName] && itemName) {
- groupFieldNames[groupItem.fieldName] = [itemName];
- }
- }
- });
-
- const validatedSelectorsGroup = Object.assign({}, selectorsGroup);
-
- // check validation for every control
- for (let i = 0; i < validatedSelectorsGroup.group.length; i += 1) {
- const validation = getControlValidation(
- validatedSelectorsGroup.group[i],
- groupFieldNames,
- );
-
- if (!isEmpty(validation) && firstInvalidIndex === null) {
- firstInvalidIndex = i;
- }
-
- validatedSelectorsGroup.group[i].validation = validation;
- }
-
- if (firstInvalidIndex !== null) {
- const activeSelectorValidation =
- validatedSelectorsGroup.group[activeSelectorIndex].validation;
- dispatch(updateSelectorsGroup(validatedSelectorsGroup));
-
- if (!isEmpty(activeSelectorValidation)) {
- dispatch(
- setSelectorDialogItem({
- validation: activeSelectorValidation,
- }),
- );
- return;
- }
- dispatch(setActiveSelectorIndex({activeSelectorIndex: firstInvalidIndex}));
- return;
- }
-
- const isSingleControl = selectorsGroup.group.length === 1;
- const autoHeight =
- !isSingleControl ||
- selectorsGroup.buttonApply ||
- selectorsGroup.buttonReset ||
- selectorsGroup.group[0].titlePlacement === TitlePlacementOption.Top
- ? selectorsGroup.autoHeight
- : false;
- const updateControlsOnChange =
- !isSingleControl && selectorsGroup.buttonApply
- ? selectorsGroup.updateControlsOnChange
- : false;
-
- const data = {
- autoHeight,
- buttonApply: selectorsGroup.buttonApply,
- buttonReset: selectorsGroup.buttonReset,
- updateControlsOnChange,
- group: selectorsGroup.group.map((selector) => {
- let hasChangedSourceType = false;
- if (openedItemId) {
- const openedItemData = selectOpenedItemData(
- state,
- ) as DashTabItemGroupControlData;
-
- const configSelectorItem = openedItemData.group?.find(
- ({id}) => id === selector.id,
- );
-
- // we check changing of sourceType only if selector was already saved and it's not the old one
- hasChangedSourceType = configSelectorItem
- ? configSelectorItem.sourceType !== selector.sourceType
- : false;
- }
-
- return {
- id: selector.id,
- title: selector.title,
- sourceType: selector.sourceType,
- source: getItemDataSource(selector) as DashTabItemControlData['source'],
- placementMode: isSingleControl ? 'auto' : selector.placementMode,
- width: isSingleControl ? '' : selector.width,
- defaults: getControlDefaultsForField(selector, hasChangedSourceType),
- namespace: selector.namespace,
- };
- }),
- };
-
- const getExtendedItemData = getExtendedItemDataAction();
- const itemData = dispatch(getExtendedItemData({data}));
-
- dispatch(
- setItemData({
- data: itemData.data,
- type: DashTabItemType.GroupControl,
- }),
- );
-
- dispatch(closeDashDialog());
- };
-};
diff --git a/src/ui/units/dash/store/actions/dashTyped.ts b/src/ui/units/dash/store/actions/dashTyped.ts
index 3ae4be2c82..0961088a5e 100644
--- a/src/ui/units/dash/store/actions/dashTyped.ts
+++ b/src/ui/units/dash/store/actions/dashTyped.ts
@@ -19,19 +19,15 @@ import type {
DashSettings,
DashTab,
DashTabItem,
+ DashTabItemControl,
+ DashTabItemGroupControl,
DashTabItemImage,
DashTabItemWidget,
RecursivePartial,
} from 'shared';
-import {DashTabItemType, EntryScope, EntryUpdateMode} from 'shared';
+import {EntryScope, EntryUpdateMode} from 'shared';
import type {AppDispatch} from 'ui/store';
-import {setSelectorDialogItem} from 'ui/store/actions/controlDialog';
-import type {ItemDataSource, SelectorDialogState} from 'ui/store/typings/controlDialog';
-import {
- getControlDefaultsForField,
- getControlValidation,
- getItemDataSource,
-} from 'ui/store/utils/controlDialog';
+import type {ItemDataSource} from 'ui/store/typings/controlDialog';
import {getLoginOrIdFromLockedError, isEntryIsLockedError} from 'utils/errors/errorByCode';
import {setLockedTextInfo} from '../../../../components/RevisionsPanel/RevisionsPanel';
@@ -55,16 +51,10 @@ import {
selectDashData,
selectDashEntry,
selectEntryId,
- selectIsControlSourceTypeHasChanged,
} from '../selectors/dashTypedSelectors';
import {save} from './base/actions';
-import {closeDialog as closeDashDialog} from './dialogs/actions';
-import {
- getBeforeCloseDialogItemAction,
- getExtendedItemDataAction,
- migrateDataSettings,
-} from './helpers';
+import {migrateDataSettings} from './helpers';
import type {DashDispatch} from './index';
@@ -324,28 +314,6 @@ export const toggleTableOfContent = (expanded?: boolean): ToggleTableOfContentAc
payload: expanded,
});
-export const SET_LAST_USED_DATASET_ID = Symbol('dash/SET_LAST_USED_DATASET_ID');
-export type SetLastUsedDatasetIdAction = {
- type: typeof SET_LAST_USED_DATASET_ID;
- payload: string;
-};
-
-export const setLastUsedDatasetId = (datasetId: string): SetLastUsedDatasetIdAction => ({
- type: SET_LAST_USED_DATASET_ID,
- payload: datasetId,
-});
-
-export const SET_LAST_USED_CONNECTION_ID = Symbol('dash/SET_LAST_USED_CONNECTION_ID');
-export type SetLastUsedConnectionIdAction = {
- type: typeof SET_LAST_USED_CONNECTION_ID;
- payload: string;
-};
-
-export const setLastUsedConnectionId = (connectionId: string): SetLastUsedConnectionIdAction => ({
- type: SET_LAST_USED_CONNECTION_ID,
- payload: connectionId,
-});
-
type SetItemDataBase = {
title?: string;
sourceType?: string;
@@ -354,11 +322,13 @@ type SetItemDataBase = {
};
export type SetItemDataText = RecursivePartial & SetItemDataBase;
export type SetItemDataTitle = RecursivePartial & SetItemDataBase;
+export type SetItemDataGroupControl = Partial & SetItemDataBase;
+export type SetItemDataExternalControl = Partial & SetItemDataBase;
export type SetItemDataImage = DashTabItemImage['data'];
export type SetItemDataDefaults = Record;
export type SetItemDataArgs = {
- data: SetItemDataText | SetItemDataTitle | SetItemDataImage;
+ data: SetItemDataText | SetItemDataTitle | SetItemDataImage | SetItemDataGroupControl;
defaults?: SetItemDataDefaults;
type?: string;
namespace?: string;
@@ -380,47 +350,6 @@ export const setItemData = (data: SetItemDataArgs) => {
};
};
-export const applyControl2Dialog = () => {
- return (dispatch: AppDispatch, getState: () => DatalensGlobalState) => {
- const state = getState();
- const selectorDialog = state.dash.selectorDialog as SelectorDialogState;
- const {title, sourceType, autoHeight} = selectorDialog;
-
- const validation = getControlValidation(selectorDialog);
-
- if (!isEmpty(validation)) {
- dispatch(
- setSelectorDialogItem({
- validation,
- }),
- );
- return;
- }
-
- const hasChangedSourceType = selectIsControlSourceTypeHasChanged(state);
- const defaults = getControlDefaultsForField(selectorDialog, hasChangedSourceType);
-
- const data = {
- title,
- sourceType,
- autoHeight,
- source: getItemDataSource(selectorDialog),
- };
- const getExtendedItemData = getExtendedItemDataAction();
- const itemData = dispatch(getExtendedItemData({data, defaults}));
-
- dispatch(
- setItemData({
- data: itemData.data,
- type: DashTabItemType.Control,
- defaults: itemData.defaults,
- }),
- );
-
- dispatch(closeDashDialog());
- };
-};
-
export const SET_DASH_VIEW_MODE = Symbol('dash/SET_DASH_VIEW_MODE');
export type SetViewModeAction = {
type: typeof SET_DASH_VIEW_MODE;
@@ -927,14 +856,6 @@ export const setWidgetCurrentTab = (
payload,
});
-export const closeControl2Dialog = () => {
- return (dispatch: AppDispatch) => {
- const beforeCloseDialogItem = getBeforeCloseDialogItemAction();
- dispatch(beforeCloseDialogItem());
- dispatch(closeDashDialog());
- };
-};
-
export function updateDeprecatedDashConfig() {
return async (dispatch: DashDispatch, getState: () => DatalensGlobalState) => {
const data = selectDashData(getState());
diff --git a/src/ui/units/dash/store/actions/index.ts b/src/ui/units/dash/store/actions/index.ts
index 1eb31a38ff..c9ed11ad82 100644
--- a/src/ui/units/dash/store/actions/index.ts
+++ b/src/ui/units/dash/store/actions/index.ts
@@ -23,8 +23,6 @@ import type {
SetHashStateAction,
SetInitialPageTabsItemsAction,
SetItemDataAction,
- SetLastUsedConnectionIdAction,
- SetLastUsedDatasetIdAction,
SetLoadingEditModeAction,
SetPageDefaultTabItemsAction,
SetPageTabAction,
@@ -55,7 +53,6 @@ export type DashAction =
| SetStateHashIdAction
| SetErrorModeAction
| ToggleTableOfContentAction
- | SetLastUsedDatasetIdAction
| SetSelectorDialogItemAction
| AddSelectorToGroupAction
| UpdateSelectorsGroupAction
@@ -79,7 +76,6 @@ export type DashAction =
| CloseDialogAction
| SaveDashSuccessAction
| SaveDashErrorAction
- | SetLastUsedConnectionIdAction
| SetSettingsAction;
export type DashDispatch = ThunkDispatch;
diff --git a/src/ui/units/dash/store/reducers/dash.js b/src/ui/units/dash/store/reducers/dash.js
index 7d9b283ae9..656690acec 100644
--- a/src/ui/units/dash/store/reducers/dash.js
+++ b/src/ui/units/dash/store/reducers/dash.js
@@ -1,18 +1,10 @@
import {DashKit} from '@gravity-ui/dashkit';
import {generateUniqId} from '@gravity-ui/dashkit/helpers';
-import {I18n} from 'i18n';
import update from 'immutability-helper';
import pick from 'lodash/pick';
-import {DashTabItemType, Feature} from 'shared';
-import {
- getGroupSelectorDialogInitialState,
- getSelectorDialogFromData,
- getSelectorDialogInitialState,
- getSelectorGroupDialogFromData,
-} from 'ui/store/reducers/controlDialog';
+import {DashTabItemType} from 'shared';
import {migrateConnectionsForGroupControl} from 'ui/store/utils/controlDialog';
import {getUpdatedConnections} from 'ui/utils/copyItems';
-import Utils from 'utils';
import {EMBEDDED_MODE} from '../../../../constants/embedded';
import {Mode} from '../../modules/constants';
@@ -21,8 +13,6 @@ import * as actionTypes from '../constants/dashActionTypes';
import {dashTypedReducer} from './dashTypedReducer';
-const i18n = I18n.keyset('dash.store.view');
-
export const TAB_PROPERTIES = [
'id',
'title',
@@ -63,11 +53,6 @@ const initialState = {
error: null,
isFullscreenMode: new URLSearchParams(window.location.search).get('mode') === EMBEDDED_MODE.TV,
- lastUsedDatasetId: null,
-
- selectorDialog: getSelectorDialogInitialState(),
- selectorsGroup: getGroupSelectorDialogInitialState(),
- activeSelectorIndex: 0,
skipReload: false,
@@ -87,40 +72,23 @@ function dash(state = initialState, action) {
case actionTypes.CLOSE_DIALOG: {
return {
...state,
- selectorsGroup: getGroupSelectorDialogInitialState(),
...action.payload,
dragOperationProps: null,
};
}
- case actionTypes.OPEN_DIALOG: {
- const selectorGroup = getGroupSelectorDialogInitialState();
- const selectorDialog =
- action.payload?.openedDialog === DashTabItemType.Control ||
- action.payload?.openedDialog === DashTabItemType.GroupControl
- ? getSelectorDialogInitialState({
- lastUsedDatasetId: state.lastUsedDatasetId,
- lastUsedConnectionId: state.lastUsedConnectionId,
- openedDialog: action.payload?.openedDialog,
- })
- : state.selectorDialog;
-
- if (
- Utils.isEnabledFeature(Feature.GroupControls) &&
- action.payload?.openedDialog === DashTabItemType.GroupControl
- ) {
- // TODO: move to getSelectorDialogInitialState after the release of the feature
- selectorDialog.title = i18n('label_selector-dialog');
- }
+ case actionTypes.OPEN_ITEM_DIALOG: {
+ const payload = action.payload;
+ return {
+ ...state,
+ openedItemId: payload.id ?? null,
+ openedDialog: payload.type,
+ };
+ }
+ case actionTypes.OPEN_DIALOG: {
return {
...state,
...action.payload,
- selectorDialog,
- selectorsGroup: {
- ...selectorGroup,
- group: [selectorDialog],
- },
- activeSelectorIndex: 0,
dragOperationProps: action.payload.dragOperationProps ?? null,
};
}
@@ -306,8 +274,6 @@ function dash(state = initialState, action) {
type: action.payload.type || state.openedDialog,
data: action.payload.data,
namespace: action.payload.namespace,
- operation: action.payload.operation,
- layout: state.dragOperationProps?.itemLayout,
...(action.payload.defaults ? {defaults: action.payload.defaults} : null),
},
config: {...tab, salt: data.salt, counter: data.counter},
@@ -342,42 +308,6 @@ function dash(state = initialState, action) {
}),
};
}
- case actionTypes.OPEN_ITEM_DIALOG: {
- const payload = action.payload;
- const {id: openedItemId, data, defaults} = payload;
- let {type: openedDialog} = tab.items.find(({id}) => id === openedItemId);
-
- const newState = {
- ...state,
- openedItemId,
- activeSelectorIndex: 0,
- };
-
- if (
- Utils.isEnabledFeature(Feature.GroupControls) &&
- openedDialog === DashTabItemType.Control &&
- data.sourceType !== 'external'
- ) {
- const selectorDialog = getSelectorDialogFromData(data);
-
- // migration forward to group
- openedDialog = DashTabItemType.GroupControl;
- newState.selectorsGroup = {
- ...getGroupSelectorDialogInitialState(),
- group: [selectorDialog],
- };
- newState.selectorDialog = selectorDialog;
- } else if (openedDialog === DashTabItemType.GroupControl) {
- newState.selectorsGroup = getSelectorGroupDialogFromData(data);
- newState.selectorDialog = newState.selectorsGroup.group[0];
- } else if (openedDialog === DashTabItemType.Control) {
- newState.selectorDialog = getSelectorDialogFromData(data, defaults);
- }
-
- newState.openedDialog = openedDialog;
-
- return newState;
- }
default:
return dashTypedReducer(state, action);
diff --git a/src/ui/units/dash/store/reducers/dashTypedReducer.ts b/src/ui/units/dash/store/reducers/dashTypedReducer.ts
index d8f9b38109..5688f69c2f 100644
--- a/src/ui/units/dash/store/reducers/dashTypedReducer.ts
+++ b/src/ui/units/dash/store/reducers/dashTypedReducer.ts
@@ -4,17 +4,7 @@ import type {DashKit} from '@gravity-ui/dashkit';
import update from 'immutability-helper';
import {cloneDeep, pick} from 'lodash';
import type {DashData, DashDragOptions, DashEntry, Permissions, WidgetType} from 'shared';
-import {
- ADD_SELECTOR_TO_GROUP,
- SET_ACTIVE_SELECTOR_INDEX,
- SET_SELECTOR_DIALOG_ITEM,
- UPDATE_SELECTORS_GROUP,
-} from 'ui/store/actions/controlDialog';
-import {getSelectorDialogInitialState} from 'ui/store/reducers/controlDialog';
-import type {SelectorDialogState, SelectorsGroupDialogState} from 'ui/store/typings/controlDialog';
-import {getActualUniqueFieldNameValidation} from 'ui/store/utils/controlDialog';
-
-import {ELEMENT_TYPE} from '../../containers/Dialogs/Control/constants';
+
import {Mode} from '../../modules/constants';
import type {DashUpdateStatus} from '../../typings/dash';
import type {TabsHashStates} from '../actions/dashTyped';
@@ -32,8 +22,6 @@ import {
SET_ERROR_MODE,
SET_HASH_STATE,
SET_INITIAL_PAGE_TABS_ITEMS,
- SET_LAST_USED_CONNECTION_ID,
- SET_LAST_USED_DATASET_ID,
SET_LOADING_EDIT_MODE,
SET_PAGE_DEFAULT_TAB_ITEMS,
SET_PAGE_TAB,
@@ -47,7 +35,6 @@ import {
} from '../actions/dashTyped';
import type {DashAction} from '../actions/index';
import {SET_NEW_RELATIONS} from '../constants/dashActionTypes';
-import {getInitialDefaultValue} from '../utils';
import {TAB_PROPERTIES} from './dash';
@@ -64,7 +51,6 @@ export type DashState = {
openedDialog: null; // TODO: clarify types
openedItemId: string | null;
showTableOfContent: boolean;
- lastUsedDatasetId: null | string;
lastUsedConnectionId: undefined | string;
entry: DashEntry;
data: DashData;
@@ -73,9 +59,6 @@ export type DashState = {
permissions?: Permissions;
lockToken: string | null;
isFullscreenMode?: boolean;
- selectorDialog: SelectorDialogState;
- selectorsGroup: SelectorsGroupDialogState;
- activeSelectorIndex: number;
isLoadingEditMode: boolean;
isNewRelationsOpened?: boolean;
skipReload?: boolean;
@@ -231,151 +214,6 @@ export function dashTypedReducer(
action.payload === undefined ? !state.showTableOfContent : action.payload,
};
- case SET_LAST_USED_DATASET_ID:
- return {
- ...state,
- lastUsedDatasetId: action.payload,
- };
-
- case SET_LAST_USED_CONNECTION_ID:
- return {
- ...state,
- lastUsedConnectionId: action.payload,
- };
-
- case SET_SELECTOR_DIALOG_ITEM: {
- const {selectorDialog, selectorsGroup, activeSelectorIndex} = state;
- const {payload} = action;
-
- const elementTypeChanged =
- payload.elementType && selectorDialog.elementType !== payload.elementType;
- const defaultValue = elementTypeChanged
- ? getInitialDefaultValue(payload.elementType!)
- : selectorDialog.defaultValue;
- const isElementTypeWithoutRequired =
- elementTypeChanged && payload.elementType === ELEMENT_TYPE.CHECKBOX;
- const required = isElementTypeWithoutRequired ? false : selectorDialog.required;
-
- const validation: SelectorDialogState['validation'] = {
- title:
- selectorDialog.title === payload.title
- ? selectorDialog.validation.title
- : undefined,
- uniqueFieldName:
- selectorDialog.fieldName === payload.fieldName
- ? getActualUniqueFieldNameValidation(
- selectorsGroup.group,
- payload.fieldName,
- selectorDialog.validation.fieldName,
- )
- : undefined,
- fieldName:
- selectorDialog.fieldName === payload.fieldName
- ? selectorDialog.validation.fieldName
- : undefined,
- datasetFieldId:
- selectorDialog.datasetFieldId === payload.datasetFieldId
- ? selectorDialog.validation.datasetFieldId
- : undefined,
- defaultValue:
- !isElementTypeWithoutRequired &&
- selectorDialog.defaultValue === payload.defaultValue
- ? selectorDialog.validation.defaultValue
- : undefined,
- };
-
- const newSelectorState = {
- ...state.selectorDialog,
- defaultValue,
- validation,
- required,
- ...payload,
- };
-
- const newSelectorsGroupState = {
- ...selectorsGroup,
- };
-
- if (state.selectorsGroup.group.length) {
- newSelectorsGroupState.group = [...selectorsGroup.group];
- newSelectorsGroupState.group[activeSelectorIndex] = newSelectorState;
- }
-
- return {
- ...state,
- selectorDialog: newSelectorState,
- selectorsGroup: newSelectorsGroupState,
- };
- }
-
- case ADD_SELECTOR_TO_GROUP: {
- const {payload} = action;
- const newSelector = getSelectorDialogInitialState(
- state.lastUsedDatasetId
- ? {
- lastUsedDatasetId: state.lastUsedDatasetId,
- }
- : {},
- );
-
- // if current length is 1, the added selector will be the second so we enable autoHeight
- const autoHeight =
- state.selectorsGroup.group.length === 1 ? true : state.selectorsGroup.autoHeight;
-
- return {
- ...state,
- selectorsGroup: {
- ...state.selectorsGroup,
- group: [...state.selectorsGroup.group, {...newSelector, title: payload.title}],
- autoHeight,
- },
- };
- }
-
- case UPDATE_SELECTORS_GROUP: {
- const {selectorsGroup} = state;
- const {group, autoHeight, buttonApply, buttonReset, updateControlsOnChange} =
- action.payload;
-
- // if the number of selectors has increased from 1 to several, we enable autoHeight
- const updatedAutoHeight =
- selectorsGroup.group.length === 1 && group.length > 1 ? true : autoHeight;
-
- return {
- ...state,
- selectorsGroup: {
- ...selectorsGroup,
- group,
- autoHeight: updatedAutoHeight,
- buttonApply,
- buttonReset,
- updateControlsOnChange,
- },
- };
- }
-
- case SET_ACTIVE_SELECTOR_INDEX: {
- const newCurrentSelector =
- state.selectorsGroup.group[action.payload.activeSelectorIndex];
-
- return {
- ...state,
- activeSelectorIndex: action.payload.activeSelectorIndex,
- selectorDialog: {
- ...newCurrentSelector,
- validation: {
- ...newCurrentSelector.validation,
- // check if validation with non-unique uniqueFieldName is still valid
- uniqueFieldName: getActualUniqueFieldNameValidation(
- state.selectorsGroup.group,
- newCurrentSelector.fieldName,
- newCurrentSelector.validation.uniqueFieldName,
- ),
- },
- },
- };
- }
-
case SET_DASH_VIEW_MODE: {
const entryData = state.convertedEntryData || state.entry.data;
diff --git a/src/ui/units/dash/store/selectors/dashTypedSelectors.ts b/src/ui/units/dash/store/selectors/dashTypedSelectors.ts
index d0c6ff8ace..159b2ad493 100644
--- a/src/ui/units/dash/store/selectors/dashTypedSelectors.ts
+++ b/src/ui/units/dash/store/selectors/dashTypedSelectors.ts
@@ -1,13 +1,7 @@
import type {DatalensGlobalState} from 'index';
import isEqual from 'lodash/isEqual';
import {createSelector} from 'reselect';
-import type {
- DashTabItem,
- DashTabItemControlData,
- DashTabItemWidget,
- DashTabItemWidgetTab,
-} from 'shared';
-import {selectSelectorSourceType} from 'ui/store/selectors/controlDialog';
+import type {DashTabItem, DashTabItemWidget, DashTabItemWidgetTab} from 'shared';
import {ITEM_TYPE} from '../../../../constants/dialogs';
import {isOrderIdsChanged} from '../../containers/Dialogs/Tabs/PopupWidgetsOrder/helpers';
@@ -186,18 +180,6 @@ export const selectOpenedItem = createSelector(
},
);
-export const selectIsControlSourceTypeHasChanged = createSelector(
- [selectOpenedItemData, selectSelectorSourceType],
- (openedItemData, sourceType) => {
- // New item
- if (!openedItemData) {
- return false;
- }
-
- return (openedItemData as DashTabItemControlData).sourceType !== sourceType;
- },
-);
-
export const selectCurrentTabConnectableItems = createSelector([selectCurrentTab], (currentTab) => {
if (!currentTab) {
return undefined;
@@ -264,4 +246,4 @@ export const selectCurrentTabAliases = createSelector(
(currentTab) => currentTab?.aliases || null,
);
-export const selectNavigationPath = (state: DatalensGlobalState) => state.dash.navigationPath;
+export const selectNavigationPath = (state: DatalensGlobalState) => state.dash?.navigationPath;
diff --git a/src/ui/units/dash/store/utils.ts b/src/ui/units/dash/store/utils.ts
index 71355ad379..d483c1ed01 100644
--- a/src/ui/units/dash/store/utils.ts
+++ b/src/ui/units/dash/store/utils.ts
@@ -3,10 +3,8 @@ import {I18n} from 'i18n';
import type {FakeDashData} from 'shared/types/dash';
import {DashLoadPriority} from 'shared/types/dash';
import {DL, URL_QUERY} from 'ui/constants';
-import type {SelectorElementType} from 'ui/store/typings/controlDialog';
import Utils from 'ui/utils';
-import {CheckboxControlValue, ELEMENT_TYPE} from '../containers/Dialogs/Control/constants';
import {Mode} from '../modules/constants';
const storeI18n = I18n.keyset('dash.store.view');
@@ -72,12 +70,3 @@ export const getFakeDashEntry = (workbookId?: string) => {
navigationPath: Utils.getNavigationPathFromKey(initialKey),
};
};
-
-export const getInitialDefaultValue = (elementType: SelectorElementType) => {
- switch (elementType) {
- case ELEMENT_TYPE.CHECKBOX:
- return CheckboxControlValue.FALSE;
- default:
- return undefined;
- }
-};