Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/icicles d3 hover #1277

Open
wants to merge 1 commit into
base: feat/refactor-icicles-d3
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
import { fromFileName } from "./../../../util/color/color-util";
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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remplacer d par un nom de variable plus explicite

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) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renommer d ici aussi, et le typer si possible

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));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Si handleZoom a besoin de _, ne pas nommer cette variable ainsi car elle est utilisée

};

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) =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Idem, essayer de trouver un meilleur nom et pourquoi pas un type pour d

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

export const createSubtitle = (title) => {
return title
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Peut se simplifier en retirant l'accolade et le return

.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";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Déplacer ce fichier dans le dossier utils, et écrire des tests pour les fonctions exportées

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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let { root, cell, rect, title, subtitle } = elements;
const { 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