@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
@@ -7,19 +7,27 @@ import classNames from 'classnames';
7
7
  import { withMemo } from '../../../helper/withMemo';
8
8
  import { HTMLAttributes } from 'react';
9
9
  import { RbmComponentProps } from '../../RbmComponentProps';
10
+ import {ButtonType} from "./ButtonType";
10
11
 
11
12
  export type ButtonProps<ClickData> = RbmComponentProps<
12
- Override<HTMLAttributes<HTMLButtonElement>, OptionalListener<'onClick', ClickData>>
13
+ Override<HTMLAttributes<HTMLButtonElement>, {
14
+ type?: ButtonType,
15
+ disabled?: boolean;
16
+ } & OptionalListener<'onClick', ClickData>>
13
17
  >;
14
18
 
15
- function Button<ClickData>({ children, className, ...props }: ButtonProps<ClickData>) {
19
+ export const Button = withMemo(function Button<ClickData>({ children, className, disabled, type=ButtonType.Primary, ...props }: ButtonProps<ClickData>) {
16
20
  const onClick = useListener<'onClick', ClickData>('onClick', props);
21
+
22
+ const classes = {
23
+ [styles.primary]: type === ButtonType.Primary,
24
+ [styles.secondary]: type === ButtonType.Secondary,
25
+ [styles.disabled]: disabled,
26
+ };
27
+
17
28
  return (
18
- <button {...props} type="button" onClick={onClick} className={classNames(styles.button, className)}>
29
+ <button {...props} disabled={disabled} type="button" onClick={onClick} className={classNames(styles.button, classes, className)}>
19
30
  {children}
20
31
  </button>
21
32
  );
22
- }
23
-
24
- const ButtonMemo = withMemo(Button, styles);
25
- export { ButtonMemo as Button };
33
+ }, styles);
@@ -0,0 +1,4 @@
1
+ export enum ButtonType {
2
+ Primary = "primary",
3
+ Secondary = "secondary",
4
+ }
@@ -1,5 +1,6 @@
1
1
  @import "../../../scss/variables";
2
2
  @import "../../../scss/designMixin";
3
+ @import "../../../scss/flavorMixin";
3
4
 
4
5
  .button {
5
6
  cursor: pointer;
@@ -10,13 +11,11 @@
10
11
  overflow: hidden;
11
12
  text-decoration: none;
12
13
  letter-spacing: 0;
13
- color: white;
14
14
  vertical-align: middle;
15
- border: 0 solid currentColor;
16
15
  border-radius: 3px;
17
16
  transition: none;
18
17
 
19
- --text-primary-default-color: white;
18
+ @include flavorSelection(--button-color);
20
19
 
21
20
  @include design($material) {
22
21
  box-shadow: $boxShadowMaterial;
@@ -26,7 +25,6 @@
26
25
  text-align: center;
27
26
  font-size: 14px;
28
27
  text-transform: uppercase;
29
- background-color: var(--flavor-accent);
30
28
  font-weight: 500;
31
29
  }
32
30
 
@@ -34,6 +32,25 @@
34
32
  line-height: 32px;
35
33
  padding: 4px 18px;
36
34
  font-size: 17px;
37
- background-color: var(--flavor-accent);
35
+ }
36
+
37
+ &.primary {
38
+ background-color: var(--button-color);
39
+ border: 0 solid currentColor;
40
+ color: white;
41
+ --text-primary-default-color: white;
42
+ }
43
+
44
+ &.secondary {
45
+ background-color: transparent;
46
+ border: 1px solid var(--button-color);
47
+ color: var(--button-color);
48
+ --text-primary-default-color: var(--button-color);
49
+ }
50
+
51
+ &.disabled {
52
+ opacity: 0.3;
53
+ cursor: default;
54
+ pointer-events: none;
38
55
  }
39
56
  }
@@ -0,0 +1,55 @@
1
+ import * as React from 'react';
2
+ import {RbmComponentProps} from '../../../RbmComponentProps';
3
+ import {Override} from '../../../../TypeHelpers';
4
+ import {useCallback, useMemo} from 'react';
5
+ import {Listener, useListenerWithExtractedProps} from '../../../Hooks/useListener';
6
+
7
+ import styles from './fileInput.scss';
8
+ import {withMemo} from '../../../../helper/withMemo';
9
+ import {FileType} from "./FileType";
10
+ import {MultipleFileInput, MultipleFileInputProps} from "./MultipleFileInput";
11
+
12
+
13
+ export type FileInputProps<OnChangeFileData> = RbmComponentProps<
14
+ Override<
15
+ Omit<MultipleFileInputProps<unknown>, "onChangeFiles" | "maxFiles" | "onChangeFilesData" | "mimeTypes"|"showDeleteButton">, {
16
+ value?: FileType,
17
+ mimeType?: string,
18
+ required?: boolean,
19
+ } & Listener<'onChangeFile', OnChangeFileData, FileType | undefined>>
20
+ >;
21
+
22
+ // TODO use MultipleFileInput internal
23
+ export const FileInput = withMemo(function FileInput<OnChangeData>({
24
+ value,
25
+ mimeType,
26
+ required = false,
27
+ ...otherProps
28
+ }: FileInputProps<OnChangeData>) {
29
+ // Variables
30
+
31
+ // Refs
32
+
33
+ // States
34
+ const mimeTypes = useMemo(() => (mimeType ? [mimeType] : undefined), [mimeType]);
35
+ const innerValue = useMemo(() => value ? [value] : [], [value]);
36
+
37
+ // Selectors
38
+
39
+ // Callbacks
40
+ const [onChangeFile, multipleFileInputProps] = useListenerWithExtractedProps<"onChangeFile", OnChangeData>("onChangeFile", otherProps);
41
+ const onChangeFiles = useCallback((files: FileType[]) => {
42
+ if (!required || files[0]) {
43
+ onChangeFile(files[0]);
44
+ }
45
+ }, [onChangeFile, required]);
46
+
47
+ // Effects
48
+
49
+ // Other
50
+
51
+ // Render Functions
52
+ return <MultipleFileInput maxFiles={1} value={innerValue} onChangeFiles={onChangeFiles}
53
+ mimeTypes={mimeTypes} showDeleteButton={!required} {...multipleFileInputProps}/>;
54
+
55
+ }, styles);
@@ -0,0 +1 @@
1
+ export type FileType = { name: string; data: string; mimeType: string; uploaded?: boolean; blob?: Blob };
@@ -0,0 +1,281 @@
1
+ import * as React from 'react';
2
+ import {RbmComponentProps} from '../../../RbmComponentProps';
3
+ import {Override} from '../../../../TypeHelpers';
4
+ import {ChangeEventHandler, DragEvent, InputHTMLAttributes, useCallback, useRef} from 'react';
5
+ import {Listener, useListenerWithExtractedProps} from '../../../Hooks/useListener';
6
+
7
+ import styles from './fileInput.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 {faFile, 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
+ import {FileType} from "./FileType";
20
+
21
+
22
+ export type MultipleFileInputProps<OnChangeFilesData> = RbmComponentProps<
23
+ Override<
24
+ Omit<InputHTMLAttributes<HTMLInputElement>, 'defaultValue' | 'onChange' | 'value'>,
25
+ {
26
+ value: FileType[];
27
+ label?: string;
28
+ mimeTypes?: string[];
29
+ maxFiles?: number;
30
+ maxSizePerFile?: number;
31
+ onError?: (error: string) => void;
32
+ allowOverride?: boolean
33
+ showDeleteButton?: boolean
34
+ } & Listener<'onChangeFiles', OnChangeFilesData, FileType[]>
35
+ >
36
+ >;
37
+
38
+ export const MultipleFileInput = withMemo(function MultipleImageInput<OnChangeData>({
39
+ className,
40
+ style,
41
+ value,
42
+ label,
43
+ mimeTypes = ['image/*'],
44
+ maxFiles = 1,
45
+ maxSizePerFile = 1024 * 1024 * 10,
46
+ allowOverride = maxFiles === 1,
47
+ onError,
48
+ showDeleteButton = true,
49
+ ...otherProps
50
+ }: MultipleFileInputProps<OnChangeData>) {
51
+ // Variables
52
+
53
+ // Refs
54
+ const inputRef = useRef<HTMLInputElement>(null);
55
+ const indexRef = useRef<number | undefined>(undefined);
56
+
57
+ // States
58
+
59
+ // Selectors
60
+
61
+ // Callbacks
62
+
63
+
64
+ const checkMimeType = useCallback(
65
+ (fileType: string) => {
66
+ return mimeTypes.some((type) => {
67
+ if (type === '*/*' || type === '*') {
68
+ return true;
69
+ }
70
+ if (type.endsWith('/*')) {
71
+ return fileType.startsWith(type.substring(0, type.length - 2));
72
+ }
73
+ return fileType === type;
74
+ });
75
+ },
76
+ [mimeTypes]
77
+ );
78
+
79
+ const [onChangeFiles, ...props] = useListenerWithExtractedProps('onChangeFiles', otherProps);
80
+ const getBase64 = useCallback((inputFiles: Blob[]) => {
81
+ const promises = inputFiles.map(
82
+ (file) =>
83
+ new Promise<string>((resolve, reject) => {
84
+ const reader = new FileReader();
85
+ reader.onload = () => {
86
+ resolve(reader.result as string);
87
+ };
88
+ reader.onerror = reject;
89
+ reader.readAsDataURL(file);
90
+ })
91
+ );
92
+ return Promise.all(promises);
93
+ }, []);
94
+
95
+ const onNewFiles = useCallback(
96
+ async (newFiles: File[], index?: number) => {
97
+ if (newFiles.some((file) => !checkMimeType(file.type))) {
98
+ onError?.('Eine Datei ist im falschen Format');
99
+ return;
100
+ }
101
+
102
+ if (newFiles.length + value.length > maxFiles && (!allowOverride || index === undefined || index + newFiles.length > maxFiles)) {
103
+ onError?.(`Es sind nur ${maxFiles} Dateien erlaubt.`);
104
+ return;
105
+ }
106
+
107
+ if (maxSizePerFile && newFiles.some((file) => file.size > maxSizePerFile)) {
108
+ onError?.(`Eine Datei ist zu groß. Jede Datei darf nur ${maxSizePerFile / 1024 / 1024}MB groß sein.`);
109
+ return;
110
+ }
111
+
112
+ const newUrls = await getBase64(newFiles);
113
+ const newValue = newFiles.map((file, fileIndex) => ({
114
+ name: file.name,
115
+ data: newUrls[fileIndex],
116
+ mimeType: file.type,
117
+ blob: file,
118
+ }));
119
+
120
+ if (newFiles.length + value.length > maxFiles && index !== undefined) {
121
+ const onChangeFilesValue = [...value];
122
+ onChangeFilesValue.splice(index, newFiles.length, ...newValue);
123
+ onChangeFiles(onChangeFilesValue);
124
+ } else {
125
+ onChangeFiles([...value, ...newValue]);
126
+ }
127
+ },
128
+ [allowOverride, checkMimeType, getBase64, maxFiles, maxSizePerFile, onChangeFiles, onError, value]
129
+ );
130
+
131
+ const onInputChange = useCallback<ChangeEventHandler<HTMLInputElement>>(
132
+ async (e) => {
133
+ const index = indexRef.current;
134
+ indexRef.current = undefined;
135
+
136
+ if (!e.target.files || e.target.files.length === 0) {
137
+ return;
138
+ }
139
+
140
+ const newFiles = Array.from(e.target.files);
141
+ await onNewFiles(newFiles, index);
142
+ },
143
+ [onNewFiles]
144
+ );
145
+
146
+ const removeFile = useCallback(
147
+ (_: any, index: number) => {
148
+ if (index >= 0 && index < value.length) {
149
+ const newData = [...value];
150
+ newData.splice(index, 1);
151
+ onChangeFiles(newData);
152
+ }
153
+ },
154
+ [onChangeFiles, value]
155
+ );
156
+
157
+ const onDrop = useCallback(
158
+ async (event: DragEvent, index: number) => {
159
+ event.preventDefault();
160
+
161
+ const files: File[] = [];
162
+ if (event.dataTransfer.items) {
163
+ for (let i = 0; i < event.dataTransfer.items.length; i++) {
164
+ if (event.dataTransfer.items[i].kind === 'file') {
165
+ const file = event.dataTransfer.items[i].getAsFile();
166
+ if (file) {
167
+ files.push(file);
168
+ }
169
+ }
170
+ }
171
+ } else {
172
+ for (let i = 0; i < event.dataTransfer.files.length; i++) {
173
+ files.push(event.dataTransfer.files[i]);
174
+ }
175
+ }
176
+
177
+ await onNewFiles(files, index);
178
+ },
179
+ [onNewFiles]
180
+ );
181
+
182
+ const onDragOver = useCallback((e: DragEvent) => e.preventDefault(), []);
183
+
184
+ const clickOnFile = useCallback((_: any, index?: number) => {
185
+ if (!allowOverride || !inputRef.current) {
186
+ return;
187
+ }
188
+
189
+ indexRef.current = index;
190
+ inputRef.current.dispatchEvent(new MouseEvent("click"));
191
+ }, [allowOverride]);
192
+
193
+ // Effects
194
+
195
+ // Other
196
+
197
+ // Render Functions
198
+ const renderFile = (file: FileType) => {
199
+ if (file.mimeType.startsWith('image/')) {
200
+ return (
201
+ <Image
202
+ key={file.data}
203
+ src={file.data}
204
+ alt={file.name}
205
+ className={classNames(styles.previewImage, file.data)}
206
+ />
207
+ );
208
+ }
209
+ // TODO style
210
+ return (
211
+ <Block className={styles.previewText}>
212
+ <Icon icon={faFile} className={styles.previewTextIcon}/><Text>{file.name}</Text>
213
+ </Block>
214
+ );
215
+ };
216
+
217
+ return (
218
+ <span
219
+ className={classNames(styles.fileInput, className)}
220
+ style={style}
221
+ >
222
+ <Flex horizontal={true}>
223
+ {!!label && (
224
+ <Grow>
225
+ <Text>{label}</Text>
226
+ </Grow>
227
+ )}
228
+ {maxFiles > 1 && (
229
+ <Inline>
230
+ <Text>
231
+ {value.length}/{maxFiles}
232
+ </Text>
233
+ </Inline>
234
+ )}
235
+ </Flex>
236
+ <Flex horizontal={true} className={styles.previewContainer}>
237
+ {value?.map((file, index) => (
238
+ <Grow className={styles.preview} center={true} key={file.data}>
239
+ <Clickable
240
+ onDrop={onDrop}
241
+ onDragOver={onDragOver}
242
+ onDropData={index}
243
+
244
+ onClick={clickOnFile}
245
+ onClickData={index}
246
+ >
247
+ {renderFile(file)}
248
+ {showDeleteButton &&
249
+ <Clickable className={styles.previewRemove} onClick={removeFile} onClickData={index}>
250
+ <Icon icon={faTimesCircle}/>
251
+ </Clickable>}
252
+ </Clickable>
253
+ </Grow>
254
+ ))}
255
+ <Grow className={classNames(styles.addFile, {[styles.hidden]: value.length >= maxFiles})} center={true}>
256
+ <Clickable
257
+ className={styles.addFileButton}
258
+ onDrop={onDrop}
259
+ onDragOver={onDragOver}
260
+ onDropData={maxFiles}
261
+ onClick={clickOnFile}
262
+ __allowChildren="html"
263
+ >
264
+ <Icon icon={faPlus}/>
265
+ <input
266
+ {...props}
267
+ ref={inputRef}
268
+ className={styles.value}
269
+ onChange={onInputChange}
270
+ value=''
271
+ type="file"
272
+ multiple={maxFiles > 1}
273
+ accept={mimeTypes.join(', ')}
274
+ />
275
+ </Clickable>
276
+ </Grow>
277
+ </Flex>
278
+ </span>
279
+ );
280
+ },
281
+ styles);
@@ -20,7 +20,7 @@
20
20
  font-size: 1.5rem;
21
21
 
22
22
  svg {
23
- background: white;
23
+ background: rgba(255, 255, 255, 0.2);
24
24
  }
25
25
  }
26
26
 
@@ -35,6 +35,21 @@
35
35
  object-fit: contain;
36
36
  }
37
37
 
38
+ .previewText {
39
+ width: 100%;
40
+ height: 100%;
41
+ min-height: 4rem;
42
+
43
+ display: flex;
44
+ justify-content: center;
45
+ align-items: center;
46
+ }
47
+
48
+ .previewTextIcon {
49
+ padding-right: 4px;
50
+ }
51
+
52
+
38
53
  .addFile {
39
54
  position: relative;
40
55
  border-radius: 3px;
@@ -44,16 +59,31 @@
44
59
  justify-content: center;
45
60
  align-items: center;
46
61
  font-size: 2rem;
62
+
63
+ .addFileButton {
64
+ width: 100%;
65
+ height: 100%;
66
+ min-height: 4rem;
67
+
68
+ display: flex;
69
+ justify-content: center;
70
+ align-items: center;
71
+ }
72
+
73
+ &.hidden {
74
+ display: none;
75
+ }
47
76
  }
48
77
  }
49
78
 
50
79
 
51
80
  .value {
52
- position: absolute;
53
- top: 0;
54
- left: 0;
55
- right: 0;
56
- bottom: 0;
57
- opacity: 0;
81
+ display: none;
82
+ //position: absolute;
83
+ //top: 0;
84
+ //left: 0;
85
+ //right: 0;
86
+ //bottom: 0;
87
+ //opacity: 0;
58
88
  }
59
89
  }
@@ -128,9 +128,9 @@ export const SearchSelectInput = withMemo(function SearchSelectInput<OnChangeDat
128
128
  }
129
129
 
130
130
  return (
131
- <Clickable onClick={toggleOption} onClickData={value} key={indexedOptions[value].key}>
131
+ <Clickable onClick={toggleOption} onClickData={value} key={indexedOptions[value]?.key}>
132
132
  <InlineBlock className={styles.tag}>
133
- <Text size={TEXT_SIZE.xSmall}>{indexedOptions[value].label}</Text>
133
+ <Text size={TEXT_SIZE.xSmall}>{indexedOptions[value]?.label}</Text>
134
134
  </InlineBlock>
135
135
  </Clickable>
136
136
  );
@@ -22,6 +22,7 @@ export type SelectProps<OnChangeData> = RbmComponentProps<
22
22
  options: SelectOption[];
23
23
  onChangeValue?: (newValue: string) => void;
24
24
  inline?: boolean;
25
+ small?: boolean;
25
26
  } & OptionalListener<'onChange', OnChangeData>
26
27
  >
27
28
  >;
@@ -33,10 +34,10 @@ export const Select = withMemo(function Select<OnChangeData>({
33
34
  style,
34
35
  onChangeValue,
35
36
  inline = false,
37
+ small = false,
36
38
  ...otherProps
37
39
  }: SelectProps<OnChangeData>) {
38
40
  // Variables
39
- console.log('LOG-d inline', inline);
40
41
 
41
42
  // Refs
42
43
 
@@ -62,7 +63,7 @@ export const Select = withMemo(function Select<OnChangeData>({
62
63
 
63
64
  return (
64
65
  // eslint-disable-next-line jsx-a11y/label-has-associated-control
65
- <label className={classNames(styles.select, { [styles.inline]: inline }, className)} style={style}>
66
+ <label className={classNames(styles.select, { [styles.inline]: inline, [styles.small]: small }, className)} style={style}>
66
67
  {label ? <span className={styles.label}>{label}</span> : null}
67
68
  <select {...otherProps} className={styles.input} onChange={onChange}>
68
69
  {options.map((option) => (
@@ -48,4 +48,9 @@
48
48
  flex: 1;
49
49
  }
50
50
  }
51
+
52
+ &.small .input {
53
+ height: initial;
54
+ line-height: 1.5rem;
55
+ }
51
56
  }
@@ -6,8 +6,9 @@ import classNames from 'classnames';
6
6
 
7
7
  import styles from './switch.scss';
8
8
  import { withMemo } from '../../../helper/withMemo';
9
+ import {OptionalListener, useListenerWithExtractedProps} from "../../Hooks/useListener";
9
10
 
10
- export type SwitchProps = RbmComponentProps<
11
+ export type SwitchProps<OnChangeCheckedData> = RbmComponentProps<
11
12
  Override<
12
13
  InputHTMLAttributes<HTMLInputElement>,
13
14
  {
@@ -16,12 +17,11 @@ export type SwitchProps = RbmComponentProps<
16
17
  children?: string;
17
18
  isLabelBeforeSwitch?: boolean;
18
19
  isDual?: boolean;
19
- onChangeChecked?(isChecked: boolean): void;
20
- }
20
+ } & OptionalListener<"onChangeChecked", OnChangeCheckedData, boolean>
21
21
  >
22
22
  >;
23
23
 
24
- export const Switch = withMemo(function Switch({
24
+ export const Switch = withMemo(function Switch<OnChangeCheckedData>({
25
25
  children,
26
26
  label = '',
27
27
  preLabel = '',
@@ -31,9 +31,8 @@ export const Switch = withMemo(function Switch({
31
31
  className,
32
32
  style,
33
33
  onChange,
34
- onChangeChecked,
35
34
  ...props
36
- }: SwitchProps) {
35
+ }: SwitchProps<OnChangeCheckedData>) {
37
36
  // Variables
38
37
 
39
38
  // States
@@ -41,10 +40,12 @@ export const Switch = withMemo(function Switch({
41
40
  // Refs
42
41
 
43
42
  // Callbacks
43
+ const [onChangeChecked, otherProps] = useListenerWithExtractedProps("onChangeChecked", props);
44
+
44
45
  const realOnChange = useCallback<ChangeEventHandler<HTMLInputElement>>(
45
46
  (e) => {
46
47
  onChange?.(e);
47
- onChangeChecked?.(e.target.checked);
48
+ onChangeChecked(e.target.checked);
48
49
  },
49
50
  [onChange, onChangeChecked]
50
51
  );
@@ -70,7 +71,7 @@ export const Switch = withMemo(function Switch({
70
71
  <span className={classNames(styles.switch, { [styles.dual]: isDual }, className)} style={style}>
71
72
  <label htmlFor={id} key={id}>
72
73
  <span className={styles.label}>{preLabel}</span>
73
- <input {...props} type="checkbox" id={id} onChange={realOnChange} />
74
+ <input {...otherProps} type="checkbox" id={id} onChange={realOnChange} />
74
75
  <div className={styles.toggle}>
75
76
  <span className={styles.handle} />
76
77
  </div>
@@ -9,6 +9,7 @@
9
9
  > label {
10
10
  display: flex;
11
11
  cursor: pointer;
12
+ align-items: center;
12
13
  }
13
14
 
14
15
  .label {
@@ -0,0 +1,13 @@
1
+ import {useLayoutEffect, useRef} from "react";
2
+
3
+ export function useMousePosition() {
4
+ const position = useRef({ x: 0, y: 0 });
5
+ useLayoutEffect(() => {
6
+ const setFromEvent = (e: MouseEvent) => {
7
+ position.current = { x: e.clientX, y: e.clientY };
8
+ };
9
+ window.addEventListener('mousemove', setFromEvent);
10
+ return () => window.removeEventListener('mousemove', setFromEvent);
11
+ }, []);
12
+ return position;
13
+ }
@@ -0,0 +1,17 @@
1
+ import {useWindow} from "../../WindowContext/WindowContext";
2
+ import {useEffect, useReducer} from "react";
3
+
4
+
5
+ export function useWindowDimensions() {
6
+ const [, redraw] = useReducer((x) => x + 1, 0);
7
+ const window = useWindow();
8
+
9
+ useEffect(() => {
10
+ window?.addEventListener('resize', redraw);
11
+ return () => {
12
+ window?.removeEventListener('resize', redraw);
13
+ };
14
+ }, [window]);
15
+
16
+ return {x: window?.innerWidth ?? 0, y: window?.innerHeight ?? 0};
17
+ }