@ainias42/react-bootstrap-mobile 1.0.3 → 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.
- package/dist/Components/ActionSheet/ActionSheet.d.ts +1 -1
- package/dist/Components/ErrorBoundary.d.ts +18 -0
- package/dist/Components/FormElements/Button/Button.d.ts +4 -1
- package/dist/Components/FormElements/Button/Button.stories.d.ts +8 -2
- package/dist/Components/Icon/BaseIcon.d.ts +15 -0
- package/dist/Components/Icon/DoubleIcon.d.ts +1 -1
- package/dist/Components/Icon/Icon.d.ts +4 -13
- package/dist/Components/Icon/Icon.stories.d.ts +1 -1
- package/dist/Components/Layout/BaseBlock.d.ts +10 -0
- package/dist/Components/Layout/BaseInlineBlock.d.ts +10 -0
- package/dist/Components/Layout/Block.d.ts +4 -10
- package/dist/Components/Layout/Flex.d.ts +6 -1
- package/dist/Components/Layout/InlineBlock.d.ts +4 -10
- package/dist/Components/Layout/View.d.ts +1 -1
- package/dist/Components/Menu/Menu.d.ts +1 -1
- package/dist/Components/Menu/MenuItem.d.ts +1 -1
- package/dist/Components/Menu/Submenu.d.ts +1 -1
- package/dist/Components/Menu/useHoverMenu.d.ts +17 -0
- package/dist/Components/SpoilerList/Spoiler/Spoiler.d.ts +1 -1
- package/dist/Components/TabBar/TabBar.d.ts +1 -1
- package/dist/Components/Title/HoverTitle.d.ts +9 -0
- package/dist/Components/Title/Title.stories.d.ts +11 -0
- package/dist/Components/Title/withTitle.d.ts +6 -0
- package/dist/Components/TopBar/TopBar.d.ts +1 -1
- package/dist/index.css +6 -5
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +7 -0
- package/dist/index.js +707 -447
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/Components/ActionSheet/ActionSheet.tsx +12 -10
- package/src/Components/Colors.stories.tsx +7 -7
- package/src/Components/Dialog/ButtonDialog.tsx +9 -9
- package/src/Components/Dialog/Dialog.tsx +6 -6
- package/src/Components/Dialog/DialogBackground.tsx +5 -5
- package/src/Components/Dialog/DialogContainer.tsx +3 -3
- package/src/Components/ErrorBoundary.tsx +46 -0
- package/src/Components/FormElements/Button/Button.tsx +2 -2
- package/src/Components/FormElements/Error/FormError.tsx +6 -6
- package/src/Components/FormElements/Input/FileInput/MultipleFileInput.tsx +7 -7
- package/src/Components/FormElements/Input/PasswordInput/PasswordInput.tsx +2 -2
- package/src/Components/FormElements/SearchSelectInput/SearchSelectInput.tsx +9 -9
- package/src/Components/Hooks/useDelayed.ts +5 -4
- package/src/Components/Icon/{Icon.tsx → BaseIcon.tsx} +6 -4
- package/src/Components/Icon/DoubleIcon.tsx +7 -7
- package/src/Components/Icon/Icon.stories.tsx +3 -3
- package/src/Components/Icon/Icon.ts +4 -0
- package/src/Components/Layout/{Block.tsx → BaseBlock.tsx} +2 -3
- package/src/Components/Layout/{InlineBlock.tsx → BaseInlineBlock.tsx} +2 -2
- package/src/Components/Layout/Block.ts +4 -0
- package/src/Components/Layout/Flex.tsx +16 -0
- package/src/Components/Layout/Grid/Grid.tsx +3 -3
- package/src/Components/Layout/Grid/GridItem.tsx +3 -3
- package/src/Components/Layout/InlineBlock.ts +4 -0
- package/src/Components/Layout/layout.module.scss +13 -0
- package/src/Components/LoadingArea/LoadingArea.stories.tsx +3 -3
- package/src/Components/Menu/HoverMenu.stories.tsx +3 -3
- package/src/Components/Menu/HoverMenu.tsx +8 -28
- package/src/Components/Menu/Menu.tsx +4 -4
- package/src/Components/Menu/MenuDivider.tsx +2 -2
- package/src/Components/Menu/MenuItem.tsx +6 -6
- package/src/Components/Menu/Submenu.tsx +7 -7
- package/src/Components/Menu/menu.module.scss +3 -2
- package/src/Components/Menu/useHoverMenu.ts +36 -0
- package/src/Components/SizeCalculator/SizeCalculator.tsx +3 -3
- package/src/Components/SpoilerList/Spoiler/Spoiler.tsx +9 -9
- package/src/Components/SpoilerList/SpoilerList.tsx +3 -3
- package/src/Components/TabBar/TabBar.tsx +3 -3
- package/src/Components/Title/HoverTitle.tsx +97 -0
- package/src/Components/Title/Title.stories.tsx +95 -0
- package/src/Components/Title/hoverTitle.module.scss +8 -0
- package/src/Components/Title/withTitle.module.scss +7 -0
- package/src/Components/Title/withTitle.tsx +48 -0
- package/src/Components/TopBar/MoreButton.tsx +3 -3
- package/src/Components/TopBar/TopBar.tsx +6 -6
- package/src/WrongChildError.ts +1 -0
- package/src/helper/withRestrictedChildren.tsx +3 -1
- package/src/index.ts +7 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { JSX } from 'react/jsx-runtime';
|
|
3
3
|
import { ViewWithoutListeners } from '@/Components/Layout/ViewWithoutListeners';
|
|
4
|
-
import { withMemo } from '@/helper/withMemo';
|
|
5
4
|
import classNames from 'classnames';
|
|
6
5
|
import styles from '@/Components/Layout/layout.module.scss';
|
|
7
6
|
import type { RbmComponentProps } from '@/Components/RbmComponentProps';
|
|
8
7
|
import type { ViewWithoutListenersProps } from '@/Components/Layout/ViewWithoutListeners';
|
|
9
8
|
|
|
10
9
|
import IntrinsicElements = JSX.IntrinsicElements;
|
|
10
|
+
import { withMemo } from '@/helper/withMemo';
|
|
11
11
|
|
|
12
12
|
export type InlineBlockProps<AsType extends keyof IntrinsicElements> = RbmComponentProps<
|
|
13
13
|
ViewWithoutListenersProps<AsType> & {
|
|
@@ -16,7 +16,7 @@ export type InlineBlockProps<AsType extends keyof IntrinsicElements> = RbmCompon
|
|
|
16
16
|
}
|
|
17
17
|
>;
|
|
18
18
|
|
|
19
|
-
export const
|
|
19
|
+
export const BaseInlineBlock = withMemo(function BaseInlineBlock<AsType extends keyof JSX.IntrinsicElements = 'span'>({
|
|
20
20
|
children,
|
|
21
21
|
as = 'span' as AsType,
|
|
22
22
|
className,
|
|
@@ -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 {
|
|
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
|
-
<
|
|
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
|
-
</
|
|
59
|
+
</BaseBlock>
|
|
60
60
|
);
|
|
61
61
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import {
|
|
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
|
-
<
|
|
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
|
-
</
|
|
160
|
+
</BaseInlineBlock>
|
|
161
161
|
);
|
|
162
162
|
}
|
|
163
163
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
2
|
|
|
3
|
-
import {
|
|
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
|
-
<
|
|
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
|
-
</
|
|
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
|
|
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: <
|
|
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
|
|
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
|
|
59
|
-
|
|
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={
|
|
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={
|
|
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 {
|
|
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/
|
|
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
|
-
<
|
|
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
|
-
</
|
|
174
|
+
</BaseBlock>
|
|
175
175
|
</MenuCloseContextProvider>,
|
|
176
176
|
portalContainer,
|
|
177
177
|
)}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
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 <
|
|
21
|
+
return <BaseBlock className={styles.divider} />;
|
|
22
22
|
});
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import {
|
|
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/
|
|
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
|
-
<
|
|
101
|
-
{!!icon && <
|
|
100
|
+
<BaseBlock className={classNames(styles.itemChildren)}>
|
|
101
|
+
{!!icon && <BaseIcon icon={icon} color={iconColor} className={styles.icon} />}
|
|
102
102
|
{childElements}
|
|
103
|
-
</
|
|
103
|
+
</BaseBlock>
|
|
104
104
|
</Clickable>
|
|
105
105
|
);
|
|
106
106
|
}, 'text');
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
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/
|
|
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 && <
|
|
103
|
+
{!!icon && <BaseIcon icon={icon} color={iconColor} className={styles.icon} />}
|
|
104
104
|
<Grow>
|
|
105
105
|
<Text>{label}</Text>
|
|
106
106
|
</Grow>
|
|
107
|
-
<
|
|
107
|
+
<BaseIcon icon={faChevronRight} />
|
|
108
108
|
</Flex>
|
|
109
|
-
<
|
|
109
|
+
<BaseBlock className={styles.container} __allowChildren="all" ref={containerRef}>
|
|
110
110
|
{children}
|
|
111
|
-
</
|
|
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
|
|
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
|
|
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 {
|
|
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
|
-
<
|
|
37
|
+
<BaseInlineBlock ref={ref} __allowChildren="all" style={{ position: absolute ? 'absolute' : 'static' }}>
|
|
38
38
|
{children}
|
|
39
|
-
</
|
|
39
|
+
</BaseInlineBlock>
|
|
40
40
|
);
|
|
41
41
|
}
|
|
42
42
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import {
|
|
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/
|
|
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
|
-
(
|
|
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 ? <
|
|
103
|
+
{icon ? <BaseIcon icon={icon} className={styles.icon} /> : null}
|
|
104
104
|
</Flex>
|
|
105
105
|
</Clickable>
|
|
106
|
-
<
|
|
107
|
-
<
|
|
106
|
+
<BaseBlock className={styles.bodyContainer}>
|
|
107
|
+
<BaseBlock __allowChildren="all" className={styles.body}>
|
|
108
108
|
{children}
|
|
109
|
-
</
|
|
110
|
-
</
|
|
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 {
|
|
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
|
-
<
|
|
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
|
-
</
|
|
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/
|
|
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 ? <
|
|
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
|
+
});
|