@ainias42/react-bootstrap-mobile 0.1.15 → 0.1.17
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/bin/updateCopies.js +9 -5
- package/bootstrapReactMobile.ts +13 -2
- package/dist/bootstrapReactMobile.d.ts +13 -2
- package/dist/bootstrapReactMobile.js +992 -395
- package/dist/bootstrapReactMobile.js.map +1 -1
- package/dist/src/Components/Clickable/Clickable.d.ts +7 -3
- package/dist/src/Components/Dialog/DialogBackground.d.ts +4 -5
- package/dist/src/Components/Dialog/DialogContainer.d.ts +7 -3
- package/dist/src/Components/DragAndDrop/DragItem.d.ts +1 -1
- package/dist/src/Components/Flavor.d.ts +4 -0
- package/dist/src/Components/FormElements/Button/Button.d.ts +8 -4
- package/dist/src/Components/FormElements/Button/ButtonType.d.ts +4 -0
- package/dist/src/Components/FormElements/Input/FileInput/FileInput.d.ts +12 -0
- package/dist/src/Components/FormElements/Input/FileInput/FileType.d.ts +7 -0
- package/dist/src/Components/FormElements/Input/FileInput/MultipleFileInput.d.ts +17 -0
- package/dist/src/Components/FormElements/Select/Select.d.ts +2 -1
- package/dist/src/Components/FormElements/Switch/Switch.d.ts +4 -4
- package/dist/src/Components/Hooks/useMousePosition.d.ts +5 -0
- package/dist/src/Components/Hooks/useWindowDimensions.d.ts +4 -0
- package/dist/src/Components/Icon/Icon.d.ts +2 -3
- package/dist/src/Components/Layout/Grid/Grid.d.ts +2 -1
- package/dist/src/Components/Layout/Grid/GridItem.d.ts +4 -1
- package/dist/src/Components/Menu/HoverMenu.d.ts +9 -0
- package/dist/src/Components/Menu/Menu.d.ts +16 -7
- package/dist/src/Components/Menu/MenuCloseContext.d.ts +3 -0
- package/dist/src/Components/Menu/MenuDivider.d.ts +2 -0
- package/dist/src/Components/Menu/MenuItem.d.ts +23 -0
- package/dist/src/Components/Menu/Submenu.d.ts +12 -0
- package/dist/src/Components/Menu/useMenu.d.ts +1 -1
- package/dist/src/Components/RbmComponentProps.d.ts +4 -0
- package/dist/src/Components/Text/Text.d.ts +1 -0
- package/dist/src/ListRow/ListRow.d.ts +1 -0
- package/package.json +8 -7
- package/src/Components/Clickable/Clickable.tsx +135 -19
- package/src/Components/Dialog/DialogBackground.tsx +5 -8
- package/src/Components/Dialog/DialogContainer.tsx +12 -8
- package/src/Components/Dialog/DialogContext.ts +1 -2
- package/src/Components/Dialog/dialogBackground.scss +5 -1
- package/src/Components/DragAndDrop/DragItem.tsx +7 -7
- package/src/Components/DragAndDrop/DropArea.tsx +2 -1
- package/src/Components/Flavor.ts +4 -0
- package/src/Components/FormElements/Button/Button.tsx +31 -13
- package/src/Components/FormElements/Button/ButtonType.ts +4 -0
- package/src/Components/FormElements/Button/button.scss +22 -5
- package/src/Components/FormElements/Input/FileInput/FileInput.tsx +55 -0
- package/src/Components/FormElements/Input/FileInput/FileType.ts +1 -0
- package/src/Components/FormElements/Input/FileInput/MultipleFileInput.tsx +281 -0
- package/src/Components/FormElements/{ImageInput/imageInput.scss → Input/FileInput/fileInput.scss} +37 -7
- package/src/Components/FormElements/Input/input.scss +1 -1
- package/src/Components/FormElements/SearchSelectInput/SearchSelectInput.tsx +2 -2
- package/src/Components/FormElements/SearchSelectInput/seachSelectInput.scss +1 -1
- package/src/Components/FormElements/Select/Select.tsx +3 -2
- package/src/Components/FormElements/Select/select.scss +5 -0
- package/src/Components/FormElements/Switch/Switch.tsx +9 -8
- package/src/Components/FormElements/Switch/switch.scss +1 -0
- package/src/Components/Hooks/useMousePosition.ts +13 -0
- package/src/Components/Hooks/useWindowDimensions.ts +17 -0
- package/src/Components/Icon/Icon.tsx +18 -14
- package/src/Components/Icon/icon.scss +9 -0
- package/src/Components/Layout/Grid/Grid.tsx +3 -2
- package/src/Components/Layout/Grid/GridItem.tsx +16 -1
- package/src/Components/Layout/Grid/grid.scss +113 -36
- package/src/Components/Menu/HoverMenu.tsx +82 -0
- package/src/Components/Menu/Menu.tsx +101 -47
- package/src/Components/Menu/MenuCloseContext.ts +10 -0
- package/src/Components/Menu/MenuDivider.tsx +22 -0
- package/src/Components/Menu/MenuItem.tsx +95 -0
- package/src/Components/Menu/Submenu.tsx +101 -0
- package/src/Components/Menu/menu.scss +99 -10
- package/src/Components/Menu/useMenu.ts +1 -1
- package/src/Components/RbmComponentProps.ts +6 -0
- package/src/Components/Text/Text.tsx +1 -0
- package/src/Components/Text/text.scss +13 -5
- package/src/ListRow/ListRow.tsx +20 -0
- package/src/WrongChildError.ts +0 -2
- package/src/scss/_colors.scss +1 -1
- package/src/scss/_flavorMixin.scss +10 -0
- package/dist/src/Components/FormElements/ImageInput/ImageInput.d.ts +0 -18
- package/dist/src/Components/FormElements/ImageInput/MultipleFileInput.d.ts +0 -21
- package/src/Components/FormElements/ImageInput/ImageInput.tsx +0 -98
- package/src/Components/FormElements/ImageInput/MultipleFileInput.tsx +0 -240
|
@@ -8,12 +8,14 @@ import styles from './grid.scss';
|
|
|
8
8
|
|
|
9
9
|
export type GridItemProps = RbmComponentProps<{
|
|
10
10
|
size: number;
|
|
11
|
+
xs?: number;
|
|
11
12
|
sm?: number;
|
|
12
13
|
md?: number;
|
|
13
14
|
lg?: number;
|
|
14
15
|
xl?: number;
|
|
15
16
|
xxl?: number;
|
|
16
17
|
print?: number;
|
|
18
|
+
startXxs?: number;
|
|
17
19
|
startXs?: number;
|
|
18
20
|
startSm?: number;
|
|
19
21
|
startMd?: number;
|
|
@@ -21,6 +23,7 @@ export type GridItemProps = RbmComponentProps<{
|
|
|
21
23
|
startXl?: number;
|
|
22
24
|
startXxl?: number;
|
|
23
25
|
startPrint?: number;
|
|
26
|
+
orderXxs?: number;
|
|
24
27
|
orderXs?: number;
|
|
25
28
|
orderSm?: number;
|
|
26
29
|
orderMd?: number;
|
|
@@ -36,12 +39,14 @@ function GridItem({
|
|
|
36
39
|
className,
|
|
37
40
|
__allowChildren,
|
|
38
41
|
size,
|
|
42
|
+
xs,
|
|
39
43
|
sm,
|
|
40
44
|
md,
|
|
41
45
|
lg,
|
|
42
46
|
xl,
|
|
43
47
|
xxl,
|
|
44
48
|
print,
|
|
49
|
+
startXxs,
|
|
45
50
|
startXs,
|
|
46
51
|
startMd,
|
|
47
52
|
startSm,
|
|
@@ -49,6 +54,7 @@ function GridItem({
|
|
|
49
54
|
startXl,
|
|
50
55
|
startXxl,
|
|
51
56
|
startPrint,
|
|
57
|
+
orderXxs,
|
|
52
58
|
orderXs,
|
|
53
59
|
orderSm,
|
|
54
60
|
orderMd,
|
|
@@ -59,7 +65,10 @@ function GridItem({
|
|
|
59
65
|
}: GridItemProps) {
|
|
60
66
|
// Variables
|
|
61
67
|
|
|
62
|
-
const classes = [`item-
|
|
68
|
+
const classes = [`item-xxs-${size}`];
|
|
69
|
+
if (xs) {
|
|
70
|
+
classes.push(`item-xs-${xs}`);
|
|
71
|
+
}
|
|
63
72
|
if (sm) {
|
|
64
73
|
classes.push(`item-sm-${sm}`);
|
|
65
74
|
}
|
|
@@ -79,6 +88,9 @@ function GridItem({
|
|
|
79
88
|
classes.push(`item-print-${print}`);
|
|
80
89
|
}
|
|
81
90
|
|
|
91
|
+
if (startXxs) {
|
|
92
|
+
classes.push(`start-xxs-${startXxs}`);
|
|
93
|
+
}
|
|
82
94
|
if (startXs) {
|
|
83
95
|
classes.push(`start-xs-${startXs}`);
|
|
84
96
|
}
|
|
@@ -101,6 +113,9 @@ function GridItem({
|
|
|
101
113
|
classes.push(`start-print-${startPrint}`);
|
|
102
114
|
}
|
|
103
115
|
|
|
116
|
+
if (orderXxs) {
|
|
117
|
+
classes.push(`order-xxs-${orderXxs}`);
|
|
118
|
+
}
|
|
104
119
|
if (orderXs) {
|
|
105
120
|
classes.push(`order-xs-${orderXs}`);
|
|
106
121
|
}
|
|
@@ -1,8 +1,68 @@
|
|
|
1
1
|
@import "../../../scss/variables";
|
|
2
2
|
@import "bootstrap/scss/mixins/breakpoints";
|
|
3
3
|
|
|
4
|
+
$grid-breakpoints: (
|
|
5
|
+
xxs: 0,
|
|
6
|
+
xs: 320px,
|
|
7
|
+
sm: 576px,
|
|
8
|
+
md: 768px,
|
|
9
|
+
lg: 992px,
|
|
10
|
+
xl: 1200px,
|
|
11
|
+
xxl: 1400px
|
|
12
|
+
);
|
|
13
|
+
|
|
4
14
|
$columns: 12;
|
|
5
15
|
$breakpoints: $grid-breakpoints;
|
|
16
|
+
$containerBreakpoints: $grid-breakpoints;
|
|
17
|
+
|
|
18
|
+
@mixin printClasses {
|
|
19
|
+
@media print {
|
|
20
|
+
@for $i from 1 through $columns {
|
|
21
|
+
.item-print-#{$i} {
|
|
22
|
+
grid-column: auto / span $i;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Start with `1` because `0` is and invalid value.
|
|
27
|
+
// Ends with `$columns - 1` because offsetting by the width of an entire row isn't possible.
|
|
28
|
+
@for $i from 1 through ($columns - 1) {
|
|
29
|
+
.start-print-#{$i} {
|
|
30
|
+
grid-column-start: $i;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Add classes for reordering
|
|
35
|
+
@for $i from -10 through 10 {
|
|
36
|
+
.order-print-#{$i} {
|
|
37
|
+
order: $i;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@mixin contentMin($breakpointName) {
|
|
44
|
+
@for $i from 1 through $columns {
|
|
45
|
+
.item-#{$breakpointName}-#{$i} {
|
|
46
|
+
grid-column: auto / span $i;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Start with `1` because `0` is and invalid value.
|
|
51
|
+
// Ends with `$columns - 1` because offsetting by the width of an entire row isn't possible.
|
|
52
|
+
@for $i from 1 through ($columns - 1) {
|
|
53
|
+
.start-#{$breakpointName}-#{$i} {
|
|
54
|
+
grid-column-start: $i;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@mixin contentOnly($breakpointName) {
|
|
60
|
+
@for $i from -10 through 10 {
|
|
61
|
+
.order-#{$breakpointName}-#{$i} {
|
|
62
|
+
order: $i;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
6
66
|
|
|
7
67
|
.grid {
|
|
8
68
|
display: grid;
|
|
@@ -14,53 +74,70 @@ $breakpoints: $grid-breakpoints;
|
|
|
14
74
|
padding: 4px;
|
|
15
75
|
}
|
|
16
76
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
@for $i from 1 through $columns {
|
|
20
|
-
.item-#{$breakpoint}-#{$i} {
|
|
21
|
-
grid-column: auto / span $i;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
77
|
+
&.useContainerWidth {
|
|
78
|
+
container-type: inline-size;
|
|
24
79
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
@
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
80
|
+
@each $breakpoint in map-keys($containerBreakpoints) {
|
|
81
|
+
$min: breakpoint-min($breakpoint, $containerBreakpoints);
|
|
82
|
+
@if $min {
|
|
83
|
+
@container (min-width: #{$min}) {
|
|
84
|
+
@include contentMin($breakpoint);
|
|
85
|
+
}
|
|
86
|
+
} @else {
|
|
87
|
+
@include contentMin($breakpoint);
|
|
31
88
|
}
|
|
32
|
-
}
|
|
33
89
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
90
|
+
// Add classes for reordering
|
|
91
|
+
$min: breakpoint-min($breakpoint, $containerBreakpoints);
|
|
92
|
+
$next: breakpoint-next($breakpoint, $containerBreakpoints);
|
|
93
|
+
$max: breakpoint-max($next, $containerBreakpoints);
|
|
94
|
+
|
|
95
|
+
@if $min != null and $max != null {
|
|
96
|
+
@container (min-width: #{$min}) and (max-width: #{$max}) {
|
|
97
|
+
@include contentOnly($breakpoint);
|
|
98
|
+
}
|
|
99
|
+
} @else if $max == null {
|
|
100
|
+
@container (min-width: #{$min}) {
|
|
101
|
+
@include contentOnly($breakpoint);
|
|
102
|
+
}
|
|
103
|
+
} @else if $min == null {
|
|
104
|
+
@container (max-width: #{$max}) {
|
|
105
|
+
@include contentOnly($breakpoint);
|
|
106
|
+
}
|
|
40
107
|
}
|
|
41
108
|
}
|
|
109
|
+
|
|
110
|
+
@include printClasses;
|
|
42
111
|
}
|
|
43
112
|
|
|
44
|
-
|
|
45
|
-
@
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
113
|
+
&:not(.useContainerWidth) {
|
|
114
|
+
@each $breakpoint in map-keys($breakpoints) {
|
|
115
|
+
@include media-breakpoint-up($breakpoint, $breakpoints) {
|
|
116
|
+
@for $i from 1 through $columns {
|
|
117
|
+
.item-#{$breakpoint}-#{$i} {
|
|
118
|
+
grid-column: auto / span $i;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
50
121
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
122
|
+
// Start with `1` because `0` is and invalid value.
|
|
123
|
+
// Ends with `$columns - 1` because offsetting by the width of an entire row isn't possible.
|
|
124
|
+
@for $i from 1 through ($columns - 1) {
|
|
125
|
+
.start-#{$breakpoint}-#{$i} {
|
|
126
|
+
grid-column-start: $i;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
56
129
|
}
|
|
57
|
-
}
|
|
58
130
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
131
|
+
// Add classes for reordering
|
|
132
|
+
@include media-breakpoint-only($breakpoint, $breakpoints) {
|
|
133
|
+
@for $i from -10 through 10 {
|
|
134
|
+
.order-#{$breakpoint}-#{$i} {
|
|
135
|
+
order: $i;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
63
138
|
}
|
|
64
139
|
}
|
|
140
|
+
|
|
141
|
+
@include printClasses;
|
|
65
142
|
}
|
|
66
143
|
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import {withMemo} from "../../helper/withMemo";
|
|
2
|
+
import React, {useCallback, useRef, useState} from "react";
|
|
3
|
+
import {Clickable} from "../Clickable/Clickable";
|
|
4
|
+
import classNames from "classnames";
|
|
5
|
+
import styles from "./menu.scss";
|
|
6
|
+
import {RbmChildWithoutString, RbmComponentProps, WithNoStringAndChildrenProps} from "../RbmComponentProps";
|
|
7
|
+
import {Menu} from "./Menu";
|
|
8
|
+
|
|
9
|
+
export type HoverMenuProps = RbmComponentProps<{
|
|
10
|
+
items: RbmChildWithoutString,
|
|
11
|
+
openToSide?: boolean;
|
|
12
|
+
onClick?: () => void;
|
|
13
|
+
onClose?: () => void;
|
|
14
|
+
}, WithNoStringAndChildrenProps>;
|
|
15
|
+
|
|
16
|
+
export const HoverMenu = withMemo(function HoverMenu({
|
|
17
|
+
children,
|
|
18
|
+
items,
|
|
19
|
+
className,
|
|
20
|
+
style,
|
|
21
|
+
onClick,
|
|
22
|
+
onClose,
|
|
23
|
+
openToSide
|
|
24
|
+
}: HoverMenuProps) {
|
|
25
|
+
// Refs
|
|
26
|
+
|
|
27
|
+
// States/Variables/Selectors
|
|
28
|
+
const hoverItemRef = useRef<HTMLDivElement>(null);
|
|
29
|
+
|
|
30
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
31
|
+
const [position, setPosition] = useState({x: 0, y: 0});
|
|
32
|
+
const [offset, setOffset] = useState({x: 0, y: 0});
|
|
33
|
+
|
|
34
|
+
// Dispatch
|
|
35
|
+
|
|
36
|
+
// Callbacks
|
|
37
|
+
const recalculatePosition = useCallback(() => {
|
|
38
|
+
if (!hoverItemRef.current) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const {top, left, bottom, right, width, height} = hoverItemRef.current.getBoundingClientRect();
|
|
42
|
+
if (openToSide) {
|
|
43
|
+
setPosition({x: right, y: top});
|
|
44
|
+
setOffset({x: width, y: -height});
|
|
45
|
+
} else {
|
|
46
|
+
setPosition({x: left, y: bottom});
|
|
47
|
+
setOffset({x: -width, y: height});
|
|
48
|
+
}
|
|
49
|
+
}, [openToSide]);
|
|
50
|
+
|
|
51
|
+
const close = useCallback(() => {
|
|
52
|
+
setIsOpen(false);
|
|
53
|
+
onClose?.();
|
|
54
|
+
}, [onClose]);
|
|
55
|
+
|
|
56
|
+
const open = useCallback(() => {
|
|
57
|
+
recalculatePosition();
|
|
58
|
+
setIsOpen(true);
|
|
59
|
+
}, [recalculatePosition]);
|
|
60
|
+
|
|
61
|
+
// Effects
|
|
62
|
+
|
|
63
|
+
// Other
|
|
64
|
+
|
|
65
|
+
// RenderFunctions
|
|
66
|
+
|
|
67
|
+
return <Clickable
|
|
68
|
+
onMouseEnter={open}
|
|
69
|
+
onMouseLeave={close}
|
|
70
|
+
useReactOnMouseLeave={true}
|
|
71
|
+
onClick={onClick}
|
|
72
|
+
className={classNames(styles.hoverMenu, {[styles.open]: isOpen}, className)}
|
|
73
|
+
style={style}
|
|
74
|
+
ref={hoverItemRef}
|
|
75
|
+
__allowChildren="all"
|
|
76
|
+
>
|
|
77
|
+
{children}
|
|
78
|
+
<Menu x={position.x} y={position.y} isOpen={true} onClose={close} offsetX={offset.x} offsetY={offset.y} className={classNames({[styles.hidden]: !isOpen})}>
|
|
79
|
+
{items}
|
|
80
|
+
</Menu>
|
|
81
|
+
</Clickable>;
|
|
82
|
+
}, styles);
|
|
@@ -1,31 +1,54 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
2
|
+
import {withMemo} from '../../helper/withMemo';
|
|
3
|
+
import {RbmComponentProps} from '../RbmComponentProps';
|
|
4
|
+
import {IconSource} from '../Icon/Icon';
|
|
5
|
+
import {Block} from '../Layout/Block';
|
|
6
6
|
import classNames from 'classnames';
|
|
7
|
-
|
|
8
7
|
import styles from './menu.scss';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
8
|
+
import {useEffect, useLayoutEffect, useRef, useState} from 'react';
|
|
9
|
+
import {withRenderBrowserOnly} from '../../helper/withRenderBrowserOnly';
|
|
10
|
+
import {useWindow} from '../../WindowContext/WindowContext';
|
|
11
|
+
import {MenuItem} from "./MenuItem";
|
|
12
|
+
import {MenuCloseContextProvider} from "./MenuCloseContext";
|
|
13
|
+
import {createPortal} from "react-dom";
|
|
14
14
|
|
|
15
|
-
export type
|
|
15
|
+
export type MenuItemType = {
|
|
16
16
|
label: string;
|
|
17
|
-
|
|
18
|
-
icon?: IconSource;
|
|
17
|
+
icon?: IconSource | { icon: IconSource, color: string };
|
|
19
18
|
key: string;
|
|
19
|
+
className?: string;
|
|
20
|
+
callback: () => void;
|
|
21
|
+
onMouseEnter?: () => void;
|
|
22
|
+
onMouseLeave?: () => void;
|
|
20
23
|
};
|
|
21
24
|
|
|
22
25
|
export type MenuProps = RbmComponentProps<
|
|
23
|
-
{
|
|
24
|
-
|
|
26
|
+
{
|
|
27
|
+
items?: MenuItemType[];
|
|
28
|
+
x: number;
|
|
29
|
+
y: number;
|
|
30
|
+
isOpen: boolean;
|
|
31
|
+
onClose: () => void,
|
|
32
|
+
offsetX?: number,
|
|
33
|
+
offsetY?: number
|
|
34
|
+
}
|
|
25
35
|
>;
|
|
26
36
|
|
|
37
|
+
export const MENU_CONTAINER_CLASS = "rbm-menu-container";
|
|
38
|
+
|
|
27
39
|
export const Menu = withMemo(
|
|
28
|
-
withRenderBrowserOnly(function Menu({
|
|
40
|
+
withRenderBrowserOnly(function Menu({
|
|
41
|
+
className,
|
|
42
|
+
style,
|
|
43
|
+
items,
|
|
44
|
+
y,
|
|
45
|
+
x,
|
|
46
|
+
isOpen,
|
|
47
|
+
onClose,
|
|
48
|
+
children,
|
|
49
|
+
offsetY = 0,
|
|
50
|
+
offsetX = 0,
|
|
51
|
+
}: MenuProps) {
|
|
29
52
|
// Variables
|
|
30
53
|
|
|
31
54
|
// Refs
|
|
@@ -33,19 +56,16 @@ export const Menu = withMemo(
|
|
|
33
56
|
const window = useWindow();
|
|
34
57
|
|
|
35
58
|
// States
|
|
59
|
+
const [portalContainer] = useState<HTMLDivElement>(() => {
|
|
60
|
+
return document.createElement('div');
|
|
61
|
+
});
|
|
62
|
+
|
|
36
63
|
const [innerX, setInnerX] = useState(x);
|
|
37
64
|
const [innerY, setInnerY] = useState(y);
|
|
38
65
|
|
|
39
66
|
// Selectors
|
|
40
67
|
|
|
41
68
|
// Callbacks
|
|
42
|
-
const callItemCallback = useCallback(
|
|
43
|
-
(_: any, cb: () => void) => {
|
|
44
|
-
onClose();
|
|
45
|
-
cb();
|
|
46
|
-
},
|
|
47
|
-
[onClose]
|
|
48
|
-
);
|
|
49
69
|
|
|
50
70
|
// Effects
|
|
51
71
|
useEffect(() => {
|
|
@@ -55,20 +75,40 @@ export const Menu = withMemo(
|
|
|
55
75
|
onClose();
|
|
56
76
|
}
|
|
57
77
|
};
|
|
58
|
-
window?.addEventListener('mousedown', listener, {
|
|
59
|
-
return () => window?.removeEventListener('mousedown', listener, {
|
|
78
|
+
window?.addEventListener('mousedown', listener, {capture: true});
|
|
79
|
+
return () => window?.removeEventListener('mousedown', listener, {capture: true});
|
|
60
80
|
}
|
|
61
81
|
return undefined;
|
|
62
82
|
}, [isOpen, onClose, window]);
|
|
63
83
|
|
|
84
|
+
useLayoutEffect(() => {
|
|
85
|
+
if (!isOpen) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
let elem = window?.document.body.querySelector("." + MENU_CONTAINER_CLASS);
|
|
89
|
+
if (!elem) {
|
|
90
|
+
elem = window?.document.body;
|
|
91
|
+
}
|
|
92
|
+
elem?.appendChild(portalContainer)
|
|
93
|
+
}, [isOpen, portalContainer, window?.document.body]);
|
|
94
|
+
|
|
95
|
+
|
|
64
96
|
useLayoutEffect(() => {
|
|
65
97
|
if (!menuRef.current) {
|
|
66
98
|
return;
|
|
67
99
|
}
|
|
68
|
-
const
|
|
69
|
-
|
|
100
|
+
const width = parseFloat(getComputedStyle(menuRef.current).width);
|
|
101
|
+
let newX = x;
|
|
102
|
+
if (newX > (window?.innerWidth ?? 0) - width) {
|
|
103
|
+
newX -= width + offsetX;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (newX < 0) {
|
|
107
|
+
newX = 0;
|
|
108
|
+
}
|
|
109
|
+
|
|
70
110
|
setInnerX(newX);
|
|
71
|
-
}, [window?.innerWidth, x]);
|
|
111
|
+
}, [offsetX, window?.innerWidth, x]);
|
|
72
112
|
|
|
73
113
|
useLayoutEffect(() => {
|
|
74
114
|
if (!menuRef.current) {
|
|
@@ -77,10 +117,15 @@ export const Menu = withMemo(
|
|
|
77
117
|
const height = parseFloat(getComputedStyle(menuRef.current).height);
|
|
78
118
|
let newY = y;
|
|
79
119
|
if (newY > (window?.innerHeight ?? 0) - height) {
|
|
80
|
-
newY -= height;
|
|
120
|
+
newY -= height + offsetY;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (newY < 0) {
|
|
124
|
+
newY = 0;
|
|
81
125
|
}
|
|
126
|
+
|
|
82
127
|
setInnerY(newY);
|
|
83
|
-
}, [window?.innerHeight, y]);
|
|
128
|
+
}, [offsetY, window?.innerHeight, y]);
|
|
84
129
|
|
|
85
130
|
// Other
|
|
86
131
|
|
|
@@ -90,23 +135,32 @@ export const Menu = withMemo(
|
|
|
90
135
|
}
|
|
91
136
|
|
|
92
137
|
return (
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
138
|
+
<>
|
|
139
|
+
{createPortal(
|
|
140
|
+
<MenuCloseContextProvider value={onClose}>
|
|
141
|
+
<Block
|
|
142
|
+
className={classNames(className, styles.menu)}
|
|
143
|
+
style={{...style, top: innerY, left: innerX}}
|
|
144
|
+
ref={menuRef}
|
|
145
|
+
__allowChildren="all"
|
|
146
|
+
>
|
|
147
|
+
{items?.map((item) => {
|
|
148
|
+
const icon = (!!item.icon && typeof item.icon === "object" && "color" in item.icon) ? item.icon.icon : item.icon;
|
|
149
|
+
const iconColor = (!!item.icon && typeof item.icon === "object" && "color" in item.icon) ? item.icon.color : undefined;
|
|
150
|
+
|
|
151
|
+
return <MenuItem key={item.key}
|
|
152
|
+
onClick={item.callback}
|
|
153
|
+
className={classNames(styles.item, item.className)}
|
|
154
|
+
onMouseEnter={item.onMouseEnter}
|
|
155
|
+
icon={icon}
|
|
156
|
+
iconColor={iconColor}
|
|
157
|
+
onMouseLeave={item.onMouseLeave}>{item.label}</MenuItem>;
|
|
158
|
+
})}
|
|
159
|
+
{children}
|
|
160
|
+
</Block>
|
|
161
|
+
</MenuCloseContextProvider>
|
|
162
|
+
, portalContainer)}
|
|
163
|
+
</>
|
|
110
164
|
);
|
|
111
165
|
}),
|
|
112
166
|
styles
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React, {useContext} from "react";
|
|
2
|
+
|
|
3
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
4
|
+
const MenuCloseContext = React.createContext<() => void>(() => {});
|
|
5
|
+
|
|
6
|
+
export const MenuCloseContextProvider = MenuCloseContext.Provider;
|
|
7
|
+
|
|
8
|
+
export function useMenuClose() {
|
|
9
|
+
return useContext(MenuCloseContext);
|
|
10
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import {withMemo} from "../../helper/withMemo";
|
|
2
|
+
import {Block} from "../Layout/Block";
|
|
3
|
+
import styles from "./menu.scss";
|
|
4
|
+
import React from "react";
|
|
5
|
+
|
|
6
|
+
export const MenuDivider = withMemo(function MenuDivider() {
|
|
7
|
+
// Refs
|
|
8
|
+
|
|
9
|
+
// States/Variables/Selectors
|
|
10
|
+
|
|
11
|
+
// Dispatch
|
|
12
|
+
|
|
13
|
+
// Callbacks
|
|
14
|
+
|
|
15
|
+
// Effects
|
|
16
|
+
|
|
17
|
+
// Other
|
|
18
|
+
|
|
19
|
+
// RenderFunctions
|
|
20
|
+
|
|
21
|
+
return <Block className={styles.divider}/>;
|
|
22
|
+
}, styles);
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import {Icon, IconSource} from "../Icon/Icon";
|
|
2
|
+
import {withMemo} from "../../helper/withMemo";
|
|
3
|
+
import {Clickable} from "../Clickable/Clickable";
|
|
4
|
+
import classNames from "classnames";
|
|
5
|
+
import styles from "./menu.scss";
|
|
6
|
+
import {Text} from "../Text/Text";
|
|
7
|
+
import React, {ReactNode, useCallback} from "react";
|
|
8
|
+
import {RbmComponentProps, WithChildren} from "../RbmComponentProps";
|
|
9
|
+
import Element = React.JSX.Element;
|
|
10
|
+
import {useMenuClose} from "./MenuCloseContext";
|
|
11
|
+
import {Block} from "../Layout/Block";
|
|
12
|
+
|
|
13
|
+
export type MenuItemProps<Item = undefined> = RbmComponentProps<{
|
|
14
|
+
icon?: IconSource;
|
|
15
|
+
iconColor?: string;
|
|
16
|
+
className?: string;
|
|
17
|
+
children: string | ReactNode
|
|
18
|
+
disabled?: boolean;
|
|
19
|
+
active?: boolean;
|
|
20
|
+
} & ({
|
|
21
|
+
onClick: (item: Item) => void;
|
|
22
|
+
onMouseEnter?: (item: Item) => void;
|
|
23
|
+
onMouseLeave?: (item: Item) => void;
|
|
24
|
+
item?: undefined
|
|
25
|
+
} | {
|
|
26
|
+
onClick: (item: Item) => void;
|
|
27
|
+
onMouseEnter?: (item: Item) => void;
|
|
28
|
+
onMouseLeave?: (item: Item) => void;
|
|
29
|
+
item: Item
|
|
30
|
+
}), WithChildren>
|
|
31
|
+
|
|
32
|
+
export const MenuItem = withMemo(function MenuItem<Item>({
|
|
33
|
+
children,
|
|
34
|
+
icon,
|
|
35
|
+
iconColor,
|
|
36
|
+
className,
|
|
37
|
+
onClick,
|
|
38
|
+
onMouseEnter,
|
|
39
|
+
onMouseLeave,
|
|
40
|
+
active,
|
|
41
|
+
item,
|
|
42
|
+
disabled = false,
|
|
43
|
+
...props
|
|
44
|
+
}: MenuItemProps<Item>) {
|
|
45
|
+
// Refs
|
|
46
|
+
|
|
47
|
+
// States/Variables/Selectors
|
|
48
|
+
const close = useMenuClose();
|
|
49
|
+
|
|
50
|
+
// Dispatch
|
|
51
|
+
|
|
52
|
+
// Callbacks
|
|
53
|
+
const onClickInner = useCallback(() => {
|
|
54
|
+
if (disabled) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
onClick(item as Item);
|
|
58
|
+
close();
|
|
59
|
+
}, [close, disabled, item, onClick]);
|
|
60
|
+
|
|
61
|
+
const onMouseEnterInner = useCallback(() => {
|
|
62
|
+
if (disabled) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
onMouseEnter?.(item as Item);
|
|
66
|
+
}, [disabled, item, onMouseEnter]);
|
|
67
|
+
|
|
68
|
+
const onMouseLeaveInner = useCallback(() => {
|
|
69
|
+
if (disabled) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
onMouseLeave?.(item as Item);
|
|
73
|
+
}, [disabled, item, onMouseLeave]);
|
|
74
|
+
|
|
75
|
+
// Effects
|
|
76
|
+
|
|
77
|
+
// Other
|
|
78
|
+
const childElements = typeof children === "string" ? <Text>{children}</Text> : children as Element;
|
|
79
|
+
|
|
80
|
+
// RenderFunctions
|
|
81
|
+
|
|
82
|
+
return <Clickable
|
|
83
|
+
className={classNames(styles.item, {[styles.disabled]: disabled, [styles.active]: active}, className)}
|
|
84
|
+
{...props}
|
|
85
|
+
onClick={onClickInner}
|
|
86
|
+
onMouseEnter={onMouseEnterInner}
|
|
87
|
+
onMouseLeave={onMouseLeaveInner}
|
|
88
|
+
__allowChildren="all"
|
|
89
|
+
>
|
|
90
|
+
<Block className={classNames(styles.itemChildren)}>
|
|
91
|
+
{!!icon && <Icon icon={icon} color={iconColor} className={styles.icon}/>}
|
|
92
|
+
{childElements}
|
|
93
|
+
</Block>
|
|
94
|
+
</Clickable>;
|
|
95
|
+
}, styles, "text");
|