@fibery/ui-kit 2.1.0 → 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/package.json +6 -3
- package/src/@types/react-color.d.ts +36 -0
- package/src/antd/{Tabs.js → Tabs.tsx} +5 -2
- package/src/color-filter.test.ts +24 -65
- package/src/color-filter.ts +62 -93
- package/src/color-picker/{ColorPickerOrLoader.js → ColorPickerOrLoader.tsx} +1 -1
- package/src/color-picker/{index.js → index.tsx} +32 -6
- package/src/color-utils.ts +3 -9
- package/src/design-system/colors-css.ts +16 -0
- package/src/design-system/colors.ts +23 -45
- package/src/design-system/def.ts +17 -0
- package/src/design-system/theme.ts +6 -2
- package/src/design-system/types.ts +102 -0
- package/src/design-system/vars.test.ts +9 -2
- package/src/design-system/vars.ts +4 -2
- package/src/design-system.ts +9 -1
- package/src/emoji-picker/emoji-picker-content-with-color.tsx +3 -3
- package/src/heat.ts +8 -0
- package/src/highlight-colors.test.ts +68 -0
- package/src/highlight-colors.ts +91 -0
- package/src/link-input/components/{AntTextAreaWithCustomReadState.js → AntTextAreaWithCustomReadState.tsx} +19 -8
- package/src/link-input/index.tsx +94 -0
- package/src/link-input/{utils.js → utils.ts} +1 -6
- package/src/number-input/decimal.ts +50 -0
- package/src/number-input/utils.ts +2 -2
- package/src/palette-generator.test.ts +2 -1
- package/src/palette-generator.ts +8 -26
- package/src/palettes/_.ts +2 -2
- package/src/palettes/common.ts +31 -0
- package/src/palettes/diff-colors.ts +29 -12
- package/src/palettes/inspect.defs.colors.neutral-arch.test.ts +72 -36
- package/src/palettes/inspect.defs.colors.neutral-user.test.ts +72 -36
- package/src/palettes/inspect.defs.colors.warm-arch.test.ts +226 -190
- package/src/palettes/inspect.defs.colors.warm-user.test.ts +226 -190
- package/src/palettes/neutral-arch.ts +3 -3
- package/src/palettes/neutral-user.ts +3 -3
- package/src/palettes/neutral.ts +4 -2
- package/src/palettes/warm-arch.ts +3 -3
- package/src/palettes/warm-user.ts +3 -3
- package/src/palettes/warm.ts +4 -3
- package/src/pretty-size.ts +5 -2
- package/src/root-theme-provider.test.tsx +1 -1
- package/src/scale-generator.ts +1 -32
- package/src/select/components/menu-list-virtua.tsx +127 -0
- package/src/select/index.tsx +1 -1
- package/src/select/reflection.ts +22 -0
- package/src/select/select.tsx +1 -0
- package/src/static-palettes.ts +2 -1
- package/src/thematic-color-picker.tsx +8 -1
- package/src/thematic-constants.tsx +2 -0
- package/src/thematic-controls.tsx +9 -1
- package/src/thematic-cvd.tsx +73 -0
- package/src/thematic-highlights.tsx +49 -0
- package/src/thematic-scales.tsx +8 -6
- package/src/thematic-state.ts +37 -16
- package/src/thematic.tsx +298 -305
- package/src/theme-provider.tsx +9 -3
- package/src/theme-styles.ts +2 -1
- package/src/link-input/index.js +0 -89
- package/src/number-input/decimal.js +0 -61
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import {neutralCore} from "./neutral";
|
|
2
|
+
import type {ThemePalette} from "../design-system/types";
|
|
3
3
|
|
|
4
4
|
export const neutralArchPalette: ThemePalette = {
|
|
5
|
-
...
|
|
5
|
+
...neutralCore,
|
|
6
6
|
key: "neutral-arch",
|
|
7
7
|
accent: [
|
|
8
8
|
null,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import {neutralCore} from "./neutral";
|
|
2
|
+
import type {ThemePalette} from "../design-system/types";
|
|
3
3
|
|
|
4
4
|
export const neutralUserPalette: ThemePalette = {
|
|
5
|
-
...
|
|
5
|
+
...neutralCore,
|
|
6
6
|
key: "neutral-user",
|
|
7
7
|
accent: [
|
|
8
8
|
null,
|
package/src/palettes/neutral.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {common} from "./common";
|
|
2
|
+
import type {ThemePaletteCore} from "../design-system/types";
|
|
2
3
|
|
|
3
|
-
export const
|
|
4
|
+
export const neutralCore: ThemePaletteCore = {
|
|
5
|
+
...common,
|
|
4
6
|
// warm-dark no-applied:
|
|
5
7
|
// "white": "rgb(255,252,247)",
|
|
6
8
|
// "black": "rgb(5,4,4)",
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import {warmCore} from "./warm";
|
|
2
|
+
import type {ThemePalette} from "../design-system/types";
|
|
3
3
|
|
|
4
4
|
export const warmArchPalette: ThemePalette = {
|
|
5
|
-
...
|
|
5
|
+
...warmCore,
|
|
6
6
|
key: "warm-arch",
|
|
7
7
|
accent: [
|
|
8
8
|
null,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import {warmCore} from "./warm";
|
|
2
|
+
import type {ThemePalette} from "../design-system/types";
|
|
3
3
|
|
|
4
4
|
export const warmUserPalette: ThemePalette = {
|
|
5
|
-
...
|
|
5
|
+
...warmCore,
|
|
6
6
|
key: "warm-user",
|
|
7
7
|
accent: [
|
|
8
8
|
null,
|
package/src/palettes/warm.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {common} from "./common";
|
|
2
|
+
import type {ThemePaletteCore} from "../design-system/types";
|
|
2
3
|
|
|
3
|
-
export const
|
|
4
|
-
|
|
4
|
+
export const warmCore: ThemePaletteCore = {
|
|
5
|
+
...common,
|
|
5
6
|
white: "rgb(255,253,250)",
|
|
6
7
|
whiteA: [
|
|
7
8
|
null,
|
package/src/pretty-size.ts
CHANGED
|
@@ -2,7 +2,10 @@ const UNITS = ["byte", "kilobyte", "megabyte", "gigabyte", "terabyte", "petabyte
|
|
|
2
2
|
const BYTES_PER_KB = 1000;
|
|
3
3
|
|
|
4
4
|
// ref: https://stackoverflow.com/a/72596863/4280642
|
|
5
|
-
export function prettySize(
|
|
5
|
+
export function prettySize(
|
|
6
|
+
sizeBytes: number | bigint,
|
|
7
|
+
unitDisplay: "short" | "long" | "narrow" | undefined = "narrow"
|
|
8
|
+
): string {
|
|
6
9
|
let size = Math.abs(Number(sizeBytes));
|
|
7
10
|
|
|
8
11
|
let unitIdx = 0;
|
|
@@ -14,7 +17,7 @@ export function prettySize(sizeBytes: number | bigint): string {
|
|
|
14
17
|
return new Intl.NumberFormat([], {
|
|
15
18
|
style: "unit",
|
|
16
19
|
unit: UNITS[unitIdx],
|
|
17
|
-
unitDisplay
|
|
20
|
+
unitDisplay,
|
|
18
21
|
notation: "standard",
|
|
19
22
|
maximumFractionDigits: 1,
|
|
20
23
|
})
|
|
@@ -8,10 +8,10 @@ import {
|
|
|
8
8
|
useThemeMode,
|
|
9
9
|
useThemePreference,
|
|
10
10
|
} from "./theme-provider";
|
|
11
|
-
import {ThemePalette} from "./palette-generator";
|
|
12
11
|
import {locateThemeBundle} from "./theme-styles";
|
|
13
12
|
import {stylesFromTheme, themeFromPalette} from "./design-system";
|
|
14
13
|
import {storeWarmPreference} from "./theme-settings";
|
|
14
|
+
import type {ThemePalette} from "./design-system/types";
|
|
15
15
|
|
|
16
16
|
const {
|
|
17
17
|
palette,
|
package/src/scale-generator.ts
CHANGED
|
@@ -1,36 +1,5 @@
|
|
|
1
1
|
import {fromOKLCH} from "./color-utils";
|
|
2
|
-
import type {ColorScale} from "./
|
|
3
|
-
|
|
4
|
-
export type OneBasedScale<T> = readonly [
|
|
5
|
-
base00: null,
|
|
6
|
-
step01: T,
|
|
7
|
-
step02: T,
|
|
8
|
-
step03: T,
|
|
9
|
-
step04: T,
|
|
10
|
-
step05: T,
|
|
11
|
-
step06: T,
|
|
12
|
-
step07: T,
|
|
13
|
-
step08: T,
|
|
14
|
-
step09: T,
|
|
15
|
-
step10: T,
|
|
16
|
-
step11: T,
|
|
17
|
-
step12: T,
|
|
18
|
-
];
|
|
19
|
-
|
|
20
|
-
export type ZeroBasedScale<T> = readonly [
|
|
21
|
-
step01: T,
|
|
22
|
-
step02: T,
|
|
23
|
-
step03: T,
|
|
24
|
-
step04: T,
|
|
25
|
-
step05: T,
|
|
26
|
-
step06: T,
|
|
27
|
-
step07: T,
|
|
28
|
-
step08: T,
|
|
29
|
-
step09: T,
|
|
30
|
-
step10: T,
|
|
31
|
-
step11: T,
|
|
32
|
-
step12: T,
|
|
33
|
-
];
|
|
2
|
+
import type {ColorScale, OneBasedScale, ZeroBasedScale} from "./design-system/types";
|
|
34
3
|
|
|
35
4
|
export function zeroBasedFromOneBased<T>(scale: OneBasedScale<T>) {
|
|
36
5
|
return scale.slice(1) as unknown as ZeroBasedScale<T>;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import compact from "lodash/compact";
|
|
2
|
+
import {
|
|
3
|
+
Children,
|
|
4
|
+
ComponentType,
|
|
5
|
+
CSSProperties,
|
|
6
|
+
ReactNode,
|
|
7
|
+
useContext,
|
|
8
|
+
useEffect,
|
|
9
|
+
useMemo,
|
|
10
|
+
useRef,
|
|
11
|
+
useState,
|
|
12
|
+
} from "react";
|
|
13
|
+
import type {GroupHeadingProps, GroupProps, MenuListProps} from "react-select";
|
|
14
|
+
import {VList, VListHandle} from "virtua";
|
|
15
|
+
import {GroupBase, ReactSelectRefContext} from "../util";
|
|
16
|
+
import {isGroupChild, isReactElementWithProps} from "../reflection";
|
|
17
|
+
|
|
18
|
+
const DEFAULT_ITEM_SIZE_ESTIMATE = 48;
|
|
19
|
+
|
|
20
|
+
function flattenGroupChildren(
|
|
21
|
+
children: ReactNode,
|
|
22
|
+
renderGroup: (Component: ComponentType<GroupHeadingProps>, props: GroupProps["headingProps"]) => ReactNode
|
|
23
|
+
): ReactNode[] {
|
|
24
|
+
return compact(
|
|
25
|
+
Children.toArray(children)
|
|
26
|
+
.map((child) => {
|
|
27
|
+
if (isReactElementWithProps(child) && isGroupChild(child)) {
|
|
28
|
+
const {props} = child;
|
|
29
|
+
const groupChildren = compact(Children.toArray(props.children));
|
|
30
|
+
if (!groupChildren.length) {
|
|
31
|
+
return [];
|
|
32
|
+
}
|
|
33
|
+
const heading = renderGroup(props.Heading, props.headingProps);
|
|
34
|
+
return [heading, ...groupChildren];
|
|
35
|
+
}
|
|
36
|
+
return child;
|
|
37
|
+
})
|
|
38
|
+
.flat()
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Virtualized MenuList using virtua — supports dynamic item heights.
|
|
44
|
+
* Use with `virtualized={false}` + `components={{ MenuList: MenuListVirtua }}`.
|
|
45
|
+
*/
|
|
46
|
+
export function MenuListVirtua<
|
|
47
|
+
Option,
|
|
48
|
+
IsMulti extends boolean = boolean,
|
|
49
|
+
Group extends GroupBase<Option> = GroupBase<Option>,
|
|
50
|
+
>(props: MenuListProps<Option, IsMulti, Group>) {
|
|
51
|
+
const {children, focusedOption, maxHeight, getStyles, selectProps, cx, theme} = props;
|
|
52
|
+
const vlistRef = useRef<VListHandle>(null);
|
|
53
|
+
const select = useContext(ReactSelectRefContext);
|
|
54
|
+
const [initialized, setInitialized] = useState(false);
|
|
55
|
+
|
|
56
|
+
const renderGroup = useMemo(
|
|
57
|
+
() => (Component: ComponentType<GroupHeadingProps>, headingProps: GroupProps["headingProps"]) => {
|
|
58
|
+
const content = selectProps.formatGroupLabel
|
|
59
|
+
? selectProps.formatGroupLabel(headingProps.data as Group)
|
|
60
|
+
: headingProps.data.label;
|
|
61
|
+
return content ? (
|
|
62
|
+
<Component
|
|
63
|
+
cx={cx}
|
|
64
|
+
selectProps={selectProps as unknown as GroupHeadingProps["selectProps"]}
|
|
65
|
+
theme={theme}
|
|
66
|
+
getStyles={getStyles}
|
|
67
|
+
{...headingProps}
|
|
68
|
+
>
|
|
69
|
+
{content}
|
|
70
|
+
</Component>
|
|
71
|
+
) : null;
|
|
72
|
+
},
|
|
73
|
+
[cx, getStyles, selectProps, theme]
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
const items = useMemo(() => flattenGroupChildren(children, renderGroup), [children, renderGroup]);
|
|
77
|
+
const estimatedHeight = Math.min(items.length * DEFAULT_ITEM_SIZE_ESTIMATE, maxHeight);
|
|
78
|
+
|
|
79
|
+
const focusedIndex = useMemo(() => {
|
|
80
|
+
if (!focusedOption) {
|
|
81
|
+
return -1;
|
|
82
|
+
}
|
|
83
|
+
return items.findIndex((child) => {
|
|
84
|
+
return isReactElementWithProps(child) && "data" in child.props && child.props.data === focusedOption;
|
|
85
|
+
});
|
|
86
|
+
}, [focusedOption, items]);
|
|
87
|
+
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
if (!select.current || !items.length || initialized) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
let firstOption: unknown;
|
|
93
|
+
let selectedOption: unknown;
|
|
94
|
+
for (const child of items) {
|
|
95
|
+
if (isReactElementWithProps(child) && "type" in child.props && child.props.type === "option") {
|
|
96
|
+
if (!firstOption && "data" in child.props) {
|
|
97
|
+
firstOption = child.props.data;
|
|
98
|
+
}
|
|
99
|
+
if ("isSelected" in child.props && child.props.isSelected && "data" in child.props) {
|
|
100
|
+
selectedOption = child.props.data;
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const target = selectedOption || firstOption;
|
|
106
|
+
if (target) {
|
|
107
|
+
setInitialized(true);
|
|
108
|
+
select.current.setState({focusedOption: target});
|
|
109
|
+
}
|
|
110
|
+
}, [items, initialized, select]);
|
|
111
|
+
|
|
112
|
+
useEffect(() => {
|
|
113
|
+
if (focusedIndex >= 0 && vlistRef.current) {
|
|
114
|
+
vlistRef.current.scrollToIndex(focusedIndex, {align: "nearest"});
|
|
115
|
+
}
|
|
116
|
+
}, [focusedIndex]);
|
|
117
|
+
|
|
118
|
+
return (
|
|
119
|
+
<div style={{height: estimatedHeight, ...(getStyles("menuList", props) as CSSProperties), overflow: "hidden"}}>
|
|
120
|
+
<VList ref={vlistRef} style={{height: "100%"}} itemSize={DEFAULT_ITEM_SIZE_ESTIMATE}>
|
|
121
|
+
{items.map((child, i) => (
|
|
122
|
+
<div key={i}>{child}</div>
|
|
123
|
+
))}
|
|
124
|
+
</VList>
|
|
125
|
+
</div>
|
|
126
|
+
);
|
|
127
|
+
}
|
package/src/select/index.tsx
CHANGED
|
@@ -27,7 +27,7 @@ export type {
|
|
|
27
27
|
SelectProps,
|
|
28
28
|
} from "./select";
|
|
29
29
|
|
|
30
|
-
export {components, MenuListVirtualized, createFilter} from "./select";
|
|
30
|
+
export {components, MenuListVirtualized, MenuListVirtua, createFilter} from "./select";
|
|
31
31
|
|
|
32
32
|
export {combineStyles} from "./select";
|
|
33
33
|
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import isObject from "lodash/isObject";
|
|
2
|
+
import {GroupBase} from "./util";
|
|
3
|
+
import type {GroupProps} from "react-select/dist/declarations/src";
|
|
4
|
+
import isFunction from "lodash/isFunction";
|
|
5
|
+
|
|
6
|
+
type ReactElementWithProps<P = object> = {props: P};
|
|
7
|
+
|
|
8
|
+
export function isReactElementWithProps(node: unknown): node is ReactElementWithProps {
|
|
9
|
+
return isObject(node) && "props" in node && isObject(node.props);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function isGroupChild<Option, IsMulti extends boolean, Group extends GroupBase<Option> = GroupBase<Option>>(
|
|
13
|
+
node: ReactElementWithProps
|
|
14
|
+
): node is ReactElementWithProps<GroupProps<Option, IsMulti, Group>> {
|
|
15
|
+
return (
|
|
16
|
+
"options" in node.props &&
|
|
17
|
+
"Heading" in node.props &&
|
|
18
|
+
isFunction(node.props.Heading) &&
|
|
19
|
+
"headingProps" in node.props &&
|
|
20
|
+
isObject(node.props.headingProps)
|
|
21
|
+
);
|
|
22
|
+
}
|
package/src/select/select.tsx
CHANGED
package/src/static-palettes.ts
CHANGED
|
@@ -58,6 +58,11 @@ export const miniSwatchCss = css`
|
|
|
58
58
|
height: 11px;
|
|
59
59
|
`;
|
|
60
60
|
|
|
61
|
+
export const tinySwatchCss = css`
|
|
62
|
+
width: 16px;
|
|
63
|
+
height: 16px;
|
|
64
|
+
`;
|
|
65
|
+
|
|
61
66
|
const splitSwatchCss = css`
|
|
62
67
|
width: 22px;
|
|
63
68
|
height: 22px;
|
|
@@ -107,6 +112,7 @@ export function SwatchWithPicker({
|
|
|
107
112
|
splitBg,
|
|
108
113
|
altColor,
|
|
109
114
|
mini,
|
|
115
|
+
tiny,
|
|
110
116
|
inspectChannel,
|
|
111
117
|
}: {
|
|
112
118
|
color: string;
|
|
@@ -118,9 +124,10 @@ export function SwatchWithPicker({
|
|
|
118
124
|
splitBg?: boolean;
|
|
119
125
|
altColor?: string;
|
|
120
126
|
mini?: boolean;
|
|
127
|
+
tiny?: boolean;
|
|
121
128
|
inspectChannel?: InspectChannel;
|
|
122
129
|
}) {
|
|
123
|
-
const baseCls = mini ? miniSwatchCss : swatchCss;
|
|
130
|
+
const baseCls = tiny ? tinySwatchCss : mini ? miniSwatchCss : swatchCss;
|
|
124
131
|
const splitCls = mini ? miniSplitSwatchCss : splitSwatchCss;
|
|
125
132
|
|
|
126
133
|
const overlay = inspectChannel
|
|
@@ -3,12 +3,14 @@ import type {ThemePreference} from "./theme-settings";
|
|
|
3
3
|
import AppearanceLight from "./icons/react/AppearanceLight";
|
|
4
4
|
import AppearanceDark from "./icons/react/AppearanceDark";
|
|
5
5
|
import AppearanceAuto from "./icons/react/AppearanceAuto";
|
|
6
|
+
import {common} from "./palettes/common";
|
|
6
7
|
|
|
7
8
|
export const defaultPrefs: ThematicPrefs = {
|
|
8
9
|
baseHue: 0,
|
|
9
10
|
baseChroma: 0,
|
|
10
11
|
accentHue: 264,
|
|
11
12
|
accentChroma: 0.16,
|
|
13
|
+
highlight: common.highlight,
|
|
12
14
|
};
|
|
13
15
|
|
|
14
16
|
export const HIGHLIGHT_COLOR = "#ff00ff";
|
|
@@ -219,6 +219,7 @@ export function Slider({
|
|
|
219
219
|
color,
|
|
220
220
|
onLabelHover,
|
|
221
221
|
onLabelLeave,
|
|
222
|
+
onLabelClick,
|
|
222
223
|
}: {
|
|
223
224
|
label: string;
|
|
224
225
|
value: number;
|
|
@@ -231,10 +232,17 @@ export function Slider({
|
|
|
231
232
|
color?: string;
|
|
232
233
|
onLabelHover?: () => void;
|
|
233
234
|
onLabelLeave?: () => void;
|
|
235
|
+
onLabelClick?: () => void;
|
|
234
236
|
}) {
|
|
235
237
|
return (
|
|
236
238
|
<div className={controlRowCss}>
|
|
237
|
-
<span
|
|
239
|
+
<span
|
|
240
|
+
className={controlLabelCss}
|
|
241
|
+
style={onLabelClick ? {cursor: "pointer"} : undefined}
|
|
242
|
+
onMouseEnter={onLabelHover}
|
|
243
|
+
onMouseLeave={onLabelLeave}
|
|
244
|
+
onClick={onLabelClick}
|
|
245
|
+
>
|
|
238
246
|
{label}
|
|
239
247
|
</span>
|
|
240
248
|
<input
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import {useState} from "react";
|
|
2
|
+
import {useThematicState} from "./thematic-state";
|
|
3
|
+
import {sectionLabelCss, sectionSeparatorCss} from "./thematic-scales";
|
|
4
|
+
import {Slider} from "./thematic-controls";
|
|
5
|
+
import {cvdChannels} from "./color-filter";
|
|
6
|
+
|
|
7
|
+
const types = [null, ...cvdChannels] as const;
|
|
8
|
+
|
|
9
|
+
const activeKey = (dn: ReturnType<typeof useThematicState>["deneutralizeSpec"]) => {
|
|
10
|
+
for (const {key} of cvdChannels) {
|
|
11
|
+
if (dn?.[key]) {
|
|
12
|
+
return key;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return null;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export function CvdSection({state}: {state: ReturnType<typeof useThematicState>}) {
|
|
19
|
+
const dn = state.deneutralizeSpec;
|
|
20
|
+
const current = activeKey(dn);
|
|
21
|
+
const currentChannel = current ? cvdChannels.find((c) => c.key === current) : null;
|
|
22
|
+
const liveSeverity = current ? (dn?.[current] ?? 0) : 0;
|
|
23
|
+
const [savedAmount, setSavedAmount] = useState(0.8);
|
|
24
|
+
|
|
25
|
+
const amount = current ? liveSeverity : savedAmount;
|
|
26
|
+
|
|
27
|
+
if (liveSeverity > 0 && liveSeverity !== savedAmount) {
|
|
28
|
+
setSavedAmount(liveSeverity);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const onCycle = () => {
|
|
32
|
+
const currentIdx = current ? types.findIndex((t) => t && t.key === current) : 0;
|
|
33
|
+
const next = types[(currentIdx + 1) % types.length];
|
|
34
|
+
|
|
35
|
+
const update: Record<string, number> = {};
|
|
36
|
+
for (const {key} of cvdChannels) {
|
|
37
|
+
update[key] = 0;
|
|
38
|
+
}
|
|
39
|
+
if (next) {
|
|
40
|
+
update[next.key] = amount;
|
|
41
|
+
}
|
|
42
|
+
state.updateFilter(update);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<>
|
|
47
|
+
<div className={sectionSeparatorCss} />
|
|
48
|
+
<div className={sectionLabelCss} style={{fontWeight: 700, cursor: "pointer"}} onClick={onCycle}>
|
|
49
|
+
cvd: {currentChannel?.label ?? "none"}
|
|
50
|
+
</div>
|
|
51
|
+
<Slider
|
|
52
|
+
label="amount"
|
|
53
|
+
value={amount}
|
|
54
|
+
min={0}
|
|
55
|
+
max={1}
|
|
56
|
+
step={0.05}
|
|
57
|
+
onChange={(v) => {
|
|
58
|
+
setSavedAmount(v);
|
|
59
|
+
if (current) {
|
|
60
|
+
state.updateFilter({[current]: v});
|
|
61
|
+
}
|
|
62
|
+
}}
|
|
63
|
+
onLabelClick={() => {
|
|
64
|
+
const next = amount > 0 ? 0 : 1;
|
|
65
|
+
setSavedAmount(next);
|
|
66
|
+
if (current) {
|
|
67
|
+
state.updateFilter({[current]: next});
|
|
68
|
+
}
|
|
69
|
+
}}
|
|
70
|
+
/>
|
|
71
|
+
</>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import {useThematicState} from "./thematic-state";
|
|
2
|
+
import {sectionLabelCss, sectionSeparatorCss} from "./thematic-scales";
|
|
3
|
+
import {Slider} from "./thematic-controls";
|
|
4
|
+
|
|
5
|
+
export function HighlightSection({state}: {state: ReturnType<typeof useThematicState>}) {
|
|
6
|
+
const side = state.showDark ? 1 : 0;
|
|
7
|
+
const h = state.palette.highlight;
|
|
8
|
+
|
|
9
|
+
const update = ({bgLc, bgC, fgC, fgLc}: {bgLc?: number; bgC?: number; fgC?: number; fgLc?: number}) => {
|
|
10
|
+
const newBg = [...h.bg] as typeof h.bg;
|
|
11
|
+
const newFg = [...h.fg] as typeof h.fg;
|
|
12
|
+
if (bgLc !== undefined) {
|
|
13
|
+
newBg[side] = {...newBg[side], Lc: bgLc};
|
|
14
|
+
}
|
|
15
|
+
if (bgC !== undefined) {
|
|
16
|
+
newBg[side] = {...newBg[side], c: bgC};
|
|
17
|
+
}
|
|
18
|
+
if (fgC !== undefined) {
|
|
19
|
+
newFg[side] = {...newFg[side], c: fgC};
|
|
20
|
+
}
|
|
21
|
+
if (fgLc !== undefined) {
|
|
22
|
+
newFg[side] = {...newFg[side], Lc: fgLc};
|
|
23
|
+
}
|
|
24
|
+
state.updateHighlight({...h, bg: newBg, fg: newFg});
|
|
25
|
+
};
|
|
26
|
+
return (
|
|
27
|
+
<>
|
|
28
|
+
<div className={sectionSeparatorCss} />
|
|
29
|
+
<div className={sectionLabelCss} style={{fontWeight: 700}}>
|
|
30
|
+
highlight ({state.showDark ? "dark" : "light"})
|
|
31
|
+
</div>
|
|
32
|
+
<Slider
|
|
33
|
+
label="bg Lc"
|
|
34
|
+
value={h.bg[side].Lc}
|
|
35
|
+
min={0}
|
|
36
|
+
max={30}
|
|
37
|
+
step={0.01}
|
|
38
|
+
onChange={(bgLc) => {
|
|
39
|
+
// oxlint-disable-next-line no-console
|
|
40
|
+
console.log(`[slider] bgLc=${bgLc} t=${performance.now().toFixed(0)}`);
|
|
41
|
+
update({bgLc});
|
|
42
|
+
}}
|
|
43
|
+
/>
|
|
44
|
+
<Slider label="bg Ch" value={h.bg[side].c} min={0} max={0.4} step={0.001} onChange={(bgC) => update({bgC})} />
|
|
45
|
+
<Slider label="fg Lc" value={h.fg[side].Lc} min={0} max={90} step={0.01} onChange={(fgLc) => update({fgLc})} />
|
|
46
|
+
<Slider label="fg Ch" value={h.fg[side].c} min={0} max={0.4} step={0.001} onChange={(fgC) => update({fgC})} />
|
|
47
|
+
</>
|
|
48
|
+
);
|
|
49
|
+
}
|
package/src/thematic-scales.tsx
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import {css} from "@linaria/core";
|
|
2
|
-
import {type ColorScale} from "./palette-generator";
|
|
3
2
|
import {space, themeVars} from "./design-system";
|
|
4
|
-
import {type InspectChannel, SwatchWithPicker, miniSwatchCss, swatchCss} from "./thematic-color-picker";
|
|
3
|
+
import {type InspectChannel, SwatchWithPicker, miniSwatchCss, swatchCss, tinySwatchCss} from "./thematic-color-picker";
|
|
5
4
|
|
|
6
5
|
export const scaleCss = css`
|
|
7
6
|
display: flex;
|
|
@@ -42,9 +41,9 @@ const gridCss = css`
|
|
|
42
41
|
overflow-x: auto;
|
|
43
42
|
`;
|
|
44
43
|
|
|
45
|
-
type ScaleEntry = {
|
|
44
|
+
export type ScaleEntry = {
|
|
46
45
|
name: string;
|
|
47
|
-
scale:
|
|
46
|
+
scale: readonly (string | null)[];
|
|
48
47
|
onColorChange?: (index: number, color: string) => void;
|
|
49
48
|
onHover?: (color: string, name: string) => void;
|
|
50
49
|
onHoverEnd?: () => void;
|
|
@@ -52,6 +51,7 @@ type ScaleEntry = {
|
|
|
52
51
|
splitBg?: boolean;
|
|
53
52
|
altColors?: (string | null)[];
|
|
54
53
|
mini?: boolean;
|
|
54
|
+
tiny?: boolean;
|
|
55
55
|
};
|
|
56
56
|
|
|
57
57
|
export function ScaleGrid({
|
|
@@ -64,11 +64,11 @@ export function ScaleGrid({
|
|
|
64
64
|
}) {
|
|
65
65
|
return (
|
|
66
66
|
<div className={gridCss}>
|
|
67
|
-
{scales.map(({name, scale, onColorChange, onHover, onHoverEnd, extra, splitBg, altColors, mini}) => (
|
|
67
|
+
{scales.map(({name, scale, onColorChange, onHover, onHoverEnd, extra, splitBg, altColors, mini, tiny}) => (
|
|
68
68
|
<div key={name} className={columnCss}>
|
|
69
69
|
{scale.map((color, i) =>
|
|
70
70
|
color === null ? null : (color as unknown) === SPACER ? (
|
|
71
|
-
<div key={i} className={`${mini ? miniSwatchCss : swatchCss} ${emptySwatchCss}`} />
|
|
71
|
+
<div key={i} className={`${tiny ? tinySwatchCss : mini ? miniSwatchCss : swatchCss} ${emptySwatchCss}`} />
|
|
72
72
|
) : (
|
|
73
73
|
<SwatchWithPicker
|
|
74
74
|
key={i}
|
|
@@ -80,6 +80,7 @@ export function ScaleGrid({
|
|
|
80
80
|
splitBg={splitBg}
|
|
81
81
|
altColor={altColors?.[i] ?? undefined}
|
|
82
82
|
mini={mini}
|
|
83
|
+
tiny={tiny}
|
|
83
84
|
inspectChannel={inspectChannel}
|
|
84
85
|
/>
|
|
85
86
|
)
|
|
@@ -92,6 +93,7 @@ export function ScaleGrid({
|
|
|
92
93
|
onHover={onHover}
|
|
93
94
|
onHoverEnd={onHoverEnd}
|
|
94
95
|
mini={mini}
|
|
96
|
+
tiny={tiny}
|
|
95
97
|
inspectChannel={inspectChannel}
|
|
96
98
|
/>
|
|
97
99
|
)}
|