typescript-overlay-essentials 1.1.5 → 1.3.0
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/package.json +21 -3
- package/.github/workflows/npm-publish.yml +0 -32
- package/ConfirmationBox.css +0 -69
- package/ConfirmationBox.tsx +0 -148
- package/Dropdown.css +0 -31
- package/Dropdown.tsx +0 -211
- package/InfoOverlay.css +0 -65
- package/InfoOverlay.tsx +0 -101
- package/InfoOverlayWithInput.css +0 -88
- package/InfoOverlayWithInput.tsx +0 -157
- package/InputFilter.tsx +0 -36
- package/LoadingOverlay.css +0 -110
- package/LoadingOverlay.tsx +0 -57
- package/MultipleChoiceOverlay.css +0 -170
- package/MultipleChoiceOverlay.tsx +0 -170
- package/MultipleRadioOverlay.css +0 -170
- package/MultipleRadioOverlay.tsx +0 -158
- package/Toast.css +0 -13
- package/Toast.tsx +0 -34
- package/ToggleSwitch.tsx +0 -56
- package/Toggleswitch.css +0 -49
- package/index.ts +0 -8
package/InfoOverlay.tsx
DELETED
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import { useEffect, useState, CSSProperties } from 'react';
|
|
2
|
-
import * as React from 'react';
|
|
3
|
-
import './InfoOverlay.css';
|
|
4
|
-
|
|
5
|
-
// Um das OnfoOverlay zu nutzen, muss der State die folgende Struktur haben:
|
|
6
|
-
export interface InfoOverlayState {
|
|
7
|
-
headline?: string | undefined;
|
|
8
|
-
message?: string | undefined;
|
|
9
|
-
proceedButtonText?: string | undefined;
|
|
10
|
-
handler?: ((args?: unknown) => void) | undefined;
|
|
11
|
-
handlerArgs?: unknown;
|
|
12
|
-
addCloseButton?: boolean | undefined;
|
|
13
|
-
style?: CSSProperties | undefined;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
// Der standard InfoOverlay Status, der zur Initialisierung genutzt werden kann
|
|
17
|
-
export const defaultInfoOverlayState: InfoOverlayState = {
|
|
18
|
-
headline: undefined,
|
|
19
|
-
message: undefined,
|
|
20
|
-
proceedButtonText: undefined,
|
|
21
|
-
handler: undefined,
|
|
22
|
-
handlerArgs: undefined,
|
|
23
|
-
addCloseButton: false,
|
|
24
|
-
style: undefined,
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
// Wobei alle Attribute grundsätzlich optional sind:
|
|
28
|
-
// - headline: ist die Überschrift und wird fett hinterlegt
|
|
29
|
-
// - message: ist die angezeigte Nachricht
|
|
30
|
-
// - proceedButtonText: ist der Text der auf dem Procceed Button stehen soll (ohne Angabe wird "OK" verwendet)
|
|
31
|
-
// - handler: Funktion, die optional beim Bestätigen ausgeführt werden kann (Struktur: handler(args))
|
|
32
|
-
// - handlerArgs: kann im handler als Argumente genutzt werden
|
|
33
|
-
// - addCloseButton: boolscher Wert, der angibt, ob ein x oben rechts als close-Button verfügbar sein soll (bricht die Aktion ohne handler ab, standardmäßig false)
|
|
34
|
-
// - style: ist der Style der Information-Box (Standardmäßig unverändert)
|
|
35
|
-
|
|
36
|
-
export function InfoOverlay({ state, setState }: { state: InfoOverlayState, setState: (state: InfoOverlayState) => void }): React.ReactElement {
|
|
37
|
-
|
|
38
|
-
// Wird verwendet um das Infoverlay ein- und auszublenden
|
|
39
|
-
const [showOverlay, setShowOverlay] = useState(false);
|
|
40
|
-
|
|
41
|
-
// Sobald der State von außen aktualisiert wird triggert diese Funktion
|
|
42
|
-
// Die setzt showOverlay auf true
|
|
43
|
-
useEffect(() => {
|
|
44
|
-
if (state?.message !== undefined || state?.headline !== undefined) {
|
|
45
|
-
setShowOverlay(true);
|
|
46
|
-
setTimeout(() => { document.getElementById("infoButton")?.focus(); }, 5);
|
|
47
|
-
}
|
|
48
|
-
}, [state]);
|
|
49
|
-
|
|
50
|
-
const handleAction = () : void => {
|
|
51
|
-
setShowOverlay(false);
|
|
52
|
-
const tempState = {
|
|
53
|
-
handler: state.handler,
|
|
54
|
-
handlerArgs: state.handlerArgs
|
|
55
|
-
};
|
|
56
|
-
setState(defaultInfoOverlayState);
|
|
57
|
-
if (typeof tempState.handler === 'function')
|
|
58
|
-
tempState.handler(tempState.handlerArgs);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return showOverlay ?
|
|
62
|
-
<div className="information-overlay"
|
|
63
|
-
tabIndex={0}>
|
|
64
|
-
<div className="information-box" style={state?.style !== undefined ? state.style : {}}>
|
|
65
|
-
{state.addCloseButton?
|
|
66
|
-
<span className="closeButton">
|
|
67
|
-
<svg xmlns="http://www.w3.org/2000/svg"
|
|
68
|
-
tabIndex={0}
|
|
69
|
-
className="close-icon"
|
|
70
|
-
onClick={() => setShowOverlay(false)}
|
|
71
|
-
onKeyDown={(e) => {
|
|
72
|
-
if (e.key === 'Enter' || e.key === ' ') {
|
|
73
|
-
e.preventDefault(); // Verhindert Scroll bei Space
|
|
74
|
-
setShowOverlay(false);
|
|
75
|
-
}
|
|
76
|
-
}}
|
|
77
|
-
role="button" aria-label="Dialog schließen"
|
|
78
|
-
width="24" height="24" viewBox="0 0 24 24" fill="none"
|
|
79
|
-
stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
80
|
-
<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
|
|
81
|
-
</svg>
|
|
82
|
-
</span> :
|
|
83
|
-
<></>}
|
|
84
|
-
<p className="headline" tabIndex={0} style={{ whiteSpace: "pre-line" }}><strong>{state?.headline !== undefined ? state.headline : ""}</strong></p>
|
|
85
|
-
<p tabIndex={0} style={{ whiteSpace: "pre-line", wordBreak: "break-word" }}>{state?.message !== undefined ? state.message : ""}</p>
|
|
86
|
-
<div className="information-buttons">
|
|
87
|
-
<button onClick={() => handleAction()}
|
|
88
|
-
onKeyDown={(e) => {
|
|
89
|
-
if (e.key === 'Enter' || e.key === ' ') {
|
|
90
|
-
e.preventDefault(); // Verhindert Scroll bei Space
|
|
91
|
-
handleAction();
|
|
92
|
-
}
|
|
93
|
-
}}
|
|
94
|
-
className="px-4 py-2 bg-blue-600 text-white rounded"
|
|
95
|
-
id="infoButton">
|
|
96
|
-
{state?.proceedButtonText !== undefined ? state.proceedButtonText : "OK"}
|
|
97
|
-
</button>
|
|
98
|
-
</div>
|
|
99
|
-
</div>
|
|
100
|
-
</div> : <></>;
|
|
101
|
-
}
|
package/InfoOverlayWithInput.css
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
.information-overlay-with-input {
|
|
2
|
-
position: fixed;
|
|
3
|
-
top: 0;
|
|
4
|
-
left: 0;
|
|
5
|
-
width: 100vw;
|
|
6
|
-
height: 100vh;
|
|
7
|
-
background-color: rgba(0, 0, 0, 0.6);
|
|
8
|
-
display: flex;
|
|
9
|
-
align-items: center;
|
|
10
|
-
justify-content: center;
|
|
11
|
-
z-index: 10000;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
.information-box-with-input {
|
|
15
|
-
background-color: var(--Hellgrau, white);
|
|
16
|
-
color: var(--HellSchwarz,#2c2c2c);
|
|
17
|
-
padding: 2rem;
|
|
18
|
-
border-radius: 12px;
|
|
19
|
-
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.5);
|
|
20
|
-
max-width: 500px;
|
|
21
|
-
width: 90%;
|
|
22
|
-
text-align: center;
|
|
23
|
-
animation: fadeIn 0.3s ease-in-out;
|
|
24
|
-
white-space: pre-line;
|
|
25
|
-
position: relative;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
@keyframes fadeIn {
|
|
29
|
-
from {
|
|
30
|
-
opacity: 0;
|
|
31
|
-
transform: scale(0.95);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
to {
|
|
35
|
-
opacity: 1;
|
|
36
|
-
transform: scale(1);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
.information-box-with-input .information-buttons {
|
|
41
|
-
display: flex;
|
|
42
|
-
justify-content: center;
|
|
43
|
-
gap: 1rem;
|
|
44
|
-
margin-top: 1.5rem;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
.information-box-with-input .information-buttons button {
|
|
48
|
-
font-size: 85%;
|
|
49
|
-
padding: 0.5rem 1.1rem;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
.information-box-with-input input[type="text"] {
|
|
53
|
-
margin-top: 15px;
|
|
54
|
-
padding: 0.75rem 1rem;
|
|
55
|
-
font-size: 1em;
|
|
56
|
-
color: var(--FastSchwarz, #111);
|
|
57
|
-
background-color: var(--FastWeiß, #fff);
|
|
58
|
-
box-shadow: inset 0 0 0 1px #ccc;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
.information-box-with-input .headline {
|
|
62
|
-
margin-bottom: 0.75em;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/* ==== Dark Mode Support ==== */
|
|
66
|
-
@media (prefers-color-scheme: dark) {
|
|
67
|
-
.information-box-with-input {
|
|
68
|
-
background-color: var(--HellSchwarz,#2c2c2c);
|
|
69
|
-
color: #fff;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
.information-box-with-input input[type="text"] {
|
|
73
|
-
background-color: var(--HellSchwarz, #2c2c2c);
|
|
74
|
-
color: var(--FastWeiß, white);
|
|
75
|
-
box-shadow: inset 0 0 0 1px #444;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
.information-box-with-input .closeButton {
|
|
80
|
-
right: 10px;
|
|
81
|
-
top: 10px;
|
|
82
|
-
position: absolute;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
.information-box-with-input .close-icon:hover {
|
|
86
|
-
color: var(--HSDAkzent);
|
|
87
|
-
cursor: pointer;
|
|
88
|
-
}
|
package/InfoOverlayWithInput.tsx
DELETED
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
import { useEffect, useState, CSSProperties } from 'react';
|
|
2
|
-
import './InfoOverlayWithInput.css';
|
|
3
|
-
|
|
4
|
-
// Um das InfoOverlayWithInput zu nutzen, muss der State die folgende Struktur haben:
|
|
5
|
-
export interface InfoOverlayWithInputState {
|
|
6
|
-
headline?: string | undefined;
|
|
7
|
-
message?: string | undefined;
|
|
8
|
-
preInput?: string | undefined;
|
|
9
|
-
placeholder?: string | undefined;
|
|
10
|
-
cancelButtonText?: string | undefined;
|
|
11
|
-
proceedButtonText?: string | undefined;
|
|
12
|
-
handlerOk?: ((userInput: string, args?: unknown) => void) | undefined;
|
|
13
|
-
handlerCancel?: ((args?: unknown) => void) | undefined;
|
|
14
|
-
handlerArgs?: unknown;
|
|
15
|
-
addCloseButton?: boolean | undefined;
|
|
16
|
-
proceedButtonStyle?: CSSProperties | undefined;
|
|
17
|
-
cancelButtonStyle?: CSSProperties | undefined;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// Um das InfoOverlayWithInput zu nutzen, muss der State die folgende Struktur haben:
|
|
21
|
-
export const defaultInfoOverlayWithInputState : InfoOverlayWithInputState = {
|
|
22
|
-
headline: undefined,
|
|
23
|
-
message: undefined,
|
|
24
|
-
preInput: undefined,
|
|
25
|
-
placeholder: undefined,
|
|
26
|
-
cancelButtonText: undefined,
|
|
27
|
-
proceedButtonText: undefined,
|
|
28
|
-
handlerOk: undefined,
|
|
29
|
-
handlerCancel: undefined,
|
|
30
|
-
handlerArgs: undefined,
|
|
31
|
-
addCloseButton: false,
|
|
32
|
-
proceedButtonStyle: undefined,
|
|
33
|
-
cancelButtonStyle: undefined,
|
|
34
|
-
};
|
|
35
|
-
// Wobei alle Attribute grundsätzlich optional sind:
|
|
36
|
-
// - headline: ist die Überschrift und wird fett hinterlegt
|
|
37
|
-
// - message: ist die angezeigte Nachricht
|
|
38
|
-
// - preInput: Beinhaltet den Text der schon vorher im Inputfield stehen soll (ohne Angabe wird nichts angezeigt)
|
|
39
|
-
// - placeholder: Was bei leerem Inputfield als Platzhalter angezeigt wird (ohne Angabe wird nichts angezeigt)
|
|
40
|
-
// - cancelButtonText: ist der Text der auf dem Cancel Button stehen soll (ohne Angabe wird "Abbrechen" verwendet)
|
|
41
|
-
// - proceedButtonText: ist der Text der auf dem Proceed Button stehen soll (ohne Angabe wird "OK" verwendet)
|
|
42
|
-
// - handlerOk: ist die Funktion die bei Bestätigung des Inputs ausgeführt wird (Struktur: handlerOk(userInput, handlerArgs)
|
|
43
|
-
// - handlerCancel: ist die Funktion die bei Ablehnung des Inputs ausgeführt wird (Struktur: handlerCancel(handlerArgs)
|
|
44
|
-
// - handlerArgs: kann im handler als Argumente genutzt werden
|
|
45
|
-
// - addCloseButton: boolscher Wert, der angibt, ob ein x oben rechts als close-Button verfügbar sein soll (bricht die Aktion ohne handler ab, standardmäßig false)
|
|
46
|
-
// - proceedButtonStyle: ist der Style des Bestätigungsbuttons (Standardmäßig unverändert)
|
|
47
|
-
// - cancelButtonStyle: ist der Style des Abbrechenbuttons (Standardmäßig unverändert)
|
|
48
|
-
|
|
49
|
-
// Output: Der Input vom User wird am Ende an den handlerOk übergeben (oder bei handlerCancel ignoriert)!
|
|
50
|
-
// Somit wird der handler so aufgerufen: handlerOk(UserInput, handlerArgs) oder handlerCancel(handlerArgs)
|
|
51
|
-
export function InfoOverlayWithInput({ state, setState }: { state: InfoOverlayWithInputState, setState: (state: InfoOverlayWithInputState) => void }): React.ReactElement {
|
|
52
|
-
|
|
53
|
-
// Wird verwendet um das Infoverlay ein- und auszublenden
|
|
54
|
-
const [showOverlay, setShowOverlay] = useState(false);
|
|
55
|
-
|
|
56
|
-
// Wird verwendet um den Inhalt des InputFields aktuell zu halten
|
|
57
|
-
const [input, setInput] = useState<string>("");
|
|
58
|
-
|
|
59
|
-
// Sobald der State von außen aktualisiert wird triggert diese Funktion
|
|
60
|
-
// Die setzt showOverlay auf true
|
|
61
|
-
useEffect(() => {
|
|
62
|
-
if (state?.message != null || state?.headline != null) {
|
|
63
|
-
setInput(state?.preInput != null ? state.preInput : "");
|
|
64
|
-
setShowOverlay(true);
|
|
65
|
-
setTimeout(() => { document.getElementById("information-inputfield")?.focus(); }, 1);
|
|
66
|
-
}
|
|
67
|
-
}, [state]);
|
|
68
|
-
|
|
69
|
-
// Sorgt dafür, dass die Eingabe beim tippen aktualisiert wird
|
|
70
|
-
const handleInputChange = (event : React.ChangeEvent<HTMLInputElement>) => {
|
|
71
|
-
setInput(event.target.value);
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
// Sorgt dafür, dass auch bei Enter bestätigt wird
|
|
75
|
-
const handleInputKeyDown = (event : React.KeyboardEvent) => {
|
|
76
|
-
if (event.key === "Enter") {
|
|
77
|
-
handleAction(state.handlerOk, true);
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
const handleAction = (
|
|
82
|
-
handler: ((args?: unknown) => void) | ((userInput: string, args?: unknown) => void) | undefined,
|
|
83
|
-
useInput: boolean
|
|
84
|
-
) : void => {
|
|
85
|
-
setShowOverlay(false);
|
|
86
|
-
var tempState = {
|
|
87
|
-
handler: handler,
|
|
88
|
-
handlerArgs: state.handlerArgs
|
|
89
|
-
};
|
|
90
|
-
setState(defaultInfoOverlayWithInputState);
|
|
91
|
-
if (typeof tempState.handler === 'function' && useInput)
|
|
92
|
-
(tempState.handler as ((userInput: string, args?: unknown) => void))(input, tempState.handlerArgs);
|
|
93
|
-
else if (typeof tempState.handler === 'function')
|
|
94
|
-
(tempState.handler as ((args?: unknown) => void))(tempState.handlerArgs);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
return showOverlay ?
|
|
98
|
-
<div className="information-overlay-with-input">
|
|
99
|
-
<div className="information-box-with-input">
|
|
100
|
-
{state.addCloseButton?
|
|
101
|
-
<span className="closeButton">
|
|
102
|
-
<svg xmlns="http://www.w3.org/2000/svg"
|
|
103
|
-
tabIndex={0}
|
|
104
|
-
className="close-icon"
|
|
105
|
-
onClick={() => handleAction(undefined, false)}
|
|
106
|
-
onKeyDown={(e) => {
|
|
107
|
-
if (e.key === 'Enter' || e.key === ' ') {
|
|
108
|
-
e.preventDefault(); // Verhindert Scroll bei Space
|
|
109
|
-
handleAction(undefined, false);
|
|
110
|
-
}
|
|
111
|
-
}}
|
|
112
|
-
role="button" aria-label="Dialog schließen"
|
|
113
|
-
width="24" height="24" viewBox="0 0 24 24" fill="none"
|
|
114
|
-
stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
115
|
-
<line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line>
|
|
116
|
-
</svg>
|
|
117
|
-
</span> :
|
|
118
|
-
<></>}
|
|
119
|
-
<p className="headline" style={{ whiteSpace: "pre-line", wordBreak: "break-word" }}><strong>{state?.headline != null ? state.headline : ""}</strong></p>
|
|
120
|
-
<p style={{ whiteSpace: "pre-line", wordBreak: "break-word" }}>{state?.message != null ? state.message : ""}</p>
|
|
121
|
-
<div className="information-input">
|
|
122
|
-
<input
|
|
123
|
-
id="information-inputfield"
|
|
124
|
-
placeholder={state?.placeholder != null ? state.placeholder : ""}
|
|
125
|
-
pattern="^[A-Za-z0-9_-~]+$"
|
|
126
|
-
required={true}
|
|
127
|
-
type="text"
|
|
128
|
-
className="form-control"
|
|
129
|
-
value={input}
|
|
130
|
-
onChange={handleInputChange}
|
|
131
|
-
onKeyDown={handleInputKeyDown}
|
|
132
|
-
tabIndex={0}
|
|
133
|
-
></input>
|
|
134
|
-
</div>
|
|
135
|
-
<div className="information-buttons">
|
|
136
|
-
<button onClick={() => handleAction(state.handlerCancel, false)}
|
|
137
|
-
className="px-4 py-2 bg-gray-300 rounded"
|
|
138
|
-
style={state?.cancelButtonStyle != null ? state.cancelButtonStyle : {}}
|
|
139
|
-
tabIndex={0}
|
|
140
|
-
onKeyDown={(e) => {
|
|
141
|
-
if (e.key === 'Enter' || e.key === ' ') {
|
|
142
|
-
e.preventDefault(); // Verhindert Scroll bei Space
|
|
143
|
-
handleAction(state.handlerCancel, false);
|
|
144
|
-
}
|
|
145
|
-
}}>
|
|
146
|
-
{state?.cancelButtonText != null ? state.cancelButtonText : "Abbrechen"}
|
|
147
|
-
</button>
|
|
148
|
-
<button onClick={() => handleAction(state.handlerOk, true)}
|
|
149
|
-
className="px-4 py-2 bg-blue-600 text-white rounded"
|
|
150
|
-
style={state?.proceedButtonStyle != null ? state.proceedButtonStyle : {}}
|
|
151
|
-
tabIndex = {0}>
|
|
152
|
-
{state?.proceedButtonText != null ? state.proceedButtonText : "OK"}
|
|
153
|
-
</button>
|
|
154
|
-
</div>
|
|
155
|
-
</div>
|
|
156
|
-
</div> : <></>;
|
|
157
|
-
}
|
package/InputFilter.tsx
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
// Restricts input for the given textbox to the given inputFilter function.
|
|
2
|
-
export function setInputFilter(textbox: HTMLElement | undefined, inputFilter: (value: string) => boolean, errMsg: string) : void {
|
|
3
|
-
[ "input", "keydown", "keyup", "mousedown", "mouseup", "select", "contextmenu", "drop", "focusout" ].forEach(function(event) {
|
|
4
|
-
if(textbox) {
|
|
5
|
-
textbox.addEventListener(event, function(e: Event) {
|
|
6
|
-
const target = e.currentTarget as HTMLInputElement;
|
|
7
|
-
if (inputFilter(target.value)) {
|
|
8
|
-
// Accepted value.
|
|
9
|
-
if ([ "keydown", "mousedown", "focusout" ].indexOf(e.type) >= 0){
|
|
10
|
-
target.classList.remove("input-error");
|
|
11
|
-
target.setCustomValidity("");
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
target.dataset.oldValue = target.value;
|
|
15
|
-
target.dataset.oldSelectionStart = target.selectionStart?.toString() ?? "";
|
|
16
|
-
target.dataset.oldSelectionEnd = target.selectionEnd?.toString() ?? "";
|
|
17
|
-
}
|
|
18
|
-
else if (target.dataset.oldValue !== undefined) {
|
|
19
|
-
// Rejected value: restore the previous one.
|
|
20
|
-
target.classList.add("input-error");
|
|
21
|
-
target.setCustomValidity(errMsg);
|
|
22
|
-
target.reportValidity();
|
|
23
|
-
target.setAttribute("aria-invalid", "true");
|
|
24
|
-
target.value = target.dataset.oldValue;
|
|
25
|
-
const start = target.dataset.oldSelectionStart ? parseInt(target.dataset.oldSelectionStart) : 0;
|
|
26
|
-
const end = target.dataset.oldSelectionEnd ? parseInt(target.dataset.oldSelectionEnd) : start;
|
|
27
|
-
target.setSelectionRange(start, end);
|
|
28
|
-
}
|
|
29
|
-
else {
|
|
30
|
-
// Rejected value: nothing to restore.
|
|
31
|
-
target.value = "";
|
|
32
|
-
}
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
});
|
|
36
|
-
}
|
package/LoadingOverlay.css
DELETED
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
.loading-overlay {
|
|
2
|
-
position: fixed;
|
|
3
|
-
top: 0;
|
|
4
|
-
left: 0;
|
|
5
|
-
width: 100vw;
|
|
6
|
-
height: 100vh;
|
|
7
|
-
background-color: rgba(255, 255, 255, 0.65);
|
|
8
|
-
z-index: 2000;
|
|
9
|
-
display: flex;
|
|
10
|
-
align-items: center;
|
|
11
|
-
justify-content: center;
|
|
12
|
-
backdrop-filter: blur(2px);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
.loading-box {
|
|
16
|
-
display: flex;
|
|
17
|
-
flex-direction: column;
|
|
18
|
-
align-items: center;
|
|
19
|
-
gap: 1rem;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/* Spinner (Standardfarbe für Dark Mode) */
|
|
23
|
-
.loading-box .spinner {
|
|
24
|
-
width: 60px;
|
|
25
|
-
height: 60px;
|
|
26
|
-
/*border: 8px solid var(--HellSchwarz, #555);*/
|
|
27
|
-
border-top: 8px solid var(--spinner-color, #e60028);
|
|
28
|
-
border-bottom: 8px solid var(--spinner-color, #e60028);
|
|
29
|
-
border-radius: 50%;
|
|
30
|
-
animation: spin 0.75s linear infinite;
|
|
31
|
-
position: relative;
|
|
32
|
-
transition:
|
|
33
|
-
border-color 0.3s ease,
|
|
34
|
-
background-color 0.3s ease,
|
|
35
|
-
transform 0.3s ease;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
.loading-box .spinner.is-success,
|
|
39
|
-
.loading-box .spinner.is-error {
|
|
40
|
-
animation: none;
|
|
41
|
-
border-color: transparent;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
.loading-box .spinner::before,
|
|
45
|
-
.loading-box .spinner::after {
|
|
46
|
-
content: "";
|
|
47
|
-
position: absolute;
|
|
48
|
-
opacity: 0;
|
|
49
|
-
transition: opacity 0.3s ease;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
.loading-box .spinner.is-success {
|
|
53
|
-
background-color: var(--success-bg, #4caf50);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
.loading-box .spinner.is-success::before {
|
|
57
|
-
width: 12px;
|
|
58
|
-
height: 24px;
|
|
59
|
-
border-right: 6px solid white;
|
|
60
|
-
border-bottom: 6px solid white;
|
|
61
|
-
transform: rotate(45deg);
|
|
62
|
-
left: 24px;
|
|
63
|
-
top: 8px;
|
|
64
|
-
opacity: 1;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
.loading-box .spinner.is-error {
|
|
68
|
-
background-color: var(--error-bg, #e60028);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
.loading-box .spinner.is-error::before,
|
|
72
|
-
.loading-box .spinner.is-error::after {
|
|
73
|
-
width: 36px;
|
|
74
|
-
height: 6px;
|
|
75
|
-
background: white;
|
|
76
|
-
top: 20px;
|
|
77
|
-
left: 12px;
|
|
78
|
-
opacity: 1;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
.loading-box .spinner.is-error::before {
|
|
82
|
-
transform: rotate(45deg);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
.loading-box .spinner.is-error::after {
|
|
86
|
-
transform: rotate(-45deg);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
.loading-box .loading-message {
|
|
90
|
-
font-size: 1.1em;
|
|
91
|
-
font-weight: bold;
|
|
92
|
-
text-align: center;
|
|
93
|
-
white-space: pre-line;
|
|
94
|
-
color: var(--FastSchwarz, #2c2c2c);
|
|
95
|
-
text-shadow:
|
|
96
|
-
-1px -1px 0 color-mix(in srgb, var(--Mittelgrau, white) 80%, var(--Dunkelgrau, black) 20%),
|
|
97
|
-
1px -1px 0 color-mix(in srgb, var(--Mittelgrau, white) 80%, var(--Dunkelgrau, black) 20%),
|
|
98
|
-
-1px 1px 0 color-mix(in srgb, var(--Mittelgrau, white) 80%, var(--Dunkelgrau, black) 20%),
|
|
99
|
-
1px 1px 0 color-mix(in srgb, var(--Mittelgrau, white) 80%, var(--Dunkelgrau, black) 20%);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
@keyframes spin {
|
|
103
|
-
0% {
|
|
104
|
-
transform: rotate(0deg);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
100% {
|
|
108
|
-
transform: rotate(360deg);
|
|
109
|
-
}
|
|
110
|
-
}
|
package/LoadingOverlay.tsx
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { useEffect, useState, ReactElement } from 'react';
|
|
2
|
-
import * as React from 'react';
|
|
3
|
-
import './LoadingOverlay.css';
|
|
4
|
-
|
|
5
|
-
// Um das LoadingOverlay zu nutzen, muss der State die folgende Struktur haben:
|
|
6
|
-
export interface LoadingOverlayState {
|
|
7
|
-
isActive?: boolean;
|
|
8
|
-
message?: string | undefined;
|
|
9
|
-
color?: string | undefined;
|
|
10
|
-
showSuccess?: boolean | undefined;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
// Der standard LoadingOverlay Status, der zur Initialisierung genutzt werden kann
|
|
14
|
-
export const defaultLoadingOverlayState: LoadingOverlayState = {
|
|
15
|
-
isActive: false,
|
|
16
|
-
message: undefined,
|
|
17
|
-
color: undefined,
|
|
18
|
-
showSuccess: undefined,
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
// Wobei alle Attribute grundsätzlich optional sind:
|
|
22
|
-
// - isActive: gibt an ob das Overlay gerade aktiv sein soll oder nicht
|
|
23
|
-
// - message: ist die angezeigte Nachricht
|
|
24
|
-
// - color: ist dir Farbe des Loading icons (ohne Angabe HSD rot)
|
|
25
|
-
// - showSuccess: zeigt bei "true" ein grünes Häkchen an und bei "false" ein rot hinterlegtes X. Bei "undefined" wird die normale Ladeanimation gezeigt.
|
|
26
|
-
|
|
27
|
-
export function LoadingOverlay({ state, setState }: { state: LoadingOverlayState, setState: (state: LoadingOverlayState) => void }): ReactElement {
|
|
28
|
-
// Wird verwendet um das LoadingOverlay ein- und auszublenden
|
|
29
|
-
const [showOverlay, setShowOverlay] = useState(false);
|
|
30
|
-
|
|
31
|
-
// Sobald der State von außen aktualisiert wird triggert diese Funktion
|
|
32
|
-
// Die setzt showOverlay auf true
|
|
33
|
-
useEffect(() => {
|
|
34
|
-
if (state?.isActive) {
|
|
35
|
-
setShowOverlay(true);
|
|
36
|
-
}
|
|
37
|
-
else {
|
|
38
|
-
setState(defaultLoadingOverlayState);
|
|
39
|
-
setShowOverlay(false);
|
|
40
|
-
}
|
|
41
|
-
}, [state, setState]);
|
|
42
|
-
|
|
43
|
-
return showOverlay ? (
|
|
44
|
-
<div className="loading-overlay">
|
|
45
|
-
<div className="loading-box">
|
|
46
|
-
<div className={"spinner " + (state.showSuccess === undefined ? "is-loading" : (state.showSuccess ? "is-success" : "is-error"))}
|
|
47
|
-
style={{
|
|
48
|
-
"--spinner-color": state.color ?? "#e60028",
|
|
49
|
-
"--success-bg": state.color ?? "#42ab34",
|
|
50
|
-
"--error-bg": state.color ?? "#e60028",
|
|
51
|
-
} as React.CSSProperties }
|
|
52
|
-
/>
|
|
53
|
-
{state.message && <p className="loading-message">{state.message}</p>}
|
|
54
|
-
</div>
|
|
55
|
-
</div>
|
|
56
|
-
) : <></>;
|
|
57
|
-
}
|