@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
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # @fibery/ui-kit
2
2
 
3
+ ## 1.38.0
4
+
5
+ ### Minor Changes
6
+
7
+ - ab88ef9: add ToggleButtonGroupXComponent
8
+
9
+ ### Patch Changes
10
+
11
+ - ece31af: Move expression-utils to typescript
12
+ - d5b2a71: add RoundTogleButton
13
+ - 0d0530a: add ToggleButton component
14
+ - Updated dependencies [586c255]
15
+ - @fibery/emoji-data@2.7.0
16
+
3
17
  ## 1.37.0
4
18
 
5
19
  ### Minor Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fibery/ui-kit",
3
- "version": "1.37.0",
3
+ "version": "1.38.0",
4
4
  "private": false,
5
5
  "license": "UNLICENSED",
6
6
  "dependencies": {
@@ -12,6 +12,8 @@
12
12
  "@radix-ui/react-dropdown-menu": "2.1.1",
13
13
  "@radix-ui/react-navigation-menu": "1.2.1",
14
14
  "@radix-ui/react-toast": "1.2.2",
15
+ "@radix-ui/react-toggle": "1.1.10",
16
+ "@radix-ui/react-toggle-group": "1.1.11",
15
17
  "@radix-ui/react-tooltip": "1.1.3",
16
18
  "@types/d3-shape": "^3.1.6",
17
19
  "@types/react-select-country-list": "^2.2.3",
@@ -36,6 +38,7 @@
36
38
  "react-color": "2.19.3",
37
39
  "react-day-picker": "8.1.4",
38
40
  "react-intersection-observer": "9.3.5",
41
+ "react-medium-image-zoom": "5.2.10",
39
42
  "react-popper": "2.3.0",
40
43
  "react-select": "5.3.2",
41
44
  "react-select-country-list": "2.2.1",
@@ -43,7 +46,7 @@
43
46
  "screenfull": "6.0.1",
44
47
  "tabbable": "5.2.1",
45
48
  "ua-parser-js": "1.0.39",
46
- "@fibery/emoji-data": "2.6.1",
49
+ "@fibery/emoji-data": "2.7.0",
47
50
  "@fibery/helpers": "1.3.3",
48
51
  "@fibery/react": "1.4.4"
49
52
  },
@@ -54,7 +57,6 @@
54
57
  "devDependencies": {
55
58
  "@babel/core": "7.23.9",
56
59
  "@babel/runtime": "7.23.9",
57
- "@wyw-in-js/babel-preset": "0.5.5",
58
60
  "@testing-library/dom": "8.19.1",
59
61
  "@testing-library/jest-dom": "5.16.5",
60
62
  "@testing-library/react": "13.4.0",
@@ -68,6 +70,7 @@
68
70
  "@types/prop-types": "15.7.5",
69
71
  "@types/react": "18.0.26",
70
72
  "@types/react-dom": "18.0.10",
73
+ "@wyw-in-js/babel-preset": "0.5.5",
71
74
  "csstype": "3.0.8",
72
75
  "fs-extra": "10.0.0",
73
76
  "glob": "7.1.7",
@@ -79,8 +82,8 @@
79
82
  "svgo": "2.8.0",
80
83
  "typescript": "5.8.3",
81
84
  "unist-util-reduce": "0.2.2",
82
- "@fibery/babel-preset": "7.4.0",
83
- "@fibery/eslint-config": "8.6.1"
85
+ "@fibery/eslint-config": "8.6.1",
86
+ "@fibery/babel-preset": "7.4.0"
84
87
  },
85
88
  "jest": {
86
89
  "testEnvironment": "jsdom",
@@ -2,12 +2,14 @@ import {stopPropagation} from "@fibery/react/src/stop-propagation";
2
2
  import {useCallbackRef} from "@fibery/react/src/use-callback-ref";
3
3
  import {css, cx} from "@linaria/core";
4
4
  import {FunctionComponent, memo, ReactNode, useId} from "react";
5
- import {border, colors, layout, lineHeight, space, textStyles, themeVars} from "../design-system";
5
+ import {border, colors, layout, layoutVars, lineHeight, space, textStyles, themeVars} from "../design-system";
6
6
  import CheckedIcon from "../icons/react/Checked";
7
7
  import {IconBaseProps} from "../icons/types";
8
8
  import {Spinner} from "../loaders";
9
+ import {mobileRootSelector} from "../mobile-styles";
9
10
  import {ShortcutBadge} from "../shortcut-badge";
10
11
  import {useTheme} from "../theme-provider";
12
+ import {useIsPhone} from "../use-is-phone";
11
13
  import {useActionsMenuContext} from "./contexts/actions-menu-context";
12
14
  import {useActiveDangerousRow, useSetActiveDangerousRow} from "./contexts/actions-menu-dangerous-rows";
13
15
 
@@ -31,8 +33,11 @@ const descriptionCss = css`
31
33
  const rowCss = css`
32
34
  display: flex;
33
35
  align-items: center;
34
- min-height: ${layout.menuItemHeight}px;
36
+ min-height: ${layoutVars.menuItemHeight};
35
37
  gap: ${space.s6}px;
38
+ ${mobileRootSelector} & {
39
+ gap: ${space.s8}px;
40
+ }
36
41
  `;
37
42
 
38
43
  const dangerousItemCss = css`
@@ -87,20 +92,25 @@ const firstLetterToLowerCase = (str: string) => {
87
92
 
88
93
  const RightCorner = ({shortcut, loading, checked}: {shortcut?: string; loading?: boolean; checked?: boolean}) => {
89
94
  const theme = useTheme();
95
+ const isPhone = useIsPhone();
90
96
 
91
97
  return loading || shortcut || checked ? (
92
98
  <div className={cornerCss}>
93
99
  {loading ? <Spinner color={theme.primary} iconSize={16} containerSize={16} /> : null}
94
100
  {shortcut ? <ShortcutBadge shortcut={shortcut} /> : null}
95
- {checked ? <CheckedIcon iconSize={18} color={themeVars.textColor} /> : null}
101
+ {checked ? <CheckedIcon iconSize={isPhone ? 20 : 18} color={themeVars.textColor} /> : null}
96
102
  </div>
97
103
  ) : null;
98
104
  };
99
105
 
100
- const LeftCorner = ({Icon}: {Icon: FunctionComponent<IconBaseProps> | void}) => {
106
+ const LeftCorner = ({Icon, iconColor}: {Icon: FunctionComponent<IconBaseProps> | void; iconColor?: string}) => {
107
+ const isPhone = useIsPhone();
101
108
  return Icon ? (
102
109
  <div className={cornerCss}>
103
- <Icon color={`var(--actions-menu-item-icon-color, ${themeVars.iconColor})`} iconSize={18} />
110
+ <Icon
111
+ color={`var(--actions-menu-item-icon-color, ${iconColor ?? themeVars.iconColor})`}
112
+ iconSize={isPhone ? 20 : 18}
113
+ />
104
114
  </div>
105
115
  ) : null;
106
116
  };
@@ -115,6 +125,7 @@ export type ActionsMenuItemProps = {
115
125
  description?: string;
116
126
  /** Icon's component */
117
127
  Icon?: FunctionComponent<IconBaseProps>;
128
+ iconColor?: string;
118
129
  /** Hint for keyboard shortcut for this action */
119
130
  shortcut?: string;
120
131
  disabled?: boolean;
@@ -135,6 +146,7 @@ const ActionsMenuItemComponent: React.FC<
135
146
  onSelect,
136
147
  description,
137
148
  Icon,
149
+ iconColor,
138
150
  shortcut,
139
151
  disabled,
140
152
  dangerous,
@@ -180,7 +192,7 @@ const ActionsMenuItemComponent: React.FC<
180
192
  )}
181
193
  disabled={disabled}
182
194
  >
183
- <LeftCorner Icon={Icon} />
195
+ <LeftCorner Icon={Icon} iconColor={iconColor} />
184
196
  <div className={rowContentCss}>
185
197
  {dangerous && (
186
198
  <div className={cx(hiddenTextCss, showWarning && visibleTextCss)}>
@@ -22,6 +22,10 @@ export type ActionsMenuProps = {
22
22
  sideOffset?: number;
23
23
  collisionPadding?: number;
24
24
  autoFocusOnClose?: boolean;
25
+ /** Show menu in a modal on mobile */
26
+ supportsMobile?: boolean;
27
+ mobileTitle?: string;
28
+ height?: string;
25
29
  onEscapeKeyDown?: (event: KeyboardEvent) => void;
26
30
  sticky?: DropdownMenuContentProps["sticky"];
27
31
  };
@@ -16,6 +16,7 @@ import {ActionsMenuSubMenu} from "./actions-menu-sub-menu";
16
16
  import {useActionsMenuContext} from "./contexts/actions-menu-context";
17
17
  // eslint-disable-next-line no-restricted-imports
18
18
  import {IconBaseProps} from "@fibery/ui-kit/src/icons/types";
19
+ import {useIsPhone} from "../use-is-phone";
19
20
 
20
21
  const [ActionsMenuSubCommandMenuProvider, useActionsMenuSubCommandMenuCtx] = createContext<{
21
22
  subMenuOpenRef: RefObject<boolean>;
@@ -113,18 +114,20 @@ export const ActionsMenuSubCommandMenuRoot: React.FC<React.ComponentPropsWithout
113
114
 
114
115
  type ActionsMenuSubCommandMenuInputProps = React.ComponentPropsWithoutRef<typeof CommandMenuInput>;
115
116
  export const ActionsMenuSubCommandMenuInput: React.FC<ActionsMenuSubCommandMenuInputProps> = ({
116
- autoFocus = true,
117
+ autoFocus,
117
118
  onKeyDown,
118
119
  ...otherProps
119
120
  }) => {
121
+ const isPhone = useIsPhone();
120
122
  const inputRef = useRef<HTMLInputElement | null>(null);
123
+ const shouldFocus = autoFocus ?? (isPhone ? false : true);
121
124
 
122
125
  useEffect(() => {
123
126
  // html autofocus doesn't work reliably in Context Menu
124
- if (autoFocus) {
127
+ if (shouldFocus) {
125
128
  setTimeout(() => inputRef.current?.focus(), 0);
126
129
  }
127
- }, [autoFocus]);
130
+ }, [shouldFocus]);
128
131
 
129
132
  return (
130
133
  <CommandMenuInput
@@ -136,7 +139,7 @@ export const ActionsMenuSubCommandMenuInput: React.FC<ActionsMenuSubCommandMenuI
136
139
  }
137
140
  onKeyDown?.(e);
138
141
  }}
139
- autoFocus={autoFocus}
142
+ autoFocus={shouldFocus}
140
143
  {...otherProps}
141
144
  />
142
145
  );
@@ -6,6 +6,7 @@ import ArrowRight from "../icons/react/ArrowRight";
6
6
  import {useActionsMenuContext} from "./contexts/actions-menu-context";
7
7
  // eslint-disable-next-line no-restricted-imports
8
8
  import {IconBaseProps} from "@fibery/ui-kit/src/icons/types";
9
+ import {useIsPhone} from "../use-is-phone";
9
10
 
10
11
  const subTriggerClass = css`
11
12
  display: flex;
@@ -24,6 +25,7 @@ type Props = React.PropsWithChildren<{
24
25
  open?: boolean;
25
26
  onOpenChange?: (v: boolean) => void;
26
27
  disabled?: boolean;
28
+ mobileTitle?: string;
27
29
  contentClassName?: string;
28
30
  container?: HTMLElement | null;
29
31
  Icon?: FunctionComponent<IconBaseProps>;
@@ -37,9 +39,11 @@ const cornerCss = css`
37
39
  `;
38
40
 
39
41
  const LeftCorner = ({Icon}: {Icon: FunctionComponent<IconBaseProps> | void}) => {
42
+ const isPhone = useIsPhone();
43
+
40
44
  return Icon ? (
41
45
  <div className={cornerCss}>
42
- <Icon color={`var(--actions-menu-item-icon-color, ${themeVars.iconColor})`} iconSize={18} />
46
+ <Icon color={`var(--actions-menu-item-icon-color, ${themeVars.iconColor})`} iconSize={isPhone ? 20 : 18} />
43
47
  </div>
44
48
  ) : null;
45
49
  };
@@ -55,14 +59,16 @@ export const ActionsMenuSubMenu: React.FC<Props> = ({
55
59
  triggerRef,
56
60
  children,
57
61
  disabled = false,
62
+ mobileTitle = trigger,
58
63
  contentClassName,
59
64
  Icon,
60
65
  onMouseEnter,
61
66
  }) => {
67
+ const isPhone = useIsPhone();
62
68
  const {MenuPrimitive, container} = useActionsMenuContext();
63
69
 
64
70
  return (
65
- <MenuPrimitive.Sub open={open} onOpenChange={onOpenChange}>
71
+ <MenuPrimitive.Sub title={mobileTitle} open={open} onOpenChange={onOpenChange}>
66
72
  <MenuPrimitive.SubTrigger
67
73
  ref={triggerRef}
68
74
  className={subTriggerClass}
@@ -75,7 +81,7 @@ export const ActionsMenuSubMenu: React.FC<Props> = ({
75
81
  <div className={arrowClass}>
76
82
  <ArrowRight
77
83
  color={disabled ? themeVars.disabledTextColor : themeVars.iconColor}
78
- containerSize={10}
84
+ containerSize={isPhone ? 20 : 10}
79
85
  aria-hidden
80
86
  />
81
87
  </div>
@@ -1,11 +1,14 @@
1
1
  import {useControllableState} from "@fibery/react/src/use-controllable-state";
2
2
  import {css, cx} from "@linaria/core";
3
+ import {useIsPhone} from "../use-is-phone";
3
4
  import * as DropdownMenu from "../dropdown-menu";
5
+ import * as ModalMenu from "../modal-menu";
4
6
  import {ActionsMenuContextProvider} from "./contexts/actions-menu-context";
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";
8
10
  import {useState} from "react";
11
+ import {useFeatures} from "../features";
9
12
  import {ActionsMenuConfirmation, ActionsMenuConfirmationProps} from "./actions-menu-confirmation";
10
13
  import {useActionsMenuOpenStateTracker} from "./actions-menu-open-state-tracker";
11
14
 
@@ -36,8 +39,12 @@ export const ActionsMenu = ({
36
39
  contentStyles,
37
40
  modal,
38
41
  autoFocusOnClose,
42
+ supportsMobile,
43
+ mobileTitle,
39
44
  sticky,
45
+ height,
40
46
  }: ActionsMenuProps): JSX.Element => {
47
+ const features = useFeatures<{enableMobileMenus: boolean}>();
41
48
  const {onMenuOpenChange} = useActionsMenuOpenStateTracker();
42
49
  const [isOpen = false, setOpen] = useControllableState({
43
50
  value: open,
@@ -48,26 +55,35 @@ export const ActionsMenu = ({
48
55
  onMenuOpenChange(value);
49
56
  },
50
57
  });
58
+ const isPhone = useIsPhone();
51
59
  const [confirmation, setConfirmation] = useState<ActionsMenuConfirmationProps | null>(null);
52
60
 
61
+ const MenuPrimitive = isPhone && features.enableMobileMenus && supportsMobile ? ModalMenu : DropdownMenu;
62
+
53
63
  return (
54
64
  <ActionsMenuContextProvider
55
65
  setOpen={setOpen}
56
66
  showConfirmation={setConfirmation}
57
- MenuPrimitive={DropdownMenu}
67
+ MenuPrimitive={MenuPrimitive}
58
68
  container={container}
59
69
  >
60
- <DropdownMenu.Root open={isOpen} onOpenChange={setOpen} modal={modal || false}>
61
- <DropdownMenu.Trigger
70
+ <MenuPrimitive.Root
71
+ open={isOpen}
72
+ height={height}
73
+ onOpenChange={setOpen}
74
+ modal={modal || false}
75
+ title={mobileTitle}
76
+ >
77
+ <MenuPrimitive.Trigger
62
78
  // backward compatibility for dropdown styles
63
- className={isOpen ? "actions-visible" : "actions-hidden"}
79
+ className={isOpen ? "actions-visible" : "actions-hidden actions-visible-on-mobile"}
64
80
  onClick={preventDefaultAndStopPropagation}
65
81
  disabled={disabled}
66
82
  asChild
67
83
  >
68
84
  {trigger}
69
- </DropdownMenu.Trigger>
70
- <DropdownMenu.Content
85
+ </MenuPrimitive.Trigger>
86
+ <MenuPrimitive.Content
71
87
  align={align}
72
88
  sticky={sticky}
73
89
  alignOffset={alignOffset}
@@ -81,8 +97,8 @@ export const ActionsMenu = ({
81
97
  onCloseAutoFocus={autoFocusOnClose ? undefined : preventDefault}
82
98
  >
83
99
  <ActionsMenuDangerousRowsProvider open={isOpen}>{children}</ActionsMenuDangerousRowsProvider>
84
- </DropdownMenu.Content>
85
- </DropdownMenu.Root>
100
+ </MenuPrimitive.Content>
101
+ </MenuPrimitive.Root>
86
102
 
87
103
  {confirmation ? <ActionsMenuConfirmation {...confirmation} /> : null}
88
104
  </ActionsMenuContextProvider>
@@ -1,6 +1,7 @@
1
1
  import {Children, forwardRef, ReactElement, ReactNode} from "react";
2
2
  import {css} from "@linaria/core";
3
3
  import {space} from "./design-system";
4
+ import {useIsPhone} from "./use-is-phone";
4
5
 
5
6
  const gridStyle = css`
6
7
  align-items: center;
@@ -12,10 +13,19 @@ type Props = {
12
13
  inline?: boolean;
13
14
  gap?: number;
14
15
  };
16
+
17
+ const getGap = (gap: number | undefined, isPhone: boolean) => {
18
+ if (gap !== undefined) {
19
+ return gap;
20
+ }
21
+ return isPhone ? 0 : space.s4;
22
+ };
23
+
15
24
  export const ActionsPanel = forwardRef<HTMLDivElement, Props>(function ActionsPanelComponent(
16
- {children, inline, gap = space.s4},
25
+ {children, inline, gap},
17
26
  ref
18
27
  ) {
28
+ const isPhone = useIsPhone();
19
29
  const childrenArray = Children.toArray(children);
20
30
  if (childrenArray.length === 0) {
21
31
  return null;
@@ -27,7 +37,7 @@ export const ActionsPanel = forwardRef<HTMLDivElement, Props>(function ActionsPa
27
37
  className={gridStyle}
28
38
  style={{
29
39
  display: inline ? "inline-grid" : "grid",
30
- gridGap: gap,
40
+ gridGap: getGap(gap, isPhone),
31
41
  gridAutoFlow: "column",
32
42
  }}
33
43
  >
@@ -1,24 +1,8 @@
1
1
  import {css} from "@linaria/core";
2
- import {styled} from "@linaria/react";
3
- import cn from "classnames";
2
+ import {default as cn, default as cx} from "classnames";
4
3
  import {FC, ReactNode} from "react";
5
- import {getThemeValue, layout, opacity, border, space, textStyles, themeVars} from "./design-system";
6
- import {useTheme} from "./theme-provider";
7
-
8
- const axisItemStyle = css`
9
- ${{
10
- display: "grid",
11
- gridGap: space.s6,
12
- minHeight: layout.itemHeight,
13
- alignItems: "center",
14
- paddingLeft: space.s8,
15
- paddingRight: space.s8,
16
- borderRadius: border.radius6,
17
- width: "100%",
18
- ...textStyles.heading5,
19
- }}
20
- `;
21
-
4
+ import {CountBadge} from "./count-badge";
5
+ import {border, opacity, space, textStyles, themeVars} from "./design-system";
22
6
  const axisItemStyleX = css`
23
7
  grid-template-columns: auto 1fr auto;
24
8
  `;
@@ -31,11 +15,23 @@ const axisItemHighlightedStyle = css`
31
15
 
32
16
  const dimensionToItemStyle = {x: axisItemStyleX, y: axisItemStyleY};
33
17
 
18
+ const wrapperStyle = css`
19
+ ${textStyles.regular};
20
+ display: grid;
21
+ width: 100%;
22
+ grid-template-columns: 1fr auto;
23
+ gap: ${space.s6}px;
24
+ padding: ${space.s6}px;
25
+ `;
26
+
34
27
  const axisItemStyleCollapsed = css`
35
- ${{
36
- paddingRight: 0,
37
- width: `${layout.itemHeight + space.s8 * 2}px`,
38
- }}
28
+ writing-mode: vertical-rl;
29
+ display: flex;
30
+ position: sticky;
31
+ top: 0;
32
+ &.${wrapperStyle} {
33
+ padding-block: 0;
34
+ }
39
35
  `;
40
36
 
41
37
  function getAdditionalClassName({
@@ -56,33 +52,61 @@ function getAdditionalClassName({
56
52
  return dimensionToItemStyle[dimension];
57
53
  }
58
54
 
59
- const mainContainerWrapperStyle = {
60
- overflow: "hidden",
61
- whiteSpace: "nowrap",
62
- cursor: "pointer",
63
- textOverflow: "ellipsis",
64
- opacity: 1,
65
- };
66
- const mainContainerWrapperClassName = css`
67
- ${mainContainerWrapperStyle}
55
+ const mainContainerWrapperStyle = css`
56
+ overflow: hidden;
57
+ white-space: nowrap;
58
+ cursor: pointer;
59
+ text-overflow: ellipsis;
60
+ opacity: 1;
68
61
  `;
69
62
 
70
- const mainContainerWrapperCollapsedClassName = css`
71
- ${{
72
- ...mainContainerWrapperStyle,
73
- textOverflow: "ellipsis",
74
- opacity: opacity.opacity40,
75
- }}
63
+ const mainContainerWrapperCollapsedClassName = cx(
64
+ mainContainerWrapperStyle,
65
+ css`
66
+ opacity: ${opacity.opacity40};
67
+ display: flex;
68
+ align-items: center;
69
+ justify-content: flex-start;
70
+ gap: ${space.s12}px;
71
+ `
72
+ );
73
+
74
+ const collapsedTextStyle = css`
75
+ overflow: hidden;
76
+ white-space: nowrap;
77
+ text-overflow: ellipsis;
78
+ min-width: 0;
79
+ flex-shrink: 1;
76
80
  `;
77
81
 
78
82
  const leftContainerWrapperStyle = css`
79
83
  cursor: pointer;
80
84
  `;
81
85
 
82
- const StyledDiv = styled.div<{backgroundColor?: string}>`
86
+ export const leftMainContainerWrapperStyle = css``;
87
+
88
+ export const axisHeaderClassName = css`
89
+ cursor: pointer;
90
+ min-height: ${space.s36}px;
83
91
  display: flex;
92
+ justify-content: center;
84
93
  flex-direction: column;
85
- background-color: ${({backgroundColor = "transparent"}) => backgroundColor};
94
+ border-radius: ${border.radius6}px;
95
+ .right-container {
96
+ opacity: 0;
97
+ }
98
+ background-color: var(--board-bg, ${themeVars.boardBg});
99
+ &:hover {
100
+ background-color: ${themeVars.boardAxisBgHover};
101
+ z-index: 20;
102
+ & .${leftMainContainerWrapperStyle} {
103
+ background-color: transparent;
104
+ }
105
+ .right-container {
106
+ visibility: visible;
107
+ opacity: 1;
108
+ }
109
+ }
86
110
  `;
87
111
 
88
112
  interface AxisHeaderProps {
@@ -90,12 +114,12 @@ interface AxisHeaderProps {
90
114
  dimension: "x" | "y";
91
115
  collapsed?: boolean;
92
116
  highlighted?: boolean;
93
- showLeftContainerWhenCollapsed?: boolean;
94
117
  bottomContainer?: ReactNode;
95
118
  leftContainer?: ReactNode;
96
119
  rightContainer?: ReactNode;
97
120
  children: ReactNode;
98
121
  onCollapse?: () => void;
122
+ count?: number;
99
123
  }
100
124
 
101
125
  export const AxisHeader: FC<AxisHeaderProps> = ({
@@ -107,54 +131,90 @@ export const AxisHeader: FC<AxisHeaderProps> = ({
107
131
  bottomContainer,
108
132
  collapsed,
109
133
  highlighted,
110
- showLeftContainerWhenCollapsed,
111
134
  onCollapse,
135
+ count,
112
136
  }) => {
113
- const theme = useTheme();
114
- const backgroundColor = getThemeValue(theme.mode, {
115
- dark: themeVars.panelContentBg,
116
- light: themeVars.panelContentBg,
117
- });
137
+ const leftContainerWrapper = <div className={leftContainerWrapperStyle}>{leftContainer}</div>;
118
138
 
119
- const leftContainerWrapper = (
120
- <div onClick={onCollapse} className={leftContainerWrapperStyle}>
121
- {leftContainer}
139
+ const rightContainerWrapper = (
140
+ <div
141
+ className={cn(
142
+ "right-container",
143
+ css`
144
+ display: flex;
145
+ align-items: center;
146
+ `
147
+ )}
148
+ >
149
+ {rightContainer}
122
150
  </div>
123
151
  );
124
152
 
125
- const rightContainerWrapper = <div>{rightContainer}</div>;
126
-
127
153
  const mainContainerWrapper = (
128
- <div
129
- onClick={onCollapse}
130
- className={collapsed ? mainContainerWrapperCollapsedClassName : mainContainerWrapperClassName}
131
- >
132
- {children}
154
+ <div className={collapsed ? mainContainerWrapperCollapsedClassName : mainContainerWrapperStyle}>
155
+ {collapsed ? <div className={collapsedTextStyle}>{children}</div> : children}
156
+ {count !== undefined && collapsed && (
157
+ <CountBadge
158
+ count={count}
159
+ className={css`
160
+ writing-mode: horizontal-tb;
161
+ `}
162
+ />
163
+ )}
133
164
  </div>
134
165
  );
135
166
  return (
136
- <StyledDiv data-testid={`axis-header-${dimension}`} backgroundColor={backgroundColor} className={cn(className)}>
137
- <div
138
- className={cn(axisItemStyle, getAdditionalClassName({dimension, highlighted, collapsed}))}
139
- style={{
140
- background: highlighted ? themeVars.actionMenuHover : backgroundColor,
141
- }}
142
- >
143
- {collapsed ? (
144
- showLeftContainerWhenCollapsed ? (
145
- leftContainerWrapper
146
- ) : (
147
- mainContainerWrapper
148
- )
149
- ) : (
150
- <>
167
+ <div
168
+ data-testid={`axis-header-${dimension}`}
169
+ onClick={onCollapse}
170
+ className={cn(
171
+ className,
172
+ axisHeaderClassName,
173
+ dimension === "y" &&
174
+ css`
175
+ position: sticky;
176
+ left: 0;
177
+ display: inline-flex;
178
+ z-index: 3;
179
+ background-color: transparent;
180
+ `,
181
+ dimension === "y" &&
182
+ highlighted &&
183
+ css`
184
+ display: flex;
185
+ width: 100%;
186
+ `,
187
+ collapsed &&
188
+ dimension === "x" &&
189
+ css`
190
+ position: sticky;
191
+ top: 0;
192
+ z-index: 2;
193
+ background-color: transparent;
194
+ `
195
+ )}
196
+ >
197
+ <div className={cn(wrapperStyle, getAdditionalClassName({dimension, highlighted, collapsed}))}>
198
+ <>
199
+ <div
200
+ className={cn(
201
+ leftMainContainerWrapperStyle,
202
+ css`
203
+ display: grid;
204
+ grid-template-columns: auto 1fr;
205
+ gap: ${space.s8}px;
206
+ padding-inline: ${space.s4}px;
207
+ align-items: center;
208
+ `
209
+ )}
210
+ >
151
211
  {leftContainerWrapper}
152
212
  {mainContainerWrapper}
153
- {rightContainerWrapper}
154
- </>
155
- )}
213
+ </div>
214
+ {rightContainerWrapper}
215
+ </>
156
216
  </div>
157
217
  {bottomContainer}
158
- </StyledDiv>
218
+ </div>
159
219
  );
160
220
  };
package/src/box.tsx CHANGED
@@ -3,7 +3,7 @@ import {styled} from "@linaria/react";
3
3
  import cx from "classnames";
4
4
  import {CSSProperties, forwardRef, HTMLProps, ReactNode, Ref, useCallback, useMemo, useState} from "react";
5
5
  import {border, getLinearGradient, getOpacities, space, textStyles, themeVars, transition} from "./design-system";
6
- import {useClickOutside} from "./popup/use-click-outside";
6
+ import {useClickOutside} from "./popover/use-click-outside";
7
7
 
8
8
  export const getBg = (backgroundColors: string[]) => {
9
9
  if (backgroundColors) {