Skip to content

Commit 651baea

Browse files
committed
[gephi-lite/sdk] Fixes #110
Details: - Removes nodesRenderingData and edgesRenderingData from GraphDataset, and replaces them with layout, that only contains nodes x and y values - Creates new field type "color", with associated heuristic and components - Improves fields inference to consider the field names as well for decision - Updates SizeRankingEditor, to allow using a field for ranking, but with exact values - Removes "data" choices for labels, sizes and colors in appearance - Improves heuristics so that GEXF files are always displayed as usual - Removes obvious fields and ranking size with exact values from caption
1 parent 85b0a9f commit 651baea

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+573
-359
lines changed

packages/gephi-lite/src/components/ColorPicker.tsx

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,26 @@ import { hexToRgba, rgbaToHex } from "../utils/colors";
55
import Tooltip, { TooltipAPI } from "./Tooltip";
66
import { CheckedIcon, CloseIcon } from "./common-icons";
77

8+
export const InlineColorPicker: FC<{ color: string | undefined; onChange: (color: string | undefined) => void }> = ({
9+
color,
10+
onChange,
11+
}) => {
12+
return (
13+
<SketchPicker
14+
color={color ? hexToRgba(color) : undefined}
15+
onChange={(color) => onChange(rgbaToHex(color.rgb))}
16+
styles={{
17+
default: {
18+
picker: {
19+
boxShadow: "none",
20+
padding: 0,
21+
},
22+
},
23+
}}
24+
/>
25+
);
26+
};
27+
828
const ColorPicker: FC<
929
(
1030
| { color: string | undefined; onChange: (color: string | undefined) => void; clearable: true }
@@ -15,22 +35,11 @@ const ColorPicker: FC<
1535

1636
return (
1737
<Tooltip ref={tooltipRef} attachment="top middle" targetAttachment="bottom middle" targetClassName={className}>
18-
<button type="button" className="btn disc border border-secondary" style={{ background: color || "#ffffff" }}>
38+
<button type="button" className="gl-btn square border border-black border-2" style={{ background: color || "#ffffff" }}>
1939
<span style={{ color: "transparent" }}>X</span>
2040
</button>
2141
<div className="custom-color-picker gl-border">
22-
<SketchPicker
23-
color={color ? hexToRgba(color) : undefined}
24-
onChange={(color) => onChange(rgbaToHex(color.rgb))}
25-
styles={{
26-
default: {
27-
picker: {
28-
boxShadow: "none",
29-
padding: 0,
30-
},
31-
},
32-
}}
33-
/>
42+
<InlineColorPicker onChange={onChange} color={color} />
3443
<div className="text-end gl-gap-1 d-flex justify-content-end">
3544
{clearable && (
3645
<button className="gl-btn gl-btn-icon gl-btn-outline" onClick={() => onChange(undefined)}>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { FieldColor } from "@gephi/gephi-lite-sdk";
2+
import { FC } from "react";
3+
import { useTranslation } from "react-i18next";
4+
5+
import { ItemType } from "../../../core/types";
6+
import ColorPicker from "../../ColorPicker";
7+
8+
export const ColorFieldEditor: FC<{
9+
itemType: ItemType;
10+
color: FieldColor;
11+
setColor: (newColor: FieldColor) => void;
12+
}> = ({ itemType, color, setColor }) => {
13+
const { t } = useTranslation();
14+
15+
return (
16+
<div className="d-flex align-items-baseline">
17+
<ColorPicker color={color.missingColor} onChange={(v) => setColor({ ...color, missingColor: v })} />
18+
<label className="form-check-label small ms-1">
19+
{t("appearance.color.default_value", { items: t(`graph.model.${itemType}`) })}
20+
</label>
21+
</div>
22+
);
23+
};

packages/gephi-lite/src/components/GraphAppearance/color/ColorFixedEditor.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1+
import { FixedColor } from "@gephi/gephi-lite-sdk";
12
import { FC } from "react";
23
import { useTranslation } from "react-i18next";
34

4-
import { FixedColor } from "../../../core/appearance/types";
55
import { ItemType } from "../../../core/types";
66
import ColorPicker from "../../ColorPicker";
77

@@ -13,7 +13,7 @@ export const ColorFixedEditor: FC<{
1313
const { t } = useTranslation();
1414

1515
return (
16-
<div className="d-flex align-items-center">
16+
<div className="d-flex align-items-baseline">
1717
<ColorPicker color={color.value} onChange={(v) => setColor({ ...color, value: v })} />
1818
<label className="form-check-label small ms-1">
1919
{t("appearance.color.color_all_items", { items: t(`graph.model.${itemType}`) })}

packages/gephi-lite/src/components/GraphAppearance/color/ColorItem.tsx

Lines changed: 61 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1+
import { Color, DEFAULT_EDGE_COLOR, DEFAULT_NODE_COLOR, DEFAULT_SHADING_COLOR, EdgeColor } from "@gephi/gephi-lite-sdk";
12
import { isEqual } from "lodash";
2-
import { FC, useMemo } from "react";
3+
import { FC, ReactNode, useMemo } from "react";
34
import { useTranslation } from "react-i18next";
45

5-
import { Color } from "../../../core/appearance/types";
6-
import { DEFAULT_EDGE_COLOR, DEFAULT_NODE_COLOR, DEFAULT_SHADING_COLOR } from "../../../core/appearance/utils";
76
import {
87
useAppearance,
98
useAppearanceActions,
@@ -16,6 +15,7 @@ import { uniqFieldValuesAsStrings } from "../../../core/graph/utils";
1615
import { ItemType } from "../../../core/types";
1716
import { FieldModelIcons } from "../../common-icons";
1817
import { Select } from "../../forms/Select";
18+
import { ColorFieldEditor } from "./ColorFieldEditor";
1919
import { ColorFixedEditor } from "./ColorFixedEditor";
2020
import { ColorPartitionEditor } from "./ColorPartitionEditor";
2121
import { ColorRankingEditor } from "./ColorRankingEditor";
@@ -24,9 +24,9 @@ import { getPalette } from "./utils";
2424

2525
type ColorOption = {
2626
value: string;
27-
label: string | JSX.Element;
27+
label: string | ReactNode;
2828
field?: FieldModel<ItemType, boolean>;
29-
type: "data" | "fixed" | "ranking" | "partition" | "unsupported" | "source" | "target";
29+
type: EdgeColor["type"] | "unsupported";
3030
};
3131

3232
export const ColorItem: FC<{ itemType: ItemType }> = ({ itemType }) => {
@@ -50,26 +50,16 @@ export const ColorItem: FC<{ itemType: ItemType }> = ({ itemType }) => {
5050
const options: ColorOption[] = useMemo(() => {
5151
const allFields: FieldModel<ItemType, boolean>[] =
5252
itemType === "nodes" ? [...nodeFields, ...dynamicNodeFields] : [...edgeFields, ...dynamicEdgeFields];
53-
const dataOption: ColorOption = {
54-
value: "data",
55-
type: "data",
56-
label: (
57-
<>
58-
{t("appearance.color.data")} <small className="text-muted">{t("appearance.no_caption")}</small>
59-
</>
60-
),
61-
};
6253
const FixedOption: ColorOption = { value: "fixed", type: "fixed", label: t("appearance.color.fixed") };
6354
const edgeOptions: ColorOption[] = [
6455
{ value: "source", type: "source", label: t("appearance.color.source") },
6556
{ value: "target", type: "target", label: t("appearance.color.target") },
6657
];
6758
return [
68-
dataOption,
6959
FixedOption,
7060
...(itemType === "edges" ? edgeOptions : []),
7161
...allFields
72-
.filter((f): f is FieldModel<ItemType, boolean> => ["date", "number", "category"].includes(f.type))
62+
.filter((f): f is FieldModel<ItemType, boolean> => ["date", "number", "category", "color"].includes(f.type))
7363
.map((field): ColorOption => {
7464
const Icon = FieldModelIcons[field.type];
7565
switch (field.type) {
@@ -79,7 +69,6 @@ export const ColorItem: FC<{ itemType: ItemType }> = ({ itemType }) => {
7969
value: `ranking::${staticDynamicAttributeKey(field)}`,
8070
field,
8171
type: "ranking",
82-
8372
label: (
8473
<>
8574
<Icon className="me-1" />
@@ -88,7 +77,6 @@ export const ColorItem: FC<{ itemType: ItemType }> = ({ itemType }) => {
8877
),
8978
};
9079
case "category":
91-
default:
9280
return {
9381
value: `partition::${staticDynamicAttributeKey(field)}`,
9482
field: field,
@@ -100,6 +88,19 @@ export const ColorItem: FC<{ itemType: ItemType }> = ({ itemType }) => {
10088
</>
10189
),
10290
};
91+
case "color":
92+
default:
93+
return {
94+
value: `field::${staticDynamicAttributeKey(field)}`,
95+
field: field,
96+
type: "field",
97+
label: (
98+
<>
99+
<Icon className="me-1" />
100+
{staticDynamicAttributeLabel(field)}
101+
</>
102+
),
103+
};
103104
}
104105
}),
105106
// unsupported options are listed after the supported ones
@@ -154,42 +155,48 @@ export const ColorItem: FC<{ itemType: ItemType }> = ({ itemType }) => {
154155
setColorAppearance(itemType, {
155156
type: option.type, // this is here to trick TS for nodes
156157
} as Color);
157-
} else {
158-
if (option.type === "ranking") {
159-
setColorAppearance(itemType, {
160-
type: "ranking",
161-
field: option.field,
162-
colorScalePoints: [
163-
{ scalePoint: 0, color: "#fc8d59" },
164-
{ scalePoint: 0.5, color: "#ffffbf" },
165-
{ scalePoint: 1, color: "#91bfdb" },
166-
],
167-
missingColor: baseValue,
168-
});
158+
} else if (option.type === "ranking") {
159+
setColorAppearance(itemType, {
160+
type: "ranking",
161+
field: option.field,
162+
colorScalePoints: [
163+
{ scalePoint: 0, color: "#fc8d59" },
164+
{ scalePoint: 0.5, color: "#ffffbf" },
165+
{ scalePoint: 1, color: "#91bfdb" },
166+
],
167+
missingColor: baseValue,
168+
});
169+
} else if (option.type === "partition") {
170+
const field = option.field;
171+
let values: string[];
172+
173+
if (field.dynamic) {
174+
const itemsData = itemType === "nodes" ? dynamicNodeData : dynamicEdgeData;
175+
values = uniqFieldValuesAsStrings(itemsData, field.id);
169176
} else {
170-
const field = option.field;
171-
let values: string[] = [];
177+
const itemsData = itemType === "nodes" ? nodeData : edgeData;
178+
values = uniqFieldValuesAsStrings(itemsData, field.id);
179+
}
172180

173-
if (field.dynamic) {
174-
const itemsData = itemType === "nodes" ? dynamicNodeData : dynamicEdgeData;
175-
values = uniqFieldValuesAsStrings(itemsData, field.id);
176-
} else {
177-
const itemsData = itemType === "nodes" ? nodeData : edgeData;
178-
values = uniqFieldValuesAsStrings(itemsData, field.id);
179-
}
181+
setColorAppearance(itemType, {
182+
type: "partition",
183+
field,
184+
colorPalette: getPalette(values),
185+
missingColor: baseValue,
186+
});
187+
} else {
188+
const field = option.field;
180189

181-
setColorAppearance(itemType, {
182-
type: "partition",
183-
field,
184-
colorPalette: getPalette(values),
185-
missingColor: baseValue,
186-
});
187-
}
190+
setColorAppearance(itemType, {
191+
type: "field",
192+
field,
193+
missingColor: baseValue,
194+
});
188195
}
189196
}}
190197
/>
191198

192-
{(color.type === "data" || color.type === "source" || color.type === "target") && (
199+
{(color.type === "source" || color.type === "target") && (
193200
<p className="form-text text-muted">
194201
{t(`appearance.color.${color.type}_description`, { items: t(`graph.model.${itemType}`) })}
195202
</p>
@@ -217,6 +224,13 @@ export const ColorItem: FC<{ itemType: ItemType }> = ({ itemType }) => {
217224
setColor={(newColor) => setColorAppearance(itemType, newColor)}
218225
/>
219226
)}
227+
{color.type === "field" && (
228+
<ColorFieldEditor
229+
itemType={itemType}
230+
color={color}
231+
setColor={(newColor) => setColorAppearance(itemType, newColor)}
232+
/>
233+
)}
220234

221235
{/* Colors shading */}
222236
{(colorShading || defaultShadingField) && (

packages/gephi-lite/src/components/GraphAppearance/color/ColorPartitionEditor.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1+
import { PartitionColor } from "@gephi/gephi-lite-sdk";
12
import { map } from "lodash";
23
import { FC, useState } from "react";
34
import AnimateHeight from "react-animate-height";
45
import { useTranslation } from "react-i18next";
56

6-
import { PartitionColor } from "../../../core/appearance/types";
77
import { ItemType } from "../../../core/types";
88
import ColorPicker from "../../ColorPicker";
99

@@ -19,7 +19,7 @@ export const ColorPartitionEditor: FC<{
1919
<div className="mt-1">
2020
<AnimateHeight height={expanded ? "auto" : 200} className="position-relative" duration={400}>
2121
{map(color.colorPalette, (c, value) => (
22-
<div key={value} className="d-inline-block w-50 d-inline-flex align-items-center flex-nowrap" title={value}>
22+
<div key={value} className="d-inline-block w-50 d-inline-flex align-items-baseline flex-nowrap" title={value}>
2323
<ColorPicker
2424
color={c}
2525
onChange={(v) =>
@@ -36,7 +36,7 @@ export const ColorPartitionEditor: FC<{
3636
</div>
3737
))}
3838

39-
<div className="d-flex align-items-center mt-1">
39+
<div className="d-flex align-items-baseline mt-1">
4040
<ColorPicker color={color.missingColor} onChange={(v) => setColor({ ...color, missingColor: v })} />
4141
<label className="form-check-label small ms-1">
4242
{t("appearance.color.default_value", { items: t(`graph.model.${itemType}`) })}

packages/gephi-lite/src/components/GraphAppearance/color/ColorPickerTooltip.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1+
import { ColorScalePointType } from "@gephi/gephi-lite-sdk";
12
import { FC, HTMLProps, useEffect, useRef, useState } from "react";
23
import { SketchPicker } from "react-color";
34
import { useTranslation } from "react-i18next";
45
import TetherComponent from "react-tether";
56

6-
import { ColorScalePointType } from "../../../core/appearance/types";
7-
87
const ColorPickerTooltip: FC<{
98
targetProps: HTMLProps<HTMLDivElement>;
109
colorScalePoint: ColorScalePointType;

packages/gephi-lite/src/components/GraphAppearance/color/ColorRankingEditor.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1+
import { RankingColor } from "@gephi/gephi-lite-sdk";
12
import chroma from "chroma-js";
23
import { last, sortBy } from "lodash";
34
import { FC, HTMLProps, useCallback, useMemo, useState } from "react";
45
import { useTranslation } from "react-i18next";
56
import ReactSlider from "react-slider";
67

7-
import { RankingColor } from "../../../core/appearance/types";
88
import { ItemType } from "../../../core/types";
99
import ColorPicker from "../../ColorPicker";
1010
import ColorPickerTooltip from "./ColorPickerTooltip";
@@ -130,7 +130,7 @@ export const ColorRankingEditor: FC<{
130130
/>
131131

132132
<div>
133-
<div className="d-flex align-items-center mt-1">
133+
<div className="d-flex align-items-baseline mt-1">
134134
<ColorPicker color={color.missingColor} onChange={(v) => setColor({ ...color, missingColor: v })} />
135135
<label className="form-check-label small ms-1">
136136
{t("appearance.color.default_value", { items: t(`graph.model.${itemType}`) })}

packages/gephi-lite/src/components/GraphAppearance/color/ShadingColorEditor.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
import { FieldModel } from "@gephi/gephi-lite-sdk";
1+
import { FieldModel, ShadingColor } from "@gephi/gephi-lite-sdk";
22
import { FC, useMemo } from "react";
33
import { useTranslation } from "react-i18next";
44

5-
import { ShadingColor } from "../../../core/appearance/types";
65
import { useGraphDataset } from "../../../core/context/dataContexts";
76
import { staticDynamicAttributeLabel } from "../../../core/graph/dynamicAttributes";
87
import { ItemType } from "../../../core/types";
@@ -48,12 +47,12 @@ export const ShadingColorEditor: FC<{
4847
onChange={(option) => option && setColor({ ...color, field: option.value })}
4948
/>
5049

51-
<div className="d-flex align-items-center mt-1">
50+
<div className="d-flex align-items-baseline mt-1">
5251
<ColorPicker color={color.targetColor} onChange={(v) => setColor({ ...color, targetColor: v })} />
5352
<label className="form-check-label small ms-1">{t("appearance.color.shading_color")}</label>
5453
</div>
5554

56-
<div className="d-flex align-items-center mt-1">
55+
<div className="d-flex align-items-baseline mt-1">
5756
<input
5857
className="form-control form-control-sm w-5"
5958
type="number"

packages/gephi-lite/src/components/GraphAppearance/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export const GraphGraphAppearance: FC<unknown> = () => {
6060
<h2>{t("appearance.menu.background")}</h2>
6161

6262
<div className="panel-block">
63-
<div className="d-flex align-items-center">
63+
<div className="d-flex align-items-baseline">
6464
<label className="me-3 flex-grow-1">{t("appearance.graph.background_color")}</label>
6565
<ColorPicker
6666
className="w-auto h-100"
@@ -70,7 +70,7 @@ export const GraphGraphAppearance: FC<unknown> = () => {
7070
/>
7171
</div>
7272

73-
<div className="d-flex align-items-center">
73+
<div className="d-flex align-items-baseline">
7474
<label className="me-3 flex-grow-1">{t("appearance.graph.layout_grid_color")}</label>
7575
<ColorPicker
7676
className="w-auto h-100"

0 commit comments

Comments
 (0)