@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.
Files changed (82) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/package.json +4 -7
  3. package/src/__snapshots__/design-system.test.ts.snap +7265 -0
  4. package/src/a11y-color.test.ts +181 -0
  5. package/src/a11y-color.ts +13 -20
  6. package/src/actions-menu/actions-menu-item.tsx +7 -7
  7. package/src/antd/ant-modal.tsx +10 -5
  8. package/src/antd/styles.ts +6 -6
  9. package/src/app-icon-with-fallback.tsx +2 -2
  10. package/src/app-icon.tsx +2 -2
  11. package/src/button/button.tsx +1 -0
  12. package/src/button/make-button-colors.ts +5 -13
  13. package/src/checkbox.tsx +2 -2
  14. package/src/color-picker/ColorPickerOrLoader.js +2 -2
  15. package/src/color-utils.test.ts +317 -0
  16. package/src/color-utils.ts +180 -0
  17. package/src/comment.tsx +3 -2
  18. package/src/context-menu/index.tsx +12 -7
  19. package/src/create-inline-theme.ts +9 -8
  20. package/src/design-system.colors.ts +760 -0
  21. package/src/design-system.test.ts +287 -7
  22. package/src/design-system.ts +146 -940
  23. package/src/dropdown-menu/index.tsx +21 -16
  24. package/src/emoji-picker/app-icon-picker.tsx +5 -5
  25. package/src/emoji-picker/emoji-picker-content-with-color.tsx +4 -4
  26. package/src/emoji-picker/icon-emoji-picker.tsx +2 -2
  27. package/src/favorites-icon.tsx +1 -1
  28. package/src/file-item/file-icon.tsx +169 -0
  29. package/src/file-item/file-menu-items.tsx +68 -0
  30. package/src/file-item/file-preview-actions.tsx +38 -0
  31. package/src/file-item/file-title.tsx +48 -0
  32. package/src/file-item/types.ts +27 -0
  33. package/src/file-item/use-register-in-image-gallery.tsx +70 -0
  34. package/src/file-item-2.tsx +35 -345
  35. package/src/file-item.tsx +6 -2
  36. package/src/hue-shift.test.ts +91 -0
  37. package/src/icons/ast/FileCounter.ts +8 -0
  38. package/src/icons/ast/FileMultiple.ts +8 -0
  39. package/src/icons/ast/ValueEdit.ts +8 -0
  40. package/src/icons/ast/index.tsx +3 -0
  41. package/src/icons/react/FileCounter.tsx +13 -0
  42. package/src/icons/react/FileMultiple.tsx +13 -0
  43. package/src/icons/react/ValueEdit.tsx +13 -0
  44. package/src/icons/react/index.tsx +3 -0
  45. package/src/icons/svg/file-counter.svg +3 -0
  46. package/src/icons/svg/file-multiple.svg +3 -0
  47. package/src/icons/svg/value-edit.svg +3 -0
  48. package/src/images-gallery/slide-buttons.tsx +4 -11
  49. package/src/lists/actions-menu-row-surface.tsx +5 -5
  50. package/src/mobile-keyboard-aware-popup.tsx +4 -3
  51. package/src/palette-generator.test.ts +566 -0
  52. package/src/palette-generator.ts +166 -0
  53. package/src/palette.ts +71 -55
  54. package/src/platform.ts +0 -3
  55. package/src/popover/index.tsx +13 -15
  56. package/src/progress.tsx +2 -2
  57. package/src/reactions/reaction-button.tsx +12 -6
  58. package/src/root-theme-provider.test.tsx +316 -0
  59. package/src/scale-generator.ts +347 -0
  60. package/src/select/components/menu-list-virtualized.tsx +7 -22
  61. package/src/select/components/menu.tsx +12 -2
  62. package/src/select/select.tsx +2 -1
  63. package/src/select/styles.ts +0 -1
  64. package/src/static-palettes.ts +146 -0
  65. package/src/thematic-color-picker.tsx +266 -0
  66. package/src/thematic-constants.tsx +27 -0
  67. package/src/thematic-controls.tsx +144 -0
  68. package/src/thematic-scales.tsx +122 -0
  69. package/src/thematic-state.ts +333 -0
  70. package/src/thematic.tsx +382 -0
  71. package/src/theme-provider.test.tsx +808 -0
  72. package/src/theme-provider.tsx +132 -69
  73. package/src/theme-settings.ts +1 -1
  74. package/src/theme-styles.ts +12 -5
  75. package/src/toast/toast-action.tsx +1 -1
  76. package/src/toggle-on-off.tsx +2 -2
  77. package/src/toggle.tsx +5 -6
  78. package/src/tooltip.tsx +13 -10
  79. package/src/type-badge.tsx +3 -3
  80. package/src/unit/styles.ts +2 -2
  81. package/src/unit/unit-with-tooltip.tsx +3 -2
  82. 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 + PADDING_BLOCK_HEIGHT * 2, maxHeight),
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 + PADDING_BLOCK_HEIGHT;
179
+ const focusedItemTop = focusedIndex * itemHeight;
192
180
  const focusedItemBottom = focusedItemTop + itemHeight;
193
181
  const viewportTop = state.scrollTop;
194
- const viewportBottom = state.scrollTop + menuHeight - PADDING_BLOCK_HEIGHT;
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, components} from "react-select";
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.colorBgSelectMenu};
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
+ }
@@ -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,
@@ -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
+ };