fix: Column tile reposition on focus state of Input element inside property pane configuration for Table and Tabs widget (#10046)
This commit is contained in:
parent
324b077ffa
commit
b7ebc7501e
|
|
@ -1,11 +1,11 @@
|
|||
{
|
||||
"tabWidget": ".t--draggable-tabswidget",
|
||||
"tabInput": ".t--draggable-tabswidget span.t--widget-name",
|
||||
"tabName": ".t--property-control-tabs input",
|
||||
"tabDefault": ".t--property-control-defaulttab .CodeMirror-code",
|
||||
"tabButton": ".t--property-control-tabs button",
|
||||
"tabDelete": ".t--property-control-tabs .t--delete-tab-btn",
|
||||
"tabContainer": "div[type='TABS_WIDGET']",
|
||||
"tabEdit": ".t--property-control-tabs .t--edit-column-btn",
|
||||
"tabVisibility": ".t--property-control-visible .bp3-control-indicator"
|
||||
}
|
||||
"tabWidget": ".t--draggable-tabswidget",
|
||||
"tabInput": ".t--draggable-tabswidget span.t--widget-name",
|
||||
"tabName": ".t--property-control-tabs input",
|
||||
"tabDefault": ".t--property-control-defaulttab .CodeMirror-code",
|
||||
"tabButton": ".t--property-control-tabs button",
|
||||
"tabDelete": ".t--property-control-tabs .t--delete-column-btn",
|
||||
"tabContainer": "div[type='TABS_WIDGET']",
|
||||
"tabEdit": ".t--property-control-tabs .t--edit-column-btn",
|
||||
"tabVisibility": ".t--property-control-visible .bp3-control-indicator"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,10 +60,27 @@ const DraggableListWrapper = styled.div`
|
|||
`;
|
||||
|
||||
function DraggableList(props: any) {
|
||||
const { itemHeight, ItemRenderer, items, onUpdate } = props;
|
||||
const {
|
||||
fixedHeight,
|
||||
focusedIndex,
|
||||
itemHeight,
|
||||
ItemRenderer,
|
||||
items,
|
||||
onUpdate,
|
||||
updateDragging,
|
||||
} = props;
|
||||
|
||||
const listContainerHeight =
|
||||
fixedHeight && fixedHeight < items.length * itemHeight
|
||||
? fixedHeight
|
||||
: items.length * itemHeight;
|
||||
const shouldReRender = get(props, "shouldReRender", true);
|
||||
// order of items in the list
|
||||
const order = useRef<any>(items.map((_: any, index: any) => index));
|
||||
const displacement = useRef<number>(0);
|
||||
const dragging = useRef<boolean>(false);
|
||||
|
||||
const listRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
const onDrop = (originalIndex: number, newIndex: number) => {
|
||||
onUpdate(order.current, originalIndex, newIndex);
|
||||
|
|
@ -82,6 +99,29 @@ function DraggableList(props: any) {
|
|||
}
|
||||
}, [items]);
|
||||
|
||||
useEffect(() => {
|
||||
if (focusedIndex && listRef && listRef.current) {
|
||||
const container = listRef.current;
|
||||
|
||||
if (focusedIndex * itemHeight < container.scrollTop) {
|
||||
listRef.current.scrollTo({
|
||||
top: (focusedIndex - 1) * itemHeight,
|
||||
left: 0,
|
||||
behavior: "smooth",
|
||||
});
|
||||
} else if (
|
||||
(focusedIndex + 1) * itemHeight >
|
||||
listRef.current.scrollTop + listRef.current.clientHeight
|
||||
) {
|
||||
listRef.current.scrollTo({
|
||||
top: (focusedIndex + 1) * itemHeight - listRef.current.clientHeight,
|
||||
left: 0,
|
||||
behavior: "smooth",
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [focusedIndex]);
|
||||
|
||||
const [springs, setSprings] = useSprings<any>(
|
||||
items.length,
|
||||
updateSpringStyles(order.current, itemHeight),
|
||||
|
|
@ -90,60 +130,122 @@ function DraggableList(props: any) {
|
|||
const bind: any = useDrag<any>((props: any) => {
|
||||
const originalIndex = props.args[0];
|
||||
const curIndex = order.current.indexOf(originalIndex);
|
||||
const curRow = clamp(
|
||||
Math.round((curIndex * itemHeight + props.movement[1]) / itemHeight),
|
||||
0,
|
||||
items.length - 1,
|
||||
);
|
||||
const newOrder = swap(order.current, curIndex, curRow);
|
||||
setSprings(
|
||||
dragIdleSpringStyles(newOrder, {
|
||||
down: props.down,
|
||||
originalIndex,
|
||||
curIndex,
|
||||
y: props.movement[1],
|
||||
itemHeight,
|
||||
}),
|
||||
);
|
||||
if (curRow !== curIndex) {
|
||||
// Feed springs new style data, they'll animate the view without causing a single render
|
||||
if (!props.down) {
|
||||
order.current = newOrder;
|
||||
setSprings(updateSpringStyles(order.current, itemHeight));
|
||||
debounce(onDrop, 400)(curIndex, curRow);
|
||||
const pointerFromTop = props.xy[1];
|
||||
if (listRef && listRef.current) {
|
||||
const containerCoordinates = listRef?.current.getBoundingClientRect();
|
||||
const container = listRef.current;
|
||||
if (containerCoordinates) {
|
||||
const containerDistanceFromTop = containerCoordinates.top;
|
||||
if (props.dragging) {
|
||||
if (pointerFromTop < containerDistanceFromTop + itemHeight / 2) {
|
||||
// Scroll inside container till first element in list is completely visible
|
||||
if (container.scrollTop > 0) {
|
||||
container.scrollTop -= itemHeight / 10;
|
||||
}
|
||||
} else if (
|
||||
pointerFromTop >=
|
||||
containerDistanceFromTop + container.clientHeight - itemHeight / 2
|
||||
) {
|
||||
// Scroll inside container till container cannnot be scrolled more towards bottom
|
||||
if (
|
||||
container.scrollTop <=
|
||||
springs.length * itemHeight -
|
||||
container.clientHeight -
|
||||
itemHeight / 2
|
||||
) {
|
||||
container.scrollTop += itemHeight / 10;
|
||||
}
|
||||
}
|
||||
// finding distance of current pointer from the top of the container to find the final position
|
||||
// currIndex * itemHeight for the initial position
|
||||
// subtraction formar with latter for displacement
|
||||
displacement.current =
|
||||
pointerFromTop -
|
||||
containerDistanceFromTop +
|
||||
container.scrollTop -
|
||||
curIndex * itemHeight -
|
||||
itemHeight / 2;
|
||||
|
||||
if (!dragging.current && Math.abs(displacement.current) > 10) {
|
||||
dragging.current = props.dragging;
|
||||
updateDragging(dragging.current);
|
||||
}
|
||||
} else {
|
||||
if (dragging.current) {
|
||||
dragging.current = props.dragging;
|
||||
updateDragging(dragging.current);
|
||||
}
|
||||
}
|
||||
|
||||
const curRow = clamp(
|
||||
Math.round(
|
||||
(curIndex * itemHeight + displacement.current) / itemHeight,
|
||||
),
|
||||
0,
|
||||
items.length - 1,
|
||||
);
|
||||
const newOrder = swap(order.current, curIndex, curRow);
|
||||
setSprings(
|
||||
dragIdleSpringStyles(newOrder, {
|
||||
down: props.down,
|
||||
originalIndex,
|
||||
curIndex,
|
||||
y: Math.abs(displacement.current) > 10 ? displacement.current : 0,
|
||||
itemHeight,
|
||||
}),
|
||||
);
|
||||
if (curRow !== curIndex) {
|
||||
// Feed springs new style data, they'll animate the view without causing a single render
|
||||
if (!props.down) {
|
||||
order.current = newOrder;
|
||||
setSprings(updateSpringStyles(order.current, itemHeight));
|
||||
debounce(onDrop, 400)(curIndex, curRow);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return (
|
||||
<DraggableListWrapper
|
||||
className="content"
|
||||
onMouseDown={() => {
|
||||
// set events to null to stop other parent draggable elements execution(ex: Property pane)
|
||||
document.onmouseup = null;
|
||||
document.onmousemove = null;
|
||||
<div
|
||||
ref={listRef}
|
||||
style={{
|
||||
height: listContainerHeight,
|
||||
overflowY: "auto",
|
||||
zIndex: 1,
|
||||
}}
|
||||
style={{ height: items.length * itemHeight }}
|
||||
>
|
||||
{springs.map(({ scale, y, zIndex }, i) => (
|
||||
<animated.div
|
||||
{...bind(i)}
|
||||
data-rbd-draggable-id={items[i].id}
|
||||
key={i}
|
||||
style={{
|
||||
zIndex,
|
||||
width: "100%",
|
||||
transform: to(
|
||||
[y, scale],
|
||||
(y, s) => `translate3d(0,${y}px,0) scale(${s})`,
|
||||
),
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<ItemRenderer index={i} item={items[i]} />
|
||||
</div>
|
||||
</animated.div>
|
||||
))}
|
||||
</DraggableListWrapper>
|
||||
<DraggableListWrapper
|
||||
className="content"
|
||||
onMouseDown={() => {
|
||||
// set events to null to stop other parent draggable elements execution(ex: Property pane)
|
||||
document.onmouseup = null;
|
||||
document.onmousemove = null;
|
||||
}}
|
||||
style={{
|
||||
height: "100%",
|
||||
}}
|
||||
>
|
||||
{springs.map(({ scale, y, zIndex }, i) => (
|
||||
<animated.div
|
||||
{...bind(i)}
|
||||
data-rbd-draggable-id={items[i].id}
|
||||
key={i}
|
||||
style={{
|
||||
zIndex,
|
||||
width: "100%",
|
||||
transform: to(
|
||||
[y, scale],
|
||||
(y, s) => `translate3d(0,${y}px,0) scale(${s})`,
|
||||
),
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<ItemRenderer index={i} item={items[i]} />
|
||||
</div>
|
||||
</animated.div>
|
||||
))}
|
||||
</DraggableListWrapper>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
DraggableList.displayName = "DraggableList";
|
||||
|
|
|
|||
162
app/client/src/components/ads/DraggableListCard.tsx
Normal file
162
app/client/src/components/ads/DraggableListCard.tsx
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
import React, { useCallback, useState, useRef, useEffect } from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
import _ from "lodash";
|
||||
import {
|
||||
StyledDragIcon,
|
||||
StyledOptionControlInputGroup,
|
||||
StyledEditIcon,
|
||||
StyledDeleteIcon,
|
||||
StyledVisibleIcon,
|
||||
StyledHiddenIcon,
|
||||
} from "components/propertyControls/StyledControls";
|
||||
import { Colors } from "constants/Colors";
|
||||
|
||||
const ItemWrapper = styled.div`
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
&.has-duplicate-label > div:nth-child(2) {
|
||||
border: 1px solid ${Colors.DANGER_SOLID};
|
||||
}
|
||||
`;
|
||||
|
||||
type RenderComponentProps = {
|
||||
focusedIndex: number | null | undefined;
|
||||
index: number;
|
||||
item: {
|
||||
label: string;
|
||||
isDerived?: boolean;
|
||||
isVisible?: boolean;
|
||||
isDuplicateLabel?: boolean;
|
||||
};
|
||||
isDelete?: boolean;
|
||||
isDragging: boolean;
|
||||
placeholder: string;
|
||||
updateFocus?: (index: number, isFocused: boolean) => void;
|
||||
updateOption: (index: number, value: string) => void;
|
||||
onEdit?: (index: number) => void;
|
||||
deleteOption: (index: number) => void;
|
||||
toggleVisibility?: (index: number) => void;
|
||||
};
|
||||
|
||||
export function DraggableListCard(props: RenderComponentProps) {
|
||||
const [value, setValue] = useState(props.item.label);
|
||||
const [isEditing, setEditing] = useState(false);
|
||||
|
||||
const {
|
||||
deleteOption,
|
||||
focusedIndex,
|
||||
index,
|
||||
isDelete,
|
||||
isDragging,
|
||||
item,
|
||||
onEdit,
|
||||
placeholder,
|
||||
toggleVisibility,
|
||||
updateFocus,
|
||||
updateOption,
|
||||
} = props;
|
||||
const [visibility, setVisibility] = useState(item.isVisible);
|
||||
const ref = useRef<HTMLInputElement | null>(null);
|
||||
const debouncedUpdate = _.debounce(updateOption, 1000);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isEditing && item && item.label) setValue(item.label);
|
||||
}, [item?.label, isEditing]);
|
||||
|
||||
useEffect(() => {
|
||||
if (focusedIndex !== null && focusedIndex === index && !isDragging) {
|
||||
if (ref && ref.current) {
|
||||
ref?.current.focus();
|
||||
}
|
||||
} else if (isDragging && focusedIndex === index) {
|
||||
if (ref && ref.current) {
|
||||
ref?.current.blur();
|
||||
}
|
||||
}
|
||||
}, [focusedIndex, isDragging]);
|
||||
|
||||
const onChange = useCallback(
|
||||
(index: number, value: string) => {
|
||||
setValue(value);
|
||||
debouncedUpdate(index, value);
|
||||
},
|
||||
[updateOption],
|
||||
);
|
||||
|
||||
const onFocus = () => {
|
||||
setEditing(false);
|
||||
if (updateFocus) {
|
||||
updateFocus(index, true);
|
||||
}
|
||||
};
|
||||
|
||||
const onBlur = () => {
|
||||
if (!isDragging) {
|
||||
setEditing(false);
|
||||
if (updateFocus) {
|
||||
updateFocus(index, false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ItemWrapper
|
||||
className={props.item.isDuplicateLabel ? "has-duplicate-label" : ""}
|
||||
>
|
||||
<StyledDragIcon height={20} width={20} />
|
||||
<StyledOptionControlInputGroup
|
||||
autoFocus={index === focusedIndex}
|
||||
dataType="text"
|
||||
onBlur={onBlur}
|
||||
onChange={(value: string) => {
|
||||
onChange(index, value);
|
||||
}}
|
||||
onFocus={onFocus}
|
||||
placeholder={placeholder}
|
||||
ref={ref}
|
||||
value={value}
|
||||
width="100%"
|
||||
/>
|
||||
<StyledEditIcon
|
||||
className="t--edit-column-btn"
|
||||
height={20}
|
||||
onClick={() => {
|
||||
onEdit && onEdit(index);
|
||||
}}
|
||||
width={20}
|
||||
/>
|
||||
{!!item.isDerived || isDelete ? (
|
||||
<StyledDeleteIcon
|
||||
className="t--delete-column-btn"
|
||||
height={20}
|
||||
onClick={() => {
|
||||
deleteOption && deleteOption(index);
|
||||
}}
|
||||
width={20}
|
||||
/>
|
||||
) : visibility ? (
|
||||
<StyledVisibleIcon
|
||||
className="t--show-column-btn"
|
||||
height={20}
|
||||
onClick={() => {
|
||||
setVisibility(!visibility);
|
||||
toggleVisibility && toggleVisibility(index);
|
||||
}}
|
||||
width={20}
|
||||
/>
|
||||
) : (
|
||||
<StyledHiddenIcon
|
||||
className="t--show-column-btn"
|
||||
height={20}
|
||||
onClick={() => {
|
||||
setVisibility(!visibility);
|
||||
toggleVisibility && toggleVisibility(index);
|
||||
}}
|
||||
width={20}
|
||||
/>
|
||||
)}
|
||||
</ItemWrapper>
|
||||
);
|
||||
}
|
||||
|
|
@ -3,11 +3,13 @@ import React from "react";
|
|||
import DraggableList from "./DraggableList";
|
||||
|
||||
type RenderComponentProps = {
|
||||
focusedIndex: number | null | undefined;
|
||||
index: number;
|
||||
item: {
|
||||
label: string;
|
||||
isDerived?: boolean;
|
||||
};
|
||||
isDragging: boolean;
|
||||
deleteOption: (index: number) => void;
|
||||
updateOption: (index: number, value: string) => void;
|
||||
toggleVisibility?: (index: number) => void;
|
||||
|
|
@ -16,6 +18,8 @@ type RenderComponentProps = {
|
|||
};
|
||||
|
||||
interface DroppableComponentProps {
|
||||
fixedHeight?: number | boolean;
|
||||
focusedIndex?: number | null | undefined;
|
||||
items: Array<Record<string, unknown>>;
|
||||
itemHeight: number;
|
||||
renderComponent: (props: RenderComponentProps) => JSX.Element;
|
||||
|
|
@ -34,11 +38,18 @@ export class DroppableComponent extends React.Component<
|
|||
super(props);
|
||||
}
|
||||
|
||||
shouldComponentUpdate(prevProps: DroppableComponentProps) {
|
||||
public readonly state = {
|
||||
isDragging: false,
|
||||
};
|
||||
|
||||
shouldComponentUpdate(prevProps: DroppableComponentProps, prevState: any) {
|
||||
const presentOrder = this.props.items.map(this.getVisibleObject);
|
||||
const previousOrder = prevProps.items.map(this.getVisibleObject);
|
||||
|
||||
return !isEqual(presentOrder, previousOrder);
|
||||
return (
|
||||
!isEqual(presentOrder, previousOrder) ||
|
||||
this.props.focusedIndex !== prevProps.focusedIndex ||
|
||||
prevState.isDragging !== this.state.isDragging
|
||||
);
|
||||
}
|
||||
|
||||
getVisibleObject(item: Record<string, unknown>) {
|
||||
|
|
@ -52,14 +63,26 @@ export class DroppableComponent extends React.Component<
|
|||
};
|
||||
}
|
||||
|
||||
onUpdate = (itemsOrder: number[]) => {
|
||||
onUpdate = (
|
||||
itemsOrder: number[],
|
||||
originalIndex: number,
|
||||
newIndex: number,
|
||||
) => {
|
||||
const newOrderedItems = itemsOrder.map((each) => this.props.items[each]);
|
||||
this.props.updateItems(newOrderedItems);
|
||||
if (this.props.updateFocus && originalIndex !== newIndex) {
|
||||
this.props.updateFocus(newIndex, true);
|
||||
}
|
||||
};
|
||||
|
||||
updateDragging = (isDragging: boolean) => {
|
||||
this.setState({ isDragging });
|
||||
};
|
||||
|
||||
renderItem = ({ index, item }: any) => {
|
||||
const {
|
||||
deleteOption,
|
||||
focusedIndex,
|
||||
onEdit,
|
||||
renderComponent,
|
||||
toggleVisibility,
|
||||
|
|
@ -73,8 +96,10 @@ export class DroppableComponent extends React.Component<
|
|||
updateOption,
|
||||
toggleVisibility,
|
||||
onEdit,
|
||||
focusedIndex,
|
||||
item,
|
||||
index,
|
||||
isDragging: this.state.isDragging,
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -82,10 +107,13 @@ export class DroppableComponent extends React.Component<
|
|||
return (
|
||||
<DraggableList
|
||||
ItemRenderer={this.renderItem}
|
||||
fixedHeight={this.props.fixedHeight}
|
||||
focusedIndex={this.props.focusedIndex}
|
||||
itemHeight={45}
|
||||
items={this.props.items}
|
||||
onUpdate={this.onUpdate}
|
||||
shouldReRender={false}
|
||||
updateDragging={this.updateDragging}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ type RenderComponentProps = {
|
|||
isDerived?: boolean;
|
||||
};
|
||||
deleteOption: (index: number) => void;
|
||||
updateCurrentFocusedInput: (index: number | null) => void;
|
||||
updateOption: (index: number, value: string) => void;
|
||||
toggleVisibility?: (index: number) => void;
|
||||
onEdit?: (index: number) => void;
|
||||
|
|
@ -26,6 +27,7 @@ interface DroppableComponentProps {
|
|||
items: Array<Record<string, unknown>>;
|
||||
renderComponent: (props: RenderComponentProps) => JSX.Element;
|
||||
deleteOption: (index: number) => void;
|
||||
updateCurrentFocusedInput: (index: number | null) => void;
|
||||
updateOption: (index: number, value: string) => void;
|
||||
toggleVisibility?: (index: number) => void;
|
||||
updateItems: (items: Array<Record<string, unknown>>) => void;
|
||||
|
|
@ -85,6 +87,7 @@ export class DroppableComponent extends React.Component<
|
|||
onEdit,
|
||||
renderComponent,
|
||||
toggleVisibility,
|
||||
updateCurrentFocusedInput,
|
||||
updateOption,
|
||||
} = this.props;
|
||||
return (
|
||||
|
|
@ -116,6 +119,7 @@ export class DroppableComponent extends React.Component<
|
|||
>
|
||||
{renderComponent({
|
||||
deleteOption,
|
||||
updateCurrentFocusedInput,
|
||||
updateOption,
|
||||
toggleVisibility,
|
||||
onEdit,
|
||||
|
|
|
|||
|
|
@ -1,20 +1,15 @@
|
|||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import React from "react";
|
||||
import BaseControl, { ControlProps } from "./BaseControl";
|
||||
import {
|
||||
StyledPropertyPaneButton,
|
||||
StyledDragIcon,
|
||||
StyledDeleteIcon,
|
||||
StyledEditIcon,
|
||||
StyledOptionControlInputGroup,
|
||||
} from "./StyledControls";
|
||||
import { StyledPropertyPaneButton } from "./StyledControls";
|
||||
import styled from "constants/DefaultTheme";
|
||||
import { generateReactKey } from "utils/generators";
|
||||
import { DroppableComponent } from "components/ads/DraggableListComponent";
|
||||
import { getNextEntityName } from "utils/AppsmithUtils";
|
||||
import _, { debounce } from "lodash";
|
||||
import _ from "lodash";
|
||||
import { Category, Size } from "components/ads/Button";
|
||||
import { Colors } from "constants/Colors";
|
||||
import { ButtonPlacementTypes } from "components/constants";
|
||||
import { DraggableListCard } from "components/ads/DraggableListCard";
|
||||
|
||||
const StyledPropertyPaneButtonWrapper = styled.div`
|
||||
display: flex;
|
||||
|
|
@ -23,12 +18,6 @@ const StyledPropertyPaneButtonWrapper = styled.div`
|
|||
margin-top: 10px;
|
||||
`;
|
||||
|
||||
const ButtonWrapper = styled.div`
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
const ButtonListWrapper = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
|
|
@ -40,77 +29,28 @@ const AddNewButton = styled(StyledPropertyPaneButton)`
|
|||
flex-grow: 1;
|
||||
`;
|
||||
|
||||
type RenderComponentProps = {
|
||||
index: number;
|
||||
item: {
|
||||
label: string;
|
||||
isVisible?: boolean;
|
||||
};
|
||||
deleteOption: (index: number) => void;
|
||||
updateOption: (index: number, value: string) => void;
|
||||
toggleVisibility?: (index: number) => void;
|
||||
onEdit?: (props: any) => void;
|
||||
type State = {
|
||||
focusedIndex: number | null;
|
||||
};
|
||||
|
||||
function GroupButtonComponent(props: RenderComponentProps) {
|
||||
const { deleteOption, index, item, updateOption } = props;
|
||||
class ButtonListControl extends BaseControl<ControlProps, State> {
|
||||
constructor(props: ControlProps) {
|
||||
super(props);
|
||||
|
||||
const [value, setValue] = useState(item.label);
|
||||
const [isEditing, setEditing] = useState(false);
|
||||
this.state = {
|
||||
focusedIndex: null,
|
||||
};
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!isEditing && item && item.label) setValue(item.label);
|
||||
}, [item?.label, isEditing]);
|
||||
|
||||
const debouncedUpdate = debounce(updateOption, 1000);
|
||||
const onChange = useCallback(
|
||||
(index: number, value: string) => {
|
||||
setValue(value);
|
||||
debouncedUpdate(index, value);
|
||||
},
|
||||
[updateOption],
|
||||
);
|
||||
const handleChange = useCallback(() => props.onEdit && props.onEdit(index), [
|
||||
index,
|
||||
]);
|
||||
|
||||
const onFocus = () => setEditing(true);
|
||||
const onBlur = () => setEditing(false);
|
||||
|
||||
return (
|
||||
<ButtonWrapper>
|
||||
<StyledDragIcon height={20} width={20} />
|
||||
<StyledOptionControlInputGroup
|
||||
dataType="text"
|
||||
onBlur={onBlur}
|
||||
onChange={(value: string) => {
|
||||
onChange(index, value);
|
||||
}}
|
||||
onFocus={onFocus}
|
||||
placeholder="Button label"
|
||||
trimValue={false}
|
||||
value={value}
|
||||
/>
|
||||
<StyledDeleteIcon
|
||||
className="t--delete-tab-btn"
|
||||
height={20}
|
||||
marginRight={12}
|
||||
onClick={() => {
|
||||
deleteOption(index);
|
||||
}}
|
||||
width={20}
|
||||
/>
|
||||
<StyledEditIcon
|
||||
className="t--edit-column-btn"
|
||||
height={20}
|
||||
onClick={handleChange}
|
||||
width={20}
|
||||
/>
|
||||
</ButtonWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
class ButtonListControl extends BaseControl<ControlProps> {
|
||||
componentDidUpdate(prevProps: ControlProps): void {
|
||||
//on adding a new column last column should get focused
|
||||
if (
|
||||
Object.keys(prevProps.propertyValue).length + 1 ===
|
||||
Object.keys(this.props.propertyValue).length
|
||||
) {
|
||||
this.updateFocus(Object.keys(this.props.propertyValue).length - 1, true);
|
||||
}
|
||||
}
|
||||
updateItems = (items: Array<Record<string, any>>) => {
|
||||
const menuItems = items.reduce((obj: any, each: any, index: number) => {
|
||||
obj[each.id] = {
|
||||
|
|
@ -146,11 +86,20 @@ class ButtonListControl extends BaseControl<ControlProps> {
|
|||
<ButtonListWrapper>
|
||||
<DroppableComponent
|
||||
deleteOption={this.deleteOption}
|
||||
fixedHeight={370}
|
||||
focusedIndex={this.state.focusedIndex}
|
||||
itemHeight={45}
|
||||
items={menuItems}
|
||||
onEdit={this.onEdit}
|
||||
renderComponent={GroupButtonComponent}
|
||||
renderComponent={(props) =>
|
||||
DraggableListCard({
|
||||
...props,
|
||||
isDelete: true,
|
||||
placeholder: "Button label",
|
||||
})
|
||||
}
|
||||
toggleVisibility={this.toggleVisibility}
|
||||
updateFocus={this.updateFocus}
|
||||
updateItems={this.updateItems}
|
||||
updateOption={this.updateOption}
|
||||
/>
|
||||
|
|
@ -246,6 +195,10 @@ class ButtonListControl extends BaseControl<ControlProps> {
|
|||
this.updateProperty(this.props.propertyName, groupButtons);
|
||||
};
|
||||
|
||||
updateFocus = (index: number, isFocused: boolean) => {
|
||||
this.setState({ focusedIndex: isFocused ? index : null });
|
||||
};
|
||||
|
||||
static getControlType() {
|
||||
return "GROUP_BUTTONS";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,13 @@
|
|||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import React from "react";
|
||||
import BaseControl, { ControlProps } from "./BaseControl";
|
||||
import {
|
||||
StyledPropertyPaneButton,
|
||||
StyledDragIcon,
|
||||
StyledDeleteIcon,
|
||||
StyledEditIcon,
|
||||
StyledOptionControlInputGroup,
|
||||
} from "./StyledControls";
|
||||
import { StyledPropertyPaneButton } from "./StyledControls";
|
||||
import styled from "constants/DefaultTheme";
|
||||
import { generateReactKey } from "utils/generators";
|
||||
import { DroppableComponent } from "components/ads/DraggableListComponent";
|
||||
import { getNextEntityName } from "utils/AppsmithUtils";
|
||||
import _, { debounce, orderBy } from "lodash";
|
||||
import _, { orderBy } from "lodash";
|
||||
import { Category, Size } from "components/ads/Button";
|
||||
import { DraggableListCard } from "components/ads/DraggableListCard";
|
||||
|
||||
const StyledPropertyPaneButtonWrapper = styled.div`
|
||||
display: flex;
|
||||
|
|
@ -21,12 +16,6 @@ const StyledPropertyPaneButtonWrapper = styled.div`
|
|||
margin-top: 10px;
|
||||
`;
|
||||
|
||||
const ItemWrapper = styled.div`
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
const MenuItemsWrapper = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
|
|
@ -38,77 +27,30 @@ const AddMenuItemButton = styled(StyledPropertyPaneButton)`
|
|||
flex-grow: 1;
|
||||
`;
|
||||
|
||||
type RenderComponentProps = {
|
||||
index: number;
|
||||
item: {
|
||||
label: string;
|
||||
isVisible?: boolean;
|
||||
};
|
||||
deleteOption: (index: number) => void;
|
||||
updateOption: (index: number, value: string) => void;
|
||||
toggleVisibility?: (index: number) => void;
|
||||
onEdit?: (props: any) => void;
|
||||
type State = {
|
||||
focusedIndex: number | null;
|
||||
};
|
||||
|
||||
function MenuItemComponent(props: RenderComponentProps) {
|
||||
const { deleteOption, index, item, updateOption } = props;
|
||||
class MenuItemsControl extends BaseControl<ControlProps, State> {
|
||||
constructor(props: ControlProps) {
|
||||
super(props);
|
||||
|
||||
const [value, setValue] = useState(item.label);
|
||||
const [isEditing, setEditing] = useState(false);
|
||||
this.state = {
|
||||
focusedIndex: null,
|
||||
};
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!isEditing && item && item.label) setValue(item.label);
|
||||
}, [item?.label, isEditing]);
|
||||
|
||||
const debouncedUpdate = debounce(updateOption, 1000);
|
||||
const onChange = useCallback(
|
||||
(index: number, value: string) => {
|
||||
setValue(value);
|
||||
debouncedUpdate(index, value);
|
||||
},
|
||||
[updateOption],
|
||||
);
|
||||
const handleChange = useCallback(() => props.onEdit && props.onEdit(index), [
|
||||
index,
|
||||
]);
|
||||
|
||||
const onFocus = () => setEditing(true);
|
||||
const onBlur = () => setEditing(false);
|
||||
|
||||
return (
|
||||
<ItemWrapper>
|
||||
<StyledDragIcon height={20} width={20} />
|
||||
<StyledOptionControlInputGroup
|
||||
dataType="text"
|
||||
onBlur={onBlur}
|
||||
onChange={(value: string) => {
|
||||
onChange(index, value);
|
||||
}}
|
||||
onFocus={onFocus}
|
||||
placeholder="Menu item label"
|
||||
trimValue={false}
|
||||
value={value}
|
||||
/>
|
||||
<StyledDeleteIcon
|
||||
className="t--delete-tab-btn"
|
||||
height={20}
|
||||
marginRight={12}
|
||||
onClick={() => {
|
||||
deleteOption(index);
|
||||
}}
|
||||
width={20}
|
||||
/>
|
||||
<StyledEditIcon
|
||||
className="t--edit-column-btn"
|
||||
height={20}
|
||||
onClick={handleChange}
|
||||
width={20}
|
||||
/>
|
||||
</ItemWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
class MenuItemsControl extends BaseControl<ControlProps> {
|
||||
componentDidUpdate(prevProps: ControlProps): void {
|
||||
//on adding a new column last column should get focused
|
||||
if (
|
||||
prevProps.propertyValue &&
|
||||
this.props.propertyValue &&
|
||||
Object.keys(prevProps.propertyValue).length + 1 ===
|
||||
Object.keys(this.props.propertyValue).length
|
||||
) {
|
||||
this.updateFocus(Object.keys(this.props.propertyValue).length - 1, true);
|
||||
}
|
||||
}
|
||||
updateItems = (items: Array<Record<string, any>>) => {
|
||||
const menuItems = items.reduce((obj: any, each: any, index: number) => {
|
||||
obj[each.id] = {
|
||||
|
|
@ -146,11 +88,20 @@ class MenuItemsControl extends BaseControl<ControlProps> {
|
|||
<MenuItemsWrapper>
|
||||
<DroppableComponent
|
||||
deleteOption={this.deleteOption}
|
||||
fixedHeight={370}
|
||||
focusedIndex={this.state.focusedIndex}
|
||||
itemHeight={45}
|
||||
items={orderBy(menuItems, ["index"], ["asc"])}
|
||||
onEdit={this.onEdit}
|
||||
renderComponent={MenuItemComponent}
|
||||
renderComponent={(props) =>
|
||||
DraggableListCard({
|
||||
...props,
|
||||
isDelete: true,
|
||||
placeholder: "Menu item label",
|
||||
})
|
||||
}
|
||||
toggleVisibility={this.toggleVisibility}
|
||||
updateFocus={this.updateFocus}
|
||||
updateItems={this.updateItems}
|
||||
updateOption={this.updateOption}
|
||||
/>
|
||||
|
|
@ -243,6 +194,10 @@ class MenuItemsControl extends BaseControl<ControlProps> {
|
|||
this.updateProperty(this.props.propertyName, menuItems);
|
||||
};
|
||||
|
||||
updateFocus = (index: number, isFocused: boolean) => {
|
||||
this.setState({ focusedIndex: isFocused ? index : null });
|
||||
};
|
||||
|
||||
static getControlType() {
|
||||
return "MENU_ITEMS";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,19 +1,11 @@
|
|||
import React, { useCallback, useEffect, useState, Component } from "react";
|
||||
import React, { 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";
|
||||
import BaseControl, { ControlProps } from "./BaseControl";
|
||||
import {
|
||||
StyledDragIcon,
|
||||
StyledEditIcon,
|
||||
StyledDeleteIcon,
|
||||
StyledVisibleIcon,
|
||||
StyledHiddenIcon,
|
||||
StyledPropertyPaneButton,
|
||||
StyledOptionControlInputGroup,
|
||||
} from "./StyledControls";
|
||||
import { StyledPropertyPaneButton } from "./StyledControls";
|
||||
import styled from "constants/DefaultTheme";
|
||||
import { Indices } from "constants/Layers";
|
||||
import { DroppableComponent } from "components/ads/DraggableListComponent";
|
||||
|
|
@ -37,17 +29,7 @@ import {
|
|||
PropertyEvaluationErrorType,
|
||||
} from "utils/DynamicBindingUtils";
|
||||
import { getNextEntityName } from "utils/AppsmithUtils";
|
||||
import { Colors } from "constants/Colors";
|
||||
import { noop } from "utils/AppsmithUtils";
|
||||
|
||||
const ItemWrapper = styled.div`
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
&.has-duplicate-label > div:nth-child(2) {
|
||||
border: 1px solid ${Colors.DANGER_SOLID};
|
||||
}
|
||||
`;
|
||||
import { DraggableListCard } from "components/ads/DraggableListCard";
|
||||
|
||||
const TabsWrapper = styled.div`
|
||||
width: 100%;
|
||||
|
|
@ -83,21 +65,6 @@ type EvaluatedValuePopupWrapperProps = ReduxStateProps & {
|
|||
children: JSX.Element;
|
||||
};
|
||||
|
||||
type RenderComponentProps = {
|
||||
index: number;
|
||||
item: {
|
||||
label: string;
|
||||
isDerived?: boolean;
|
||||
isVisible?: boolean;
|
||||
isDuplicateLabel?: boolean;
|
||||
};
|
||||
updateFocus?: (index: number, isFocused: boolean) => void;
|
||||
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,
|
||||
|
|
@ -110,107 +77,6 @@ const getOriginalColumn = (
|
|||
return column;
|
||||
};
|
||||
|
||||
function ColumnControlComponent(props: RenderComponentProps) {
|
||||
const [value, setValue] = useState(props.item.label);
|
||||
const [isEditing, setEditing] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isEditing && props.item && props.item.label)
|
||||
setValue(props.item.label);
|
||||
}, [props.item?.label, isEditing]);
|
||||
|
||||
const {
|
||||
deleteOption,
|
||||
index,
|
||||
item,
|
||||
onEdit,
|
||||
toggleVisibility,
|
||||
updateFocus,
|
||||
updateOption,
|
||||
} = props;
|
||||
|
||||
const [visibility, setVisibility] = useState(item.isVisible);
|
||||
useEffect(() => {
|
||||
setVisibility(item.isVisible);
|
||||
}, [item.isVisible]);
|
||||
const debouncedUpdate = _.debounce(updateOption, 1000);
|
||||
const debouncedFocus = updateFocus ? _.debounce(updateFocus, 400) : noop;
|
||||
const onChange = useCallback(
|
||||
(index: number, value: string) => {
|
||||
setValue(value);
|
||||
debouncedUpdate(index, value);
|
||||
},
|
||||
[updateOption],
|
||||
);
|
||||
|
||||
const onFocus = () => {
|
||||
setEditing(false);
|
||||
debouncedFocus(index, true);
|
||||
};
|
||||
const onBlur = () => {
|
||||
setEditing(false);
|
||||
debouncedFocus(index, false);
|
||||
};
|
||||
|
||||
return (
|
||||
<ItemWrapper
|
||||
className={props.item.isDuplicateLabel ? "has-duplicate-label" : ""}
|
||||
>
|
||||
<StyledDragIcon height={20} width={20} />
|
||||
<StyledOptionControlInputGroup
|
||||
dataType="text"
|
||||
onBlur={onBlur}
|
||||
onChange={(value: string) => {
|
||||
onChange(index, value);
|
||||
}}
|
||||
onFocus={onFocus}
|
||||
placeholder="Column Title"
|
||||
trimValue={false}
|
||||
value={value}
|
||||
width="100%"
|
||||
/>
|
||||
<StyledEditIcon
|
||||
className="t--edit-column-btn"
|
||||
height={20}
|
||||
onClick={() => {
|
||||
onEdit && onEdit(index);
|
||||
}}
|
||||
width={20}
|
||||
/>
|
||||
{!!item.isDerived ? (
|
||||
<StyledDeleteIcon
|
||||
className="t--delete-column-btn"
|
||||
height={20}
|
||||
onClick={() => {
|
||||
deleteOption && deleteOption(index);
|
||||
}}
|
||||
width={20}
|
||||
/>
|
||||
) : visibility ? (
|
||||
<StyledVisibleIcon
|
||||
className="t--show-column-btn"
|
||||
height={20}
|
||||
onClick={() => {
|
||||
setVisibility(!visibility);
|
||||
toggleVisibility && toggleVisibility(index);
|
||||
}}
|
||||
width={20}
|
||||
/>
|
||||
) : (
|
||||
<StyledHiddenIcon
|
||||
className="t--show-column-btn"
|
||||
height={20}
|
||||
onClick={() => {
|
||||
setVisibility(!visibility);
|
||||
toggleVisibility && toggleVisibility(index);
|
||||
}}
|
||||
width={20}
|
||||
/>
|
||||
)}
|
||||
</ItemWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
type State = {
|
||||
focusedIndex: number | null;
|
||||
duplicateColumnIds: string[];
|
||||
|
|
@ -241,6 +107,16 @@ class PrimaryColumnsControl extends BaseControl<ControlProps, State> {
|
|||
};
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: ControlProps): void {
|
||||
//on adding a new column last column should get focused
|
||||
if (
|
||||
Object.keys(prevProps.propertyValue).length + 1 ===
|
||||
Object.keys(this.props.propertyValue).length
|
||||
) {
|
||||
this.updateFocus(Object.keys(this.props.propertyValue).length - 1, true);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
// Get columns from widget properties
|
||||
const columns: Record<string, ColumnProperties> =
|
||||
|
|
@ -291,10 +167,18 @@ class PrimaryColumnsControl extends BaseControl<ControlProps, State> {
|
|||
<EvaluatedValuePopupWrapper {...this.props} isFocused={isFocused}>
|
||||
<DroppableComponent
|
||||
deleteOption={this.deleteOption}
|
||||
fixedHeight={370}
|
||||
focusedIndex={this.state.focusedIndex}
|
||||
itemHeight={45}
|
||||
items={draggableComponentColumns}
|
||||
onEdit={this.onEdit}
|
||||
renderComponent={ColumnControlComponent}
|
||||
renderComponent={(props) =>
|
||||
DraggableListCard({
|
||||
...props,
|
||||
isDelete: false,
|
||||
placeholder: "Column Title",
|
||||
})
|
||||
}
|
||||
toggleVisibility={this.toggleVisibility}
|
||||
updateFocus={this.updateFocus}
|
||||
updateItems={this.updateItems}
|
||||
|
|
@ -445,6 +329,8 @@ class PrimaryColumnsControl extends BaseControl<ControlProps, State> {
|
|||
this.setState({ focusedIndex: isFocused ? index : null });
|
||||
};
|
||||
|
||||
// updateCurrentFocusedInput = (index: number | null) => {};
|
||||
|
||||
static getControlType() {
|
||||
return "PRIMARY_COLUMNS";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +1,16 @@
|
|||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import React from "react";
|
||||
import BaseControl, { ControlProps } from "./BaseControl";
|
||||
import {
|
||||
StyledPropertyPaneButton,
|
||||
StyledDragIcon,
|
||||
StyledDeleteIcon,
|
||||
StyledEditIcon,
|
||||
StyledOptionControlInputGroup,
|
||||
} from "./StyledControls";
|
||||
import { StyledPropertyPaneButton } from "./StyledControls";
|
||||
import styled from "constants/DefaultTheme";
|
||||
import { generateReactKey } from "utils/generators";
|
||||
import { DroppableComponent } from "components/ads/DraggableListComponent";
|
||||
import { getNextEntityName, noop } from "utils/AppsmithUtils";
|
||||
import _, { debounce, orderBy } from "lodash";
|
||||
import _, { orderBy } from "lodash";
|
||||
import * as Sentry from "@sentry/react";
|
||||
import { Category, Size } from "components/ads/Button";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { ReduxActionTypes } from "constants/ReduxActionConstants";
|
||||
import { DraggableListCard } from "components/ads/DraggableListCard";
|
||||
|
||||
const StyledPropertyPaneButtonWrapper = styled.div`
|
||||
display: flex;
|
||||
|
|
@ -24,12 +19,6 @@ const StyledPropertyPaneButtonWrapper = styled.div`
|
|||
margin-top: 10px;
|
||||
`;
|
||||
|
||||
const ItemWrapper = styled.div`
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
const TabsWrapper = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
|
|
@ -37,12 +26,15 @@ const TabsWrapper = styled.div`
|
|||
`;
|
||||
|
||||
type RenderComponentProps = {
|
||||
focusedIndex: number | null | undefined;
|
||||
index: number;
|
||||
isDragging: boolean;
|
||||
item: {
|
||||
label: string;
|
||||
isVisible?: boolean;
|
||||
};
|
||||
deleteOption: (index: number) => void;
|
||||
updateFocus?: (index: number, isFocused: boolean) => void;
|
||||
updateOption: (index: number, value: string) => void;
|
||||
toggleVisibility?: (index: number) => void;
|
||||
onEdit?: (props: any) => void;
|
||||
|
|
@ -74,7 +66,7 @@ function AddTabButtonComponent({ widgetId }: any) {
|
|||
}
|
||||
|
||||
function TabControlComponent(props: RenderComponentProps) {
|
||||
const { index, item, updateOption } = props;
|
||||
const { index, item } = props;
|
||||
const dispatch = useDispatch();
|
||||
const deleteOption = () => {
|
||||
dispatch({
|
||||
|
|
@ -83,65 +75,42 @@ function TabControlComponent(props: RenderComponentProps) {
|
|||
});
|
||||
};
|
||||
|
||||
const [value, setValue] = useState(item.label);
|
||||
const [isEditing, setEditing] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isEditing && item && item.label) setValue(item.label);
|
||||
}, [item?.label, isEditing]);
|
||||
|
||||
const debouncedUpdate = debounce(updateOption, 1000);
|
||||
const handleChange = useCallback(() => props.onEdit && props.onEdit(index), [
|
||||
index,
|
||||
]);
|
||||
|
||||
const onChange = useCallback(
|
||||
(index: number, value: string) => {
|
||||
setValue(value);
|
||||
debouncedUpdate(index, value);
|
||||
},
|
||||
[updateOption],
|
||||
);
|
||||
|
||||
const onFocus = () => setEditing(true);
|
||||
const onBlur = () => setEditing(false);
|
||||
|
||||
return (
|
||||
<ItemWrapper>
|
||||
<StyledDragIcon height={20} width={20} />
|
||||
<StyledOptionControlInputGroup
|
||||
dataType="text"
|
||||
onBlur={onBlur}
|
||||
onChange={(value: string) => {
|
||||
onChange(index, value);
|
||||
}}
|
||||
onFocus={onFocus}
|
||||
placeholder="Tab Title"
|
||||
trimValue={false}
|
||||
value={value}
|
||||
/>
|
||||
<StyledDeleteIcon
|
||||
className="t--delete-tab-btn"
|
||||
height={20}
|
||||
marginRight={12}
|
||||
onClick={deleteOption}
|
||||
width={20}
|
||||
/>
|
||||
<StyledEditIcon
|
||||
className="t--edit-column-btn"
|
||||
height={20}
|
||||
onClick={handleChange}
|
||||
width={20}
|
||||
/>
|
||||
</ItemWrapper>
|
||||
<DraggableListCard
|
||||
{...props}
|
||||
deleteOption={deleteOption}
|
||||
isDelete
|
||||
placeholder="Tab Title"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
class TabControl extends BaseControl<ControlProps> {
|
||||
type State = {
|
||||
focusedIndex: number | null;
|
||||
};
|
||||
|
||||
class TabControl extends BaseControl<ControlProps, State> {
|
||||
constructor(props: ControlProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
focusedIndex: null,
|
||||
};
|
||||
}
|
||||
componentDidMount() {
|
||||
this.migrateTabData(this.props.propertyValue);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: ControlProps): void {
|
||||
//on adding a new column last column should get focused
|
||||
if (
|
||||
Object.keys(prevProps.propertyValue).length + 1 ===
|
||||
Object.keys(this.props.propertyValue).length
|
||||
) {
|
||||
this.updateFocus(Object.keys(this.props.propertyValue).length - 1, true);
|
||||
}
|
||||
}
|
||||
|
||||
migrateTabData(
|
||||
tabData: Array<{
|
||||
id: string;
|
||||
|
|
@ -204,11 +173,14 @@ class TabControl extends BaseControl<ControlProps> {
|
|||
<TabsWrapper>
|
||||
<DroppableComponent
|
||||
deleteOption={noop}
|
||||
fixedHeight={370}
|
||||
focusedIndex={this.state.focusedIndex}
|
||||
itemHeight={45}
|
||||
items={orderBy(tabs, ["index"], ["asc"])}
|
||||
onEdit={this.onEdit}
|
||||
renderComponent={TabControlComponent}
|
||||
toggleVisibility={this.toggleVisibility}
|
||||
updateFocus={this.updateFocus}
|
||||
updateItems={this.updateItems}
|
||||
updateOption={this.updateOption}
|
||||
/>
|
||||
|
|
@ -269,6 +241,10 @@ class TabControl extends BaseControl<ControlProps> {
|
|||
this.updateProperty(this.props.propertyName, tabs);
|
||||
};
|
||||
|
||||
updateFocus = (index: number, isFocused: boolean) => {
|
||||
this.setState({ focusedIndex: isFocused ? index : null });
|
||||
};
|
||||
|
||||
static getControlType() {
|
||||
return "TABS_INPUT";
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user