@fibery/ui-kit 1.40.3 → 1.40.4
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 +4 -7
- package/src/__snapshots__/design-system.test.ts.snap +7265 -0
- package/src/a11y-color.test.ts +181 -0
- package/src/a11y-color.ts +13 -20
- package/src/actions-menu/actions-menu-item.tsx +7 -7
- package/src/antd/ant-modal.tsx +10 -5
- package/src/antd/styles.ts +6 -6
- package/src/app-icon-with-fallback.tsx +2 -2
- package/src/app-icon.tsx +2 -2
- package/src/button/button.tsx +1 -0
- package/src/button/make-button-colors.ts +5 -13
- package/src/checkbox.tsx +2 -2
- package/src/color-picker/ColorPickerOrLoader.js +2 -2
- package/src/color-utils.test.ts +317 -0
- package/src/color-utils.ts +180 -0
- package/src/comment.tsx +3 -2
- package/src/context-menu/index.tsx +12 -7
- package/src/create-inline-theme.ts +9 -8
- package/src/design-system.colors.ts +760 -0
- package/src/design-system.test.ts +287 -7
- package/src/design-system.ts +146 -940
- package/src/dropdown-menu/index.tsx +21 -16
- package/src/emoji-picker/app-icon-picker.tsx +5 -5
- package/src/emoji-picker/emoji-picker-content-with-color.tsx +4 -4
- package/src/emoji-picker/icon-emoji-picker.tsx +2 -2
- package/src/favorites-icon.tsx +1 -1
- package/src/file-item/file-icon.tsx +169 -0
- package/src/file-item/file-menu-items.tsx +68 -0
- package/src/file-item/file-preview-actions.tsx +38 -0
- package/src/file-item/file-title.tsx +48 -0
- package/src/file-item/types.ts +27 -0
- package/src/file-item/use-register-in-image-gallery.tsx +70 -0
- package/src/file-item-2.tsx +35 -345
- package/src/file-item.tsx +6 -2
- package/src/hue-shift.test.ts +91 -0
- package/src/icons/ast/FileCounter.ts +8 -0
- package/src/icons/ast/FileMultiple.ts +8 -0
- package/src/icons/ast/ValueEdit.ts +8 -0
- package/src/icons/ast/index.tsx +3 -0
- package/src/icons/react/FileCounter.tsx +13 -0
- package/src/icons/react/FileMultiple.tsx +13 -0
- package/src/icons/react/ValueEdit.tsx +13 -0
- package/src/icons/react/index.tsx +3 -0
- package/src/icons/svg/file-counter.svg +3 -0
- package/src/icons/svg/file-multiple.svg +3 -0
- package/src/icons/svg/value-edit.svg +3 -0
- package/src/images-gallery/slide-buttons.tsx +4 -11
- package/src/lists/actions-menu-row-surface.tsx +5 -5
- package/src/mobile-keyboard-aware-popup.tsx +4 -3
- package/src/palette-generator.test.ts +566 -0
- package/src/palette-generator.ts +166 -0
- package/src/palette.ts +71 -55
- package/src/platform.ts +0 -3
- package/src/popover/index.tsx +13 -15
- package/src/progress.tsx +2 -2
- package/src/reactions/reaction-button.tsx +12 -6
- package/src/root-theme-provider.test.tsx +316 -0
- package/src/scale-generator.ts +347 -0
- package/src/select/components/menu-list-virtualized.tsx +7 -22
- package/src/select/components/menu.tsx +12 -2
- package/src/select/select.tsx +2 -1
- package/src/select/styles.ts +0 -1
- package/src/static-palettes.ts +146 -0
- package/src/thematic-color-picker.tsx +266 -0
- package/src/thematic-constants.tsx +27 -0
- package/src/thematic-controls.tsx +144 -0
- package/src/thematic-scales.tsx +122 -0
- package/src/thematic-state.ts +333 -0
- package/src/thematic.tsx +382 -0
- package/src/theme-provider.test.tsx +808 -0
- package/src/theme-provider.tsx +132 -69
- package/src/theme-settings.ts +1 -1
- package/src/theme-styles.ts +12 -5
- package/src/toast/toast-action.tsx +1 -1
- package/src/toggle-on-off.tsx +2 -2
- package/src/toggle.tsx +5 -6
- package/src/tooltip.tsx +13 -10
- package/src/type-badge.tsx +3 -3
- package/src/unit/styles.ts +2 -2
- package/src/unit/unit-with-tooltip.tsx +3 -2
- package/src/use-long-press.tsx +2 -2
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import {css} from "@linaria/core";
|
|
2
1
|
import _, {isFunction, isObject} from "lodash";
|
|
3
2
|
import {
|
|
4
3
|
Children,
|
|
@@ -18,23 +17,9 @@ import {GroupBase, ReactSelectRefContext} from "../util";
|
|
|
18
17
|
import {useSelectControlSettings} from "../select-control-settings-context";
|
|
19
18
|
import {useIsPhone} from "../../use-is-phone";
|
|
20
19
|
|
|
21
|
-
const PADDING_BLOCK_HEIGHT = 6;
|
|
22
|
-
|
|
23
|
-
const Spacer = () => (
|
|
24
|
-
<div
|
|
25
|
-
className={css`
|
|
26
|
-
height: ${PADDING_BLOCK_HEIGHT}px;
|
|
27
|
-
`}
|
|
28
|
-
/>
|
|
29
|
-
);
|
|
30
|
-
|
|
31
|
-
const components = {
|
|
32
|
-
Footer: Spacer,
|
|
33
|
-
Header: Spacer,
|
|
34
|
-
};
|
|
35
|
-
|
|
36
20
|
// Threshold to determine when to scroll: if focused item is less than this visible, scroll it into view
|
|
37
21
|
const MINIMAL_VISIBLE_PORTION_TO_SCROLL_INTO_VIEW = 0.2;
|
|
22
|
+
|
|
38
23
|
function isGroupChild<Option, isMulty extends boolean, Group extends GroupBase<Option> = GroupBase<Option>>(
|
|
39
24
|
props: unknown
|
|
40
25
|
): props is GroupProps<Option, isMulty, Group> {
|
|
@@ -47,6 +32,7 @@ function isGroupChild<Option, isMulty extends boolean, Group extends GroupBase<O
|
|
|
47
32
|
isObject(props.headingProps)
|
|
48
33
|
);
|
|
49
34
|
}
|
|
35
|
+
|
|
50
36
|
const flattenGroupChildren: (
|
|
51
37
|
children: ReactNode,
|
|
52
38
|
renderGroup: (Component: ComponentType<GroupHeadingProps>, props: GroupProps["headingProps"]) => ReactNode
|
|
@@ -73,7 +59,9 @@ const flattenGroupChildren: (
|
|
|
73
59
|
.flat()
|
|
74
60
|
);
|
|
75
61
|
};
|
|
62
|
+
|
|
76
63
|
const drawListElement: ItemContent<ReactNode, never> = (index, child) => child;
|
|
64
|
+
|
|
77
65
|
export function MenuListVirtualized<
|
|
78
66
|
Option,
|
|
79
67
|
IsMulti extends boolean = boolean,
|
|
@@ -117,7 +105,7 @@ export function MenuListVirtualized<
|
|
|
117
105
|
|
|
118
106
|
// Calculate menu height
|
|
119
107
|
const menuHeight = useMemo(
|
|
120
|
-
() => Math.min(flattenChildren.length * itemHeight
|
|
108
|
+
() => Math.min(flattenChildren.length * itemHeight, maxHeight),
|
|
121
109
|
[flattenChildren.length, itemHeight, maxHeight]
|
|
122
110
|
);
|
|
123
111
|
|
|
@@ -188,10 +176,10 @@ export function MenuListVirtualized<
|
|
|
188
176
|
}
|
|
189
177
|
|
|
190
178
|
virtuoso.current.getState((state) => {
|
|
191
|
-
const focusedItemTop = focusedIndex * itemHeight
|
|
179
|
+
const focusedItemTop = focusedIndex * itemHeight;
|
|
192
180
|
const focusedItemBottom = focusedItemTop + itemHeight;
|
|
193
181
|
const viewportTop = state.scrollTop;
|
|
194
|
-
const viewportBottom = state.scrollTop + menuHeight
|
|
182
|
+
const viewportBottom = state.scrollTop + menuHeight;
|
|
195
183
|
|
|
196
184
|
// Check if item is cut off at the top (even partially)
|
|
197
185
|
const isTopCutOff = focusedItemTop < viewportTop;
|
|
@@ -232,18 +220,15 @@ export function MenuListVirtualized<
|
|
|
232
220
|
}, [focusedOption, focusedIndex, initialFocusedOption, itemHeight, menuHeight, scrollThreshold]);
|
|
233
221
|
|
|
234
222
|
return (
|
|
235
|
-
// Needed for correct display inside portals and etc.
|
|
236
223
|
<div style={getStyles("menuList", props) as CSSProperties}>
|
|
237
224
|
<Virtuoso
|
|
238
225
|
ref={virtuoso}
|
|
239
|
-
// in reality only HTMLDivElement | null will appear here. We do not use Virtuoso's useWindowScroll prop
|
|
240
226
|
scrollerRef={innerRef as (ref: HTMLElement | Window | null) => void}
|
|
241
227
|
style={isPhone && inPopover ? undefined : {height: menuHeight}}
|
|
242
228
|
fixedItemHeight={itemHeight}
|
|
243
229
|
data={flattenChildren}
|
|
244
230
|
totalCount={flattenChildren.length}
|
|
245
231
|
itemContent={drawListElement}
|
|
246
|
-
components={components}
|
|
247
232
|
/>
|
|
248
233
|
</div>
|
|
249
234
|
);
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import {css, cx} from "@linaria/core";
|
|
2
|
-
import {CSSObjectWithLabel, MenuProps
|
|
2
|
+
import {components, CSSObjectWithLabel, MenuProps} from "react-select";
|
|
3
|
+
import type {MenuPortalProps} from "react-select/dist/declarations/src/components/Menu";
|
|
3
4
|
import {border, space, themeVars} from "../../design-system";
|
|
5
|
+
import {ThemeProvider} from "../../theme-provider";
|
|
4
6
|
import {GroupBase} from "../index";
|
|
5
7
|
|
|
6
8
|
export const MenuListNulledStyles: CSSObjectWithLabel = {
|
|
@@ -20,7 +22,7 @@ export const MenuNulledStyles: CSSObjectWithLabel = {
|
|
|
20
22
|
const menuCss = css`
|
|
21
23
|
color: ${themeVars.disabledTextColor};
|
|
22
24
|
z-index: 1050;
|
|
23
|
-
background-color: ${themeVars.
|
|
25
|
+
background-color: ${themeVars.colorBgPopup};
|
|
24
26
|
box-shadow: ${themeVars.shadowSelectMenu};
|
|
25
27
|
border-radius: ${border.radius6}px;
|
|
26
28
|
overflow: hidden;
|
|
@@ -38,3 +40,11 @@ const menuTransparentCss = css`
|
|
|
38
40
|
export function MenuTransparent<T, U extends boolean, V extends GroupBase<T>>(props: MenuProps<T, U, V>) {
|
|
39
41
|
return <components.Menu {...props} className={cx(menuTransparentCss, "react-select-menu", props.className)} />;
|
|
40
42
|
}
|
|
43
|
+
|
|
44
|
+
export function MenuPortal<T, U extends boolean, V extends GroupBase<T>>(props: MenuPortalProps<T, U, V>) {
|
|
45
|
+
return (
|
|
46
|
+
<components.MenuPortal {...props}>
|
|
47
|
+
<ThemeProvider portal>{props.children}</ThemeProvider>
|
|
48
|
+
</components.MenuPortal>
|
|
49
|
+
);
|
|
50
|
+
}
|
package/src/select/select.tsx
CHANGED
|
@@ -25,7 +25,7 @@ import BaseCreatableSelect, {CreatableProps} from "react-select/creatable";
|
|
|
25
25
|
import {ClearIndicator, NoClearIndicator} from "./components/clear-indicator";
|
|
26
26
|
import {DropdownIndicator} from "./components/drop-down-indicator";
|
|
27
27
|
import {GroupHeading} from "./components/group-heading";
|
|
28
|
-
import {Menu} from "./components/menu";
|
|
28
|
+
import {Menu, MenuPortal} from "./components/menu";
|
|
29
29
|
import {MenuListVirtualized} from "./components/menu-list-virtualized";
|
|
30
30
|
import {NoOptionsMessage} from "./components/no-option-message";
|
|
31
31
|
import {Option, OptionSlow} from "./components/option";
|
|
@@ -94,6 +94,7 @@ function useSelectComponents<T, U extends boolean, V extends GroupBase<T>>({
|
|
|
94
94
|
|
|
95
95
|
return {
|
|
96
96
|
Menu,
|
|
97
|
+
MenuPortal,
|
|
97
98
|
DropdownIndicator,
|
|
98
99
|
ClearIndicator,
|
|
99
100
|
MultiValueRemove: shouldHideMultiValueRemove ? NoClearIndicator : ClearIndicator,
|
package/src/select/styles.ts
CHANGED
|
@@ -190,7 +190,6 @@ export const makeSelectInPopoverStyles = <T, U extends boolean, V extends GroupB
|
|
|
190
190
|
margin: space.s2,
|
|
191
191
|
marginBottom: space.s4,
|
|
192
192
|
width: `calc(100% - ${space.s2 * 2}px)`,
|
|
193
|
-
borderRadius: border.radius4,
|
|
194
193
|
}),
|
|
195
194
|
menuList: (props) => {
|
|
196
195
|
if (isPhone) {
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import {makeDefaultPalette, type ThemePalette} from "./palette-generator";
|
|
2
|
+
|
|
3
|
+
export interface LabPalettes {
|
|
4
|
+
make: () => ThemePalette;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export const StaticPalettes: LabPalettes[] = [
|
|
8
|
+
{
|
|
9
|
+
make: () => {
|
|
10
|
+
const base = makeDefaultPalette();
|
|
11
|
+
return {
|
|
12
|
+
...base,
|
|
13
|
+
key: "vivid1",
|
|
14
|
+
base: [
|
|
15
|
+
"#fff",
|
|
16
|
+
"#ffe6f7",
|
|
17
|
+
"#ffccef",
|
|
18
|
+
"#ffb3e7",
|
|
19
|
+
"#ff99df",
|
|
20
|
+
"#ff80d7",
|
|
21
|
+
"#ff66cf",
|
|
22
|
+
"#ff4dc7",
|
|
23
|
+
"#ff33bf",
|
|
24
|
+
"#ff00aa",
|
|
25
|
+
"#e6009a",
|
|
26
|
+
"#cc0088",
|
|
27
|
+
"#990066",
|
|
28
|
+
],
|
|
29
|
+
baseDark: [
|
|
30
|
+
"#1a0011",
|
|
31
|
+
"#330022",
|
|
32
|
+
"#4d0033",
|
|
33
|
+
"#660044",
|
|
34
|
+
"#800055",
|
|
35
|
+
"#990066",
|
|
36
|
+
"#b30077",
|
|
37
|
+
"#cc0088",
|
|
38
|
+
"#e6009a",
|
|
39
|
+
"#ff00aa",
|
|
40
|
+
"#ff33bf",
|
|
41
|
+
"#ff66cf",
|
|
42
|
+
"#ffccef",
|
|
43
|
+
],
|
|
44
|
+
accent: [
|
|
45
|
+
"#fff",
|
|
46
|
+
"#e6ffff",
|
|
47
|
+
"#ccffff",
|
|
48
|
+
"#b3ffff",
|
|
49
|
+
"#99ffff",
|
|
50
|
+
"#80ffff",
|
|
51
|
+
"#66ffff",
|
|
52
|
+
"#4dffff",
|
|
53
|
+
"#33ffff",
|
|
54
|
+
"#00ffff",
|
|
55
|
+
"#00e6e6",
|
|
56
|
+
"#00cccc",
|
|
57
|
+
"#009999",
|
|
58
|
+
],
|
|
59
|
+
accentDark: [
|
|
60
|
+
"#001a1a",
|
|
61
|
+
"#003333",
|
|
62
|
+
"#004d4d",
|
|
63
|
+
"#006666",
|
|
64
|
+
"#008080",
|
|
65
|
+
"#009999",
|
|
66
|
+
"#00b3b3",
|
|
67
|
+
"#00cccc",
|
|
68
|
+
"#00e6e6",
|
|
69
|
+
"#00ffff",
|
|
70
|
+
"#33ffff",
|
|
71
|
+
"#66ffff",
|
|
72
|
+
"#ccffff",
|
|
73
|
+
],
|
|
74
|
+
};
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
make: () => {
|
|
79
|
+
const base = makeDefaultPalette();
|
|
80
|
+
return {
|
|
81
|
+
...base,
|
|
82
|
+
key: "vivid2",
|
|
83
|
+
base: [
|
|
84
|
+
"#FFFFFF",
|
|
85
|
+
"#e6ffe6",
|
|
86
|
+
"#ccffcc",
|
|
87
|
+
"#99FF99",
|
|
88
|
+
"#66ff66",
|
|
89
|
+
"#33ff33",
|
|
90
|
+
"#00ff00",
|
|
91
|
+
"#00e600",
|
|
92
|
+
"#00CC00",
|
|
93
|
+
"#00b300",
|
|
94
|
+
"#009900",
|
|
95
|
+
"#007700",
|
|
96
|
+
"#005500",
|
|
97
|
+
],
|
|
98
|
+
baseDark: [
|
|
99
|
+
"#001a00",
|
|
100
|
+
"#003300",
|
|
101
|
+
"#004d00",
|
|
102
|
+
"#006600",
|
|
103
|
+
"#008000",
|
|
104
|
+
"#009900",
|
|
105
|
+
"#00b300",
|
|
106
|
+
"#00cc00",
|
|
107
|
+
"#00e600",
|
|
108
|
+
"#00ff00",
|
|
109
|
+
"#33ff33",
|
|
110
|
+
"#66ff66",
|
|
111
|
+
"#ccffcc",
|
|
112
|
+
],
|
|
113
|
+
accent: [
|
|
114
|
+
"#fff",
|
|
115
|
+
"#fff5e6",
|
|
116
|
+
"#ffebcc",
|
|
117
|
+
"#ffd699",
|
|
118
|
+
"#ffc266",
|
|
119
|
+
"#ffad33",
|
|
120
|
+
"#ff9900",
|
|
121
|
+
"#e68a00",
|
|
122
|
+
"#cc7a00",
|
|
123
|
+
"#b36b00",
|
|
124
|
+
"#995c00",
|
|
125
|
+
"#804d00",
|
|
126
|
+
"#663d00",
|
|
127
|
+
],
|
|
128
|
+
accentDark: [
|
|
129
|
+
"#1a1200",
|
|
130
|
+
"#332400",
|
|
131
|
+
"#4d3600",
|
|
132
|
+
"#664800",
|
|
133
|
+
"#805a00",
|
|
134
|
+
"#996b00",
|
|
135
|
+
"#b37d00",
|
|
136
|
+
"#cc8f00",
|
|
137
|
+
"#e6a000",
|
|
138
|
+
"#ff9900",
|
|
139
|
+
"#ffad33",
|
|
140
|
+
"#ffc266",
|
|
141
|
+
"#ffebcc",
|
|
142
|
+
],
|
|
143
|
+
};
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
];
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import {css} from "@linaria/core";
|
|
2
|
+
import {useCallback, useEffect, useRef, useState} from "react";
|
|
3
|
+
// @ts-expect-error - no types
|
|
4
|
+
import {ColorWrap, EditableInput, Hue, Saturation} from "react-color/lib/components/common";
|
|
5
|
+
import {Popover} from "./popover";
|
|
6
|
+
import {getAlpha, produceColor, setAlpha} from "./color-utils";
|
|
7
|
+
import {border, space} from "./design-system";
|
|
8
|
+
|
|
9
|
+
const colorPickerPopupCss = css`
|
|
10
|
+
padding: ${space.s8}px;
|
|
11
|
+
width: 200px;
|
|
12
|
+
`;
|
|
13
|
+
|
|
14
|
+
const saturationWrapperCss = css`
|
|
15
|
+
position: relative;
|
|
16
|
+
height: 120px;
|
|
17
|
+
`;
|
|
18
|
+
|
|
19
|
+
const hueWrapperCss = css`
|
|
20
|
+
height: 12px;
|
|
21
|
+
position: relative;
|
|
22
|
+
margin-top: ${space.s8}px;
|
|
23
|
+
`;
|
|
24
|
+
|
|
25
|
+
const hexInputWrapperCss = css`
|
|
26
|
+
margin-top: ${space.s8}px;
|
|
27
|
+
`;
|
|
28
|
+
|
|
29
|
+
const hexInputStyle = {
|
|
30
|
+
input: {
|
|
31
|
+
width: "100%",
|
|
32
|
+
padding: `${space.s4}px ${space.s8}px`,
|
|
33
|
+
border: `1px solid var(--fibery-color-separator-color)`,
|
|
34
|
+
borderRadius: border.radius4,
|
|
35
|
+
background: "var(--fibery-color-bg-popup)",
|
|
36
|
+
color: "var(--fibery-color-text-color)",
|
|
37
|
+
fontSize: 11,
|
|
38
|
+
fontFamily: "monospace",
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const colorPickerPopoverCss = css`
|
|
43
|
+
z-index: 999999;
|
|
44
|
+
`;
|
|
45
|
+
|
|
46
|
+
const SliderPointer = () => (
|
|
47
|
+
<div
|
|
48
|
+
className={css`
|
|
49
|
+
width: 12px;
|
|
50
|
+
height: 12px;
|
|
51
|
+
border-radius: 50%;
|
|
52
|
+
background: white;
|
|
53
|
+
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
|
|
54
|
+
transform: translate(-50%, -50%);
|
|
55
|
+
`}
|
|
56
|
+
/>
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
const alphaSliderCss = css`
|
|
60
|
+
height: 12px;
|
|
61
|
+
position: relative;
|
|
62
|
+
margin-top: ${space.s8}px;
|
|
63
|
+
background: linear-gradient(to right, transparent, var(--alpha-color));
|
|
64
|
+
border-radius: ${border.radius4}px;
|
|
65
|
+
`;
|
|
66
|
+
|
|
67
|
+
const alphaTrackCss = css`
|
|
68
|
+
position: absolute;
|
|
69
|
+
inset: 0;
|
|
70
|
+
background: repeating-conic-gradient(#ccc 0% 25%, #fff 0% 50%) 50% / 8px 8px;
|
|
71
|
+
border-radius: ${border.radius4}px;
|
|
72
|
+
z-index: -1;
|
|
73
|
+
`;
|
|
74
|
+
|
|
75
|
+
type ColorPickerInnerProps = {
|
|
76
|
+
hex: string;
|
|
77
|
+
onChange: (color: {hex: string}) => void;
|
|
78
|
+
hsl: {h: number; s: number; l: number};
|
|
79
|
+
hsv: {h: number; s: number; v: number};
|
|
80
|
+
originalColor: string;
|
|
81
|
+
onColorWithAlpha: (color: string) => void;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const ColorPickerInner = ColorWrap(
|
|
85
|
+
({hex, onChange, hsl, hsv, originalColor, onColorWithAlpha}: ColorPickerInnerProps) => {
|
|
86
|
+
const alpha = getAlpha(originalColor);
|
|
87
|
+
|
|
88
|
+
const handleAlphaChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
89
|
+
const newAlpha = parseFloat(e.target.value);
|
|
90
|
+
const newColor = produceColor(hex, (c) => setAlpha(c, newAlpha));
|
|
91
|
+
onColorWithAlpha(newColor);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
<div className={colorPickerPopupCss}>
|
|
96
|
+
<div className={saturationWrapperCss}>
|
|
97
|
+
<Saturation hsl={hsl} hsv={hsv} onChange={onChange} pointer={SliderPointer} />
|
|
98
|
+
</div>
|
|
99
|
+
<div className={hueWrapperCss}>
|
|
100
|
+
<Hue hsl={hsl} onChange={onChange} pointer={SliderPointer} />
|
|
101
|
+
</div>
|
|
102
|
+
<div className={alphaSliderCss} style={{"--alpha-color": hex} as React.CSSProperties}>
|
|
103
|
+
<div className={alphaTrackCss} />
|
|
104
|
+
<input
|
|
105
|
+
type="range"
|
|
106
|
+
min={0}
|
|
107
|
+
max={1}
|
|
108
|
+
step={0.01}
|
|
109
|
+
value={alpha}
|
|
110
|
+
onChange={handleAlphaChange}
|
|
111
|
+
className={css`
|
|
112
|
+
width: 100%;
|
|
113
|
+
height: 100%;
|
|
114
|
+
opacity: 0;
|
|
115
|
+
cursor: pointer;
|
|
116
|
+
`}
|
|
117
|
+
/>
|
|
118
|
+
<div
|
|
119
|
+
className={css`
|
|
120
|
+
position: absolute;
|
|
121
|
+
top: 50%;
|
|
122
|
+
transform: translate(-50%, -50%);
|
|
123
|
+
pointer-events: none;
|
|
124
|
+
`}
|
|
125
|
+
style={{left: `${alpha * 100}%`}}
|
|
126
|
+
>
|
|
127
|
+
<SliderPointer />
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
<div className={hexInputWrapperCss}>
|
|
131
|
+
<EditableInput
|
|
132
|
+
style={hexInputStyle}
|
|
133
|
+
value={alpha < 1 ? `${hex} ${Math.round(alpha * 100)}%` : hex}
|
|
134
|
+
onChange={(value: string | {hex: string}) => {
|
|
135
|
+
const input = typeof value === "string" ? value : value.hex;
|
|
136
|
+
const match = input.match(/^(#?[0-9a-fA-F]{6})\s*(\d+%)?$/);
|
|
137
|
+
if (match) {
|
|
138
|
+
const newHex = match[1].startsWith("#") ? match[1] : `#${match[1]}`;
|
|
139
|
+
const newAlpha = match[2] ? parseInt(match[2]) / 100 : alpha;
|
|
140
|
+
const newColor = produceColor(newHex, (c) => setAlpha(c, newAlpha));
|
|
141
|
+
onColorWithAlpha(newColor);
|
|
142
|
+
}
|
|
143
|
+
}}
|
|
144
|
+
/>
|
|
145
|
+
</div>
|
|
146
|
+
</div>
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
const CHECKERBOARD = "repeating-conic-gradient(#ccc 0% 25%, #fff 0% 50%) 50% / 8px 8px";
|
|
152
|
+
|
|
153
|
+
function swatchStyle(color: string): React.CSSProperties {
|
|
154
|
+
const hasAlpha = getAlpha(color) < 1;
|
|
155
|
+
return hasAlpha ? {background: `linear-gradient(${color}, ${color}), ${CHECKERBOARD}`} : {background: color};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export const swatchCss = css`
|
|
159
|
+
width: 20px;
|
|
160
|
+
height: 20px;
|
|
161
|
+
border-radius: 4px;
|
|
162
|
+
border: 1px solid var(--fibery-color-separator-color);
|
|
163
|
+
`;
|
|
164
|
+
|
|
165
|
+
export const swatchClickableCss = css`
|
|
166
|
+
cursor: pointer;
|
|
167
|
+
&:hover {
|
|
168
|
+
transform: scale(1.15);
|
|
169
|
+
}
|
|
170
|
+
`;
|
|
171
|
+
|
|
172
|
+
export function SwatchWithPicker({
|
|
173
|
+
color,
|
|
174
|
+
title,
|
|
175
|
+
onChange,
|
|
176
|
+
onHighlight,
|
|
177
|
+
onHighlightEnd,
|
|
178
|
+
isAltPressed,
|
|
179
|
+
}: {
|
|
180
|
+
color: string;
|
|
181
|
+
title: string;
|
|
182
|
+
onChange?: (color: string) => void;
|
|
183
|
+
onHighlight?: () => void;
|
|
184
|
+
onHighlightEnd?: () => void;
|
|
185
|
+
isAltPressed?: boolean;
|
|
186
|
+
}) {
|
|
187
|
+
const [open, setOpen] = useState(false);
|
|
188
|
+
const [triggerEl, setTriggerEl] = useState<HTMLDivElement | null>(null);
|
|
189
|
+
|
|
190
|
+
const handleClose = useCallback(() => setOpen(false), []);
|
|
191
|
+
|
|
192
|
+
const handleColorChange = useCallback(
|
|
193
|
+
(newColor: {hex: string}) => {
|
|
194
|
+
const alpha = getAlpha(color);
|
|
195
|
+
const newColorWithAlpha = produceColor(newColor.hex, (c) => setAlpha(c, alpha));
|
|
196
|
+
onChange?.(newColorWithAlpha);
|
|
197
|
+
},
|
|
198
|
+
[onChange, color]
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
const handleColorWithAlpha = useCallback(
|
|
202
|
+
(newColor: string) => {
|
|
203
|
+
onChange?.(newColor);
|
|
204
|
+
},
|
|
205
|
+
[onChange]
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
const handleClick = useCallback(() => {
|
|
209
|
+
if (onChange) {
|
|
210
|
+
setOpen(true);
|
|
211
|
+
}
|
|
212
|
+
}, [onChange]);
|
|
213
|
+
|
|
214
|
+
const hoveredRef = useRef(false);
|
|
215
|
+
const wasHighlightingRef = useRef(false);
|
|
216
|
+
|
|
217
|
+
useEffect(() => {
|
|
218
|
+
const shouldHighlight = hoveredRef.current && isAltPressed;
|
|
219
|
+
if (shouldHighlight) {
|
|
220
|
+
onHighlight?.();
|
|
221
|
+
wasHighlightingRef.current = true;
|
|
222
|
+
} else if (wasHighlightingRef.current) {
|
|
223
|
+
onHighlightEnd?.();
|
|
224
|
+
wasHighlightingRef.current = false;
|
|
225
|
+
}
|
|
226
|
+
}, [isAltPressed, onHighlight, onHighlightEnd]);
|
|
227
|
+
|
|
228
|
+
return (
|
|
229
|
+
<>
|
|
230
|
+
<div
|
|
231
|
+
ref={setTriggerEl}
|
|
232
|
+
className={`${swatchCss} ${onChange ? swatchClickableCss : ""}`}
|
|
233
|
+
style={{...swatchStyle(color), ...(isAltPressed && onHighlight ? {cursor: "crosshair"} : undefined)}}
|
|
234
|
+
title={title}
|
|
235
|
+
onClick={handleClick}
|
|
236
|
+
onMouseEnter={() => {
|
|
237
|
+
hoveredRef.current = true;
|
|
238
|
+
if (isAltPressed) {
|
|
239
|
+
onHighlight?.();
|
|
240
|
+
wasHighlightingRef.current = true;
|
|
241
|
+
}
|
|
242
|
+
}}
|
|
243
|
+
onMouseLeave={() => {
|
|
244
|
+
hoveredRef.current = false;
|
|
245
|
+
if (wasHighlightingRef.current) {
|
|
246
|
+
onHighlightEnd?.();
|
|
247
|
+
wasHighlightingRef.current = false;
|
|
248
|
+
}
|
|
249
|
+
}}
|
|
250
|
+
/>
|
|
251
|
+
<Popover.Root open={open} onClose={handleClose}>
|
|
252
|
+
<Popover.Trigger reference={triggerEl} />
|
|
253
|
+
<Popover.Portal>
|
|
254
|
+
<Popover.Content placement="top" offset={[0, 4]} popupContainerClassName={colorPickerPopoverCss}>
|
|
255
|
+
<ColorPickerInner
|
|
256
|
+
color={color}
|
|
257
|
+
onChange={handleColorChange}
|
|
258
|
+
originalColor={color}
|
|
259
|
+
onColorWithAlpha={handleColorWithAlpha}
|
|
260
|
+
/>
|
|
261
|
+
</Popover.Content>
|
|
262
|
+
</Popover.Portal>
|
|
263
|
+
</Popover.Root>
|
|
264
|
+
</>
|
|
265
|
+
);
|
|
266
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import {type ThematicPrefs} from "./palette-generator";
|
|
2
|
+
import type {ThemePreference} from "./theme-settings";
|
|
3
|
+
import AppearanceLight from "./icons/react/AppearanceLight";
|
|
4
|
+
import AppearanceDark from "./icons/react/AppearanceDark";
|
|
5
|
+
import AppearanceAuto from "./icons/react/AppearanceAuto";
|
|
6
|
+
|
|
7
|
+
export const defaultPrefs: ThematicPrefs = {
|
|
8
|
+
baseHue: 0,
|
|
9
|
+
baseChroma: 0,
|
|
10
|
+
accentHue: 264,
|
|
11
|
+
accentChroma: 0.16,
|
|
12
|
+
contrast: 1,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const HIGHLIGHT_COLOR = "#ff00ff";
|
|
16
|
+
|
|
17
|
+
export const themeIcons: Record<ThemePreference, React.ReactNode> = {
|
|
18
|
+
light: <AppearanceLight />,
|
|
19
|
+
dark: <AppearanceDark />,
|
|
20
|
+
auto: <AppearanceAuto />,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const nextThemePref: Record<ThemePreference, ThemePreference> = {
|
|
24
|
+
light: "dark",
|
|
25
|
+
dark: "light",
|
|
26
|
+
auto: "light",
|
|
27
|
+
};
|