@fibery/ui-kit 1.37.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 (194) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/config/jest/testFrameworkSetup.js +1 -1
  3. package/package.json +20 -16
  4. package/src/abbreviate-name.ts +42 -0
  5. package/src/actions-menu/actions-menu-item.tsx +32 -11
  6. package/src/actions-menu/actions-menu-props.tsx +5 -0
  7. package/src/actions-menu/actions-menu-sub-command-menu.tsx +7 -4
  8. package/src/actions-menu/actions-menu-sub-menu.tsx +9 -3
  9. package/src/actions-menu/actions-menu.tsx +28 -9
  10. package/src/actions-menu/context-actions-menu.tsx +22 -0
  11. package/src/actions-panel.tsx +12 -2
  12. package/src/animated-height-container.tsx +24 -5
  13. package/src/antd/ant-upload.tsx +1 -1
  14. package/src/antd/input.tsx +4 -0
  15. package/src/avatar.tsx +1 -27
  16. package/src/axis-header.tsx +136 -76
  17. package/src/box.tsx +1 -1
  18. package/src/breadcrumb.tsx +78 -0
  19. package/src/button/actions-button-compact.tsx +4 -1
  20. package/src/button/base-button.tsx +7 -3
  21. package/src/button/button-group.tsx +1 -1
  22. package/src/button/button.tsx +33 -1
  23. package/src/button/icon-button.tsx +10 -1
  24. package/src/button/select-button.tsx +4 -0
  25. package/src/card-container.tsx +10 -6
  26. package/src/checkbox.tsx +17 -0
  27. package/src/collapsible-section.tsx +3 -1
  28. package/src/color-picker/swatch.tsx +1 -0
  29. package/src/command-menu/index.tsx +3 -3
  30. package/src/context-menu/index.tsx +10 -1
  31. package/src/date-picker/autocomplete.tsx +11 -5
  32. package/src/date-picker/date-range-input.tsx +5 -1
  33. package/src/date-picker/date-range-picker-popup-content.tsx +31 -13
  34. package/src/date-picker/date-range-picker.tsx +6 -3
  35. package/src/date-picker/relative-date-picker.tsx +6 -2
  36. package/src/date-picker/single-date-input.tsx +5 -1
  37. package/src/date-picker/single-date-picker.tsx +8 -5
  38. package/src/date-picker/styles.ts +52 -26
  39. package/src/date-picker/types.ts +3 -1
  40. package/src/day-select/day-select.tsx +21 -75
  41. package/src/day-select/iso-week-day-select.tsx +1 -1
  42. package/src/day-select/mount-day-select.tsx +2 -2
  43. package/src/day-select/week-day-select.tsx +1 -5
  44. package/src/design-system.ts +45 -12
  45. package/src/dropdown-menu/index.tsx +19 -2
  46. package/src/emoji-picker/app-icon-picker.tsx +6 -4
  47. package/src/emoji-picker/emoji-picker-preview-footer.tsx +5 -1
  48. package/src/emoji-picker/emoji-picker.tsx +3 -0
  49. package/src/emoji-picker/emoji.tsx +5 -3
  50. package/src/emoji-picker/icon-emoji-picker.tsx +10 -1
  51. package/src/emoji-picker/primitives/emoji.tsx +49 -51
  52. package/src/emoji-picker/primitives/footer.tsx +5 -0
  53. package/src/emoji-picker/primitives/search.tsx +8 -0
  54. package/src/emoji-picker/primitives/skin-tone.tsx +12 -1
  55. package/src/emoji-picker/use-responsive-emoji-size.ts +11 -0
  56. package/src/emoji-picker/utils/emoji-set.ts +2 -1
  57. package/src/empty-state/empty-state.tsx +6 -0
  58. package/src/fibermoji-placeholder.tsx +1 -27
  59. package/src/field-icon.tsx +4 -1
  60. package/src/file-item-2.tsx +408 -0
  61. package/src/file-item.tsx +215 -47
  62. package/src/header-layout.tsx +11 -0
  63. package/src/html-styles.ts +21 -4
  64. package/src/icons/ast/ActivityFilled.ts +8 -0
  65. package/src/icons/ast/AiAssistantFilled.ts +8 -0
  66. package/src/icons/ast/Back.ts +1 -1
  67. package/src/icons/ast/CollapseAllHorizontal.ts +8 -0
  68. package/src/icons/ast/DateRange.ts +1 -1
  69. package/src/icons/ast/EntityCreateFilled.ts +8 -0
  70. package/src/icons/ast/ExpandAllHorizontal.ts +8 -0
  71. package/src/icons/ast/ExtensionFiles.ts +1 -1
  72. package/src/icons/ast/FileUpload.ts +1 -1
  73. package/src/icons/ast/GlobeUnpublish.ts +8 -0
  74. package/src/icons/ast/HomeFilled.ts +8 -0
  75. package/src/icons/ast/KeyboardHideDown.ts +8 -0
  76. package/src/icons/ast/LayoutMultiple.ts +8 -0
  77. package/src/icons/ast/RicheditorHistoryRedo.ts +1 -1
  78. package/src/icons/ast/RicheditorHistoryUndo.ts +1 -1
  79. package/src/icons/ast/SearchFilled.ts +8 -0
  80. package/src/icons/ast/SettingsFilled.ts +8 -0
  81. package/src/icons/ast/SlideMenuFilled.ts +8 -0
  82. package/src/icons/ast/SwitchToMention.ts +8 -0
  83. package/src/icons/ast/SwitchToText.ts +8 -0
  84. package/src/icons/ast/SwitchToWidget.ts +8 -0
  85. package/src/icons/ast/TypeDate.ts +1 -1
  86. package/src/icons/ast/ViewGallery.ts +8 -0
  87. package/src/icons/ast/index.tsx +16 -0
  88. package/src/icons/react/ActivityFilled.tsx +13 -0
  89. package/src/icons/react/AiAssistantFilled.tsx +13 -0
  90. package/src/icons/react/CollapseAllHorizontal.tsx +13 -0
  91. package/src/icons/react/EntityCreateFilled.tsx +13 -0
  92. package/src/icons/react/ExpandAllHorizontal.tsx +13 -0
  93. package/src/icons/react/GlobeUnpublish.tsx +13 -0
  94. package/src/icons/react/HomeFilled.tsx +13 -0
  95. package/src/icons/react/KeyboardHideDown.tsx +13 -0
  96. package/src/icons/react/LayoutMultiple.tsx +13 -0
  97. package/src/icons/react/SearchFilled.tsx +13 -0
  98. package/src/icons/react/SettingsFilled.tsx +13 -0
  99. package/src/icons/react/SlideMenuFilled.tsx +13 -0
  100. package/src/icons/react/SwitchToMention.tsx +13 -0
  101. package/src/icons/react/SwitchToText.tsx +13 -0
  102. package/src/icons/react/SwitchToWidget.tsx +13 -0
  103. package/src/icons/react/ViewGallery.tsx +13 -0
  104. package/src/icons/react/index.tsx +16 -0
  105. package/src/icons/svg/activity-filled.svg +4 -0
  106. package/src/icons/svg/ai-assistant-filled.svg +3 -0
  107. package/src/icons/svg/back.svg +3 -1
  108. package/src/icons/svg/collapse-all-horizontal.svg +3 -0
  109. package/src/icons/svg/date-range.svg +3 -3
  110. package/src/icons/svg/entity-create-filled.svg +4 -0
  111. package/src/icons/svg/expand-all-horizontal.svg +3 -0
  112. package/src/icons/svg/extension/files.svg +3 -1
  113. package/src/icons/svg/file-upload.svg +3 -6
  114. package/src/icons/svg/globe-unpublish.svg +4 -0
  115. package/src/icons/svg/home-filled.svg +3 -0
  116. package/src/icons/svg/keyboard-hide-down.svg +1 -0
  117. package/src/icons/svg/layout-multiple.svg +3 -0
  118. package/src/icons/svg/richeditor/history/redo.svg +3 -2
  119. package/src/icons/svg/richeditor/history/undo.svg +3 -2
  120. package/src/icons/svg/search-filled.svg +3 -0
  121. package/src/icons/svg/settings-filled.svg +3 -0
  122. package/src/icons/svg/slide-menu-filled.svg +1 -0
  123. package/src/icons/svg/switch-to-mention.svg +3 -0
  124. package/src/icons/svg/switch-to-text.svg +4 -0
  125. package/src/icons/svg/switch-to-widget.svg +4 -0
  126. package/src/icons/svg/type/date.svg +3 -2
  127. package/src/icons/svg/view-gallery.svg +4 -0
  128. package/src/images-gallery/images-gallery-fall-through-provider.tsx +11 -0
  129. package/src/images-gallery/images-gallery.tsx +257 -0
  130. package/src/images-gallery/zoom.tsx +123 -0
  131. package/src/images-gallery-2/images-gallery-2.tsx +442 -0
  132. package/src/images-gallery-2/slide-buttons.tsx +230 -0
  133. package/src/images-gallery-2/zoom.tsx +35 -0
  134. package/src/is-in-popup.ts +1 -1
  135. package/src/kbd.tsx +46 -0
  136. package/src/layout-styles.ts +21 -1
  137. package/src/lists/actions-menu-row-surface.tsx +3 -1
  138. package/src/lists/list-row-surface.tsx +7 -1
  139. package/src/mobile-keyboard-aware-popup.tsx +87 -0
  140. package/src/mobile-styles.ts +8 -0
  141. package/src/modal-menu/contexts/modal-menu-context.tsx +22 -0
  142. package/src/modal-menu/contexts/modal-submenu-context.tsx +16 -0
  143. package/src/modal-menu/index.tsx +14 -0
  144. package/src/modal-menu/modal-menu-content.tsx +68 -0
  145. package/src/modal-menu/modal-menu-group.tsx +10 -0
  146. package/src/modal-menu/modal-menu-item.tsx +45 -0
  147. package/src/modal-menu/modal-menu-label.tsx +24 -0
  148. package/src/modal-menu/modal-menu-root.tsx +57 -0
  149. package/src/modal-menu/modal-menu-separator.tsx +14 -0
  150. package/src/modal-menu/modal-menu-trigger.tsx +54 -0
  151. package/src/modal-menu/modal-submenu-content.tsx +49 -0
  152. package/src/modal-menu/modal-submenu-trigger.tsx +34 -0
  153. package/src/modal-menu/modal-submenu.tsx +17 -0
  154. package/src/notification-dot.tsx +16 -7
  155. package/src/online-users.tsx +3 -3
  156. package/src/palette.ts +1 -1
  157. package/src/platform-symbols.ts +9 -0
  158. package/src/platform.ts +2 -0
  159. package/src/popover/index.tsx +710 -0
  160. package/src/popover/mobile-popup-context.tsx +29 -0
  161. package/src/{popup → popover}/modifiers.tsx +25 -25
  162. package/src/{popup → popover}/popup-modifiers-context.ts +3 -3
  163. package/src/reactions/reaction-button.tsx +342 -135
  164. package/src/reactions/reaction-picker.tsx +51 -44
  165. package/src/reactions/reactions.tsx +11 -1
  166. package/src/reactions/tootltip.tsx +55 -0
  167. package/src/rich-text-embed-error.tsx +2 -1
  168. package/src/select/components/group-heading.tsx +8 -0
  169. package/src/select/components/menu-list-virtualized.tsx +6 -2
  170. package/src/select/components/option.tsx +4 -0
  171. package/src/select/index.tsx +8 -4
  172. package/src/select/select-control-settings-context.tsx +11 -2
  173. package/src/select/select-in-popover.tsx +30 -39
  174. package/src/select/styles.ts +21 -1
  175. package/src/tab-nav/tab-nav.tsx +36 -10
  176. package/src/toast/primitives.tsx +8 -1
  177. package/src/toggle-button/round-toggle-button.tsx +23 -0
  178. package/src/toggle-button/toggle-button-group.tsx +34 -0
  179. package/src/toggle-button/toggle-button.tsx +164 -0
  180. package/src/toggle.tsx +11 -3
  181. package/src/tooltip.tsx +29 -20
  182. package/src/type-badge.tsx +5 -2
  183. package/src/unit/styles.ts +2 -2
  184. package/src/unit/unit-with-tooltip.tsx +1 -1
  185. package/src/use-is-phone.tsx +50 -4
  186. package/src/use-is-support-hover.ts +1 -1
  187. package/src/use-on-screen-keyboard-data.tsx +66 -0
  188. package/src/workflow-progress-icon.tsx +16 -22
  189. package/src/popup/index.tsx +0 -383
  190. package/src/shortcut-badge.tsx +0 -29
  191. /package/src/{popup → popover}/popup-stack-context.tsx +0 -0
  192. /package/src/{popup → popover}/reference.ts +0 -0
  193. /package/src/{popup → popover}/styles.ts +0 -0
  194. /package/src/{popup → popover}/use-click-outside.ts +0 -0
@@ -14,6 +14,7 @@ import {
14
14
  import {forwardRef, ReactNode, RefObject, useLayoutEffect, useRef} from "react";
15
15
  import {border, fontWeight, space, textStyles, themeVars} from "../design-system";
16
16
  import {ellipsis} from "../styles-utils";
17
+ import {mobileRootSelector} from "../mobile-styles";
17
18
  import {TooltipIfOverflown} from "../tooltip-if-overflown";
18
19
  import {useCallbackRef} from "@fibery/react/src/use-callback-ref";
19
20
  import {useResizeObserver} from "@fibery/react/src/use-resize-observer";
@@ -35,7 +36,11 @@ const tabNavItem = css`
35
36
  list-style: none;
36
37
  `;
37
38
 
38
- const tabButton = css`
39
+ const tabNavItemWide = css`
40
+ flex: 1 0 0;
41
+ `;
42
+
43
+ export const tabButton = css`
39
44
  all: unset;
40
45
  display: flex;
41
46
  align-items: center;
@@ -54,23 +59,40 @@ const tabButton = css`
54
59
  font-weight: ${fontWeight.medium};
55
60
  line-height: 16px;
56
61
 
57
- &:hover,
58
- &:focus-visible {
59
- background-color: ${themeVars.colorBgTabHover};
60
- color: ${themeVars.textColor};
62
+ @media (any-hover: hover) {
63
+ &:hover {
64
+ background-color: ${themeVars.colorBgTabHover};
65
+ color: ${themeVars.textColor};
66
+ }
61
67
  }
62
68
 
63
69
  &:focus-visible {
70
+ color: ${themeVars.textColor};
71
+ background-color: ${themeVars.colorBgTabHover};
64
72
  outline: 2px solid ${themeVars.colorBorderNeutralFocusRing};
65
73
  }
66
74
 
67
75
  &[data-active] {
68
76
  color: ${themeVars.textColor};
69
77
  }
78
+
79
+ .${tabNavItemWide} & {
80
+ justify-content: center;
81
+ text-align: center;
82
+ }
83
+
84
+ ${mobileRootSelector} & {
85
+ padding: ${space.s4}px ${space.s8}px ${space.s8}px ${space.s4}px;
86
+ font-size: ${textStyles.regular.fontSize}px;
87
+ }
70
88
  `;
71
89
 
72
90
  const tabNavButtonText = css`
73
91
  padding: 0 ${space.s4}px;
92
+
93
+ ${mobileRootSelector} & {
94
+ padding: 0 ${space.s6}px;
95
+ }
74
96
  `;
75
97
 
76
98
  const tabNavIndicatorTrack = css`
@@ -145,15 +167,15 @@ const useTabNavIndicator = ({active, itemRef}: {active?: boolean; itemRef: RefOb
145
167
  useResizeObserver(itemRef, () => updateIndicator());
146
168
  };
147
169
 
148
- export const TabNavItem = forwardRef<HTMLLIElement, NavigationMenuItemProps & {active?: boolean}>(
149
- ({className, children, active, ...props}, forwardedRef) => {
170
+ export const TabNavItem = forwardRef<HTMLLIElement, NavigationMenuItemProps & {active?: boolean; wide?: boolean}>(
171
+ ({className, children, active, wide, ...props}, forwardedRef) => {
150
172
  const itemRef = useRef(null);
151
173
  const ref = useComposedRefs(itemRef, forwardedRef);
152
174
 
153
175
  useTabNavIndicator({active, itemRef});
154
176
 
155
177
  return (
156
- <NavigationMenuItem ref={ref} className={cx(tabNavItem, className)} {...props}>
178
+ <NavigationMenuItem ref={ref} className={cx(tabNavItem, wide && tabNavItemWide, className)} {...props}>
157
179
  {children}
158
180
  </NavigationMenuItem>
159
181
  );
@@ -165,9 +187,13 @@ export const TabNavLink = forwardRef<
165
187
  Omit<NavigationMenuLinkProps, "asChild"> & {
166
188
  before?: ReactNode;
167
189
  after?: ReactNode;
190
+ wide?: boolean;
168
191
  itemClassName?: string;
169
192
  }
170
- >(function TabNavLink({children, className, itemClassName, active, before, after, href, ...linkProps}, forwardedRef) {
193
+ >(function TabNavLink(
194
+ {children, className, itemClassName, active, wide, before, after, href, ...linkProps},
195
+ forwardedRef
196
+ ) {
171
197
  const renderAsButton = typeof href === "undefined";
172
198
 
173
199
  const Component = renderAsButton ? "button" : "a";
@@ -178,7 +204,7 @@ export const TabNavLink = forwardRef<
178
204
  return (
179
205
  <TooltipIfOverflown title={children}>
180
206
  {({setNode}) => (
181
- <TabNavItem className={itemClassName} active={active}>
207
+ <TabNavItem className={itemClassName} active={active} wide={wide}>
182
208
  <NavigationMenuLink
183
209
  ref={forwardedRef}
184
210
  className={cx(tabButton, className)}
@@ -5,6 +5,12 @@ import {ElementRef, forwardRef} from "react";
5
5
  import {IconButton} from "../button/icon-button";
6
6
  import {border, space, textStyles, themeVars, transition} from "../design-system";
7
7
  import CloseIcon from "../icons/react/Close";
8
+ import {
9
+ safeAreaInsetBottomVar,
10
+ safeAreaInsetLeftVar,
11
+ safeAreaInsetRightVar,
12
+ safeAreaInsetTopVar,
13
+ } from "../mobile-styles";
8
14
 
9
15
  const viewportCss = css`
10
16
  position: fixed;
@@ -17,7 +23,8 @@ const viewportCss = css`
17
23
  gap: ${space.s16}px;
18
24
  margin: 0;
19
25
  z-index: 1010; // inherited from old toast implementation
20
- padding: 0;
26
+ padding: var(${safeAreaInsetTopVar}) var(${safeAreaInsetRightVar}) var(${safeAreaInsetBottomVar})
27
+ var(${safeAreaInsetLeftVar});
21
28
  list-style: none;
22
29
 
23
30
  transition: bottom ${transition}; // bottom may change dynamically
@@ -0,0 +1,23 @@
1
+ import {ComponentRef, forwardRef} from "react";
2
+ import {ToggleButton, ToggleButtonProps} from "./toggle-button";
3
+ import {css, cx} from "@linaria/core";
4
+
5
+ const roundToggleButtonCss = css`
6
+ &:is(&) {
7
+ border-radius: 100%;
8
+ width: 28px;
9
+ height: 28px;
10
+ padding: 0;
11
+ justify-content: center;
12
+ }
13
+ `;
14
+
15
+ export const RoundToggleButton = forwardRef<ComponentRef<typeof ToggleButton>, Omit<ToggleButtonProps, "iconStart">>(
16
+ function RoundToggleButton({children, className, ...props}, ref) {
17
+ return (
18
+ <ToggleButton ref={ref} className={cx(roundToggleButtonCss, className)} {...props}>
19
+ {children}
20
+ </ToggleButton>
21
+ );
22
+ }
23
+ );
@@ -0,0 +1,34 @@
1
+ import {css, cx} from "@linaria/core";
2
+ import {
3
+ ToggleGroup,
4
+ ToggleGroupItem,
5
+ ToggleGroupItemProps,
6
+ ToggleGroupMultipleProps,
7
+ ToggleGroupSingleProps,
8
+ } from "@radix-ui/react-toggle-group";
9
+ import {ComponentRef, forwardRef} from "react";
10
+ import {ToggleButton, ToggleButtonProps} from "./toggle-button";
11
+ import {space} from "../design-system";
12
+
13
+ export const toggleButtonGroupCss = css`
14
+ display: flex;
15
+ flex-direction: row;
16
+ `;
17
+
18
+ export const ToggleButtonGroup = forwardRef<
19
+ ComponentRef<typeof ToggleGroup>,
20
+ (ToggleGroupSingleProps | ToggleGroupMultipleProps) & {gap?: number}
21
+ >(function ToggleButtonGroup({className, gap = space.s4, ...props}, ref) {
22
+ return <ToggleGroup ref={ref} style={{gap}} className={cx(toggleButtonGroupCss, className)} {...props} />;
23
+ });
24
+
25
+ export const ToggleButtonGroupItem = forwardRef<
26
+ ComponentRef<typeof ToggleGroupItem>,
27
+ ToggleGroupItemProps & ToggleButtonProps
28
+ >(function ToggleButtonGroupItem({className, children, asChild, ...props}, ref) {
29
+ return (
30
+ <ToggleGroupItem ref={ref} className={className} {...props} asChild>
31
+ {asChild ? children : <ToggleButton>{children}</ToggleButton>}
32
+ </ToggleGroupItem>
33
+ );
34
+ });
@@ -0,0 +1,164 @@
1
+ import {css, cx} from "@linaria/core";
2
+ import {Toggle, ToggleProps} from "@radix-ui/react-toggle";
3
+ import {forwardRef} from "react";
4
+ import {border, space, textStyles, themeVars} from "../design-system";
5
+ import {iconColorVar, iconSizeVar} from "../icons/Icon";
6
+
7
+ export const toggleButtonCss = css`
8
+ button:where(&) {
9
+ all: unset;
10
+ }
11
+
12
+ padding-inline: ${space.s6}px;
13
+ box-sizing: border-box;
14
+ display: flex;
15
+ align-items: center;
16
+ min-height: 24px;
17
+ font-size: ${textStyles.small.fontSize}px;
18
+ border-radius: ${border.radius8}px;
19
+
20
+ transition-property: border, background-color, color, opacity, box-shadow;
21
+ transition-duration: 0.15s;
22
+
23
+ color: ${themeVars.textColor};
24
+ ${iconColorVar}: ${themeVars.colorIconButtonGhostNeutral};
25
+
26
+ ${iconSizeVar}: 16px;
27
+
28
+ border: 1.5px solid ${themeVars.colorBorderReactionsHover};
29
+ background-color: ${themeVars.colorBgReactionsDefault};
30
+
31
+ &[aria-readonly="true"] {
32
+ background-color: ${themeVars.colorBgReactionsHover};
33
+ border-color: ${themeVars.colorBgReactionsHover};
34
+ }
35
+
36
+ cursor: pointer;
37
+ line-height: 16px;
38
+ user-select: none;
39
+
40
+ &[data-size="medium"] {
41
+ min-height: 28px;
42
+ }
43
+
44
+ &[data-size="large"] {
45
+ min-height: 32px;
46
+ padding-inline: 7px;
47
+ }
48
+
49
+ &:not(:disabled, [aria-readonly="true"]) {
50
+ &:hover {
51
+ background-color: ${themeVars.colorBgReactionsHover};
52
+ border-color: ${themeVars.colorBorderReactionsHover};
53
+ }
54
+
55
+ &:is(:active, [aria-expanded="true"]) {
56
+ background-color: ${themeVars.colorBgReactionsHover};
57
+ border-color: ${themeVars.colorBorderReactionsHover};
58
+ }
59
+ }
60
+
61
+ &:disabled,
62
+ &[aria-readonly="true"] {
63
+ cursor: default;
64
+ }
65
+
66
+ &:focus {
67
+ outline: none;
68
+ }
69
+
70
+ &:focus-visible {
71
+ background-color: ${themeVars.colorBgReactionsHover};
72
+ outline: 2px solid ${themeVars.colorBorderReactionsHover};
73
+ }
74
+
75
+ &:is([aria-pressed="true"], [aria-checked="true"]) {
76
+ color: ${themeVars.colorTextReactionsSelected};
77
+ ${iconColorVar}: ${themeVars.colorIconButtonGhostAccent};
78
+ background-color: ${themeVars.colorBgReactionsSelectedDefault};
79
+ border-color: ${themeVars.colorBorderReactionsSelectedDefault};
80
+
81
+ &[aria-readonly="true"] {
82
+ background-color: ${themeVars.colorBgReactionsSelectedDefault};
83
+ border-color: ${themeVars.colorBgReactionsSelectedDefault};
84
+ }
85
+
86
+ &:not(:disabled, [aria-readonly="true"]) {
87
+ &:hover {
88
+ background-color: ${themeVars.colorBgReactionsSelectedHover};
89
+ border-color: ${themeVars.colorBorderReactionsSelectedHover};
90
+ }
91
+
92
+ &:active {
93
+ background-color: ${themeVars.colorBgReactionsSelectedDefault};
94
+ border-color: ${themeVars.colorBorderReactionsSelectedHover};
95
+ }
96
+ }
97
+ }
98
+ `;
99
+
100
+ export const toggleButtonCssWithIconStart = css`
101
+ padding-left: ${space.s4}px;
102
+ `;
103
+
104
+ export const toggleButtonContent = css`
105
+ display: flex;
106
+ align-items: center;
107
+ gap: ${space.s4}px;
108
+ height: 100%;
109
+
110
+ transition-property: opacity;
111
+ transition-duration: 0.15s;
112
+
113
+ .${toggleButtonCss}:is(:active, [aria-expanded="true"]) > & {
114
+ opacity: 0.8;
115
+ }
116
+
117
+ .${toggleButtonCss}:disabled > & {
118
+ opacity: 0.6;
119
+ }
120
+
121
+ overflow: hidden;
122
+ `;
123
+
124
+ export type ToggleButtonVariant = "outline";
125
+ export type ToggleButtonSize = "small" | "medium" | "large";
126
+
127
+ export type ToggleButtonProps = {
128
+ iconStart?: React.ReactNode;
129
+ variant?: ToggleButtonVariant;
130
+ size?: ToggleButtonSize;
131
+ readOnly?: boolean;
132
+ } & Omit<ToggleProps, "asChild">;
133
+
134
+ export const ToggleButton = forwardRef<HTMLButtonElement, ToggleButtonProps>(
135
+ (
136
+ {
137
+ className,
138
+ iconStart,
139
+ size = "small",
140
+ variant = "outline",
141
+ children,
142
+ "aria-readonly": ariaReadonly,
143
+ readOnly = ariaReadonly,
144
+ ...props
145
+ },
146
+ ref
147
+ ): JSX.Element => {
148
+ return (
149
+ <Toggle
150
+ ref={ref}
151
+ className={cx(toggleButtonCss, iconStart && toggleButtonCssWithIconStart, className)}
152
+ data-variant={variant}
153
+ data-size={size}
154
+ aria-readonly={readOnly}
155
+ {...props}
156
+ >
157
+ <div className={toggleButtonContent}>
158
+ {iconStart}
159
+ {children}
160
+ </div>
161
+ </Toggle>
162
+ );
163
+ }
164
+ );
package/src/toggle.tsx CHANGED
@@ -2,9 +2,10 @@ import {css, cx} from "@linaria/core";
2
2
  import {styled} from "@linaria/react";
3
3
  import chroma from "chroma-js";
4
4
  import {ComponentProps, FC, ReactNode} from "react";
5
- import {layout, space, textStyles, transition} from "./design-system";
5
+ import {space, textStyles, transition} from "./design-system";
6
6
  import _ from "lodash";
7
7
  import SpinnerIcon from "./icons/react/Spinner";
8
+ import {mobileRootSelector} from "./mobile-styles";
8
9
 
9
10
  const toggleIconHeightVar = "--fibery-toggle-icon-height";
10
11
  const toggleIconWidthVar = "--fibery-toggle-icon-width";
@@ -26,15 +27,16 @@ const Input = styled.input<{outlineColor: string}>`
26
27
  }
27
28
 
28
29
  &:focus-visible {
29
- box-shadow: ${({outlineColor}) => `inset 0 0 0 1px ${outlineColor}`};
30
+ outline: ${({outlineColor}) => `2px solid ${outlineColor}`};
31
+ outline-offset: 1px;
30
32
  }
31
33
  `;
32
34
 
33
35
  const Label = styled.div<{labelPosition: "last" | "first"; disabled?: boolean}>`
36
+ display: flex;
34
37
  margin-left: ${({labelPosition}) => (labelPosition === "last" ? `${space.s8}px` : 0)};
35
38
  margin-right: ${({labelPosition}) => (labelPosition === "first" ? `${space.s8}px` : 0)};
36
39
  opacity: ${({disabled}) => (disabled ? 0.5 : 1)};
37
- line-height: ${layout.checkboxSize}px;
38
40
  width: 100%;
39
41
  min-width: 0;
40
42
  `;
@@ -94,10 +96,16 @@ const toggleSize = {
94
96
  large: css`
95
97
  ${textStyles.regular}
96
98
  ${toggleIconHeightVar}: 18px;
99
+ ${mobileRootSelector} & {
100
+ ${textStyles.big}
101
+ }
97
102
  `,
98
103
  "extra-large": css`
99
104
  ${textStyles.regular}
100
105
  ${toggleIconHeightVar}: 20px;
106
+ ${mobileRootSelector} & {
107
+ ${textStyles.big}
108
+ }
101
109
  `,
102
110
  };
103
111
  const toggleSizeStyle = css`
package/src/tooltip.tsx CHANGED
@@ -2,7 +2,6 @@ import {css, cx} from "@linaria/core";
2
2
  import * as TooltipPrimitive from "@radix-ui/react-tooltip";
3
3
  import {forwardRef, type ReactNode, type SyntheticEvent} from "react";
4
4
  import {border, fontWeight, lineHeight, space, themeVars, tooltipDelay} from "./design-system";
5
- import {ShortcutBadge} from "./shortcut-badge";
6
5
 
7
6
  const isFlagPresent = (flag: string) => new RegExp(`[?&]${flag}\\b`).test(window.location.search);
8
7
 
@@ -24,10 +23,10 @@ const descriptionStyle = css`
24
23
  `;
25
24
 
26
25
  export const tooltipStyle = css`
27
- font-size: 13px;
26
+ font-size: 12px;
28
27
  color: ${themeVars.whiteColor};
29
28
  z-index: 100000;
30
- min-height: 30px;
29
+ min-height: 28px;
31
30
  max-width: 300px;
32
31
  background-color: ${themeVars.tooltipBgColor};
33
32
  padding: ${space.s6}px ${space.s8}px;
@@ -76,27 +75,37 @@ const whiteBgStyle = css`
76
75
  `;
77
76
 
78
77
  export const TooltipTitle = ({children}: {children: ReactNode}) => <div className={titleStyle}>{children}</div>;
78
+
79
79
  export const TooltipDescription = ({children}: {children: ReactNode}) => (
80
80
  <div className={descriptionStyle}>{children}</div>
81
81
  );
82
82
 
83
- export const TooltipContent = ({title, shortcut}: {title: string; shortcut?: string}) => (
84
- <div
85
- className={css`
86
- display: flex;
87
- flex-direction: column;
88
- align-items: center;
89
- gap: ${space.s4}px;
90
- `}
91
- >
92
- <TooltipTitle>{title}</TooltipTitle>
93
- {shortcut ? (
94
- <TooltipDescription>
95
- <ShortcutBadge shortcut={shortcut} />
96
- </TooltipDescription>
97
- ) : null}
98
- </div>
99
- );
83
+ export const TooltipContent = ({
84
+ direction = "horizontal",
85
+ className,
86
+ ...rest
87
+ }: {direction?: "horizontal" | "vertical"} & React.ComponentProps<"div">) => {
88
+ return (
89
+ <div
90
+ className={cx(
91
+ className,
92
+ direction === "horizontal"
93
+ ? css`
94
+ display: flex;
95
+ align-items: center;
96
+ gap: ${space.s8}px;
97
+ `
98
+ : css`
99
+ display: flex;
100
+ flex-direction: column;
101
+ align-items: center;
102
+ gap: ${space.s4}px;
103
+ `
104
+ )}
105
+ {...rest}
106
+ />
107
+ );
108
+ };
100
109
 
101
110
  export const TooltipProvider: React.FC<TooltipPrimitive.TooltipProviderProps> = (props) => (
102
111
  <TooltipPrimitive.Provider disableHoverableContent skipDelayDuration={150} {...props} />
@@ -150,6 +150,7 @@ type TypeBadgeProps = {
150
150
  tooltip?: ReactNode;
151
151
  faded?: boolean;
152
152
  big?: boolean;
153
+ style?: CSSProperties;
153
154
  vertical?: boolean;
154
155
  className?: string;
155
156
  noTooltip?: boolean; // pass true to force disable tooltip for abbr
@@ -173,6 +174,7 @@ export const TypeBadge = memo(
173
174
  vertical = false,
174
175
  className,
175
176
  noTooltip,
177
+ style,
176
178
  },
177
179
  ref
178
180
  ) {
@@ -206,7 +208,8 @@ export const TypeBadge = memo(
206
208
  light2: baseColor,
207
209
  });
208
210
 
209
- const style = {
211
+ const resultingStyle = {
212
+ ...style,
210
213
  [colorProp]: baseColor ? textBadgeColor : colors.inversedTextColor,
211
214
  //[colorProp]: baseColor ? getTextColor(baseColor) : colors.inversedTextColor,
212
215
  [colorBackgroundProp]: backgroundBadgeColor || colors.shades.opacity25,
@@ -220,7 +223,7 @@ export const TypeBadge = memo(
220
223
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
221
224
  // @ts-ignore
222
225
  ref={ref}
223
- style={style}
226
+ style={resultingStyle}
224
227
  className={cx(
225
228
  typeBadgeStyle,
226
229
  big ? bigTypeBadgeStaticStyle : compact ? compactTypeBadgeStaticStyle : normalTypeBadgeStaticStyle,
@@ -146,14 +146,14 @@ export const unitContentContainerClassName = css`
146
146
  `;
147
147
 
148
148
  export const unitDescriptionClassName = css`
149
- margin-bottom: ${space.s4}px;
149
+ margin-bottom: ${space.s1}px;
150
150
  `;
151
151
 
152
152
  export const fullUnitWrapperClassName = css`
153
153
  display: flex;
154
154
  align-items: center;
155
155
  padding: ${space.s6}px;
156
- border-radius: ${space.s4 + space.s1}px;
156
+ border-radius: ${space.s5}px;
157
157
  background-color: ${themeVars.cardBg};
158
158
  margin-bottom: ${space.s4}px;
159
159
  `;
@@ -31,7 +31,7 @@ export const UnitWithTooltip = ({children, title, description}: Props) => {
31
31
  // Had to use additional way to validate if we are still over unit
32
32
  if (isHoveredRef.current && unitElementRef.current && unitElementRef.current.matches(":hover")) {
33
33
  setShouldDisplayTooltip(true);
34
- const intervalId = setInterval(() => {
34
+ const intervalId = window.setInterval(() => {
35
35
  if (isHoveredRef.current && unitElementRef.current) {
36
36
  if (!unitElementRef.current.matches(":hover")) {
37
37
  isHoveredRef.current = false;
@@ -1,12 +1,58 @@
1
- import {useIsMediaQueryMatched} from "./use-is-media-query-matched";
1
+ import {ReactNode, useLayoutEffect} from "react";
2
2
  import {createContext} from "@fibery/react/src/create-context";
3
- import {ReactNode} from "react";
3
+ import {useIsMediaQueryMatched} from "./use-is-media-query-matched";
4
+ import {getMediaQueryList} from "./media-query-utils";
4
5
 
5
6
  const [Provider, useIsPhone] = createContext<boolean>("PhoneContext", false);
6
7
 
7
8
  export {useIsPhone};
8
9
 
10
+ declare global {
11
+ const __DISABLE_VERTICAL_MOBILE_BREAKPOINT__: boolean | undefined;
12
+ }
13
+
14
+ function isVerticalMobileBreakpointDisabled() {
15
+ // noinspection PointlessBooleanExpressionJS
16
+ if (
17
+ typeof __DISABLE_VERTICAL_MOBILE_BREAKPOINT__ !== "undefined" &&
18
+ __DISABLE_VERTICAL_MOBILE_BREAKPOINT__ === true
19
+ ) {
20
+ return true;
21
+ }
22
+ const urlParams = new URLSearchParams(window.location.search);
23
+ return urlParams.has("no-vertical-mobile-breakpoint");
24
+ }
25
+
26
+ export const isPhoneApp = Boolean(
27
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
28
+ // @ts-ignore - injected by consumer app
29
+ window.__FIBERY_MOBILE_APP_API
30
+ );
31
+
32
+ export const phoneMediaQuery = isVerticalMobileBreakpointDisabled()
33
+ ? "(max-width: 450px)"
34
+ : "(max-width: 450px), (max-height: 450px)";
35
+
36
+ export function isPhoneMode() {
37
+ return isPhoneApp || getMediaQueryList(phoneMediaQuery).matches;
38
+ }
39
+
9
40
  export function IsPhoneProvider({children}: {children: ReactNode}) {
10
- const isPhone = useIsMediaQueryMatched("(max-width: 440px), (max-height: 440px)");
11
- return <Provider value={isPhone}>{children}</Provider>;
41
+ const isPhone = useIsMediaQueryMatched(phoneMediaQuery);
42
+ useLayoutEffect(() => {
43
+ const htmlElement = document.querySelector("html");
44
+ if (!htmlElement || isPhoneApp) {
45
+ return;
46
+ }
47
+ if (isPhone) {
48
+ htmlElement.classList.add("fibery-mobile");
49
+ } else {
50
+ htmlElement.classList.remove("fibery-mobile");
51
+ }
52
+ // eslint-disable-next-line consistent-return
53
+ return () => {
54
+ htmlElement.classList.remove("fibery-mobile");
55
+ };
56
+ }, [isPhone]);
57
+ return <Provider value={isPhoneApp || isPhone}>{children}</Provider>;
12
58
  }
@@ -1,5 +1,5 @@
1
1
  import {useIsMediaQueryMatched} from "./use-is-media-query-matched";
2
2
 
3
3
  export function useIsSupportHover() {
4
- return !useIsMediaQueryMatched("(any-hover: none)");
4
+ return useIsMediaQueryMatched("(any-hover: hover)");
5
5
  }
@@ -0,0 +1,66 @@
1
+ import {ReactNode, useEffect, useState} from "react";
2
+ import {createContext} from "@fibery/react/src/create-context";
3
+ import _ from "lodash";
4
+ import {useIsPhone} from "./use-is-phone";
5
+ import {keyboardHeightVar, mobileKeyboardOpenClassName} from "./mobile-styles";
6
+
7
+ const defaultKeyboardHeight = 350;
8
+
9
+ const [OnScreenKeyboardHeightValueProvider, useOnScreenKeyboardHeight] = createContext<number>(
10
+ "OnScreenKeyboardHeightProvider",
11
+ defaultKeyboardHeight
12
+ );
13
+
14
+ const [IsOnScreenKeyboardOpenedValueProvider, useIsOnScreenKeyboardOpened] = createContext<boolean>(
15
+ "IsOnScreenKeyboardOpenedValueProvider",
16
+ false
17
+ );
18
+
19
+ export function OnScreenKeyboardContextProvider({children}: {children: ReactNode}) {
20
+ const isPhone = useIsPhone();
21
+ const [keyboardHeight, setKeyboardHeight] = useState<number>(defaultKeyboardHeight);
22
+ const [keyboardVisible, setKeyboardVisible] = useState<boolean>(false);
23
+ const [initialViewPortHeight] = useState(() => window.visualViewport?.height || 0);
24
+ useEffect(() => {
25
+ const viewport = window.visualViewport;
26
+ if (!viewport || !isPhone) {
27
+ return _.noop;
28
+ }
29
+
30
+ function viewportHandler() {
31
+ if (viewport) {
32
+ // console.log({height: viewport.height, initialViewPortHeight, windowHeight: window.innerHeight});
33
+ // Calculate keyboard height as difference between window height and viewport height
34
+ const calculatedHeight = Math.max(0, initialViewPortHeight - viewport.height);
35
+ if (calculatedHeight && keyboardHeight !== calculatedHeight && calculatedHeight > 200) {
36
+ setKeyboardHeight(calculatedHeight);
37
+ document.body.style.setProperty(keyboardHeightVar, `${calculatedHeight}px`);
38
+ }
39
+ if (viewport.height !== initialViewPortHeight && !keyboardVisible) {
40
+ const htmlElement = document.querySelector("html");
41
+ htmlElement?.classList.add(mobileKeyboardOpenClassName);
42
+ setKeyboardVisible(true);
43
+ } else if (viewport.height === initialViewPortHeight && keyboardVisible) {
44
+ setKeyboardVisible(false);
45
+ const htmlElement = document.querySelector("html");
46
+ htmlElement?.classList.remove(mobileKeyboardOpenClassName);
47
+ }
48
+ }
49
+ }
50
+
51
+ viewport.addEventListener("resize", viewportHandler);
52
+ // Initial calculation
53
+ viewportHandler();
54
+
55
+ return () => {
56
+ viewport.removeEventListener("resize", viewportHandler);
57
+ };
58
+ }, [initialViewPortHeight, isPhone, keyboardHeight, keyboardVisible]);
59
+ return (
60
+ <OnScreenKeyboardHeightValueProvider value={keyboardHeight}>
61
+ <IsOnScreenKeyboardOpenedValueProvider value={keyboardVisible}>{children}</IsOnScreenKeyboardOpenedValueProvider>
62
+ </OnScreenKeyboardHeightValueProvider>
63
+ );
64
+ }
65
+
66
+ export {useOnScreenKeyboardHeight, useIsOnScreenKeyboardOpened};