typescript-overlay-essentials 1.2.0 → 1.3.3

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 (41) hide show
  1. package/dist/ConfirmationBox.d.ts +23 -0
  2. package/dist/ConfirmationBox.js +90 -0
  3. package/dist/Dropdown.d.ts +21 -0
  4. package/dist/Dropdown.js +141 -0
  5. package/dist/InfoOverlay.d.ts +17 -0
  6. package/dist/InfoOverlay.js +57 -0
  7. package/dist/InfoOverlayWithInput.d.ts +22 -0
  8. package/dist/InfoOverlayWithInput.js +84 -0
  9. package/dist/InputFilter.d.ts +1 -0
  10. package/{InputFilter.tsx → dist/InputFilter.js} +12 -12
  11. package/dist/LoadingOverlay.d.ts +13 -0
  12. package/dist/LoadingOverlay.js +36 -0
  13. package/dist/MultipleChoiceOverlay.d.ts +22 -0
  14. package/dist/MultipleChoiceOverlay.js +94 -0
  15. package/dist/MultipleRadioOverlay.d.ts +22 -0
  16. package/dist/MultipleRadioOverlay.js +84 -0
  17. package/dist/Toast.d.ts +6 -0
  18. package/{Toast.tsx → dist/Toast.js} +7 -15
  19. package/dist/ToggleSwitch.d.ts +11 -0
  20. package/dist/ToggleSwitch.js +27 -0
  21. package/{index.ts → dist/index.d.ts} +10 -10
  22. package/dist/index.js +10 -0
  23. package/package.json +19 -3
  24. package/.github/workflows/npm-publish.yml +0 -32
  25. package/ConfirmationBox.css +0 -69
  26. package/ConfirmationBox.tsx +0 -148
  27. package/Dropdown.css +0 -31
  28. package/Dropdown.tsx +0 -211
  29. package/InfoOverlay.css +0 -65
  30. package/InfoOverlay.tsx +0 -101
  31. package/InfoOverlayWithInput.css +0 -88
  32. package/InfoOverlayWithInput.tsx +0 -157
  33. package/LoadingOverlay.css +0 -110
  34. package/LoadingOverlay.tsx +0 -57
  35. package/MultipleChoiceOverlay.css +0 -170
  36. package/MultipleChoiceOverlay.tsx +0 -170
  37. package/MultipleRadioOverlay.css +0 -170
  38. package/MultipleRadioOverlay.tsx +0 -158
  39. package/Toast.css +0 -13
  40. package/ToggleSwitch.tsx +0 -56
  41. package/Toggleswitch.css +0 -49
package/Dropdown.css DELETED
@@ -1,31 +0,0 @@
1
- .custom-select {
2
- border-radius: 8px;
3
- border: 1px solid var(--Mittelgrau);
4
- color: black;
5
- background-color: white;
6
- -moz-box-align: center;
7
- align-items: center;
8
- cursor: default;
9
- display: flex;
10
- flex-wrap: wrap;
11
- -moz-box-pack: justify;
12
- justify-content: space-between;
13
- margin-bottom: 0.5rem;
14
- font-size: 0.8em;
15
- }
16
-
17
- .dropdown__control {
18
- width: 100%;
19
- }
20
-
21
- .custom-select:hover, .dropdown__menu-list div:hover, .card .dropdown__menu-list div:hover {
22
- cursor: pointer;
23
- }
24
-
25
- .dropdown__multi-value__remove {
26
- color: var(--FastSchwarz, black);
27
- }
28
-
29
- .custom-select .dropdown__single-value {
30
- color: var(--FastSchwarz, black);
31
- }
package/Dropdown.tsx DELETED
@@ -1,211 +0,0 @@
1
- import { useRef, useEffect, useState } from 'react';
2
- import * as React from 'react';
3
- import Select, { SingleValue, MultiValue} from 'react-select'
4
- import type { CSSObject } from '@emotion/react';
5
- import './Dropdown.css';
6
-
7
- // Um das Dropdown Menü zu nutzen, müssen die Optionen das folgende Interface nutzen:
8
- export interface DropdownOption<T = string> {
9
- value: T;
10
- label: string;
11
- disabled?: boolean;
12
- indent?: number;
13
- }
14
-
15
- // Wobei alle Attribute außer dem value und dem label optional sind:
16
- // - value: Der Wert, der bei Auswahl gesetzt werden soll
17
- // -> Dabei kann es sich um komplexe Objekte handeln, diese müssen dann ein Attribut id haben, mithilfe derer sie verglichen werden!
18
- // - label: Der String, der in der Auswahl angezeigt werden soll
19
- // - disabled: Kann auf true gesetzt werden, wenn diese Option nicht zur Auswahl verfügbar sein soll
20
- // - indent: Eine Zahl größer 0, kann angegeben werden, wenn die Option mit so vielen Strichen eingerückt werden soll (nur im Auswahlmenü)
21
-
22
- // Attribute vom Dropdown:
23
- // - selections: Das Array mit den Optionen die zur Auswahl stehen sollen
24
- // - value: Der Wert, der ausgewählt angezeigt werden soll (useState)
25
- // - onChange: Die Funktion die bei Änderung ausgeführt werden soll.
26
- // Standardmäßig könnte z.B. {(value) => {value ? setValue(value) : setValue("")}} genutzt werden, wenn es sich bei value um einen String handelt
27
- // - maxMenuHeight: Maximale Menühöhe in Pixeln (Optional)
28
- // - menuPlacement: Ob das Menü oben oder unten platziert sein soll (Optional, Standard: Auto)
29
- // - width: Breite des Menüs als CSS Property (Optional)
30
- // - placeHolder: Der Platzhalter der angezeigt wird, wenn noch nichts ausgewählt ist (Optional) (Wird auch nur angezeigt, wenn value noch keinen validen Wert hat)
31
- // - defaultValue: Die Auswahl die von Anfang an ausgewählt ist (Optional) (sollten mehrere Werte übergeben werden, wird "isMulti" automatisch gesetzt)
32
- // - isMulti: Ob mehrere Optionen gleichzeitig ausgewählt werden können (Optional)
33
- // - Daraufhin MUSS value ein Array sein und falls defaultValue angegeben ist, MUSS das auch ein Array sein
34
- // - cardColorVariant: Wenn auf true gesetzt, wird eine leicht andere Farbvariante gewählt
35
- // - ignoreDarkMode: Wenn auf true gesetzt wird der DarkMode ignoriert, der sonst standardmäßig angewendet wird
36
- // - ...rest (Optional): sorgt dafür, dass alle weiteren angegebenen Attribute (z.B. aria-label) direkt an React-Select weitergegeben werden
37
-
38
- export function Dropdown<T = string>({ selections, value, onChange, maxMenuHeight, menuPlacement, width, placeHolder, defaultValue, isMulti = false, cardColorVariant = false, ignoreDarkMode = false, ...rest} :
39
- {
40
- selections: DropdownOption<T>[],
41
- value: T | T[],
42
- onChange: (value: T | T[] | undefined) => void,
43
- maxMenuHeight?: number,
44
- menuPlacement?: "top" | "bottom",
45
- width?: React.CSSProperties["width"],
46
- placeHolder?: string,
47
- defaultValue?: DropdownOption<T> | DropdownOption<T>[],
48
- isMulti?: boolean,
49
- cardColorVariant?: boolean,
50
- ignoreDarkMode?: boolean,
51
- }): React.ReactElement {
52
-
53
- const initializedRef = useRef(false);
54
-
55
- const options = selections.map(({value, label, disabled, indent}) => ({value: value, label: label, isDisabled: disabled ?? false, indent}));
56
-
57
- if(!initializedRef.current && Array.isArray(value) && value.length === 0 && Array.isArray(defaultValue) && defaultValue.length > 0) {
58
- value = options.filter(opt => (defaultValue as DropdownOption<T>[]).map(({value}) => (value)).includes(opt.value)).map(opt => opt.value);
59
- }
60
-
61
- function usePrefersDarkMode() : boolean {
62
- const [isDarkMode, setIsDarkMode] = useState(
63
- window.matchMedia('(prefers-color-scheme: dark)').matches
64
- );
65
-
66
- useEffect(() => {
67
- const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
68
- const handler = (event: MediaQueryListEvent) : void => setIsDarkMode(event.matches);
69
-
70
- // EventListener hinzufügen
71
- mediaQuery.addEventListener('change', handler);
72
-
73
- // Aufräumen
74
- return () => mediaQuery.removeEventListener('change', handler);
75
- }, []);
76
-
77
- return isDarkMode;
78
- }
79
- const isDarkModeRaw = usePrefersDarkMode()
80
- const isDarkMode = ignoreDarkMode ? false : isDarkModeRaw;
81
-
82
- function hasId(value: unknown): value is { id: any } {
83
- return typeof value === "object" && value !== null && "id" in value;
84
- }
85
-
86
- return isMulti || Array.isArray(defaultValue) ? (
87
- <Select<DropdownOption<T>, true>
88
- className = "custom-select"
89
- isMulti={true}
90
- placeholder = {placeHolder ?? ""}
91
- options = {options}
92
- value={options.length > 0 && hasId(options[0].value) ? // Wenn die values der Optionen ids haben, handelt es sich um komplexe Objekte, die zur Auswahl stehen und müssen dahingehend verglichen werden
93
- options.filter(opt => (hasId(opt.value) && (value as T[]).find(val => hasId(val) && hasId(opt.value) && val.id === opt.value.id))) // Dann alle Optionen, die in den values per id vorkommen
94
- : options.filter(opt => ((value as T[]).includes(opt.value))) // Ansonsten einfach alle Optionen, die in den values vorkommen
95
- }
96
- onChange={(selectedOption: MultiValue<DropdownOption<T>>) => {initializedRef.current = true; onChange(selectedOption.map((opt: DropdownOption<T>) => opt.value))}}
97
- maxMenuHeight={maxMenuHeight ?? 280}
98
- menuPlacement={menuPlacement ?? "auto"}
99
- menuPosition="fixed"
100
- classNamePrefix="dropdown"
101
- defaultValue={options.filter(opt => (defaultValue as DropdownOption<T>[]).map(({value}) => (value)).includes(opt.value))}
102
- formatOptionLabel={({ label, indent }: { label: string; indent?: number },{ context } : { context: string }) => {
103
- if (context === 'menu') {
104
- // Alle mit indent angegebenen Werte im Dropdown-Menü einrücken
105
- return indent && indent > 0 ? '-'.repeat(indent) + '\u00A0' + label : label;
106
- } else {
107
- // Im ausgewählten Zustand: normal
108
- return label;
109
- }
110
- }}
111
- styles={{
112
- container: (provided: CSSObject) => ({
113
- ...provided,
114
- backgroundColor: isDarkMode? (cardColorVariant ? 'var(--FastSchwarz, black)' : 'black') : cardColorVariant ? 'var(--FastWeiß, white)' : 'white',
115
- color: isDarkMode? 'white' : 'black',
116
- ...(width ? { width } : {})
117
- }),
118
- control: () => ({
119
- display: 'flex',
120
- ...(width ? { width } : {})
121
- }),
122
- menu: (provided: CSSObject) => ({
123
- ...provided,
124
- borderRadius: '8px', // runde Ecken für das Dropdown
125
- overflow: 'hidden',
126
- backgroundColor: isDarkMode? (cardColorVariant ? 'var(--FastSchwarz, black)' : 'black') : cardColorVariant ? 'var(--FastWeiß, white)' : 'white',
127
- color: isDarkMode? 'white' : 'black',
128
- ...(width ? { width } : {})
129
- }),
130
- menuList: (provided: CSSObject) => ({
131
- ...provided,
132
- overflowY: "auto",
133
- padding: 0,
134
- backgroundColor: isDarkMode? (cardColorVariant ? 'var(--FastSchwarz, black)' : 'black') : cardColorVariant ? 'var(--FastWeiß, white)' : 'white',
135
- color: isDarkMode? 'white' : 'black',
136
- }),
137
- option: (provided: CSSObject, state: { isFocused: boolean; isSelected: boolean }) => ({
138
- ...provided,
139
- backgroundColor: state.isFocused ? 'var(--DunkelAkzent, red)' : state.isSelected ? 'var(--MittelAkzent, blue)' :
140
- isDarkMode? (cardColorVariant ? 'var(--FastSchwarz, black)' : 'black') : cardColorVariant ? 'var(--FastWeiß, white)' : 'white',
141
- color: state.isFocused ? 'white' : state.isSelected ? 'var(--FastSchwarz, black)' :
142
- isDarkMode? (cardColorVariant ? 'white' : 'white') : cardColorVariant ? 'black' : 'var(--FastSchwarz, black)',
143
- cursor: 'pointer',
144
- }),
145
- }}
146
- {...rest}
147
- />
148
- ) : (
149
- <Select<DropdownOption<T>, false>
150
- className = "custom-select"
151
- isMulti={false}
152
- placeholder = {placeHolder ?? ""}
153
- options = {options}
154
- value={options.length > 0 && hasId(options[0].value) ? // Wenn die values der Optionen ids haben, handelt es sich um komplexe Objekte, die zur Auswahl stehen und müssen dahingehend verglichen werden
155
- options.find(opt => hasId(opt.value) && hasId(value) && opt.value.id === value.id) // Die eine Option mit der passenden id finden
156
- : options.find(opt => opt.value === value)} // Ohne Multi oder id nur die eine Option finden
157
-
158
- onChange={(selectedOption: SingleValue<DropdownOption<T>>) => onChange(selectedOption?.value ?? undefined)}
159
- maxMenuHeight={maxMenuHeight ?? 280}
160
- menuPlacement={menuPlacement ?? "auto"}
161
- menuPosition="fixed"
162
- classNamePrefix="dropdown"
163
- defaultValue={defaultValue}
164
- formatOptionLabel={({ label, indent }: { label: string; indent?: number },{ context } : { context: string }) => {
165
- if (context === 'menu') {
166
- // Alle mit indent angegebenen Werte im Dropdown-Menü einrücken
167
- return indent && indent > 0 ? '-'.repeat(indent) + '\u00A0' + label : label;
168
- } else {
169
- // Im ausgewählten Zustand: normal
170
- return label;
171
- }
172
- }}
173
- styles={{
174
- container: (provided: CSSObject) => ({
175
- ...provided,
176
- backgroundColor: isDarkMode? (cardColorVariant ? 'var(--FastSchwarz, black)' : 'black') : cardColorVariant ? 'var(--FastWeiß, white)' : 'white',
177
- color: isDarkMode? 'white' : 'black',
178
- ...(width ? { width } : {})
179
- }),
180
- control: () => ({
181
- display: 'flex',
182
- ...(width ? { width } : {})
183
- }),
184
- menu: (provided: CSSObject) => ({
185
- ...provided,
186
- borderRadius: '8px', // runde Ecken für das Dropdown
187
- overflow: 'hidden',
188
- backgroundColor: isDarkMode? (cardColorVariant ? 'var(--FastSchwarz, black)' : 'black') : cardColorVariant ? 'var(--FastWeiß, white)' : 'white',
189
- color: isDarkMode? 'white' : 'black',
190
- ...(width ? { width } : {})
191
- }),
192
- menuList: (provided: CSSObject) => ({
193
- ...provided,
194
- overflowY: "auto",
195
- padding: 0,
196
- backgroundColor: isDarkMode? (cardColorVariant ? 'var(--FastSchwarz, black)' : 'black') : cardColorVariant ? 'var(--FastWeiß, white)' : 'white',
197
- color: isDarkMode? 'white' : 'black',
198
- }),
199
- option: (provided: CSSObject, state: { isFocused: boolean; isSelected: boolean }) => ({
200
- ...provided,
201
- backgroundColor: state.isFocused ? 'var(--DunkelAkzent, red)' : state.isSelected ? 'var(--MittelAkzent, blue)' :
202
- isDarkMode? (cardColorVariant ? 'var(--FastSchwarz, black)' : 'black') : cardColorVariant ? 'var(--FastWeiß, white)' : 'white',
203
- color: state.isFocused ? 'white' : state.isSelected ? 'var(--FastSchwarz, black)' :
204
- isDarkMode? (cardColorVariant ? 'white' : 'white') : cardColorVariant ? 'black' : 'var(--FastSchwarz, black)',
205
- cursor: 'pointer',
206
- }),
207
- }}
208
- {...rest}
209
- />
210
- );
211
- }
package/InfoOverlay.css DELETED
@@ -1,65 +0,0 @@
1
- .information-overlay {
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 {
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
- .information-box .headline {
29
- margin-bottom: 0.75em;
30
- }
31
-
32
- @keyframes fadeIn {
33
- from {
34
- opacity: 0;
35
- transform: scale(0.95);
36
- }
37
-
38
- to {
39
- opacity: 1;
40
- transform: scale(1);
41
- }
42
- }
43
-
44
- .information-box .information-buttons {
45
- display: flex;
46
- justify-content: center;
47
- gap: 1rem;
48
- margin-top: 1.5rem;
49
- }
50
-
51
- .information-box .information-buttons button {
52
- font-size: 85%;
53
- padding: 0.5rem 1.1rem;
54
- }
55
-
56
- .information-box .closeButton {
57
- right: 10px;
58
- top: 10px;
59
- position: absolute;
60
- }
61
-
62
- .information-box .close-icon:hover {
63
- color: var(--HSDAkzent);
64
- cursor: pointer;
65
- }
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
- }
@@ -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
- }
@@ -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
- }