@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
@@ -0,0 +1,215 @@
1
+ import type {Coords, PlainColorObject} from "colorjs.io/fn";
2
+ import {
3
+ ColorSpace,
4
+ contrastWCAG21,
5
+ deltaE76,
6
+ getLuminance as getLuminanceUnsafe,
7
+ HSL,
8
+ Lab,
9
+ LCH,
10
+ mix,
11
+ OKLCH,
12
+ P3,
13
+ parse,
14
+ serialize,
15
+ set,
16
+ sRGB,
17
+ to,
18
+ toGamut,
19
+ } from "colorjs.io/fn";
20
+
21
+ ColorSpace.register(sRGB);
22
+ ColorSpace.register(HSL);
23
+ ColorSpace.register(Lab);
24
+ ColorSpace.register(LCH);
25
+ ColorSpace.register(OKLCH);
26
+ ColorSpace.register(P3);
27
+
28
+ type Color = PlainColorObject;
29
+
30
+ // FIXME consolidate as common util
31
+ const isFlagPresent = (flag: string) => globalThis.window && new RegExp(`[?&]${flag}\\b`).test(window.location.search);
32
+
33
+ const defaultColorSpace = isFlagPresent("oklch") ? OKLCH : sRGB;
34
+
35
+ function colorSafe(
36
+ color: string,
37
+ fallback = "hsl(220, 62%, 56%)" /* formerly brandColor.blue, currently accent[9] */,
38
+ space: ColorSpace = defaultColorSpace
39
+ ): Color {
40
+ try {
41
+ return to(color, space);
42
+ } catch {
43
+ return to(fallback, space);
44
+ }
45
+ }
46
+
47
+ const coords = (a: number, b: number, c: number): Coords => [a, b, c];
48
+ const clamp01 = (v: number | null): number => Math.max(0, Math.min(1, v ?? 0));
49
+ const white: PlainColorObject = {space: sRGB, coords: [1, 1, 1], alpha: 1};
50
+ const black: PlainColorObject = {space: sRGB, coords: [0, 0, 0], alpha: 1};
51
+
52
+ function serializesRGB(color: Color): string {
53
+ const rgb = toGamut(to(color, sRGB), {space: sRGB});
54
+ const r = Math.round(clamp01(rgb.coords[0]) * 255);
55
+ const g = Math.round(clamp01(rgb.coords[1]) * 255);
56
+ const b = Math.round(clamp01(rgb.coords[2]) * 255);
57
+ const a = rgb.alpha ?? 1;
58
+ return a < 1 ? `rgba(${r},${g},${b},${a})` : `rgb(${r},${g},${b})`;
59
+ }
60
+
61
+ type ColorTransform = (color: PlainColorObject) => PlainColorObject | void;
62
+
63
+ export function produceColor(
64
+ color: string,
65
+ transforms: ColorTransform | ColorTransform[],
66
+ space: ColorSpace = defaultColorSpace
67
+ ): string {
68
+ let c = colorSafe(color);
69
+ for (const transform of Array.isArray(transforms) ? transforms : [transforms]) {
70
+ const result = transform(c);
71
+ if (result !== undefined) {
72
+ c = result;
73
+ }
74
+ }
75
+ // TODO make sRGB default space and apply colorjs.io outputs CSS Level 4 format (rgb(100% 0% 0%))
76
+ // instead of compatible format (rgb(255,0,0))
77
+ if (space === sRGB) {
78
+ return serializesRGB(c);
79
+ }
80
+ return serialize(to(c, space));
81
+ }
82
+
83
+ /**
84
+ * @deprecated chroma-js compatibility artifact, try using newer apis
85
+ * Set relative luminance by binary-search interpolation toward white/black in sRGB.
86
+ * This targets relative luminance (Y in XYZ), not perceptual lightness.
87
+ */
88
+ export function setLuminanceChroma(color: PlainColorObject, target: number): PlainColorObject {
89
+ const curLum = getLuminanceUnsafe(color);
90
+ if (Math.abs(curLum - target) < 1e-7) {
91
+ return color;
92
+ }
93
+
94
+ const endpoint = target > curLum ? white : black;
95
+ let lo = 0;
96
+ let hi = 1;
97
+
98
+ for (let i = 0; i < 12; i++) {
99
+ const mid = (lo + hi) / 2;
100
+ const lum = getLuminanceUnsafe(mix(color, endpoint, mid, {space: sRGB}));
101
+ if (target > curLum ? lum < target : lum > target) {
102
+ lo = mid;
103
+ } else {
104
+ hi = mid;
105
+ }
106
+ }
107
+
108
+ const result = mix(color, endpoint, (lo + hi) / 2, {space: sRGB});
109
+ result.alpha = color.alpha ?? 1;
110
+ return result;
111
+ }
112
+
113
+ /**
114
+ * @deprecated chroma-js compatibility artifact, try using newer apis
115
+ * Darken by reducing L* in Lab color space using colorjs.io set().
116
+ * Matches chroma-js: darken(1) reduces Lab L* by 18 units.
117
+ * Negative L* values are handled by toGamut during serialization.
118
+ */
119
+ export function darkenChroma(color: PlainColorObject, amount: number): PlainColorObject {
120
+ return set(color, "lab.l", (l) => l - amount * 18);
121
+ }
122
+
123
+ /**
124
+ * @deprecated chroma-js compatibility artifact, try using newer apis
125
+ * Desaturate by reducing C (chroma) in LCH color space using colorjs.io set().
126
+ * Matches chroma-js: desaturate(1) reduces LCH C by 18 units.
127
+ * Negative C values are handled by toGamut during serialization.
128
+ */
129
+ export function desaturateChroma(color: PlainColorObject, amount: number): PlainColorObject {
130
+ return set(color, "lch.c", (c) => c - amount * 18);
131
+ }
132
+
133
+ export function setAlpha(color: PlainColorObject, value: number): void {
134
+ color.alpha = value;
135
+ }
136
+
137
+ export function isValidColor(color: string): boolean {
138
+ if (color === "transparent") {
139
+ return true;
140
+ }
141
+ try {
142
+ parse(color);
143
+ return true;
144
+ } catch {
145
+ return false;
146
+ }
147
+ }
148
+
149
+ export function getAlpha(color: string): number {
150
+ return colorSafe(color).alpha ?? 1;
151
+ }
152
+
153
+ export function getContrastRatio(color1: string, color2: string): number {
154
+ return contrastWCAG21(colorSafe(color1), colorSafe(color2));
155
+ }
156
+
157
+ export function getColorDistance(color1: string, color2: string): number {
158
+ return deltaE76(colorSafe(color1), colorSafe(color2));
159
+ }
160
+
161
+ export function getLuminance(color: string): number {
162
+ return getLuminanceUnsafe(colorSafe(color));
163
+ }
164
+
165
+ export function fromHSL(h: number, s: number, l: number): string {
166
+ return serializesRGB({space: HSL, coords: coords(h, s * 100, l * 100), alpha: null});
167
+ }
168
+
169
+ export function fromOKLCH(L: number, C: number, H: number, alpha = 1): string {
170
+ return serializesRGB({space: OKLCH, coords: [L, C, H] as [number, number, number], alpha});
171
+ }
172
+
173
+ export function toOKLCH(color: string) {
174
+ return produceColor(color, [], OKLCH);
175
+ }
176
+
177
+ export function toRGB(color: string) {
178
+ return produceColor(color, [], sRGB);
179
+ }
180
+
181
+ export function getHsl(color: string): [number, number, number] {
182
+ const c = to(colorSafe(color), HSL);
183
+ return [c.coords[0] || 0, (c.coords[1] || 0) / 100, (c.coords[2] || 0) / 100];
184
+ }
185
+
186
+ /** @deprecated do not use that, chroma-js compatibility */
187
+ export function clampRgb(color: PlainColorObject): PlainColorObject {
188
+ const rgb = to(color, sRGB);
189
+ rgb.coords = rgb.coords.map(clamp01) as Coords;
190
+ return rgb;
191
+ }
192
+
193
+ export function toCss(color: string): string {
194
+ return produceColor(color, []);
195
+ }
196
+
197
+ /**
198
+ * @deprecated use toCss instead
199
+ */
200
+ export function toHex(color: string): string {
201
+ const c = toGamut(to(colorSafe(color), sRGB), {space: sRGB});
202
+ const r = Math.round(clamp01(c.coords[0]) * 255);
203
+ const g = Math.round(clamp01(c.coords[1]) * 255);
204
+ const b = Math.round(clamp01(c.coords[2]) * 255);
205
+ const a = c.alpha ?? 1;
206
+ const hex = "#" + [r, g, b].map((v) => v.toString(16).padStart(2, "0")).join("");
207
+ return a < 1
208
+ ? hex +
209
+ Math.round(a * 255)
210
+ .toString(16)
211
+ .padStart(2, "0")
212
+ : hex;
213
+ }
214
+
215
+ export {ColorSpace, mix, OKLCH, parse, set, sRGB, to, HSL, Lab, LCH};
@@ -124,6 +124,7 @@ export const commandItemCss = css`
124
124
  min-height: ${layoutVars.menuItemHeight};
125
125
  margin: 0 ${space.s4}px;
126
126
  border-radius: ${border.radius6}px;
127
+ color: ${themeVars.textColor};
127
128
 
128
129
  &[data-selected="true"] {
129
130
  margin: 0 4px;
package/src/comment.tsx CHANGED
@@ -6,7 +6,7 @@ import {border, iconSize, space, textStyles, themeVars, transition} from "./desi
6
6
  import {formatDateFromNow} from "./format-date-from-now";
7
7
  import {LoadingSausage, LoadingSausageList} from "./loading-sausage";
8
8
  import {Tooltip} from "./tooltip";
9
- import {isMobile} from "./platform";
9
+ import {useIsPhone} from "./use-is-phone";
10
10
 
11
11
  export const commentContentStyle = css`
12
12
  ${{
@@ -115,6 +115,7 @@ export const Comment = memo(
115
115
 
116
116
  const {formatted, relative} = formatDateFromNow(date, {format: dateTimeFormat});
117
117
  const IconComponent = icon ? icon : null;
118
+ const isPhone = useIsPhone();
118
119
  return (
119
120
  <div
120
121
  ref={ref}
@@ -153,7 +154,7 @@ export const Comment = memo(
153
154
  <div
154
155
  className={cx(
155
156
  actionsClassName,
156
- (actionsShowed || isMobile) &&
157
+ (actionsShowed || isPhone) &&
157
158
  css`
158
159
  display: flex;
159
160
  `
@@ -4,6 +4,7 @@ import * as ContextMenuPrimitive from "@radix-ui/react-context-menu";
4
4
  import {forwardRef} from "react";
5
5
  import {border, layout, space, textStyles, themeVars} from "../design-system";
6
6
  import {mobileRootSelector} from "../mobile-styles";
7
+ import {RadixPortalThemeProvider} from "../theme-provider";
7
8
 
8
9
  export const Root = (contextMenuProps: ContextMenuPrimitive.ContextMenuProps) => (
9
10
  <ContextMenuPrimitive.Root dir="ltr" {...contextMenuProps} />
@@ -94,12 +95,14 @@ export type ContentProps = ContextMenuPrimitive.ContextMenuContentProps;
94
95
  export const Content = forwardRef<HTMLDivElement, ContentProps>(function ContextMenuContent({className, ...rest}, ref) {
95
96
  return (
96
97
  <ContextMenuPrimitive.Portal>
97
- <ContextMenuPrimitive.Content
98
- ref={ref}
99
- className={cx(dropdownMenuContentStyles, className)}
100
- collisionPadding={space.s12 + space.s8}
101
- {...rest}
102
- />
98
+ <RadixPortalThemeProvider>
99
+ <ContextMenuPrimitive.Content
100
+ ref={ref}
101
+ className={cx(dropdownMenuContentStyles, className)}
102
+ collisionPadding={space.s12 + space.s8}
103
+ {...rest}
104
+ />
105
+ </RadixPortalThemeProvider>
103
106
  </ContextMenuPrimitive.Portal>
104
107
  );
105
108
  });
@@ -132,7 +135,9 @@ export const SubContent = forwardRef<HTMLDivElement, ContextMenuPrimitive.Contex
132
135
  function DropdownMenuSubContent({className, ...rest}, ref) {
133
136
  return (
134
137
  <ContextMenuPrimitive.Portal>
135
- <ContextMenuPrimitive.SubContent ref={ref} className={cx(dropdownMenuContentStyles, className)} {...rest} />
138
+ <RadixPortalThemeProvider>
139
+ <ContextMenuPrimitive.SubContent ref={ref} className={cx(dropdownMenuContentStyles, className)} {...rest} />
140
+ </RadixPortalThemeProvider>
136
141
  </ContextMenuPrimitive.Portal>
137
142
  );
138
143
  }
@@ -1,7 +1,7 @@
1
1
  import {createContext} from "react";
2
- import _ from "lodash";
2
+ import noop from "lodash/noop";
3
3
 
4
4
  export const StartEndFocusContext = createContext({
5
5
  focusOnStart: true,
6
- setFocusOnStart: _.noop,
6
+ setFocusOnStart: noop,
7
7
  });
@@ -7,7 +7,7 @@ import {css} from "@linaria/core";
7
7
  import "react-day-picker/dist/style.css";
8
8
  import {stopPropagation} from "@fibery/react/src/stop-propagation";
9
9
  import {forwardRef, useCallback, useRef} from "react";
10
- import _ from "lodash";
10
+ import omit from "lodash/omit";
11
11
  import moment from "moment";
12
12
  import {FakeDateInputWrapper} from "./fake-date-input";
13
13
  import {formatDateRangeInputValue} from "./utils";
@@ -27,7 +27,6 @@ export const DateRangePicker = forwardRef<
27
27
  bypassedRef,
28
28
  value,
29
29
  disabled = false,
30
- renderInPortal = true,
31
30
  onChange,
32
31
  showTime = false,
33
32
  dateFormat,
@@ -107,14 +106,14 @@ export const DateRangePicker = forwardRef<
107
106
  popupContainerClassName={css`
108
107
  z-index: 10001;
109
108
  `}
110
- renderInPortal={renderInPortal}
109
+ renderInPortal={true}
111
110
  preventOverflowOptions={{
112
111
  altAxis: true,
113
112
  }}
114
113
  >
115
114
  <DateRangePickerPopupContent
116
115
  ref={ref}
117
- {..._.omit(props, ["onChange", "value"])}
116
+ {...omit(props, ["onChange", "value"])}
118
117
  onChange={onDatesChange}
119
118
  value={dateValue}
120
119
  onKeyDown={onDatePickerKeyDown}
@@ -1,17 +1,22 @@
1
- import {ControlledDatePickerProps, RelativeDateValue, SingleDatePickerProps} from "./types";
1
+ import {
2
+ ControlledDatePickerProps,
3
+ RelativeDateValue,
4
+ SingleDatePickerPopoverContentProps,
5
+ SingleDatePickerProps,
6
+ } from "./types";
2
7
  import {FakeDateInputWrapper} from "./fake-date-input";
3
8
  import {space, themeVars} from "../design-system";
4
9
  import {withPopupControl} from "./with-popup-control";
5
- import {css} from "@linaria/core";
10
+ import {css, cx} from "@linaria/core";
6
11
  import "react-day-picker/dist/style.css";
7
12
  import {SingleDatePickerPopupContent} from "./single-date-picker-popup-content";
8
13
  import {stopPropagation} from "@fibery/react/src/stop-propagation";
9
14
  import {forwardRef, useCallback, useRef, useState} from "react";
10
15
  import moment, {Moment} from "moment";
11
- import _ from "lodash";
16
+ import omit from "lodash/omit";
12
17
  import {EmptyValue} from "../empty-value";
13
18
  import {formatDateInputValue} from "./utils";
14
- import {PopoverOld} from "../popover";
19
+ import {Popover} from "../popover";
15
20
  import {focusable} from "tabbable";
16
21
  import {TabNavLink, TabNavList, TabNavRoot} from "../tab-nav/tab-nav";
17
22
  import {RelativeDatePicker} from "./relative-date-picker";
@@ -69,43 +74,18 @@ interface ControlledSingleDatePickerWithForwardRefType extends React.FC<Uncontro
69
74
  >;
70
75
  }
71
76
 
72
- export const SingleDatePicker: SingleDatePickerWithForwardRefType = forwardRef<
73
- {
74
- setValue: (value: string) => void;
75
- },
76
- SingleDatePickerProps<boolean>
77
+ type DatePickerRef = {
78
+ setValue: (value: string) => void;
79
+ };
80
+
81
+ export const SingleDatePickerPopoverContent = forwardRef<
82
+ DatePickerRef,
83
+ {className?: string} & SingleDatePickerPopoverContentProps<boolean>
77
84
  >((props, ref) => {
78
- const {
79
- title,
80
- open,
81
- onOpen,
82
- onClose,
83
- children,
84
- bypassedRef,
85
- value,
86
- disabled = false,
87
- placeholder,
88
- renderInPortal = true,
89
- onChange,
90
- showTime = false,
91
- dateFormat,
92
- dateTimeFormat,
93
- onKeyDown,
94
- allowRelativeDate = false,
95
- relativeDateToExactDate,
96
- } = props;
85
+ const {className, value, onChange, onKeyDown, allowRelativeDate = false, relativeDateToExactDate} = props;
97
86
 
98
87
  const isRelativeValue = allowRelativeDate ? isRelativeDatePlaceholder(value) : false;
99
88
 
100
- const dateValueFormatted = isRelativeValue
101
- ? formatRelativeDate(value as RelativeDateValue)
102
- : value
103
- ? formatDateInputValue((value as Moment).toDate(), {
104
- showTime,
105
- dateFormat,
106
- dateTimeFormat,
107
- })
108
- : null;
109
89
  const onDateChange = useCallback(
110
90
  (value: Date | null) => {
111
91
  // @ts-expect-error fix types
@@ -124,12 +104,6 @@ export const SingleDatePicker: SingleDatePickerWithForwardRefType = forwardRef<
124
104
 
125
105
  const fakeWrapperRef = useRef<HTMLDivElement>(null);
126
106
 
127
- const pickerValue = children || (
128
- <FakeDateInputWrapper disabled={disabled} isPickerOpen={open}>
129
- {!dateValueFormatted ? <EmptyValue>{placeholder}</EmptyValue> : <>{dateValueFormatted}</>}
130
- </FakeDateInputWrapper>
131
- );
132
-
133
107
  const onDatePickerKeyDown = useCallback(
134
108
  (e: React.KeyboardEvent<HTMLInputElement>) => {
135
109
  if (e.key === "Tab" || e.key === "Enter") {
@@ -151,93 +125,147 @@ export const SingleDatePicker: SingleDatePickerWithForwardRefType = forwardRef<
151
125
  allowRelativeDate && (isRelativeValue || value === null) ? "relative" : "exact"
152
126
  );
153
127
 
128
+ return (
129
+ <div
130
+ className={cx(
131
+ css`
132
+ display: flex;
133
+ flex-direction: column;
134
+ `,
135
+ allowRelativeDate &&
136
+ css`
137
+ min-width: 320px;
138
+ `,
139
+ className
140
+ )}
141
+ >
142
+ {allowRelativeDate ? (
143
+ <div
144
+ className={css`
145
+ border-bottom: ${themeVars.separators.opacity5};
146
+ `}
147
+ >
148
+ <TabNavRoot>
149
+ <TabNavList
150
+ className={css`
151
+ padding-left: ${space.s12}px;
152
+ padding-right: ${space.s12}px;
153
+ padding-top: ${space.s6}px;
154
+ display: flex;
155
+ width: 100%;
156
+ justify-content: space-evenly;
157
+ `}
158
+ >
159
+ <TabNavLink
160
+ className={tabNavLinkClassName}
161
+ itemClassName={tabItemClassName}
162
+ active={activeButton === "relative"}
163
+ onSelect={() => setActiveButton("relative")}
164
+ >
165
+ Relative
166
+ </TabNavLink>
167
+ <TabNavLink
168
+ className={tabNavLinkClassName}
169
+ itemClassName={tabItemClassName}
170
+ active={activeButton === "exact"}
171
+ onSelect={() => setActiveButton("exact")}
172
+ >
173
+ Exact
174
+ </TabNavLink>
175
+ </TabNavList>
176
+ </TabNavRoot>
177
+ </div>
178
+ ) : null}
179
+ <div>
180
+ {!allowRelativeDate || activeButton === "exact" ? (
181
+ <SingleDatePickerPopupContent
182
+ ref={ref}
183
+ {...omit(props, ["onChange", "value"])}
184
+ onChange={onDateChange}
185
+ value={!isRelativeValue && value ? (value as Moment).toDate() : null}
186
+ onKeyDown={onDatePickerKeyDown}
187
+ />
188
+ ) : (
189
+ <RelativeDatePicker
190
+ value={isRelativeValue ? (value as RelativeDateValue) : null}
191
+ onChange={onRelativeDateChange}
192
+ relativeDateToExactDate={relativeDateToExactDate}
193
+ />
194
+ )}
195
+ </div>
196
+ </div>
197
+ );
198
+ });
199
+
200
+ export const SingleDatePicker: SingleDatePickerWithForwardRefType = forwardRef<
201
+ DatePickerRef,
202
+ SingleDatePickerProps<boolean>
203
+ >((props, ref) => {
204
+ const {
205
+ title,
206
+ open,
207
+ onOpen,
208
+ onClose,
209
+ children,
210
+ bypassedRef,
211
+ value,
212
+ disabled = false,
213
+ placeholder,
214
+ showTime = false,
215
+ dateFormat,
216
+ dateTimeFormat,
217
+ allowRelativeDate = false,
218
+ } = props;
219
+
220
+ const isRelativeValue = allowRelativeDate ? isRelativeDatePlaceholder(value) : false;
221
+
222
+ const dateValueFormatted = isRelativeValue
223
+ ? formatRelativeDate(value as RelativeDateValue)
224
+ : value
225
+ ? formatDateInputValue((value as Moment).toDate(), {
226
+ showTime,
227
+ dateFormat,
228
+ dateTimeFormat,
229
+ })
230
+ : null;
231
+
232
+ const fakeWrapperRef = useRef<HTMLDivElement>(null);
233
+
234
+ const pickerValue = children || (
235
+ <FakeDateInputWrapper disabled={disabled} isPickerOpen={open}>
236
+ {!dateValueFormatted ? <EmptyValue>{placeholder}</EmptyValue> : <>{dateValueFormatted}</>}
237
+ </FakeDateInputWrapper>
238
+ );
239
+
154
240
  if (disabled) {
155
241
  return pickerValue;
156
242
  }
157
243
 
158
244
  return (
159
245
  <div ref={bypassedRef}>
160
- <PopoverOld
161
- title={title}
162
- offset={[0, space.s6]}
163
- trigger={
246
+ <Popover.Root open={open} onOpen={onOpen} onClose={onClose}>
247
+ <Popover.Trigger>
164
248
  <div ref={fakeWrapperRef} onClick={stopPropagation}>
165
249
  {pickerValue}
166
250
  </div>
167
- }
168
- placement="bottom-start"
169
- open={open}
170
- onOpen={onOpen}
171
- onClose={onClose}
172
- supportMobile
173
- popupContainerClassName={css`
174
- z-index: 10001;
175
- `}
176
- renderInPortal={renderInPortal}
177
- preventOverflowOptions={{
178
- altAxis: true,
179
- }}
180
- >
181
- {allowRelativeDate ? (
182
- <div
183
- className={css`
184
- border-bottom: ${themeVars.separators.opacity5};
251
+ </Popover.Trigger>
252
+ <Popover.Portal>
253
+ <Popover.Content
254
+ title={title}
255
+ offset={[0, space.s6]}
256
+ placement="bottom-start"
257
+ supportMobile
258
+ popupContainerClassName={css`
259
+ z-index: 10001;
185
260
  `}
261
+ preventOverflowOptions={{
262
+ altAxis: true,
263
+ }}
186
264
  >
187
- <TabNavRoot>
188
- <TabNavList
189
- className={css`
190
- padding-left: ${space.s12}px;
191
- padding-right: ${space.s12}px;
192
- padding-top: ${space.s6}px;
193
- display: flex;
194
- width: 100%;
195
- justify-content: space-evenly;
196
- `}
197
- >
198
- <TabNavLink
199
- className={tabNavLinkClassName}
200
- itemClassName={tabItemClassName}
201
- active={activeButton === "relative"}
202
- onSelect={() => setActiveButton("relative")}
203
- >
204
- Relative
205
- </TabNavLink>
206
- <TabNavLink
207
- className={tabNavLinkClassName}
208
- itemClassName={tabItemClassName}
209
- active={activeButton === "exact"}
210
- onSelect={() => setActiveButton("exact")}
211
- >
212
- Exact
213
- </TabNavLink>
214
- </TabNavList>
215
- </TabNavRoot>
216
- </div>
217
- ) : null}
218
- <div
219
- style={allowRelativeDate ? {minWidth: 320} : undefined}
220
- className={css`
221
- width: 100%;
222
- `}
223
- >
224
- {!allowRelativeDate || activeButton === "exact" ? (
225
- <SingleDatePickerPopupContent
226
- ref={ref}
227
- {..._.omit(props, ["onChange", "value"])}
228
- onChange={onDateChange}
229
- value={!isRelativeValue && value ? (value as Moment).toDate() : null}
230
- onKeyDown={onDatePickerKeyDown}
231
- />
232
- ) : (
233
- <RelativeDatePicker
234
- value={isRelativeValue ? (value as RelativeDateValue) : null}
235
- onChange={onRelativeDateChange}
236
- relativeDateToExactDate={relativeDateToExactDate}
237
- />
238
- )}
239
- </div>
240
- </PopoverOld>
265
+ <SingleDatePickerPopoverContent ref={ref} {...props} />
266
+ </Popover.Content>
267
+ </Popover.Portal>
268
+ </Popover.Root>
241
269
  </div>
242
270
  );
243
271
  });
@@ -27,7 +27,6 @@ export type DatePickerCommonProps = {
27
27
  allowClear?: boolean;
28
28
  bypassedRef?: React.Ref<$TSFixMe>;
29
29
  disabled?: boolean;
30
- renderInPortal?: boolean;
31
30
  onKeyDown?: React.KeyboardEventHandler<$TSFixMe>;
32
31
  placeholder: string;
33
32
  weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined;
@@ -49,10 +48,12 @@ export type NativeDateRangePickerProps = {
49
48
 
50
49
  export type ControlledDatePickerProps = {open: boolean; onOpen: () => void; onClose: () => void};
51
50
 
52
- export type SingleDatePickerProps<IsRelativeDate extends boolean = false> = DatePickerCommonProps &
53
- ControlledDatePickerProps &
51
+ export type SingleDatePickerPopoverContentProps<IsRelativeDate extends boolean = false> = DatePickerCommonProps &
54
52
  MomentSingleDateProps<IsRelativeDate> & {
55
53
  allowRelativeDate?: IsRelativeDate;
56
54
  } & (IsRelativeDate extends true
57
55
  ? {relativeDateToExactDate?: (value: RelativeDateValue) => string} // Required if IsRelativeDate is true
58
56
  : {relativeDateToExactDate?: never}); // Undefined if IsRelativeDate is false;
57
+
58
+ export type SingleDatePickerProps<IsRelativeDate extends boolean = false> = ControlledDatePickerProps &
59
+ SingleDatePickerPopoverContentProps<IsRelativeDate>;
@@ -1,8 +1,8 @@
1
- import _ from "lodash";
1
+ import range from "lodash/range";
2
2
  import {DaySelect, Props} from "./day-select";
3
3
  import moment from "moment";
4
4
 
5
- const isoWeekDays = _.range(1, 8).map((day) => {
5
+ const isoWeekDays = range(1, 8).map((day) => {
6
6
  return {
7
7
  key: String(day),
8
8
  title: moment().isoWeekday(day).format("dd").charAt(0),