@fibery/ui-kit 1.40.3 → 1.41.0

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 (174) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/eslint.config.mjs +16 -0
  3. package/package.json +11 -13
  4. package/src/a11y-color.test.ts +162 -0
  5. package/src/a11y-color.ts +13 -20
  6. package/src/actions-menu/actions-menu-item.tsx +9 -12
  7. package/src/actions-menu/context-actions-menu.tsx +8 -3
  8. package/src/ai/model.tsx +0 -1
  9. package/src/ai/temperature.tsx +0 -1
  10. package/src/animated-height-container.tsx +3 -3
  11. package/src/antd/ant-modal.tsx +10 -5
  12. package/src/antd/index.tsx +3 -0
  13. package/src/antd/input.tsx +0 -1
  14. package/src/antd/styles.ts +15 -15
  15. package/src/app-icon-with-fallback.tsx +6 -6
  16. package/src/app-icon-wrapper.tsx +2 -3
  17. package/src/app-icon.tsx +4 -4
  18. package/src/avatar.tsx +3 -3
  19. package/src/button/base-button.tsx +0 -1
  20. package/src/button/button.tsx +1 -0
  21. package/src/button/make-button-colors.ts +5 -13
  22. package/src/checkbox.tsx +2 -2
  23. package/src/collapsible-section.tsx +11 -1
  24. package/src/color-picker/ColorPickerOrLoader.js +2 -2
  25. package/src/color-utils.test.ts +307 -0
  26. package/src/color-utils.ts +215 -0
  27. package/src/command-menu/index.tsx +1 -0
  28. package/src/comment.tsx +3 -2
  29. package/src/context-menu/index.tsx +12 -7
  30. package/src/date-picker/contexts.ts +2 -2
  31. package/src/date-picker/date-range-picker.tsx +3 -4
  32. package/src/date-picker/single-date-picker.tsx +147 -119
  33. package/src/date-picker/types.ts +4 -3
  34. package/src/day-select/iso-week-day-select.tsx +2 -2
  35. package/src/day-select/week-day-select.tsx +2 -2
  36. package/src/delayed.tsx +0 -1
  37. package/src/design-system/alpha.test.ts +59 -0
  38. package/src/design-system/alpha.ts +41 -0
  39. package/src/design-system/animation.ts +10 -0
  40. package/src/design-system/colors-css.test.ts +39 -0
  41. package/src/design-system/colors-css.ts +25 -0
  42. package/src/design-system/colors-js.test.ts +232 -0
  43. package/src/design-system/colors-js.ts +107 -0
  44. package/src/design-system/colors.test.ts +74 -0
  45. package/src/design-system/colors.ts +759 -0
  46. package/src/design-system/date.ts +3 -0
  47. package/src/design-system/layout.ts +106 -0
  48. package/src/design-system/theme.test.ts +94 -0
  49. package/src/design-system/theme.ts +93 -0
  50. package/src/design-system/typography.ts +179 -0
  51. package/src/design-system/vars.test.ts +1679 -0
  52. package/src/design-system/vars.ts +52 -0
  53. package/src/design-system.test.ts +45 -22
  54. package/src/design-system.ts +25 -1339
  55. package/src/dropdown-menu/index.tsx +21 -16
  56. package/src/emoji-picker/app-icon-picker.tsx +5 -5
  57. package/src/emoji-picker/emoji-picker-content-with-color.tsx +4 -4
  58. package/src/emoji-picker/icon-emoji-picker.tsx +3 -3
  59. package/src/favorites-icon.tsx +1 -1
  60. package/src/fibermoji-placeholder.tsx +3 -3
  61. package/src/field-container.tsx +11 -2
  62. package/src/file-item/file-icon.tsx +169 -0
  63. package/src/file-item/file-menu-items.tsx +68 -0
  64. package/src/file-item/file-preview-actions.tsx +38 -0
  65. package/src/file-item/file-title.tsx +48 -0
  66. package/src/file-item/types.ts +27 -0
  67. package/src/file-item/use-register-in-image-gallery.tsx +70 -0
  68. package/src/file-item-2.tsx +32 -348
  69. package/src/file-item.tsx +6 -3
  70. package/src/hue-shift.test.ts +91 -0
  71. package/src/icons/ast/ChatBubble.ts +8 -0
  72. package/src/icons/ast/ChatFloat.ts +8 -0
  73. package/src/icons/ast/ChatSidebar.ts +8 -0
  74. package/src/icons/ast/FileCounter.ts +8 -0
  75. package/src/icons/ast/FileMultiple.ts +8 -0
  76. package/src/icons/ast/FileOther.ts +8 -0
  77. package/src/icons/ast/NoBorder.ts +8 -0
  78. package/src/icons/ast/NoFill.ts +8 -0
  79. package/src/icons/ast/ValueEdit.ts +8 -0
  80. package/src/icons/ast/index.tsx +9 -0
  81. package/src/icons/react/ChatBubble.tsx +13 -0
  82. package/src/icons/react/ChatFloat.tsx +13 -0
  83. package/src/icons/react/ChatSidebar.tsx +13 -0
  84. package/src/icons/react/FileCounter.tsx +13 -0
  85. package/src/icons/react/FileMultiple.tsx +13 -0
  86. package/src/icons/react/FileOther.tsx +13 -0
  87. package/src/icons/react/NoBorder.tsx +13 -0
  88. package/src/icons/react/NoFill.tsx +13 -0
  89. package/src/icons/react/ValueEdit.tsx +13 -0
  90. package/src/icons/react/index.tsx +9 -0
  91. package/src/icons/svg/chat-bubble.svg +4 -0
  92. package/src/icons/svg/chat-float.svg +4 -0
  93. package/src/icons/svg/chat-sidebar.svg +4 -0
  94. package/src/icons/svg/file-counter.svg +3 -0
  95. package/src/icons/svg/file-multiple.svg +3 -0
  96. package/src/icons/svg/file-other.svg +3 -0
  97. package/src/icons/svg/no-border.svg +17 -0
  98. package/src/icons/svg/no-fill.svg +4 -0
  99. package/src/icons/svg/value-edit.svg +3 -0
  100. package/src/images-gallery/images-gallery.tsx +8 -6
  101. package/src/images-gallery/slide-buttons.tsx +4 -11
  102. package/src/is-iOS.ts +0 -1
  103. package/src/is-in-popup.ts +2 -1
  104. package/src/lists/actions-menu-row-surface.tsx +7 -7
  105. package/src/loading-sausage.tsx +2 -2
  106. package/src/media-query-utils.ts +1 -2
  107. package/src/mobile-keyboard-aware-popup.tsx +6 -5
  108. package/src/modal-menu/modal-menu-content.tsx +2 -2
  109. package/src/number-input/decimal.js +9 -7
  110. package/src/number-input/{number-inline-input-with-autosize.tsx → number-input-inline-with-autosize.tsx} +20 -41
  111. package/src/number-input/number-input-inline.tsx +118 -0
  112. package/src/number-input/{index.test.js → number-input.test.tsx} +38 -43
  113. package/src/number-input/number-input.tsx +63 -121
  114. package/src/number-input/types.ts +19 -0
  115. package/src/number-input/utils.ts +61 -0
  116. package/src/online-users.tsx +2 -2
  117. package/src/palette-generator.test.ts +309 -0
  118. package/src/palette-generator.ts +160 -0
  119. package/src/palettes/_.ts +72 -0
  120. package/src/palettes/slate-arch.colors-snapshot.test.ts +1689 -0
  121. package/src/palettes/slate-arch.ts +188 -0
  122. package/src/palettes/slate-user.colors-snapshot.test.ts +1689 -0
  123. package/src/palettes/slate-user.ts +187 -0
  124. package/src/palettes/warm-arch.colors-snapshot.test.ts +1689 -0
  125. package/src/palettes/warm-arch.ts +222 -0
  126. package/src/palettes/warm-user.colors-snapshot.test.ts +1689 -0
  127. package/src/palettes/warm-user.ts +222 -0
  128. package/src/platform.ts +0 -3
  129. package/src/popover/get-element-ref.ts +28 -0
  130. package/src/popover/index.tsx +236 -339
  131. package/src/popover/{mobile-popup-context.tsx → mobile-popover-context.tsx} +7 -7
  132. package/src/popover/mobile-popover.tsx +169 -0
  133. package/src/popover/modifiers.tsx +2 -2
  134. package/src/popover/popup-stack-context.tsx +8 -9
  135. package/src/progress.tsx +2 -2
  136. package/src/reactions/reaction-button.tsx +12 -6
  137. package/src/root-theme-provider.test.tsx +411 -0
  138. package/src/scale-generator.ts +356 -0
  139. package/src/select/components/menu-list-virtualized.tsx +12 -25
  140. package/src/select/components/menu.tsx +12 -2
  141. package/src/select/index.tsx +6 -5
  142. package/src/select/select.tsx +38 -39
  143. package/src/select/styles.ts +0 -1
  144. package/src/select/util.ts +1 -1
  145. package/src/static-palettes.ts +356 -0
  146. package/src/thematic-color-picker.tsx +266 -0
  147. package/src/thematic-constants.tsx +27 -0
  148. package/src/thematic-controls.tsx +144 -0
  149. package/src/thematic-scales.tsx +122 -0
  150. package/src/thematic-state.ts +370 -0
  151. package/src/thematic.tsx +386 -0
  152. package/src/theme-provider.test.tsx +820 -0
  153. package/src/theme-provider.tsx +158 -92
  154. package/src/theme-settings.ts +67 -12
  155. package/src/theme-styles.ts +58 -6
  156. package/src/toast/toast-action.tsx +1 -1
  157. package/src/toast/toast.tsx +1 -2
  158. package/src/toggle-on-off.tsx +2 -2
  159. package/src/toggle.tsx +7 -8
  160. package/src/tooltip.tsx +14 -10
  161. package/src/type-badge.tsx +7 -14
  162. package/src/unit/styles.ts +2 -25
  163. package/src/unit/unit-with-tooltip.tsx +3 -2
  164. package/src/use-is-phone.tsx +7 -2
  165. package/src/use-long-press.tsx +2 -2
  166. package/src/use-on-screen-keyboard-data.tsx +2 -2
  167. package/src/with-data.tsx +4 -3
  168. package/src/workflow-progress-icon.tsx +2 -2
  169. package/.eslintignore +0 -3
  170. package/.eslintrc +0 -14
  171. package/src/__mocks__/createInlineTheme.js +0 -3
  172. package/src/create-inline-theme.ts +0 -66
  173. package/src/number-input/index.js +0 -191
  174. package/src/palette.ts +0 -237
@@ -1,5 +1,5 @@
1
1
  import {GroupBase as ReactSelectGroupBase, OptionsOrGroups, SelectInstance} from "react-select";
2
- import {isObject} from "lodash";
2
+ import isObject from "lodash/isObject";
3
3
  import {Children, createContext, ReactNode, RefObject} from "react";
4
4
 
5
5
  /**
@@ -0,0 +1,356 @@
1
+ import {makeDefaultPalette, selectPalette, type ThemePalette} from "./palette-generator";
2
+
3
+ export interface LabPalettes {
4
+ make: () => ThemePalette;
5
+ }
6
+
7
+ export const StaticPalettes: LabPalettes[] = [
8
+ // warm grays (hue ~50) + orange accent (hue 15)
9
+ {
10
+ make: () => {
11
+ const base = selectPalette({warmLook: true, isArchitect: false});
12
+ return {
13
+ ...base,
14
+ key: "clawd",
15
+ base: [
16
+ null,
17
+ "hsl(0, 0%, 100%)", // bg-000
18
+ "hsl(48, 33%, 97%)", // bg-100
19
+ "hsl(53, 29%, 95%)", // bg-200
20
+ "hsl(48, 25%, 92%)", // bg-300
21
+ "hsl(50, 21%, 89%)", // bg-400
22
+ "hsl(51, 17%, 85%)", // pictogram area
23
+ "hsl(51, 10%, 70%)",
24
+ "hsl(51, 6%, 57%)",
25
+ "hsl(51, 3%, 44%)", // text-500
26
+ "hsl(55, 3%, 34%)",
27
+ "hsl(60, 3%, 23%)", // text-200
28
+ "hsl(60, 3%, 8%)", // text-000
29
+ ],
30
+ baseDark: [
31
+ null,
32
+ "hsl(60, 3%, 8%)", // bg-300
33
+ "hsl(30, 3%, 12%)", // bg-200
34
+ "hsl(60, 3%, 15%)", // bg-100
35
+ "hsl(60, 2%, 18%)", // bg-000
36
+ "hsl(60, 3%, 23%)", // pictogram-200
37
+ "hsl(48, 3%, 29%)", // pictogram-100
38
+ "hsl(50, 4%, 37%)",
39
+ "hsl(49, 4%, 47%)",
40
+ "hsl(48, 5%, 59%)", // text-400
41
+ "hsl(50, 7%, 67%)",
42
+ "hsl(50, 9%, 74%)", // text-200
43
+ "hsl(48, 33%, 97%)", // text-000
44
+ ],
45
+ accent: [
46
+ null,
47
+ "hsl(15, 60%, 99%)",
48
+ "hsl(15, 85%, 97%)",
49
+ "hsl(15, 80%, 94%)",
50
+ "hsl(15, 75%, 91%)",
51
+ "hsl(15, 70%, 87%)",
52
+ "hsl(15, 66%, 82%)",
53
+ "hsl(15, 62%, 74%)",
54
+ "hsl(15, 60%, 67%)",
55
+ "hsl(15, 63%, 60%)", // accent-brand
56
+ "hsl(15, 57%, 53%)",
57
+ "hsl(15, 54%, 46%)",
58
+ "hsl(15, 50%, 18%)",
59
+ ],
60
+ accentDark: [
61
+ null,
62
+ "hsl(16, 22%, 10%)",
63
+ "hsl(16, 34%, 13%)",
64
+ "hsl(15, 42%, 18%)",
65
+ "hsl(15, 46%, 22%)",
66
+ "hsl(15, 48%, 26%)",
67
+ "hsl(15, 50%, 30%)",
68
+ "hsl(15, 53%, 38%)",
69
+ "hsl(15, 56%, 48%)",
70
+ "hsl(15, 63%, 60%)", // accent-brand
71
+ "hsl(15, 65%, 66%)",
72
+ "hsl(15, 72%, 78%)",
73
+ "hsl(15, 82%, 96%)",
74
+ ],
75
+ };
76
+ },
77
+ },
78
+ // Console theme — pure neutral blacks + teal accent (hue 158)
79
+ {
80
+ make: () => {
81
+ const base = selectPalette({warmLook: true, isArchitect: false});
82
+ return {
83
+ ...base,
84
+ key: "clawd-console",
85
+ base: [
86
+ null,
87
+ "hsl(0, 0%, 99%)",
88
+ "hsl(0, 0%, 97%)",
89
+ "hsl(0, 0%, 95%)",
90
+ "hsl(0, 0%, 92%)",
91
+ "hsl(0, 0%, 88%)",
92
+ "hsl(0, 0%, 83%)",
93
+ "hsl(0, 0%, 75%)",
94
+ "hsl(0, 0%, 65%)",
95
+ "hsl(0, 0%, 48%)",
96
+ "hsl(0, 0%, 35%)",
97
+ "hsl(0, 0%, 23%)",
98
+ "hsl(0, 0%, 6%)",
99
+ ],
100
+ baseDark: [
101
+ null,
102
+ "hsl(0, 0%, 6%)", // bg-000
103
+ "hsl(0, 0%, 10%)", // bg-100
104
+ "hsl(0, 0%, 14%)", // bg-200
105
+ "hsl(0, 0%, 17%)", // bg-300
106
+ "hsl(0, 0%, 19%)", // bg-400
107
+ "hsl(0, 0%, 23%)", // bg-500
108
+ "hsl(0, 0%, 33%)",
109
+ "hsl(0, 0%, 41%)",
110
+ "hsl(45, 1%, 48%)", // text-500
111
+ "hsl(50, 1%, 65%)", // text-400
112
+ "hsl(30, 1%, 72%)", // text-300
113
+ "hsl(60, 4%, 95%)", // text-100
114
+ ],
115
+ accent: [
116
+ null,
117
+ "hsl(158, 60%, 99%)",
118
+ "hsl(158, 70%, 96%)",
119
+ "hsl(158, 66%, 92%)",
120
+ "hsl(158, 62%, 87%)",
121
+ "hsl(158, 58%, 80%)",
122
+ "hsl(158, 55%, 72%)",
123
+ "hsl(158, 58%, 60%)",
124
+ "hsl(158, 64%, 46%)",
125
+ "hsl(158, 68%, 32%)", // accent-secondary-000
126
+ "hsl(158, 80%, 28%)", // accent-secondary-100
127
+ "hsl(158, 79%, 25%)", // accent-secondary-200
128
+ "hsl(160, 18%, 16%)", // accent-secondary-900
129
+ ],
130
+ accentDark: [
131
+ null,
132
+ "hsl(160, 18%, 8%)",
133
+ "hsl(158, 36%, 10%)",
134
+ "hsl(158, 52%, 13%)",
135
+ "hsl(158, 60%, 16%)",
136
+ "hsl(158, 66%, 19%)",
137
+ "hsl(158, 70%, 22%)",
138
+ "hsl(158, 76%, 26%)",
139
+ "hsl(158, 78%, 30%)",
140
+ "hsl(158, 68%, 32%)", // accent-secondary-000
141
+ "hsl(158, 60%, 42%)",
142
+ "hsl(158, 50%, 58%)",
143
+ "hsl(158, 45%, 92%)",
144
+ ],
145
+ };
146
+ },
147
+ },
148
+ // Console theme variant — neutral blacks + orange accent (hue 18, darker than claude's hue 15)
149
+ {
150
+ make: () => {
151
+ const base = selectPalette({warmLook: true, isArchitect: false});
152
+ return {
153
+ ...base,
154
+ key: "clawd-console-warm",
155
+ base: [
156
+ null,
157
+ "hsl(0, 0%, 99%)",
158
+ "hsl(0, 0%, 97%)",
159
+ "hsl(0, 0%, 95%)",
160
+ "hsl(0, 0%, 92%)",
161
+ "hsl(0, 0%, 88%)",
162
+ "hsl(0, 0%, 83%)",
163
+ "hsl(0, 0%, 75%)",
164
+ "hsl(0, 0%, 65%)",
165
+ "hsl(0, 0%, 48%)",
166
+ "hsl(0, 0%, 35%)",
167
+ "hsl(0, 0%, 23%)",
168
+ "hsl(0, 0%, 6%)",
169
+ ],
170
+ baseDark: [
171
+ null,
172
+ "hsl(0, 0%, 6%)", // bg-000
173
+ "hsl(0, 0%, 10%)", // bg-100
174
+ "hsl(0, 0%, 14%)", // bg-200
175
+ "hsl(0, 0%, 17%)", // bg-300
176
+ "hsl(0, 0%, 19%)", // bg-400
177
+ "hsl(0, 0%, 23%)", // bg-500
178
+ "hsl(0, 0%, 33%)",
179
+ "hsl(0, 0%, 41%)",
180
+ "hsl(45, 1%, 48%)", // text-500
181
+ "hsl(50, 1%, 65%)", // text-400
182
+ "hsl(30, 1%, 72%)", // text-300
183
+ "hsl(60, 4%, 95%)", // text-100
184
+ ],
185
+ accent: [
186
+ null,
187
+ "hsl(18, 55%, 99%)",
188
+ "hsl(18, 78%, 96%)",
189
+ "hsl(18, 72%, 92%)",
190
+ "hsl(18, 66%, 87%)",
191
+ "hsl(18, 60%, 81%)",
192
+ "hsl(18, 56%, 73%)",
193
+ "hsl(18, 54%, 63%)",
194
+ "hsl(18, 52%, 54%)",
195
+ "hsl(18, 50%, 48%)", // accent-main-000
196
+ "hsl(18, 57%, 44%)", // accent-main-100
197
+ "hsl(19, 58%, 40%)", // accent-main-200
198
+ "hsl(16, 22%, 20%)", // accent-main-900
199
+ ],
200
+ accentDark: [
201
+ null,
202
+ "hsl(16, 22%, 10%)",
203
+ "hsl(17, 30%, 14%)",
204
+ "hsl(17, 36%, 18%)",
205
+ "hsl(17, 40%, 22%)",
206
+ "hsl(18, 44%, 27%)",
207
+ "hsl(18, 48%, 32%)",
208
+ "hsl(18, 50%, 38%)",
209
+ "hsl(18, 53%, 44%)", // accent-main-100
210
+ "hsl(18, 50%, 48%)", // accent-main-000
211
+ "hsl(18, 54%, 56%)",
212
+ "hsl(18, 60%, 72%)",
213
+ "hsl(18, 72%, 96%)",
214
+ ],
215
+ };
216
+ },
217
+ },
218
+ {
219
+ make: () => {
220
+ const base = makeDefaultPalette();
221
+ return {
222
+ ...base,
223
+ key: "oops",
224
+ base: [
225
+ null,
226
+ "#ffe6f7",
227
+ "#ffccef",
228
+ "#ffb3e7",
229
+ "#ff99df",
230
+ "#ff80d7",
231
+ "#ff66cf",
232
+ "#ff4dc7",
233
+ "#ff33bf",
234
+ "#ff00aa",
235
+ "#e6009a",
236
+ "#cc0088",
237
+ "#990066",
238
+ ],
239
+ baseDark: [
240
+ null,
241
+ "#330022",
242
+ "#4d0033",
243
+ "#660044",
244
+ "#800055",
245
+ "#990066",
246
+ "#b30077",
247
+ "#cc0088",
248
+ "#e6009a",
249
+ "#ff00aa",
250
+ "#ff33bf",
251
+ "#ff66cf",
252
+ "#ffccef",
253
+ ],
254
+ accent: [
255
+ null,
256
+ "#e6ffff",
257
+ "#ccffff",
258
+ "#b3ffff",
259
+ "#99ffff",
260
+ "#80ffff",
261
+ "#66ffff",
262
+ "#4dffff",
263
+ "#33ffff",
264
+ "#00ffff",
265
+ "#00e6e6",
266
+ "#00cccc",
267
+ "#009999",
268
+ ],
269
+ accentDark: [
270
+ null,
271
+ "#003333",
272
+ "#004d4d",
273
+ "#006666",
274
+ "#008080",
275
+ "#009999",
276
+ "#00b3b3",
277
+ "#00cccc",
278
+ "#00e6e6",
279
+ "#00ffff",
280
+ "#33ffff",
281
+ "#66ffff",
282
+ "#ccffff",
283
+ ],
284
+ };
285
+ },
286
+ },
287
+ {
288
+ make: () => {
289
+ const base = makeDefaultPalette();
290
+ return {
291
+ ...base,
292
+ key: "shmoops",
293
+ base: [
294
+ null,
295
+ "#e6ffe6",
296
+ "#ccffcc",
297
+ "#99FF99",
298
+ "#66ff66",
299
+ "#33ff33",
300
+ "#00ff00",
301
+ "#00e600",
302
+ "#00CC00",
303
+ "#00b300",
304
+ "#009900",
305
+ "#007700",
306
+ "#005500",
307
+ ],
308
+ baseDark: [
309
+ null,
310
+ "#003300",
311
+ "#004d00",
312
+ "#006600",
313
+ "#008000",
314
+ "#009900",
315
+ "#00b300",
316
+ "#00cc00",
317
+ "#00e600",
318
+ "#00ff00",
319
+ "#33ff33",
320
+ "#66ff66",
321
+ "#ccffcc",
322
+ ],
323
+ accent: [
324
+ null,
325
+ "#fff5e6",
326
+ "#ffebcc",
327
+ "#ffd699",
328
+ "#ffc266",
329
+ "#ffad33",
330
+ "#ff9900",
331
+ "#e68a00",
332
+ "#cc7a00",
333
+ "#b36b00",
334
+ "#995c00",
335
+ "#804d00",
336
+ "#663d00",
337
+ ],
338
+ accentDark: [
339
+ null,
340
+ "#332400",
341
+ "#4d3600",
342
+ "#664800",
343
+ "#805a00",
344
+ "#996b00",
345
+ "#b37d00",
346
+ "#cc8f00",
347
+ "#e6a000",
348
+ "#ff9900",
349
+ "#ffad33",
350
+ "#ffc266",
351
+ "#ffebcc",
352
+ ],
353
+ };
354
+ },
355
+ },
356
+ ];
@@ -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
+ };