@ainias42/react-bootstrap-mobile 0.1.14 → 0.1.16

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 (71) hide show
  1. package/bin/updateCopies.js +8 -5
  2. package/bootstrapReactMobile.ts +12 -2
  3. package/dist/bootstrapReactMobile.d.ts +12 -2
  4. package/dist/bootstrapReactMobile.js +841 -329
  5. package/dist/bootstrapReactMobile.js.map +1 -1
  6. package/dist/src/Components/Clickable/Clickable.d.ts +5 -3
  7. package/dist/src/Components/Dialog/DialogBackground.d.ts +4 -5
  8. package/dist/src/Components/Dialog/DialogContainer.d.ts +7 -3
  9. package/dist/src/Components/FormElements/Button/Button.d.ts +6 -4
  10. package/dist/src/Components/FormElements/Button/ButtonType.d.ts +4 -0
  11. package/dist/src/Components/FormElements/Input/FileInput/FileInput.d.ts +12 -0
  12. package/dist/src/Components/FormElements/Input/FileInput/FileType.d.ts +7 -0
  13. package/dist/src/Components/FormElements/Input/FileInput/MultipleFileInput.d.ts +17 -0
  14. package/dist/src/Components/FormElements/Select/Select.d.ts +2 -1
  15. package/dist/src/Components/FormElements/Switch/Switch.d.ts +4 -4
  16. package/dist/src/Components/Hooks/useMousePosition.d.ts +5 -0
  17. package/dist/src/Components/Hooks/useWindowDimensions.d.ts +4 -0
  18. package/dist/src/Components/Icon/Icon.d.ts +2 -3
  19. package/dist/src/Components/Layout/Grid/Grid.d.ts +2 -1
  20. package/dist/src/Components/Layout/Grid/GridItem.d.ts +4 -1
  21. package/dist/src/Components/Menu/HoverMenu.d.ts +7 -0
  22. package/dist/src/Components/Menu/Menu.d.ts +15 -7
  23. package/dist/src/Components/Menu/MenuCloseContext.d.ts +3 -0
  24. package/dist/src/Components/Menu/MenuDivider.d.ts +2 -0
  25. package/dist/src/Components/Menu/MenuItem.d.ts +23 -0
  26. package/dist/src/Components/Menu/Submenu.d.ts +10 -0
  27. package/dist/src/Components/Menu/useMenu.d.ts +1 -1
  28. package/dist/src/Components/RbmComponentProps.d.ts +4 -0
  29. package/dist/src/ListRow/ListRow.d.ts +1 -0
  30. package/package.json +8 -7
  31. package/src/Components/Clickable/Clickable.tsx +95 -13
  32. package/src/Components/Dialog/DialogBackground.tsx +5 -8
  33. package/src/Components/Dialog/DialogContainer.tsx +12 -8
  34. package/src/Components/Dialog/DialogContext.ts +1 -2
  35. package/src/Components/Dialog/dialogBackground.scss +5 -1
  36. package/src/Components/DragAndDrop/DropArea.tsx +2 -1
  37. package/src/Components/FormElements/Button/Button.tsx +15 -7
  38. package/src/Components/FormElements/Button/ButtonType.ts +4 -0
  39. package/src/Components/FormElements/Button/button.scss +22 -5
  40. package/src/Components/FormElements/Input/FileInput/FileInput.tsx +55 -0
  41. package/src/Components/FormElements/Input/FileInput/FileType.ts +1 -0
  42. package/src/Components/FormElements/Input/FileInput/MultipleFileInput.tsx +281 -0
  43. package/src/Components/FormElements/{ImageInput/imageInput.scss → Input/FileInput/fileInput.scss} +37 -7
  44. package/src/Components/FormElements/SearchSelectInput/SearchSelectInput.tsx +2 -2
  45. package/src/Components/FormElements/Select/Select.tsx +3 -2
  46. package/src/Components/FormElements/Select/select.scss +5 -0
  47. package/src/Components/FormElements/Switch/Switch.tsx +9 -8
  48. package/src/Components/FormElements/Switch/switch.scss +1 -0
  49. package/src/Components/Hooks/useMousePosition.ts +13 -0
  50. package/src/Components/Hooks/useWindowDimensions.ts +17 -0
  51. package/src/Components/Icon/Icon.tsx +8 -7
  52. package/src/Components/Icon/icon.scss +4 -0
  53. package/src/Components/Layout/Grid/Grid.tsx +3 -2
  54. package/src/Components/Layout/Grid/GridItem.tsx +16 -1
  55. package/src/Components/Layout/Grid/grid.scss +88 -17
  56. package/src/Components/Menu/HoverMenu.tsx +71 -0
  57. package/src/Components/Menu/Menu.tsx +78 -47
  58. package/src/Components/Menu/MenuCloseContext.ts +10 -0
  59. package/src/Components/Menu/MenuDivider.tsx +22 -0
  60. package/src/Components/Menu/MenuItem.tsx +95 -0
  61. package/src/Components/Menu/Submenu.tsx +95 -0
  62. package/src/Components/Menu/menu.scss +95 -10
  63. package/src/Components/Menu/useMenu.ts +1 -1
  64. package/src/Components/RbmComponentProps.ts +6 -0
  65. package/src/ListRow/ListRow.tsx +20 -0
  66. package/src/WrongChildError.ts +0 -2
  67. package/src/scss/_flavorMixin.scss +3 -0
  68. package/dist/src/Components/FormElements/ImageInput/ImageInput.d.ts +0 -18
  69. package/dist/src/Components/FormElements/ImageInput/MultipleFileInput.d.ts +0 -21
  70. package/src/Components/FormElements/ImageInput/ImageInput.tsx +0 -98
  71. package/src/Components/FormElements/ImageInput/MultipleFileInput.tsx +0 -240
@@ -5,6 +5,9 @@ import { Override } from '../../TypeHelpers';
5
5
  import { IconProp } from '@fortawesome/fontawesome-svg-core';
6
6
  import { withMemo } from '../../helper/withMemo';
7
7
  import { IconDefinition } from '@fortawesome/free-regular-svg-icons';
8
+ import classNames from "classnames";
9
+
10
+ import styles from "./icon.scss";
8
11
 
9
12
  export type IconSource = IconProp | string | IconDefinition;
10
13
 
@@ -12,13 +15,14 @@ export type IconProps = RbmComponentProps<
12
15
  Override<
13
16
  FontAwesomeIconProps,
14
17
  {
18
+ noMargin?: boolean
15
19
  icon: IconSource;
16
20
  alt?: string;
17
21
  }
18
22
  >
19
23
  >;
20
24
 
21
- function Icon({ icon, alt, className, style, title, ...props }: IconProps) {
25
+ export const Icon = withMemo(function Icon({ icon, alt, className, noMargin=true, style, title, ...props }: IconProps) {
22
26
  // Variables
23
27
 
24
28
  // States
@@ -37,10 +41,7 @@ function Icon({ icon, alt, className, style, title, ...props }: IconProps) {
37
41
  // Render Functions
38
42
 
39
43
  if (typeof icon === 'string' && icon.indexOf('.') !== -1) {
40
- return <img src={icon} alt={alt} className={className} style={style} title={title} />;
44
+ return <img src={icon} alt={alt} className={classNames(className, {[styles.margin]: !noMargin})} style={style} title={title} />;
41
45
  }
42
- return <FontAwesomeIcon {...props} icon={icon as IconProp} className={className} style={style} title={title} />;
43
- }
44
-
45
- const IconMemo = withMemo(Icon);
46
- export { IconMemo as Icon };
46
+ return <FontAwesomeIcon {...props} icon={icon as IconProp} className={classNames(className, {[styles.margin]: !noMargin})} style={style} title={title} />;
47
+ }, styles);
@@ -0,0 +1,4 @@
1
+ .margin {
2
+ margin-left: 0.3rem;
3
+ margin-right: 0.3rem;
4
+ }
@@ -10,9 +10,10 @@ import { useMemo } from 'react';
10
10
  export type GridProps = RbmComponentProps<{
11
11
  columns?: number;
12
12
  rows?: number;
13
+ useContainerWidth?: boolean;
13
14
  }>;
14
15
 
15
- function Grid({ style, children, columns = 12, rows = 1, className, __allowChildren }: GridProps) {
16
+ function Grid({ style, children, columns = 12, rows = 1, useContainerWidth = false, className, __allowChildren }: GridProps) {
16
17
  // Variables
17
18
  const appliedStyle = useMemo(
18
19
  () => ({
@@ -40,7 +41,7 @@ function Grid({ style, children, columns = 12, rows = 1, className, __allowChild
40
41
  return (
41
42
  <Block
42
43
  style={appliedStyle}
43
- className={classNames(styles.grid, className)}
44
+ className={classNames(styles.grid, className, {[styles.useContainerWidth]: useContainerWidth})}
44
45
  __allowChildren={__allowChildren as 'all'}
45
46
  >
46
47
  {children}
@@ -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-xs-${size}`];
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,43 @@
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 contentMin($breakpointName) {
19
+ @for $i from 1 through $columns {
20
+ .item-#{$breakpointName}-#{$i} {
21
+ grid-column: auto / span $i;
22
+ }
23
+ }
24
+
25
+ // Start with `1` because `0` is and invalid value.
26
+ // Ends with `$columns - 1` because offsetting by the width of an entire row isn't possible.
27
+ @for $i from 1 through ($columns - 1) {
28
+ .start-#{$breakpointName}-#{$i} {
29
+ grid-column-start: $i;
30
+ }
31
+ }
32
+ }
33
+
34
+ @mixin contentOnly($breakpointName) {
35
+ @for $i from -10 through 10 {
36
+ .order-#{$breakpointName}-#{$i} {
37
+ order: $i;
38
+ }
39
+ }
40
+ }
6
41
 
7
42
  .grid {
8
43
  display: grid;
@@ -14,28 +49,64 @@ $breakpoints: $grid-breakpoints;
14
49
  padding: 4px;
15
50
  }
16
51
 
17
- @each $breakpoint in map-keys($breakpoints) {
18
- @include media-breakpoint-up($breakpoint, $breakpoints) {
19
- @for $i from 1 through $columns {
20
- .item-#{$breakpoint}-#{$i} {
21
- grid-column: auto / span $i;
22
- }
52
+ &.useContainerWidth {
53
+ container-type: inline-size;
54
+
55
+ @each $breakpoint in map-keys($containerBreakpoints) {
56
+ $min: breakpoint-min($breakpoint, $containerBreakpoints);
57
+ @if $min {
58
+ @container (min-width: #{$min}) {
59
+ @include contentMin($breakpoint);
60
+ }
61
+ } @else {
62
+ @include contentMin($breakpoint);
23
63
  }
24
64
 
25
- // Start with `1` because `0` is and invalid value.
26
- // Ends with `$columns - 1` because offsetting by the width of an entire row isn't possible.
27
- @for $i from 1 through ($columns - 1) {
28
- .start-#{$breakpoint}-#{$i} {
29
- grid-column-start: $i;
30
- }
65
+ // Add classes for reordering
66
+ $min: breakpoint-min($breakpoint, $containerBreakpoints);
67
+ $next: breakpoint-next($breakpoint, $containerBreakpoints);
68
+ $max: breakpoint-max($next, $containerBreakpoints);
69
+
70
+ @if $min != null and $max != null {
71
+ @container (min-width: #{$min}) and (max-width: #{$max}) {
72
+ @include contentOnly($breakpoint);
73
+ }
74
+ } @else if $max == null {
75
+ @container (min-width: #{$min}) {
76
+ @include contentOnly($breakpoint);
77
+ }
78
+ } @else if $min == null {
79
+ @container (max-width: #{$max}) {
80
+ @include contentOnly($breakpoint);
81
+ }
31
82
  }
32
83
  }
84
+ }
33
85
 
34
- // Add classes for reordering
35
- @include media-breakpoint-only($breakpoint, $breakpoints) {
36
- @for $i from -10 through 10 {
37
- .order-#{$breakpoint}-#{$i} {
38
- order: $i;
86
+ &:not(.useContainerWidth) {
87
+ @each $breakpoint in map-keys($breakpoints) {
88
+ @include media-breakpoint-up($breakpoint, $breakpoints) {
89
+ @for $i from 1 through $columns {
90
+ .item-#{$breakpoint}-#{$i} {
91
+ grid-column: auto / span $i;
92
+ }
93
+ }
94
+
95
+ // Start with `1` because `0` is and invalid value.
96
+ // Ends with `$columns - 1` because offsetting by the width of an entire row isn't possible.
97
+ @for $i from 1 through ($columns - 1) {
98
+ .start-#{$breakpoint}-#{$i} {
99
+ grid-column-start: $i;
100
+ }
101
+ }
102
+ }
103
+
104
+ // Add classes for reordering
105
+ @include media-breakpoint-only($breakpoint, $breakpoints) {
106
+ @for $i from -10 through 10 {
107
+ .order-#{$breakpoint}-#{$i} {
108
+ order: $i;
109
+ }
39
110
  }
40
111
  }
41
112
  }
@@ -0,0 +1,71 @@
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
+ onClick?: () => void;
12
+ }, WithNoStringAndChildrenProps>;
13
+
14
+ export const HoverMenu = withMemo(function HoverMenu({
15
+ children,
16
+ items,
17
+ className,
18
+ style,
19
+ onClick,
20
+ }: HoverMenuProps) {
21
+ // Refs
22
+
23
+ // States/Variables/Selectors
24
+ const hoverItemRef = useRef<HTMLDivElement>(null);
25
+
26
+ const [isOpen, setIsOpen] = useState(false);
27
+ const [position, setPosition] = useState({x: 0, y: 0});
28
+ const [offset, setOffset] = useState({x: 0, y: 0});
29
+
30
+ // Dispatch
31
+
32
+ // Callbacks
33
+ const recalculatePosition = useCallback(() => {
34
+ if (!hoverItemRef.current) {
35
+ return;
36
+ }
37
+ const {left, bottom, width, height} = hoverItemRef.current.getBoundingClientRect();
38
+ setPosition({x: left, y: bottom});
39
+ setOffset({x: -width, y: height});
40
+ }, []);
41
+
42
+ const close = useCallback(() => {
43
+ setIsOpen(false);
44
+ }, []);
45
+
46
+ const open = useCallback(() => {
47
+ recalculatePosition();
48
+ setIsOpen(true);
49
+ }, [recalculatePosition]);
50
+
51
+ // Effects
52
+
53
+ // Other
54
+
55
+ // RenderFunctions
56
+
57
+ return <Clickable
58
+ onMouseEnter={open}
59
+ onMouseLeave={close}
60
+ onClick={onClick}
61
+ className={classNames(styles.hoverMenu, {[styles.open]: isOpen}, className)}
62
+ style={style}
63
+ ref={hoverItemRef}
64
+ __allowChildren="all"
65
+ >
66
+ {children}
67
+ <Menu x={position.x} y={position.y} isOpen={true} onClose={close} offsetX={offset.x} offsetY={offset.y}>
68
+ {items}
69
+ </Menu>
70
+ </Clickable>;
71
+ }, styles);
@@ -1,31 +1,51 @@
1
1
  import * as React from 'react';
2
- import { withMemo } from '../../helper/withMemo';
3
- import { RbmComponentProps, WithNoChildren } from '../RbmComponentProps';
4
- import { Icon, IconSource } from '../Icon/Icon';
5
- import { Block } from '../Layout/Block';
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 { Text } from '../Text/Text';
10
- import { Clickable } from '../Clickable/Clickable';
11
- import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
12
- import { withRenderBrowserOnly } from '../../helper/withRenderBrowserOnly';
13
- import { useWindow } from '../../WindowContext/WindowContext';
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";
14
13
 
15
- export type MenuItem = {
14
+ export type MenuItemType = {
16
15
  label: string;
17
- callback: () => void;
18
- icon?: IconSource;
16
+ icon?: IconSource | { icon: IconSource, color: string };
19
17
  key: string;
18
+ className?: string;
19
+ callback: () => void;
20
+ onMouseEnter?: () => void;
21
+ onMouseLeave?: () => void;
20
22
  };
21
23
 
22
24
  export type MenuProps = RbmComponentProps<
23
- { items: MenuItem[]; x: number; y: number; isOpen: boolean; onClose: () => void },
24
- WithNoChildren
25
+ {
26
+ items?: MenuItemType[];
27
+ x: number;
28
+ y: number;
29
+ isOpen: boolean;
30
+ onClose: () => void,
31
+ offsetX?: number,
32
+ offsetY?: number
33
+ }
25
34
  >;
26
35
 
27
36
  export const Menu = withMemo(
28
- withRenderBrowserOnly(function Menu({ className, style, items, y, x, isOpen, onClose }: MenuProps) {
37
+ withRenderBrowserOnly(function Menu({
38
+ className,
39
+ style,
40
+ items,
41
+ y,
42
+ x,
43
+ isOpen,
44
+ onClose,
45
+ children,
46
+ offsetY = 0,
47
+ offsetX = 0,
48
+ }: MenuProps) {
29
49
  // Variables
30
50
 
31
51
  // Refs
@@ -39,13 +59,6 @@ export const Menu = withMemo(
39
59
  // Selectors
40
60
 
41
61
  // Callbacks
42
- const callItemCallback = useCallback(
43
- (_: any, cb: () => void) => {
44
- onClose();
45
- cb();
46
- },
47
- [onClose]
48
- );
49
62
 
50
63
  // Effects
51
64
  useEffect(() => {
@@ -55,8 +68,8 @@ export const Menu = withMemo(
55
68
  onClose();
56
69
  }
57
70
  };
58
- window?.addEventListener('mousedown', listener, { capture: true });
59
- return () => window?.removeEventListener('mousedown', listener, { capture: true });
71
+ window?.addEventListener('mousedown', listener, {capture: true});
72
+ return () => window?.removeEventListener('mousedown', listener, {capture: true});
60
73
  }
61
74
  return undefined;
62
75
  }, [isOpen, onClose, window]);
@@ -65,10 +78,18 @@ export const Menu = withMemo(
65
78
  if (!menuRef.current) {
66
79
  return;
67
80
  }
68
- const { width } = getComputedStyle(menuRef.current);
69
- const newX = Math.min(x, (window?.innerWidth ?? 0) - parseFloat(width));
81
+ const width = parseFloat(getComputedStyle(menuRef.current).width);
82
+ let newX = x;
83
+ if (newX > (window?.innerWidth ?? 0) - width) {
84
+ newX -= width+offsetX;
85
+ }
86
+
87
+ if (newX < 0) {
88
+ newX = 0;
89
+ }
90
+
70
91
  setInnerX(newX);
71
- }, [window?.innerWidth, x]);
92
+ }, [offsetX, window?.innerWidth, x]);
72
93
 
73
94
  useLayoutEffect(() => {
74
95
  if (!menuRef.current) {
@@ -77,10 +98,15 @@ export const Menu = withMemo(
77
98
  const height = parseFloat(getComputedStyle(menuRef.current).height);
78
99
  let newY = y;
79
100
  if (newY > (window?.innerHeight ?? 0) - height) {
80
- newY -= height;
101
+ newY -= height+offsetY;
102
+ }
103
+
104
+ if (newY < 0) {
105
+ newY = 0;
81
106
  }
107
+
82
108
  setInnerY(newY);
83
- }, [window?.innerHeight, y]);
109
+ }, [offsetY, window?.innerHeight, y]);
84
110
 
85
111
  // Other
86
112
 
@@ -90,23 +116,28 @@ export const Menu = withMemo(
90
116
  }
91
117
 
92
118
  return (
93
- <Block
94
- className={classNames(className, styles.menu)}
95
- style={{ ...style, top: innerY, left: innerX }}
96
- ref={menuRef}
97
- >
98
- {items.map((item) => (
99
- <Clickable
100
- onClick={callItemCallback}
101
- onClickData={item.callback}
102
- className={styles.item}
103
- key={item.key}
104
- >
105
- {!!item.icon && <Icon icon={item.icon} />}
106
- <Text>{item.label}</Text>
107
- </Clickable>
108
- ))}
109
- </Block>
119
+ <MenuCloseContextProvider value={onClose}>
120
+ <Block
121
+ className={classNames(className, styles.menu)}
122
+ style={{...style, top: innerY, left: innerX}}
123
+ ref={menuRef}
124
+ __allowChildren="all"
125
+ >
126
+ {items?.map((item) => {
127
+ const icon = (!!item.icon && typeof item.icon === "object" && "color" in item.icon) ? item.icon.icon : item.icon;
128
+ const iconColor = (!!item.icon && typeof item.icon === "object" && "color" in item.icon) ? item.icon.color : undefined;
129
+
130
+ return <MenuItem key={item.key}
131
+ onClick={item.callback}
132
+ className={classNames(styles.item, item.className)}
133
+ onMouseEnter={item.onMouseEnter}
134
+ icon={icon}
135
+ iconColor={iconColor}
136
+ onMouseLeave={item.onMouseLeave}>{item.label}</MenuItem>;
137
+ })}
138
+ {children}
139
+ </Block>
140
+ </MenuCloseContextProvider>
110
141
  );
111
142
  }),
112
143
  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");