@ainias42/react-bootstrap-mobile 0.1.15 → 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 +840 -328
  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 +94 -12
  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
@@ -0,0 +1,95 @@
1
+ import {RbmComponentProps, WithNoStringAndChildrenProps} from "../RbmComponentProps";
2
+ import {Icon, IconSource} from "../Icon/Icon";
3
+ import {Clickable} from "../Clickable/Clickable";
4
+ import classNames from "classnames";
5
+ import styles from "./menu.scss";
6
+ import React, {useCallback, useRef, useState} from "react";
7
+ import {Text} from "../Text/Text";
8
+ import {withMemo} from "../../helper/withMemo";
9
+ import {Block} from "../Layout/Block";
10
+ import {useWindow} from "../../WindowContext/WindowContext";
11
+ import {Flex} from "../Layout/Flex";
12
+ import {Grow} from "../Layout/Grow";
13
+ import {faChevronRight} from "@fortawesome/free-solid-svg-icons";
14
+ import {MenuCloseContextProvider, useMenuClose} from "./MenuCloseContext";
15
+
16
+ export type SubmenuProps = RbmComponentProps<{
17
+ label: string, icon?: IconSource;
18
+ iconColor?: string;
19
+ disabled?: boolean;
20
+ }, WithNoStringAndChildrenProps>;
21
+
22
+ export const Submenu = withMemo(function Submenu({
23
+ children,
24
+ label,
25
+ icon,
26
+ iconColor,
27
+ className,
28
+ style,
29
+ disabled = false
30
+ }: SubmenuProps) {
31
+ // Refs
32
+
33
+ // States/Variables/Selectors
34
+ const window = useWindow();
35
+ const containerRef = useRef<HTMLDivElement>(null);
36
+ const submenuRef = useRef<HTMLDivElement>(null);
37
+
38
+ const [openLeft, setOpenLeft] = useState(false);
39
+ const [openTop, setOpenTop] = useState(false);
40
+ const [isOpen, setIsOpen] = useState(false);
41
+
42
+ // Dispatch
43
+
44
+ // Callbacks
45
+ const recalculateSubmenuPosition = useCallback(() => {
46
+ if (!submenuRef.current || !containerRef.current) {
47
+ return;
48
+ }
49
+ const {width, height} = getComputedStyle(containerRef.current);
50
+ const {right, top} = submenuRef.current.getBoundingClientRect();
51
+
52
+ setOpenLeft(right + parseFloat(width) >= (window?.innerWidth ?? 0));
53
+ setOpenTop(top + parseFloat(height) >= (window?.innerHeight ?? 0));
54
+ setIsOpen(true);
55
+ }, [window?.innerHeight, window?.innerWidth]);
56
+
57
+ const closeSubmenu = useCallback(() => {
58
+ setIsOpen(false);
59
+ }, []);
60
+
61
+ const closeParent = useMenuClose();
62
+ const closeAllMenus = useCallback(() => {
63
+ closeSubmenu();
64
+ closeParent();
65
+ }, [closeParent, closeSubmenu]);
66
+
67
+ // Effects
68
+
69
+ // Other
70
+
71
+ // RenderFunctions
72
+
73
+ return <MenuCloseContextProvider value={closeAllMenus}>
74
+ <Clickable
75
+ onMouseEnter={recalculateSubmenuPosition}
76
+ onMouseLeave={closeSubmenu}
77
+ className={classNames(styles.item, styles.submenu, {
78
+ [styles.open]: isOpen,
79
+ [styles.openLeft]: openLeft,
80
+ [styles.openTop]: openTop,
81
+ [styles.disabled]: disabled
82
+ }, className)}
83
+ style={style}
84
+ >
85
+ <Flex ref={submenuRef} className={classNames(styles.itemChildren)} horizontal={true}>
86
+ {!!icon && <Icon icon={icon} color={iconColor} className={styles.icon}/>}
87
+ <Grow><Text>{label}</Text></Grow>
88
+ <Icon icon={faChevronRight}/>
89
+ </Flex>
90
+ <Block className={styles.container} __allowChildren="all" ref={containerRef}>
91
+ {children}
92
+ </Block>
93
+ </Clickable>
94
+ </MenuCloseContextProvider>;
95
+ }, styles);
@@ -1,21 +1,106 @@
1
-
2
1
  .menu {
3
2
  position: fixed;
4
3
  background: #ffffff;
5
4
  border: 1px solid var(--border-light);
5
+ border-radius: 2px;
6
+ z-index: 1000;
7
+ }
8
+
9
+ .divider {
10
+ margin-left: 4px;
11
+ margin-right: 4px;
12
+ padding-top: 1px;
13
+ margin-top: 1px;
14
+ border-top: 1px solid var(--border-light);
15
+ }
16
+
17
+ .item {
18
+ padding: 0 4px;
19
+ display: flex;
20
+ //background: #ffffff;
21
+
22
+ &.active {
23
+ background-color: #e1e1e1;
24
+ }
25
+
26
+ .itemChildren {
27
+ flex: 1;
28
+ padding: 1px;
29
+ display: flex;
30
+ flex-direction: row;
31
+ align-items: center;
32
+ //border-top: 1px solid var(--border-light);
33
+ white-space: nowrap;
6
34
 
7
- .item {
8
- display: block;
9
- border-top: 1px solid var(--border-light);
10
- padding: 8px;
11
- margin: 0 4px;
35
+ .icon {
36
+ padding-right: 4px;
37
+ }
38
+ }
12
39
 
13
- &:hover {
14
- background-color: #f5f5f5;
40
+ &.disabled {
41
+ pointer-events: none;
42
+ cursor: default;
43
+
44
+ > .itemChildren > * {
45
+ opacity: 0.3;
15
46
  }
47
+ }
48
+
49
+ &:hover {
50
+ background-color: #f5f5f5;
51
+ }
52
+
53
+ &:first-child > .itemChildren {
54
+ border-top: none;
55
+ }
56
+ }
57
+
58
+ .hoverMenu {
59
+ padding: 0;
60
+ display: inline;
61
+
62
+ .menu {
63
+ visibility: hidden;
64
+ pointer-events: none;
65
+ }
66
+
67
+ &.open:hover {
68
+ .menu {
69
+ visibility: visible;
70
+ pointer-events: initial;
71
+ }
72
+ }
73
+ }
74
+
75
+ .submenu {
76
+ position: relative;
77
+
78
+ .container {
79
+ visibility: hidden;
80
+ pointer-events: none;
81
+ position: absolute;
82
+ top: 3px;
83
+ left: calc(100% - 3px);
84
+ background: #ffffff;
85
+ border: 1px solid var(--border-light);
86
+ border-radius: 2px;
87
+ z-index: 10;
88
+ }
89
+
90
+ &.openLeft .container {
91
+ left: initial;
92
+ right: calc(100% - 3px);
93
+ }
94
+
95
+ &.openTop .container {
96
+ top: initial;
97
+ bottom: 3px;
98
+ }
16
99
 
17
- &:first-child {
18
- border-top: none;
100
+ &.open:hover {
101
+ .container {
102
+ visibility: visible;
103
+ pointer-events: initial;
19
104
  }
20
105
  }
21
106
  }
@@ -9,7 +9,7 @@ export function useMenu() {
9
9
  setIsOpen(true);
10
10
  }, []);
11
11
 
12
- const props: Omit<MenuProps, 'items'> = {
12
+ const props: Omit<MenuProps, 'items'|"children"> = {
13
13
  isOpen,
14
14
  x: position.x,
15
15
  y: position.y,
@@ -26,6 +26,12 @@ export type WithStringProps = {
26
26
  children?: Recursive<string>;
27
27
  };
28
28
 
29
+ export type WithChildren =
30
+ | {
31
+ children: ReactNode
32
+ __allowChildren?: "text"|"all"|"html";
33
+ };
34
+
29
35
  export type WithStringAndChildrenProps = {
30
36
  children: Recursive<string>;
31
37
  };
@@ -0,0 +1,20 @@
1
+ import {withMemo} from "../helper/withMemo";
2
+
3
+
4
+ export const ListRow = withMemo(function ListRow() {
5
+ // Refs
6
+
7
+ // States/Variables/Selectors
8
+
9
+ // Dispatch
10
+
11
+ // Callbacks
12
+
13
+ // Effects
14
+
15
+ // Other
16
+
17
+ // RenderFunctions
18
+
19
+ return null;
20
+ });
@@ -13,7 +13,5 @@ export class WrongChildError extends Error {
13
13
  );
14
14
  this.name = 'WrongChildError';
15
15
  this.childValue = child;
16
-
17
- console.log('LOG-d stack', this.stack);
18
16
  }
19
17
  }
@@ -0,0 +1,3 @@
1
+ @mixin flavorSelection($varName) {
2
+ #{$varName}: var(--flavor-accent);
3
+ }
@@ -1,18 +0,0 @@
1
- import * as React from 'react';
2
- import { RbmComponentProps } from '../../RbmComponentProps';
3
- import { Override } from '../../../TypeHelpers';
4
- import { InputHTMLAttributes } from 'react';
5
- import { OptionalListener } from '../../Hooks/useListener';
6
- export type ImageType = {
7
- name: string;
8
- url: string;
9
- };
10
- export type ImageInputProps<OnChangeData> = RbmComponentProps<Override<InputHTMLAttributes<HTMLInputElement>, {
11
- defaultValue?: ImageType;
12
- value?: ImageType;
13
- label?: string;
14
- onChangeImage?: (image: ImageType, imageData: File) => void;
15
- } & OptionalListener<'onChange', OnChangeData>>>;
16
- declare function ImageInput<OnChangeData>({ className, style, value, defaultValue, onChangeImage, label, ...otherProps }: ImageInputProps<OnChangeData>): React.JSX.Element;
17
- declare const ImageInputMemo: typeof ImageInput;
18
- export { ImageInputMemo as ImageInput };
@@ -1,21 +0,0 @@
1
- import * as React from 'react';
2
- import { RbmComponentProps } from '../../RbmComponentProps';
3
- import { Override } from '../../../TypeHelpers';
4
- import { InputHTMLAttributes } from 'react';
5
- import { Listener } from '../../Hooks/useListener';
6
- export type FileType = {
7
- name: string;
8
- url: string;
9
- mimeType: string;
10
- uploaded?: boolean;
11
- blob?: Blob;
12
- };
13
- export type MultipleImageInputProps<OnChangeFilesData> = RbmComponentProps<Override<Omit<InputHTMLAttributes<HTMLInputElement>, 'defaultValue' | 'onChange'>, {
14
- value: FileType[];
15
- label?: string;
16
- mimeTypes?: string[];
17
- maxFiles?: number;
18
- maxSizePerFile?: number;
19
- onError?: (error: string) => void;
20
- } & Listener<'onChangeFiles', OnChangeFilesData, FileType[]>>>;
21
- export declare const MultipleFileInput: <OnChangeData>({ className, style, value, label, mimeTypes, maxFiles, maxSizePerFile, onError, ...otherProps }: MultipleImageInputProps<OnChangeData>) => React.JSX.Element;
@@ -1,98 +0,0 @@
1
- import * as React from 'react';
2
- import { RbmComponentProps } from '../../RbmComponentProps';
3
- import { Override } from '../../../TypeHelpers';
4
- import { ChangeEventHandler, InputHTMLAttributes, useCallback, useState } from 'react';
5
- import { OptionalListener, useListener } from '../../Hooks/useListener';
6
- import { Button } from '../Button/Button';
7
-
8
- import styles from './imageInput.scss';
9
- import { withMemo } from '../../../helper/withMemo';
10
- import classNames from 'classnames';
11
-
12
- export type ImageType = { name: string; url: string };
13
-
14
- export type ImageInputProps<OnChangeData> = RbmComponentProps<
15
- Override<
16
- InputHTMLAttributes<HTMLInputElement>,
17
- {
18
- defaultValue?: ImageType;
19
- value?: ImageType;
20
- label?: string;
21
- onChangeImage?: (image: ImageType, imageData: File) => void;
22
- } & OptionalListener<'onChange', OnChangeData>
23
- >
24
- >;
25
-
26
- // TODO use MultipleFileInput internal
27
- function ImageInput<OnChangeData>({
28
- className,
29
- style,
30
- value,
31
- defaultValue,
32
- onChangeImage,
33
- label,
34
- ...otherProps
35
- }: ImageInputProps<OnChangeData>) {
36
- // Variables
37
- const [image, setImage] = useState(defaultValue);
38
-
39
- // Refs
40
-
41
- // States
42
-
43
- // Selectors
44
-
45
- // Callbacks
46
- const onChangeWithData = useListener<'onChange', OnChangeData>('onChange', otherProps);
47
- const getBase64 = useCallback((file: Blob) => {
48
- return new Promise<string>((resolve, reject) => {
49
- const reader = new FileReader();
50
- reader.onload = () => {
51
- resolve(reader.result as string);
52
- };
53
- reader.onerror = reject;
54
- reader.readAsDataURL(file);
55
- });
56
- }, []);
57
- const onChange = useCallback<ChangeEventHandler<HTMLInputElement>>(
58
- async (e) => {
59
- if (!e.target.files || e.target.files.length === 0) {
60
- return;
61
- }
62
-
63
- onChangeWithData(e);
64
- const newUrl = await getBase64(e.target.files[0]);
65
- const newValue = { name: e.target.files[0].name, url: newUrl };
66
- setImage(newValue);
67
-
68
- if (onChangeImage) {
69
- onChangeImage(newValue, e.target.files[0]);
70
- }
71
- },
72
- [onChangeWithData, onChangeImage, getBase64]
73
- );
74
-
75
- // Effects
76
-
77
- // Other
78
-
79
- // Render Functions
80
-
81
- return (
82
- // eslint-disable-next-line jsx-a11y/label-has-associated-control
83
- <label className={classNames(styles.fileInput, className)} style={style}>
84
- {label ? <span>{label}</span> : null}
85
- <img
86
- src={(value ?? image)?.url}
87
- alt={(value ?? image)?.name}
88
- className={classNames(styles.preview, (value ?? image)?.url ? undefined : styles.empty)}
89
- />
90
- <Button __allowChildren="all">{(value ?? image)?.name ?? <i>Select Image</i>}</Button>
91
- <input {...otherProps} className={styles.value} onChange={onChange} type="file" />
92
- </label>
93
- );
94
- }
95
-
96
- // Need ImageInputMemo for autocompletion of phpstorm
97
- const ImageInputMemo = withMemo(ImageInput, styles);
98
- export { ImageInputMemo as ImageInput };
@@ -1,240 +0,0 @@
1
- import * as React from 'react';
2
- import { RbmComponentProps } from '../../RbmComponentProps';
3
- import { Override } from '../../../TypeHelpers';
4
- import { ChangeEventHandler, DragEvent, DragEventHandler, InputHTMLAttributes, useCallback, useRef } from 'react';
5
- import { Listener, useListenerWithExtractedProps } from '../../Hooks/useListener';
6
-
7
- import styles from './imageInput.scss';
8
- import { withMemo } from '../../../helper/withMemo';
9
- import classNames from 'classnames';
10
- import { Block } from '../../Layout/Block';
11
- import { Text } from '../../Text/Text';
12
- import { Flex } from '../../Layout/Flex';
13
- import { Grow } from '../../Layout/Grow';
14
- import { Icon } from '../../Icon/Icon';
15
- import { faPlus, faTimesCircle } from '@fortawesome/free-solid-svg-icons';
16
- import { Image } from '../../Image/Image';
17
- import { Clickable } from '../../Clickable/Clickable';
18
- import { Inline } from '../../Layout/Inline';
19
-
20
- export type FileType = { name: string; url: string; mimeType: string; uploaded?: boolean; blob?: Blob };
21
-
22
- export type MultipleImageInputProps<OnChangeFilesData> = RbmComponentProps<
23
- Override<
24
- Omit<InputHTMLAttributes<HTMLInputElement>, 'defaultValue' | 'onChange'>,
25
- {
26
- value: FileType[];
27
- label?: string;
28
- mimeTypes?: string[];
29
- maxFiles?: number;
30
- maxSizePerFile?: number;
31
- onError?: (error: string) => void;
32
- } & Listener<'onChangeFiles', OnChangeFilesData, FileType[]>
33
- >
34
- >;
35
-
36
- export const MultipleFileInput = withMemo(function MultipleImageInput<OnChangeData>({
37
- className,
38
- style,
39
- value,
40
- label,
41
- mimeTypes = ['image/*'],
42
- maxFiles = 1,
43
- maxSizePerFile = 1024 * 1024 * 10,
44
- onError,
45
- ...otherProps
46
- }: MultipleImageInputProps<OnChangeData>) {
47
- // Variables
48
-
49
- // Refs
50
- const inputRef = useRef<HTMLInputElement>(null);
51
-
52
- // States
53
-
54
- // Selectors
55
-
56
- // Callbacks
57
- const checkMimeType = useCallback(
58
- (fileType: string) => {
59
- return mimeTypes.some((type) => {
60
- if (type === '*/*' || type === '*') {
61
- return true;
62
- }
63
- if (type.endsWith('/*')) {
64
- return fileType.startsWith(type.substring(0, type.length - 2));
65
- }
66
- return fileType === type;
67
- });
68
- },
69
- [mimeTypes]
70
- );
71
-
72
- const [onChangeFiles, ...props] = useListenerWithExtractedProps('onChangeFiles', otherProps);
73
- const getBase64 = useCallback((inputFiles: Blob[]) => {
74
- const promises = inputFiles.map(
75
- (file) =>
76
- new Promise<string>((resolve, reject) => {
77
- const reader = new FileReader();
78
- reader.onload = () => {
79
- resolve(reader.result as string);
80
- };
81
- reader.onerror = reject;
82
- reader.readAsDataURL(file);
83
- })
84
- );
85
- return Promise.all(promises);
86
- }, []);
87
-
88
- const onNewFiles = useCallback(
89
- async (newFiles: File[]) => {
90
- if (newFiles.length + value.length > maxFiles) {
91
- onError?.(`Es sind nur ${maxFiles} Dateien erlaubt.`);
92
- return;
93
- }
94
-
95
- if (newFiles.some((file) => file.size > maxSizePerFile)) {
96
- onError?.(`Eine Datei ist zu groß. Jede Datei darf nur ${maxSizePerFile / 1024 / 1024}MB groß sein.`);
97
- return;
98
- }
99
-
100
- if (newFiles.some((file) => !checkMimeType(file.type))) {
101
- onError?.('Eine Datei ist im falschen Format');
102
- return;
103
- }
104
-
105
- const newUrls = await getBase64(newFiles);
106
- const newValue = newFiles.map((file, index) => ({
107
- name: file.name,
108
- url: newUrls[index],
109
- mimeType: file.type,
110
- blob: file,
111
- }));
112
-
113
- onChangeFiles([...value, ...newValue]);
114
- },
115
- [checkMimeType, getBase64, maxFiles, maxSizePerFile, onChangeFiles, onError, value]
116
- );
117
-
118
- const onInputChange = useCallback<ChangeEventHandler<HTMLInputElement>>(
119
- async (e) => {
120
- if (!e.target.files || e.target.files.length === 0) {
121
- return;
122
- }
123
-
124
- const newFiles = Array.from(e.target.files);
125
- await onNewFiles(newFiles);
126
- },
127
- [onNewFiles]
128
- );
129
-
130
- const removeFile = useCallback(
131
- (_: any, index: number) => {
132
- if (index >= 0 && index < value.length) {
133
- const newData = [...value];
134
- newData.splice(index, 1);
135
- onChangeFiles(newData);
136
- }
137
- },
138
- [onChangeFiles, value]
139
- );
140
-
141
- const onDrop = useCallback<DragEventHandler>(
142
- async (event) => {
143
- event.preventDefault();
144
-
145
- const files: File[] = [];
146
- if (event.dataTransfer.items) {
147
- for (let i = 0; i < event.dataTransfer.items.length; i++) {
148
- if (event.dataTransfer.items[i].kind === 'file') {
149
- const file = event.dataTransfer.items[i].getAsFile();
150
- if (file) {
151
- files.push(file);
152
- }
153
- }
154
- }
155
- } else {
156
- for (let i = 0; i < event.dataTransfer.files.length; i++) {
157
- files.push(event.dataTransfer.files[i]);
158
- }
159
- }
160
-
161
- await onNewFiles(files);
162
- },
163
- [onNewFiles]
164
- );
165
-
166
- const onDragOver = useCallback((e: DragEvent) => e.preventDefault(), []);
167
-
168
- // Effects
169
-
170
- // Other
171
-
172
- // Render Functions
173
- const renderFile = (file: FileType) => {
174
- if (file.mimeType.startsWith('image/')) {
175
- return (
176
- <Image
177
- key={file.url}
178
- src={file.url}
179
- alt={file.name}
180
- className={classNames(styles.previewImage, file.url)}
181
- />
182
- );
183
- }
184
- // TODO style
185
- return (
186
- <Block>
187
- <Text>{file.name}</Text>
188
- </Block>
189
- );
190
- };
191
-
192
- return (
193
- <label
194
- className={classNames(styles.fileInput, className)}
195
- style={style}
196
- onDrop={onDrop}
197
- onDragOver={onDragOver}
198
- >
199
- <Flex horizontal={true}>
200
- {!!label && (
201
- <Grow>
202
- <Text>{label}</Text>
203
- </Grow>
204
- )}
205
- {maxFiles > 1 && (
206
- <Inline>
207
- <Text>
208
- {value.length}/{maxFiles}
209
- </Text>
210
- </Inline>
211
- )}
212
- </Flex>
213
- <Flex horizontal={true} className={styles.previewContainer}>
214
- {value?.map((file, index) => (
215
- <Grow className={styles.preview} center={true} key={file.url}>
216
- {renderFile(file)}
217
- <Clickable className={styles.previewRemove} onClick={removeFile} onClickData={index}>
218
- <Icon icon={faTimesCircle} />
219
- </Clickable>
220
- </Grow>
221
- ))}
222
- {value.length < maxFiles && (
223
- <Grow className={styles.addFile} center={true} __allowChildren="html">
224
- <Icon icon={faPlus} />
225
- <input
226
- {...props}
227
- ref={inputRef}
228
- className={styles.value}
229
- onChange={onInputChange}
230
- type="file"
231
- multiple={maxFiles > 1}
232
- accept={mimeTypes.join(', ')}
233
- />
234
- </Grow>
235
- )}
236
- </Flex>
237
- </label>
238
- );
239
- },
240
- styles);