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:
albinAppsmith 2022-05-16 12:09:32 +05:30 committed by GitHub
parent a956ff6d71
commit 7f241cb98e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 167 additions and 27 deletions

View File

@ -24,6 +24,7 @@ const SwitchBlock = styled.div<{ active?: boolean }>`
height: 100%;
flex: 1;
border: 1px solid transparent;
user-select: none;
${(props) =>
props.active &&

View File

@ -1,4 +1,4 @@
import React, { ReactNode } from "react";
import React, { RefObject, ReactNode } from "react";
import styled from "styled-components";
import { Collapse } from "@blueprintjs/core";
@ -10,11 +10,16 @@ export function EntityCollapse(props: {
isOpen: boolean;
step: number;
active?: boolean;
collapseRef?: RefObject<HTMLDivElement> | null;
}) {
if (!props.children) return null;
return (
<Collapse isOpen={props.isOpen}>
<CollapsedContainer active={props.active} step={props.step}>
<CollapsedContainer
active={props.active}
ref={props.collapseRef}
step={props.step}
>
{props.children}
</CollapsedContainer>
</Collapse>

View File

@ -5,6 +5,7 @@ import React, {
useRef,
forwardRef,
useCallback,
RefObject,
} from "react";
import styled, { css } from "styled-components";
import { Colors } from "constants/Colors";
@ -205,6 +206,7 @@ export type EntityProps = {
preRightIcon?: ReactNode;
onClickPreRightIcon?: () => void;
isSticky?: boolean;
collapseRef?: RefObject<HTMLDivElement> | null;
customAddButton?: ReactNode;
};
@ -363,7 +365,12 @@ export const Entity = forwardRef(
)}
<Loader isVisible={isUpdating} />
</EntityItem>
<Collapse active={props.active} isOpen={isOpen} step={props.step}>
<Collapse
active={props.active}
collapseRef={props.collapseRef}
isOpen={isOpen}
step={props.step}
>
{props.children}
</Collapse>
</Wrapper>

View File

@ -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 {
getCurrentApplicationId,
@ -33,12 +33,26 @@ import { setExplorerPinnedAction } from "actions/explorerActions";
import { selectAllPages } from "selectors/entitiesSelector";
import { builderURL, pageListEditorURL } from "RouteBuilder";
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 {
& > div:not(.t--entity-item) {
max-height: 144px !important;
overflow-y: auto !important;
& > div:not(.t--entity-item) > div > div {
max-height: 40vh;
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} {
@ -52,6 +66,10 @@ const StyledEntity = styled(Entity)`
}
`;
const RelativeContainer = styled.div`
position: relative;
`;
function Pages() {
const applicationId = useSelector(getCurrentApplicationId);
const pages: Page[] = useSelector(selectAllPages);
@ -59,11 +77,30 @@ function Pages() {
const pinned = useSelector(getExplorerPinned);
const dispatch = useDispatch();
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(() => {
document.getElementsByClassName("activePage")[0]?.scrollIntoView();
}, [currentPageId]);
useEffect(() => {
if ((isPagesOpen === null ? true : isPagesOpen) && pageResizeRef.current) {
pageResizeRef.current.style.height = storedHeight + "px";
}
}, [pageResizeRef]);
const switchPage = useCallback((page: Page) => {
history.push(
builderURL({
@ -165,25 +202,39 @@ function Pages() {
);
return (
<StyledEntity
action={onPageListSelection}
addButtonHelptext={createMessage(ADD_PAGE_TOOLTIP)}
alwaysShowRightIcon
className="group pages"
entityId="Pages"
icon={""}
isDefaultExpanded={isPagesOpen === null ? true : isPagesOpen}
name="PAGES"
onClickPreRightIcon={onPin}
onClickRightIcon={onClickRightIcon}
onCreate={createPageCallback}
onToggle={onPageToggle}
rightIcon={settingsIconWithTooltip}
searchKeyword={""}
step={0}
>
{pageElements}
</StyledEntity>
<RelativeContainer>
<StyledEntity
action={onPageListSelection}
addButtonHelptext={createMessage(ADD_PAGE_TOOLTIP)}
alwaysShowRightIcon
className="group pages"
collapseRef={pageResizeRef}
entityId="Pages"
icon={""}
isDefaultExpanded={isPagesOpen === null ? true : isPagesOpen}
name="PAGES"
onClickPreRightIcon={onPin}
onClickRightIcon={onClickRightIcon}
onCreate={createPageCallback}
onToggle={onPageToggle}
pagesSize={ENTITY_HEIGHT * pages.length}
rightIcon={settingsIconWithTooltip}
searchKeyword={""}
step={0}
>
{pageElements}
</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>
);
}

View 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;