2021-11-23 06:05:01 +00:00
|
|
|
import React, { useCallback, useEffect, useState, Component } from "react";
|
|
|
|
|
import { AppState } from "reducers";
|
|
|
|
|
import { connect } from "react-redux";
|
|
|
|
|
import { Placement } from "popper.js";
|
|
|
|
|
import * as Sentry from "@sentry/react";
|
|
|
|
|
import _ from "lodash";
|
2021-02-16 10:29:08 +00:00
|
|
|
import BaseControl, { ControlProps } from "./BaseControl";
|
|
|
|
|
import {
|
|
|
|
|
StyledDragIcon,
|
|
|
|
|
StyledEditIcon,
|
|
|
|
|
StyledDeleteIcon,
|
|
|
|
|
StyledVisibleIcon,
|
|
|
|
|
StyledHiddenIcon,
|
|
|
|
|
StyledPropertyPaneButton,
|
2021-11-02 04:33:21 +00:00
|
|
|
StyledOptionControlInputGroup,
|
2021-02-16 10:29:08 +00:00
|
|
|
} from "./StyledControls";
|
|
|
|
|
import styled from "constants/DefaultTheme";
|
2021-11-23 06:05:01 +00:00
|
|
|
import { Indices } from "constants/Layers";
|
2021-03-15 12:17:56 +00:00
|
|
|
import { DroppableComponent } from "components/ads/DraggableListComponent";
|
2021-11-23 06:05:01 +00:00
|
|
|
import { Size, Category } from "components/ads/Button";
|
2021-02-16 10:29:08 +00:00
|
|
|
import EmptyDataState from "components/utils/EmptyDataState";
|
2021-11-23 06:05:01 +00:00
|
|
|
import EvaluatedValuePopup from "components/editorComponents/CodeEditor/EvaluatedValuePopup";
|
|
|
|
|
import { EditorTheme } from "components/editorComponents/CodeEditor/EditorConfig";
|
|
|
|
|
import { CodeEditorExpected } from "components/editorComponents/CodeEditor";
|
|
|
|
|
import { ColumnProperties } from "widgets/TableWidget/component/Constants";
|
2021-02-16 10:29:08 +00:00
|
|
|
import {
|
|
|
|
|
getDefaultColumnProperties,
|
|
|
|
|
getTableStyles,
|
2021-09-09 15:10:22 +00:00
|
|
|
} from "widgets/TableWidget/component/TableUtilities";
|
|
|
|
|
import { reorderColumns } from "widgets/TableWidget/component/TableHelpers";
|
2021-11-23 06:05:01 +00:00
|
|
|
import { DataTree } from "entities/DataTree/dataTreeFactory";
|
|
|
|
|
import { getDataTreeForAutocomplete } from "selectors/dataTreeSelectors";
|
|
|
|
|
import {
|
|
|
|
|
EvaluationError,
|
|
|
|
|
getEvalErrorPath,
|
|
|
|
|
getEvalValuePath,
|
|
|
|
|
PropertyEvaluationErrorType,
|
|
|
|
|
} from "utils/DynamicBindingUtils";
|
|
|
|
|
import { getNextEntityName } from "utils/AppsmithUtils";
|
|
|
|
|
import { Colors } from "constants/Colors";
|
|
|
|
|
import { noop } from "utils/AppsmithUtils";
|
2021-02-16 10:29:08 +00:00
|
|
|
|
|
|
|
|
const ItemWrapper = styled.div`
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: flex-start;
|
|
|
|
|
align-items: center;
|
2021-11-23 06:05:01 +00:00
|
|
|
&.has-duplicate-label > div:nth-child(2) {
|
|
|
|
|
border: 1px solid ${Colors.DANGER_SOLID};
|
|
|
|
|
}
|
2021-02-16 10:29:08 +00:00
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
const TabsWrapper = styled.div`
|
|
|
|
|
width: 100%;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
const AddColumnButton = styled(StyledPropertyPaneButton)`
|
|
|
|
|
width: 100%;
|
2021-03-15 12:17:56 +00:00
|
|
|
display: flex;
|
|
|
|
|
justify-content: center;
|
2021-02-16 10:29:08 +00:00
|
|
|
&&&& {
|
|
|
|
|
margin-top: 12px;
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
}
|
|
|
|
|
`;
|
|
|
|
|
|
2021-11-23 06:05:01 +00:00
|
|
|
interface ReduxStateProps {
|
|
|
|
|
dynamicData: DataTree;
|
|
|
|
|
datasources: any;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type EvaluatedValuePopupWrapperProps = ReduxStateProps & {
|
|
|
|
|
isFocused: boolean;
|
|
|
|
|
theme: EditorTheme;
|
|
|
|
|
popperPlacement?: Placement;
|
|
|
|
|
popperZIndex?: Indices;
|
|
|
|
|
dataTreePath?: string;
|
|
|
|
|
evaluatedValue?: any;
|
|
|
|
|
expected?: CodeEditorExpected;
|
|
|
|
|
hideEvaluatedValue?: boolean;
|
|
|
|
|
useValidationMessage?: boolean;
|
|
|
|
|
children: JSX.Element;
|
|
|
|
|
};
|
|
|
|
|
|
2021-02-16 10:29:08 +00:00
|
|
|
type RenderComponentProps = {
|
|
|
|
|
index: number;
|
|
|
|
|
item: {
|
|
|
|
|
label: string;
|
|
|
|
|
isDerived?: boolean;
|
|
|
|
|
isVisible?: boolean;
|
2021-11-23 06:05:01 +00:00
|
|
|
isDuplicateLabel?: boolean;
|
2021-02-16 10:29:08 +00:00
|
|
|
};
|
2021-11-23 06:05:01 +00:00
|
|
|
updateFocus?: (index: number, isFocused: boolean) => void;
|
2021-02-16 10:29:08 +00:00
|
|
|
updateOption: (index: number, value: string) => void;
|
|
|
|
|
onEdit?: (index: number) => void;
|
|
|
|
|
deleteOption: (index: number) => void;
|
|
|
|
|
toggleVisibility?: (index: number) => void;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const getOriginalColumn = (
|
|
|
|
|
columns: Record<string, ColumnProperties>,
|
|
|
|
|
index: number,
|
|
|
|
|
columnOrder?: string[],
|
|
|
|
|
): ColumnProperties | undefined => {
|
|
|
|
|
const reorderedColumns = reorderColumns(columns, columnOrder || []);
|
|
|
|
|
const column: ColumnProperties | undefined = Object.values(
|
|
|
|
|
reorderedColumns,
|
|
|
|
|
).find((column: ColumnProperties) => column.index === index);
|
|
|
|
|
return column;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function ColumnControlComponent(props: RenderComponentProps) {
|
|
|
|
|
const [value, setValue] = useState(props.item.label);
|
2021-09-21 07:55:56 +00:00
|
|
|
const [isEditing, setEditing] = useState(false);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (!isEditing && props.item && props.item.label)
|
|
|
|
|
setValue(props.item.label);
|
|
|
|
|
}, [props.item?.label, isEditing]);
|
2021-03-30 08:14:25 +00:00
|
|
|
|
2021-02-16 10:29:08 +00:00
|
|
|
const {
|
|
|
|
|
deleteOption,
|
|
|
|
|
index,
|
2021-05-13 08:35:39 +00:00
|
|
|
item,
|
|
|
|
|
onEdit,
|
|
|
|
|
toggleVisibility,
|
2021-11-23 06:05:01 +00:00
|
|
|
updateFocus,
|
2021-05-13 08:35:39 +00:00
|
|
|
updateOption,
|
2021-02-16 10:29:08 +00:00
|
|
|
} = props;
|
2021-03-30 08:14:25 +00:00
|
|
|
const [visibility, setVisibility] = useState(item.isVisible);
|
2021-11-23 06:05:01 +00:00
|
|
|
const debouncedUpdate = _.debounce(updateOption, 1000);
|
|
|
|
|
const debouncedFocus = updateFocus ? _.debounce(updateFocus, 400) : noop;
|
2021-02-16 10:29:08 +00:00
|
|
|
const onChange = useCallback(
|
|
|
|
|
(index: number, value: string) => {
|
|
|
|
|
setValue(value);
|
|
|
|
|
debouncedUpdate(index, value);
|
|
|
|
|
},
|
|
|
|
|
[updateOption],
|
|
|
|
|
);
|
2021-09-21 07:55:56 +00:00
|
|
|
|
2021-11-23 06:05:01 +00:00
|
|
|
const onFocus = () => {
|
2021-12-24 09:31:26 +00:00
|
|
|
setEditing(false);
|
2021-11-23 06:05:01 +00:00
|
|
|
debouncedFocus(index, true);
|
|
|
|
|
};
|
|
|
|
|
const onBlur = () => {
|
|
|
|
|
setEditing(false);
|
|
|
|
|
debouncedFocus(index, false);
|
|
|
|
|
};
|
2021-09-21 07:55:56 +00:00
|
|
|
|
2021-02-16 10:29:08 +00:00
|
|
|
return (
|
2021-11-23 06:05:01 +00:00
|
|
|
<ItemWrapper
|
|
|
|
|
className={props.item.isDuplicateLabel ? "has-duplicate-label" : ""}
|
|
|
|
|
>
|
2021-02-16 10:29:08 +00:00
|
|
|
<StyledDragIcon height={20} width={20} />
|
|
|
|
|
<StyledOptionControlInputGroup
|
2021-03-15 12:17:56 +00:00
|
|
|
dataType="text"
|
2021-09-21 07:55:56 +00:00
|
|
|
onBlur={onBlur}
|
2021-03-15 12:17:56 +00:00
|
|
|
onChange={(value: string) => {
|
|
|
|
|
onChange(index, value);
|
2021-02-16 10:29:08 +00:00
|
|
|
}}
|
2021-09-21 07:55:56 +00:00
|
|
|
onFocus={onFocus}
|
2021-04-28 10:28:39 +00:00
|
|
|
placeholder="Column Title"
|
2021-09-21 07:55:56 +00:00
|
|
|
value={value}
|
2021-11-23 08:01:46 +00:00
|
|
|
width="100%"
|
2021-02-16 10:29:08 +00:00
|
|
|
/>
|
|
|
|
|
<StyledEditIcon
|
|
|
|
|
className="t--edit-column-btn"
|
|
|
|
|
height={20}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
onEdit && onEdit(index);
|
|
|
|
|
}}
|
2021-04-28 10:28:39 +00:00
|
|
|
width={20}
|
2021-02-16 10:29:08 +00:00
|
|
|
/>
|
|
|
|
|
{!!item.isDerived ? (
|
|
|
|
|
<StyledDeleteIcon
|
|
|
|
|
className="t--delete-column-btn"
|
|
|
|
|
height={20}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
deleteOption && deleteOption(index);
|
|
|
|
|
}}
|
2021-04-28 10:28:39 +00:00
|
|
|
width={20}
|
2021-02-16 10:29:08 +00:00
|
|
|
/>
|
2021-03-30 08:14:25 +00:00
|
|
|
) : visibility ? (
|
2021-02-16 10:29:08 +00:00
|
|
|
<StyledVisibleIcon
|
|
|
|
|
className="t--show-column-btn"
|
|
|
|
|
height={20}
|
|
|
|
|
onClick={() => {
|
2021-03-30 08:14:25 +00:00
|
|
|
setVisibility(!visibility);
|
2021-02-16 10:29:08 +00:00
|
|
|
toggleVisibility && toggleVisibility(index);
|
|
|
|
|
}}
|
2021-04-28 10:28:39 +00:00
|
|
|
width={20}
|
2021-02-16 10:29:08 +00:00
|
|
|
/>
|
|
|
|
|
) : (
|
|
|
|
|
<StyledHiddenIcon
|
|
|
|
|
className="t--show-column-btn"
|
|
|
|
|
height={20}
|
|
|
|
|
onClick={() => {
|
2021-03-30 08:14:25 +00:00
|
|
|
setVisibility(!visibility);
|
2021-02-16 10:29:08 +00:00
|
|
|
toggleVisibility && toggleVisibility(index);
|
|
|
|
|
}}
|
2021-04-28 10:28:39 +00:00
|
|
|
width={20}
|
2021-02-16 10:29:08 +00:00
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
</ItemWrapper>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-23 06:05:01 +00:00
|
|
|
type State = {
|
|
|
|
|
focusedIndex: number | null;
|
|
|
|
|
duplicateColumnIds: string[];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class PrimaryColumnsControl extends BaseControl<ControlProps, State> {
|
|
|
|
|
constructor(props: ControlProps) {
|
|
|
|
|
super(props);
|
|
|
|
|
|
|
|
|
|
const columns: Record<string, ColumnProperties> = props.propertyValue || {};
|
|
|
|
|
const columnOrder = Object.keys(columns);
|
|
|
|
|
const reorderedColumns = reorderColumns(columns, columnOrder);
|
|
|
|
|
const tableColumnLabels = _.map(reorderedColumns, "label");
|
|
|
|
|
const duplicateColumnIds = [];
|
|
|
|
|
|
|
|
|
|
for (let index = 0; index < tableColumnLabels.length; index++) {
|
|
|
|
|
const currLabel = tableColumnLabels[index] as string;
|
|
|
|
|
const duplicateValueIndex = tableColumnLabels.indexOf(currLabel);
|
|
|
|
|
if (duplicateValueIndex !== index) {
|
|
|
|
|
// get column id from columnOrder index
|
|
|
|
|
duplicateColumnIds.push(reorderedColumns[columnOrder[index]].id);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.state = {
|
|
|
|
|
focusedIndex: null,
|
|
|
|
|
duplicateColumnIds,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-16 10:29:08 +00:00
|
|
|
render() {
|
|
|
|
|
// Get columns from widget properties
|
|
|
|
|
const columns: Record<string, ColumnProperties> =
|
|
|
|
|
this.props.propertyValue || {};
|
|
|
|
|
|
|
|
|
|
// If there are no columns, show empty state
|
|
|
|
|
if (Object.keys(columns).length === 0) {
|
|
|
|
|
return <EmptyDataState />;
|
|
|
|
|
}
|
|
|
|
|
// Get an empty array of length of columns
|
|
|
|
|
let columnOrder: string[] = new Array(Object.keys(columns).length);
|
|
|
|
|
|
|
|
|
|
if (this.props.widgetProperties.columnOrder) {
|
|
|
|
|
columnOrder = this.props.widgetProperties.columnOrder;
|
|
|
|
|
} else {
|
|
|
|
|
columnOrder = Object.keys(columns);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const reorderedColumns = reorderColumns(columns, columnOrder);
|
|
|
|
|
|
|
|
|
|
const draggableComponentColumns = Object.values(reorderedColumns).map(
|
|
|
|
|
(column: ColumnProperties) => {
|
|
|
|
|
return {
|
|
|
|
|
label: column.label,
|
|
|
|
|
id: column.id,
|
|
|
|
|
isVisible: column.isVisible,
|
|
|
|
|
isDerived: column.isDerived,
|
|
|
|
|
index: column.index,
|
2021-11-23 06:05:01 +00:00
|
|
|
isDuplicateLabel: _.includes(
|
|
|
|
|
this.state.duplicateColumnIds,
|
|
|
|
|
column.id,
|
|
|
|
|
),
|
2021-02-16 10:29:08 +00:00
|
|
|
};
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
|
2021-11-23 06:05:01 +00:00
|
|
|
const column: ColumnProperties | undefined = Object.values(
|
|
|
|
|
reorderedColumns,
|
|
|
|
|
).find(
|
|
|
|
|
(column: ColumnProperties) => column.index === this.state.focusedIndex,
|
|
|
|
|
);
|
|
|
|
|
// show popup on duplicate column label input focused
|
|
|
|
|
const isFocused =
|
|
|
|
|
!_.isNull(this.state.focusedIndex) &&
|
|
|
|
|
_.includes(this.state.duplicateColumnIds, column?.id);
|
2021-02-16 10:29:08 +00:00
|
|
|
return (
|
|
|
|
|
<TabsWrapper>
|
2021-11-23 06:05:01 +00:00
|
|
|
<EvaluatedValuePopupWrapper {...this.props} isFocused={isFocused}>
|
|
|
|
|
<DroppableComponent
|
|
|
|
|
deleteOption={this.deleteOption}
|
2021-12-27 19:53:49 +00:00
|
|
|
fixedHeight={370}
|
2021-11-23 06:05:01 +00:00
|
|
|
itemHeight={45}
|
|
|
|
|
items={draggableComponentColumns}
|
|
|
|
|
onEdit={this.onEdit}
|
|
|
|
|
renderComponent={ColumnControlComponent}
|
|
|
|
|
toggleVisibility={this.toggleVisibility}
|
|
|
|
|
updateItems={this.updateItems}
|
|
|
|
|
updateOption={this.updateOption}
|
|
|
|
|
/>
|
|
|
|
|
</EvaluatedValuePopupWrapper>
|
2021-03-15 12:17:56 +00:00
|
|
|
|
2021-02-16 10:29:08 +00:00
|
|
|
<AddColumnButton
|
2021-04-28 10:28:39 +00:00
|
|
|
category={Category.tertiary}
|
2021-02-16 10:29:08 +00:00
|
|
|
className="t--add-column-btn"
|
|
|
|
|
icon="plus"
|
|
|
|
|
onClick={this.addNewColumn}
|
2021-03-15 12:17:56 +00:00
|
|
|
size={Size.medium}
|
2021-04-28 10:28:39 +00:00
|
|
|
tag="button"
|
|
|
|
|
text="Add a new column"
|
|
|
|
|
type="button"
|
2021-02-16 10:29:08 +00:00
|
|
|
/>
|
|
|
|
|
</TabsWrapper>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
addNewColumn = () => {
|
|
|
|
|
const columns: Record<string, ColumnProperties> =
|
|
|
|
|
this.props.propertyValue || {};
|
|
|
|
|
const columnIds = Object.keys(columns);
|
|
|
|
|
const newColumnName = getNextEntityName("customColumn", columnIds);
|
|
|
|
|
const nextIndex = columnIds.length;
|
|
|
|
|
const columnProps: ColumnProperties = getDefaultColumnProperties(
|
|
|
|
|
newColumnName,
|
|
|
|
|
nextIndex,
|
|
|
|
|
this.props.widgetProperties.widgetName,
|
|
|
|
|
true,
|
|
|
|
|
);
|
|
|
|
|
const tableStyles = getTableStyles(this.props.widgetProperties);
|
|
|
|
|
const column = {
|
|
|
|
|
...columnProps,
|
2021-03-02 03:13:43 +00:00
|
|
|
buttonStyle: "rgb(3, 179, 101)",
|
2021-02-16 10:29:08 +00:00
|
|
|
buttonLabelColor: "#FFFFFF",
|
2021-08-17 12:54:43 +00:00
|
|
|
isDisabled: false,
|
2021-02-16 10:29:08 +00:00
|
|
|
...tableStyles,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
this.updateProperty(`${this.props.propertyName}.${column.id}`, column);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
onEdit = (index: number) => {
|
|
|
|
|
const columns: Record<string, ColumnProperties> =
|
|
|
|
|
this.props.propertyValue || [];
|
|
|
|
|
|
|
|
|
|
const originalColumn = getOriginalColumn(
|
|
|
|
|
columns,
|
|
|
|
|
index,
|
|
|
|
|
this.props.widgetProperties.columnOrder,
|
|
|
|
|
);
|
|
|
|
|
|
2021-03-29 15:47:22 +00:00
|
|
|
this.props.openNextPanel({
|
|
|
|
|
...originalColumn,
|
2021-04-27 07:16:54 +00:00
|
|
|
propPaneId: this.props.widgetProperties.widgetId,
|
2021-03-29 15:47:22 +00:00
|
|
|
});
|
2021-02-16 10:29:08 +00:00
|
|
|
};
|
|
|
|
|
//Used to reorder columns
|
|
|
|
|
updateItems = (items: Array<Record<string, unknown>>) => {
|
|
|
|
|
this.updateProperty(
|
|
|
|
|
"columnOrder",
|
|
|
|
|
items.map(({ id }) => id),
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
toggleVisibility = (index: number) => {
|
|
|
|
|
const columns: Record<string, ColumnProperties> =
|
|
|
|
|
this.props.propertyValue || {};
|
|
|
|
|
const originalColumn = getOriginalColumn(
|
|
|
|
|
columns,
|
|
|
|
|
index,
|
|
|
|
|
this.props.widgetProperties.columnOrder,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (originalColumn) {
|
|
|
|
|
this.updateProperty(
|
|
|
|
|
`${this.props.propertyName}.${originalColumn.id}.isVisible`,
|
|
|
|
|
!originalColumn.isVisible,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
deleteOption = (index: number) => {
|
|
|
|
|
const columns: Record<string, ColumnProperties> =
|
|
|
|
|
this.props.propertyValue || {};
|
|
|
|
|
const derivedColumns = this.props.widgetProperties.derivedColumns || {};
|
2021-03-15 05:27:34 +00:00
|
|
|
const columnOrder = this.props.widgetProperties.columnOrder || [];
|
2021-02-16 10:29:08 +00:00
|
|
|
|
2021-03-15 05:27:34 +00:00
|
|
|
const originalColumn = getOriginalColumn(columns, index, columnOrder);
|
2021-02-16 10:29:08 +00:00
|
|
|
|
|
|
|
|
if (originalColumn) {
|
|
|
|
|
const propertiesToDelete = [
|
|
|
|
|
`${this.props.propertyName}.${originalColumn.id}`,
|
|
|
|
|
];
|
|
|
|
|
if (derivedColumns[originalColumn.id])
|
|
|
|
|
propertiesToDelete.push(`derivedColumns.${originalColumn.id}`);
|
|
|
|
|
|
2021-03-15 05:27:34 +00:00
|
|
|
const columnOrderIndex = columnOrder.findIndex(
|
2021-02-16 10:29:08 +00:00
|
|
|
(column: string) => column === originalColumn.id,
|
|
|
|
|
);
|
|
|
|
|
if (columnOrderIndex > -1)
|
|
|
|
|
propertiesToDelete.push(`columnOrder[${columnOrderIndex}]`);
|
|
|
|
|
|
|
|
|
|
this.deleteProperties(propertiesToDelete);
|
2021-11-23 06:05:01 +00:00
|
|
|
// if column deleted, clean up duplicateIndexes
|
|
|
|
|
let duplicateColumnIds = [...this.state.duplicateColumnIds];
|
|
|
|
|
duplicateColumnIds = duplicateColumnIds.filter(
|
|
|
|
|
(id) => id !== originalColumn.id,
|
|
|
|
|
);
|
|
|
|
|
this.setState({ duplicateColumnIds });
|
2021-02-16 10:29:08 +00:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
updateOption = (index: number, updatedLabel: string) => {
|
|
|
|
|
const columns: Record<string, ColumnProperties> =
|
|
|
|
|
this.props.propertyValue || {};
|
|
|
|
|
const originalColumn = getOriginalColumn(
|
|
|
|
|
columns,
|
|
|
|
|
index,
|
|
|
|
|
this.props.widgetProperties.columnOrder,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (originalColumn) {
|
|
|
|
|
this.updateProperty(
|
|
|
|
|
`${this.props.propertyName}.${originalColumn.id}.label`,
|
|
|
|
|
updatedLabel,
|
|
|
|
|
);
|
2021-11-23 06:05:01 +00:00
|
|
|
// check entered label is unique or duplicate
|
|
|
|
|
const tableColumnLabels = _.map(columns, "label");
|
|
|
|
|
let duplicateColumnIds = [...this.state.duplicateColumnIds];
|
|
|
|
|
// if duplicate, add into array
|
|
|
|
|
if (_.includes(tableColumnLabels, updatedLabel)) {
|
|
|
|
|
duplicateColumnIds.push(originalColumn.id);
|
|
|
|
|
this.setState({ duplicateColumnIds });
|
|
|
|
|
} else {
|
|
|
|
|
duplicateColumnIds = duplicateColumnIds.filter(
|
|
|
|
|
(id) => id !== originalColumn.id,
|
|
|
|
|
);
|
|
|
|
|
this.setState({ duplicateColumnIds });
|
|
|
|
|
}
|
2021-02-16 10:29:08 +00:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2021-11-23 06:05:01 +00:00
|
|
|
updateFocus = (index: number, isFocused: boolean) => {
|
|
|
|
|
this.setState({ focusedIndex: isFocused ? index : null });
|
|
|
|
|
};
|
|
|
|
|
|
2021-02-16 10:29:08 +00:00
|
|
|
static getControlType() {
|
|
|
|
|
return "PRIMARY_COLUMNS";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default PrimaryColumnsControl;
|
2021-11-23 06:05:01 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* wrapper component on dragable primary columns
|
|
|
|
|
* render popup if primary column labels are not unique
|
|
|
|
|
* show unique name error in PRIMARY_COLUMNS
|
|
|
|
|
*/
|
|
|
|
|
class EvaluatedValuePopupWrapperClass extends Component<
|
|
|
|
|
EvaluatedValuePopupWrapperProps
|
|
|
|
|
> {
|
|
|
|
|
getPropertyValidation = (
|
|
|
|
|
dataTree: DataTree,
|
|
|
|
|
dataTreePath?: string,
|
|
|
|
|
): {
|
|
|
|
|
isInvalid: boolean;
|
|
|
|
|
errors: EvaluationError[];
|
|
|
|
|
pathEvaluatedValue: unknown;
|
|
|
|
|
} => {
|
|
|
|
|
if (!dataTreePath) {
|
|
|
|
|
return {
|
|
|
|
|
isInvalid: false,
|
|
|
|
|
errors: [],
|
|
|
|
|
pathEvaluatedValue: undefined,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const errors = _.get(
|
|
|
|
|
dataTree,
|
|
|
|
|
getEvalErrorPath(dataTreePath),
|
|
|
|
|
[],
|
|
|
|
|
) as EvaluationError[];
|
|
|
|
|
|
|
|
|
|
const filteredLintErrors = errors.filter(
|
|
|
|
|
(error) => error.errorType !== PropertyEvaluationErrorType.LINT,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const pathEvaluatedValue = _.get(dataTree, getEvalValuePath(dataTreePath));
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
isInvalid: filteredLintErrors.length > 0,
|
|
|
|
|
errors: filteredLintErrors,
|
|
|
|
|
pathEvaluatedValue,
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
render = () => {
|
|
|
|
|
const {
|
|
|
|
|
dataTreePath,
|
|
|
|
|
dynamicData,
|
|
|
|
|
evaluatedValue,
|
|
|
|
|
expected,
|
|
|
|
|
hideEvaluatedValue,
|
|
|
|
|
useValidationMessage,
|
|
|
|
|
} = this.props;
|
|
|
|
|
const {
|
|
|
|
|
errors,
|
|
|
|
|
isInvalid,
|
|
|
|
|
pathEvaluatedValue,
|
|
|
|
|
} = this.getPropertyValidation(dynamicData, dataTreePath);
|
|
|
|
|
let evaluated = evaluatedValue;
|
|
|
|
|
if (dataTreePath) {
|
|
|
|
|
evaluated = pathEvaluatedValue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<EvaluatedValuePopup
|
|
|
|
|
errors={errors}
|
|
|
|
|
evaluatedValue={evaluated}
|
|
|
|
|
expected={expected}
|
|
|
|
|
hasError={isInvalid}
|
|
|
|
|
hideEvaluatedValue={hideEvaluatedValue}
|
|
|
|
|
isOpen={this.props.isFocused && isInvalid}
|
|
|
|
|
popperPlacement={this.props.popperPlacement}
|
|
|
|
|
popperZIndex={this.props.popperZIndex}
|
|
|
|
|
theme={this.props.theme || EditorTheme.LIGHT}
|
|
|
|
|
useValidationMessage={useValidationMessage}
|
|
|
|
|
>
|
|
|
|
|
{this.props.children}
|
|
|
|
|
</EvaluatedValuePopup>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
const mapStateToProps = (state: AppState): ReduxStateProps => ({
|
|
|
|
|
dynamicData: getDataTreeForAutocomplete(state),
|
|
|
|
|
datasources: state.entities.datasources,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const EvaluatedValuePopupWrapper = Sentry.withProfiler(
|
|
|
|
|
connect(mapStateToProps)(EvaluatedValuePopupWrapperClass),
|
|
|
|
|
);
|