Skip to content
This repository was archived by the owner on Jul 28, 2025. It is now read-only.

Commit af60cd9

Browse files
committed
color contrast improved
1 parent 8230157 commit af60cd9

File tree

22 files changed

+310
-232
lines changed

22 files changed

+310
-232
lines changed

src/__tests__/reactTable.test.tsx

Lines changed: 0 additions & 55 deletions
This file was deleted.
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { Db } from "services/CoreService";
2+
3+
jest.mock("obsidian");
4+
describe("services: colorsFn", () => {
5+
beforeAll(() => {
6+
// Init the db service
7+
Db.init();
8+
});
9+
test('colors service: greyScale', () => {
10+
const defaultValue = Db.coreFns.colors.greyScale();
11+
const customValue = Db.coreFns.colors.greyScale(1);
12+
13+
// Regular expression to test if the string is a valid hex color
14+
const hexColorRegex = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/;
15+
expect(hexColorRegex.test(defaultValue)).toBe(true);
16+
expect(hexColorRegex.test(customValue)).toBe(true);
17+
18+
// Test when an invalid value is passed
19+
const invalidValue = Db.coreFns.colors.greyScale(100);
20+
expect(invalidValue).toBe(defaultValue);
21+
});
22+
23+
test('colors service: randomColor', () => {
24+
const hslRandomValue = Db.coreFns.colors.randomColor();
25+
// Regular expression to test if the string is a valid hsl color
26+
const hslColorRegex = /^hsl\(\d{1,3},\s\d{1,3}%,\s\d{1,3}%\)$/;
27+
expect(hslColorRegex.test(hslRandomValue)).toBe(true);
28+
29+
});
30+
31+
test('colors service: parsing bidirectional hsl <-> string', () => {
32+
const hsl = { h: 1, s: 2, l: 3 };
33+
const hslString = Db.coreFns.colors.hslToString(hsl);
34+
const hslParsed = Db.coreFns.colors.stringtoHsl(hslString);
35+
36+
expect(hslParsed).toEqual(hsl);
37+
});
38+
39+
test('colors service: getContrast', () => {
40+
// Test with a black color (contrast should be white)
41+
const hsl = { h: 1, s: 2, l: 3 };
42+
const hslString = Db.coreFns.colors.hslToString(hsl);
43+
const contrast = Db.coreFns.colors.getContrast(hslString);
44+
45+
expect(contrast).toBe('hsl(1,2%,80%)');
46+
47+
// Test with a white color (contrast should be black)
48+
const hsl2 = { h: 1, s: 2, l: 98 };
49+
const hslString2 = Db.coreFns.colors.hslToString(hsl2);
50+
const contrast2 = Db.coreFns.colors.getContrast(hslString2);
51+
52+
expect(contrast2).toBe('hsl(1,2%,20%)');
53+
});
54+
});

src/__tests__/services/coreService.test.ts renamed to src/__tests__/services/luxonFn.test.ts

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,11 @@ import { DateTime } from "luxon";
22
import { Db } from "services/CoreService";
33

44
jest.mock("obsidian");
5-
describe("services: CORE", () => {
5+
describe("services: luxonFn", () => {
66
beforeAll(() => {
77
// Init the db service
88
Db.init();
99
});
10-
/**
11-
* LUXON MODULE
12-
*/
1310
test('luxon service: dateToString', () => {
1411
const parsedString = Db.coreFns.luxon.dateToString(DateTime.now(), "yyyy-MM-dd");
1512
expect(typeof parsedString).toBe("string");
@@ -40,22 +37,4 @@ describe("services: CORE", () => {
4037
const range = Db.coreFns.luxon.range([date1, date2]);
4138
expect(range).toBe(1);
4239
});
43-
44-
/**
45-
* NUMBERS MODULE
46-
*/
47-
test('numbers service: sum', () => {
48-
const sum = Db.coreFns.numbers.sum([1, 2, 3]);
49-
expect(sum).toBe(6);
50-
});
51-
52-
test('numbers service: min', () => {
53-
const min = Db.coreFns.numbers.min([1, 2, 3]);
54-
expect(min).toBe(1);
55-
});
56-
57-
test('numbers service: max', () => {
58-
const max = Db.coreFns.numbers.max([1, 2, 3]);
59-
expect(max).toBe(3);
60-
});
6140
});
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { Db } from "services/CoreService";
2+
3+
jest.mock("obsidian");
4+
describe("services: numbersFn", () => {
5+
beforeAll(() => {
6+
// Init the db service
7+
Db.init();
8+
});
9+
test('numbers service: sum', () => {
10+
const sum = Db.coreFns.numbers.sum([1, 2, 3]);
11+
expect(sum).toBe(6);
12+
});
13+
14+
test('numbers service: min', () => {
15+
const min = Db.coreFns.numbers.min([1, 2, 3]);
16+
expect(min).toBe(1);
17+
});
18+
19+
test('numbers service: max', () => {
20+
const max = Db.coreFns.numbers.max([1, 2, 3]);
21+
expect(max).toBe(3);
22+
});
23+
});

src/cdm/ModulesFnModel.ts

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Literal } from "obsidian-dataview"
22
import { DateTime, DurationLikeObject } from "luxon";
3+
import { HSL } from "obsidian";
34

45
export interface NumbersInterface {
56
/**
@@ -64,11 +65,49 @@ export interface LuxonInterface {
6465
stringToDate(datetime: string, format?: string): DateTime;
6566
}
6667

68+
export interface ColorsInterface {
69+
/**
70+
* Converts a color from HSL to String
71+
* @param hsl
72+
* @returns @type {string} color
73+
*/
74+
hslToString(hsl: HSL): string;
75+
76+
/**
77+
* Transform a string to a valid HSL color or return a default color if the string is not valid
78+
* expected string format: `hsl(0-360, 0-100%, 0-100%)`
79+
* I.E.:
80+
* str `hsl(5, 60%, 20%)` returns {h: 5, s: 60, l: 20}
81+
* @param str
82+
* @returns @type {HSL} object
83+
*/
84+
stringtoHsl(str: string): HSL;
85+
86+
/**
87+
* Get the contrast color for a given HSL color based on the luminosity of the color
88+
* @param str or HSL object
89+
* @returns @type {string} color
90+
*/
91+
getContrast(hsl: HSL | string): string;
92+
93+
/**
94+
* Generate a random color in HSL format under a string type
95+
* @returns @type {string} color
96+
*/
97+
randomColor(): string;
98+
99+
/**
100+
* Between a scale from 0 to 9, returns a grey tone color in Hex format. Default value is 5 (medium grey)
101+
* @param value
102+
*/
103+
greyScale(value?: number): string;
104+
}
105+
67106
/**
68107
* Exposed functionalities under the `db` variable of Formulas
69108
*/
70109
export type DatabaseFnType = {
71110
numbers: NumbersInterface;
72111
luxon: LuxonInterface;
73-
[key: string]: unknown
112+
colors: ColorsInterface;
74113
}

src/components/RelationShip.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React, { useEffect, useRef } from "react";
22
import { RelationshipProps } from "cdm/FolderModel";
33
import { c } from "helpers/StylesHelper";
44
import { MarkdownService } from "services/MarkdownRenderService";
5+
import { Db } from "services/CoreService";
56

67
export default function Relationship(relationShipProps: RelationshipProps) {
78
const { option, view } = relationShipProps;
@@ -24,6 +25,7 @@ export default function Relationship(relationShipProps: RelationshipProps) {
2425
ref={ref}
2526
style={{
2627
backgroundColor: option.color,
28+
color: Db.coreFns.colors.getContrast(option.color),
2729
}}
2830
/>
2931
);

src/components/cellTypes/RelationCell.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { DataviewService } from "services/DataviewService";
99
import { ParseService } from "services/ParseService";
1010
import RelationEditor from "components/cellTypes/Editor/RelationEditor";
1111
import Relationship from "components/RelationShip";
12-
import { grey } from "helpers/Colors";
12+
import { Db } from "services/CoreService";
1313

1414
const RelationCell = (mdProps: CellComponentProps) => {
1515
const { defaultCell } = mdProps;
@@ -115,7 +115,9 @@ const RelationCell = (mdProps: CellComponentProps) => {
115115
option={{
116116
value: link.markdown(),
117117
label: link.markdown(),
118-
color: tableColumn.config.relation_color || grey(300),
118+
color:
119+
tableColumn.config.relation_color ||
120+
Db.coreFns.colors.greyScale(3),
119121
}}
120122
view={view}
121123
/>

src/components/cellTypes/SelectCell.tsx

Lines changed: 49 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import Relationship from "components/RelationShip";
2-
import { grey, randomColor } from "helpers/Colors";
32
import React, { useMemo, useState } from "react";
43
import {
54
CellComponentProps,
@@ -8,12 +7,14 @@ import {
87
} from "cdm/ComponentsModel";
98
import { TableColumn } from "cdm/FolderModel";
109
import CreatableSelect from "react-select/creatable";
10+
import Select from "react-select";
1111
import CustomTagsStyles from "components/styles/TagsStyles";
1212
import { c, getAlignmentClassname } from "helpers/StylesHelper";
1313
import { satinizedColumnOption } from "helpers/FileManagement";
1414
import { ActionMeta, OnChangeValue } from "react-select";
1515
import { ParseService } from "services/ParseService";
16-
import { InputType } from "helpers/Constants";
16+
import { InputType, OptionSource } from "helpers/Constants";
17+
import { Db } from "services/CoreService";
1718

1819
const SelectCell = (popperProps: CellComponentProps) => {
1920
const { defaultCell } = popperProps;
@@ -48,7 +49,7 @@ const SelectCell = (popperProps: CellComponentProps) => {
4849
const option: ColumnOption = {
4950
label: cellValue,
5051
value: cellValue,
51-
color: randomColor(),
52+
color: Db.coreFns.colors.randomColor(),
5253
};
5354
columnActions.addOptionToColumn(tableColumn, option);
5455
return option;
@@ -86,7 +87,7 @@ const SelectCell = (popperProps: CellComponentProps) => {
8687
const option: ColumnOption = {
8788
label: sanitized,
8889
value: sanitized,
89-
color: randomColor(),
90+
color: Db.coreFns.colors.randomColor(),
9091
};
9192

9293
await columnActions.addOptionToColumn(tableColumn, option);
@@ -96,35 +97,52 @@ const SelectCell = (popperProps: CellComponentProps) => {
9697
const options = columnsInfo.getColumnOptions(column.id, cellValue !== "");
9798

9899
function SelectComponent() {
100+
const selectProps = {
101+
defaultValue: defaultValue,
102+
isSearchable: true,
103+
autoFocus: true,
104+
isClearable: true,
105+
openMenuOnFocus: true,
106+
menuPosition: "fixed" as const,
107+
styles: CustomTagsStyles,
108+
options: options,
109+
onMenuClose: () => setShowSelect(false),
110+
onChange: handleOnChange,
111+
isMulti: false,
112+
menuPortalTarget: activeDocument.body,
113+
menuPlacement: "auto" as const,
114+
menuShouldBlockScroll: true,
115+
className: `react-select-container ${c(
116+
"tags-container text-align-center"
117+
)}`,
118+
classNamePrefix: "react-select",
119+
key: `${tableColumn.id}-select-open`,
120+
};
99121
return (
100122
<div className={c("tags")}>
101-
<CreatableSelect
102-
defaultValue={defaultValue}
103-
isSearchable
104-
autoFocus
105-
isClearable
106-
openMenuOnFocus
107-
menuPosition="fixed"
108-
components={{
109-
DropdownIndicator: () => null,
110-
IndicatorSeparator: () => null,
111-
ClearIndicator: () => null,
112-
CrossIcon: () => null,
113-
}}
114-
styles={CustomTagsStyles}
115-
options={options}
116-
onMenuClose={() => setShowSelect(false)}
117-
onChange={handleOnChange}
118-
isMulti={false}
119-
menuPortalTarget={activeDocument.body}
120-
menuPlacement="auto"
121-
menuShouldBlockScroll={true}
122-
className={`react-select-container ${c(
123-
"tags-container text-align-center"
124-
)}`}
125-
classNamePrefix="react-select"
126-
key={`${tableColumn.id}-select-open`}
127-
/>
123+
{tableColumn.config.option_source === OptionSource.MANUAL ? (
124+
<CreatableSelect
125+
{...selectProps}
126+
defaultValue={defaultValue}
127+
components={{
128+
DropdownIndicator: () => null,
129+
IndicatorSeparator: () => null,
130+
ClearIndicator: () => null,
131+
CrossIcon: () => null,
132+
}}
133+
/>
134+
) : (
135+
<Select
136+
{...selectProps}
137+
defaultValue={defaultValue}
138+
components={{
139+
DropdownIndicator: () => null,
140+
IndicatorSeparator: () => null,
141+
ClearIndicator: () => null,
142+
CrossIcon: () => null,
143+
}}
144+
/>
145+
)}
128146
</div>
129147
);
130148
}

0 commit comments

Comments
 (0)