Add animation to group open
This commit is contained in:
parent
0917ef05a1
commit
4e5bd8bf02
@ -16,9 +16,13 @@ function MapTileGroup({ group, maps, isSelected, onSelect, onDoubleClick }) {
|
|||||||
onSelect={() => onSelect(group.id)}
|
onSelect={() => onSelect(group.id)}
|
||||||
onDoubleClick={onDoubleClick}
|
onDoubleClick={onDoubleClick}
|
||||||
>
|
>
|
||||||
<Grid columns={layout.gridTemplate} p={2} sx={{ gridGap: 2 }}>
|
<Grid columns={layout.groupGridTemplate} p={2} sx={{ gridGap: 2 }}>
|
||||||
{maps.slice(0, 16).map((map) => (
|
{maps.slice(0, 16).map((map) => (
|
||||||
<MapTileImage sx={{ borderRadius: "8px" }} map={map} key={map.id} />
|
<MapTileImage
|
||||||
|
sx={{ borderRadius: "8px" }}
|
||||||
|
map={map}
|
||||||
|
key={`${map.id}-group-tile`}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</Grid>
|
</Grid>
|
||||||
</Tile>
|
</Tile>
|
||||||
|
@ -13,6 +13,7 @@ function MapTiles({ maps, onMapEdit, onMapSelect, subgroup }) {
|
|||||||
const {
|
const {
|
||||||
groups,
|
groups,
|
||||||
selectedGroupIds,
|
selectedGroupIds,
|
||||||
|
openGroupId,
|
||||||
openGroupItems,
|
openGroupItems,
|
||||||
selectMode,
|
selectMode,
|
||||||
onGroupOpen,
|
onGroupOpen,
|
||||||
@ -63,6 +64,7 @@ function MapTiles({ maps, onMapEdit, onMapSelect, subgroup }) {
|
|||||||
renderTile={renderTile}
|
renderTile={renderTile}
|
||||||
onTileSelect={onGroupSelect}
|
onTileSelect={onGroupSelect}
|
||||||
disableGrouping={subgroup}
|
disableGrouping={subgroup}
|
||||||
|
openGroupId={openGroupId}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,9 @@ import React from "react";
|
|||||||
import { Box } from "theme-ui";
|
import { Box } from "theme-ui";
|
||||||
import { useDroppable } from "@dnd-kit/core";
|
import { useDroppable } from "@dnd-kit/core";
|
||||||
import { useSortable } from "@dnd-kit/sortable";
|
import { useSortable } from "@dnd-kit/sortable";
|
||||||
|
import { animated, useSpring } from "react-spring";
|
||||||
|
|
||||||
function Sortable({ id, disableGrouping, children }) {
|
function Sortable({ id, disableGrouping, hidden, children }) {
|
||||||
const {
|
const {
|
||||||
attributes,
|
attributes,
|
||||||
listeners,
|
listeners,
|
||||||
@ -48,8 +49,10 @@ function Sortable({ id, disableGrouping, children }) {
|
|||||||
over?.id === `__group__${id}` && active.id !== id ? "solid" : "none",
|
over?.id === `__group__${id}` && active.id !== id ? "solid" : "none",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const { opacity } = useSpring({ opacity: hidden ? 0 : 1 });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ position: "relative" }}>
|
<animated.div style={{ opacity, position: "relative" }}>
|
||||||
<Box
|
<Box
|
||||||
ref={setDraggableNodeRef}
|
ref={setDraggableNodeRef}
|
||||||
style={dragStyle}
|
style={dragStyle}
|
||||||
@ -75,7 +78,7 @@ function Sortable({ id, disableGrouping, children }) {
|
|||||||
sx={{ borderColor: "primary" }}
|
sx={{ borderColor: "primary" }}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</animated.div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,12 +11,9 @@ import {
|
|||||||
} from "@dnd-kit/core";
|
} from "@dnd-kit/core";
|
||||||
import { SortableContext, arrayMove } from "@dnd-kit/sortable";
|
import { SortableContext, arrayMove } from "@dnd-kit/sortable";
|
||||||
import { animated, useSpring, config } from "react-spring";
|
import { animated, useSpring, config } from "react-spring";
|
||||||
import { Grid } from "theme-ui";
|
|
||||||
|
|
||||||
import { combineGroups, moveGroups } from "../../helpers/group";
|
import { combineGroups, moveGroups } from "../../helpers/group";
|
||||||
|
|
||||||
import useResponsiveLayout from "../../hooks/useResponsiveLayout";
|
|
||||||
|
|
||||||
import SortableTile from "./SortableTile";
|
import SortableTile from "./SortableTile";
|
||||||
|
|
||||||
function SortableTiles({
|
function SortableTiles({
|
||||||
@ -25,9 +22,8 @@ function SortableTiles({
|
|||||||
renderTile,
|
renderTile,
|
||||||
onTileSelect,
|
onTileSelect,
|
||||||
disableGrouping,
|
disableGrouping,
|
||||||
|
openGroupId,
|
||||||
}) {
|
}) {
|
||||||
const layout = useResponsiveLayout();
|
|
||||||
|
|
||||||
const mouseSensor = useSensor(MouseSensor, {
|
const mouseSensor = useSensor(MouseSensor, {
|
||||||
activationConstraint: { delay: 250, tolerance: 5 },
|
activationConstraint: { delay: 250, tolerance: 5 },
|
||||||
});
|
});
|
||||||
@ -105,27 +101,16 @@ function SortableTiles({
|
|||||||
collisionDetection={closestCenter}
|
collisionDetection={closestCenter}
|
||||||
>
|
>
|
||||||
<SortableContext items={groups}>
|
<SortableContext items={groups}>
|
||||||
<Grid
|
{groups.map((group) => (
|
||||||
p={3}
|
<SortableTile
|
||||||
pb={4}
|
id={group.id}
|
||||||
sx={{
|
key={group.id}
|
||||||
borderRadius: "4px",
|
disableGrouping={disableGrouping}
|
||||||
overflow: "hidden",
|
hidden={group.id === openGroupId}
|
||||||
}}
|
>
|
||||||
gap={2}
|
{renderSortableGroup(group)}
|
||||||
columns={layout.gridTemplate}
|
</SortableTile>
|
||||||
onClick={() => onTileSelect()}
|
))}
|
||||||
>
|
|
||||||
{groups.map((group) => (
|
|
||||||
<SortableTile
|
|
||||||
id={group.id}
|
|
||||||
key={group.id}
|
|
||||||
disableGrouping={disableGrouping}
|
|
||||||
>
|
|
||||||
{renderSortableGroup(group)}
|
|
||||||
</SortableTile>
|
|
||||||
))}
|
|
||||||
</Grid>
|
|
||||||
{createPortal(
|
{createPortal(
|
||||||
<DragOverlay dropAnimation={null}>
|
<DragOverlay dropAnimation={null}>
|
||||||
{dragId && (
|
{dragId && (
|
||||||
|
@ -1,14 +1,40 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { Box, Grid } from "theme-ui";
|
||||||
import SimpleBar from "simplebar-react";
|
import SimpleBar from "simplebar-react";
|
||||||
|
|
||||||
|
import { useGroup } from "../../contexts/GroupContext";
|
||||||
|
|
||||||
import useResponsiveLayout from "../../hooks/useResponsiveLayout";
|
import useResponsiveLayout from "../../hooks/useResponsiveLayout";
|
||||||
|
|
||||||
function TilesContainer({ children }) {
|
function TilesContainer({ children }) {
|
||||||
|
const { onGroupSelect } = useGroup();
|
||||||
|
|
||||||
const layout = useResponsiveLayout();
|
const layout = useResponsiveLayout();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SimpleBar style={{ height: layout.tileContainerHeight }}>
|
<SimpleBar style={{ height: layout.tileContainerHeight }}>
|
||||||
{children}
|
<Box
|
||||||
|
sx={{
|
||||||
|
position: "absolute",
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
top: 0,
|
||||||
|
}}
|
||||||
|
bg="muted"
|
||||||
|
onClick={() => onGroupSelect()}
|
||||||
|
/>
|
||||||
|
<Grid
|
||||||
|
p={3}
|
||||||
|
pb={4}
|
||||||
|
sx={{
|
||||||
|
borderRadius: "4px",
|
||||||
|
overflow: "hidden",
|
||||||
|
}}
|
||||||
|
gap={2}
|
||||||
|
columns={layout.gridTemplate}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Grid>
|
||||||
</SimpleBar>
|
</SimpleBar>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,48 +1,111 @@
|
|||||||
import React from "react";
|
import React, { useState } from "react";
|
||||||
import { Box, Close } from "theme-ui";
|
import { Box, Close, Grid } from "theme-ui";
|
||||||
|
import { useSpring, animated, config } from "react-spring";
|
||||||
|
import ReactResizeDetector from "react-resize-detector";
|
||||||
|
import SimpleBar from "simplebar-react";
|
||||||
|
|
||||||
import { useGroup } from "../../contexts/GroupContext";
|
import { useGroup } from "../../contexts/GroupContext";
|
||||||
|
|
||||||
function TilesOverlay({ children }) {
|
import useResponsiveLayout from "../../hooks/useResponsiveLayout";
|
||||||
const { openGroupId, onGroupClose } = useGroup();
|
|
||||||
|
|
||||||
if (!openGroupId) {
|
function TilesOverlay({ children }) {
|
||||||
return null;
|
const { openGroupId, onGroupClose, onGroupSelect } = useGroup();
|
||||||
|
|
||||||
|
const openAnimation = useSpring({
|
||||||
|
opacity: openGroupId ? 1 : 0,
|
||||||
|
transform: openGroupId ? "scale(1)" : "scale(0.95)",
|
||||||
|
config: config.gentle,
|
||||||
|
});
|
||||||
|
|
||||||
|
const [containerSize, setContinerSize] = useState(0);
|
||||||
|
function handleResize(width, height) {
|
||||||
|
const size = Math.min(width, height) - 16;
|
||||||
|
setContinerSize(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const layout = useResponsiveLayout();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<>
|
||||||
sx={{
|
{openGroupId && (
|
||||||
position: "absolute",
|
<Box
|
||||||
width: "100%",
|
sx={{
|
||||||
height: "100%",
|
position: "absolute",
|
||||||
top: 0,
|
width: "100%",
|
||||||
cursor: "pointer",
|
height: "100%",
|
||||||
}}
|
top: 0,
|
||||||
p={3}
|
}}
|
||||||
bg="overlay"
|
bg="overlay"
|
||||||
onClick={() => onGroupClose()}
|
/>
|
||||||
>
|
)}
|
||||||
<Box
|
<ReactResizeDetector handleWidth handleHeight onResize={handleResize}>
|
||||||
sx={{
|
<animated.div
|
||||||
width: "100%",
|
style={{
|
||||||
height: "100%",
|
...openAnimation,
|
||||||
borderRadius: "8px",
|
position: "absolute",
|
||||||
border: "1px solid",
|
width: "100%",
|
||||||
borderColor: "border",
|
height: "100%",
|
||||||
cursor: "default",
|
top: 0,
|
||||||
}}
|
display: "flex",
|
||||||
bg="muted"
|
alignItems: "center",
|
||||||
onClick={(e) => e.stopPropagation()}
|
justifyContent: "center",
|
||||||
p={3}
|
cursor: "pointer",
|
||||||
>
|
pointerEvents: openGroupId ? undefined : "none",
|
||||||
{children}
|
}}
|
||||||
</Box>
|
onClick={() => openGroupId && onGroupClose()}
|
||||||
<Close
|
>
|
||||||
onClick={() => onGroupClose()}
|
<Box
|
||||||
sx={{ position: "absolute", top: "16px", right: "16px" }}
|
sx={{
|
||||||
/>
|
width: containerSize,
|
||||||
</Box>
|
height: containerSize,
|
||||||
|
borderRadius: "8px",
|
||||||
|
border: "1px solid",
|
||||||
|
borderColor: "border",
|
||||||
|
cursor: "default",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
position: "relative",
|
||||||
|
}}
|
||||||
|
bg="background"
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
position: "absolute",
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
top: 0,
|
||||||
|
}}
|
||||||
|
bg="muted"
|
||||||
|
onClick={() => onGroupSelect()}
|
||||||
|
/>
|
||||||
|
<SimpleBar
|
||||||
|
style={{
|
||||||
|
width: containerSize - 16,
|
||||||
|
height: containerSize - 48,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Grid
|
||||||
|
sx={{
|
||||||
|
borderRadius: "4px",
|
||||||
|
overflow: "hidden",
|
||||||
|
}}
|
||||||
|
gap={2}
|
||||||
|
columns={layout.groupGridTemplate}
|
||||||
|
px={3}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Grid>
|
||||||
|
</SimpleBar>
|
||||||
|
<Close
|
||||||
|
onClick={() => onGroupClose()}
|
||||||
|
sx={{ position: "absolute", top: 0, right: 0 }}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</animated.div>
|
||||||
|
</ReactResizeDetector>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ function TokenBar({ onMapTokenStateCreate }) {
|
|||||||
height: "100%",
|
height: "100%",
|
||||||
width: "80px",
|
width: "80px",
|
||||||
minWidth: "80px",
|
minWidth: "80px",
|
||||||
overflowY: "scroll",
|
overflowY: "hidden",
|
||||||
overflowX: "hidden",
|
overflowX: "hidden",
|
||||||
display: fullScreen ? "none" : "block",
|
display: fullScreen ? "none" : "block",
|
||||||
}}
|
}}
|
||||||
|
@ -23,12 +23,12 @@ function TokenTileGroup({
|
|||||||
onDoubleClick={onDoubleClick}
|
onDoubleClick={onDoubleClick}
|
||||||
columns="1fr 1fr"
|
columns="1fr 1fr"
|
||||||
>
|
>
|
||||||
<Grid columns={layout.gridTemplate} p={2} sx={{ gridGap: 2 }}>
|
<Grid columns={layout.groupGridTemplate} p={2} sx={{ gridGap: 2 }}>
|
||||||
{tokens.slice(0, 16).map((token) => (
|
{tokens.slice(0, 16).map((token) => (
|
||||||
<TokenTileImage
|
<TokenTileImage
|
||||||
sx={{ borderRadius: "8px" }}
|
sx={{ borderRadius: "8px" }}
|
||||||
token={token}
|
token={token}
|
||||||
key={token.id}
|
key={`${token.id}-group-tile`}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Grid>
|
</Grid>
|
||||||
|
@ -13,6 +13,7 @@ function TokenTiles({ tokens, onTokenEdit, subgroup }) {
|
|||||||
const {
|
const {
|
||||||
groups,
|
groups,
|
||||||
selectedGroupIds,
|
selectedGroupIds,
|
||||||
|
openGroupId,
|
||||||
openGroupItems,
|
openGroupItems,
|
||||||
selectMode,
|
selectMode,
|
||||||
onGroupOpen,
|
onGroupOpen,
|
||||||
@ -68,6 +69,7 @@ function TokenTiles({ tokens, onTokenEdit, subgroup }) {
|
|||||||
renderTile={renderTile}
|
renderTile={renderTile}
|
||||||
onTileSelect={onGroupSelect}
|
onTileSelect={onGroupSelect}
|
||||||
disableGrouping={subgroup}
|
disableGrouping={subgroup}
|
||||||
|
openGroupId={openGroupId}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -27,9 +27,18 @@ function useResponsiveLayout() {
|
|||||||
? "1fr 1fr 1fr"
|
? "1fr 1fr 1fr"
|
||||||
: "1fr 1fr";
|
: "1fr 1fr";
|
||||||
|
|
||||||
|
const groupGridTemplate = isLargeScreen ? "1fr 1fr 1fr" : "1fr 1fr";
|
||||||
|
|
||||||
const tileContainerHeight = isLargeScreen ? "600px" : "400px";
|
const tileContainerHeight = isLargeScreen ? "600px" : "400px";
|
||||||
|
|
||||||
return { screenSize, modalSize, tileSize, gridTemplate, tileContainerHeight };
|
return {
|
||||||
|
screenSize,
|
||||||
|
modalSize,
|
||||||
|
tileSize,
|
||||||
|
gridTemplate,
|
||||||
|
tileContainerHeight,
|
||||||
|
groupGridTemplate,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default useResponsiveLayout;
|
export default useResponsiveLayout;
|
||||||
|
@ -252,7 +252,7 @@ function SelectMapModal({
|
|||||||
<Label pt={2} pb={1}>
|
<Label pt={2} pb={1}>
|
||||||
Select or import a map
|
Select or import a map
|
||||||
</Label>
|
</Label>
|
||||||
<Box sx={{ position: "relative" }} bg="muted">
|
<Box sx={{ position: "relative" }}>
|
||||||
<GroupProvider
|
<GroupProvider
|
||||||
groups={mapGroups}
|
groups={mapGroups}
|
||||||
onGroupsChange={updateMapGroups}
|
onGroupsChange={updateMapGroups}
|
||||||
|
@ -193,7 +193,7 @@ function SelectTokensModal({ isOpen, onRequestClose }) {
|
|||||||
<Label pt={2} pb={1}>
|
<Label pt={2} pb={1}>
|
||||||
Edit or import a token
|
Edit or import a token
|
||||||
</Label>
|
</Label>
|
||||||
<Box sx={{ position: "relative" }} bg="muted">
|
<Box sx={{ position: "relative" }}>
|
||||||
<GroupProvider
|
<GroupProvider
|
||||||
groups={tokenGroups}
|
groups={tokenGroups}
|
||||||
onGroupsChange={updateTokenGroups}
|
onGroupsChange={updateTokenGroups}
|
||||||
|
Loading…
Reference in New Issue
Block a user