feat: Entity explorer pages section resize (#13576)
* * Page container resize changes * * changed inline style to styled component * * Added preserve height functionality
This commit is contained in:
parent
a956ff6d71
commit
7f241cb98e
|
|
@ -24,6 +24,7 @@ const SwitchBlock = styled.div<{ active?: boolean }>`
|
||||||
height: 100%;
|
height: 100%;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
${(props) =>
|
${(props) =>
|
||||||
props.active &&
|
props.active &&
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { ReactNode } from "react";
|
import React, { RefObject, ReactNode } from "react";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { Collapse } from "@blueprintjs/core";
|
import { Collapse } from "@blueprintjs/core";
|
||||||
|
|
||||||
|
|
@ -10,11 +10,16 @@ export function EntityCollapse(props: {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
step: number;
|
step: number;
|
||||||
active?: boolean;
|
active?: boolean;
|
||||||
|
collapseRef?: RefObject<HTMLDivElement> | null;
|
||||||
}) {
|
}) {
|
||||||
if (!props.children) return null;
|
if (!props.children) return null;
|
||||||
return (
|
return (
|
||||||
<Collapse isOpen={props.isOpen}>
|
<Collapse isOpen={props.isOpen}>
|
||||||
<CollapsedContainer active={props.active} step={props.step}>
|
<CollapsedContainer
|
||||||
|
active={props.active}
|
||||||
|
ref={props.collapseRef}
|
||||||
|
step={props.step}
|
||||||
|
>
|
||||||
{props.children}
|
{props.children}
|
||||||
</CollapsedContainer>
|
</CollapsedContainer>
|
||||||
</Collapse>
|
</Collapse>
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import React, {
|
||||||
useRef,
|
useRef,
|
||||||
forwardRef,
|
forwardRef,
|
||||||
useCallback,
|
useCallback,
|
||||||
|
RefObject,
|
||||||
} from "react";
|
} from "react";
|
||||||
import styled, { css } from "styled-components";
|
import styled, { css } from "styled-components";
|
||||||
import { Colors } from "constants/Colors";
|
import { Colors } from "constants/Colors";
|
||||||
|
|
@ -205,6 +206,7 @@ export type EntityProps = {
|
||||||
preRightIcon?: ReactNode;
|
preRightIcon?: ReactNode;
|
||||||
onClickPreRightIcon?: () => void;
|
onClickPreRightIcon?: () => void;
|
||||||
isSticky?: boolean;
|
isSticky?: boolean;
|
||||||
|
collapseRef?: RefObject<HTMLDivElement> | null;
|
||||||
customAddButton?: ReactNode;
|
customAddButton?: ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -363,7 +365,12 @@ export const Entity = forwardRef(
|
||||||
)}
|
)}
|
||||||
<Loader isVisible={isUpdating} />
|
<Loader isVisible={isUpdating} />
|
||||||
</EntityItem>
|
</EntityItem>
|
||||||
<Collapse active={props.active} isOpen={isOpen} step={props.step}>
|
<Collapse
|
||||||
|
active={props.active}
|
||||||
|
collapseRef={props.collapseRef}
|
||||||
|
isOpen={isOpen}
|
||||||
|
step={props.step}
|
||||||
|
>
|
||||||
{props.children}
|
{props.children}
|
||||||
</Collapse>
|
</Collapse>
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useCallback, useMemo, useEffect } from "react";
|
import React, { useCallback, useMemo, useEffect, useRef } from "react";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import {
|
import {
|
||||||
getCurrentApplicationId,
|
getCurrentApplicationId,
|
||||||
|
|
@ -33,12 +33,26 @@ import { setExplorerPinnedAction } from "actions/explorerActions";
|
||||||
import { selectAllPages } from "selectors/entitiesSelector";
|
import { selectAllPages } from "selectors/entitiesSelector";
|
||||||
import { builderURL, pageListEditorURL } from "RouteBuilder";
|
import { builderURL, pageListEditorURL } from "RouteBuilder";
|
||||||
import { saveExplorerStatus, getExplorerStatus } from "../helpers";
|
import { saveExplorerStatus, getExplorerStatus } from "../helpers";
|
||||||
|
import { tailwindLayers } from "constants/Layers";
|
||||||
|
import useResize, {
|
||||||
|
DIRECTION,
|
||||||
|
CallbackResponseType,
|
||||||
|
} from "utils/hooks/useResize";
|
||||||
|
|
||||||
const StyledEntity = styled(Entity)`
|
const ENTITY_HEIGHT = 36;
|
||||||
|
const MIN_PAGES_HEIGHT = 60;
|
||||||
|
|
||||||
|
const StyledEntity = styled(Entity)<{ pagesSize?: number }>`
|
||||||
&.pages {
|
&.pages {
|
||||||
& > div:not(.t--entity-item) {
|
& > div:not(.t--entity-item) > div > div {
|
||||||
max-height: 144px !important;
|
max-height: 40vh;
|
||||||
overflow-y: auto !important;
|
min-height: ${(props) =>
|
||||||
|
props.pagesSize && props.pagesSize > MIN_PAGES_HEIGHT
|
||||||
|
? MIN_PAGES_HEIGHT
|
||||||
|
: props.pagesSize}px;
|
||||||
|
height: ${(props) =>
|
||||||
|
props.pagesSize && props.pagesSize > 128 ? 128 : props.pagesSize}px;
|
||||||
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&.page .${EntityClassNames.PRE_RIGHT_ICON} {
|
&.page .${EntityClassNames.PRE_RIGHT_ICON} {
|
||||||
|
|
@ -52,6 +66,10 @@ const StyledEntity = styled(Entity)`
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const RelativeContainer = styled.div`
|
||||||
|
position: relative;
|
||||||
|
`;
|
||||||
|
|
||||||
function Pages() {
|
function Pages() {
|
||||||
const applicationId = useSelector(getCurrentApplicationId);
|
const applicationId = useSelector(getCurrentApplicationId);
|
||||||
const pages: Page[] = useSelector(selectAllPages);
|
const pages: Page[] = useSelector(selectAllPages);
|
||||||
|
|
@ -59,11 +77,30 @@ function Pages() {
|
||||||
const pinned = useSelector(getExplorerPinned);
|
const pinned = useSelector(getExplorerPinned);
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const isPagesOpen = getExplorerStatus(applicationId, "pages");
|
const isPagesOpen = getExplorerStatus(applicationId, "pages");
|
||||||
|
const pageResizeRef = useRef<HTMLDivElement>(null);
|
||||||
|
const storedHeightKey = "pagesContainerHeight_" + applicationId;
|
||||||
|
const storedHeight = localStorage.getItem(storedHeightKey);
|
||||||
|
|
||||||
|
const resizeAfterCallback = (data: CallbackResponseType) => {
|
||||||
|
localStorage.setItem(storedHeightKey, data.height.toString());
|
||||||
|
};
|
||||||
|
|
||||||
|
const { mouseDown, setMouseDown } = useResize(
|
||||||
|
pageResizeRef,
|
||||||
|
DIRECTION.vertical,
|
||||||
|
resizeAfterCallback,
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.getElementsByClassName("activePage")[0]?.scrollIntoView();
|
document.getElementsByClassName("activePage")[0]?.scrollIntoView();
|
||||||
}, [currentPageId]);
|
}, [currentPageId]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if ((isPagesOpen === null ? true : isPagesOpen) && pageResizeRef.current) {
|
||||||
|
pageResizeRef.current.style.height = storedHeight + "px";
|
||||||
|
}
|
||||||
|
}, [pageResizeRef]);
|
||||||
|
|
||||||
const switchPage = useCallback((page: Page) => {
|
const switchPage = useCallback((page: Page) => {
|
||||||
history.push(
|
history.push(
|
||||||
builderURL({
|
builderURL({
|
||||||
|
|
@ -165,11 +202,13 @@ function Pages() {
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<RelativeContainer>
|
||||||
<StyledEntity
|
<StyledEntity
|
||||||
action={onPageListSelection}
|
action={onPageListSelection}
|
||||||
addButtonHelptext={createMessage(ADD_PAGE_TOOLTIP)}
|
addButtonHelptext={createMessage(ADD_PAGE_TOOLTIP)}
|
||||||
alwaysShowRightIcon
|
alwaysShowRightIcon
|
||||||
className="group pages"
|
className="group pages"
|
||||||
|
collapseRef={pageResizeRef}
|
||||||
entityId="Pages"
|
entityId="Pages"
|
||||||
icon={""}
|
icon={""}
|
||||||
isDefaultExpanded={isPagesOpen === null ? true : isPagesOpen}
|
isDefaultExpanded={isPagesOpen === null ? true : isPagesOpen}
|
||||||
|
|
@ -178,12 +217,24 @@ function Pages() {
|
||||||
onClickRightIcon={onClickRightIcon}
|
onClickRightIcon={onClickRightIcon}
|
||||||
onCreate={createPageCallback}
|
onCreate={createPageCallback}
|
||||||
onToggle={onPageToggle}
|
onToggle={onPageToggle}
|
||||||
|
pagesSize={ENTITY_HEIGHT * pages.length}
|
||||||
rightIcon={settingsIconWithTooltip}
|
rightIcon={settingsIconWithTooltip}
|
||||||
searchKeyword={""}
|
searchKeyword={""}
|
||||||
step={0}
|
step={0}
|
||||||
>
|
>
|
||||||
{pageElements}
|
{pageElements}
|
||||||
</StyledEntity>
|
</StyledEntity>
|
||||||
|
<div
|
||||||
|
className={`absolute -bottom-2 left-0 w-full h-2 group cursor-ns-resize ${tailwindLayers.resizer}`}
|
||||||
|
onMouseDown={() => setMouseDown(true)}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={`w-full h-1 bg-transparent hover:bg-gray-300 transform transition
|
||||||
|
${mouseDown ? "hover:bg-blue-500" : ""}
|
||||||
|
`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</RelativeContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
76
app/client/src/utils/hooks/useResize.tsx
Normal file
76
app/client/src/utils/hooks/useResize.tsx
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
import React, { MutableRefObject } from "react";
|
||||||
|
|
||||||
|
export enum DIRECTION {
|
||||||
|
vertical,
|
||||||
|
horizontal,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CallbackResponseType {
|
||||||
|
height: number;
|
||||||
|
width: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
function useResize(
|
||||||
|
ref: MutableRefObject<HTMLElement | null>,
|
||||||
|
direction: DIRECTION,
|
||||||
|
afterResizeCallback?: (data: CallbackResponseType) => void,
|
||||||
|
) {
|
||||||
|
const [mouseDown, setMouseDown] = React.useState(false);
|
||||||
|
|
||||||
|
const pointer =
|
||||||
|
direction === DIRECTION.vertical ? "cursor-ns-resize" : "cursor-ew-resize";
|
||||||
|
|
||||||
|
const onMouseMove = (e: MouseEvent) => {
|
||||||
|
document.body.classList.add(pointer);
|
||||||
|
if (ref.current) {
|
||||||
|
// below lines stop selection of texts
|
||||||
|
if (e.stopPropagation) e.stopPropagation();
|
||||||
|
if (e.preventDefault) e.preventDefault();
|
||||||
|
|
||||||
|
const { bottom, left, right, top } = ref.current.getBoundingClientRect();
|
||||||
|
|
||||||
|
const currentMouseYPosition = e.clientY;
|
||||||
|
const currentMouseXPosition = e.clientX;
|
||||||
|
|
||||||
|
const distanceYDragged = currentMouseYPosition - bottom;
|
||||||
|
const distanceXDragged = currentMouseXPosition - right;
|
||||||
|
|
||||||
|
const currentHeight = bottom - top;
|
||||||
|
const currentWidth = right - left;
|
||||||
|
|
||||||
|
const newHeight = currentHeight + distanceYDragged;
|
||||||
|
const newWidth = currentWidth + distanceXDragged;
|
||||||
|
|
||||||
|
if (direction === DIRECTION.vertical) {
|
||||||
|
ref.current.style.height = newHeight + "px";
|
||||||
|
} else {
|
||||||
|
ref.current.style.width = newWidth + "px";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (afterResizeCallback) {
|
||||||
|
afterResizeCallback({ height: newHeight, width: newWidth });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onMouseUp = () => {
|
||||||
|
setMouseDown(false);
|
||||||
|
document.body.classList.remove(pointer);
|
||||||
|
};
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (mouseDown) {
|
||||||
|
document.addEventListener("mousemove", onMouseMove);
|
||||||
|
document.addEventListener("mouseup", onMouseUp);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener("mousemove", onMouseMove);
|
||||||
|
document.removeEventListener("mouseup", onMouseUp);
|
||||||
|
};
|
||||||
|
}, [mouseDown]);
|
||||||
|
|
||||||
|
return { mouseDown, setMouseDown };
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useResize;
|
||||||
Loading…
Reference in New Issue
Block a user