Skip to content

Commit

Permalink
feat(icicles): handle events, state & refacto icicles
Browse files Browse the repository at this point in the history
  • Loading branch information
mehdilouraoui committed Jun 11, 2021
1 parent f0040d1 commit 0e9e9e8
Show file tree
Hide file tree
Showing 5 changed files with 295 additions and 133 deletions.
39 changes: 37 additions & 2 deletions src/components/main-space/new-icicle/icicles-container.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,54 @@
import React, { FC } from "react";
import { useSelector } from "react-redux";
import React, { FC, useCallback } from "react";
import { useDispatch, useSelector } from "react-redux";
import { getFilesAndFoldersFromStore } from "reducers/files-and-folders/files-and-folders-selectors";
import Icicles from "./icicles";
import { getFilesAndFoldersMetadataFromStore } from "reducers/files-and-folders-metadata/files-and-folders-metadata-selectors";
import {
setHoveredElementId,
setLockedElementId,
} from "reducers/workspace-metadata/workspace-metadata-actions";
import { getWorkspaceMetadataFromStore } from "reducers/workspace-metadata/workspace-metadata-selectors";

const IciclesContainer: FC = () => {
const filesAndFoldersMap = useSelector(getFilesAndFoldersFromStore);
const filesAndFoldersMetadataMap = useSelector(
getFilesAndFoldersMetadataFromStore
);

const { lockedElementId } = useSelector(getWorkspaceMetadataFromStore);

const dispatch = useDispatch();

const setHoveredElement = useCallback(
(id) => dispatch(setHoveredElementId(id)),
[dispatch]
);

const resetHoveredElement = useCallback(
() => dispatch(setHoveredElementId("")),
[dispatch]
);

const setLockedElement = useCallback(
(id) => {
dispatch(setLockedElementId(id));
},
[dispatch]
);
const resetLockedElement = useCallback(
() => dispatch(setLockedElementId("")),
[dispatch]
);

return (
<Icicles
filesAndFolders={filesAndFoldersMap}
filesAndFoldersMetadata={filesAndFoldersMetadataMap}
setHoveredElement={setHoveredElement}
resetHoveredElement={resetHoveredElement}
setLockedElement={setLockedElement}
resetLockedElement={resetLockedElement}
lockedElementId={lockedElementId}
/>
);
};
Expand Down
89 changes: 89 additions & 0 deletions src/components/main-space/new-icicle/icicles-elements.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { ROOT_FF_ID } from "reducers/files-and-folders/files-and-folders-selectors";
import { FilesAndFolders } from "reducers/files-and-folders/files-and-folders-types";
import { fromFileName } from "./../../../util/color/color-util";
import {
getRectangleHeight,
isLabelVisible,
format,
VIEWBOX_WIDTH,
VIEWBOX_HEIGHT,
handleZoom,
} from "./icicles-utils";
import { FOLDER_COLOR } from "util/color/color-util";
import * as d3 from "d3";

export const createPartition = (filesAndFolders) => {
const root = d3
.hierarchy<FilesAndFolders>(
filesAndFolders[ROOT_FF_ID],
(element) =>
element?.children.map((childId) => filesAndFolders[childId]) ?? []
)
.sum((d) => d?.file_size ?? 0);
return d3
.partition<FilesAndFolders>()
.size([VIEWBOX_HEIGHT, ((root.height + 1) * VIEWBOX_WIDTH) / 10])(root);
};

export const createSvg = (ref) =>
d3
.select(ref.current)
.append("svg")
.attr("viewBox", [0, 0, VIEWBOX_WIDTH, VIEWBOX_HEIGHT].join(" "))
.style("font", "10px Quicksand");

export const createCell = (svg, root) => {
return svg
.selectAll("g")
.data(root.descendants())
.join("g")
.attr("transform", ({ x0, y0 }) => `translate(${y0},${x0})`);
};

export const createRect = (cell, elements) => {
return cell
.append("rect")
.attr("width", ({ y0, y1 }) => y1 - y0 - 1)
.attr("height", (d) => getRectangleHeight(d))
.attr("fill-opacity", 0.5)
.attr("fill", (d: any) => {
if (!d.depth) {
return "#ccc";
}
if (d.data.children.length > 0) {
return FOLDER_COLOR;
}
return fromFileName(d.data.name);
})
.style("cursor", "pointer")
.on("dblclick", (_, currentRect) => handleZoom(_, currentRect, elements));
};

export const createTitle = (cellElement) => {
const cell = cellElement
.append("text")
.style("user-select", "none")
.attr("pointer-events", "none")
.attr("x", 4)
.attr("y", 13)
.attr("fill-opacity", (d) => +isLabelVisible(d))
.append("tspan")
.text((d: any) => d.data.name);

cell.append("title").text(
(d: any) =>
`${d
.ancestors()
.map((d: any) => d.data.name)
.reverse()
.join("/")}\n${format(d.value)}`
);
return cell;
};

export const createSubtitle = (title) => {
return title
.append("tspan")
.attr("fill-opacity", (d: any) => (isLabelVisible(d) ? 0.7 : 0))
.text((d: any) => ` ${format(d.value)}`);
};
79 changes: 79 additions & 0 deletions src/components/main-space/new-icicle/icicles-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import * as d3 from "d3";
export const VIEWBOX_WIDTH = 1000;
export const VIEWBOX_HEIGHT = 300;
export const TRANSITION_DURATION = 750;

type Dimensions = {
x0: number;
y0: number;
x1: number;
y1: number;
};

export const getRectangleHeight = ({ x0, x1 }): number => {
return x1 - x0 - Math.min(1, (x1 - x0) / 2);
};

export const getCurrentRect = (icicles, currentElementId) => {
const rects = getAllRects(icicles);
return rects.filter(({ data: { id } }) => id === currentElementId.data.id);
};

export const getAllRects = (icicles) =>
d3.select(icicles.current).selectAll("rect");

export const getRectById = (icicles, rectId) => {
const rects = getAllRects(icicles);
return rects.filter(({ data: { id } }) => id === rectId);
};

export const format = d3.format(",d");

export const isLabelVisible = ({ x0, x1, y0, y1 }): boolean => {
return y1 <= VIEWBOX_WIDTH && y0 >= 0 && x1 - x0 > 16;
};

export const dimensionsTarget: Record<string, Dimensions> = {};

export const getDimensions = (dimensions) =>
dimensionsTarget[dimensions.data.id];

export const handleZoom = (_, currentRect, elements) => {
let { root, cell, rect, title, subtitle } = elements;

elements.focus =
elements.focus === currentRect
? (currentRect = currentRect.parent)
: currentRect;

root.each((dimensions) => {
dimensionsTarget[dimensions.data.id] = {
x0:
((dimensions.x0 - currentRect.x0) / (currentRect.x1 - currentRect.x0)) *
VIEWBOX_HEIGHT,
x1:
((dimensions.x1 - currentRect.x0) / (currentRect.x1 - currentRect.x0)) *
VIEWBOX_HEIGHT,
y0: dimensions.y0 - currentRect.y0,
y1: dimensions.y1 - currentRect.y0,
};
});

const transition = cell
.transition()
.duration(TRANSITION_DURATION)
.attr(
"transform",
(d: any) => `translate(${getDimensions(d).y0},${getDimensions(d).x0})`
);

rect
.transition(transition)
.attr("height", (d: any) => getRectangleHeight(getDimensions(d)));
title
.transition(transition)
.attr("fill-opacity", (d: any) => +isLabelVisible(getDimensions(d)));
subtitle
.transition(transition)
.attr("fill-opacity", (d: any) => +isLabelVisible(getDimensions(d)) * 0.7);
};
Loading

0 comments on commit 0e9e9e8

Please sign in to comment.