tgui-core 1.1.7 → 1.1.9
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/lib/common/assets.ts +38 -0
- package/lib/common/collections.ts +27 -0
- package/lib/common/color.ts +88 -0
- package/lib/common/constants.ts +349 -0
- package/lib/common/events.ts +262 -0
- package/{dist/common/exhaustive.d.ts → lib/common/exhaustive.ts} +3 -1
- package/lib/common/format.ts +167 -0
- package/{dist/common/fp.d.ts → lib/common/fp.ts} +16 -2
- package/lib/common/hotkeys.ts +207 -0
- package/lib/common/http.ts +16 -0
- package/lib/common/keycodes.ts +86 -0
- package/{dist/common/keys.d.ts → lib/common/keys.ts} +24 -21
- package/lib/common/math.ts +76 -0
- package/lib/common/perf.ts +72 -0
- package/lib/common/random.ts +32 -0
- package/lib/common/react.ts +59 -0
- package/lib/common/redux.ts +187 -0
- package/lib/common/storage.ts +207 -0
- package/lib/common/string.ts +169 -0
- package/lib/common/timer.ts +63 -0
- package/lib/common/type-utils.ts +41 -0
- package/lib/common/types.d.ts +12 -0
- package/lib/common/uuid.ts +18 -0
- package/lib/components/AnimatedNumber.tsx +180 -0
- package/lib/components/Autofocus.tsx +23 -0
- package/lib/components/Blink.tsx +91 -0
- package/lib/components/BlockQuote.tsx +9 -0
- package/lib/components/BodyZoneSelector.tsx +149 -0
- package/lib/components/Box.tsx +252 -0
- package/lib/components/Button.tsx +425 -0
- package/lib/components/ByondUi.jsx +110 -0
- package/lib/components/Chart.tsx +155 -0
- package/lib/components/Collapsible.tsx +43 -0
- package/lib/components/ColorBox.tsx +29 -0
- package/lib/components/Dialog.tsx +81 -0
- package/lib/components/Dimmer.tsx +13 -0
- package/lib/components/Divider.tsx +20 -0
- package/lib/components/DmIcon.tsx +86 -0
- package/lib/components/DraggableControl.jsx +276 -0
- package/lib/components/Dropdown.tsx +246 -0
- package/lib/components/FakeTerminal.jsx +52 -0
- package/lib/components/FitText.tsx +99 -0
- package/lib/components/Flex.tsx +159 -0
- package/lib/components/Icon.tsx +95 -0
- package/lib/components/Image.tsx +54 -0
- package/lib/components/InfinitePlane.jsx +192 -0
- package/lib/components/Input.tsx +176 -0
- package/lib/components/KeyListener.tsx +40 -0
- package/lib/components/Knob.tsx +178 -0
- package/lib/components/LabeledControls.tsx +44 -0
- package/lib/components/LabeledList.tsx +154 -0
- package/lib/components/MenuBar.tsx +228 -0
- package/lib/components/Modal.tsx +23 -0
- package/lib/components/NoticeBox.tsx +45 -0
- package/lib/components/NumberInput.tsx +328 -0
- package/lib/components/Popper.tsx +100 -0
- package/lib/components/ProgressBar.tsx +105 -0
- package/lib/components/RestrictedInput.jsx +301 -0
- package/lib/components/RoundGauge.tsx +180 -0
- package/lib/components/Section.tsx +120 -0
- package/lib/components/Slider.tsx +169 -0
- package/lib/components/Stack.tsx +96 -0
- package/lib/components/StyleableSection.tsx +33 -0
- package/lib/components/Table.tsx +84 -0
- package/lib/components/Tabs.tsx +89 -0
- package/lib/components/TextArea.tsx +182 -0
- package/lib/components/TimeDisplay.jsx +64 -0
- package/lib/components/Tooltip.tsx +152 -0
- package/lib/components/TrackOutsideClicks.tsx +35 -0
- package/lib/components/VirtualList.tsx +69 -0
- package/lib/styles/atomic/candystripe.scss +8 -0
- package/lib/styles/atomic/centered-image.scss +7 -0
- package/lib/styles/atomic/color.scss +21 -0
- package/lib/styles/atomic/debug-layout.scss +17 -0
- package/lib/styles/atomic/fit-text.scss +14 -0
- package/lib/styles/atomic/links.scss +12 -0
- package/lib/styles/atomic/outline.scss +47 -0
- package/lib/styles/atomic/text.scss +44 -0
- package/lib/styles/base.scss +32 -0
- package/lib/styles/colors.scss +92 -0
- package/lib/styles/components/BlockQuote.module.scss +20 -0
- package/lib/styles/components/BlockQuote.module.scss.d.ts +4 -0
- package/lib/styles/components/Button.module.scss +157 -0
- package/lib/styles/components/Button.module.scss.d.ts +46 -0
- package/lib/styles/components/ColorBox.module.scss +12 -0
- package/lib/styles/components/ColorBox.module.scss.d.ts +4 -0
- package/lib/styles/components/Dialog.module.scss +60 -0
- package/lib/styles/components/Dialog.module.scss.d.ts +10 -0
- package/lib/styles/components/Dimmer.module.scss +22 -0
- package/lib/styles/components/Dimmer.module.scss.d.ts +4 -0
- package/lib/styles/components/Divider.module.scss +27 -0
- package/lib/styles/components/Divider.module.scss.d.ts +6 -0
- package/lib/styles/components/Dropdown.scss +72 -0
- package/lib/styles/components/Flex.module.scss +13 -0
- package/lib/styles/components/Flex.module.scss.d.ts +5 -0
- package/lib/styles/components/Icon.module.scss +25 -0
- package/lib/styles/components/Icon.module.scss.d.ts +5 -0
- package/lib/styles/components/Input.module.scss +64 -0
- package/lib/styles/components/Input.module.scss.d.ts +8 -0
- package/lib/styles/components/Knob.module.scss +131 -0
- package/lib/styles/components/Knob.module.scss.d.ts +33 -0
- package/lib/styles/components/LabeledList.module.scss +49 -0
- package/lib/styles/components/LabeledList.module.scss.d.ts +8 -0
- package/lib/styles/components/MenuBar.module.scss +75 -0
- package/lib/styles/components/MenuBar.module.scss.d.ts +14 -0
- package/lib/styles/components/Modal.module.scss +14 -0
- package/lib/styles/components/Modal.module.scss.d.ts +4 -0
- package/lib/styles/components/NoticeBox.module.scss +65 -0
- package/lib/styles/components/NoticeBox.module.scss.d.ts +27 -0
- package/lib/styles/components/NumberInput.module.scss +71 -0
- package/lib/styles/components/NumberInput.module.scss.d.ts +9 -0
- package/lib/styles/components/ProgressBar.module.scss +63 -0
- package/lib/styles/components/ProgressBar.module.scss.d.ts +27 -0
- package/lib/styles/components/RoundGauge.module.scss +85 -0
- package/lib/styles/components/RoundGauge.module.scss.d.ts +49 -0
- package/lib/styles/components/Section.module.scss +130 -0
- package/lib/styles/components/Section.module.scss.d.ts +13 -0
- package/lib/styles/components/Slider.module.scss +54 -0
- package/lib/styles/components/Slider.module.scss.d.ts +8 -0
- package/lib/styles/components/Stack.module.scss +60 -0
- package/lib/styles/components/Stack.module.scss.d.ts +12 -0
- package/lib/styles/components/Table.module.scss +44 -0
- package/lib/styles/components/Table.module.scss.d.ts +10 -0
- package/lib/styles/components/Tabs.module.scss +144 -0
- package/lib/styles/components/Tabs.module.scss.d.ts +35 -0
- package/lib/styles/components/TextArea.module.scss +86 -0
- package/lib/styles/components/TextArea.module.scss.d.ts +11 -0
- package/lib/styles/components/Tooltip.module.scss +24 -0
- package/lib/styles/components/Tooltip.module.scss.d.ts +4 -0
- package/lib/styles/functions.scss +79 -0
- package/lib/styles/input.scss +9 -0
- package/lib/styles/main.scss +20 -0
- package/lib/styles/reset.scss +68 -0
- package/package.json +6 -6
- package/dist/ProgressBar.module-BkAFfFy0.js +0 -29
- package/dist/Section.module-CLVHJ4yA.js +0 -15
- package/dist/assets/BlockQuote.css +0 -1
- package/dist/assets/Button.css +0 -1
- package/dist/assets/ColorBox.css +0 -1
- package/dist/assets/Dialog.css +0 -1
- package/dist/assets/Dimmer.css +0 -1
- package/dist/assets/Divider.css +0 -1
- package/dist/assets/Flex.css +0 -1
- package/dist/assets/Icon.css +0 -6
- package/dist/assets/Input.css +0 -1
- package/dist/assets/Knob.css +0 -1
- package/dist/assets/LabeledList.css +0 -1
- package/dist/assets/MenuBar.css +0 -1
- package/dist/assets/Modal.css +0 -1
- package/dist/assets/NoticeBox.css +0 -1
- package/dist/assets/NumberInput.css +0 -1
- package/dist/assets/ProgressBar.css +0 -1
- package/dist/assets/RoundGauge.css +0 -1
- package/dist/assets/Section.css +0 -1
- package/dist/assets/Slider.css +0 -1
- package/dist/assets/Stack.css +0 -1
- package/dist/assets/Table.css +0 -1
- package/dist/assets/Tabs.css +0 -1
- package/dist/assets/TextArea.css +0 -1
- package/dist/assets/Tooltip.css +0 -1
- package/dist/common/assets.d.ts +0 -4
- package/dist/common/assets.js +0 -21
- package/dist/common/collections.d.ts +0 -10
- package/dist/common/collections.js +0 -15
- package/dist/common/color.d.ts +0 -25
- package/dist/common/color.js +0 -69
- package/dist/common/constants.d.ts +0 -102
- package/dist/common/constants.js +0 -312
- package/dist/common/events.d.ts +0 -33
- package/dist/common/events.js +0 -147
- package/dist/common/exhaustive.js +0 -6
- package/dist/common/format.d.ts +0 -11
- package/dist/common/format.js +0 -114
- package/dist/common/fp.js +0 -9
- package/dist/common/hotkeys.d.ts +0 -25
- package/dist/common/hotkeys.js +0 -112
- package/dist/common/http.d.ts +0 -4
- package/dist/common/http.js +0 -10
- package/dist/common/keycodes.d.ts +0 -85
- package/dist/common/keycodes.js +0 -88
- package/dist/common/keys.js +0 -8
- package/dist/common/math.d.ts +0 -39
- package/dist/common/math.js +0 -41
- package/dist/common/perf.d.ts +0 -24
- package/dist/common/perf.js +0 -33
- package/dist/common/random.d.ts +0 -16
- package/dist/common/random.js +0 -18
- package/dist/common/react.d.ts +0 -23
- package/dist/common/react.js +0 -30
- package/dist/common/redux.d.ts +0 -64
- package/dist/common/redux.js +0 -72
- package/dist/common/storage.js +0 -124
- package/dist/common/string.d.ts +0 -65
- package/dist/common/string.js +0 -83
- package/dist/common/timer.d.ts +0 -18
- package/dist/common/timer.js +0 -28
- package/dist/common/type-utils.d.ts +0 -9
- package/dist/common/type-utils.js +0 -25
- package/dist/common/uuid.d.ts +0 -9
- package/dist/common/uuid.js +0 -10
- package/dist/components/AnimatedNumber.d.ts +0 -60
- package/dist/components/AnimatedNumber.js +0 -76
- package/dist/components/Autofocus.d.ts +0 -4
- package/dist/components/Autofocus.js +0 -17
- package/dist/components/Blink.d.ts +0 -26
- package/dist/components/Blink.js +0 -56
- package/dist/components/BlockQuote.d.ts +0 -3
- package/dist/components/BlockQuote.js +0 -13
- package/dist/components/BodyZoneSelector.d.ts +0 -28
- package/dist/components/BodyZoneSelector.js +0 -115
- package/dist/components/Box.d.ts +0 -91
- package/dist/components/Box.js +0 -133
- package/dist/components/Button.d.ts +0 -93
- package/dist/components/Button.js +0 -298
- package/dist/components/ByondUi.js +0 -73
- package/dist/components/Chart.d.ts +0 -28
- package/dist/components/Chart.js +0 -95
- package/dist/components/Collapsible.d.ts +0 -15
- package/dist/components/Collapsible.js +0 -27
- package/dist/components/ColorBox.d.ts +0 -8
- package/dist/components/ColorBox.js +0 -24
- package/dist/components/Dialog.d.ts +0 -24
- package/dist/components/Dialog.js +0 -67
- package/dist/components/Dimmer.d.ts +0 -3
- package/dist/components/Dimmer.js +0 -13
- package/dist/components/Divider.d.ts +0 -6
- package/dist/components/Divider.js +0 -22
- package/dist/components/DmIcon.d.ts +0 -31
- package/dist/components/DmIcon.js +0 -31
- package/dist/components/DraggableControl.js +0 -176
- package/dist/components/Dropdown.d.ts +0 -48
- package/dist/components/Dropdown.js +0 -152
- package/dist/components/FakeTerminal.js +0 -38
- package/dist/components/FitText.d.ts +0 -22
- package/dist/components/FitText.js +0 -63
- package/dist/components/Flex.d.ts +0 -93
- package/dist/components/Flex.js +0 -72
- package/dist/components/Icon.d.ts +0 -30
- package/dist/components/Icon.js +0 -51
- package/dist/components/Image.d.ts +0 -14
- package/dist/components/Image.js +0 -35
- package/dist/components/InfinitePlane.js +0 -139
- package/dist/components/Input.d.ts +0 -61
- package/dist/components/Input.js +0 -89
- package/dist/components/KeyListener.d.ts +0 -15
- package/dist/components/KeyListener.js +0 -23
- package/dist/components/Knob.d.ts +0 -49
- package/dist/components/Knob.js +0 -162
- package/dist/components/LabeledControls.d.ts +0 -11
- package/dist/components/LabeledControls.js +0 -39
- package/dist/components/LabeledList.d.ts +0 -57
- package/dist/components/LabeledList.js +0 -94
- package/dist/components/MenuBar.d.ts +0 -28
- package/dist/components/MenuBar.js +0 -174
- package/dist/components/Modal.d.ts +0 -3
- package/dist/components/Modal.js +0 -25
- package/dist/components/NoticeBox.d.ts +0 -20
- package/dist/components/NoticeBox.js +0 -49
- package/dist/components/NumberInput.d.ts +0 -45
- package/dist/components/NumberInput.js +0 -221
- package/dist/components/Popper.d.ts +0 -27
- package/dist/components/Popper.js +0 -177
- package/dist/components/ProgressBar.d.ts +0 -46
- package/dist/components/ProgressBar.js +0 -37
- package/dist/components/RestrictedInput.js +0 -155
- package/dist/components/RoundGauge.d.ts +0 -53
- package/dist/components/RoundGauge.js +0 -147
- package/dist/components/Section.d.ts +0 -63
- package/dist/components/Section.js +0 -62
- package/dist/components/Slider.d.ts +0 -46
- package/dist/components/Slider.js +0 -124
- package/dist/components/Stack.d.ts +0 -27
- package/dist/components/Stack.js +0 -67
- package/dist/components/StyleableSection.d.ts +0 -11
- package/dist/components/StyleableSection.js +0 -16
- package/dist/components/Table.d.ts +0 -29
- package/dist/components/Table.js +0 -67
- package/dist/components/Tabs.d.ts +0 -23
- package/dist/components/Tabs.js +0 -89
- package/dist/components/TextArea.d.ts +0 -39
- package/dist/components/TextArea.js +0 -118
- package/dist/components/TimeDisplay.js +0 -34
- package/dist/components/Tooltip.d.ts +0 -29
- package/dist/components/Tooltip.js +0 -83
- package/dist/components/TrackOutsideClicks.d.ts +0 -13
- package/dist/components/TrackOutsideClicks.js +0 -24
- package/dist/components/VirtualList.d.ts +0 -8
- package/dist/components/VirtualList.js +0 -34
- package/dist/components/index.js +0 -92
- package/dist/popper-CiqSDJTE.js +0 -906
- /package/{dist/components/index.d.ts → lib/components/index.ts} +0 -0
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { ReactNode, useEffect, useRef, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import { classes } from '../common/react';
|
|
4
|
+
import { BoxProps, unit } from './Box';
|
|
5
|
+
import { Button } from './Button';
|
|
6
|
+
import { Icon } from './Icon';
|
|
7
|
+
import { Popper } from './Popper';
|
|
8
|
+
|
|
9
|
+
export type DropdownEntry = {
|
|
10
|
+
displayText: ReactNode;
|
|
11
|
+
value: string | number;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
type DropdownOption = string | DropdownEntry;
|
|
15
|
+
|
|
16
|
+
type Props = {
|
|
17
|
+
/** Called when a value is picked from the list, `value` is the value that was picked */
|
|
18
|
+
onSelected: (value: any) => void;
|
|
19
|
+
/** An array of strings which will be displayed in the
|
|
20
|
+
dropdown when open. See Dropdown.tsx for more advanced usage with DropdownEntry */
|
|
21
|
+
options: DropdownOption[];
|
|
22
|
+
/** Currently selected entry to display. Can be left stateless to permanently display this value. */
|
|
23
|
+
selected: DropdownOption | null | undefined;
|
|
24
|
+
} & Partial<{
|
|
25
|
+
/** Whether to scroll automatically on open. Defaults to true */
|
|
26
|
+
autoScroll: boolean;
|
|
27
|
+
/** Whether to display previous / next buttons */
|
|
28
|
+
buttons: boolean;
|
|
29
|
+
/** Whether to clip the selected text */
|
|
30
|
+
clipSelectedText: boolean;
|
|
31
|
+
/** Color of dropdown button */
|
|
32
|
+
color: string;
|
|
33
|
+
/** Disables the dropdown */
|
|
34
|
+
disabled: boolean;
|
|
35
|
+
/** Overwrites selection text with this. Good for objects etc. */
|
|
36
|
+
displayText: ReactNode;
|
|
37
|
+
/** Icon to display in dropdown button */
|
|
38
|
+
icon: string;
|
|
39
|
+
/** Angle of the icon */
|
|
40
|
+
iconRotation: number;
|
|
41
|
+
/** Whether or not the icon should spin */
|
|
42
|
+
iconSpin: boolean;
|
|
43
|
+
/** Width of the dropdown menu. Default: 15rem */
|
|
44
|
+
menuWidth: string;
|
|
45
|
+
/** Whether or not the arrow on the right hand side of the dropdown button is visible */
|
|
46
|
+
noChevron: boolean;
|
|
47
|
+
/** Called when dropdown button is clicked */
|
|
48
|
+
onClick: (event) => void;
|
|
49
|
+
/** Dropdown renders over instead of below */
|
|
50
|
+
over: boolean;
|
|
51
|
+
/** Text to show when nothing has been selected. */
|
|
52
|
+
placeholder: string;
|
|
53
|
+
}> &
|
|
54
|
+
BoxProps;
|
|
55
|
+
|
|
56
|
+
enum DIRECTION {
|
|
57
|
+
Current = 'current',
|
|
58
|
+
Next = 'next',
|
|
59
|
+
Previous = 'previous',
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const NONE = -1;
|
|
63
|
+
|
|
64
|
+
function getOptionValue(option: DropdownOption) {
|
|
65
|
+
return typeof option === 'string' ? option : option.value;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function Dropdown(props: Props) {
|
|
69
|
+
const {
|
|
70
|
+
autoScroll = true,
|
|
71
|
+
buttons,
|
|
72
|
+
className,
|
|
73
|
+
clipSelectedText = true,
|
|
74
|
+
color = 'default',
|
|
75
|
+
disabled,
|
|
76
|
+
displayText,
|
|
77
|
+
icon,
|
|
78
|
+
iconRotation,
|
|
79
|
+
iconSpin,
|
|
80
|
+
menuWidth = '15rem',
|
|
81
|
+
noChevron,
|
|
82
|
+
onClick,
|
|
83
|
+
onSelected,
|
|
84
|
+
options = [],
|
|
85
|
+
over,
|
|
86
|
+
placeholder = 'Select...',
|
|
87
|
+
selected,
|
|
88
|
+
width = '15rem',
|
|
89
|
+
} = props;
|
|
90
|
+
|
|
91
|
+
const [open, setOpen] = useState(false);
|
|
92
|
+
const adjustedOpen = over ? !open : open;
|
|
93
|
+
const innerRef = useRef<HTMLDivElement>(null);
|
|
94
|
+
|
|
95
|
+
const selectedIndex =
|
|
96
|
+
options.findIndex((option) => getOptionValue(option) === selected) || 0;
|
|
97
|
+
|
|
98
|
+
function scrollTo(position: number) {
|
|
99
|
+
let scrollPos = position;
|
|
100
|
+
if (position < selectedIndex) {
|
|
101
|
+
scrollPos = position < 2 ? 0 : position - 2;
|
|
102
|
+
} else {
|
|
103
|
+
scrollPos =
|
|
104
|
+
position > options.length - 3 ? options.length - 1 : position - 2;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const element = innerRef.current?.children[scrollPos];
|
|
108
|
+
element?.scrollIntoView({ block: 'nearest' });
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Update the selected value when clicking the left/right buttons */
|
|
112
|
+
function updateSelected(direction: DIRECTION) {
|
|
113
|
+
if (options.length < 1 || disabled) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const startIndex = 0;
|
|
118
|
+
const endIndex = options.length - 1;
|
|
119
|
+
|
|
120
|
+
let newIndex: number;
|
|
121
|
+
if (selectedIndex < 0) {
|
|
122
|
+
newIndex = direction === 'next' ? endIndex : startIndex; // No selection yet
|
|
123
|
+
} else if (direction === 'next') {
|
|
124
|
+
newIndex = selectedIndex === endIndex ? startIndex : selectedIndex + 1; // Move to next option
|
|
125
|
+
} else {
|
|
126
|
+
newIndex = selectedIndex === startIndex ? endIndex : selectedIndex - 1; // Move to previous option
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (open && autoScroll) {
|
|
130
|
+
scrollTo(newIndex);
|
|
131
|
+
}
|
|
132
|
+
onSelected?.(getOptionValue(options[newIndex]));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** Allows the menu to be scrollable on open */
|
|
136
|
+
useEffect(() => {
|
|
137
|
+
if (!open) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (autoScroll && selectedIndex !== NONE) {
|
|
142
|
+
scrollTo(selectedIndex);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
innerRef.current?.focus();
|
|
146
|
+
}, [open]);
|
|
147
|
+
|
|
148
|
+
return (
|
|
149
|
+
<Popper
|
|
150
|
+
isOpen={open}
|
|
151
|
+
onClickOutside={() => setOpen(false)}
|
|
152
|
+
placement={over ? 'top-start' : 'bottom-start'}
|
|
153
|
+
content={
|
|
154
|
+
<div
|
|
155
|
+
className="Layout Dropdown__menu"
|
|
156
|
+
style={{ minWidth: menuWidth }}
|
|
157
|
+
ref={innerRef}
|
|
158
|
+
>
|
|
159
|
+
{options.length === 0 && (
|
|
160
|
+
<div className="Dropdown__menuentry">No options</div>
|
|
161
|
+
)}
|
|
162
|
+
|
|
163
|
+
{options.map((option, index) => {
|
|
164
|
+
const value = getOptionValue(option);
|
|
165
|
+
|
|
166
|
+
return (
|
|
167
|
+
<div
|
|
168
|
+
className={classes([
|
|
169
|
+
'Dropdown__menuentry',
|
|
170
|
+
selected === value && 'selected',
|
|
171
|
+
])}
|
|
172
|
+
key={index}
|
|
173
|
+
onClick={() => {
|
|
174
|
+
setOpen(false);
|
|
175
|
+
onSelected?.(value);
|
|
176
|
+
}}
|
|
177
|
+
>
|
|
178
|
+
{typeof option === 'string' ? option : option.displayText}
|
|
179
|
+
</div>
|
|
180
|
+
);
|
|
181
|
+
})}
|
|
182
|
+
</div>
|
|
183
|
+
}
|
|
184
|
+
>
|
|
185
|
+
<div className="Dropdown" style={{ width: unit(width) }}>
|
|
186
|
+
<div
|
|
187
|
+
className={classes([
|
|
188
|
+
'Dropdown__control',
|
|
189
|
+
'Button',
|
|
190
|
+
'Button--dropdown',
|
|
191
|
+
'Button--color--' + color,
|
|
192
|
+
disabled && 'Button--disabled',
|
|
193
|
+
className,
|
|
194
|
+
])}
|
|
195
|
+
onClick={(event) => {
|
|
196
|
+
if (disabled && !open) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
setOpen(!open);
|
|
200
|
+
onClick?.(event);
|
|
201
|
+
}}
|
|
202
|
+
>
|
|
203
|
+
{icon && (
|
|
204
|
+
<Icon mr={1} name={icon} rotation={iconRotation} spin={iconSpin} />
|
|
205
|
+
)}
|
|
206
|
+
<span
|
|
207
|
+
className="Dropdown__selected-text"
|
|
208
|
+
style={{
|
|
209
|
+
overflow: clipSelectedText ? 'hidden' : 'visible',
|
|
210
|
+
}}
|
|
211
|
+
>
|
|
212
|
+
{displayText ||
|
|
213
|
+
(selected && getOptionValue(selected)) ||
|
|
214
|
+
placeholder}
|
|
215
|
+
</span>
|
|
216
|
+
{!noChevron && (
|
|
217
|
+
<span className="Dropdown__arrow-button">
|
|
218
|
+
<Icon name={adjustedOpen ? 'chevron-up' : 'chevron-down'} />
|
|
219
|
+
</span>
|
|
220
|
+
)}
|
|
221
|
+
</div>
|
|
222
|
+
{buttons && (
|
|
223
|
+
<>
|
|
224
|
+
<Button
|
|
225
|
+
disabled={disabled}
|
|
226
|
+
height={1.8}
|
|
227
|
+
icon="chevron-left"
|
|
228
|
+
onClick={() => {
|
|
229
|
+
updateSelected(DIRECTION.Previous);
|
|
230
|
+
}}
|
|
231
|
+
/>
|
|
232
|
+
|
|
233
|
+
<Button
|
|
234
|
+
disabled={disabled}
|
|
235
|
+
height={1.8}
|
|
236
|
+
icon="chevron-right"
|
|
237
|
+
onClick={() => {
|
|
238
|
+
updateSelected(DIRECTION.Next);
|
|
239
|
+
}}
|
|
240
|
+
/>
|
|
241
|
+
</>
|
|
242
|
+
)}
|
|
243
|
+
</div>
|
|
244
|
+
</Popper>
|
|
245
|
+
);
|
|
246
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Component, Fragment } from 'react';
|
|
2
|
+
|
|
3
|
+
import { Box } from './Box';
|
|
4
|
+
|
|
5
|
+
export class FakeTerminal extends Component {
|
|
6
|
+
constructor(props) {
|
|
7
|
+
super(props);
|
|
8
|
+
this.timer = null;
|
|
9
|
+
this.state = {
|
|
10
|
+
currentIndex: 0,
|
|
11
|
+
currentDisplay: [],
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
tick() {
|
|
16
|
+
const { props, state } = this;
|
|
17
|
+
if (state.currentIndex <= props.allMessages.length) {
|
|
18
|
+
this.setState((prevState) => {
|
|
19
|
+
return {
|
|
20
|
+
currentIndex: prevState.currentIndex + 1,
|
|
21
|
+
};
|
|
22
|
+
});
|
|
23
|
+
const { currentDisplay } = state;
|
|
24
|
+
currentDisplay.push(props.allMessages[state.currentIndex]);
|
|
25
|
+
} else {
|
|
26
|
+
clearTimeout(this.timer);
|
|
27
|
+
setTimeout(props.onFinished, props.finishedTimeout);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
componentDidMount() {
|
|
32
|
+
const { linesPerSecond = 2.5 } = this.props;
|
|
33
|
+
this.timer = setInterval(() => this.tick(), 1000 / linesPerSecond);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
componentWillUnmount() {
|
|
37
|
+
clearTimeout(this.timer);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
render() {
|
|
41
|
+
return (
|
|
42
|
+
<Box m={1}>
|
|
43
|
+
{this.state.currentDisplay.map((value) => (
|
|
44
|
+
<Fragment key={value}>
|
|
45
|
+
{value}
|
|
46
|
+
<br />
|
|
47
|
+
</Fragment>
|
|
48
|
+
))}
|
|
49
|
+
</Box>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Component,
|
|
3
|
+
createRef,
|
|
4
|
+
HTMLAttributes,
|
|
5
|
+
PropsWithChildren,
|
|
6
|
+
RefObject,
|
|
7
|
+
} from 'react';
|
|
8
|
+
|
|
9
|
+
const DEFAULT_ACCEPTABLE_DIFFERENCE = 5;
|
|
10
|
+
|
|
11
|
+
type Props = {
|
|
12
|
+
acceptableDifference?: number;
|
|
13
|
+
maxFontSize: number;
|
|
14
|
+
maxWidth: number;
|
|
15
|
+
native?: HTMLAttributes<HTMLDivElement>;
|
|
16
|
+
} & PropsWithChildren;
|
|
17
|
+
|
|
18
|
+
type State = {
|
|
19
|
+
fontSize: number;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export class FitText extends Component<Props, State> {
|
|
23
|
+
ref: RefObject<HTMLDivElement> = createRef();
|
|
24
|
+
state: State = {
|
|
25
|
+
fontSize: 0,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
constructor(props: Props) {
|
|
29
|
+
super(props);
|
|
30
|
+
|
|
31
|
+
this.resize = this.resize.bind(this);
|
|
32
|
+
|
|
33
|
+
window.addEventListener('resize', this.resize);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
componentDidUpdate(prevProps) {
|
|
37
|
+
if (prevProps.children !== this.props.children) {
|
|
38
|
+
this.resize();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
componentWillUnmount() {
|
|
43
|
+
window.removeEventListener('resize', this.resize);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
resize() {
|
|
47
|
+
const element = this.ref.current;
|
|
48
|
+
if (!element) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const maxWidth = this.props.maxWidth;
|
|
53
|
+
|
|
54
|
+
let start = 0;
|
|
55
|
+
let end = this.props.maxFontSize;
|
|
56
|
+
|
|
57
|
+
for (let _ = 0; _ < 10; _++) {
|
|
58
|
+
const middle = Math.round((start + end) / 2);
|
|
59
|
+
element.style.fontSize = `${middle}px`;
|
|
60
|
+
|
|
61
|
+
const difference = element.offsetWidth - maxWidth;
|
|
62
|
+
|
|
63
|
+
if (difference > 0) {
|
|
64
|
+
end = middle;
|
|
65
|
+
} else if (
|
|
66
|
+
difference <
|
|
67
|
+
(this.props.acceptableDifference ?? DEFAULT_ACCEPTABLE_DIFFERENCE)
|
|
68
|
+
) {
|
|
69
|
+
start = middle;
|
|
70
|
+
} else {
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
this.setState({
|
|
76
|
+
fontSize: Math.round((start + end) / 2),
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
componentDidMount() {
|
|
81
|
+
this.resize();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
render() {
|
|
85
|
+
return (
|
|
86
|
+
<span
|
|
87
|
+
ref={this.ref}
|
|
88
|
+
style={{
|
|
89
|
+
fontSize: `${this.state.fontSize}px`,
|
|
90
|
+
...(typeof this.props.native?.style === 'object'
|
|
91
|
+
? this.props.native.style
|
|
92
|
+
: {}),
|
|
93
|
+
}}
|
|
94
|
+
>
|
|
95
|
+
{this.props.children}
|
|
96
|
+
</span>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { classes } from '../common/react';
|
|
2
|
+
import styles from '../styles/components/Flex.module.scss';
|
|
3
|
+
import { BoxProps, computeBoxClassName, computeBoxProps, unit } from './Box';
|
|
4
|
+
|
|
5
|
+
export type FlexProps = Partial<{
|
|
6
|
+
/**
|
|
7
|
+
* Default alignment of all children.
|
|
8
|
+
*
|
|
9
|
+
* - `stretch` (default) - stretch to fill the container.
|
|
10
|
+
* - `start` - items are placed at the start of the cross axis.
|
|
11
|
+
* - `end` - items are placed at the end of the cross axis.
|
|
12
|
+
* - `center` - items are centered on the cross axis.
|
|
13
|
+
* - `baseline` - items are aligned such as their baselines align.
|
|
14
|
+
*/
|
|
15
|
+
align: string | boolean;
|
|
16
|
+
/**
|
|
17
|
+
* This establishes the main-axis, thus defining the direction flex items are placed in the flex container.
|
|
18
|
+
*
|
|
19
|
+
* - `row` (default) - left to right.
|
|
20
|
+
* - `row-reverse` - right to left.
|
|
21
|
+
* - `column` - top to bottom.
|
|
22
|
+
* - `column-reverse` - bottom to top.
|
|
23
|
+
*/
|
|
24
|
+
direction: string;
|
|
25
|
+
/** Makes flexbox container inline, with similar behavior to an `inline` property on a `Box`. */
|
|
26
|
+
inline: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* This defines the alignment along the main axis. It helps distribute extra free space leftover when either all the flex items on a line are
|
|
29
|
+
* inflexible, or are flexible but have reached their maximum size. It also exerts some control over the alignment of items when they overflow
|
|
30
|
+
* the line.
|
|
31
|
+
*
|
|
32
|
+
* - `flex-start` (default) - items are packed toward the start of the
|
|
33
|
+
* flex-direction.
|
|
34
|
+
* - `flex-end` - items are packed toward the end of the flex-direction.
|
|
35
|
+
* - `space-between` - items are evenly distributed in the line; first item is
|
|
36
|
+
* on the start line, last item on the end line
|
|
37
|
+
* - `space-around` - items are evenly distributed in the line with equal space
|
|
38
|
+
* around them. Note that visually the spaces aren't equal, since all the items
|
|
39
|
+
* have equal space on both sides. The first item will have one unit of space
|
|
40
|
+
* against the container edge, but two units of space between the next item
|
|
41
|
+
* because that next item has its own spacing that applies.
|
|
42
|
+
* - `space-evenly` - items are distributed so that the spacing between any two
|
|
43
|
+
* items (and the space to the edges) is equal.
|
|
44
|
+
*/
|
|
45
|
+
justify: string;
|
|
46
|
+
/** By default, flex items will all try to fit onto one line. You can change that and allow the items to wrap as needed with this property. */
|
|
47
|
+
scrollable: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* This defines the alignment along the cross axis. It helps distribute extra free space leftover when either all the flex items on a line are
|
|
50
|
+
* inflexible, or are flexible but have reached their maximum size. It also exerts some control over the alignment of items when they overflow
|
|
51
|
+
* the line.
|
|
52
|
+
*
|
|
53
|
+
* - `nowrap` (default) - all flex items will be on one line
|
|
54
|
+
* - `wrap` - flex items will wrap onto multiple lines, from top to bottom.
|
|
55
|
+
* - `wrap-reverse` - flex items will wrap onto multiple lines from bottom to top.
|
|
56
|
+
*/
|
|
57
|
+
wrap: string | boolean;
|
|
58
|
+
}> &
|
|
59
|
+
BoxProps;
|
|
60
|
+
|
|
61
|
+
export function computeFlexClassName(props: FlexProps) {
|
|
62
|
+
return classes([
|
|
63
|
+
styles.flex,
|
|
64
|
+
props.inline && styles.inline,
|
|
65
|
+
computeBoxClassName(props),
|
|
66
|
+
]);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function computeFlexProps(props: FlexProps) {
|
|
70
|
+
const { direction, wrap, align, justify, ...rest } = props;
|
|
71
|
+
|
|
72
|
+
return computeBoxProps({
|
|
73
|
+
style: {
|
|
74
|
+
...rest.style,
|
|
75
|
+
flexDirection: direction,
|
|
76
|
+
flexWrap: wrap === true ? 'wrap' : wrap,
|
|
77
|
+
alignItems: align,
|
|
78
|
+
justifyContent: justify,
|
|
79
|
+
},
|
|
80
|
+
...rest,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function Flex(props) {
|
|
85
|
+
const { className, ...rest } = props;
|
|
86
|
+
return (
|
|
87
|
+
<div
|
|
88
|
+
className={classes([className, computeFlexClassName(rest)])}
|
|
89
|
+
{...computeFlexProps(rest)}
|
|
90
|
+
/>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export type FlexItemProps = Partial<{
|
|
95
|
+
/** This allows the default alignment (or the one specified by align-items) to be overridden for individual flex items. */
|
|
96
|
+
align: string | boolean;
|
|
97
|
+
/**
|
|
98
|
+
* This defines the default size of an element
|
|
99
|
+
* before any flex-related calculations are done. Has to be a length
|
|
100
|
+
* (e.g. `20%`, `5rem`), an `auto` or `content` keyword.
|
|
101
|
+
*
|
|
102
|
+
* - **Important:** IE11 flex is buggy, and auto width/height calculations
|
|
103
|
+
* can sometimes end up in a circular dependency. This usually happens, when
|
|
104
|
+
* working with tables inside flex (they have wacky internal widths and such).
|
|
105
|
+
* Setting basis to `0` breaks the loop and fixes all of the problems.
|
|
106
|
+
*/
|
|
107
|
+
basis: string | number;
|
|
108
|
+
/**
|
|
109
|
+
* This defines the ability for a flex item to grow if necessary. It accepts a unitless value that serves as a proportion.
|
|
110
|
+
* It dictates what amount of the available space inside the flex container the item should take up.
|
|
111
|
+
* This number is unit-less and is relative to other siblings.
|
|
112
|
+
*/
|
|
113
|
+
grow: number | boolean;
|
|
114
|
+
/**
|
|
115
|
+
* By default, flex items are laid out in the source order. However, the order property controls the order in which they appear in the
|
|
116
|
+
* flex container
|
|
117
|
+
*/
|
|
118
|
+
order: number;
|
|
119
|
+
/** This defines the ability for a flex item to shrink if necessary. Inverse of `grow`. */
|
|
120
|
+
shrink: number | boolean;
|
|
121
|
+
}> &
|
|
122
|
+
BoxProps;
|
|
123
|
+
|
|
124
|
+
export function computeFlexItemProps(props: FlexItemProps) {
|
|
125
|
+
const { style, grow, order, shrink, basis, align, ...rest } = props;
|
|
126
|
+
|
|
127
|
+
const computedBasis =
|
|
128
|
+
basis ??
|
|
129
|
+
// IE11: Set basis to specified width if it's known, which fixes certain
|
|
130
|
+
// bugs when rendering tables inside the flex.
|
|
131
|
+
props.width ??
|
|
132
|
+
// If grow is used, basis should be set to 0 to be consistent with
|
|
133
|
+
// flex css shorthand `flex: 1`.
|
|
134
|
+
(grow !== undefined ? 0 : undefined);
|
|
135
|
+
|
|
136
|
+
return computeBoxProps({
|
|
137
|
+
style: {
|
|
138
|
+
...style,
|
|
139
|
+
flexGrow: grow !== undefined && Number(grow),
|
|
140
|
+
flexShrink: shrink !== undefined && Number(shrink),
|
|
141
|
+
flexBasis: unit(computedBasis),
|
|
142
|
+
order: order,
|
|
143
|
+
alignSelf: align,
|
|
144
|
+
},
|
|
145
|
+
...rest,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function FlexItem(props) {
|
|
150
|
+
const { className, ...rest } = props;
|
|
151
|
+
return (
|
|
152
|
+
<div
|
|
153
|
+
className={classes([className, computeBoxClassName(props)])}
|
|
154
|
+
{...computeFlexItemProps(rest)}
|
|
155
|
+
/>
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
Flex.Item = FlexItem;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { CSSProperties, ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
import { BooleanLike, classes } from '../common/react';
|
|
4
|
+
import style from '../styles/components/Icon.module.scss';
|
|
5
|
+
import { BoxProps, computeBoxClassName, computeBoxProps } from './Box';
|
|
6
|
+
|
|
7
|
+
type Props = {
|
|
8
|
+
/** Icon name. See [icon list](https://fontawesome.com/v5/search?o=r&m=free) */
|
|
9
|
+
name: string;
|
|
10
|
+
} & Partial<{
|
|
11
|
+
/** Custom CSS class. */
|
|
12
|
+
className: string;
|
|
13
|
+
/** Icon rotation, in degrees. */
|
|
14
|
+
rotation: number;
|
|
15
|
+
/** Icon size. `1` is normal size, `2` is two times bigger. Fractional numbers are supported. */
|
|
16
|
+
size: number;
|
|
17
|
+
/** Whether an icon should be spinning. Good for load indicators. */
|
|
18
|
+
spin: BooleanLike;
|
|
19
|
+
/** Custom CSS. */
|
|
20
|
+
style: CSSProperties;
|
|
21
|
+
}> &
|
|
22
|
+
BoxProps;
|
|
23
|
+
|
|
24
|
+
const FA_OUTLINE_REGEX = /-o$/;
|
|
25
|
+
|
|
26
|
+
export function Icon(props: Props) {
|
|
27
|
+
const { name, size, spin, className, rotation, ...rest } = props;
|
|
28
|
+
|
|
29
|
+
const customStyle = rest.style || {};
|
|
30
|
+
if (size) {
|
|
31
|
+
customStyle.fontSize = size * 100 + '%';
|
|
32
|
+
}
|
|
33
|
+
if (rotation) {
|
|
34
|
+
customStyle.transform = `rotate(${rotation}deg)`;
|
|
35
|
+
}
|
|
36
|
+
rest.style = customStyle;
|
|
37
|
+
|
|
38
|
+
const boxProps = computeBoxProps(rest);
|
|
39
|
+
|
|
40
|
+
let iconClass = '';
|
|
41
|
+
if (name.startsWith('tg-')) {
|
|
42
|
+
// tgfont icon
|
|
43
|
+
iconClass = name;
|
|
44
|
+
} else {
|
|
45
|
+
// font awesome icon
|
|
46
|
+
const faRegular = FA_OUTLINE_REGEX.test(name);
|
|
47
|
+
const faName = name.replace(FA_OUTLINE_REGEX, '');
|
|
48
|
+
const preprendFa = !faName.startsWith('fa-');
|
|
49
|
+
|
|
50
|
+
iconClass = faRegular ? 'far ' : 'fas ';
|
|
51
|
+
if (preprendFa) {
|
|
52
|
+
iconClass += 'fa-';
|
|
53
|
+
}
|
|
54
|
+
iconClass += faName;
|
|
55
|
+
if (spin) {
|
|
56
|
+
iconClass += ' fa-spin';
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return (
|
|
60
|
+
<i
|
|
61
|
+
className={classes([
|
|
62
|
+
style.icon,
|
|
63
|
+
iconClass,
|
|
64
|
+
className,
|
|
65
|
+
computeBoxClassName(rest),
|
|
66
|
+
])}
|
|
67
|
+
{...boxProps}
|
|
68
|
+
/>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
type IconStackUnique = {
|
|
73
|
+
children: ReactNode;
|
|
74
|
+
className?: string;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export type IconStackProps = IconStackUnique & BoxProps;
|
|
78
|
+
|
|
79
|
+
export function IconStack(props: IconStackProps) {
|
|
80
|
+
const { className, children, ...rest } = props;
|
|
81
|
+
return (
|
|
82
|
+
<span
|
|
83
|
+
className={classes([
|
|
84
|
+
style.iconStack,
|
|
85
|
+
className,
|
|
86
|
+
computeBoxClassName(rest),
|
|
87
|
+
])}
|
|
88
|
+
{...computeBoxProps(rest)}
|
|
89
|
+
>
|
|
90
|
+
{children}
|
|
91
|
+
</span>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
Icon.Stack = IconStack;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
import { BoxProps, computeBoxProps } from './Box';
|
|
4
|
+
|
|
5
|
+
type Props = Partial<{
|
|
6
|
+
className: string;
|
|
7
|
+
/** True is default, this fixes an ie thing */
|
|
8
|
+
fixBlur: boolean;
|
|
9
|
+
/** False by default. Good if you're fetching images on UIs that do not auto update. This will attempt to fix the 'x' icon 5 times. */
|
|
10
|
+
fixErrors: boolean;
|
|
11
|
+
/** Fill is default. */
|
|
12
|
+
objectFit: 'contain' | 'cover';
|
|
13
|
+
src: string;
|
|
14
|
+
}> &
|
|
15
|
+
BoxProps;
|
|
16
|
+
|
|
17
|
+
// at least one of these is required
|
|
18
|
+
|
|
19
|
+
const maxAttempts = 5;
|
|
20
|
+
|
|
21
|
+
export function Image(props: Props) {
|
|
22
|
+
const {
|
|
23
|
+
fixBlur = true,
|
|
24
|
+
fixErrors = false,
|
|
25
|
+
objectFit = 'fill',
|
|
26
|
+
src,
|
|
27
|
+
...rest
|
|
28
|
+
} = props;
|
|
29
|
+
const attempts = useRef(0);
|
|
30
|
+
|
|
31
|
+
const computedProps = computeBoxProps(rest);
|
|
32
|
+
computedProps['style'] = {
|
|
33
|
+
...computedProps.style,
|
|
34
|
+
'-ms-interpolation-mode': fixBlur ? 'nearest-neighbor' : 'auto',
|
|
35
|
+
objectFit,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<img
|
|
40
|
+
onError={(event) => {
|
|
41
|
+
if (fixErrors && attempts.current < maxAttempts) {
|
|
42
|
+
const imgElement = event.currentTarget;
|
|
43
|
+
|
|
44
|
+
setTimeout(() => {
|
|
45
|
+
imgElement.src = `${src}?attempt=${attempts.current}`;
|
|
46
|
+
attempts.current++;
|
|
47
|
+
}, 1000);
|
|
48
|
+
}
|
|
49
|
+
}}
|
|
50
|
+
src={src}
|
|
51
|
+
{...computedProps}
|
|
52
|
+
/>
|
|
53
|
+
);
|
|
54
|
+
}
|