Skip to content

Commit

Permalink
Apparence : graph backend color
Browse files Browse the repository at this point in the history
  • Loading branch information
sim51 committed Feb 1, 2024
1 parent f3b068a commit 2a58c08
Show file tree
Hide file tree
Showing 16 changed files with 217 additions and 35 deletions.
76 changes: 76 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"byte-size": "^8.1.1",
"chroma-js": "^2.4.2",
"classnames": "^2.5.1",
"color": "^4.2.3",
"copy-to-clipboard": "^3.3.3",
"file-saver": "^2.0.5",
"graphology": "^0.25.4",
Expand Down Expand Up @@ -101,6 +102,7 @@
"sigma": "3.0.0-beta.5"
},
"devDependencies": {
"@types/color": "^3.0.6",
"@types/react-linkify": "^1.0.4",
"@typescript-eslint/eslint-plugin": "^6.20.0",
"@typescript-eslint/parser": "^6.20.0",
Expand Down
7 changes: 4 additions & 3 deletions src/components/ColorPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { FC, useRef } from "react";
import { SketchPicker } from "react-color";
import { AiOutlineCheck, AiOutlineClose } from "react-icons/ai";

import { hexToRgba, rgbaToHex } from "../utils/colors";
import Tooltip, { TooltipAPI } from "./Tooltip";

const ColorPicker: FC<
Expand All @@ -14,13 +15,13 @@ const ColorPicker: FC<

return (
<Tooltip ref={tooltipRef} attachment="top middle" targetAttachment="bottom middle" targetClassName={className}>
<button type="button" className="btn disc" style={{ background: color || "#ffffff" }}>
<button type="button" className="btn disc border" style={{ background: color || "#ffffff" }}>
<span style={{ color: "transparent" }}>X</span>
</button>
<div className="custom-color-picker">
<SketchPicker
color={color}
onChange={(color) => onChange(color.hex)}
color={color ? hexToRgba(color) : undefined}
onChange={(color) => onChange(rgbaToHex(color.rgb))}
styles={{
default: {
picker: {
Expand Down
78 changes: 56 additions & 22 deletions src/components/GraphAppearance/index.tsx
Original file line number Diff line number Diff line change
@@ -1,41 +1,58 @@
import { FC, useState } from "react";
import { FC, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";

import { useAppearance, useAppearanceActions } from "../../core/context/dataContexts";
import { ItemType } from "../../core/types";
import { Toggle } from "../Toggle";
import { EdgeIcon, NodeIcon } from "../common-icons";
import ColorPicker from "../ColorPicker";
import { ToggleBar } from "../Toggle";
import { EdgeIcon, GraphIcon, NodeIcon } from "../common-icons";
import { ColorItem } from "./color/ColorItem";
import { LabelItem } from "./label/LabelItem";
import { LabelSizeItem } from "./label/LabelSizeItem";
import { SizeItem } from "./size/SizeItem";

export const GraphAppearance: FC = () => {
const { t } = useTranslation();
const [showEdges, setShowEdges] = useState(false);
const [selected, setSelected] = useState("nodes");

const tabs = useMemo(() => {
return [
{
value: "nodes",
label: (
<>
<NodeIcon className="me-1" /> {t("graph.model.nodes")}
</>
),
},
{
value: "edges",
label: (
<>
<EdgeIcon className="me-1" /> {t("graph.model.edges")}
</>
),
},
{
value: "graph",
label: (
<>
<GraphIcon className="me-1" /> {t("graph.model.graph")}
</>
),
},
];
}, [t]);

return (
<>
<ToggleBar className="mt-1" value={selected} onChange={(e) => setSelected(e)} options={tabs} />
<hr className="m-0" />
<div className="panel-block">
<Toggle
value={showEdges}
onChange={setShowEdges}
leftLabel={
<>
<NodeIcon className="me-1" /> {t("graph.model.nodes")}
</>
}
rightLabel={
<>
<EdgeIcon className="me-1" /> {t("graph.model.edges")}
</>
}
/>
{selected === "nodes" && <GraphItemAppearance itemType="nodes" />}
{selected === "edges" && <GraphItemAppearance itemType="edges" />}
{selected === "graph" && <GraphGraphAppearance />}
</div>

<hr className="m-0" />

{showEdges ? <GraphItemAppearance itemType="edges" /> : <GraphItemAppearance itemType="nodes" />}
</>
);
};
Expand Down Expand Up @@ -73,3 +90,20 @@ const GraphItemAppearance: FC<{ itemType: ItemType }> = ({ itemType }) => {
</>
);
};

const GraphGraphAppearance: FC<unknown> = () => {
const { t } = useTranslation();
const { backgroundColor } = useAppearance();
const { setBackgroundColorAppearance } = useAppearanceActions();

return (
<div className="panel-block">
<h3 className="fs-5">{t("appearance.graph.background.title")}</h3>

<div className="d-flex align-items-center">
<label className="me-3">{t("appearance.graph.background.color")}</label>
<ColorPicker color={backgroundColor} clearable onChange={(v) => setBackgroundColorAppearance(v)} />
</div>
</div>
);
};
29 changes: 28 additions & 1 deletion src/components/Toggle.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import cx from "classnames";
import { FC } from "react";
import { FC, Fragment } from "react";

/**
* This toggle button displays and controls a boolean value, with two "left" and
Expand Down Expand Up @@ -48,3 +48,30 @@ export const Toggle: FC<{
</div>
);
};

export function ToggleBar<T>(props: {
value: T;
options: Array<{ label: JSX.Element | string; value: T }>;
onChange: (value: T) => void;
className?: string;
disabled?: boolean;
}) {
const { value, onChange, options, className, disabled } = props;
return (
<ul className={cx("nav nav-tabs", className)}>
{options.map((option) => (
<Fragment key={`${option.value}`}>
<li className="nav-item">
<button
className={cx("nav-link link-dark", className, option.value === value && "active")}
onClick={() => onChange(option.value)}
disabled={disabled}
>
{option.label}
</button>
</li>
</Fragment>
))}
</ul>
);
}
6 changes: 5 additions & 1 deletion src/core/appearance/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { ItemType } from "../types";
import { atom } from "../utils/atoms";
import { Producer, producerToAction } from "../utils/producers";
import { AppearanceState, BooleanAppearance, Color, Label, LabelSize, Size } from "./types";
import { getEmptyAppearanceState, serializeAppearanceState } from "./utils";
import { DEFAULT_BACKGROUND_COLOR, getEmptyAppearanceState, serializeAppearanceState } from "./utils";

const resetState: Producer<AppearanceState, []> = () => {
return () => getEmptyAppearanceState();
Expand All @@ -16,6 +16,9 @@ const setSizeAppearance: Producer<AppearanceState, [ItemType, Size]> = (itemType
return (state) => ({ ...state, [itemType === "nodes" ? "nodesSize" : "edgesSize"]: size });
};

const setBackgroundColorAppearance: Producer<AppearanceState, [string | undefined]> = (color) => {
return (state) => ({ ...state, backgroundColor: color || DEFAULT_BACKGROUND_COLOR });
};
const setColorAppearance: Producer<AppearanceState, [ItemType, Color]> = (itemType, color) => {
return (state) => ({ ...state, [itemType === "nodes" ? "nodesColor" : "edgesColor"]: color });
};
Expand All @@ -39,6 +42,7 @@ export const appearanceActions = {
setShowEdges: producerToAction(setShowEdges, appearanceAtom),
setSizeAppearance: producerToAction(setSizeAppearance, appearanceAtom),
setColorAppearance: producerToAction(setColorAppearance, appearanceAtom),
setBackgroundColorAppearance: producerToAction(setBackgroundColorAppearance, appearanceAtom),
setLabelAppearance: producerToAction(setLabelAppearance, appearanceAtom),
setLabelSizeAppearance: producerToAction(setLabelSizeAppearance, appearanceAtom),
} as const;
Expand Down
1 change: 1 addition & 0 deletions src/core/appearance/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ export interface AppearanceState {
showEdges: BooleanAppearance;
nodesSize: Size;
edgesSize: Size;
backgroundColor: string;
nodesColor: Color;
edgesColor: EdgeColor;
nodesLabel: Label;
Expand Down
2 changes: 2 additions & 0 deletions src/core/appearance/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export const DEFAULT_NODE_SIZE = 20;
export const DEFAULT_EDGE_SIZE = 6;
export const DEFAULT_NODE_LABEL_SIZE = 14;
export const DEFAULT_EDGE_LABEL_SIZE = 14;
export const DEFAULT_BACKGROUND_COLOR = "#FFFFFFFF";

export function getEmptyAppearanceState(): AppearanceState {
return {
Expand All @@ -47,6 +48,7 @@ export function getEmptyAppearanceState(): AppearanceState {
itemType: "edges",
type: "data",
},
backgroundColor: DEFAULT_BACKGROUND_COLOR,
nodesColor: {
itemType: "nodes",
type: "data",
Expand Down
3 changes: 2 additions & 1 deletion src/core/graph/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Attributes } from "graphology-types";
import { isNil, last, mapValues, omit, omitBy } from "lodash";
import { isNil, isString, last, mapValues, omit, omitBy } from "lodash";
import { Coordinates } from "sigma/types";

import { appearanceAtom } from "../appearance";
Expand Down Expand Up @@ -263,6 +263,7 @@ graphDatasetAtom.bind((graphDataset, previousGraphDataset) => {
...initialState,
...omitBy(appearanceState, (appearanceElement) => {
if (
!isString(appearanceElement) &&
appearanceElement.field &&
((appearanceElement.itemType === "edges" && !edgeFields.includes(appearanceElement.field)) ||
(appearanceElement.itemType === "nodes" && !nodeFields.includes(appearanceElement.field)))
Expand Down
7 changes: 7 additions & 0 deletions src/locales/dev.json
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,12 @@
"log": "log",
"spline": "spline"
},
"graph": {
"background": {
"title":"Background",
"color": "Color"
}
},
"color": {
"title": "Color",
"set_color_from": "Set color from...",
Expand Down Expand Up @@ -244,6 +250,7 @@
"directed": "Directed",
"undirected": "Undirected",
"mixed": "Mixed",
"graph":"graph",
"nodes_one": "node",
"nodes_zero": "node",
"nodes": "nodes",
Expand Down
Loading

0 comments on commit 2a58c08

Please sign in to comment.