Skip to content

Commit 4614cca

Browse files
committed
[gephi-lite] Improves edition inputs
Details: - Improves common bases for React-Select instances - Migrates all react-select uses to our custom constructors from components/forms/Select.tsx - Improves edition inputs stylings
1 parent 4af32a8 commit 4614cca

File tree

10 files changed

+139
-57
lines changed

10 files changed

+139
-57
lines changed

packages/gephi-lite/src/components/GraphFilters/TermsFilter.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ const TermsFilterEditor: FC<{ filter: TermsFilterType }> = ({ filter }) => {
2626
useEffect(() => {
2727
const itemData = mergeStaticDynamicData(
2828
filter.itemType === "nodes" ? nodeData : edgeData,
29-
// dynamic field should be calculated from parentraph and not from the useDynamicItemData which provide data in the current graph
29+
// dynamic field should be calculated from parent graph and not from the useDynamicItemData which provide data in the current graph
3030
filter.itemType === "nodes"
3131
? computeAllDynamicAttributes("nodes", parentGraph)
3232
: computeAllDynamicAttributes("edges", parentGraph),

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

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@ import { debounce } from "lodash";
33
import { FC, useCallback } from "react";
44
import { useTranslation } from "react-i18next";
55
import { type DropdownIndicatorProps, OptionProps, SingleValueProps, components } from "react-select";
6-
import AsyncSelect from "react-select/async";
76

87
import { useAppearance, useSearch } from "../core/context/dataContexts";
98
import { ItemType } from "../core/types";
9+
import { SearchIcon } from "./common-icons";
1010
import { EdgeComponentById } from "./data/Edge";
1111
import { NodeComponentById } from "./data/Node";
12-
import { SearchIcon } from "./common-icons";
13-
import { DEFAULT_SELECT_PROPS } from "./forms/Select";
12+
import { AsyncSelect } from "./forms/Select";
1413

1514
export interface OptionItem {
1615
id: string;
@@ -110,7 +109,6 @@ export const GraphSearch: FC<GraphSearchProps> = ({ className, onChange, postPro
110109

111110
return (
112111
<AsyncSelect<Option>
113-
{...DEFAULT_SELECT_PROPS}
114112
className={className}
115113
isClearable
116114
controlShouldRenderValue={!!value}
@@ -121,7 +119,6 @@ export const GraphSearch: FC<GraphSearchProps> = ({ className, onChange, postPro
121119
components={{
122120
SingleValue,
123121
Option: OptionComponent,
124-
IndicatorSeparator: null,
125122
DropdownIndicator: IndicatorComponent,
126123
NoOptionsMessage: (props) => {
127124
const { t } = useTranslation();

packages/gephi-lite/src/components/common-icons.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ import {
3737
PiMoonStarsFill,
3838
PiPalette,
3939
PiPaletteFill,
40+
PiPlusCircle,
41+
PiPlusCircleFill,
4042
PiPolygon,
4143
PiPolygonFill,
4244
PiSun,
@@ -52,6 +54,8 @@ export const GraphIcon = PiGraph;
5254
export const GraphIconFill = PiGraphFill;
5355
export const DataLaboratoryIcon = PiTable;
5456
export const DataLaboratoryIconFill = PiTableFill;
57+
export const DataCreationIcon = PiPlusCircle;
58+
export const DataCreationIconFill = PiPlusCircleFill;
5559
export const CaretDownIcon = PiCaretDown;
5660
export const CaretUpIcon = PiCaretUp;
5761
export const CaretLeftIcon = PiCaretLeft;

packages/gephi-lite/src/components/data/Attribute.tsx

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,16 @@ import { DateTime } from "luxon";
44
import { FC, createElement } from "react";
55
import { useTranslation } from "react-i18next";
66
import ReactLinkify from "react-linkify";
7-
import CreatableSelect from "react-select/creatable";
87

98
import { castScalarToModelValue, serializeModelValueToScalar } from "../../core/graph/fieldModel";
10-
import { Option, optionize } from "../../utils/select";
119
import { DEFAULT_LINKIFY_PROPS } from "../../utils/url";
10+
import { BaseOption, CreatableSelect, optionize } from "../forms/Select";
1211

1312
/**
1413
* Render values:
1514
* **************
1615
*/
17-
export const RenderValue: {
16+
export const AttributeRenderers: {
1817
[K in keyof FieldModelAbstraction]: FC<
1918
{
2019
value?: FieldModelAbstraction[K]["expectedOutput"];
@@ -40,14 +39,14 @@ export const RenderValue: {
4039
) : null,
4140
date: ({ value, format }) => (!isNil(value) ? value.toFormat(format) : null),
4241
};
43-
export const RenderText = RenderValue.text;
44-
export const RenderNumber = RenderValue.number;
45-
export const RenderCategory = RenderValue.category;
46-
export const RenderKeywords = RenderValue.keywords;
47-
export const RenderDate = RenderValue.date;
42+
export const RenderText = AttributeRenderers.text;
43+
export const RenderNumber = AttributeRenderers.number;
44+
export const RenderCategory = AttributeRenderers.category;
45+
export const RenderKeywords = AttributeRenderers.keywords;
46+
export const RenderDate = AttributeRenderers.date;
4847

4948
export const RenderItemAttribute: FC<{ field: FieldModelTypeSpec; value: Scalar }> = ({ field, value }) =>
50-
createElement(RenderValue[field.type] as FC<{ value?: ModelValueType }>, {
49+
createElement(AttributeRenderers[field.type] as FC<{ value?: ModelValueType }>, {
5150
...field,
5251
value: castScalarToModelValue(value, field),
5352
});
@@ -56,7 +55,7 @@ export const RenderItemAttribute: FC<{ field: FieldModelTypeSpec; value: Scalar
5655
* Edit values:
5756
* ************
5857
*/
59-
export const EditValue: {
58+
export const AttributeEditors: {
6059
[K in keyof FieldModelAbstraction]: FC<
6160
{
6261
value?: FieldModelAbstraction[K]["expectedOutput"];
@@ -65,23 +64,34 @@ export const EditValue: {
6564
>;
6665
} = {
6766
text: ({ value, onChange }) => (
68-
<input type="string" value={value || ""} onChange={(e) => onChange(e.target.value || undefined)} />
67+
<input
68+
className="form-control"
69+
type="string"
70+
value={value || ""}
71+
onChange={(e) => onChange(e.target.value || undefined)}
72+
/>
6973
),
7074
number: ({ value, onChange }) => (
71-
<input type="number" value={value || ""} onChange={(e) => onChange(e.target.value ? +e.target.value : undefined)} />
75+
<input
76+
className="form-control"
77+
type="number"
78+
value={value || ""}
79+
onChange={(e) => onChange(e.target.value ? +e.target.value : undefined)}
80+
/>
7281
),
7382
category: ({ value, onChange }) => {
74-
console.log({ value });
7583
return (
76-
<CreatableSelect<Option>
84+
<CreatableSelect<BaseOption>
85+
menuPosition="absolute"
7786
value={!isNil(value) ? optionize(value) : undefined}
7887
onChange={(newValue) => onChange(newValue?.value)}
7988
/>
8089
);
8190
},
8291
keywords: ({ value, onChange }) => {
8392
return (
84-
<CreatableSelect<Option, true>
93+
<CreatableSelect<BaseOption, true>
94+
menuPosition="absolute"
8595
isMulti
8696
value={value?.map(optionize)}
8797
onChange={(newValue) => onChange(newValue.length ? newValue.map((o) => o.value) : undefined)}
@@ -90,6 +100,7 @@ export const EditValue: {
90100
},
91101
date: ({ value, onChange }) => (
92102
<input
103+
className="form-control"
93104
type="datetime-local"
94105
value={value?.toFormat("yyyy-MM-dd'T'HH:mm") ?? ""}
95106
onChange={(e) => {
@@ -99,18 +110,18 @@ export const EditValue: {
99110
/>
100111
),
101112
};
102-
export const EditText = EditValue.text;
103-
export const EditNumber = EditValue.number;
104-
export const EditCategory = EditValue.category;
105-
export const EditKeywords = EditValue.keywords;
106-
export const EditDate = EditValue.date;
113+
export const EditText = AttributeEditors.text;
114+
export const EditNumber = AttributeEditors.number;
115+
export const EditCategory = AttributeEditors.category;
116+
export const EditKeywords = AttributeEditors.keywords;
117+
export const EditDate = AttributeEditors.date;
107118

108119
export const EditItemAttribute: FC<{ field: FieldModelTypeSpec; value: Scalar; onChange: (value: Scalar) => void }> = ({
109120
field,
110121
value,
111122
onChange,
112123
}) => {
113-
const EditComponent = EditValue[field.type] as FC<{
124+
const EditComponent = AttributeEditors[field.type] as FC<{
114125
value?: ModelValueType;
115126
onChange: (value?: ModelValueType) => void;
116127
}>;

packages/gephi-lite/src/components/forms/Select.tsx

Lines changed: 77 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,94 @@
1-
import type { LegacyRef } from "react";
2-
import ReactSelect, { Props as ReactSelectProps, type SelectInstance } from "react-select";
1+
import { isNil } from "lodash";
2+
import { LegacyRef, ReactNode } from "react";
3+
import { PiCaretDown } from "react-icons/pi";
4+
import ReactSelect, { Props, type SelectInstance } from "react-select";
5+
import AsyncReactSelect, { AsyncProps } from "react-select/async";
6+
import AsyncCreatableReactSelect, { AsyncCreatableProps } from "react-select/async-creatable";
7+
import CreatableReactSelect, { CreatableProps } from "react-select/creatable";
8+
import { GroupBase } from "react-select/dist/declarations/src/types";
39

410
export const DEFAULT_SELECT_PROPS = {
511
classNamePrefix: "react-select",
6-
menuPosition: "fixed" as ReactSelectProps["menuPosition"],
12+
menuPosition: "fixed" as Props["menuPosition"],
13+
components: {
14+
IndicatorSeparator: null,
15+
DropdownIndicator: () => <PiCaretDown />,
16+
},
717
};
818

9-
export interface DefaultOption {
10-
value: string;
11-
label: string | JSX.Element;
19+
export interface BaseOption<T extends string = string> {
20+
value: T;
21+
label: ReactNode;
1222
}
1323

14-
export function Select<T = DefaultOption, IsMulti extends boolean = false>({
24+
export function optionize(value: undefined): undefined;
25+
export function optionize<T extends string = string>(value: T): BaseOption<T>;
26+
export function optionize<T extends string = string>(value?: T): BaseOption<T> | undefined {
27+
return !isNil(value) ? { value, label: value } : undefined;
28+
}
29+
30+
export function Select<T = BaseOption, IsMulti extends boolean = false>({
1531
ref,
1632
...props
17-
}: ReactSelectProps<T, IsMulti> & { ref?: LegacyRef<SelectInstance<T, IsMulti>> }) {
33+
}: Props<T, IsMulti> & { ref?: LegacyRef<SelectInstance<T, IsMulti>> }) {
1834
return (
1935
<ReactSelect<T, IsMulti>
2036
{...DEFAULT_SELECT_PROPS}
2137
{...props}
2238
components={{
23-
IndicatorSeparator: null,
39+
...DEFAULT_SELECT_PROPS.components,
40+
...(props.components || {}),
41+
}}
42+
ref={ref}
43+
/>
44+
);
45+
}
46+
47+
export function AsyncSelect<T = BaseOption, IsMulti extends boolean = false>({
48+
ref,
49+
...props
50+
}: AsyncProps<T, IsMulti, GroupBase<T>> & { ref?: LegacyRef<SelectInstance<T, IsMulti>> }) {
51+
return (
52+
<AsyncReactSelect<T, IsMulti>
53+
{...DEFAULT_SELECT_PROPS}
54+
{...props}
55+
components={{
56+
...DEFAULT_SELECT_PROPS.components,
57+
...(props.components || {}),
58+
}}
59+
ref={ref}
60+
/>
61+
);
62+
}
63+
64+
export function CreatableSelect<T = BaseOption, IsMulti extends boolean = false>({
65+
ref,
66+
...props
67+
}: CreatableProps<T, IsMulti, GroupBase<T>> & { ref?: LegacyRef<SelectInstance<T, IsMulti>> }) {
68+
return (
69+
<CreatableReactSelect<T, IsMulti>
70+
{...DEFAULT_SELECT_PROPS}
71+
{...props}
72+
components={{
73+
...DEFAULT_SELECT_PROPS.components,
74+
...(props.components || {}),
75+
}}
76+
ref={ref}
77+
/>
78+
);
79+
}
80+
81+
export function AsyncCreatableSelect<T = BaseOption, IsMulti extends boolean = false>({
82+
ref,
83+
...props
84+
}: AsyncCreatableProps<T, IsMulti, GroupBase<T>> & { ref?: LegacyRef<SelectInstance<T, IsMulti>> }) {
85+
return (
86+
<AsyncCreatableReactSelect<T, IsMulti>
87+
{...DEFAULT_SELECT_PROPS}
88+
{...props}
89+
components={{
90+
...DEFAULT_SELECT_PROPS.components,
91+
...(props.components || {}),
2492
}}
2593
ref={ref}
2694
/>

packages/gephi-lite/src/styles/_data-table.scss

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
width: 100%;
4040
display: flex;
4141
flex-direction: row;
42-
align-items: baseline;
42+
align-items: center;
4343

4444
& > * {
4545
flex-shrink: 0;
@@ -98,6 +98,12 @@
9898
&.editable {
9999
padding: 0;
100100

101+
.tether-target {
102+
width: 100%;
103+
padding: map.get($spacers, 1) 0;
104+
@extend .text-ellipsis;
105+
}
106+
101107
.data-cell {
102108
width: 100%;
103109
padding: map.get($spacers, 1);
@@ -108,3 +114,7 @@
108114
}
109115
}
110116
}
117+
118+
.data-cell-edition {
119+
z-index: $zindex-tooltip;
120+
}

packages/gephi-lite/src/utils/select.ts

Lines changed: 0 additions & 12 deletions
This file was deleted.

packages/gephi-lite/src/views/dataPage/dataTable/DataCell.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,18 +55,23 @@ export const EditDataCell: FC<{
5555
<TetherComponent
5656
attachment="top left"
5757
targetAttachment="top left"
58-
className=""
59-
constraints={[{ to: "scrollparent", attachment: "together", pin: true }]}
58+
className="data-cell-edition"
59+
constraints={[{ to: "scrollParent", attachment: "together", pin: true }]}
6060
renderTarget={(ref) => (
6161
<div ref={ref}>
6262
<ReadDataCell ref={targetWrapper} value={value} field={field} />
6363
</div>
6464
)}
6565
renderElement={(ref) => (
66-
<div ref={ref}>
66+
<div
67+
ref={ref}
68+
style={{
69+
minWidth: Math.max(200, targetWrapper?.current?.offsetWidth ?? 0),
70+
}}
71+
>
6772
<form
6873
ref={elementWrapper}
69-
className="bg-light"
74+
className="bg-light z-"
7075
onSubmit={(e) => {
7176
e.preventDefault();
7277
update(id, { [field.id]: value }, { merge: true });

packages/gephi-lite/src/views/dataPage/index.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ import { GraphSearchSelection } from "../../components/GraphSearchSelection";
99
import { GraphSummary } from "../../components/GraphSummary";
1010
import { type MenuItem, NavMenu } from "../../components/NavMenu";
1111
import {
12+
DataCreationIcon,
13+
DataCreationIconFill,
1214
FiltersIcon,
1315
FiltersIconFill,
14-
GraphIcon,
15-
GraphIconFill,
1616
StatisticsIcon,
1717
StatisticsIconFill,
1818
} from "../../components/common-icons";
@@ -27,7 +27,7 @@ const MENU: MenuItem<{ panel?: ComponentType }>[] = [
2727
{
2828
id: "data-creation",
2929
i18nKey: "edition.data_creation",
30-
icon: { normal: GraphIcon, fill: GraphIconFill },
30+
icon: { normal: DataCreationIcon, fill: DataCreationIconFill },
3131
children: [
3232
{
3333
id: "data-creation-node",

packages/gephi-lite/src/views/layout/Header.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,6 @@ export const Header: FC = () => {
133133
openModal({
134134
component: ConfirmModal,
135135
beforeSubmit: () => {
136-
console.log("test");
137136
setUser(null);
138137
},
139138
arguments: {

0 commit comments

Comments
 (0)