@fibery/ui-kit 1.38.0 → 1.38.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/config/jest/testFrameworkSetup.js +1 -1
  3. package/package.json +16 -15
  4. package/src/abbreviate-name.ts +42 -0
  5. package/src/actions-menu/actions-menu-item.tsx +14 -5
  6. package/src/actions-menu/actions-menu-props.tsx +1 -0
  7. package/src/actions-menu/actions-menu.tsx +4 -1
  8. package/src/actions-menu/context-actions-menu.tsx +22 -0
  9. package/src/animated-height-container.tsx +24 -5
  10. package/src/antd/ant-upload.tsx +1 -1
  11. package/src/antd/input.tsx +4 -0
  12. package/src/avatar.tsx +1 -27
  13. package/src/button/base-button.tsx +2 -0
  14. package/src/button/button.tsx +3 -2
  15. package/src/button/icon-button.tsx +3 -3
  16. package/src/card-container.tsx +3 -2
  17. package/src/checkbox.tsx +17 -0
  18. package/src/collapsible-section.tsx +3 -1
  19. package/src/date-picker/autocomplete.tsx +11 -5
  20. package/src/date-picker/date-range-input.tsx +5 -1
  21. package/src/date-picker/date-range-picker-popup-content.tsx +31 -13
  22. package/src/date-picker/single-date-input.tsx +5 -1
  23. package/src/date-picker/styles.ts +20 -0
  24. package/src/design-system.ts +24 -0
  25. package/src/dropdown-menu/index.tsx +11 -1
  26. package/src/emoji-picker/emoji-picker-preview-footer.tsx +4 -0
  27. package/src/emoji-picker/emoji.tsx +4 -3
  28. package/src/emoji-picker/primitives/emoji.tsx +49 -51
  29. package/src/emoji-picker/primitives/footer.tsx +6 -0
  30. package/src/emoji-picker/primitives/search.tsx +8 -0
  31. package/src/emoji-picker/primitives/skin-tone.tsx +12 -2
  32. package/src/fibermoji-placeholder.tsx +1 -27
  33. package/src/file-item-2.tsx +408 -0
  34. package/src/file-item.tsx +63 -12
  35. package/src/header-layout.tsx +11 -0
  36. package/src/html-styles.ts +13 -4
  37. package/src/icons/ast/Back.ts +1 -1
  38. package/src/icons/ast/ExtensionFiles.ts +1 -1
  39. package/src/icons/ast/FileUpload.ts +1 -1
  40. package/src/icons/ast/SwitchToMention.ts +8 -0
  41. package/src/icons/ast/SwitchToText.ts +8 -0
  42. package/src/icons/ast/SwitchToWidget.ts +8 -0
  43. package/src/icons/ast/ViewGallery.ts +8 -0
  44. package/src/icons/ast/index.tsx +4 -0
  45. package/src/icons/react/SwitchToMention.tsx +13 -0
  46. package/src/icons/react/SwitchToText.tsx +13 -0
  47. package/src/icons/react/SwitchToWidget.tsx +13 -0
  48. package/src/icons/react/ViewGallery.tsx +13 -0
  49. package/src/icons/react/index.tsx +4 -0
  50. package/src/icons/svg/back.svg +3 -1
  51. package/src/icons/svg/extension/files.svg +3 -1
  52. package/src/icons/svg/file-upload.svg +3 -6
  53. package/src/icons/svg/switch-to-mention.svg +3 -0
  54. package/src/icons/svg/switch-to-text.svg +4 -0
  55. package/src/icons/svg/switch-to-widget.svg +4 -0
  56. package/src/icons/svg/view-gallery.svg +4 -0
  57. package/src/images-gallery/images-gallery-fall-through-provider.tsx +11 -0
  58. package/src/images-gallery/images-gallery.tsx +13 -10
  59. package/src/images-gallery/zoom.tsx +13 -2
  60. package/src/images-gallery-2/images-gallery-2.tsx +442 -0
  61. package/src/images-gallery-2/slide-buttons.tsx +230 -0
  62. package/src/images-gallery-2/zoom.tsx +35 -0
  63. package/src/kbd.tsx +46 -0
  64. package/src/modal-menu/modal-menu-item.tsx +10 -2
  65. package/src/modal-menu/modal-menu-trigger.tsx +19 -2
  66. package/src/notification-dot.tsx +16 -7
  67. package/src/platform-symbols.ts +9 -0
  68. package/src/platform.ts +2 -0
  69. package/src/popover/index.tsx +31 -8
  70. package/src/popover/modifiers.tsx +26 -19
  71. package/src/reactions/reaction-button.tsx +323 -101
  72. package/src/reactions/reaction-picker.tsx +14 -1
  73. package/src/reactions/reactions.tsx +11 -1
  74. package/src/reactions/tootltip.tsx +55 -0
  75. package/src/rich-text-embed-error.tsx +2 -1
  76. package/src/select/components/group-heading.tsx +8 -0
  77. package/src/select/components/menu-list-virtualized.tsx +6 -2
  78. package/src/select/components/option.tsx +4 -0
  79. package/src/select/select-control-settings-context.tsx +11 -2
  80. package/src/select/select-in-popover.tsx +10 -7
  81. package/src/select/styles.ts +21 -1
  82. package/src/tab-nav/tab-nav.tsx +1 -1
  83. package/src/toggle.tsx +8 -0
  84. package/src/tooltip.tsx +27 -18
  85. package/src/type-badge.tsx +5 -2
  86. package/src/unit/unit-with-tooltip.tsx +1 -1
  87. package/src/use-is-phone.tsx +27 -6
  88. package/src/shortcut-badge.tsx +0 -29
package/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # @fibery/ui-kit
2
2
 
3
+ ## 1.38.1
4
+
5
+ ### Patch Changes
6
+
7
+ - db89c7a: Add Gallery View
8
+ - 00766c6: switch to pnpm catalogs. Update to latest
9
+ - Updated dependencies [00766c6]
10
+ - @fibery/emoji-data@2.7.1
11
+ - @fibery/helpers@1.3.4
12
+ - @fibery/react@1.4.5
13
+
3
14
  ## 1.38.0
4
15
 
5
16
  ### Minor Changes
@@ -1 +1 @@
1
- import "@testing-library/jest-dom/extend-expect";
1
+ import "@testing-library/jest-dom";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fibery/ui-kit",
3
- "version": "1.38.0",
3
+ "version": "1.38.1",
4
4
  "private": false,
5
5
  "license": "UNLICENSED",
6
6
  "dependencies": {
@@ -31,6 +31,7 @@
31
31
  "lodash": "4.17.21",
32
32
  "md5": "2.2.1",
33
33
  "moment": "2.29.4",
34
+ "photoswipe": "5.4.4",
34
35
  "popper-max-size-modifier": "0.2.0",
35
36
  "prop-types": "15.7.2",
36
37
  "rc-input-number": "8.5.0",
@@ -46,26 +47,26 @@
46
47
  "screenfull": "6.0.1",
47
48
  "tabbable": "5.2.1",
48
49
  "ua-parser-js": "1.0.39",
49
- "@fibery/emoji-data": "2.7.0",
50
- "@fibery/helpers": "1.3.3",
51
- "@fibery/react": "1.4.4"
50
+ "@fibery/emoji-data": "2.7.1",
51
+ "@fibery/helpers": "1.3.4",
52
+ "@fibery/react": "1.4.5"
52
53
  },
53
54
  "peerDependencies": {
54
- "react": "^18.2.0",
55
- "react-dom": "^18.2.0"
55
+ "react": "18.2.0",
56
+ "react-dom": "18.2.0"
56
57
  },
57
58
  "devDependencies": {
58
- "@babel/core": "7.23.9",
59
- "@babel/runtime": "7.23.9",
59
+ "@babel/core": "7.28.5",
60
+ "@babel/runtime": "7.28.4",
60
61
  "@testing-library/dom": "8.19.1",
61
- "@testing-library/jest-dom": "5.16.5",
62
+ "@testing-library/jest-dom": "6.9.1",
62
63
  "@testing-library/react": "13.4.0",
63
64
  "@testing-library/user-event": "13.5.0",
64
65
  "@types/chroma-js": "2.1.3",
65
66
  "@types/color-hash": "1.0.2",
66
67
  "@types/history": "4.7.11",
67
68
  "@types/invariant": "2.2.34",
68
- "@types/jest": "27.0.1",
69
+ "@types/jest": "30.0.0",
69
70
  "@types/lodash": "4.14.172",
70
71
  "@types/prop-types": "15.7.5",
71
72
  "@types/react": "18.0.26",
@@ -74,16 +75,16 @@
74
75
  "csstype": "3.0.8",
75
76
  "fs-extra": "10.0.0",
76
77
  "glob": "7.1.7",
77
- "jest": "27.5.1",
78
- "jest-junit": "13.0.0",
78
+ "jest": "30.2.0",
79
+ "jest-junit": "16.0.0",
79
80
  "react": "18.2.0",
80
81
  "react-dom": "18.2.0",
81
82
  "svg-parser": "2.0.4",
82
83
  "svgo": "2.8.0",
83
- "typescript": "5.8.3",
84
+ "typescript": "5.9.3",
84
85
  "unist-util-reduce": "0.2.2",
85
- "@fibery/eslint-config": "8.6.1",
86
- "@fibery/babel-preset": "7.4.0"
86
+ "@fibery/babel-preset": "7.4.1",
87
+ "@fibery/eslint-config": "8.6.2"
87
88
  },
88
89
  "jest": {
89
90
  "testEnvironment": "jsdom",
@@ -0,0 +1,42 @@
1
+ const preservedNameEmojis = ["🤖", "👻", "🧮", "🪄", "🔌", "🔗", "🔒"];
2
+
3
+ export const abbreviateName = (name?: string | null) => {
4
+ if (name === undefined) {
5
+ return "…";
6
+ }
7
+ if (name && preservedNameEmojis.includes(name)) {
8
+ return name;
9
+ }
10
+
11
+ // Check if name looks like "Surname, Name"
12
+ const hasComma = (name || "").includes(",");
13
+
14
+ const cleaned = (name || "")
15
+ .replace(/\p{Emoji_Presentation}|\p{Emoji}\uFE0F?/gu, "") // remove emojis
16
+ .replace(/[^\p{L}\p{M}'\-\s,]+/gu, " ") // keep comma to detect order before removing
17
+ .trim();
18
+
19
+ // Split on whitespace and commas
20
+ let parts = cleaned.split(/[,\s]+/).filter(Boolean);
21
+
22
+ // If there was a comma, assume it's "Surname, Name" and reverse
23
+ if (hasComma && parts.length > 1) {
24
+ parts = parts.reverse();
25
+ }
26
+ let result: string;
27
+ if (parts.length === 1) {
28
+ // Take the first two codepoints (safer for emoji / astral / surrogate pairs)
29
+ result = [...parts[0]].slice(0, 2).join("");
30
+ } else {
31
+ // Take the first codepoint of the first two parts and upper-case
32
+ result = parts
33
+ .slice(0, 2)
34
+ .map((p) => {
35
+ const first = [...p][0] || "";
36
+ return first.toUpperCase();
37
+ })
38
+ .join("");
39
+ }
40
+
41
+ return result || "?";
42
+ };
@@ -7,11 +7,12 @@ import CheckedIcon from "../icons/react/Checked";
7
7
  import {IconBaseProps} from "../icons/types";
8
8
  import {Spinner} from "../loaders";
9
9
  import {mobileRootSelector} from "../mobile-styles";
10
- import {ShortcutBadge} from "../shortcut-badge";
10
+ import {Kbd, KbdGroup} from "../kbd";
11
11
  import {useTheme} from "../theme-provider";
12
12
  import {useIsPhone} from "../use-is-phone";
13
13
  import {useActionsMenuContext} from "./contexts/actions-menu-context";
14
14
  import {useActiveDangerousRow, useSetActiveDangerousRow} from "./contexts/actions-menu-dangerous-rows";
15
+ import _ from "lodash";
15
16
 
16
17
  const hiddenTextCss = css`
17
18
  visibility: hidden;
@@ -90,14 +91,22 @@ const firstLetterToLowerCase = (str: string) => {
90
91
  return str.charAt(0).toLowerCase() + str.slice(1);
91
92
  };
92
93
 
93
- const RightCorner = ({shortcut, loading, checked}: {shortcut?: string; loading?: boolean; checked?: boolean}) => {
94
+ const RightCorner = ({shortcut, loading, checked}: {shortcut?: string[]; loading?: boolean; checked?: boolean}) => {
94
95
  const theme = useTheme();
95
96
  const isPhone = useIsPhone();
96
97
 
97
- return loading || shortcut || checked ? (
98
+ const shortcuts = _.isArray(shortcut) ? shortcut : shortcut ? [shortcut] : null;
99
+
100
+ return loading || shortcuts || checked ? (
98
101
  <div className={cornerCss}>
99
102
  {loading ? <Spinner color={theme.primary} iconSize={16} containerSize={16} /> : null}
100
- {shortcut ? <ShortcutBadge shortcut={shortcut} /> : null}
103
+ {shortcuts ? (
104
+ <KbdGroup>
105
+ {shortcuts.map((item) => (
106
+ <Kbd key={item}>{item}</Kbd>
107
+ ))}
108
+ </KbdGroup>
109
+ ) : null}
101
110
  {checked ? <CheckedIcon iconSize={isPhone ? 20 : 18} color={themeVars.textColor} /> : null}
102
111
  </div>
103
112
  ) : null;
@@ -127,7 +136,7 @@ export type ActionsMenuItemProps = {
127
136
  Icon?: FunctionComponent<IconBaseProps>;
128
137
  iconColor?: string;
129
138
  /** Hint for keyboard shortcut for this action */
130
- shortcut?: string;
139
+ shortcut?: string[];
131
140
  disabled?: boolean;
132
141
  /** Whether the action requires confirmation */
133
142
  dangerous?: boolean;
@@ -5,6 +5,7 @@ import {ContentProps} from "../dropdown-menu";
5
5
  export type ActionsMenuProps = {
6
6
  /** Clicking on this node will open actions menu. Make sure that trigger forwards ref AND props to html element */
7
7
  trigger: ReactNode;
8
+ triggerEvent?: "click" | "context-menu";
8
9
  children: ReactNode;
9
10
  align?: ContentProps["align"];
10
11
  disabled?: boolean;
@@ -23,6 +23,7 @@ const wideCss = css`
23
23
 
24
24
  export const ActionsMenu = ({
25
25
  trigger,
26
+ triggerEvent = "click",
26
27
  wide,
27
28
  align = "start",
28
29
  alignOffset,
@@ -77,7 +78,9 @@ export const ActionsMenu = ({
77
78
  <MenuPrimitive.Trigger
78
79
  // backward compatibility for dropdown styles
79
80
  className={isOpen ? "actions-visible" : "actions-hidden actions-visible-on-mobile"}
80
- onClick={preventDefaultAndStopPropagation}
81
+ onClick={triggerEvent === "click" ? preventDefaultAndStopPropagation : undefined}
82
+ onContextMenu={triggerEvent === "context-menu" ? preventDefaultAndStopPropagation : undefined}
83
+ triggerEvent={triggerEvent}
81
84
  disabled={disabled}
82
85
  asChild
83
86
  >
@@ -2,6 +2,8 @@ import _ from "lodash";
2
2
  import {useControllableState} from "@fibery/react/src/use-controllable-state";
3
3
  import {ActionsMenuContextProvider} from "./contexts/actions-menu-context";
4
4
  import * as ContextMenu from "../context-menu";
5
+ import {ActionsMenu} from "./actions-menu";
6
+ import {useIsPhone} from "../use-is-phone";
5
7
  import {ActionsMenuDangerousRowsProvider} from "./contexts/actions-menu-dangerous-rows";
6
8
  import {ActionsMenuProps} from "./actions-menu-props";
7
9
  import {preventDefault} from "@fibery/react/src/prevent-default";
@@ -14,11 +16,31 @@ export const ContextActionsMenu = ({
14
16
  trigger,
15
17
  children,
16
18
  disabled,
19
+ mobileTitle,
20
+ supportsMobile,
17
21
  autoFocusOnClose = true,
18
22
  }: ActionsMenuProps): JSX.Element => {
23
+ const isPhone = useIsPhone();
19
24
  const [isOpen = false, setOpen] = useControllableState({value: open, onChange: onOpenChange});
20
25
  const [confirmation, setConfirmation] = useState<ActionsMenuConfirmationProps | null>(null);
21
26
 
27
+ // Special mode - on mobile, open context menu in modal
28
+ if (isPhone && supportsMobile) {
29
+ return (
30
+ <ActionsMenu
31
+ open={open}
32
+ onOpenChange={onOpenChange}
33
+ mobileTitle={mobileTitle}
34
+ supportsMobile
35
+ // Extra div is important here to not loose the original onClick handler
36
+ trigger={<div>{trigger}</div>}
37
+ triggerEvent="context-menu"
38
+ >
39
+ {children}
40
+ </ActionsMenu>
41
+ );
42
+ }
43
+
22
44
  return (
23
45
  <ActionsMenuContextProvider setOpen={setOpen} showConfirmation={setConfirmation} MenuPrimitive={ContextMenu}>
24
46
  <ContextMenu.Root onOpenChange={onOpenChange}>
@@ -1,16 +1,20 @@
1
1
  import {useSize} from "@fibery/react/src/use-size";
2
2
  import React, {ComponentProps, forwardRef, useEffect, useRef, useState} from "react";
3
3
  import {duration, transition} from "./design-system";
4
- import cx from "classnames";
5
4
  import _ from "lodash";
6
5
  import {useComposedRefs} from "@fibery/react/src/use-composed-refs";
7
6
 
8
- interface AnimatedHeightContainerProps extends ComponentProps<"div"> {
7
+ type AnimatedHeightContainerProps = ComponentProps<"div"> & {
9
8
  noAnimation?: boolean;
10
- }
9
+ onTransitionStart?: (event: TransitionEvent) => void;
10
+ onTransitionEnd?: (event: TransitionEvent) => void;
11
+ };
11
12
 
12
13
  export const AnimatedHeightContainer = forwardRef<HTMLDivElement, AnimatedHeightContainerProps>(
13
- function AnimatedHeightContainerComponent({className, noAnimation = false, children, ...otherProps}, ref) {
14
+ function AnimatedHeightContainerComponent(
15
+ {className, noAnimation = false, onTransitionStart = _.noop, onTransitionEnd = _.noop, children, ...otherProps},
16
+ ref
17
+ ) {
14
18
  const measuredContainer = useRef<HTMLDivElement | null>(null);
15
19
  const size = useSize(measuredContainer);
16
20
 
@@ -60,6 +64,21 @@ export const AnimatedHeightContainer = forwardRef<HTMLDivElement, AnimatedHeight
60
64
  return cleanup;
61
65
  }, [animating]);
62
66
 
67
+ useEffect(() => {
68
+ const element = containerRef.current;
69
+ if (!element) {
70
+ return () => {};
71
+ }
72
+
73
+ element.addEventListener("transitionstart", onTransitionStart);
74
+ element.addEventListener("transitionend", onTransitionEnd);
75
+
76
+ return () => {
77
+ element.removeEventListener("transitionstart", onTransitionStart);
78
+ element.removeEventListener("transitionend", onTransitionEnd);
79
+ };
80
+ }, [onTransitionStart, onTransitionEnd]);
81
+
63
82
  const style = animating
64
83
  ? {
65
84
  ...otherProps.style,
@@ -74,7 +93,7 @@ export const AnimatedHeightContainer = forwardRef<HTMLDivElement, AnimatedHeight
74
93
  };
75
94
 
76
95
  return (
77
- <div ref={composedRef} className={cx(className)} {...otherProps} style={style}>
96
+ <div ref={composedRef} className={className} {...otherProps} style={style}>
78
97
  <div ref={measuredContainer}>{children}</div>
79
98
  </div>
80
99
  );
@@ -27,7 +27,7 @@ export const antUploadStyle = css`
27
27
  alignItems: "stretch",
28
28
  alignContent: "stretch",
29
29
  },
30
- "& .ant-upload.ant-upload-drag, .ant-upload.ant-upload-select-picture-card": {
30
+ "& .ant-upload:where(.ant-upload-drag), .ant-upload:where(.ant-upload-select-picture-card)": {
31
31
  border: `1px dashed ${themeVars.separator}`,
32
32
  position: "relative",
33
33
  backgroundColor: themeVars.transparent,
@@ -2,6 +2,7 @@ import {getWrapper} from "./get-wrapper";
2
2
  import Input from "antd/lib/input";
3
3
  import "antd/lib/input/style/css";
4
4
  import {css} from "@linaria/core";
5
+ import {mobileRootSelector} from "../mobile-styles";
5
6
  import {opacity, textStyles, themeVars} from "../design-system";
6
7
 
7
8
  import {inputStyles} from "./styles";
@@ -24,6 +25,9 @@ export const transparentInputStyle = css`
24
25
  transition: "none",
25
26
  ...textStyles.regular,
26
27
  ...textStyles.numeric.tabularOff,
28
+ [`${mobileRootSelector} &`]: {
29
+ ...textStyles.big,
30
+ },
27
31
  },
28
32
  "& textarea.ant-input": {
29
33
  fontVariant: "inherit",
package/src/avatar.tsx CHANGED
@@ -5,33 +5,7 @@ import {getDarkenColor, getObjectColorMemoized, textStyles, themeVars} from "./d
5
5
  import {FibermojiPlaceholder} from "./fibermoji-placeholder";
6
6
  import {getShiftStyle} from "./icons/get-shift-style";
7
7
  import {useFeatures} from "./features";
8
-
9
- const preservedNameEmojis = ["🤖", "👻", "🧮", "🪄", "🔌", "🔗", "🔒"];
10
-
11
- export const abbreviateName = (name?: string) => {
12
- if (name === undefined) {
13
- return "…";
14
- }
15
- if (preservedNameEmojis.includes(name)) {
16
- return name;
17
- }
18
- const filteredName = (name || "").replace(/[!~.\-@[\]]/g, "").trim();
19
- const match = filteredName.match(/\p{Letter}[\p{Letter} ]+/gu);
20
- const processedName = match ? match[0] : "";
21
- const parts = processedName.split(" ").filter((part) => part !== "");
22
- let result;
23
- if (parts.length === 1) {
24
- result = processedName.slice(0, 2);
25
- } else {
26
- result = parts
27
- .filter((part, index) => index < 2)
28
- .map((part) => {
29
- return part[0].toUpperCase();
30
- })
31
- .join("");
32
- }
33
- return result || "?";
34
- };
8
+ import {abbreviateName} from "./abbreviate-name";
35
9
 
36
10
  export const getContainerStyle = _.memoize((containerSize) => {
37
11
  return {
@@ -14,6 +14,8 @@ export type BaseButtonProps = {
14
14
  supportMobile?: boolean;
15
15
  } & React.ComponentPropsWithRef<"button">;
16
16
 
17
+ export const xLargeButtonHeight = 40;
18
+
17
19
  export const baseButton = css`
18
20
  position: relative;
19
21
  box-sizing: border-box;
@@ -3,7 +3,7 @@ import {forwardRef} from "react";
3
3
  import {border, fontWeight, space, textStyles} from "../design-system";
4
4
  import {iconSizeVar} from "../icons/Icon";
5
5
  import SpinnerIcon from "../icons/react/Spinner";
6
- import {BaseButton, BaseButtonProps, ButtonSize} from "./base-button";
6
+ import {BaseButton, BaseButtonProps, ButtonSize, xLargeButtonHeight} from "./base-button";
7
7
  import {useIsSupportHardwareKeyboard} from "../use-is-support-hardware-keyboard";
8
8
  import {useIsPhone} from "../use-is-phone";
9
9
 
@@ -150,7 +150,7 @@ const buttonSize = {
150
150
  letter-spacing: ${textStyles.heading3.letterSpacing};
151
151
 
152
152
  border-radius: ${border.radius6}px;
153
- min-height: 44px;
153
+ min-height: ${xLargeButtonHeight}px;
154
154
  padding: ${space.s6}px ${space.s12}px;
155
155
  gap: ${space.s6}px;
156
156
 
@@ -205,6 +205,7 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
205
205
  ref={ref}
206
206
  disabled={disabled || pending}
207
207
  className={cx(
208
+ "notranslate",
208
209
  className,
209
210
  button,
210
211
  buttonSize[supportMobile && isPhone ? "large" : size],
@@ -3,7 +3,7 @@ import {forwardRef} from "react";
3
3
  import {border, space} from "../design-system";
4
4
  import {iconSizeVar} from "../icons/Icon";
5
5
  import Spinner from "../icons/react/Spinner";
6
- import {BaseButton, BaseButtonProps, ButtonSize} from "./base-button";
6
+ import {BaseButton, BaseButtonProps, ButtonSize, xLargeButtonHeight} from "./base-button";
7
7
  import {useIsPhone} from "../use-is-phone";
8
8
 
9
9
  export type IconButtonProps = {
@@ -55,8 +55,8 @@ export const iconButtonSizes = {
55
55
  xLarge: css`
56
56
  padding: ${space.s6}px;
57
57
 
58
- min-width: 44px;
59
- min-height: 44px;
58
+ min-width: ${xLargeButtonHeight}px;
59
+ min-height: ${xLargeButtonHeight}px;
60
60
  border-radius: ${border.radius6}px;
61
61
 
62
62
  ${iconSizeVar}: 24px;
@@ -247,7 +247,8 @@ const floatingStyles = {
247
247
  };
248
248
 
249
249
  const coverClassName = css`
250
- margin: ${-space.s4}px;
250
+ // box padding -1px to go below the outline
251
+ margin: ${-space.s5}px;
251
252
 
252
253
  &:empty {
253
254
  display: none;
@@ -255,7 +256,7 @@ const coverClassName = css`
255
256
  `;
256
257
 
257
258
  const coverWithContentClassName = css`
258
- border-bottom: ${themeVars.separators.opacity5};
259
+ border-bottom: ${themeVars.shortcutBorderColor};
259
260
  margin-bottom: 0;
260
261
  `;
261
262
 
package/src/checkbox.tsx CHANGED
@@ -14,6 +14,7 @@ import {
14
14
  } from "react";
15
15
  import {colors, getDarkenColor, getOpacities, layout, space, textStyles, ThemeColors, themeVars} from "./design-system";
16
16
  import {smallHtmlStylesClass} from "./html-styles";
17
+ import {mobileRootSelector} from "./mobile-styles";
17
18
 
18
19
  const checkboxColor = `--fibery-checkbox-color`;
19
20
  const checkboxColorVar = `var(${checkboxColor})`;
@@ -36,6 +37,10 @@ const backgroundStyle = css`
36
37
  box-shadow: 0 0 0 1.5px ${borderColorVar};
37
38
  background-color: ${themeVars.panelBg};
38
39
 
40
+ ${mobileRootSelector} & {
41
+ box-shadow: 0 0 0 1.6px ${borderColorVar};
42
+ }
43
+
39
44
  .${smallHtmlStylesClass} & {
40
45
  width: ${layout.checkboxSize - 2}px;
41
46
  height: ${layout.checkboxSize - 2}px;
@@ -53,6 +58,9 @@ const checkboxRootStyle = css`
53
58
  & input[type="checkbox"]:checked + .${backgroundStyle} {
54
59
  background-color: ${checkboxColorVar};
55
60
  box-shadow: 0 0 0 1.5px ${checkboxColorVar} !important;
61
+ ${mobileRootSelector} & {
62
+ box-shadow: 0 0 0 1.6px ${checkboxColorVar} !important;
63
+ }
56
64
  }
57
65
 
58
66
  & input[type="checkbox"]:indeterminate + .${backgroundStyle} {
@@ -66,15 +74,24 @@ const checkboxRootStyle = css`
66
74
  &:hover input[type="checkbox"]:checked:not(:disabled) + .${backgroundStyle} {
67
75
  background-color: ${darkenColorVar};
68
76
  box-shadow: 0 0 0 1.5px ${darkenColorVar};
77
+ ${mobileRootSelector} & {
78
+ box-shadow: 0 0 0 1.6px ${darkenColorVar};
79
+ }
69
80
  }
70
81
 
71
82
  &:hover input[type="checkbox"]:checked:focus:not(:disabled) + .${backgroundStyle} {
72
83
  background-color: ${darkenColorVar};
73
84
  box-shadow: 0 0 0 1.5px ${darkenColorVar}, 0 0 0 4px ${focusColorVar};
85
+ ${mobileRootSelector} & {
86
+ box-shadow: 0 0 0 1.6px ${darkenColorVar}, 0 0 0 4px ${focusColorVar};
87
+ }
74
88
  }
75
89
 
76
90
  & input[type="checkbox"]:focus + .${backgroundStyle} {
77
91
  box-shadow: 0 0 0 1.5px ${checkboxColorVar}, 0 0 0 4px ${focusColorVar};
92
+ ${mobileRootSelector} & {
93
+ box-shadow: 0 0 0 1.6px ${checkboxColorVar}, 0 0 0 4px ${focusColorVar};
94
+ }
78
95
  }
79
96
 
80
97
  .${smallHtmlStylesClass} & {
@@ -25,6 +25,7 @@ type Props = {
25
25
  children: ReactNode;
26
26
  badge?: ReactNode;
27
27
  keepChildren?: boolean;
28
+ className?: string;
28
29
  };
29
30
 
30
31
  export const CollapsibleSection = ({
@@ -35,6 +36,7 @@ export const CollapsibleSection = ({
35
36
  onCollapsedChange,
36
37
  children,
37
38
  badge,
39
+ className,
38
40
  }: Props) => {
39
41
  const [isCollapsed, setIsCollapsed] = useControllableState({
40
42
  value: collapsed,
@@ -71,7 +73,7 @@ export const CollapsibleSection = ({
71
73
  const regularHeader = label && <div className={headerCss}>{label}</div>;
72
74
 
73
75
  return (
74
- <div>
76
+ <div className={className}>
75
77
  {collapsible ? collapsibleHeader : regularHeader}
76
78
  <div style={{display: collapsible && isCollapsed ? "none" : "block"}}>{children}</div>
77
79
  </div>
@@ -2,7 +2,8 @@ import moment from "moment";
2
2
  import {useMemo} from "react";
3
3
  import {Select} from "../select";
4
4
  import {SelectControlSettingsProvider} from "../select/select-control-settings-context";
5
- import {timeFormat as defaultTimeFormat} from "../design-system";
5
+ import {timeFormat as defaultTimeFormat, space} from "../design-system";
6
+ import {useIsPhone} from "../use-is-phone";
6
7
 
7
8
  const step = 30;
8
9
 
@@ -26,6 +27,8 @@ export const AutoComplete = ({
26
27
  menuIsOpen: boolean;
27
28
  position: number;
28
29
  }) => {
30
+ const isPhone = useIsPhone();
31
+
29
32
  const options = useMemo(
30
33
  () =>
31
34
  Array.from({length: (24 * 60) / step}, (value, index) => {
@@ -61,12 +64,15 @@ export const AutoComplete = ({
61
64
  ...props,
62
65
  zIndex: 1000000,
63
66
  position: "absolute",
64
- width: "auto",
65
- top: position,
67
+ width: isPhone ? `calc(100vw - ${2 * space.s12}px)` : "auto",
68
+ top: isPhone ? space.s18 : position,
66
69
  margin: 0,
67
- left: 100,
70
+ left: isPhone ? -space.s12 : 100,
71
+ }),
72
+ menuList: (props) => ({
73
+ ...props,
74
+ width: isPhone ? "auto" : 100,
68
75
  }),
69
- menuList: (props) => ({...props, width: 100}),
70
76
  }}
71
77
  />
72
78
  </SelectControlSettingsProvider>
@@ -2,6 +2,7 @@ import {css} from "@linaria/core";
2
2
  import cx from "classnames";
3
3
  import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState} from "react";
4
4
  import {MonthChangeEventHandler} from "react-day-picker";
5
+ import {mobileRootSelector} from "../mobile-styles";
5
6
  import {AntTransparentInput} from "../antd/input";
6
7
  import {AntTransparentTextArea} from "../antd/input";
7
8
  import {inputOverrides} from "../antd/styles";
@@ -292,6 +293,9 @@ export const DateRangeInput = forwardRef<
292
293
  display: flex;
293
294
  margin-bottom: ${space.s12 + space.s6}px;
294
295
  padding-right: ${space.s12 - 2}px;
296
+ ${mobileRootSelector} & {
297
+ min-height: ${space.s44}px;
298
+ }
295
299
  `,
296
300
  {[selectedStyle]: selected, [disabledStyle]: disabled}
297
301
  )}
@@ -335,7 +339,7 @@ export const DateRangeInput = forwardRef<
335
339
  />
336
340
  </div>
337
341
  {allowClear && inputValue !== "" && (
338
- <IconButton onClick={onClear} size={"tiny"}>
342
+ <IconButton onClick={onClear} size={"tiny"} supportMobile>
339
343
  <RemoveIcon />
340
344
  </IconButton>
341
345
  )}
@@ -19,6 +19,7 @@ import {
19
19
  weekendClassName,
20
20
  } from "./styles";
21
21
  import {getWeekendsForMonth} from "./get-weekends-for-month";
22
+ import {mobileRootSelector} from "../mobile-styles";
22
23
 
23
24
  export const sortDates = ({from, to}: {from: Date; to: Date}) => {
24
25
  const [sortedFrom, sortedTo] = [from, to].sort((a, b) => a.getTime() - b.getTime());
@@ -108,19 +109,36 @@ export const DateRangePickerPopupContent = forwardRef<
108
109
  : null;
109
110
 
110
111
  const footer = duration ? (
111
- <div
112
- className={css`
113
- background-color: ${themeVars.inputDisabledBgColor};
114
- border-radius: ${border.radius6}px;
115
- padding: ${space.s4}px ${space.s8}px;
116
- ${textStyles.small}
117
- text-align: center;
118
- color: ${themeVars.disabledTextColor};
119
- margin-top: ${space.s12}px;
120
- `}
121
- >
122
- {duration}
123
- </div>
112
+ <>
113
+ <hr
114
+ className={css`
115
+ display: none;
116
+ margin: 0 ${-space.s12}px;
117
+ border: none;
118
+ border-bottom: ${themeVars.separators.opacity5};
119
+ ${mobileRootSelector} & {
120
+ display: block;
121
+ }
122
+ `}
123
+ />
124
+ <div
125
+ className={css`
126
+ background-color: ${themeVars.inputDisabledBgColor};
127
+ border-radius: ${border.radius6}px;
128
+ padding: ${space.s4}px ${space.s8}px;
129
+ ${textStyles.small}
130
+ text-align: center;
131
+ color: ${themeVars.disabledTextColor};
132
+ margin-top: ${space.s12}px;
133
+ ${mobileRootSelector} & {
134
+ ${textStyles.big}
135
+ color: ${themeVars.disabledTextColor};
136
+ }
137
+ `}
138
+ >
139
+ {duration}
140
+ </div>
141
+ </>
124
142
  ) : null;
125
143
 
126
144
  const placeholder = useRef<string>(`e.g. ${getRandomPlaceholder(showTime, true)}`);