@ainias42/react-bootstrap-mobile 1.0.4 → 1.0.5

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 (76) hide show
  1. package/dist/Components/ActionSheet/ActionSheet.d.ts +1 -1
  2. package/dist/Components/FormElements/Button/Button.d.ts +4 -1
  3. package/dist/Components/FormElements/Button/Button.stories.d.ts +8 -2
  4. package/dist/Components/Icon/BaseIcon.d.ts +15 -0
  5. package/dist/Components/Icon/DoubleIcon.d.ts +1 -1
  6. package/dist/Components/Icon/Icon.d.ts +4 -13
  7. package/dist/Components/Icon/Icon.stories.d.ts +1 -1
  8. package/dist/Components/Layout/BaseBlock.d.ts +10 -0
  9. package/dist/Components/Layout/BaseInlineBlock.d.ts +10 -0
  10. package/dist/Components/Layout/Block.d.ts +4 -10
  11. package/dist/Components/Layout/Flex.d.ts +6 -1
  12. package/dist/Components/Layout/InlineBlock.d.ts +4 -10
  13. package/dist/Components/Menu/Menu.d.ts +1 -1
  14. package/dist/Components/Menu/MenuItem.d.ts +1 -1
  15. package/dist/Components/Menu/Submenu.d.ts +1 -1
  16. package/dist/Components/Menu/useHoverMenu.d.ts +17 -0
  17. package/dist/Components/SpoilerList/Spoiler/Spoiler.d.ts +1 -1
  18. package/dist/Components/TabBar/TabBar.d.ts +1 -1
  19. package/dist/Components/Title/HoverTitle.d.ts +9 -0
  20. package/dist/Components/Title/Title.stories.d.ts +11 -0
  21. package/dist/Components/Title/withTitle.d.ts +6 -0
  22. package/dist/Components/TopBar/TopBar.d.ts +1 -1
  23. package/dist/index.css +4 -3
  24. package/dist/index.css.map +1 -1
  25. package/dist/index.d.ts +6 -0
  26. package/dist/index.js +422 -205
  27. package/dist/index.js.map +1 -1
  28. package/package.json +1 -1
  29. package/src/Components/ActionSheet/ActionSheet.tsx +12 -10
  30. package/src/Components/Colors.stories.tsx +7 -7
  31. package/src/Components/Dialog/ButtonDialog.tsx +9 -9
  32. package/src/Components/Dialog/Dialog.tsx +6 -6
  33. package/src/Components/Dialog/DialogBackground.tsx +5 -5
  34. package/src/Components/Dialog/DialogContainer.tsx +3 -3
  35. package/src/Components/ErrorBoundary.tsx +3 -3
  36. package/src/Components/FormElements/Button/Button.tsx +2 -2
  37. package/src/Components/FormElements/Error/FormError.tsx +6 -6
  38. package/src/Components/FormElements/Input/FileInput/MultipleFileInput.tsx +7 -7
  39. package/src/Components/FormElements/Input/PasswordInput/PasswordInput.tsx +2 -2
  40. package/src/Components/FormElements/SearchSelectInput/SearchSelectInput.tsx +9 -9
  41. package/src/Components/Hooks/useDelayed.ts +5 -4
  42. package/src/Components/Icon/{Icon.tsx → BaseIcon.tsx} +6 -4
  43. package/src/Components/Icon/DoubleIcon.tsx +7 -7
  44. package/src/Components/Icon/Icon.stories.tsx +3 -3
  45. package/src/Components/Icon/Icon.ts +4 -0
  46. package/src/Components/Layout/{Block.tsx → BaseBlock.tsx} +2 -3
  47. package/src/Components/Layout/{InlineBlock.tsx → BaseInlineBlock.tsx} +2 -2
  48. package/src/Components/Layout/Block.ts +4 -0
  49. package/src/Components/Layout/Flex.tsx +16 -0
  50. package/src/Components/Layout/Grid/Grid.tsx +3 -3
  51. package/src/Components/Layout/Grid/GridItem.tsx +3 -3
  52. package/src/Components/Layout/InlineBlock.ts +4 -0
  53. package/src/Components/Layout/layout.module.scss +13 -0
  54. package/src/Components/LoadingArea/LoadingArea.stories.tsx +3 -3
  55. package/src/Components/Menu/HoverMenu.stories.tsx +3 -3
  56. package/src/Components/Menu/HoverMenu.tsx +8 -28
  57. package/src/Components/Menu/Menu.tsx +4 -4
  58. package/src/Components/Menu/MenuDivider.tsx +2 -2
  59. package/src/Components/Menu/MenuItem.tsx +6 -6
  60. package/src/Components/Menu/Submenu.tsx +7 -7
  61. package/src/Components/Menu/menu.module.scss +3 -2
  62. package/src/Components/Menu/useHoverMenu.ts +36 -0
  63. package/src/Components/SizeCalculator/SizeCalculator.tsx +3 -3
  64. package/src/Components/SpoilerList/Spoiler/Spoiler.tsx +9 -9
  65. package/src/Components/SpoilerList/SpoilerList.tsx +3 -3
  66. package/src/Components/TabBar/TabBar.tsx +3 -3
  67. package/src/Components/Title/HoverTitle.tsx +97 -0
  68. package/src/Components/Title/Title.stories.tsx +95 -0
  69. package/src/Components/Title/hoverTitle.module.scss +8 -0
  70. package/src/Components/Title/withTitle.module.scss +7 -0
  71. package/src/Components/Title/withTitle.tsx +48 -0
  72. package/src/Components/TopBar/MoreButton.tsx +3 -3
  73. package/src/Components/TopBar/TopBar.tsx +6 -6
  74. package/src/WrongChildError.ts +1 -0
  75. package/src/helper/withRestrictedChildren.tsx +3 -1
  76. package/src/index.ts +6 -0
@@ -13,6 +13,12 @@ export type FlexProps<AsType extends keyof IntrinsicElements> = RbmComponentProp
13
13
  ViewWithoutListenersProps<AsType> & {
14
14
  horizontal?: boolean;
15
15
  grow?: boolean;
16
+ unaligned?: boolean;
17
+ fillWidth?: boolean;
18
+ fillHeight?: boolean;
19
+ fill?: boolean;
20
+ // TODO gap as enum?
21
+ gap?: number;
16
22
  }
17
23
  >;
18
24
 
@@ -23,6 +29,12 @@ export const Flex = withMemo(function Flex<AsType extends keyof JSX.IntrinsicEle
23
29
  horizontal = false,
24
30
  ref,
25
31
  grow = false,
32
+ unaligned = false,
33
+ fill = false,
34
+ fillWidth = fill,
35
+ fillHeight = fill,
36
+ gap,
37
+ style,
26
38
  ...props
27
39
  }: FlexProps<AsType>) {
28
40
  // Variables
@@ -45,7 +57,11 @@ export const Flex = withMemo(function Flex<AsType extends keyof JSX.IntrinsicEle
45
57
  [styles.horizontal]: horizontal,
46
58
  [styles.grow]: grow,
47
59
  [styles.weight1]: grow,
60
+ [styles.unaligned]: unaligned,
61
+ [styles.fillWidth]: fillWidth,
62
+ [styles.fillHeight]: fillHeight,
48
63
  })}
64
+ style={{ gap, ...style }}
49
65
  as={as as AsType}
50
66
  ref={ref}
51
67
  {...(props as ViewWithoutListenersProps<AsType>)}
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { Block } from '@/Components/Layout/Block';
2
+ import { BaseBlock } from '@/Components/Layout/BaseBlock';
3
3
  import { useMemo } from 'react';
4
4
  import { withMemo } from '@/helper/withMemo';
5
5
  import classNames from 'classnames';
@@ -49,13 +49,13 @@ export const Grid = withMemo(function Grid({
49
49
  // Render Functions
50
50
 
51
51
  return (
52
- <Block
52
+ <BaseBlock
53
53
  ref={ref}
54
54
  style={appliedStyle}
55
55
  className={classNames(styles.grid, className, { [styles.useContainerWidth]: useContainerWidth })}
56
56
  __allowChildren={__allowChildren as 'all'}
57
57
  >
58
58
  {children}
59
- </Block>
59
+ </BaseBlock>
60
60
  );
61
61
  });
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { InlineBlock } from '@/Components/Layout/InlineBlock';
2
+ import { BaseInlineBlock } from '@/Components/Layout/BaseInlineBlock';
3
3
  import { withMemo } from '@/helper/withMemo';
4
4
  import classNames from 'classnames';
5
5
  import styles from '@/Components/Layout/Grid/grid.module.scss';
@@ -151,13 +151,13 @@ function GridItem({
151
151
 
152
152
  // Render Functions
153
153
  return (
154
- <InlineBlock
154
+ <BaseInlineBlock
155
155
  style={style}
156
156
  className={classNames(...classes.map((name) => styles[name]), styles.item, className)}
157
157
  __allowChildren={__allowChildren as 'all'}
158
158
  >
159
159
  {children}
160
- </InlineBlock>
160
+ </BaseInlineBlock>
161
161
  );
162
162
  }
163
163
 
@@ -0,0 +1,4 @@
1
+ import { BaseInlineBlock } from '@/Components/Layout/BaseInlineBlock';
2
+ import { withTitle } from '@/Components/Title/withTitle';
3
+
4
+ export const InlineBlock = withTitle(BaseInlineBlock);
@@ -7,6 +7,19 @@
7
7
  &.horizontal {
8
8
  flex-direction: row;
9
9
  }
10
+
11
+ &.unaligned {
12
+ align-items: unset;
13
+ justify-content: unset;
14
+ }
15
+
16
+ &.fillWidth {
17
+ width: 100%;
18
+ }
19
+
20
+ &.fillHeight {
21
+ height: 100%;
22
+ }
10
23
  }
11
24
 
12
25
  .grow {
@@ -1,6 +1,6 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-vite';
2
2
 
3
- import { Block } from '@/Components/Layout/Block';
3
+ import { BaseBlock } from '@/Components/Layout/BaseBlock';
4
4
  import { LoadingArea } from '@/Components/LoadingArea/LoadingArea';
5
5
  import { Text } from '@/Components/Text/Text';
6
6
  import React from 'react';
@@ -9,14 +9,14 @@ const meta = {
9
9
  component: LoadingArea,
10
10
  render: (args) => (
11
11
  <LoadingArea {...args}>
12
- <Block
12
+ <BaseBlock
13
13
  style={{
14
14
  height: '200px',
15
15
  background: 'gray',
16
16
  }}
17
17
  >
18
18
  <Text>Content goes here Error within Story. Normally it would only cover the content and not more</Text>
19
- </Block>
19
+ </BaseBlock>
20
20
  </LoadingArea>
21
21
  ),
22
22
  } satisfies Meta<typeof LoadingArea>;
@@ -1,11 +1,11 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-vite';
2
2
 
3
3
  import * as MenuStories from '@/Components/Menu/Menu.stories';
4
+ import { BaseIcon } from '@/Components/Icon/BaseIcon';
4
5
  import { HoverMenu } from '@/Components/Menu/HoverMenu';
5
- import { Icon } from '@/Components/Icon/Icon';
6
6
  import { faCogs } from '@fortawesome/free-solid-svg-icons';
7
7
  import React from 'react';
8
- import type { ReactElement } from 'react';
8
+ import type { ReactElement } from 'react';
9
9
 
10
10
  const meta = {
11
11
  component: HoverMenu,
@@ -26,7 +26,7 @@ type Story = StoryObj<typeof meta>;
26
26
  export const Default: Story = {
27
27
  args: {
28
28
  openToSide: false,
29
- children: <Icon icon={faCogs} />,
29
+ children: <BaseIcon icon={faCogs} />,
30
30
  items: MenuStories.Default.args?.children as ReactElement,
31
31
  },
32
32
  };
@@ -1,7 +1,8 @@
1
1
  import { Clickable } from '@/Components/Clickable/Clickable';
2
2
  import { Menu } from '@/Components/Menu/Menu';
3
+ import { useHoverMenu } from '@/Components/Menu/useHoverMenu';
3
4
  import { withMemo } from '@/helper/withMemo';
4
- import React, { useCallback, useRef, useState } from 'react';
5
+ import React, { useCallback, useRef } from 'react';
5
6
  import classNames from 'classnames';
6
7
  import styles from '@/Components/Menu/menu.module.scss';
7
8
  import type {
@@ -33,37 +34,16 @@ export const HoverMenu = withMemo(function HoverMenu({
33
34
 
34
35
  // States/Variables/Selectors
35
36
  const hoverItemRef = useRef<HTMLDivElement>(null);
36
-
37
- const [isOpen, setIsOpen] = useState(false);
38
- const [position, setPosition] = useState({ x: 0, y: 0 });
39
- const [offset, setOffset] = useState({ x: 0, y: 0 });
37
+ const { isOpen, position, offset, open, close } = useHoverMenu({ ref: hoverItemRef, openToSide });
40
38
 
41
39
  // Dispatch
42
40
 
43
41
  // Callbacks
44
- const recalculatePosition = useCallback(() => {
45
- if (!hoverItemRef.current) {
46
- return;
47
- }
48
- const { top, left, bottom, right, width, height } = hoverItemRef.current.getBoundingClientRect();
49
- if (openToSide) {
50
- setPosition({ x: right, y: top });
51
- setOffset({ x: width, y: -height });
52
- } else {
53
- setPosition({ x: left, y: bottom });
54
- setOffset({ x: -width, y: height });
55
- }
56
- }, [openToSide]);
57
42
 
58
- const close = useCallback(() => {
59
- setIsOpen(false);
43
+ const innerOnClose = useCallback(() => {
44
+ close();
60
45
  onClose?.();
61
- }, [onClose]);
62
-
63
- const open = useCallback(() => {
64
- recalculatePosition();
65
- setIsOpen(true);
66
- }, [recalculatePosition]);
46
+ }, [onClose, close]);
67
47
 
68
48
  const onClickInner = useCallback(() => {
69
49
  if (onClick?.() !== false) {
@@ -80,7 +60,7 @@ export const HoverMenu = withMemo(function HoverMenu({
80
60
  return (
81
61
  <Clickable
82
62
  onMouseEnter={open}
83
- onMouseLeave={close}
63
+ onMouseLeave={innerOnClose}
84
64
  useReactOnMouseLeave={true}
85
65
  onClick={onClickInner}
86
66
  className={classNames(styles.hoverMenu, { [styles.open]: isOpen }, className)}
@@ -93,7 +73,7 @@ export const HoverMenu = withMemo(function HoverMenu({
93
73
  x={position.x}
94
74
  y={position.y}
95
75
  isOpen={true}
96
- onClose={close}
76
+ onClose={innerOnClose}
97
77
  offsetX={offset.x}
98
78
  offsetY={offset.y}
99
79
  className={classNames({ [styles.hidden]: !isOpen })}
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { Block } from '@/Components/Layout/Block';
2
+ import { BaseBlock } from '@/Components/Layout/BaseBlock';
3
3
  import { MenuCloseContextProvider } from '@/Components/Menu/MenuCloseContext';
4
4
  import { MenuItem } from '@/Components/Menu/MenuItem';
5
5
  import { createPortal } from 'react-dom';
@@ -10,7 +10,7 @@ import { withMemo } from '@/helper/withMemo';
10
10
  import { withRenderBrowserOnly } from '@/helper/withRenderBrowserOnly';
11
11
  import classNames from 'classnames';
12
12
  import styles from '@/Components/Menu/menu.module.scss';
13
- import type { IconSource } from '@/Components/Icon/Icon';
13
+ import type { IconSource } from '@/Components/Icon/BaseIcon';
14
14
  import type { RbmComponentProps } from '@/Components/RbmComponentProps';
15
15
 
16
16
  export type MenuItemType = {
@@ -140,7 +140,7 @@ export const Menu = withMemo(
140
140
  <>
141
141
  {createPortal(
142
142
  <MenuCloseContextProvider value={onClose}>
143
- <Block
143
+ <BaseBlock
144
144
  className={classNames(className, styles.menu)}
145
145
  style={{ ...style, top: innerY, left: innerX }}
146
146
  ref={menuRef}
@@ -171,7 +171,7 @@ export const Menu = withMemo(
171
171
  );
172
172
  })}
173
173
  {children}
174
- </Block>
174
+ </BaseBlock>
175
175
  </MenuCloseContextProvider>,
176
176
  portalContainer,
177
177
  )}
@@ -1,4 +1,4 @@
1
- import { Block } from '@/Components/Layout/Block';
1
+ import { BaseBlock } from '@/Components/Layout/BaseBlock';
2
2
  import { withMemo } from '@/helper/withMemo';
3
3
  import React from 'react';
4
4
  import styles from '@/Components/Menu/menu.module.scss';
@@ -18,5 +18,5 @@ export const MenuDivider = withMemo(function MenuDivider() {
18
18
 
19
19
  // RenderFunctions
20
20
 
21
- return <Block className={styles.divider} />;
21
+ return <BaseBlock className={styles.divider} />;
22
22
  });
@@ -1,13 +1,13 @@
1
- import { Block } from '@/Components/Layout/Block';
1
+ import { BaseBlock } from '@/Components/Layout/BaseBlock';
2
+ import { BaseIcon } from '@/Components/Icon/BaseIcon';
2
3
  import { Clickable } from '@/Components/Clickable/Clickable';
3
- import { Icon } from '@/Components/Icon/Icon';
4
4
  import { Text } from '@/Components/Text/Text';
5
5
  import { useMenuClose } from '@/Components/Menu/MenuCloseContext';
6
6
  import { withMemo } from '@/helper/withMemo';
7
7
  import React, { useCallback } from 'react';
8
8
  import classNames from 'classnames';
9
9
  import styles from '@/Components/Menu/menu.module.scss';
10
- import type { IconSource } from '@/Components/Icon/Icon';
10
+ import type { IconSource } from '@/Components/Icon/BaseIcon';
11
11
  import type { RbmComponentProps, WithChildren } from '@/Components/RbmComponentProps';
12
12
  import type { ReactNode } from 'react';
13
13
 
@@ -97,10 +97,10 @@ export const MenuItem = withMemo(function MenuItem<Item>({
97
97
  onMouseLeave={onMouseLeaveInner}
98
98
  __allowChildren="all"
99
99
  >
100
- <Block className={classNames(styles.itemChildren)}>
101
- {!!icon && <Icon icon={icon} color={iconColor} className={styles.icon} />}
100
+ <BaseBlock className={classNames(styles.itemChildren)}>
101
+ {!!icon && <BaseIcon icon={icon} color={iconColor} className={styles.icon} />}
102
102
  {childElements}
103
- </Block>
103
+ </BaseBlock>
104
104
  </Clickable>
105
105
  );
106
106
  }, 'text');
@@ -1,8 +1,8 @@
1
- import { Block } from '@/Components/Layout/Block';
1
+ import { BaseBlock } from '@/Components/Layout/BaseBlock';
2
+ import { BaseIcon } from '@/Components/Icon/BaseIcon';
2
3
  import { Clickable } from '@/Components/Clickable/Clickable';
3
4
  import { Flex } from '@/Components/Layout/Flex';
4
5
  import { Grow } from '@/Components/Layout/Grow';
5
- import { Icon } from '@/Components/Icon/Icon';
6
6
  import { MenuCloseContextProvider, useMenuClose } from '@/Components/Menu/MenuCloseContext';
7
7
  import { Text } from '@/Components/Text/Text';
8
8
  import { faChevronRight } from '@fortawesome/free-solid-svg-icons';
@@ -11,7 +11,7 @@ import { withMemo } from '@/helper/withMemo';
11
11
  import React, { useCallback, useRef, useState } from 'react';
12
12
  import classNames from 'classnames';
13
13
  import styles from '@/Components/Menu/menu.module.scss';
14
- import type { IconSource } from '@/Components/Icon/Icon';
14
+ import type { IconSource } from '@/Components/Icon/BaseIcon';
15
15
  import type { RbmComponentProps, WithNoStringAndChildrenProps } from '@/Components/RbmComponentProps';
16
16
 
17
17
  export type SubmenuProps = RbmComponentProps<
@@ -100,15 +100,15 @@ export const Submenu = withMemo(function Submenu({
100
100
  style={style}
101
101
  >
102
102
  <Flex ref={submenuRef} className={classNames(styles.itemChildren)} horizontal={true}>
103
- {!!icon && <Icon icon={icon} color={iconColor} className={styles.icon} />}
103
+ {!!icon && <BaseIcon icon={icon} color={iconColor} className={styles.icon} />}
104
104
  <Grow>
105
105
  <Text>{label}</Text>
106
106
  </Grow>
107
- <Icon icon={faChevronRight} />
107
+ <BaseIcon icon={faChevronRight} />
108
108
  </Flex>
109
- <Block className={styles.container} __allowChildren="all" ref={containerRef}>
109
+ <BaseBlock className={styles.container} __allowChildren="all" ref={containerRef}>
110
110
  {children}
111
- </Block>
111
+ </BaseBlock>
112
112
  </Clickable>
113
113
  </MenuCloseContextProvider>
114
114
  );
@@ -30,6 +30,7 @@
30
30
  padding-top: 1px;
31
31
  margin-top: 1px;
32
32
  border-top: 1px solid var(--menu-divider-color);
33
+ width: 100%;
33
34
  }
34
35
 
35
36
  .item {
@@ -80,7 +81,7 @@
80
81
  pointer-events: none;
81
82
  }
82
83
 
83
- &.open:hover {
84
+ &.open {
84
85
  .menu {
85
86
  visibility: visible;
86
87
  pointer-events: initial;
@@ -113,7 +114,7 @@
113
114
  bottom: 3px;
114
115
  }
115
116
 
116
- &.open:hover {
117
+ &.open {
117
118
  .container {
118
119
  visibility: visible;
119
120
  pointer-events: initial;
@@ -0,0 +1,36 @@
1
+ import { useCallback, useState } from 'react';
2
+ import type { RefObject } from 'react';
3
+
4
+ export function useHoverMenu({ openToSide, ref }: { ref: RefObject<HTMLElement | null>; openToSide?: boolean }) {
5
+ const [isOpen, setIsOpen] = useState(false);
6
+ const [position, setPosition] = useState({ x: 0, y: 0 });
7
+ const [offset, setOffset] = useState({ x: 0, y: 0 });
8
+
9
+ // Dispatch
10
+
11
+ // Callbacks
12
+ const recalculatePosition = useCallback(() => {
13
+ if (!ref.current) {
14
+ return;
15
+ }
16
+ const { top, left, bottom, right, width, height } = ref.current.getBoundingClientRect();
17
+ if (openToSide) {
18
+ setPosition({ x: right, y: top });
19
+ setOffset({ x: width, y: -height });
20
+ } else {
21
+ setPosition({ x: left, y: bottom });
22
+ setOffset({ x: -width, y: height });
23
+ }
24
+ }, [openToSide, ref]);
25
+
26
+ const close = useCallback(() => {
27
+ setIsOpen(false);
28
+ }, []);
29
+
30
+ const open = useCallback(() => {
31
+ recalculatePosition();
32
+ setIsOpen(true);
33
+ }, [recalculatePosition]);
34
+
35
+ return { isOpen, position, open, close, offset };
36
+ }
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { InlineBlock } from '@/Components/Layout/InlineBlock';
2
+ import { BaseInlineBlock } from '@/Components/Layout/BaseInlineBlock';
3
3
  import { useEffect, useRef } from 'react';
4
4
  import { withMemo } from '@/helper/withMemo';
5
5
  import type { WithNoStringAndChildrenProps } from '@/Components/RbmComponentProps';
@@ -34,9 +34,9 @@ function SizeCalculator({ onSize, children, absolute = false }: SizeCalculatorPr
34
34
  // Render Functions
35
35
 
36
36
  return (
37
- <InlineBlock ref={ref} __allowChildren="all" style={{ position: absolute ? 'absolute' : 'static' }}>
37
+ <BaseInlineBlock ref={ref} __allowChildren="all" style={{ position: absolute ? 'absolute' : 'static' }}>
38
38
  {children}
39
- </InlineBlock>
39
+ </BaseInlineBlock>
40
40
  );
41
41
  }
42
42
 
@@ -1,9 +1,9 @@
1
1
  import * as React from 'react';
2
- import { Block } from '@/Components/Layout/Block';
2
+ import { BaseBlock } from '@/Components/Layout/BaseBlock';
3
+ import { BaseIcon } from '@/Components/Icon/BaseIcon';
3
4
  import { Clickable } from '@/Components/Clickable/Clickable';
4
5
  import { Flex } from '@/Components/Layout/Flex';
5
6
  import { Grow } from '@/Components/Layout/Grow';
6
- import { Icon } from '@/Components/Icon/Icon';
7
7
  import { TEXT_SIZE, Text } from '@/Components/Text/Text';
8
8
  import { faChevronDown, faChevronUp } from '@fortawesome/free-solid-svg-icons';
9
9
  import { useCallback, useEffect, useRef, useState } from 'react';
@@ -11,7 +11,7 @@ import { useListener } from '@/Components/Hooks/useListener';
11
11
  import { withMemo } from '@/helper/withMemo';
12
12
  import classNames from 'classnames';
13
13
  import styles from '@/Components/SpoilerList/Spoiler/spoiler.module.scss';
14
- import type { IconSource } from '@/Components/Icon/Icon';
14
+ import type { IconSource } from '@/Components/Icon/BaseIcon';
15
15
  import type { MouseEvent, ReactNode } from 'react';
16
16
  import type { OptionalListener } from '@/Components/Hooks/useListener';
17
17
  import type { RbmComponentProps, WithChildren } from '@/Components/RbmComponentProps';
@@ -59,7 +59,7 @@ export const Spoiler = withMemo(function Spoiler<OnClickData>({
59
59
  const onClickListener = useListener<'onClick', OnClickData, boolean>('onClick', listenerProps);
60
60
 
61
61
  const toggleOpen = useCallback(
62
- (ev: MouseEvent) => {
62
+ (_: MouseEvent) => {
63
63
  if (open !== undefined) {
64
64
  onClickListener?.(!open);
65
65
  } else {
@@ -100,14 +100,14 @@ export const Spoiler = withMemo(function Spoiler<OnClickData>({
100
100
  <Clickable onClick={toggleOpen}>
101
101
  <Flex horizontal={true}>
102
102
  <Grow __allowChildren="all">{titleComponent}</Grow>
103
- {icon ? <Icon icon={icon} className={styles.icon} /> : null}
103
+ {icon ? <BaseIcon icon={icon} className={styles.icon} /> : null}
104
104
  </Flex>
105
105
  </Clickable>
106
- <Block className={styles.bodyContainer}>
107
- <Block __allowChildren="all" className={styles.body}>
106
+ <BaseBlock className={styles.bodyContainer}>
107
+ <BaseBlock __allowChildren="all" className={styles.body}>
108
108
  {children}
109
- </Block>
110
- </Block>
109
+ </BaseBlock>
110
+ </BaseBlock>
111
111
  </Clickable>
112
112
  );
113
113
  }, 'all');
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { Block } from '@/Components/Layout/Block';
2
+ import { BaseBlock } from '@/Components/Layout/BaseBlock';
3
3
  import { Spoiler } from '@/Components/SpoilerList/Spoiler/Spoiler';
4
4
  import { useSpoilerGroup } from '@/Components/SpoilerList/useSpoilerGroup';
5
5
  import { withMemo } from '@/helper/withMemo';
@@ -48,12 +48,12 @@ export const SpoilerList = withMemo(function SpoilerList<BodyData, TitleData = s
48
48
 
49
49
  // Render Functions
50
50
  return (
51
- <Block className={classNames(className)} style={style}>
51
+ <BaseBlock className={classNames(className)} style={style}>
52
52
  {data.map((item) => (
53
53
  <Spoiler title={renderTitle(item)} {...propsGenerator(item.key)}>
54
54
  {renderBody(item)}
55
55
  </Spoiler>
56
56
  ))}
57
- </Block>
57
+ </BaseBlock>
58
58
  );
59
59
  });
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
+ import { BaseIcon } from '@/Components/Icon/BaseIcon';
2
3
  import { Container } from '@/Components/Layout/Container';
3
- import { Icon } from '@/Components/Icon/Icon';
4
4
  import { Inline } from '@/Components/Layout/Inline';
5
5
  import { TabBarButton } from '@/Components/TabBar/TabBarButton';
6
6
  import { Text } from '@/Components/Text/Text';
@@ -9,7 +9,7 @@ import { withMemo } from '@/helper/withMemo';
9
9
  import classNames from 'classnames';
10
10
  import styles from '@/Components/TabBar/tabBar.module.scss';
11
11
  import type { ComponentType } from 'react';
12
- import type { IconSource } from '@/Components/Icon/Icon';
12
+ import type { IconSource } from '@/Components/Icon/BaseIcon';
13
13
  import type { ListenerWithData } from '@/Components/Hooks/useListener';
14
14
  import type { RbmComponentProps } from '@/Components/RbmComponentProps';
15
15
 
@@ -55,7 +55,7 @@ function getButtonComponents(buttons: TabBarButtonType[], activeTab: number, onS
55
55
  return (
56
56
  <TabBarButton key={key} active={isActive} onClickData={index} onClick={onSelect}>
57
57
  <Inline>
58
- {button.icon ? <Icon icon={button.icon} className={styles.buttonIcon} /> : null}
58
+ {button.icon ? <BaseIcon icon={button.icon} className={styles.buttonIcon} /> : null}
59
59
  {button.title ? <Text className={styles.buttonTitle}>{button.title}</Text> : null}
60
60
  </Inline>
61
61
  </TabBarButton>
@@ -0,0 +1,97 @@
1
+ import { BaseInlineBlock } from '@/Components/Layout/BaseInlineBlock';
2
+ import { useClientLayoutEffect } from '@/Components/Hooks/useClientLayoutEffect';
3
+ import { useWindow } from '@/WindowContext/WindowContext';
4
+ import { withMemo } from '@/helper/withMemo';
5
+ import React, { useEffect, useRef, useState } from 'react';
6
+ import styles from '@/Components/Title/hoverTitle.module.scss';
7
+ import type { ReactElement, RefObject } from 'react';
8
+
9
+ export type HoverTitleProps = {
10
+ children: ReactElement | undefined;
11
+ baseElement: RefObject<HTMLElement | null>;
12
+ isOpen: boolean;
13
+ onClose: () => void;
14
+ };
15
+
16
+ export const HoverTitle = withMemo(function HoverTitle({ children, baseElement, isOpen, onClose }: HoverTitleProps) {
17
+ // Refs
18
+ const titleRef = useRef<HTMLDivElement>(null);
19
+
20
+ // States/Variables/Selectors
21
+ const [top, setTop] = useState(0);
22
+ const [left, setLeft] = useState(0);
23
+
24
+ const window = useWindow();
25
+
26
+ // Callbacks
27
+
28
+ // Effects
29
+ useEffect(() => {
30
+ if (!isOpen) {
31
+ return undefined;
32
+ }
33
+ const listener = (e: MouseEvent | TouchEvent) => {
34
+ if (!titleRef.current?.contains(e.target as Node)) {
35
+ onClose();
36
+ }
37
+ };
38
+
39
+ window?.addEventListener('mousedown', listener, { capture: true });
40
+ window?.addEventListener('touchstart', listener, { capture: true });
41
+ return () => {
42
+ window?.removeEventListener('mousedown', listener, { capture: true });
43
+ window?.removeEventListener('touchstart', listener, { capture: true });
44
+ };
45
+ }, [isOpen, onClose, window]);
46
+
47
+ useClientLayoutEffect(() => {
48
+ if (!titleRef.current || !baseElement.current || !isOpen) {
49
+ return undefined;
50
+ }
51
+ const titleElement = titleRef.current;
52
+ const base = baseElement.current;
53
+
54
+ const updateInnerPositions = () => {
55
+ const computedStyleElement = base.getBoundingClientRect();
56
+ const computedStyleTitle = getComputedStyle(titleElement);
57
+
58
+ const height = Number.parseFloat(computedStyleTitle.height);
59
+ let newY = computedStyleElement.top - height;
60
+ if (newY < 0) {
61
+ newY = computedStyleElement.top + computedStyleElement.height;
62
+ }
63
+ setTop(Math.min(window?.innerHeight ?? 1600, newY));
64
+
65
+ const width = Number.parseFloat(computedStyleTitle.width);
66
+ let newX = computedStyleElement.left;
67
+ if (newX > (window?.innerWidth ?? 0) - width) {
68
+ newX -= width;
69
+ }
70
+ setLeft(Math.max(0, newX));
71
+ };
72
+
73
+ const observer = new ResizeObserver(() => {
74
+ updateInnerPositions();
75
+ });
76
+ observer.observe(titleElement);
77
+ observer.observe(base);
78
+ updateInnerPositions();
79
+
80
+ return () => {
81
+ observer.disconnect();
82
+ };
83
+ }, [window, baseElement, isOpen]);
84
+
85
+ // Other
86
+
87
+ // RenderFunctions
88
+ if (!isOpen || !children) {
89
+ return null;
90
+ }
91
+
92
+ return (
93
+ <BaseInlineBlock ref={titleRef} style={{ top, left }} className={styles.title}>
94
+ {children}
95
+ </BaseInlineBlock>
96
+ );
97
+ });