Skip to content

Commit

Permalink
Adding dark/ligth theme
Browse files Browse the repository at this point in the history
- change sigma settings on theme switch
- adding "data-bs-theme" attribut on html
- save the theme in user preference
- change btn-outline-danger to just btn-danger for visibility in dark
  mode
  • Loading branch information
sim51 committed Jun 18, 2024
1 parent b9c421a commit afe983c
Show file tree
Hide file tree
Showing 36 changed files with 1,058 additions and 87 deletions.
716 changes: 713 additions & 3 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
"seedrandom": "^3.0.5",
"typescript": "^5.4.5",
"vite": "^5.2.13",
"vite-plugin-svgr": "^4.2.0",
"vitest": "^1.6.0"
},
"overrides": {
Expand Down
12 changes: 7 additions & 5 deletions public/gephi-logo.svg → src/assets/gephi-logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 9 additions & 8 deletions src/components/GraphSearch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { ItemType } from "../core/types";
import { EdgeComponentById } from "./Edge";
import { NodeComponentById } from "./Node";
import { SearchIcon } from "./common-icons";
import { DEFAULT_SELECT_PROPS } from "./consts";

export interface OptionItem {
id: string;
Expand All @@ -28,14 +29,13 @@ const OptionComponent = ({ data, innerProps, className, isFocused }: OptionProps
const { t } = useTranslation();

return (
<div {...innerProps} className={className} onMouseMove={undefined} onMouseOver={undefined}>
<div
className={cx(
className,
"d-flex m-1 hoverable text-ellipsis d-flex align-items-center",
isFocused && "bg-light",
)}
>
<div
{...innerProps}
className={cx(className, isFocused && "selected")}
onMouseMove={undefined}
onMouseOver={undefined}
>
<div className={cx(className, "d-flex m-1 hoverable text-ellipsis d-flex align-items-center")}>
{data.type === "nodes" && <NodeComponentById id={data.id} />}
{data.type === "edges" && <EdgeComponentById id={data.id} />}
{data.type === "message" && (
Expand Down Expand Up @@ -112,6 +112,7 @@ export const GraphSearch: FC<GraphSearchProps> = ({ className, onChange, postPro

return (
<AsyncSelect<Option>
{...DEFAULT_SELECT_PROPS}
className={className}
isClearable
controlShouldRenderValue={!!value}
Expand Down
41 changes: 41 additions & 0 deletions src/components/ThemeSwitcher.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { FC } from "react";
import { MdContrast, MdDarkMode, MdLightMode } from "react-icons/md";

import { usePreferences, usePreferencesActions } from "../core/context/dataContexts";
import { Preferences } from "../core/preferences/types";
import Tooltip from "./Tooltip";

export const ThemeSwicther: FC<unknown> = () => {
const { theme } = usePreferences();
const { changeTheme } = usePreferencesActions();
return (
<Tooltip closeOnClickContent attachment="top middle" targetAttachment="bottom middle">
<button className="btn p-0 fs-4">
{theme === "auto" && <MdContrast />}
{theme === "light" && <MdLightMode />}
{theme === "dark" && <MdDarkMode />}
</button>
<div className="dropdown-menu show over-modal position-relative">
{(["auto", "light", "dark"] as Preferences["theme"][]).map((theme) => (
<button className="dropdown-item" onClick={() => changeTheme(theme)}>
{theme === "auto" && (
<span>
<MdContrast /> Auto
</span>
)}
{theme === "light" && (
<span>
<MdLightMode /> Light
</span>
)}
{theme === "dark" && (
<span>
<MdDarkMode /> Dark
</span>
)}
</button>
))}
</div>
</Tooltip>
);
};
2 changes: 1 addition & 1 deletion src/components/forms/GraphModelForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ const FieldModelsComponent: FC<{ fields: FieldModelWithStats[] }> = ({ fields })
</div>
<button
type="button"
className="btn btn-outline-danger btn-sm"
className="btn btn-danger btn-sm"
title={`${t(`edition.delete_${field.itemType}_attributes`, { name: field.id })}`}
onClick={() => {
openModal({
Expand Down
17 changes: 15 additions & 2 deletions src/core/preferences/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { RemoteFile } from "../graph/import/types";
import { atom } from "../utils/atoms";
import { Producer, producerToAction } from "../utils/producers";
import { Preferences } from "./types";
import { getCurrentPreferences, serializePreferences } from "./utils";
import { getAppliedTheme, getCurrentPreferences, serializePreferences } from "./utils";

/**
* Producers:
Expand All @@ -25,6 +25,13 @@ const changeLocale: Producer<Preferences, [string]> = (locale) => {
});
};

const changeTheme: Producer<Preferences, [Preferences["theme"]]> = (theme) => {
return (preferences) => ({
...preferences,
theme,
});
};

/**
* Public API:
* ***********
Expand All @@ -34,12 +41,18 @@ export const preferencesAtom = atom<Preferences>(getCurrentPreferences());
export const preferencesActions = {
addRemoteFile: producerToAction(addRemoteFile, preferencesAtom),
changeLocale: producerToAction(changeLocale, preferencesAtom),
changeTheme: producerToAction(changeTheme, preferencesAtom),
};

/**
* Bindings:
* *********
*/
preferencesAtom.bind((preferences) => {
preferencesAtom.bind((preferences, prevPreferences) => {
localStorage.setItem("preferences", serializePreferences(preferences));

// Apply theme change
if (prevPreferences.theme !== preferences.theme || !document.documentElement.getAttribute("data-bs-theme")) {
document.documentElement.setAttribute("data-bs-theme", getAppliedTheme(preferences.theme));
}
});
2 changes: 2 additions & 0 deletions src/core/preferences/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ export interface Preferences {
};
// current locale
locale: string;
// theme
theme: "light" | "dark" | "auto";
}
9 changes: 9 additions & 0 deletions src/core/preferences/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export function getEmptyPreferences(): Preferences {
metrics: {},
// default is the local detected by i18n
locale: i18n.language,
theme: "auto",
};
}

Expand Down Expand Up @@ -38,3 +39,11 @@ export function parsePreferences(rawPreferences: string): Preferences | null {
return null;
}
}

export function getAppliedTheme(theme: Preferences["theme"]): "light" | "dark" {
if (theme === "auto") {
if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) return "dark";
else return "light";
}
return theme;
}
61 changes: 61 additions & 0 deletions src/core/sigma/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
import { Attributes } from "graphology-types";
import { drawDiscNodeLabel } from "sigma/rendering";
import { Settings } from "sigma/settings";
import { NodeDisplayData, PartialButFor } from "sigma/types";

import { SigmaState } from "./types";

/**
Expand All @@ -12,3 +17,59 @@ export function getEmptySigmaState(): SigmaState {
highlightedNodes: null,
};
}

export function drawDiscNodeHover<
N extends Attributes = Attributes,
E extends Attributes = Attributes,
G extends Attributes = Attributes,
>(
context: CanvasRenderingContext2D,
data: PartialButFor<NodeDisplayData, "x" | "y" | "size" | "label" | "color">,
settings: Settings<N, E, G>,
): void {
const size = settings.labelSize,
font = settings.labelFont,
weight = settings.labelWeight;

context.font = `${weight} ${size}px ${font}`;

// Then we draw the label background
context.fillStyle = (settings as Settings & { nodeHoverBackgoundColor?: string }).nodeHoverBackgoundColor || "#FFF";
context.shadowOffsetX = 0;
context.shadowOffsetY = 0;
context.shadowBlur = 8;
context.shadowColor = "#000";

const PADDING = 2;

if (typeof data.label === "string") {
const textWidth = context.measureText(data.label).width,
boxWidth = Math.round(textWidth + 5),
boxHeight = Math.round(size + 2 * PADDING),
radius = Math.max(data.size, size / 2) + PADDING;

const angleRadian = Math.asin(boxHeight / 2 / radius);
const xDeltaCoord = Math.sqrt(Math.abs(Math.pow(radius, 2) - Math.pow(boxHeight / 2, 2)));

context.beginPath();
context.moveTo(data.x + xDeltaCoord, data.y + boxHeight / 2);
context.lineTo(data.x + radius + boxWidth, data.y + boxHeight / 2);
context.lineTo(data.x + radius + boxWidth, data.y - boxHeight / 2);
context.lineTo(data.x + xDeltaCoord, data.y - boxHeight / 2);
context.arc(data.x, data.y, radius, angleRadian, -angleRadian);
context.closePath();
context.fill();
} else {
context.beginPath();
context.arc(data.x, data.y, data.size + PADDING, 0, Math.PI * 2);
context.closePath();
context.fill();
}

context.shadowOffsetX = 0;
context.shadowOffsetY = 0;
context.shadowBlur = 0;

// And finally we draw the label
drawDiscNodeLabel(context, data, settings);
}
15 changes: 9 additions & 6 deletions src/locales/dev.json
Original file line number Diff line number Diff line change
Expand Up @@ -220,10 +220,10 @@
"confirm_delete_edges_one": "Are you sure you want to delete this one edge?",
"confirm_delete_edges": "Are you sure you want to delete {{count}} edges?",
"search_nodes": "Search on node labels...",
"delete_nodes_attributes": "Delete the {{name}} node attribute",
"delete_edges_attributes": "Delete the {{name}} edge attribute",
"delete_nodes_attributes": "Delete the '{{name}}' node attribute",
"delete_edges_attributes": "Delete the '{{name}}' edge attribute",
"confirm_delete_attributes": "This deletion will remove {{nbValues}} values from the graph. Are you sure you want to delete the attribute {{name}}?",
"delete_attributes_success": "The {{name}} attribute has been deleted."
"delete_attributes_success": "The '{{name}'} attribute has been deleted."
},
"graph": {
"title": "Graph",
Expand Down Expand Up @@ -740,13 +740,15 @@
"placeholder": "Search a node ...",
"no_result": "No node found",
"help": "Type something to find a node",
"other_result": "... and {{count}} other nodes"
"other_result": "... and {{count}} other nodes",
"select_all": "Select all {{count}} nodes"
},
"edges": {
"placeholder": "Search an edge...",
"no_result": "No edge found",
"help": "Type something to find an edge",
"other_result": "... and {{count}} other edges"
"other_result": "... and {{count}} other edges",
"select_all": "Select all {{count}} edges"
}
},
"settings": {
Expand All @@ -758,6 +760,7 @@
"description": "All your data will be deleted, even the settings saved locally (ie. in your browser). Are you sure ?",
"success": "Application's state has been cleared"
}
}
},
"theme": "Choose a theme"
}
}
18 changes: 18 additions & 0 deletions src/styles/_base.scss
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,21 @@
--bs-btn-font-size: 0.85rem;
}
}

.btn-outline-dark {
color: var(--bs-body-color);
border-color: var(--bs-body-color);

&:hover {
color: var(--bs-body-bg);
background-color: var(--bs-body-color);
}
}

.nav {
.nav-item {
.nav-link.link-dark {
color: var(--bs-btn-hover-color)!important;
}
}
}
10 changes: 10 additions & 0 deletions src/styles/_dark.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
@include color-mode(dark) {
--toolbar-bg: #{$black};
--panels-bg: #{$gray-700};
--panels-bg-rgb: 73,80,87;
--range-in-bg: #{$gray-800};
--range-in-body: #{$gray-800};
--range-out-bg: #{$gray-200};
--range-out-body: #{$gray-200};
}

8 changes: 4 additions & 4 deletions src/styles/_filters.scss
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,10 @@
}

.global {
background: $gray-400;
background: var(--range-in-bg);
}
.filtered {
background: $gray-800;
background: var(--range-out-bg);
}
.label {
position: absolute;
Expand All @@ -76,11 +76,11 @@

&.inside {
top: 0;
color: $gray-100;
color: var(--range-in-body);
}
&.outside {
bottom: 100%;
color: $gray-800;
color: var(--range-out-body);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/styles/_graph-caption.scss
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.graph-caption {
&:not(.collapsed) {
background-color: rgba($panels-bg, 0.9);
background-color: rgba(var(--panels-bg-rgb), 0.9);
}
display: flex;
align-items: center;
Expand Down
Loading

0 comments on commit afe983c

Please sign in to comment.