@fibery/ui-kit 1.37.0 → 1.38.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 (139) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/package.json +8 -5
  3. package/src/actions-menu/actions-menu-item.tsx +18 -6
  4. package/src/actions-menu/actions-menu-props.tsx +4 -0
  5. package/src/actions-menu/actions-menu-sub-command-menu.tsx +7 -4
  6. package/src/actions-menu/actions-menu-sub-menu.tsx +9 -3
  7. package/src/actions-menu/actions-menu.tsx +24 -8
  8. package/src/actions-panel.tsx +12 -2
  9. package/src/axis-header.tsx +136 -76
  10. package/src/box.tsx +1 -1
  11. package/src/breadcrumb.tsx +78 -0
  12. package/src/button/actions-button-compact.tsx +4 -1
  13. package/src/button/base-button.tsx +5 -3
  14. package/src/button/button-group.tsx +1 -1
  15. package/src/button/button.tsx +31 -0
  16. package/src/button/icon-button.tsx +9 -0
  17. package/src/button/select-button.tsx +4 -0
  18. package/src/card-container.tsx +7 -4
  19. package/src/color-picker/swatch.tsx +1 -0
  20. package/src/command-menu/index.tsx +3 -3
  21. package/src/context-menu/index.tsx +10 -1
  22. package/src/date-picker/date-range-picker.tsx +6 -3
  23. package/src/date-picker/relative-date-picker.tsx +6 -2
  24. package/src/date-picker/single-date-picker.tsx +8 -5
  25. package/src/date-picker/styles.ts +32 -26
  26. package/src/date-picker/types.ts +3 -1
  27. package/src/day-select/day-select.tsx +21 -75
  28. package/src/day-select/iso-week-day-select.tsx +1 -1
  29. package/src/day-select/mount-day-select.tsx +2 -2
  30. package/src/day-select/week-day-select.tsx +1 -5
  31. package/src/design-system.ts +21 -12
  32. package/src/dropdown-menu/index.tsx +8 -1
  33. package/src/emoji-picker/app-icon-picker.tsx +6 -4
  34. package/src/emoji-picker/emoji-picker-preview-footer.tsx +1 -1
  35. package/src/emoji-picker/emoji-picker.tsx +3 -0
  36. package/src/emoji-picker/emoji.tsx +2 -1
  37. package/src/emoji-picker/icon-emoji-picker.tsx +10 -1
  38. package/src/emoji-picker/primitives/footer.tsx +0 -1
  39. package/src/emoji-picker/primitives/skin-tone.tsx +1 -0
  40. package/src/emoji-picker/use-responsive-emoji-size.ts +11 -0
  41. package/src/emoji-picker/utils/emoji-set.ts +2 -1
  42. package/src/empty-state/empty-state.tsx +6 -0
  43. package/src/field-icon.tsx +4 -1
  44. package/src/file-item.tsx +164 -47
  45. package/src/html-styles.ts +8 -0
  46. package/src/icons/ast/ActivityFilled.ts +8 -0
  47. package/src/icons/ast/AiAssistantFilled.ts +8 -0
  48. package/src/icons/ast/CollapseAllHorizontal.ts +8 -0
  49. package/src/icons/ast/DateRange.ts +1 -1
  50. package/src/icons/ast/EntityCreateFilled.ts +8 -0
  51. package/src/icons/ast/ExpandAllHorizontal.ts +8 -0
  52. package/src/icons/ast/GlobeUnpublish.ts +8 -0
  53. package/src/icons/ast/HomeFilled.ts +8 -0
  54. package/src/icons/ast/KeyboardHideDown.ts +8 -0
  55. package/src/icons/ast/LayoutMultiple.ts +8 -0
  56. package/src/icons/ast/RicheditorHistoryRedo.ts +1 -1
  57. package/src/icons/ast/RicheditorHistoryUndo.ts +1 -1
  58. package/src/icons/ast/SearchFilled.ts +8 -0
  59. package/src/icons/ast/SettingsFilled.ts +8 -0
  60. package/src/icons/ast/SlideMenuFilled.ts +8 -0
  61. package/src/icons/ast/TypeDate.ts +1 -1
  62. package/src/icons/ast/index.tsx +12 -0
  63. package/src/icons/react/ActivityFilled.tsx +13 -0
  64. package/src/icons/react/AiAssistantFilled.tsx +13 -0
  65. package/src/icons/react/CollapseAllHorizontal.tsx +13 -0
  66. package/src/icons/react/EntityCreateFilled.tsx +13 -0
  67. package/src/icons/react/ExpandAllHorizontal.tsx +13 -0
  68. package/src/icons/react/GlobeUnpublish.tsx +13 -0
  69. package/src/icons/react/HomeFilled.tsx +13 -0
  70. package/src/icons/react/KeyboardHideDown.tsx +13 -0
  71. package/src/icons/react/LayoutMultiple.tsx +13 -0
  72. package/src/icons/react/SearchFilled.tsx +13 -0
  73. package/src/icons/react/SettingsFilled.tsx +13 -0
  74. package/src/icons/react/SlideMenuFilled.tsx +13 -0
  75. package/src/icons/react/index.tsx +12 -0
  76. package/src/icons/svg/activity-filled.svg +4 -0
  77. package/src/icons/svg/ai-assistant-filled.svg +3 -0
  78. package/src/icons/svg/collapse-all-horizontal.svg +3 -0
  79. package/src/icons/svg/date-range.svg +3 -3
  80. package/src/icons/svg/entity-create-filled.svg +4 -0
  81. package/src/icons/svg/expand-all-horizontal.svg +3 -0
  82. package/src/icons/svg/globe-unpublish.svg +4 -0
  83. package/src/icons/svg/home-filled.svg +3 -0
  84. package/src/icons/svg/keyboard-hide-down.svg +1 -0
  85. package/src/icons/svg/layout-multiple.svg +3 -0
  86. package/src/icons/svg/richeditor/history/redo.svg +3 -2
  87. package/src/icons/svg/richeditor/history/undo.svg +3 -2
  88. package/src/icons/svg/search-filled.svg +3 -0
  89. package/src/icons/svg/settings-filled.svg +3 -0
  90. package/src/icons/svg/slide-menu-filled.svg +1 -0
  91. package/src/icons/svg/type/date.svg +3 -2
  92. package/src/images-gallery/images-gallery.tsx +254 -0
  93. package/src/images-gallery/zoom.tsx +112 -0
  94. package/src/is-in-popup.ts +1 -1
  95. package/src/layout-styles.ts +21 -1
  96. package/src/lists/actions-menu-row-surface.tsx +3 -1
  97. package/src/lists/list-row-surface.tsx +7 -1
  98. package/src/mobile-keyboard-aware-popup.tsx +87 -0
  99. package/src/mobile-styles.ts +8 -0
  100. package/src/modal-menu/contexts/modal-menu-context.tsx +22 -0
  101. package/src/modal-menu/contexts/modal-submenu-context.tsx +16 -0
  102. package/src/modal-menu/index.tsx +14 -0
  103. package/src/modal-menu/modal-menu-content.tsx +68 -0
  104. package/src/modal-menu/modal-menu-group.tsx +10 -0
  105. package/src/modal-menu/modal-menu-item.tsx +37 -0
  106. package/src/modal-menu/modal-menu-label.tsx +24 -0
  107. package/src/modal-menu/modal-menu-root.tsx +57 -0
  108. package/src/modal-menu/modal-menu-separator.tsx +14 -0
  109. package/src/modal-menu/modal-menu-trigger.tsx +37 -0
  110. package/src/modal-menu/modal-submenu-content.tsx +49 -0
  111. package/src/modal-menu/modal-submenu-trigger.tsx +34 -0
  112. package/src/modal-menu/modal-submenu.tsx +17 -0
  113. package/src/online-users.tsx +3 -3
  114. package/src/palette.ts +1 -1
  115. package/src/popover/index.tsx +687 -0
  116. package/src/popover/mobile-popup-context.tsx +29 -0
  117. package/src/{popup → popover}/modifiers.tsx +0 -7
  118. package/src/{popup → popover}/popup-modifiers-context.ts +3 -3
  119. package/src/reactions/reaction-button.tsx +30 -45
  120. package/src/reactions/reaction-picker.tsx +39 -45
  121. package/src/select/index.tsx +8 -4
  122. package/src/select/select-in-popover.tsx +21 -33
  123. package/src/tab-nav/tab-nav.tsx +35 -9
  124. package/src/toast/primitives.tsx +8 -1
  125. package/src/toggle-button/round-toggle-button.tsx +23 -0
  126. package/src/toggle-button/toggle-button-group.tsx +34 -0
  127. package/src/toggle-button/toggle-button.tsx +164 -0
  128. package/src/toggle.tsx +3 -3
  129. package/src/tooltip.tsx +2 -2
  130. package/src/unit/styles.ts +2 -2
  131. package/src/use-is-phone.tsx +28 -3
  132. package/src/use-is-support-hover.ts +1 -1
  133. package/src/use-on-screen-keyboard-data.tsx +66 -0
  134. package/src/workflow-progress-icon.tsx +16 -22
  135. package/src/popup/index.tsx +0 -383
  136. /package/src/{popup → popover}/popup-stack-context.tsx +0 -0
  137. /package/src/{popup → popover}/reference.ts +0 -0
  138. /package/src/{popup → popover}/styles.ts +0 -0
  139. /package/src/{popup → popover}/use-click-outside.ts +0 -0
@@ -26,13 +26,6 @@ export const getMaxSize = (overflowOptions: Partial<PreventOverflowOptions>): Po
26
26
  },
27
27
  ];
28
28
 
29
- export const disableGpuAcceleration = {
30
- name: "computeStyles",
31
- options: {
32
- gpuAcceleration: false,
33
- },
34
- } as unknown as PopupModifier;
35
-
36
29
  const observeWidthEffect: (arg0: ModifierArguments<Record<string, unknown>>) => void = ({state, instance}) => {
37
30
  const {popper, reference} = state.elements;
38
31
 
@@ -2,11 +2,11 @@ import {createContext} from "@fibery/react/src/create-context";
2
2
  import {getPopupContainerElement} from "../utils-dom";
3
3
  import {$TSFixMe} from "../tsfixme";
4
4
  import {Modifier} from "react-popper";
5
- import {Props} from "./index";
5
+ import {PopoverOldProps} from "./index";
6
6
 
7
7
  export const [PopupModifiersProvider, usePopupModifiers] = createContext<{
8
8
  getPopupContainer: () => HTMLElement;
9
9
  additionalModifiers?: Array<Partial<Modifier<$TSFixMe, $TSFixMe>>>;
10
- preventOverflowOptions?: Props["preventOverflowOptions"];
11
- flipOptions?: Props["flipOptions"];
10
+ preventOverflowOptions?: PopoverOldProps["preventOverflowOptions"];
11
+ flipOptions?: PopoverOldProps["flipOptions"];
12
12
  }>("PopupModifiersProvider", {getPopupContainer: getPopupContainerElement});
@@ -1,11 +1,13 @@
1
1
  import {css, cx} from "@linaria/core";
2
- import {createPortal} from "react-dom";
3
- import {useLongPress} from "../use-long-press";
4
2
  import {Fragment, ReactNode, useState} from "react";
3
+ import {createPortal} from "react-dom";
4
+ import {border, lineHeight, space, themeVars} from "../design-system";
5
5
  import {Emoji} from "../emoji-picker/emoji";
6
- import {border, lineHeight, space, textStyles, themeVars} from "../design-system";
6
+ import {ToggleButton} from "../toggle-button/toggle-button";
7
7
  import {Tooltip} from "../tooltip";
8
8
  import {useIsPhone} from "../use-is-phone";
9
+ import {safeAreaInsetBottomVar} from "../mobile-styles";
10
+ import {useLongPress} from "../use-long-press";
9
11
  import {getWordDivider} from "./get-word-divider";
10
12
 
11
13
  export type Reaction = {
@@ -22,6 +24,7 @@ type Props = {
22
24
  onSelect: (reactionKey: string) => unknown;
23
25
  };
24
26
 
27
+ // @deprecated. remove it
25
28
  export const reactionButtonCss = css`
26
29
  all: unset;
27
30
  padding-inline: ${space.s6}px;
@@ -50,27 +53,8 @@ export const reactionButtonCss = css`
50
53
  }
51
54
  `;
52
55
 
53
- export const selectedReactionButtonCss = css`
54
- .${reactionButtonCss}& {
55
- &:not([aria-disabled="true"]) {
56
- background-color: ${themeVars.colorBgReactionsSelectedDefault};
57
- border-color: ${themeVars.colorBorderReactionsSelectedDefault};
58
- }
59
-
60
- &:not([aria-disabled="true"]):hover {
61
- background-color: ${themeVars.colorBgReactionsSelectedHover};
62
- border-color: ${themeVars.colorBorderReactionsSelectedHover};
63
- }
64
- }
65
- `;
66
-
67
- const countCss = css`
68
- ${textStyles.small};
69
- line-height: ${lineHeight.nowrap};
70
- padding: ${space.s2}px ${space.s2}px ${space.s2}px ${space.s4}px;
71
- .${selectedReactionButtonCss}:not(:disabled) & {
72
- color: ${themeVars.colorTextReactionsSelected};
73
- }
56
+ const reactionButtonCssV2 = css`
57
+ min-width: ${space.s24 + space.s12}px;
74
58
  `;
75
59
 
76
60
  const overlayClassName = css`
@@ -99,6 +83,7 @@ function MobileOverlay({content, onClose}: {content: ReactNode; onClose: () => v
99
83
  className={css`
100
84
  flex-shrink: 0;
101
85
  background-color: ${themeVars.tooltipBgColor};
86
+ padding-bottom: var(${safeAreaInsetBottomVar});
102
87
  `}
103
88
  >
104
89
  {content}
@@ -131,30 +116,30 @@ export const ReactionButton = ({reaction, disabled, onSelect}: Props): JSX.Eleme
131
116
  isPreventDefault: true,
132
117
  delay: 300,
133
118
  });
119
+
134
120
  const button = (
135
- <button
136
- type="button"
137
- aria-disabled={disabled}
138
- className={cx(reactionButtonCss, reaction.selected && selectedReactionButtonCss)}
139
- onClick={() => {
140
- if (!disabled) {
141
- onSelect(reaction.key);
142
- }
143
- }}
144
- {...longPressEvent}
121
+ <ToggleButton
122
+ className={reactionButtonCssV2}
123
+ disabled={disabled}
124
+ iconStart={
125
+ <div
126
+ className={css`
127
+ font-size: 16px;
128
+ height: 16px;
129
+ width: 16px;
130
+ `}
131
+ {...longPressEvent}
132
+ >
133
+ <Emoji emoji={reaction.key} />
134
+ </div>
135
+ }
136
+ onPressedChange={() => onSelect(reaction.key)}
137
+ pressed={reaction.selected}
145
138
  >
146
- <div
147
- className={css`
148
- font-size: 18px;
149
- height: 18px;
150
- width: 18px;
151
- `}
152
- >
153
- <Emoji emoji={reaction.key} />
154
- </div>
155
- <span className={countCss}>{reaction.count}</span>
156
- </button>
139
+ {reaction.count}
140
+ </ToggleButton>
157
141
  );
142
+
158
143
  if (reaction?.userNames?.length) {
159
144
  // noinspection CssInvalidPropertyValue
160
145
  const title = (
@@ -1,16 +1,15 @@
1
1
  import {EmojiItem} from "@fibery/emoji-data";
2
- import {measureScrollbar} from "@fibery/helpers/utils/measure-scrollbar";
3
2
  import {useControllableState} from "@fibery/react/src/use-controllable-state";
4
3
  import {css} from "@linaria/core";
5
4
  import {CSSProperties, type FC, useCallback, useState} from "react";
6
5
  import {IconButton} from "../button/icon-button";
7
6
  import {layout, space, themeVars} from "../design-system";
8
7
  import {type EmojiPickerProps} from "../emoji-picker/emoji-picker";
8
+ import {useResponsiveEmojiSize} from "../emoji-picker/use-responsive-emoji-size";
9
9
  import AddReactions from "../icons/react/AddReactions";
10
- import {Popup} from "../popup";
10
+ import {Popover} from "../popover";
11
+ import {ToggleButton} from "../toggle-button/toggle-button";
11
12
  import {Tooltip} from "../tooltip";
12
- import {useIsPhone} from "../use-is-phone";
13
- import {reactionButtonCss} from "./reaction-button";
14
13
 
15
14
  type Props = {
16
15
  mode: "default" | "in-row";
@@ -52,7 +51,7 @@ const pickerOffset: [number, number] = [0, space.s4];
52
51
  export const ReactionPicker = ({mode, onSelect, open, onOpenChange, EmojiPicker}: Props): JSX.Element => {
53
52
  const [isOpen = false, setOpen] = useControllableState({value: open, onChange: onOpenChange});
54
53
  const [triggerDomElement, setTriggerDomElement] = useState<HTMLButtonElement | null>(null);
55
- const isPhone = useIsPhone();
54
+ const emojiSize = useResponsiveEmojiSize();
56
55
  const onEmojiSelect = useCallback(
57
56
  (emojiData: EmojiItem) => {
58
57
  onSelect(emojiData.colons);
@@ -65,62 +64,57 @@ export const ReactionPicker = ({mode, onSelect, open, onOpenChange, EmojiPicker}
65
64
  <div>
66
65
  <Tooltip side="bottom" title="Add reaction">
67
66
  {mode === "in-row" ? (
68
- <button
67
+ <ToggleButton
69
68
  ref={setTriggerDomElement}
70
- type="button"
71
- onClick={() => {
72
- setOpen(!isOpen);
69
+ onClick={() => setOpen(!isOpen)}
70
+ aria-expanded={isOpen}
71
+ aria-pressed={undefined}
72
+ style={{
73
+ // TODO: add IconToggleButton? or a separate prop?
74
+ width: "24px",
75
+ justifyContent: "center",
76
+ paddingInline: 0,
73
77
  }}
74
- className={reactionButtonCss}
75
78
  >
76
- <AddReactions viewBox={"0 0 20 20"} containerSize={16} iconSize={20} color={themeVars.iconColor} />
77
- </button>
79
+ <AddReactions />
80
+ </ToggleButton>
78
81
  ) : (
79
82
  <IconButton
80
83
  ref={setTriggerDomElement}
84
+ aria-expanded={isOpen}
81
85
  onClick={() => {
82
86
  setOpen(!isOpen);
83
87
  }}
84
88
  size="small"
89
+ supportMobile
85
90
  >
86
91
  <AddReactions />
87
92
  </IconButton>
88
93
  )}
89
94
  </Tooltip>
90
- {triggerDomElement ? (
91
- <Popup
92
- supportMobile
93
- mobilePopupStyle={
94
- {
95
- "--fibery-emoji-container-width": "100%",
96
- "--fibery-emoji-grid-height": "auto",
97
- "--fibery-emoji-content-max-height": "auto",
98
- zIndex: zIndex,
99
- } as CSSProperties
100
- }
101
- title={"Add reaction"}
102
- placement="auto"
103
- offset={pickerOffset}
104
- popupContainerClassName={popupContainerCss}
105
- popupClassName={popupCss}
106
- open={isOpen}
107
- triggerDomElement={triggerDomElement}
108
- trigger={<></>}
109
- renderInPortal
110
- onOpen={() => setOpen(true)}
111
- onClose={() => setOpen(false)}
112
- >
113
- <EmojiPicker
114
- emojiSize={
115
- isPhone
116
- ? Math.min(Math.floor((document.body.clientWidth - 24 - measureScrollbar() - 9 * 12) / 9), 40)
117
- : 20
95
+ <Popover.Root open={isOpen} onOpen={() => setOpen(true)} onClose={() => setOpen(false)}>
96
+ <Popover.Trigger reference={triggerDomElement}></Popover.Trigger>
97
+ <Popover.Portal>
98
+ <Popover.Content
99
+ supportMobile
100
+ mobilePopupStyle={
101
+ {
102
+ "--fibery-emoji-container-width": "100%",
103
+ "--fibery-emoji-grid-height": "auto",
104
+ "--fibery-emoji-content-max-height": "auto",
105
+ zIndex: zIndex,
106
+ } as CSSProperties
118
107
  }
119
- showSkinTones
120
- onSelect={onEmojiSelect}
121
- />
122
- </Popup>
123
- ) : null}
108
+ title={"Add reaction"}
109
+ placement="auto"
110
+ offset={pickerOffset}
111
+ popupContainerClassName={popupContainerCss}
112
+ popupClassName={popupCss}
113
+ >
114
+ <EmojiPicker emojiSize={emojiSize} showSkinTones onSelect={onEmojiSelect} />
115
+ </Popover.Content>
116
+ </Popover.Portal>
117
+ </Popover.Root>
124
118
  </div>
125
119
  );
126
120
  };
@@ -7,6 +7,7 @@ import BaseSelect, {
7
7
  createFilter,
8
8
  MenuListProps,
9
9
  MenuProps,
10
+ ControlProps,
10
11
  mergeStyles,
11
12
  MultiValue,
12
13
  MultiValueProps,
@@ -44,6 +45,7 @@ export type {
44
45
  GroupBase,
45
46
  MenuListProps,
46
47
  MenuProps,
48
+ ControlProps,
47
49
  MultiValue,
48
50
  MultiValueProps,
49
51
  OnChangeValue,
@@ -53,6 +55,8 @@ export type {
53
55
  SingleValue,
54
56
  SingleValueProps,
55
57
  StylesConfig,
58
+ SelectInstance,
59
+ BaseSelectProps,
56
60
  };
57
61
 
58
62
  export {reactSelectComponents as components, MenuListVirtualized, createFilter};
@@ -207,8 +211,8 @@ export const Select = forwardRef(function Select<
207
211
  [onKeyDown, menuOpenState]
208
212
  );
209
213
 
210
- const contextRef = useRef<SelectInstance<T, U, V> | null>(null);
211
- const combined = useComposedRefs(forwardedRef, contextRef);
214
+ const selectRef = useRef<SelectInstance<T, U, V> | null>(null);
215
+ const composedRef = useComposedRefs(forwardedRef, selectRef);
212
216
 
213
217
  const selectComponents = useSelectComponents({
214
218
  components,
@@ -221,9 +225,9 @@ export const Select = forwardRef(function Select<
221
225
  const combinedStyles = useSelectStyles({virtualized, zIndex, styles});
222
226
 
223
227
  return (
224
- <ReactSelectRefContext.Provider value={contextRef as RefObject<SelectInstance>}>
228
+ <ReactSelectRefContext.Provider value={selectRef as RefObject<SelectInstance>}>
225
229
  <BaseSelect<T, U, V>
226
- ref={combined}
230
+ ref={composedRef}
227
231
  // There are places, where it is useful to override menuPortalTarget globally for all selects.
228
232
  // When you are already in some popup. e.g. user input in buttons, relation filter popover.
229
233
  menuPortalTarget={getPopupContainerElement ? getPopupContainerElement() : menuPortalTarget}
@@ -5,6 +5,7 @@ import React, {
5
5
  ForwardedRef,
6
6
  forwardRef,
7
7
  ReactNode,
8
+ RefObject,
8
9
  useCallback,
9
10
  useEffect,
10
11
  useImperativeHandle,
@@ -14,7 +15,7 @@ import React, {
14
15
  } from "react";
15
16
  import {inputOverrides} from "../antd/styles";
16
17
  import {border, space, textStyles} from "../design-system";
17
- import {Popup} from "../popup";
18
+ import {Popover} from "../popover";
18
19
  import {useIsPhone} from "../use-is-phone";
19
20
  import {MenuTransparent} from "./components/menu";
20
21
  import {
@@ -33,6 +34,7 @@ import {ButtonColor, ButtonVariant} from "../button/base-button";
33
34
  import {makeSelectInPopoverStyles} from "./styles";
34
35
  import {SelectInstance} from "react-select";
35
36
  import {SELECT_IN_POPOVER_VALUE_MAX_HEIGHT} from "./constants";
37
+ import {Wrap} from "@fibery/react/src/wrap";
36
38
 
37
39
  const offset = [0, space.s4] as [number, number];
38
40
  const popupHeightVH = 45;
@@ -63,7 +65,8 @@ const mobileClassName = css`
63
65
  padding-right: ${space.s12}px;
64
66
  `;
65
67
 
66
- const valueClassName = css`
68
+ const triggerCss = css`
69
+ min-width: 0;
67
70
  border-radius: ${border.radius6}px;
68
71
 
69
72
  &:focus {
@@ -109,7 +112,7 @@ export type TriggerProps = {
109
112
 
110
113
  const TriggerDefault = ({innerRef, children, onClick, onKeyDown}: TriggerProps) => {
111
114
  return (
112
- <div ref={innerRef} onClick={onClick} onKeyDown={onKeyDown} tabIndex={0} className={valueClassName}>
115
+ <div ref={innerRef} onClick={onClick} onKeyDown={onKeyDown} tabIndex={0} className={triggerCss}>
113
116
  {children}
114
117
  </div>
115
118
  );
@@ -119,9 +122,9 @@ export type SelectComponents<T, U extends boolean, V extends GroupBase<T>> = Sel
119
122
  Trigger?: ComponentType<TriggerProps>;
120
123
  };
121
124
 
122
- type SelectInPopoverRef<T, U extends boolean, V extends GroupBase<T>> = {
125
+ export type SelectInPopoverRef<T, U extends boolean, V extends GroupBase<T>> = {
123
126
  valueRef: HTMLDivElement | null;
124
- selectRef: SelectInstance<T, U, V> | null;
127
+ selectRef: RefObject<SelectInstance<T, U, V>>;
125
128
  };
126
129
 
127
130
  type Props<T, U extends boolean, V extends GroupBase<T>> = {
@@ -198,7 +201,6 @@ function SelectInPopoverInner<T, U extends boolean, V extends GroupBase<T>>(
198
201
  forbidValuesClearInCollnMode,
199
202
  isOptionDisabled,
200
203
  } = props;
201
- const [triggerElement, setTriggerElement] = useState<HTMLDivElement | null>(null);
202
204
  const [visible, setVisible] = useControllableState({
203
205
  value: menuIsOpen,
204
206
  defaultValue: false,
@@ -208,20 +210,12 @@ function SelectInPopoverInner<T, U extends boolean, V extends GroupBase<T>>(
208
210
  });
209
211
  const blurTimeoutRef = useRef<ReturnType<typeof setTimeout>>();
210
212
  const selectRef = useRef<SelectInstance<T, U, V>>(null);
211
- const ref = useRef<HTMLDivElement | null>(null);
212
-
213
- useImperativeHandle(
214
- forwardedRef,
215
- () =>
216
- ({
217
- valueRef: ref.current,
218
- selectRef: selectRef.current,
219
- } as {valueRef: HTMLDivElement; selectRef: SelectInstance<T, U, V> | null})
220
- );
213
+ const [triggerElement, setTriggerElement] = useState<HTMLDivElement | null>(null);
221
214
 
222
- useEffect(() => {
223
- setTriggerElement(ref.current);
224
- }, []);
215
+ useImperativeHandle(forwardedRef, () => ({
216
+ valueRef: triggerElement,
217
+ selectRef,
218
+ }));
225
219
 
226
220
  useEffect(() => {
227
221
  if (blurTimeoutRef.current) {
@@ -356,9 +350,9 @@ function SelectInPopoverInner<T, U extends boolean, V extends GroupBase<T>>(
356
350
  };
357
351
 
358
352
  return (
359
- <>
353
+ <Popover.Root open={visible} onOpen={() => onShow()} onClose={() => onHide()}>
360
354
  <Trigger
361
- innerRef={ref}
355
+ innerRef={setTriggerElement}
362
356
  menuOpened={visible}
363
357
  disabled={disabled}
364
358
  valueSelected={Boolean(value)}
@@ -367,8 +361,9 @@ function SelectInPopoverInner<T, U extends boolean, V extends GroupBase<T>>(
367
361
  >
368
362
  {renderValue ? renderValue() : null}
369
363
  </Trigger>
370
- {Boolean(triggerElement) && (
371
- <Popup
364
+ <Popover.Trigger reference={triggerElement} />
365
+ <Wrap with={Popover.Portal} if={renderInPortal} getPopupContainer={getPopupContainer}>
366
+ <Popover.Content
372
367
  supportMobile
373
368
  title={title}
374
369
  offset={offset}
@@ -376,13 +371,6 @@ function SelectInPopoverInner<T, U extends boolean, V extends GroupBase<T>>(
376
371
  popupClassName={cx(popupClassName, popupStyles)}
377
372
  mobilePopupContentClassName={mobileClassName}
378
373
  placement="bottom-start"
379
- renderInPortal={renderInPortal}
380
- open={visible}
381
- onOpen={() => onShow()}
382
- onClose={() => onHide()}
383
- trigger={<></>}
384
- triggerDomElement={triggerElement}
385
- getPopupContainer={getPopupContainer}
386
374
  >
387
375
  {title && !isPhone && <div className={titleClassName}>{title}</div>}
388
376
  {/*Usually we set popupContainerElement for react-select via context (SelectControlSettingsProvider). But here we render react-select in popover.*/}
@@ -423,9 +411,9 @@ function SelectInPopoverInner<T, U extends boolean, V extends GroupBase<T>>(
423
411
  isOptionDisabled={isOptionDisabled}
424
412
  />
425
413
  </SelectControlSettingsProvider>
426
- </Popup>
427
- )}
428
- </>
414
+ </Popover.Content>
415
+ </Wrap>
416
+ </Popover.Root>
429
417
  );
430
418
  }
431
419
 
@@ -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,6 +36,10 @@ const tabNavItem = css`
35
36
  list-style: none;
36
37
  `;
37
38
 
39
+ const tabNavItemWide = css`
40
+ flex: 1 0 0;
41
+ `;
42
+
38
43
  const tabButton = css`
39
44
  all: unset;
40
45
  display: flex;
@@ -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
+ });