@sortsys/ui 0.1.0 → 0.1.2

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/dist/index.jsx CHANGED
@@ -1,1601 +1,326 @@
1
- // src/components/SSButton.tsx
2
- function SSButton(props) {
3
- const classes = () => [
4
- "ss_button",
5
- props.isIconOnly ? "ss_button--icon" : "",
6
- props.class ?? ""
7
- ].filter(Boolean).join(" ");
8
- return <button
9
- type={props.type ?? "button"}
10
- class={classes()}
11
- disabled={props.disabled}
12
- aria-label={props.ariaLabel}
13
- form={props.form}
14
- onclick={props.onclick}
15
- >
16
- {props.children}
17
- </button>;
18
- }
19
-
20
- // src/components/SSCallout.tsx
21
- function SSCallout(props) {
22
- const { icon, color, class: className, style, children: children2, ...rest } = props;
23
- return <div
24
- {...rest}
25
- class={`ss_callout ss_callout--${color} ${className ?? ""}`}
26
- style={style}
27
- >
1
+ function F(e){let t=()=>["ss_button",e.isIconOnly?"ss_button--icon":"",e.class??""].filter(Boolean).join(" ");return<button type={e.type??"button"}class={t()}disabled={e.disabled}aria-label={e.ariaLabel}form={e.form}onclick={e.onclick}>
2
+ {e.children}
3
+ </button>}function we(e){let{icon:t,color:l,class:i,style:r,children:o,...h}=e;return<div{...h}class={`ss_callout ss_callout--${l} ${i??""}`}style={r}>
28
4
  <span class="ss_callout__icon">
29
- {icon}
5
+ {t}
30
6
  </span>
31
7
  <div class="ss_callout__content">
32
- <span>{children2}</span>
8
+ <span>{o}</span>
33
9
  </div>
34
- </div>;
35
- }
36
-
37
- // src/components/SSChip.tsx
38
- function SSChip(props) {
39
- const commonClass = `ss_chip ss_chip--${props.color ?? "blue"} ${props.class ?? ""}`;
40
- if ("onclick" in props && props.onclick) {
41
- return <button
42
- type="button"
43
- class={`${commonClass} ss_chip--clickable`}
44
- style={props.style}
45
- onclick={props.onclick}
46
- >
47
- <span class="ss_chip__label">{props.children}</span>
48
- </button>;
49
- }
50
- return <div class={commonClass} style={props.style}>
51
- <span class="ss_chip__label">{props.children}</span>
52
- {"ondismiss" in props && props.ondismiss && <button
53
- type="button"
54
- class="ss_chip__dismiss"
55
- aria-label="Entfernen"
56
- onclick={props.ondismiss}
57
- >
58
- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-x"><path stroke="none" d="M0 0h24v24H0z" fill="none" /><path d="M18 6l-12 12" /><path d="M6 6l12 12" /></svg>
10
+ </div>}function pe(e){let t=`ss_chip ss_chip--${e.color??"blue"} ${e.class??""}`;return"onclick"in e&&e.onclick?<button type="button"class={`${t} ss_chip--clickable`}style={e.style}onclick={e.onclick}>
11
+ <span class="ss_chip__label">{e.children}</span>
12
+ </button>:<div class={t}style={e.style}>
13
+ <span class="ss_chip__label">{e.children}</span>
14
+ {"ondismiss"in e&&e.ondismiss&&<button type="button"class="ss_chip__dismiss"aria-label="Entfernen"onclick={e.ondismiss}>
15
+ <svg xmlns="http://www.w3.org/2000/svg"width="24"height="24"viewBox="0 0 24 24"fill="none"stroke="currentColor"stroke-width="2"stroke-linecap="round"stroke-linejoin="round"class="icon icon-tabler icons-tabler-outline icon-tabler-x"><path stroke="none"d="M0 0h24v24H0z"fill="none"/><path d="M18 6l-12 12"/><path d="M6 6l12 12"/></svg>
59
16
  </button>}
60
- </div>;
61
- }
62
-
63
- // src/components/SSDataTable.tsx
64
- import { createEffect, createMemo, createResource, createSignal, For } from "solid-js";
65
- function SSDataCell(props) {
66
- const rendered = createMemo(() => props.render(props.row));
67
- const [asyncContent] = createResource(async () => {
68
- const value = rendered();
69
- if (value && typeof value.then === "function") {
70
- return await value;
71
- }
72
- return null;
73
- });
74
- return <div>
75
- {(() => {
76
- const value = rendered();
77
- if (value && typeof value.then === "function") {
78
- return asyncContent() ?? "";
79
- }
80
- return value;
81
- })()}
82
- </div>;
83
- }
84
- function SSDataTable(props) {
85
- const [sortIndex, setSortIndex] = createSignal(-1);
86
- const [sortDir, setSortDir] = createSignal(null);
87
- const [page, setPage] = createSignal(1);
88
- const pageSize = () => Math.max(1, props.pageSize ?? 25);
89
- const sortedRows = createMemo(() => {
90
- const index = sortIndex();
91
- const dir = sortDir();
92
- if (index < 0 || !dir) return props.rows;
93
- const column = props.columns[index];
94
- if (!column?.sortKey) return props.rows;
95
- const entries = props.rows.map((row, idx) => ({
96
- row,
97
- idx,
98
- key: column.sortKey(row)
99
- }));
100
- entries.sort((a, b) => {
101
- if (a.key === b.key) return a.idx - b.idx;
102
- if (a.key < b.key) return dir === "asc" ? -1 : 1;
103
- return dir === "asc" ? 1 : -1;
104
- });
105
- return entries.map((entry) => entry.row);
106
- });
107
- const totalPages = createMemo(() => {
108
- return Math.max(1, Math.ceil(sortedRows().length / pageSize()));
109
- });
110
- const pagedRows = createMemo(() => {
111
- const current = Math.min(page(), totalPages());
112
- const start = (current - 1) * pageSize();
113
- return sortedRows().slice(start, start + pageSize());
114
- });
115
- createEffect(() => {
116
- if (page() > totalPages()) setPage(totalPages());
117
- });
118
- const toggleSort = (index) => {
119
- if (sortIndex() !== index) {
120
- setSortIndex(index);
121
- setSortDir("asc");
122
- setPage(1);
123
- return;
124
- }
125
- if (sortDir() === "asc") {
126
- setSortDir("desc");
127
- return;
128
- }
129
- if (sortDir() === "desc") {
130
- setSortIndex(-1);
131
- setSortDir(null);
132
- setPage(1);
133
- return;
134
- }
135
- setSortDir("asc");
136
- setPage(1);
137
- };
138
- const goToPage = (next) => {
139
- const safePage = Math.min(Math.max(1, next), totalPages());
140
- setPage(safePage);
141
- };
142
- const paginationPosition = () => props.paginationPosition ?? "bottom";
143
- const containerClass = () => `ss_table ${paginationPosition() === "top" ? "ss_table--pagination-top" : ""} ${props.class ?? ""}`;
144
- const paginationBar = () => <div class="ss_table__pagination">
145
- <button
146
- type="button"
147
- class="ss_table__page_button"
148
- disabled={page() === 1}
149
- aria-label="Erste Seite"
150
- onclick={() => goToPage(1)}
151
- >
152
- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-chevron-left-pipe"><path stroke="none" d="M0 0h24v24H0z" fill="none" /><path d="M7 6v12" /><path d="M18 6l-6 6l6 6" /></svg>
17
+ </div>}import{createEffect as ye,createMemo as V,createResource as ke,createSignal as j,For as N}from"solid-js";function Me(e){let t=V(()=>e.render(e.row)),[l]=ke(async()=>{let i=t();return i&&typeof i.then=="function"?await i:null});return<div>
18
+ {(()=>{let i=t();return i&&typeof i.then=="function"?l()??"":i})()}
19
+ </div>}function Ee(e){let[t,l]=j(-1),[i,r]=j(null),[o,h]=j(1),c=()=>Math.max(1,e.pageSize??25),f=V(()=>{let k=t(),E=i();if(k<0||!E)return e.rows;let D=e.columns[k];if(!D?.sortKey)return e.rows;let u=e.rows.map((b,n)=>({row:b,idx:n,key:D.sortKey(b)}));return u.sort((b,n)=>b.key===n.key?b.idx-n.idx:b.key<n.key?E==="asc"?-1:1:E==="asc"?1:-1),u.map(b=>b.row)}),a=V(()=>Math.max(1,Math.ceil(f().length/c()))),d=V(()=>{let E=(Math.min(o(),a())-1)*c();return f().slice(E,E+c())});ye(()=>{o()>a()&&h(a())});let s=k=>{if(t()!==k){l(k),r("asc"),h(1);return}if(i()==="asc"){r("desc");return}if(i()==="desc"){l(-1),r(null),h(1);return}r("asc"),h(1)},v=k=>{let E=Math.min(Math.max(1,k),a());h(E)},_=()=>e.paginationPosition??"bottom",y=()=>`ss_table ${_()==="top"?"ss_table--pagination-top":""} ${e.class??""}`,M=()=><div class="ss_table__pagination">
20
+ <button type="button"class="ss_table__page_button"disabled={o()===1}aria-label="Erste Seite"onclick={()=>v(1)}>
21
+ <svg xmlns="http://www.w3.org/2000/svg"width="24"height="24"viewBox="0 0 24 24"fill="none"stroke="currentColor"stroke-width="2"stroke-linecap="round"stroke-linejoin="round"class="icon icon-tabler icons-tabler-outline icon-tabler-chevron-left-pipe"><path stroke="none"d="M0 0h24v24H0z"fill="none"/><path d="M7 6v12"/><path d="M18 6l-6 6l6 6"/></svg>
153
22
  </button>
154
- <button
155
- type="button"
156
- class="ss_table__page_button"
157
- disabled={page() === 1}
158
- aria-label="Vorherige Seite"
159
- onclick={() => goToPage(page() - 1)}
160
- >
161
- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-chevron-left"><path stroke="none" d="M0 0h24v24H0z" fill="none" /><path d="M15 6l-6 6l6 6" /></svg>
23
+ <button type="button"class="ss_table__page_button"disabled={o()===1}aria-label="Vorherige Seite"onclick={()=>v(o()-1)}>
24
+ <svg xmlns="http://www.w3.org/2000/svg"width="24"height="24"viewBox="0 0 24 24"fill="none"stroke="currentColor"stroke-width="2"stroke-linecap="round"stroke-linejoin="round"class="icon icon-tabler icons-tabler-outline icon-tabler-chevron-left"><path stroke="none"d="M0 0h24v24H0z"fill="none"/><path d="M15 6l-6 6l6 6"/></svg>
162
25
  </button>
163
26
  <span class="ss_table__page_info">
164
- Seite {page()} von {totalPages()}
27
+ Seite {o()} von {a()}
165
28
  </span>
166
- <button
167
- type="button"
168
- class="ss_table__page_button"
169
- disabled={page() === totalPages()}
170
- aria-label="Nächste Seite"
171
- onclick={() => goToPage(page() + 1)}
172
- >
173
- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-chevron-right"><path stroke="none" d="M0 0h24v24H0z" fill="none" /><path d="M9 6l6 6l-6 6" /></svg>
29
+ <button type="button"class="ss_table__page_button"disabled={o()===a()}aria-label="Nächste Seite"onclick={()=>v(o()+1)}>
30
+ <svg xmlns="http://www.w3.org/2000/svg"width="24"height="24"viewBox="0 0 24 24"fill="none"stroke="currentColor"stroke-width="2"stroke-linecap="round"stroke-linejoin="round"class="icon icon-tabler icons-tabler-outline icon-tabler-chevron-right"><path stroke="none"d="M0 0h24v24H0z"fill="none"/><path d="M9 6l6 6l-6 6"/></svg>
174
31
  </button>
175
- <button
176
- type="button"
177
- class="ss_table__page_button"
178
- disabled={page() === totalPages()}
179
- aria-label="Letzte Seite"
180
- onclick={() => goToPage(totalPages())}
181
- >
182
- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-chevron-right-pipe"><path stroke="none" d="M0 0h24v24H0z" fill="none" /><path d="M6 6l6 6l-6 6" /><path d="M17 5v13" /></svg>
32
+ <button type="button"class="ss_table__page_button"disabled={o()===a()}aria-label="Letzte Seite"onclick={()=>v(a())}>
33
+ <svg xmlns="http://www.w3.org/2000/svg"width="24"height="24"viewBox="0 0 24 24"fill="none"stroke="currentColor"stroke-width="2"stroke-linecap="round"stroke-linejoin="round"class="icon icon-tabler icons-tabler-outline icon-tabler-chevron-right-pipe"><path stroke="none"d="M0 0h24v24H0z"fill="none"/><path d="M6 6l6 6l-6 6"/><path d="M17 5v13"/></svg>
183
34
  </button>
184
- </div>;
185
- return <div class={containerClass()} style={props.style}>
186
- {paginationPosition() === "top" && paginationBar()}
35
+ </div>;return<div class={y()}style={e.style}>
36
+ {_()==="top"&&M()}
187
37
  <div class="ss_table__scroll">
188
38
  <table>
189
39
  <thead>
190
40
  <tr>
191
- <For each={props.columns}>
192
- {(column, index) => {
193
- const sortable = !!column.sortKey;
194
- const isActive = () => sortIndex() === index();
195
- const currentDir = () => isActive() ? sortDir() : null;
196
- return <th>
197
- {sortable ? <button
198
- type="button"
199
- class="ss_table__sort_button"
200
- aria-sort={currentDir() === "asc" ? "ascending" : currentDir() === "desc" ? "descending" : "none"}
201
- data-sort={currentDir() ?? "none"}
202
- onclick={() => toggleSort(index())}
203
- >
204
- {column.label}
205
- <span class="ss_table__sort_icon" aria-hidden="true">
206
- {currentDir() === "asc" ? <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-chevron-down"><path stroke="none" d="M0 0h24v24H0z" fill="none" /><path d="M6 9l6 6l6 -6" /></svg> : currentDir() === "desc" ? <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-chevron-up"><path stroke="none" d="M0 0h24v24H0z" fill="none" /><path d="M6 15l6 -6l6 6" /></svg> : <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-selector"><path stroke="none" d="M0 0h24v24H0z" fill="none" /><path d="M8 9l4 -4l4 4" /><path d="M16 15l-4 4l-4 -4" /></svg>}
41
+ <N each={e.columns}>
42
+ {(k,E)=>{let D=!!k.sortKey,u=()=>t()===E(),b=()=>u()?i():null;return<th>
43
+ {D?<button type="button"class="ss_table__sort_button"aria-sort={b()==="asc"?"ascending":b()==="desc"?"descending":"none"}data-sort={b()??"none"}onclick={()=>s(E())}>
44
+ {k.label}
45
+ <span class="ss_table__sort_icon"aria-hidden="true">
46
+ {b()==="asc"?<svg xmlns="http://www.w3.org/2000/svg"width="24"height="24"viewBox="0 0 24 24"fill="none"stroke="currentColor"stroke-width="2"stroke-linecap="round"stroke-linejoin="round"class="icon icon-tabler icons-tabler-outline icon-tabler-chevron-down"><path stroke="none"d="M0 0h24v24H0z"fill="none"/><path d="M6 9l6 6l6 -6"/></svg>:b()==="desc"?<svg xmlns="http://www.w3.org/2000/svg"width="24"height="24"viewBox="0 0 24 24"fill="none"stroke="currentColor"stroke-width="2"stroke-linecap="round"stroke-linejoin="round"class="icon icon-tabler icons-tabler-outline icon-tabler-chevron-up"><path stroke="none"d="M0 0h24v24H0z"fill="none"/><path d="M6 15l6 -6l6 6"/></svg>:<svg xmlns="http://www.w3.org/2000/svg"width="24"height="24"viewBox="0 0 24 24"fill="none"stroke="currentColor"stroke-width="2"stroke-linecap="round"stroke-linejoin="round"class="icon icon-tabler icons-tabler-outline icon-tabler-selector"><path stroke="none"d="M0 0h24v24H0z"fill="none"/><path d="M8 9l4 -4l4 4"/><path d="M16 15l-4 4l-4 -4"/></svg>}
207
47
  </span>
208
- </button> : <span class="ss_table__header_label">{column.label}</span>}
209
- </th>;
210
- }}
211
- </For>
48
+ </button>:<span class="ss_table__header_label">{k.label}</span>}
49
+ </th>}}
50
+ </N>
212
51
  </tr>
213
52
  </thead>
214
53
  <tbody>
215
- <For each={pagedRows()}>
216
- {(row) => <tr
217
- data-clickable={props.onRowClick ? "true" : void 0}
218
- onclick={() => props.onRowClick?.(row)}
219
- >
220
- <For each={props.columns}>
221
- {(column) => <td>
222
- <SSDataCell row={row} render={column.render} />
54
+ <N each={d()}>
55
+ {k=><tr data-clickable={e.onRowClick?"true":void 0}onclick={()=>e.onRowClick?.(k)}>
56
+ <N each={e.columns}>
57
+ {E=><td>
58
+ <Me row={k}render={E.render}/>
223
59
  </td>}
224
- </For>
60
+ </N>
225
61
  </tr>}
226
- </For>
62
+ </N>
227
63
  </tbody>
228
64
  </table>
229
65
  </div>
230
- {paginationPosition() === "bottom" && paginationBar()}
231
- </div>;
232
- }
233
-
234
- // src/components/SSDropdown.tsx
235
- import { createSignal as createSignal2, For as For2, onCleanup, onMount, Show, createEffect as createEffect2 } from "solid-js";
236
- function SSDropdown(props) {
237
- const [open, setOpen] = createSignal2(false);
238
- const [renderMenu, setRenderMenu] = createSignal2(false);
239
- const [menuState, setMenuState] = createSignal2("closed");
240
- let rootRef;
241
- const close = () => setOpen(false);
242
- createEffect2(() => {
243
- if (open()) {
244
- setRenderMenu(true);
245
- requestAnimationFrame(() => setMenuState("open"));
246
- return;
247
- }
248
- if (!renderMenu()) return;
249
- setMenuState("closed");
250
- const timeout = window.setTimeout(() => setRenderMenu(false), 160);
251
- onCleanup(() => window.clearTimeout(timeout));
252
- });
253
- onMount(() => {
254
- const handlePointerDown = (event) => {
255
- const target = event.target;
256
- if (!rootRef || !target) return;
257
- if (!rootRef.contains(target)) close();
258
- };
259
- const handleKeyDown = (event) => {
260
- if (event.key === "Escape") close();
261
- };
262
- document.addEventListener("mousedown", handlePointerDown);
263
- window.addEventListener("keydown", handleKeyDown);
264
- onCleanup(() => {
265
- document.removeEventListener("mousedown", handlePointerDown);
266
- window.removeEventListener("keydown", handleKeyDown);
267
- });
268
- });
269
- return <div class={`ss_dropdown ${props.class ?? ""}`} style={props.style} ref={(el) => rootRef = el}>
270
- <button
271
- type="button"
272
- class="ss_dropdown__trigger ss_button ss_button--icon"
273
- aria-haspopup="menu"
274
- aria-expanded={open()}
275
- aria-label={props.ariaLabel ?? "Aktionen \xF6ffnen"}
276
- onclick={() => setOpen((value) => !value)}
277
- >
278
- {props.icon ?? <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-dots-vertical"><path stroke="none" d="M0 0h24v24H0z" fill="none" /><path d="M11 12a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" /><path d="M11 19a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" /><path d="M11 5a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" /></svg>}
66
+ {_()==="bottom"&&M()}
67
+ </div>}import{createSignal as K,For as Te,onCleanup as Q,onMount as Pe,Show as Ce,createEffect as De}from"solid-js";function Fe(e){let[t,l]=K(!1),[i,r]=K(!1),[o,h]=K("closed"),c,f=()=>l(!1);return De(()=>{if(t()){r(!0),requestAnimationFrame(()=>h("open"));return}if(!i())return;h("closed");let a=window.setTimeout(()=>r(!1),160);Q(()=>window.clearTimeout(a))}),Pe(()=>{let a=s=>{let v=s.target;!c||!v||c.contains(v)||f()},d=s=>{s.key==="Escape"&&f()};document.addEventListener("mousedown",a),window.addEventListener("keydown",d),Q(()=>{document.removeEventListener("mousedown",a),window.removeEventListener("keydown",d)})}),<div class={`ss_dropdown ${e.class??""}`}style={e.style}ref={a=>c=a}>
68
+ <button type="button"class="ss_dropdown__trigger ss_button ss_button--icon"aria-haspopup="menu"aria-expanded={t()}aria-label={e.ariaLabel??"Aktionen \xF6ffnen"}onclick={()=>l(a=>!a)}>
69
+ {e.icon??<svg xmlns="http://www.w3.org/2000/svg"width="24"height="24"viewBox="0 0 24 24"fill="none"stroke="currentColor"stroke-width="2"stroke-linecap="round"stroke-linejoin="round"class="icon icon-tabler icons-tabler-outline icon-tabler-dots-vertical"><path stroke="none"d="M0 0h24v24H0z"fill="none"/><path d="M11 12a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"/><path d="M11 19a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"/><path d="M11 5a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"/></svg>}
279
70
  </button>
280
71
 
281
- <Show when={renderMenu()}>
282
- <div
283
- class="ss_dropdown__menu"
284
- role="menu"
285
- data-state={menuState()}
286
- >
287
- <For2 each={props.items}>
288
- {(item) => <button
289
- type="button"
290
- class="ss_dropdown__item"
291
- role={item.checked ? "menuitemcheckbox" : "menuitem"}
292
- aria-checked={item.checked ? "true" : void 0}
293
- onclick={async () => {
294
- close();
295
- await item.onclick?.();
296
- }}
297
- >
298
- <span class="ss_dropdown__item_icon">{item.icon}</span>
299
- <span class="ss_dropdown__item_label">{item.label}</span>
300
- {item.checked && <span class="ss_dropdown__item_check" aria-hidden="true">
301
- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-check"><path stroke="none" d="M0 0h24v24H0z" fill="none" /><path d="M5 12l5 5l10 -10" /></svg>
72
+ <Ce when={i()}>
73
+ <div class="ss_dropdown__menu"role="menu"data-state={o()}>
74
+ <Te each={e.items}>
75
+ {a=><button type="button"class="ss_dropdown__item"role={a.checked?"menuitemcheckbox":"menuitem"}aria-checked={a.checked?"true":void 0}onclick={async()=>{f(),await a.onclick?.()}}>
76
+ <span class="ss_dropdown__item_icon">{a.icon}</span>
77
+ <span class="ss_dropdown__item_label">{a.label}</span>
78
+ {a.checked&&<span class="ss_dropdown__item_check"aria-hidden="true">
79
+ <svg xmlns="http://www.w3.org/2000/svg"width="24"height="24"viewBox="0 0 24 24"fill="none"stroke="currentColor"stroke-width="2"stroke-linecap="round"stroke-linejoin="round"class="icon icon-tabler icons-tabler-outline icon-tabler-check"><path stroke="none"d="M0 0h24v24H0z"fill="none"/><path d="M5 12l5 5l10 -10"/></svg>
302
80
  </span>}
303
81
  </button>}
304
- </For2>
82
+ </Te>
305
83
  </div>
306
- </Show>
307
- </div>;
308
- }
309
-
310
- // src/components/SSExpandable.tsx
311
- import { createMemo as createMemo2, createSignal as createSignal3, onCleanup as onCleanup2 } from "solid-js";
312
- var TRANSITION_MS = 200;
313
- function SSExpandable(props) {
314
- const [height, setHeight] = createSignal3(props.initiallyExpanded ? "auto" : 0);
315
- const isExpanded = createMemo2(() => height() !== 0);
316
- let contentRef;
317
- let timeoutId;
318
- const toggle = () => {
319
- if (timeoutId) clearTimeout(timeoutId);
320
- const targetHeight = contentRef?.scrollHeight ?? 0;
321
- if (isExpanded()) {
322
- setHeight(targetHeight);
323
- timeoutId = window.setTimeout(() => setHeight(0), 1);
324
- return;
325
- }
326
- setHeight(targetHeight);
327
- timeoutId = window.setTimeout(() => setHeight("auto"), TRANSITION_MS);
328
- };
329
- onCleanup2(() => {
330
- if (timeoutId) clearTimeout(timeoutId);
331
- });
332
- return <div
333
- class={`ss_expandable ${props.class ?? ""}`}
334
- style={props.style}
335
- data-state={isExpanded() ? "open" : "closed"}
336
- >
337
- <div
338
- class="ss_expandable__header"
339
- role="button"
340
- tabindex="0"
341
- aria-expanded={isExpanded()}
342
- onclick={toggle}
343
- onkeydown={(event) => {
344
- if (event.key === "Enter" || event.key === " ") {
345
- event.preventDefault();
346
- toggle();
347
- }
348
- }}
349
- >
350
- <span class="ss_expandable__icon" aria-hidden="true">
351
- {isExpanded() ? <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-chevron-down"><path stroke="none" d="M0 0h24v24H0z" fill="none" /><path d="M6 9l6 6l6 -6" /></svg> : <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-chevron-right"><path stroke="none" d="M0 0h24v24H0z" fill="none" /><path d="M9 6l6 6l-6 6" /></svg>}
84
+ </Ce>
85
+ </div>}import{createMemo as Le,createSignal as Ie,onCleanup as Je}from"solid-js";var ee=200;function Oe(e){let[t,l]=Ie(e.initiallyExpanded?"auto":0),i=Le(()=>t()!==0),r,o,h=()=>{o&&clearTimeout(o);let c=r?.scrollHeight??0;if(i()){l(c),o=window.setTimeout(()=>l(0),1);return}l(c),o=window.setTimeout(()=>l("auto"),ee)};return Je(()=>{o&&clearTimeout(o)}),<div class={`ss_expandable ${e.class??""}`}style={e.style}data-state={i()?"open":"closed"}>
86
+ <div class="ss_expandable__header"role="button"tabindex="0"aria-expanded={i()}onclick={h}onkeydown={c=>{(c.key==="Enter"||c.key===" ")&&(c.preventDefault(),h())}}>
87
+ <span class="ss_expandable__icon"aria-hidden="true">
88
+ {i()?<svg xmlns="http://www.w3.org/2000/svg"width="24"height="24"viewBox="0 0 24 24"fill="none"stroke="currentColor"stroke-width="2"stroke-linecap="round"stroke-linejoin="round"class="icon icon-tabler icons-tabler-outline icon-tabler-chevron-down"><path stroke="none"d="M0 0h24v24H0z"fill="none"/><path d="M6 9l6 6l6 -6"/></svg>:<svg xmlns="http://www.w3.org/2000/svg"width="24"height="24"viewBox="0 0 24 24"fill="none"stroke="currentColor"stroke-width="2"stroke-linecap="round"stroke-linejoin="round"class="icon icon-tabler icons-tabler-outline icon-tabler-chevron-right"><path stroke="none"d="M0 0h24v24H0z"fill="none"/><path d="M9 6l6 6l-6 6"/></svg>}
352
89
  </span>
353
- <span class="ss_expandable__title">{props.title}</span>
90
+ <span class="ss_expandable__title">{e.title}</span>
354
91
  </div>
355
92
 
356
- <div
357
- ref={(el) => contentRef = el}
358
- class="ss_expandable__content"
359
- style={{
360
- height: typeof height() === "number" ? `${height()}px` : height(),
361
- "transition-duration": `${TRANSITION_MS}ms`
362
- }}
363
- >
93
+ <div ref={c=>r=c}class="ss_expandable__content"style={{height:typeof t()=="number"?`${t()}px`:t(),"transition-duration":`${ee}ms`}}>
364
94
  <div class='ss_expandable__divider_wrapper'>
365
- <div class="ss_expandable__divider" />
366
- {props.children}
95
+ <div class="ss_expandable__divider"/>
96
+ {e.children}
367
97
  </div>
368
98
  </div>
369
- </div>;
370
- }
371
-
372
- // src/components/SSForm.tsx
373
- import { createContext, createEffect as createEffect3, createMemo as createMemo3, createSignal as createSignal5, createUniqueId, For as For3, onCleanup as onCleanup3, onMount as onMount2, untrack, useContext } from "solid-js";
374
-
375
- // src/hooks/createLoading.ts
376
- import { createSignal as createSignal4 } from "solid-js";
377
- function createLoading() {
378
- const [loading, setLoading] = createSignal4(false);
379
- return [loading, async (callback) => {
380
- if (loading()) return;
381
- try {
382
- setLoading(true);
383
- await callback();
384
- } finally {
385
- setLoading(false);
386
- }
387
- }];
388
- }
389
-
390
- // src/components/SSForm.tsx
391
- var SSFormContext = createContext();
392
- var SSForm = function(props) {
393
- const [loading, process] = createLoading();
394
- const fields = {};
395
- const context = {
396
- loading,
397
- process: (action) => process(() => action(context)),
398
- field: (name) => fields[name] ?? null,
399
- setField: (name, field) => {
400
- fields[name] = field;
401
- },
402
- delField: (name) => {
403
- delete fields[name];
404
- },
405
- setValues: (values) => Object.entries(values).forEach(([name, value]) => fields[name]?.setValue(value)),
406
- getValues: () => Object.fromEntries(Object.entries(fields).map(([name, field]) => [name, field.getValue()])),
407
- validate: () => Promise.all(Object.values(fields).map((field) => field.validate())).then(),
408
- hasError: () => Object.values(fields).some((field) => field.hasError()),
409
- submit: () => process(async () => {
410
- await context.validate();
411
- if (context.hasError()) return;
412
- await props.onsubmit?.(context);
413
- })
414
- };
415
- return <form class="ss_form" onsubmit={(e) => {
416
- e.preventDefault();
417
- context.submit();
418
- }}>
419
- <SSFormContext.Provider value={context}>
420
- {props.children}
421
- </SSFormContext.Provider>
422
- </form>;
423
- };
424
- SSForm.Input = function(props) {
425
- const context = useContext(SSFormContext);
426
- const [value, setValue] = createSignal5("");
427
- const [error, setError] = createSignal5(null);
428
- const field = {
429
- getValue: () => {
430
- const trimmed = value().trim();
431
- if (!trimmed) return null;
432
- return trimmed;
433
- },
434
- setValue: (value2) => {
435
- if (typeof value2 === "number") {
436
- setValue(value2.toLocaleString());
437
- return;
438
- }
439
- setValue(`${value2}`);
440
- },
441
- hasError: () => !!error(),
442
- validate: async () => {
443
- const rules = [...props.rules ?? []];
444
- if (props.required) rules.unshift(SSForm.rules.required);
445
- const value2 = await field.getValue();
446
- for (const rule of rules) {
447
- const newError = await rule(value2);
448
- if (newError) {
449
- setError(newError);
450
- return;
451
- }
452
- }
453
- setError(null);
454
- }
455
- };
456
- if (props.name && context) {
457
- const name = props.name;
458
- onMount2(() => context.setField(name, field));
459
- onCleanup3(() => context.delField(name));
460
- }
461
- let suggInitData = null;
462
- const [suggItems, setSuggItems] = createSignal5([]);
463
- const [suggIndex, setSuggIndex] = createSignal5(-1);
464
- let suggListRef;
465
- if (props.suggestions) {
466
- const sugg = props.suggestions;
467
- const [initialized, setInitialized] = createSignal5(!sugg.prepare);
468
- if (sugg.prepare) onMount2(async () => {
469
- if (!sugg.prepare) return;
470
- suggInitData = await sugg.prepare();
471
- setInitialized(true);
472
- });
473
- let timeout;
474
- createEffect3(() => {
475
- clearTimeout(timeout ?? void 0);
476
- if (!initialized()) return;
477
- setSuggItems([]);
478
- const query = value().trim().toLowerCase();
479
- if (!query) return;
480
- timeout = setTimeout(async () => {
481
- const items = await sugg.getItems({ query, init: suggInitData });
482
- setSuggItems(items);
483
- }, 250);
484
- });
485
- onCleanup3(() => clearTimeout(timeout ?? void 0));
486
- }
487
- createEffect3(() => {
488
- const items = suggItems();
489
- if (!items.length) {
490
- setSuggIndex(-1);
491
- return;
492
- }
493
- if (suggIndex() >= items.length) setSuggIndex(items.length - 1);
494
- });
495
- const scrollToIndex = (idx) => {
496
- const list = suggListRef;
497
- if (!list || idx < 0) return;
498
- const item = list.querySelectorAll("li")[idx];
499
- if (!item) return;
500
- const itemTop = item.offsetTop;
501
- const itemBottom = itemTop + item.offsetHeight;
502
- const viewTop = list.scrollTop;
503
- const viewBottom = viewTop + list.clientHeight;
504
- if (itemTop < viewTop) list.scrollTop = itemTop;
505
- else if (itemBottom > viewBottom) list.scrollTop = itemBottom - list.clientHeight;
506
- };
507
- const moveIndex = (delta) => {
508
- const items = suggItems();
509
- if (!items.length) return;
510
- const next = Math.max(-1, Math.min(suggIndex() + delta, items.length - 1));
511
- setSuggIndex(next);
512
- scrollToIndex(next);
513
- };
514
- const selectSuggestion = (item) => {
515
- setValue(props.suggestions.stringify({ item, init: suggInitData }));
516
- setSuggIndex(-1);
517
- };
518
- const handleKeyDown = (e) => {
519
- if (!suggItems().length) return;
520
- if (e.key === "ArrowDown") {
521
- e.preventDefault();
522
- moveIndex(1);
523
- } else if (e.key === "ArrowUp") {
524
- e.preventDefault();
525
- moveIndex(-1);
526
- } else if (e.key === "Enter" && suggIndex() >= 0) {
527
- e.preventDefault();
528
- const item = suggItems()[suggIndex()];
529
- if (item) selectSuggestion(item);
530
- } else if (e.key === "Escape") {
531
- setSuggIndex(-1);
532
- }
533
- };
534
- const handleInputChange = (next) => {
535
- setValue(next);
536
- setSuggIndex(-1);
537
- };
538
- const _id = createUniqueId();
539
- return <div class="ss_form_input">
540
- <label for={props.id || _id}>{props.label}</label>
99
+ </div>}import{createContext as He,createEffect as B,createMemo as Xe,createSignal as T,createUniqueId as X,For as Y,onCleanup as I,onMount as J,untrack as Re,useContext as H}from"solid-js";import{createSignal as Be}from"solid-js";function A(){let[e,t]=Be(!1);return[e,async l=>{if(!e())try{t(!0),await l()}finally{t(!1)}}]}var O=He(),C=function(e){let[t,l]=A(),i={},r={loading:t,process:o=>l(()=>o(r)),field:o=>i[o]??null,setField:(o,h)=>{i[o]=h},delField:o=>{delete i[o]},setValues:o=>Object.entries(o).forEach(([h,c])=>i[h]?.setValue(c)),getValues:()=>Object.fromEntries(Object.entries(i).map(([o,h])=>[o,h.getValue()])),validate:()=>Promise.all(Object.values(i).map(o=>o.validate())).then(),hasError:()=>Object.values(i).some(o=>o.hasError()),submit:()=>l(async()=>{await r.validate(),!r.hasError()&&await e.onsubmit?.(r)})};return<form class="ss_form"onsubmit={o=>{o.preventDefault(),r.submit()}}>
100
+ <O.Provider value={r}>
101
+ {e.children}
102
+ </O.Provider>
103
+ </form>};C.Input=function(e){let t=H(O),[l,i]=T(""),[r,o]=T(null),h={getValue:()=>{let u=l().trim();return u||null},setValue:u=>{if(typeof u=="number"){i(u.toLocaleString());return}i(`${u}`)},hasError:()=>!!r(),validate:async()=>{let u=[...e.rules??[]];e.required&&u.unshift(C.rules.required);let b=await h.getValue();for(let n of u){let g=await n(b);if(g){o(g);return}}o(null)}};if(e.name&&t){let u=e.name;J(()=>t.setField(u,h)),I(()=>t.delField(u))}let c=null,[f,a]=T([]),[d,s]=T(-1),v;if(e.suggestions){let u=e.suggestions,[b,n]=T(!u.prepare);u.prepare&&J(async()=>{u.prepare&&(c=await u.prepare(),n(!0))});let g;B(()=>{if(clearTimeout(g??void 0),!b())return;a([]);let x=l().trim().toLowerCase();x&&(g=setTimeout(async()=>{let w=await u.getItems({query:x,init:c});a(w)},250))}),I(()=>clearTimeout(g??void 0))}B(()=>{let u=f();if(!u.length){s(-1);return}d()>=u.length&&s(u.length-1)});let _=u=>{let b=v;if(!b||u<0)return;let n=b.querySelectorAll("li")[u];if(!n)return;let g=n.offsetTop,x=g+n.offsetHeight,w=b.scrollTop,m=w+b.clientHeight;g<w?b.scrollTop=g:x>m&&(b.scrollTop=x-b.clientHeight)},y=u=>{let b=f();if(!b.length)return;let n=Math.max(-1,Math.min(d()+u,b.length-1));s(n),_(n)},M=u=>{i(e.suggestions.stringify({item:u,init:c})),s(-1)},k=u=>{if(f().length)if(u.key==="ArrowDown")u.preventDefault(),y(1);else if(u.key==="ArrowUp")u.preventDefault(),y(-1);else if(u.key==="Enter"&&d()>=0){u.preventDefault();let b=f()[d()];b&&M(b)}else u.key==="Escape"&&s(-1)},E=u=>{i(u),s(-1)},D=X();return<div class="ss_form_input">
104
+ <label for={e.id||D}>{e.label}</label>
541
105
 
542
106
  <div class="ss__wrapper">
543
- {props.textArea ? <textarea
544
- id={props.id || _id}
545
- value={value()}
546
- oninput={(e) => handleInputChange(e.target.value)}
547
- onkeydown={handleKeyDown}
548
- disabled={props.disabled || context?.loading()}
549
- /> : <input
550
- id={props.id || _id}
551
- value={value()}
552
- oninput={(e) => handleInputChange(e.target.value)}
553
- onkeydown={handleKeyDown}
554
- disabled={props.disabled || context?.loading()}
555
- type={props.type}
556
- />}
557
-
558
- {!!suggItems().length && <ul class="ss__suggestions" ref={(el) => suggListRef = el}>
559
- <For3 each={suggItems()}>
560
- {(item, idx) => {
561
- const handleSelect = () => {
562
- const active = document.activeElement;
563
- if (active instanceof HTMLElement) active.blur();
564
- selectSuggestion(item);
565
- };
566
- return <li
567
- onpointerdown={(e) => e.preventDefault()}
568
- onclick={handleSelect}
569
- classList={{ "ss__hovered": idx() === suggIndex() }}
570
- >
571
- {props.suggestions.stringify({ item, init: suggInitData })}
572
- </li>;
573
- }}
574
- </For3>
107
+ {e.textArea?<textarea id={e.id||D}value={l()}oninput={u=>E(u.target.value)}onkeydown={k}disabled={e.disabled||t?.loading()}/>:<input id={e.id||D}value={l()}oninput={u=>E(u.target.value)}onkeydown={k}disabled={e.disabled||t?.loading()}type={e.type}/>}
108
+
109
+ {!!f().length&&<ul class="ss__suggestions"ref={u=>v=u}>
110
+ <Y each={f()}>
111
+ {(u,b)=>{let n=()=>{let g=document.activeElement;g instanceof HTMLElement&&g.blur(),M(u)};return<li onpointerdown={g=>g.preventDefault()}onclick={n}classList={{ss__hovered:b()===d()}}>
112
+ {e.suggestions.stringify({item:u,init:c})}
113
+ </li>}}
114
+ </Y>
575
115
  </ul>}
576
116
  </div>
577
117
 
578
- {!!error() && <span role="alert">{error()}</span>}
579
- </div>;
580
- };
581
- SSForm.Date = function(props) {
582
- const context = useContext(SSFormContext);
583
- const [value, setValue] = createSignal5("");
584
- const [error, setError] = createSignal5(null);
585
- const [open, setOpen] = createSignal5(false);
586
- const [viewDate, setViewDate] = createSignal5(/* @__PURE__ */ new Date());
587
- let containerRef;
588
- const pad = (num) => `${num}`.padStart(2, "0");
589
- const formatDate = (date) => {
590
- const day = pad(date.getDate());
591
- const month = pad(date.getMonth() + 1);
592
- const year = date.getFullYear();
593
- return `${day}.${month}.${year}`;
594
- };
595
- const parseDate = (input) => {
596
- const trimmed = input.trim();
597
- if (!trimmed) return null;
598
- const match = /^(\d{1,2})\.(\d{1,2})\.(\d{4})$/.exec(trimmed);
599
- if (!match) return null;
600
- const day = Number(match[1]);
601
- const month = Number(match[2]);
602
- const year = Number(match[3]);
603
- if (!day || !month) return null;
604
- const date = new Date(year, month - 1, day);
605
- if (date.getFullYear() !== year || date.getMonth() !== month - 1 || date.getDate() !== day) {
606
- return null;
607
- }
608
- return date;
609
- };
610
- createEffect3(() => {
611
- if (!open()) return;
612
- const parsed = parseDate(value());
613
- setViewDate(parsed ?? /* @__PURE__ */ new Date());
614
- });
615
- const field = {
616
- getValue: () => {
617
- const trimmed = value().trim();
618
- if (!trimmed) return null;
619
- return trimmed;
620
- },
621
- setValue: (value2) => {
622
- if (value2 instanceof Date) {
623
- setValue(formatDate(value2));
624
- return;
625
- }
626
- setValue(`${value2 ?? ""}`);
627
- },
628
- hasError: () => !!error(),
629
- validate: async () => {
630
- const rules = [...props.rules ?? []];
631
- if (props.required) rules.unshift(SSForm.rules.required);
632
- const value2 = await field.getValue();
633
- for (const rule of rules) {
634
- const newError = await rule(value2);
635
- if (newError) {
636
- setError(newError);
637
- return;
638
- }
639
- }
640
- if (value2 && !parseDate(value2)) {
641
- setError("Ung\xFCltiges Datum");
642
- return;
643
- }
644
- setError(null);
645
- }
646
- };
647
- if (props.name && context) {
648
- const name = props.name;
649
- onMount2(() => context.setField(name, field));
650
- onCleanup3(() => context.delField(name));
651
- }
652
- const _id = createUniqueId();
653
- const isSelected = (day) => {
654
- const parsed = parseDate(value());
655
- if (!parsed) return false;
656
- return parsed.getFullYear() === viewDate().getFullYear() && parsed.getMonth() === viewDate().getMonth() && parsed.getDate() === day;
657
- };
658
- const isToday = (day) => {
659
- const today = /* @__PURE__ */ new Date();
660
- return today.getFullYear() === viewDate().getFullYear() && today.getMonth() === viewDate().getMonth() && today.getDate() === day;
661
- };
662
- const buildCalendar = () => {
663
- const current = viewDate();
664
- const year = current.getFullYear();
665
- const month = current.getMonth();
666
- const firstDay = new Date(year, month, 1);
667
- const daysInMonth = new Date(year, month + 1, 0).getDate();
668
- const startOffset = (firstDay.getDay() + 6) % 7;
669
- const cells = [];
670
- for (let i = 0; i < startOffset; i += 1) cells.push(null);
671
- for (let day = 1; day <= daysInMonth; day += 1) cells.push(day);
672
- while (cells.length % 7 !== 0) cells.push(null);
673
- return cells;
674
- };
675
- const monthLabel = () => {
676
- return viewDate().toLocaleDateString("de-DE", { month: "long", year: "numeric" });
677
- };
678
- const handleSelect = (day) => {
679
- const next = new Date(viewDate().getFullYear(), viewDate().getMonth(), day);
680
- setValue(formatDate(next));
681
- setOpen(false);
682
- };
683
- onMount2(() => {
684
- const handleOutside = (event) => {
685
- if (!open()) return;
686
- const target = event.target;
687
- if (containerRef && target && !containerRef.contains(target)) {
688
- setOpen(false);
689
- }
690
- };
691
- const handleKeyDown = (event) => {
692
- if (event.key === "Escape") setOpen(false);
693
- };
694
- document.addEventListener("mousedown", handleOutside);
695
- window.addEventListener("keydown", handleKeyDown);
696
- onCleanup3(() => {
697
- document.removeEventListener("mousedown", handleOutside);
698
- window.removeEventListener("keydown", handleKeyDown);
699
- });
700
- });
701
- return <div class="ss_form_date" ref={(el) => containerRef = el}>
702
- <label for={props.id || _id}>{props.label}</label>
118
+ {!!r()&&<span role="alert">{r()}</span>}
119
+ </div>};C.Date=function(e){let t=H(O),[l,i]=T(""),[r,o]=T(null),[h,c]=T(!1),[f,a]=T(new Date),d,s=n=>`${n}`.padStart(2,"0"),v=n=>{let g=s(n.getDate()),x=s(n.getMonth()+1),w=n.getFullYear();return`${g}.${x}.${w}`},_=n=>{let g=n.trim();if(!g)return null;let x=/^(\d{1,2})\.(\d{1,2})\.(\d{4})$/.exec(g);if(!x)return null;let w=Number(x[1]),m=Number(x[2]),S=Number(x[3]);if(!w||!m)return null;let p=new Date(S,m-1,w);return p.getFullYear()!==S||p.getMonth()!==m-1||p.getDate()!==w?null:p};B(()=>{if(!h())return;let n=_(l());a(n??new Date)});let y={getValue:()=>{let n=l().trim();return n||null},setValue:n=>{if(n instanceof Date){i(v(n));return}i(`${n??""}`)},hasError:()=>!!r(),validate:async()=>{let n=[...e.rules??[]];e.required&&n.unshift(C.rules.required);let g=await y.getValue();for(let x of n){let w=await x(g);if(w){o(w);return}}if(g&&!_(g)){o("Ung\xFCltiges Datum");return}o(null)}};if(e.name&&t){let n=e.name;J(()=>t.setField(n,y)),I(()=>t.delField(n))}let M=X(),k=n=>{let g=_(l());return g?g.getFullYear()===f().getFullYear()&&g.getMonth()===f().getMonth()&&g.getDate()===n:!1},E=n=>{let g=new Date;return g.getFullYear()===f().getFullYear()&&g.getMonth()===f().getMonth()&&g.getDate()===n},D=()=>{let n=f(),g=n.getFullYear(),x=n.getMonth(),w=new Date(g,x,1),m=new Date(g,x+1,0).getDate(),S=(w.getDay()+6)%7,p=[];for(let P=0;P<S;P+=1)p.push(null);for(let P=1;P<=m;P+=1)p.push(P);for(;p.length%7!==0;)p.push(null);return p},u=()=>f().toLocaleDateString("de-DE",{month:"long",year:"numeric"}),b=n=>{let g=new Date(f().getFullYear(),f().getMonth(),n);i(v(g)),c(!1)};return J(()=>{let n=x=>{if(!h())return;let w=x.target;d&&w&&!d.contains(w)&&c(!1)},g=x=>{x.key==="Escape"&&c(!1)};document.addEventListener("mousedown",n),window.addEventListener("keydown",g),I(()=>{document.removeEventListener("mousedown",n),window.removeEventListener("keydown",g)})}),<div class="ss_form_date"ref={n=>d=n}>
120
+ <label for={e.id||M}>{e.label}</label>
703
121
 
704
122
  <div class="ss__wrapper">
705
- <input
706
- id={props.id || _id}
707
- value={value()}
708
- oninput={(e) => setValue(e.target.value)}
709
- onfocus={() => setOpen(true)}
710
- disabled={props.disabled || context?.loading()}
711
- readonly={!props.editable}
712
- type="text"
713
- inputmode="numeric"
714
- autocomplete="off"
715
- placeholder="TT.MM.JJJJ"
716
- />
717
- <button
718
- type="button"
719
- class="ss_form_date__icon"
720
- aria-label="Kalender öffnen"
721
- aria-haspopup="dialog"
722
- aria-expanded={open()}
723
- onclick={() => setOpen((value2) => !value2)}
724
- disabled={props.disabled || context?.loading()}
725
- >
726
- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor" class="icon icon-tabler icons-tabler-filled icon-tabler-calendar-week"><path stroke="none" d="M0 0h24v24H0z" fill="none" /><path d="M16 2c.183 0 .355 .05 .502 .135l.033 .02c.28 .177 .465 .49 .465 .845v1h1a3 3 0 0 1 2.995 2.824l.005 .176v12a3 3 0 0 1 -2.824 2.995l-.176 .005h-12a3 3 0 0 1 -2.995 -2.824l-.005 -.176v-12a3 3 0 0 1 2.824 -2.995l.176 -.005h1v-1a1 1 0 0 1 .514 -.874l.093 -.046l.066 -.025l.1 -.029l.107 -.019l.12 -.007q .083 0 .161 .013l.122 .029l.04 .012l.06 .023c.328 .135 .568 .44 .61 .806l.007 .117v1h6v-1a1 1 0 0 1 1 -1m3 7h-14v9.625c0 .705 .386 1.286 .883 1.366l.117 .009h12c.513 0 .936 -.53 .993 -1.215l.007 -.16z" /><path d="M9.015 13a1 1 0 0 1 -1 1a1.001 1.001 0 1 1 -.005 -2c.557 0 1.005 .448 1.005 1" /><path d="M13.015 13a1 1 0 0 1 -1 1a1.001 1.001 0 1 1 -.005 -2c.557 0 1.005 .448 1.005 1" /><path d="M17.02 13a1 1 0 0 1 -1 1a1.001 1.001 0 1 1 -.005 -2c.557 0 1.005 .448 1.005 1" /><path d="M12.02 15a1 1 0 0 1 0 2a1.001 1.001 0 1 1 -.005 -2z" /><path d="M9.015 16a1 1 0 0 1 -1 1a1.001 1.001 0 1 1 -.005 -2c.557 0 1.005 .448 1.005 1" /></svg>
123
+ <input id={e.id||M}value={l()}oninput={n=>i(n.target.value)}onfocus={()=>c(!0)}disabled={e.disabled||t?.loading()}readonly={!e.editable}type="text"inputmode="numeric"autocomplete="off"placeholder="TT.MM.JJJJ"/>
124
+ <button type="button"class="ss_form_date__icon"aria-label="Kalender öffnen"aria-haspopup="dialog"aria-expanded={h()}onclick={()=>c(n=>!n)}disabled={e.disabled||t?.loading()}>
125
+ <svg xmlns="http://www.w3.org/2000/svg"width="24"height="24"viewBox="0 0 24 24"fill="currentColor"class="icon icon-tabler icons-tabler-filled icon-tabler-calendar-week"><path stroke="none"d="M0 0h24v24H0z"fill="none"/><path d="M16 2c.183 0 .355 .05 .502 .135l.033 .02c.28 .177 .465 .49 .465 .845v1h1a3 3 0 0 1 2.995 2.824l.005 .176v12a3 3 0 0 1 -2.824 2.995l-.176 .005h-12a3 3 0 0 1 -2.995 -2.824l-.005 -.176v-12a3 3 0 0 1 2.824 -2.995l.176 -.005h1v-1a1 1 0 0 1 .514 -.874l.093 -.046l.066 -.025l.1 -.029l.107 -.019l.12 -.007q .083 0 .161 .013l.122 .029l.04 .012l.06 .023c.328 .135 .568 .44 .61 .806l.007 .117v1h6v-1a1 1 0 0 1 1 -1m3 7h-14v9.625c0 .705 .386 1.286 .883 1.366l.117 .009h12c.513 0 .936 -.53 .993 -1.215l.007 -.16z"/><path d="M9.015 13a1 1 0 0 1 -1 1a1.001 1.001 0 1 1 -.005 -2c.557 0 1.005 .448 1.005 1"/><path d="M13.015 13a1 1 0 0 1 -1 1a1.001 1.001 0 1 1 -.005 -2c.557 0 1.005 .448 1.005 1"/><path d="M17.02 13a1 1 0 0 1 -1 1a1.001 1.001 0 1 1 -.005 -2c.557 0 1.005 .448 1.005 1"/><path d="M12.02 15a1 1 0 0 1 0 2a1.001 1.001 0 1 1 -.005 -2z"/><path d="M9.015 16a1 1 0 0 1 -1 1a1.001 1.001 0 1 1 -.005 -2c.557 0 1.005 .448 1.005 1"/></svg>
727
126
  </button>
728
127
  </div>
729
128
 
730
- {open() && <div class="ss_form_date__picker" role="dialog" aria-label="Datum auswählen">
129
+ {h()&&<div class="ss_form_date__picker"role="dialog"aria-label="Datum auswählen">
731
130
  <div class="ss_form_date__header">
732
131
  <div class="ss_form_date__nav_group">
733
- <button type="button" class="ss_form_date__nav" onclick={() => {
734
- const current = viewDate();
735
- setViewDate(new Date(current.getFullYear() - 1, current.getMonth(), 1));
736
- }}>
737
- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-chevron-left-pipe"><path stroke="none" d="M0 0h24v24H0z" fill="none" /><path d="M7 6v12" /><path d="M18 6l-6 6l6 6" /></svg>
132
+ <button type="button"class="ss_form_date__nav"onclick={()=>{let n=f();a(new Date(n.getFullYear()-1,n.getMonth(),1))}}>
133
+ <svg xmlns="http://www.w3.org/2000/svg"width="24"height="24"viewBox="0 0 24 24"fill="none"stroke="currentColor"stroke-width="2"stroke-linecap="round"stroke-linejoin="round"class="icon icon-tabler icons-tabler-outline icon-tabler-chevron-left-pipe"><path stroke="none"d="M0 0h24v24H0z"fill="none"/><path d="M7 6v12"/><path d="M18 6l-6 6l6 6"/></svg>
738
134
  </button>
739
- <button type="button" class="ss_form_date__nav" onclick={() => {
740
- const current = viewDate();
741
- setViewDate(new Date(current.getFullYear(), current.getMonth() - 1, 1));
742
- }}>
743
- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-chevron-left"><path stroke="none" d="M0 0h24v24H0z" fill="none" /><path d="M15 6l-6 6l6 6" /></svg>
135
+ <button type="button"class="ss_form_date__nav"onclick={()=>{let n=f();a(new Date(n.getFullYear(),n.getMonth()-1,1))}}>
136
+ <svg xmlns="http://www.w3.org/2000/svg"width="24"height="24"viewBox="0 0 24 24"fill="none"stroke="currentColor"stroke-width="2"stroke-linecap="round"stroke-linejoin="round"class="icon icon-tabler icons-tabler-outline icon-tabler-chevron-left"><path stroke="none"d="M0 0h24v24H0z"fill="none"/><path d="M15 6l-6 6l6 6"/></svg>
744
137
  </button>
745
138
  </div>
746
- <div class="ss_form_date__title">{monthLabel()}</div>
139
+ <div class="ss_form_date__title">{u()}</div>
747
140
  <div class="ss_form_date__nav_group">
748
- <button type="button" class="ss_form_date__nav" onclick={() => {
749
- const current = viewDate();
750
- setViewDate(new Date(current.getFullYear(), current.getMonth() + 1, 1));
751
- }}>
752
- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-chevron-right"><path stroke="none" d="M0 0h24v24H0z" fill="none" /><path d="M9 6l6 6l-6 6" /></svg>
141
+ <button type="button"class="ss_form_date__nav"onclick={()=>{let n=f();a(new Date(n.getFullYear(),n.getMonth()+1,1))}}>
142
+ <svg xmlns="http://www.w3.org/2000/svg"width="24"height="24"viewBox="0 0 24 24"fill="none"stroke="currentColor"stroke-width="2"stroke-linecap="round"stroke-linejoin="round"class="icon icon-tabler icons-tabler-outline icon-tabler-chevron-right"><path stroke="none"d="M0 0h24v24H0z"fill="none"/><path d="M9 6l6 6l-6 6"/></svg>
753
143
  </button>
754
- <button type="button" class="ss_form_date__nav" onclick={() => {
755
- const current = viewDate();
756
- setViewDate(new Date(current.getFullYear() + 1, current.getMonth(), 1));
757
- }}>
758
- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-chevron-right-pipe"><path stroke="none" d="M0 0h24v24H0z" fill="none" /><path d="M6 6l6 6l-6 6" /><path d="M17 5v13" /></svg>
144
+ <button type="button"class="ss_form_date__nav"onclick={()=>{let n=f();a(new Date(n.getFullYear()+1,n.getMonth(),1))}}>
145
+ <svg xmlns="http://www.w3.org/2000/svg"width="24"height="24"viewBox="0 0 24 24"fill="none"stroke="currentColor"stroke-width="2"stroke-linecap="round"stroke-linejoin="round"class="icon icon-tabler icons-tabler-outline icon-tabler-chevron-right-pipe"><path stroke="none"d="M0 0h24v24H0z"fill="none"/><path d="M6 6l6 6l-6 6"/><path d="M17 5v13"/></svg>
759
146
  </button>
760
147
  </div>
761
148
  </div>
762
149
  <div class="ss_form_date__weekdays">
763
- {["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"].map((day) => <span>{day}</span>)}
150
+ {["Mo","Di","Mi","Do","Fr","Sa","So"].map(n=><span>{n}</span>)}
764
151
  </div>
765
152
  <div class="ss_form_date__grid">
766
- {buildCalendar().map((day) => day ? <button
767
- type="button"
768
- class="ss_form_date__day"
769
- classList={{ "is-selected": isSelected(day), "is-today": isToday(day) }}
770
- onclick={() => handleSelect(day)}
771
- >
772
- {day}
773
- </button> : <span class="ss_form_date__day is-empty" />)}
153
+ {D().map(n=>n?<button type="button"class="ss_form_date__day"classList={{"is-selected":k(n),"is-today":E(n)}}onclick={()=>b(n)}>
154
+ {n}
155
+ </button>:<span class="ss_form_date__day is-empty"/>)}
774
156
  </div>
775
157
  </div>}
776
158
 
777
- {!!error() && <span role="alert">{error()}</span>}
778
- </div>;
779
- };
780
- SSForm.Checkbox = function(props) {
781
- const context = useContext(SSFormContext);
782
- const [value, setValue] = createSignal5(false);
783
- const [error, setError] = createSignal5(null);
784
- const field = {
785
- getValue: () => value(),
786
- setValue: (value2) => setValue(!!value2),
787
- hasError: () => !!error(),
788
- validate: async () => {
789
- const rules = [...props.rules ?? []];
790
- if (props.required) rules.unshift(SSForm.rules.required);
791
- const value2 = await field.getValue();
792
- for (const rule of rules) {
793
- const newError = await rule(value2);
794
- if (newError) {
795
- setError(newError);
796
- return;
797
- }
798
- }
799
- setError(null);
800
- }
801
- };
802
- if (props.name && context) {
803
- const name = props.name;
804
- onMount2(() => context.setField(name, field));
805
- onCleanup3(() => context.delField(name));
806
- }
807
- const _id = createUniqueId();
808
- return <div class="ss_form_checkbox">
159
+ {!!r()&&<span role="alert">{r()}</span>}
160
+ </div>};C.Checkbox=function(e){let t=H(O),[l,i]=T(!1),[r,o]=T(null),h={getValue:()=>l(),setValue:f=>i(!!f),hasError:()=>!!r(),validate:async()=>{let f=[...e.rules??[]];e.required&&f.unshift(C.rules.required);let a=await h.getValue();for(let d of f){let s=await d(a);if(s){o(s);return}}o(null)}};if(e.name&&t){let f=e.name;J(()=>t.setField(f,h)),I(()=>t.delField(f))}let c=X();return<div class="ss_form_checkbox">
809
161
  <div class="ss__wrapper">
810
- <input
811
- id={props.id || _id}
812
- type="checkbox"
813
- checked={value()}
814
- onchange={(e) => setValue(e.target.checked)}
815
- disabled={props.disabled || context?.loading()}
816
- />
817
- <label for={props.id || _id}>{props.label}</label>
162
+ <input id={e.id||c}type="checkbox"checked={l()}onchange={f=>i(f.target.checked)}disabled={e.disabled||t?.loading()}/>
163
+ <label for={e.id||c}>{e.label}</label>
818
164
  </div>
819
165
 
820
- {!!error() && <span role="alert">{error()}</span>}
821
- </div>;
822
- };
823
- SSForm.useContext = function() {
824
- const context = useContext(SSFormContext);
825
- if (!context) return null;
826
- const { setField, delField, ...publicContext } = context;
827
- return publicContext;
828
- };
829
- SSForm.Select = function(props) {
830
- const context = useContext(SSFormContext);
831
- const [options, setOptions] = createSignal5([]);
832
- const [value, setValue] = createSignal5(null);
833
- const [error, setError] = createSignal5(null);
834
- const handleSetOptions = (result) => {
835
- const built = result.map((opt) => ({ id: opt.id, label: props.buildOption(opt) }));
836
- setOptions(built);
837
- if (!result.length) {
838
- setValue(null);
839
- return;
840
- }
841
- if (value() && built.some((e) => e.id === value())) return;
842
- setValue(built[0].id);
843
- };
844
- createEffect3(() => {
845
- const newOptions = props.getOptions();
846
- if ("then" in newOptions && typeof newOptions.then === "function" || newOptions instanceof Promise) {
847
- newOptions.then(handleSetOptions);
848
- } else {
849
- handleSetOptions(newOptions);
850
- }
851
- });
852
- const field = {
853
- getValue: () => {
854
- const _value = value();
855
- const availableOptions = options();
856
- if (!availableOptions.some((e) => e.id === _value)) {
857
- return null;
858
- }
859
- return _value;
860
- },
861
- setValue: (newValue) => {
862
- const availableOptions = options();
863
- if (!availableOptions.length) {
864
- setValue(null);
865
- return;
866
- }
867
- const defaultOption = availableOptions[0].id;
868
- if (typeof newValue !== "string") {
869
- setValue(defaultOption);
870
- return;
871
- }
872
- const _newValue = newValue.trim();
873
- if (!availableOptions.some((e) => e.id === _newValue)) {
874
- setValue(defaultOption);
875
- return;
876
- }
877
- setValue(_newValue);
878
- },
879
- hasError: () => !!error(),
880
- validate: async () => {
881
- const availableOptions = options();
882
- const value2 = await field.getValue();
883
- if (!value2) {
884
- setError("Pflichtfeld");
885
- return;
886
- }
887
- if (!availableOptions.some((e) => e.id === value2)) {
888
- setError("Option nicht verf\xFCgbar");
889
- return;
890
- }
891
- for (const rule of props.rules ?? []) {
892
- const err = await rule(value2);
893
- if (err) {
894
- setError(err);
895
- return;
896
- }
897
- }
898
- setError(null);
899
- }
900
- };
901
- field.setOptions = handleSetOptions;
902
- if (props.name && context) {
903
- const name = props.name;
904
- onMount2(() => context.setField(name, field));
905
- onCleanup3(() => context.delField(name));
906
- }
907
- const _id = createUniqueId();
908
- return <div class="ss_form_select">
909
- <label for={props.id || _id}>{props.label}</label>
166
+ {!!r()&&<span role="alert">{r()}</span>}
167
+ </div>};C.useContext=function(){let e=H(O);if(!e)return null;let{setField:t,delField:l,...i}=e;return i};C.Select=function(e){let t=H(O),[l,i]=T([]),[r,o]=T(null),[h,c]=T(null),f=s=>{let v=s.map(_=>({id:_.id,label:e.buildOption(_)}));if(i(v),!s.length){o(null);return}r()&&v.some(_=>_.id===r())||o(v[0].id)};B(()=>{let s=e.getOptions();"then"in s&&typeof s.then=="function"||s instanceof Promise?s.then(f):f(s)});let a={getValue:()=>{let s=r();return l().some(_=>_.id===s)?s:null},setValue:s=>{let v=l();if(!v.length){o(null);return}let _=v[0].id;if(typeof s!="string"){o(_);return}let y=s.trim();if(!v.some(M=>M.id===y)){o(_);return}o(y)},hasError:()=>!!h(),validate:async()=>{let s=l(),v=await a.getValue();if(!v){c("Pflichtfeld");return}if(!s.some(_=>_.id===v)){c("Option nicht verf\xFCgbar");return}for(let _ of e.rules??[]){let y=await _(v);if(y){c(y);return}}c(null)}};if(a.setOptions=f,e.name&&t){let s=e.name;J(()=>t.setField(s,a)),I(()=>t.delField(s))}let d=X();return<div class="ss_form_select">
168
+ <label for={e.id||d}>{e.label}</label>
910
169
 
911
170
  <div class="ss_form_select__field">
912
- <select
913
- id={props.id || _id}
914
- onchange={(e) => setValue(e.target.value)}
915
- disabled={props.disabled || context?.loading()}
916
- >
917
- <For3 each={options()}>
918
- {(opt) => <option value={opt.id} selected={value() === opt.id}>{opt.label}</option>}
919
- </For3>
171
+ <select id={e.id||d}onchange={s=>o(s.target.value)}disabled={e.disabled||t?.loading()}>
172
+ <Y each={l()}>
173
+ {s=><option value={s.id}selected={r()===s.id}>{s.label}</option>}
174
+ </Y>
920
175
  </select>
921
- <span class="ss_form_select__icon" aria-hidden="true">
922
- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-chevron-down"><path stroke="none" d="M0 0h24v24H0z" fill="none" /><path d="M6 9l6 6l6 -6" /></svg>
176
+ <span class="ss_form_select__icon"aria-hidden="true">
177
+ <svg xmlns="http://www.w3.org/2000/svg"width="24"height="24"viewBox="0 0 24 24"fill="none"stroke="currentColor"stroke-width="2"stroke-linecap="round"stroke-linejoin="round"class="icon icon-tabler icons-tabler-outline icon-tabler-chevron-down"><path stroke="none"d="M0 0h24v24H0z"fill="none"/><path d="M6 9l6 6l6 -6"/></svg>
923
178
  </span>
924
179
  </div>
925
180
 
926
- {!!error() && <span role="alert">{error()}</span>}
927
- </div>;
928
- };
929
- SSForm.ACSelect = function(props) {
930
- const context = useContext(SSFormContext);
931
- const [query, setQuery] = createSignal5("");
932
- const [value, setValue] = createSignal5([]);
933
- const [error, setError] = createSignal5(null);
934
- const field = {
935
- getValue: () => value(),
936
- setValue: (newValue) => setValue(newValue),
937
- hasError: () => !!error(),
938
- validate: async () => {
939
- const value2 = await field.getValue();
940
- const min = props.minSelectedItems;
941
- const max = props.maxSelectedItems;
942
- if (min) {
943
- if (value2.length < min) {
944
- setError(`Mindestens ${min} ${min > 1 ? "Elemente m\xFCssen" : "Element muss"} ausgew\xE4hlt werden`);
945
- return;
946
- }
947
- }
948
- if (max) {
949
- if (value2.length > max) {
950
- setError(`Maximal ${max} ${max > 1 ? "Elemente d\xFCrfen" : "Element darf"} ausgew\xE4hlt werden`);
951
- return;
952
- }
953
- }
954
- for (const rule of props.rules ?? []) {
955
- const err = await rule(value2);
956
- if (err) {
957
- setError(err);
958
- return;
959
- }
960
- }
961
- setError(null);
962
- }
963
- };
964
- if (props.name && context) {
965
- const name = props.name;
966
- onMount2(() => context.setField(name, field));
967
- onCleanup3(() => context.delField(name));
968
- }
969
- let initData = null;
970
- const [initialized, setInitialized] = createSignal5(!props.prepare);
971
- if (props.prepare) onMount2(async () => {
972
- if (!props.prepare) return;
973
- initData = await props.prepare();
974
- setInitialized(true);
975
- });
976
- const [suggItems, setSuggItems] = createSignal5([]);
977
- const [suggIndex, setSuggIndex] = createSignal5(-1);
978
- let suggListRef;
979
- let timeout;
980
- createEffect3(() => {
981
- if (!initialized()) return;
982
- clearTimeout(timeout);
983
- const _query = query().trim().toLowerCase();
984
- if (!_query) {
985
- setSuggItems(untrack(() => value()));
986
- return;
987
- }
988
- setSuggItems([]);
989
- timeout = setTimeout(async () => {
990
- const items = await props.getOptions({ query: _query, init: initData });
991
- setSuggItems(items);
992
- }, 250);
993
- });
994
- createEffect3(() => {
995
- const _query = query().trim().toLowerCase();
996
- if (_query) return;
997
- if (suggItems().length !== value().length) setSuggItems(value());
998
- });
999
- onCleanup3(() => clearTimeout(timeout));
1000
- createEffect3(() => {
1001
- const items = suggItems();
1002
- if (!items.length) {
1003
- setSuggIndex(-1);
1004
- return;
1005
- }
1006
- if (suggIndex() >= items.length) setSuggIndex(items.length - 1);
1007
- });
1008
- const scrollToIndex = (idx) => {
1009
- const list = suggListRef;
1010
- if (!list || idx < 0) return;
1011
- const item = list.querySelectorAll("li")[idx];
1012
- if (!item) return;
1013
- const itemTop = item.offsetTop;
1014
- const itemBottom = itemTop + item.offsetHeight;
1015
- const viewTop = list.scrollTop;
1016
- const viewBottom = viewTop + list.clientHeight;
1017
- if (itemTop < viewTop) list.scrollTop = itemTop;
1018
- else if (itemBottom > viewBottom) list.scrollTop = itemBottom - list.clientHeight;
1019
- };
1020
- const moveIndex = (delta) => {
1021
- const items = suggItems();
1022
- if (!items.length) return;
1023
- const next = Math.max(-1, Math.min(suggIndex() + delta, items.length - 1));
1024
- setSuggIndex(next);
1025
- scrollToIndex(next);
1026
- };
1027
- const _id = createUniqueId();
1028
- const selectedIds = createMemo3(() => {
1029
- return new Set(value().map(({ id }) => id));
1030
- });
1031
- const selectItem = (item, resetIndex = true) => {
1032
- const isSelected = selectedIds().has(item.id);
1033
- if (props.maxSelectedItems === 1) {
1034
- setValue(isSelected ? [] : [item]);
1035
- setQuery("");
1036
- if (resetIndex) setSuggIndex(-1);
1037
- return;
1038
- }
1039
- if (isSelected) {
1040
- setValue((value2) => value2.filter(({ id }) => id !== item.id));
1041
- } else {
1042
- setValue((value2) => [...value2.filter(({ id }) => id !== item.id), item]);
1043
- }
1044
- if (resetIndex) setSuggIndex(-1);
1045
- };
1046
- const handleKeyDown = (e) => {
1047
- if (!suggItems().length) return;
1048
- if (e.key === "ArrowDown") {
1049
- e.preventDefault();
1050
- moveIndex(1);
1051
- } else if (e.key === "ArrowUp") {
1052
- e.preventDefault();
1053
- moveIndex(-1);
1054
- } else if (e.key === "Enter" && suggIndex() >= 0) {
1055
- e.preventDefault();
1056
- const item = suggItems()[suggIndex()];
1057
- if (item) selectItem(item, false);
1058
- } else if (e.key === "Escape") {
1059
- setSuggIndex(-1);
1060
- }
1061
- };
1062
- const isDisabled = () => props.disabled || context?.loading();
1063
- return <div class="ss_form_ac_select">
1064
- <label for={props.id || _id}>{props.label}</label>
181
+ {!!h()&&<span role="alert">{h()}</span>}
182
+ </div>};C.ACSelect=function(e){let t=H(O),[l,i]=T(""),[r,o]=T([]),[h,c]=T(null),f={getValue:()=>r(),setValue:m=>o(m),hasError:()=>!!h(),validate:async()=>{let m=await f.getValue(),S=e.minSelectedItems,p=e.maxSelectedItems;if(S&&m.length<S){c(`Mindestens ${S} ${S>1?"Elemente m\xFCssen":"Element muss"} ausgew\xE4hlt werden`);return}if(p&&m.length>p){c(`Maximal ${p} ${p>1?"Elemente d\xFCrfen":"Element darf"} ausgew\xE4hlt werden`);return}for(let P of e.rules??[]){let L=await P(m);if(L){c(L);return}}c(null)}};if(e.name&&t){let m=e.name;J(()=>t.setField(m,f)),I(()=>t.delField(m))}let a=null,[d,s]=T(!e.prepare);e.prepare&&J(async()=>{e.prepare&&(a=await e.prepare(),s(!0))});let[v,_]=T([]),[y,M]=T(-1),k,E;B(()=>{if(!d())return;clearTimeout(E);let m=l().trim().toLowerCase();if(!m){_(Re(()=>r()));return}_([]),E=setTimeout(async()=>{let S=await e.getOptions({query:m,init:a});_(S)},250)}),B(()=>{l().trim().toLowerCase()||v().length!==r().length&&_(r())}),I(()=>clearTimeout(E)),B(()=>{let m=v();if(!m.length){M(-1);return}y()>=m.length&&M(m.length-1)});let D=m=>{let S=k;if(!S||m<0)return;let p=S.querySelectorAll("li")[m];if(!p)return;let P=p.offsetTop,L=P+p.offsetHeight,G=S.scrollTop,Se=G+S.clientHeight;P<G?S.scrollTop=P:L>Se&&(S.scrollTop=L-S.clientHeight)},u=m=>{let S=v();if(!S.length)return;let p=Math.max(-1,Math.min(y()+m,S.length-1));M(p),D(p)},b=X(),n=Xe(()=>new Set(r().map(({id:m})=>m))),g=(m,S=!0)=>{let p=n().has(m.id);if(e.maxSelectedItems===1){o(p?[]:[m]),i(""),S&&M(-1);return}o(p?P=>P.filter(({id:L})=>L!==m.id):P=>[...P.filter(({id:L})=>L!==m.id),m]),S&&M(-1)},x=m=>{if(v().length)if(m.key==="ArrowDown")m.preventDefault(),u(1);else if(m.key==="ArrowUp")m.preventDefault(),u(-1);else if(m.key==="Enter"&&y()>=0){m.preventDefault();let S=v()[y()];S&&g(S,!1)}else m.key==="Escape"&&M(-1)},w=()=>e.disabled||t?.loading();return<div class="ss_form_ac_select">
183
+ <label for={e.id||b}>{e.label}</label>
1065
184
 
1066
- {props.maxSelectedItems === 1 && !!props.renderSelection && value().length === 1 ? <div class="ss__selection">
185
+ {e.maxSelectedItems===1&&e.renderSelection&&r().length===1?<div class="ss__selection">
1067
186
  <div class="ss__content">
1068
- {props.renderSelection({ item: value()[0], init: initData })}
187
+ {e.renderSelection({item:r()[0],init:a})}
1069
188
  </div>
1070
- <button
1071
- type="button"
1072
- class="ss__clear"
1073
- disabled={isDisabled()}
1074
- aria-label="Auswahl entfernen"
1075
- onclick={() => {
1076
- setValue([]);
1077
- setQuery("");
1078
- setTimeout(() => document.getElementById(props.id || _id)?.focus(), 0);
1079
- }}
1080
- >
189
+ <button type="button"class="ss__clear"disabled={w()}aria-label="Auswahl entfernen"onclick={()=>{o([]),i(""),setTimeout(()=>document.getElementById(e.id||b)?.focus(),0)}}>
1081
190
  ×
1082
191
  </button>
1083
- </div> : <div class="ss__wrapper">
192
+ </div>:<div class="ss__wrapper">
1084
193
 
1085
194
  <div class="ss__input_row">
1086
- {!!value().length && <span
1087
- class="ss__prefix"
1088
- role="button"
1089
- tabindex={isDisabled() ? -1 : 0}
1090
- aria-disabled={isDisabled() ? "true" : void 0}
1091
- onkeydown={(event) => {
1092
- if (isDisabled()) return;
1093
- if (event.key === "Enter" || event.key === " ") {
1094
- event.preventDefault();
1095
- setValue([]);
1096
- }
1097
- }}
1098
- onclick={() => {
1099
- if (isDisabled()) return;
1100
- setValue([]);
1101
- }}
1102
- >
1103
- {value().length}
195
+ {!!r().length&&<span class="ss__prefix"role="button"tabindex={w()?-1:0}aria-disabled={w()?"true":void 0}onkeydown={m=>{w()||(m.key==="Enter"||m.key===" ")&&(m.preventDefault(),o([]))}}onclick={()=>{w()||o([])}}>
196
+ {r().length}
1104
197
  </span>}
1105
198
 
1106
- <input
1107
- id={props.id || _id}
1108
- value={query()}
1109
- oninput={(e) => {
1110
- if (isDisabled()) return;
1111
- setQuery(e.target.value);
1112
- setSuggIndex(-1);
1113
- }}
1114
- onkeydown={handleKeyDown}
1115
- disabled={isDisabled()}
1116
- />
199
+ <input id={e.id||b}value={l()}oninput={m=>{w()||(i(m.target.value),M(-1))}}onkeydown={x}disabled={w()}/>
1117
200
  </div>
1118
201
 
1119
- {!!suggItems().length && <ul class="ss__suggestions" ref={(el) => suggListRef = el}>
1120
- <For3 each={suggItems()}>
1121
- {(item, idx) => {
1122
- const handleSelect = () => {
1123
- if (isDisabled()) return;
1124
- selectItem(item);
1125
- document.getElementById(props.id || _id)?.focus();
1126
- };
1127
- return <li
1128
- onpointerdown={(e) => e.preventDefault()}
1129
- onclick={handleSelect}
1130
- classList={{
1131
- "ss__selected": selectedIds().has(item.id),
1132
- "ss__hovered": idx() === suggIndex()
1133
- }}
1134
- >
1135
- {props.renderItem({ item, init: initData })}
1136
- </li>;
1137
- }}
1138
- </For3>
202
+ {!!v().length&&<ul class="ss__suggestions"ref={m=>k=m}>
203
+ <Y each={v()}>
204
+ {(m,S)=>{let p=()=>{w()||(g(m),document.getElementById(e.id||b)?.focus())};return<li onpointerdown={P=>P.preventDefault()}onclick={p}classList={{ss__selected:n().has(m.id),ss__hovered:S()===y()}}>
205
+ {e.renderItem({item:m,init:a})}
206
+ </li>}}
207
+ </Y>
1139
208
  </ul>}
1140
209
  </div>}
1141
210
 
1142
- {!!error() && <span role="alert">{error()}</span>}
1143
- </div>;
1144
- };
1145
- SSForm.SubmitButton = function(props) {
1146
- const context = useContext(SSFormContext);
1147
- return <SSButton
1148
- type="submit"
1149
- class="ss_form_submit"
1150
- disabled={props.disabled || context?.loading()}
1151
- >
1152
- {props.children}
1153
- </SSButton>;
1154
- };
1155
- SSForm.rules = {
1156
- required: (value) => !value ? "Pflichtfeld" : null,
1157
- minLength: (length) => (value) => {
1158
- if (value === null) value = "";
1159
- if (typeof value !== "string") {
1160
- throw new Error(`invalid rule minLength(..) for non-string value ${value}`);
1161
- }
1162
- if (value.length < length) return `Mindestens ${length} Zeichen`;
1163
- return null;
1164
- },
1165
- maxLength: (length) => (value) => {
1166
- if (value === null) value = "";
1167
- if (typeof value !== "string") {
1168
- throw new Error(`invalid rule maxLength(..) for non-string value ${value}`);
1169
- }
1170
- if (value.length < length) return `Maximal ${length} Zeichen`;
1171
- return null;
1172
- },
1173
- pattern: (pattern) => (value) => {
1174
- if (value === null) value = "";
1175
- if (typeof value !== "string") {
1176
- throw new Error(`invalid rule pattern(..) for non-string value ${value}`);
1177
- }
1178
- if (!pattern.test(value)) return "Eingabe widerspricht erwartetem Muster";
1179
- return null;
1180
- }
1181
- };
1182
-
1183
- // src/components/SSHeader.tsx
1184
- function SSHeader(props) {
1185
- return <div class={`ss_header ${props.class ?? ""}`} style={props.style}>
211
+ {!!h()&&<span role="alert">{h()}</span>}
212
+ </div>};C.SubmitButton=function(e){let t=H(O);return<F type="submit"class="ss_form_submit"disabled={e.disabled||t?.loading()}>
213
+ {e.children}
214
+ </F>};C.rules={required:e=>e?null:"Pflichtfeld",minLength:e=>t=>{if(t===null&&(t=""),typeof t!="string")throw new Error(`invalid rule minLength(..) for non-string value ${t}`);return t.length<e?`Mindestens ${e} Zeichen`:null},maxLength:e=>t=>{if(t===null&&(t=""),typeof t!="string")throw new Error(`invalid rule maxLength(..) for non-string value ${t}`);return t.length<e?`Maximal ${e} Zeichen`:null},pattern:e=>t=>{if(t===null&&(t=""),typeof t!="string")throw new Error(`invalid rule pattern(..) for non-string value ${t}`);return e.test(t)?null:"Eingabe widerspricht erwartetem Muster"}};function Ve(e){return<div class={`ss_header ${e.class??""}`}style={e.style}>
1186
215
  <div class="ss_header__text">
1187
- <h3 class="ss_header__title">{props.title}</h3>
1188
- {props.subtitle && <h5 class="ss_header__subtitle">{props.subtitle}</h5>}
216
+ <h3 class="ss_header__title">{e.title}</h3>
217
+ {e.subtitle&&<h5 class="ss_header__subtitle">{e.subtitle}</h5>}
1189
218
  </div>
1190
- <div class="ss_header__actions">{props.actions}</div>
1191
- </div>;
1192
- }
1193
-
1194
- // src/components/SSModal.tsx
1195
- import { Show as Show3, createEffect as createEffect4, createSignal as createSignal6, createUniqueId as createUniqueId2, onCleanup as onCleanup4, onMount as onMount3 } from "solid-js";
1196
- import { Portal } from "solid-js/web";
1197
- var CLOSE_ANIMATION_MS = 180;
1198
- function SSModal(props) {
1199
- const [isMounted, setIsMounted] = createSignal6(props.open);
1200
- const [state, setState] = createSignal6("closed");
1201
- const titleId = createUniqueId2();
1202
- let closeTimeout;
1203
- let rafId;
1204
- let panelRef;
1205
- createEffect4(() => {
1206
- if (closeTimeout) clearTimeout(closeTimeout);
1207
- if (rafId) cancelAnimationFrame(rafId);
1208
- if (props.open) {
1209
- if (!isMounted()) setIsMounted(true);
1210
- setState("closed");
1211
- rafId = requestAnimationFrame(() => setState("open"));
1212
- } else {
1213
- setState("closed");
1214
- if (isMounted()) {
1215
- closeTimeout = window.setTimeout(() => setIsMounted(false), CLOSE_ANIMATION_MS);
1216
- }
1217
- }
1218
- });
1219
- onMount3(() => {
1220
- if (!props.open) return;
1221
- if (props.lockScroll !== false) {
1222
- const prev = document.body.style.overflowY;
1223
- document.body.style.overflowY = "hidden";
1224
- onCleanup4(() => {
1225
- document.body.style.overflowY = prev;
1226
- });
1227
- }
1228
- rafId = requestAnimationFrame(() => panelRef?.focus());
1229
- const handleKeyDown = (event) => {
1230
- if (event.key === "Escape" && props.dismissible !== false) {
1231
- props.onClose?.();
1232
- }
1233
- };
1234
- window.addEventListener("keydown", handleKeyDown);
1235
- onCleanup4(() => window.removeEventListener("keydown", handleKeyDown));
1236
- });
1237
- onCleanup4(() => {
1238
- if (closeTimeout) clearTimeout(closeTimeout);
1239
- if (rafId) cancelAnimationFrame(rafId);
1240
- });
1241
- const handleBackdropClick = () => {
1242
- if (props.dismissible === false) return;
1243
- props.onClose?.();
1244
- };
1245
- return <Show3 when={isMounted()}>
1246
- <Portal>
1247
- <div class="ss_modal" data-state={state()} aria-hidden={state() === "closed"}>
1248
- <div class="ss_modal__backdrop" onclick={handleBackdropClick} />
1249
- <div
1250
- class="ss_modal__panel"
1251
- classList={{
1252
- "ss_modal__panel--sm": props.size === "sm",
1253
- "ss_modal__panel--lg": props.size === "lg",
1254
- "ss_modal__panel--fullscreen": props.fullscreen,
1255
- "ss_modal__panel--no-fullscreen": props.disableResponsiveFullscreen
1256
- }}
1257
- ref={(el) => panelRef = el}
1258
- role="dialog"
1259
- aria-modal="true"
1260
- aria-labelledby={props.title ? titleId : void 0}
1261
- tabindex="-1"
1262
- >
1263
- {(props.title || props.onClose) && <div class="ss_modal__header">
1264
- {props.title && <h2 id={titleId} class="ss_modal__title">
1265
- {props.title}
219
+ <div class="ss_header__actions">{e.actions}</div>
220
+ </div>}import{Show as U,createEffect as Ae,createSignal as te,createUniqueId as ze,onCleanup as W,onMount as $e}from"solid-js";import{Portal as Ne}from"solid-js/web";var Ke=180;function $(e){let[t,l]=te(e.open),[i,r]=te("closed"),o=ze(),h,c,f;Ae(()=>{h&&clearTimeout(h),c&&cancelAnimationFrame(c),e.open?(t()||l(!0),r("closed"),c=requestAnimationFrame(()=>r("open"))):(r("closed"),t()&&(h=window.setTimeout(()=>l(!1),Ke)))}),$e(()=>{if(!e.open)return;if(e.lockScroll!==!1){let s=document.body.style.overflowY;document.body.style.overflowY="hidden",W(()=>{document.body.style.overflowY=s})}c=requestAnimationFrame(()=>f?.focus());let d=s=>{s.key==="Escape"&&e.dismissible!==!1&&e.onClose?.()};window.addEventListener("keydown",d),W(()=>window.removeEventListener("keydown",d))}),W(()=>{h&&clearTimeout(h),c&&cancelAnimationFrame(c)});let a=()=>{e.dismissible!==!1&&e.onClose?.()};return<U when={t()}>
221
+ <Ne>
222
+ <div class="ss_modal"data-state={i()}aria-hidden={i()==="closed"}>
223
+ <div class="ss_modal__backdrop"onclick={a}/>
224
+ <div class="ss_modal__panel"classList={{"ss_modal__panel--sm":e.size==="sm","ss_modal__panel--lg":e.size==="lg","ss_modal__panel--fullscreen":e.fullscreen,"ss_modal__panel--no-fullscreen":e.disableResponsiveFullscreen}}ref={d=>f=d}role="dialog"aria-modal="true"aria-labelledby={e.title?o:void 0}tabindex="-1">
225
+ {(e.title||e.onClose)&&<div class="ss_modal__header">
226
+ {e.title&&<h2 id={o}class="ss_modal__title">
227
+ {e.title}
1266
228
  </h2>}
1267
- <Show3 when={props.onClose}>
1268
- <SSButton
1269
- type="button"
1270
- class="ss_modal__close"
1271
- isIconOnly
1272
- ariaLabel="Dialog schließen"
1273
- onclick={() => {
1274
- if (props.dismissible === false) return;
1275
- props.onClose?.();
1276
- }}
1277
- >
1278
- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-x"><path stroke="none" d="M0 0h24v24H0z" fill="none" /><path d="M18 6l-12 12" /><path d="M6 6l12 12" /></svg>
1279
- </SSButton>
1280
- </Show3>
229
+ <U when={e.onClose}>
230
+ <F type="button"class="ss_modal__close"isIconOnly ariaLabel="Dialog schließen"onclick={()=>{e.dismissible!==!1&&e.onClose?.()}}>
231
+ <svg xmlns="http://www.w3.org/2000/svg"width="24"height="24"viewBox="0 0 24 24"fill="none"stroke="currentColor"stroke-width="2"stroke-linecap="round"stroke-linejoin="round"class="icon icon-tabler icons-tabler-outline icon-tabler-x"><path stroke="none"d="M0 0h24v24H0z"fill="none"/><path d="M18 6l-12 12"/><path d="M6 6l12 12"/></svg>
232
+ </F>
233
+ </U>
1281
234
  </div>}
1282
235
 
1283
236
  <div class="ss_modal__body">
1284
- <div class="ss_modal__body_inner">{props.children}</div>
237
+ <div class="ss_modal__body_inner">{e.children}</div>
1285
238
  </div>
1286
239
 
1287
- <Show3 when={props.footer}>
1288
- <div class="ss_modal__footer">{props.footer}</div>
1289
- </Show3>
240
+ <U when={e.footer}>
241
+ <div class="ss_modal__footer">{e.footer}</div>
242
+ </U>
1290
243
  </div>
1291
244
  </div>
1292
- </Portal>
1293
- </Show3>;
1294
- }
1295
-
1296
- // src/components/SSModals.tsx
1297
- import { createContext as createContext2, createSignal as createSignal7, For as For4, onCleanup as onCleanup5, useContext as useContext2 } from "solid-js";
1298
- import { useLocation, useNavigate } from "@solidjs/router";
1299
- var SSModalsContext = createContext2();
1300
- var modalCounter = 0;
1301
- var nextModalId = () => `ss-modal-${modalCounter++}`;
1302
- function useSSModals() {
1303
- const context = useContext2(SSModalsContext);
1304
- if (!context) {
1305
- throw new Error("useSSModals must be used within SSModalsProvider");
1306
- }
1307
- return context;
1308
- }
1309
- function DefaultModal(props) {
1310
- const navigate = useNavigate();
1311
- const location = useLocation();
1312
- const [loading, process] = createLoading();
1313
- const modalProps = () => props.config.modalProps?.({ hide: props.hide }) ?? {};
1314
- const {
1315
- primaryButtonText,
1316
- secondaryButtonText,
1317
- hideSecondaryButton,
1318
- danger,
1319
- ...rest
1320
- } = modalProps();
1321
- return <SSModal
1322
- open={props.visible()}
1323
- onClose={props.hide}
1324
- {...rest}
1325
- footer={<>
1326
- {!hideSecondaryButton && <SSButton class="secondary" onclick={props.hide}>
1327
- {secondaryButtonText ?? "Abbrechen"}
1328
- </SSButton>}
1329
- <SSButton
1330
- class={danger ? "danger" : void 0}
1331
- onclick={() => process(
1332
- () => props.config.onPrimaryAction?.({
1333
- hide: props.hide,
1334
- navigate,
1335
- pathname: location.pathname
1336
- }) ?? props.hide()
1337
- )}
1338
- disabled={loading()}
1339
- >
1340
- {primaryButtonText ?? "Weiter"}
1341
- </SSButton>
1342
- </>}
1343
- >
1344
- {props.config.content({ hide: props.hide })}
1345
- </SSModal>;
1346
- }
1347
- function FormModal(props) {
1348
- const navigate = useNavigate();
1349
- const location = useLocation();
1350
- return <SSForm
1351
- onsubmit={(context) => props.config.onSubmit({
1352
- hide: props.hide,
1353
- context,
1354
- navigate,
1355
- pathname: location.pathname
1356
- })}
1357
- >
1358
- <FormModalInner visible={props.visible} hide={props.hide} config={props.config} />
1359
- </SSForm>;
1360
- }
1361
- function FormModalInner(props) {
1362
- const context = SSForm.useContext();
1363
- if (!context) return null;
1364
- const modalProps = () => props.config.modalProps?.({ hide: props.hide, context }) ?? {};
1365
- const {
1366
- primaryButtonText,
1367
- secondaryButtonText,
1368
- hideSecondaryButton,
1369
- danger,
1370
- ...rest
1371
- } = modalProps();
1372
- return <SSModal
1373
- open={props.visible()}
1374
- onClose={props.hide}
1375
- {...rest}
1376
- footer={<>
1377
- {!hideSecondaryButton && <SSButton class="secondary" onclick={props.hide} disabled={context.loading()}>
1378
- {secondaryButtonText ?? "Abbrechen"}
1379
- </SSButton>}
1380
- <SSButton
1381
- class={danger ? "danger" : void 0}
1382
- onclick={() => context.submit()}
1383
- disabled={context.loading()}
1384
- >
1385
- {primaryButtonText ?? "Speichern"}
1386
- </SSButton>
1387
- </>}
1388
- >
1389
- {props.config.content({ hide: props.hide, context })}
1390
- </SSModal>;
1391
- }
1392
- function SSModalsProvider(props) {
1393
- const [modals, setModals] = createSignal7([]);
1394
- const modalsById = /* @__PURE__ */ new Map();
1395
- const closeTimeouts = /* @__PURE__ */ new Map();
1396
- const removeDelayMs = 220;
1397
- const hide = (id) => {
1398
- const modal = modalsById.get(id);
1399
- if (!modal) return;
1400
- modal.setVisible(false);
1401
- const existing = closeTimeouts.get(id);
1402
- if (existing) window.clearTimeout(existing);
1403
- const timeout = window.setTimeout(() => {
1404
- setModals((list) => list.filter((modal2) => modal2.id !== id));
1405
- modalsById.delete(id);
1406
- closeTimeouts.delete(id);
1407
- }, removeDelayMs);
1408
- closeTimeouts.set(id, timeout);
1409
- };
1410
- const show = (render) => {
1411
- const id = nextModalId();
1412
- const [visible, setVisible] = createSignal7(true);
1413
- const entry = { id, visible, setVisible, render };
1414
- modalsById.set(id, entry);
1415
- setModals((list) => [...list, entry]);
1416
- return id;
1417
- };
1418
- const showDefault = (config) => {
1419
- return show(({ hide: hide2, visible }) => <DefaultModal visible={visible} hide={hide2} config={config} />);
1420
- };
1421
- const showForm = (config) => {
1422
- return show(({ hide: hide2, visible }) => <FormModal visible={visible} hide={hide2} config={config} />);
1423
- };
1424
- onCleanup5(() => {
1425
- closeTimeouts.forEach((timeout) => window.clearTimeout(timeout));
1426
- closeTimeouts.clear();
1427
- });
1428
- return <SSModalsContext.Provider value={{ show, showDefault, showForm, hide }}>
1429
- {props.children}
1430
-
1431
- <For4 each={modals()}>
1432
- {(modal) => {
1433
- const hideModal = () => hide(modal.id);
1434
- return modal.render({ id: modal.id, hide: hideModal, visible: modal.visible });
1435
- }}
1436
- </For4>
1437
- </SSModalsContext.Provider>;
1438
- }
1439
-
1440
- // src/components/SSShell.tsx
1441
- import { createContext as createContext3, createMemo as createMemo4, createSignal as createSignal8, createUniqueId as createUniqueId3, onCleanup as onCleanup6, onMount as onMount4, useContext as useContext3 } from "solid-js";
1442
- import { useLocation as useLocation2 } from "@solidjs/router";
1443
- var SSShellContext = createContext3();
1444
- function SSShell(props) {
1445
- const drawerId = createUniqueId3();
1446
- const location = useLocation2();
1447
- const [hrefs, setHrefs] = createSignal8([]);
1448
- const closeDrawer = () => {
1449
- const input = document.getElementById(drawerId);
1450
- if (input) input.checked = false;
1451
- };
1452
- const registerHref = (href) => {
1453
- setHrefs((prev) => prev.includes(href) ? prev : [...prev, href]);
1454
- };
1455
- const unregisterHref = (href) => {
1456
- setHrefs((prev) => prev.filter((item) => item !== href));
1457
- };
1458
- const activeHref = createMemo4(() => {
1459
- const path = location.pathname;
1460
- let best = null;
1461
- for (const href of hrefs()) {
1462
- if (!path.startsWith(href)) continue;
1463
- if (!best || href.length > best.length) {
1464
- best = href;
1465
- }
1466
- }
1467
- return best;
1468
- });
1469
- return <SSShellContext.Provider value={{ closeDrawer, activeHref, registerHref, unregisterHref }}>
1470
- <div class={`ss_shell ${props.class ?? ""}`} style={props.style}>
1471
- <input id={drawerId} type="checkbox" class="ss_shell__drawer_toggle_input" />
245
+ </Ne>
246
+ </U>}import{createContext as Ye,createSignal as ne,For as Ue,onCleanup as We,useContext as Ze}from"solid-js";import{useLocation as oe,useNavigate as se}from"@solidjs/router";var ie=Ye(),Ge=0,Qe=()=>`ss-modal-${Ge++}`;function et(){let e=Ze(ie);if(!e)throw new Error("useSSModals must be used within SSModalsProvider");return e}function _t(e){let t=se(),l=oe(),[i,r]=A(),o=()=>e.config.modalProps?.({hide:e.hide})??{},{primaryButtonText:h,secondaryButtonText:c,hideSecondaryButton:f,danger:a,...d}=o();return<$ open={e.visible()}onClose={e.hide}{...d}footer={<>
247
+ {!f&&<F class="secondary"onclick={e.hide}>
248
+ {c??"Abbrechen"}
249
+ </F>}
250
+ <F class={a?"danger":void 0}onclick={()=>r(()=>e.config.onPrimaryAction?.({hide:e.hide,navigate:t,pathname:l.pathname})??e.hide())}disabled={i()}>
251
+ {h??"Weiter"}
252
+ </F>
253
+ </>}>
254
+ {e.config.content({hide:e.hide})}
255
+ </$>}function St(e){let t=se(),l=oe();return<C onsubmit={i=>e.config.onSubmit({hide:e.hide,context:i,navigate:t,pathname:l.pathname})}>
256
+ <Mt visible={e.visible}hide={e.hide}config={e.config}/>
257
+ </C>}function Mt(e){let t=C.useContext();if(!t)return null;let l=()=>e.config.modalProps?.({hide:e.hide,context:t})??{},{primaryButtonText:i,secondaryButtonText:r,hideSecondaryButton:o,danger:h,...c}=l();return<$ open={e.visible()}onClose={e.hide}{...c}footer={<>
258
+ {!o&&<F class="secondary"onclick={e.hide}disabled={t.loading()}>
259
+ {r??"Abbrechen"}
260
+ </F>}
261
+ <F class={h?"danger":void 0}onclick={()=>t.submit()}disabled={t.loading()}>
262
+ {i??"Speichern"}
263
+ </F>
264
+ </>}>
265
+ {e.config.content({hide:e.hide,context:t})}
266
+ </$>}function Et(e){let[t,l]=ne([]),i=new Map,r=new Map,o=220,h=d=>{let s=i.get(d);if(!s)return;s.setVisible(!1);let v=r.get(d);v&&window.clearTimeout(v);let _=window.setTimeout(()=>{l(y=>y.filter(M=>M.id!==d)),i.delete(d),r.delete(d)},o);r.set(d,_)},c=d=>{let s=Qe(),[v,_]=ne(!0),y={id:s,visible:v,setVisible:_,render:d};return i.set(s,y),l(M=>[...M,y]),s},f=d=>c(({hide:s,visible:v})=><_t visible={v}hide={s}config={d}/>),a=d=>c(({hide:s,visible:v})=><St visible={v}hide={s}config={d}/>);return We(()=>{r.forEach(d=>window.clearTimeout(d)),r.clear()}),<ie.Provider value={{show:c,showDefault:f,showForm:a,hide:h}}>
267
+ {e.children}
268
+
269
+ <Ue each={t()}>
270
+ {d=>{let s=()=>h(d.id);return d.render({id:d.id,hide:s,visible:d.visible})}}
271
+ </Ue>
272
+ </ie.Provider>}import{createContext as Tt,createMemo as Pt,createSignal as Ct,createUniqueId as Dt,onCleanup as Ft,onMount as Lt,useContext as le}from"solid-js";import{useLocation as It}from"@solidjs/router";var Z=Tt();function R(e){let t=Dt(),l=It(),[i,r]=Ct([]),o=()=>{let a=document.getElementById(t);a&&(a.checked=!1)},h=a=>{r(d=>d.includes(a)?d:[...d,a])},c=a=>{r(d=>d.filter(s=>s!==a))},f=Pt(()=>{let a=l.pathname,d=null;for(let s of i())a.startsWith(s)&&(!d||s.length>d.length)&&(d=s);return d});return<Z.Provider value={{closeDrawer:o,activeHref:f,registerHref:h,unregisterHref:c}}>
273
+ <div class={`ss_shell ${e.class??""}`}style={e.style}>
274
+ <input id={t}type="checkbox"class="ss_shell__drawer_toggle_input"/>
1472
275
 
1473
276
  <header class="ss_shell__header">
1474
277
  <div class="ss_shell__header_left">
1475
- <label
1476
- for={drawerId}
1477
- class="ss_shell__drawer_toggle ss_button ss_button--icon"
1478
- aria-label="Navigation öffnen"
1479
- role="button"
1480
- tabindex="0"
1481
- >
1482
- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-menu-2"><path stroke="none" d="M0 0h24v24H0z" fill="none" /><path d="M4 6l16 0" /><path d="M4 12l16 0" /><path d="M4 18l16 0" /></svg>
278
+ <label for={t}class="ss_shell__drawer_toggle ss_button ss_button--icon"aria-label="Navigation öffnen"role="button"tabindex="0">
279
+ <svg xmlns="http://www.w3.org/2000/svg"width="24"height="24"viewBox="0 0 24 24"fill="none"stroke="currentColor"stroke-width="2"stroke-linecap="round"stroke-linejoin="round"class="icon icon-tabler icons-tabler-outline icon-tabler-menu-2"><path stroke="none"d="M0 0h24v24H0z"fill="none"/><path d="M4 6l16 0"/><path d="M4 12l16 0"/><path d="M4 18l16 0"/></svg>
1483
280
  </label>
1484
- <div class="ss_shell__title">{props.title}</div>
281
+ <div class="ss_shell__title">{e.title}</div>
1485
282
  </div>
1486
- <div class="ss_shell__actions">{props.actions}</div>
283
+ <div class="ss_shell__actions">{e.actions}</div>
1487
284
  </header>
1488
285
 
1489
286
  <div class="ss_shell__body">
1490
- <nav class="ss_shell__nav" aria-label="Hauptnavigation">
1491
- <div class="ss_shell__nav_inner">{props.nav}</div>
287
+ <nav class="ss_shell__nav"aria-label="Hauptnavigation">
288
+ <div class="ss_shell__nav_inner">{e.nav}</div>
1492
289
  </nav>
1493
- <div class="ss_shell__main">{props.children}</div>
1494
- <label for={drawerId} class="ss_shell__scrim" aria-label="Navigation schließen" />
290
+ <div class="ss_shell__main">{e.children}</div>
291
+ <label for={t}class="ss_shell__scrim"aria-label="Navigation schließen"/>
1495
292
  </div>
1496
293
  </div>
1497
- </SSShellContext.Provider>;
1498
- }
1499
- SSShell.Nav = function(props) {
1500
- return <div class="ss_shell__nav_list">{props.children}</div>;
1501
- };
1502
- SSShell.NavLink = function(props) {
1503
- const context = useContext3(SSShellContext);
1504
- onMount4(() => context?.registerHref(props.href));
1505
- onCleanup6(() => context?.unregisterHref(props.href));
1506
- const isActive = () => context?.activeHref() === props.href;
1507
- return <a
1508
- class="ss_shell__nav_item"
1509
- classList={{ "ss_shell__nav_item--active": isActive() }}
1510
- href={props.href}
1511
- onclick={() => {
1512
- props.onclick?.();
1513
- context?.closeDrawer();
1514
- }}
1515
- >
1516
- {props.icon && <span class="ss_shell__nav_icon">{props.icon}</span>}
1517
- <span class="ss_shell__nav_label">{props.children}</span>
1518
- </a>;
1519
- };
1520
- SSShell.NavAction = function(props) {
1521
- const context = useContext3(SSShellContext);
1522
- return <button
1523
- type="button"
1524
- class="ss_shell__nav_item"
1525
- onclick={() => {
1526
- props.onclick();
1527
- context?.closeDrawer();
1528
- }}
1529
- >
1530
- {props.icon && <span class="ss_shell__nav_icon">{props.icon}</span>}
1531
- <span class="ss_shell__nav_label">{props.children}</span>
1532
- </button>;
1533
- };
1534
- SSShell.NavGroup = function(props) {
1535
- return <details class="ss_shell__nav_group" open={props.initiallyExpanded}>
294
+ </Z.Provider>}R.Nav=function(e){return<div class="ss_shell__nav_list">{e.children}</div>};R.NavLink=function(e){let t=le(Z);Lt(()=>t?.registerHref(e.href)),Ft(()=>t?.unregisterHref(e.href));let l=()=>t?.activeHref()===e.href;return<a class="ss_shell__nav_item"classList={{"ss_shell__nav_item--active":l()}}href={e.href}onclick={()=>{e.onclick?.(),t?.closeDrawer()}}>
295
+ {e.icon&&<span class="ss_shell__nav_icon">{e.icon}</span>}
296
+ <span class="ss_shell__nav_label">{e.children}</span>
297
+ </a>};R.NavAction=function(e){let t=le(Z);return<button type="button"class="ss_shell__nav_item"onclick={()=>{e.onclick(),t?.closeDrawer()}}>
298
+ {e.icon&&<span class="ss_shell__nav_icon">{e.icon}</span>}
299
+ <span class="ss_shell__nav_label">{e.children}</span>
300
+ </button>};R.NavGroup=function(e){return<details class="ss_shell__nav_group"open={e.initiallyExpanded}>
1536
301
  <summary class="ss_shell__nav_group_header">
1537
- {props.icon && <span class="ss_shell__nav_icon">{props.icon}</span>}
1538
- <span class="ss_shell__nav_label">{props.title}</span>
1539
- <span class="ss_shell__nav_group_chevron" aria-hidden="true">
1540
- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="ss_shell__nav_group_chevron_svg"><path stroke="none" d="M0 0h24v24H0z" fill="none" /><path d="M9 6l6 6l-6 6" /></svg>
302
+ {e.icon&&<span class="ss_shell__nav_icon">{e.icon}</span>}
303
+ <span class="ss_shell__nav_label">{e.title}</span>
304
+ <span class="ss_shell__nav_group_chevron"aria-hidden="true">
305
+ <svg xmlns="http://www.w3.org/2000/svg"width="24"height="24"viewBox="0 0 24 24"fill="none"stroke="currentColor"stroke-width="2"stroke-linecap="round"stroke-linejoin="round"class="ss_shell__nav_group_chevron_svg"><path stroke="none"d="M0 0h24v24H0z"fill="none"/><path d="M9 6l6 6l-6 6"/></svg>
1541
306
  </span>
1542
307
  </summary>
1543
- <div class="ss_shell__nav_group_items">{props.children}</div>
1544
- </details>;
1545
- };
1546
-
1547
- // src/components/SSSurface.tsx
1548
- function SSSurface(props) {
1549
- return <div class={`ss_surface ${props.class ?? ""}`} style={props.style}>
1550
- {props.children}
1551
- </div>;
1552
- }
1553
-
1554
- // src/components/SSTile.tsx
1555
- import { A } from "@solidjs/router";
1556
- function SSTile(props) {
1557
- return <div class={`ss_tile ${props.class ?? ""}`} style={props.style}>
308
+ <div class="ss_shell__nav_group_items">{e.children}</div>
309
+ </details>};function Jt(e){return<div class={`ss_surface ${e.class??""}`}style={e.style}>
310
+ {e.children}
311
+ </div>}import{A as Ot}from"@solidjs/router";function _e(e){return<div class={`ss_tile ${e.class??""}`}style={e.style}>
1558
312
  <div class="ss_tile__row">
1559
- {props.icon && <span class="ss_tile__icon">{props.icon}</span>}
313
+ {e.icon&&<span class="ss_tile__icon">{e.icon}</span>}
1560
314
  <div class="ss_tile__content">
1561
- {props.href ? <h5 class="ss_tile__title">
1562
- <A class="ss_tile__link" href={props.href} onclick={props.onLinkClick}>
1563
- {props.title}
1564
- </A>
1565
- </h5> : <h5 class="ss_tile__title">
1566
- <span class="ss_tile__text">{props.title}</span>
315
+ {e.href?<h5 class="ss_tile__title">
316
+ <Ot class="ss_tile__link"href={e.href}onclick={e.onLinkClick}>
317
+ {e.title}
318
+ </Ot>
319
+ </h5>:<h5 class="ss_tile__title">
320
+ <span class="ss_tile__text">{e.title}</span>
1567
321
  </h5>}
1568
- {props.subtitle && <div class="ss_tile__subtitle">{props.subtitle}</div>}
322
+ {e.subtitle&&<div class="ss_tile__subtitle">{e.subtitle}</div>}
1569
323
  </div>
1570
- {props.trailing && <div class="ss_tile__trailing">{props.trailing}</div>}
324
+ {e.trailing&&<div class="ss_tile__trailing">{e.trailing}</div>}
1571
325
  </div>
1572
- </div>;
1573
- }
1574
- function createSSTile(build) {
1575
- return function(props) {
1576
- const built = build(props.data);
1577
- return <SSTile
1578
- {...built}
1579
- onLinkClick={props.onLinkClick ?? built.onLinkClick}
1580
- href={props.noLink ? void 0 : built.href}
1581
- icon={props.noIcon ? void 0 : built.icon}
1582
- />;
1583
- };
1584
- }
1585
- export {
1586
- SSButton,
1587
- SSCallout,
1588
- SSChip,
1589
- SSDataTable,
1590
- SSDropdown,
1591
- SSExpandable,
1592
- SSForm,
1593
- SSHeader,
1594
- SSModal,
1595
- SSModalsProvider,
1596
- SSShell,
1597
- SSSurface,
1598
- SSTile,
1599
- createSSTile,
1600
- useSSModals
1601
- };
326
+ </div>}function Bt(e){return function(t){let l=e(t.data);return<_e{...l}onLinkClick={t.onLinkClick??l.onLinkClick}href={t.noLink?void 0:l.href}icon={t.noIcon?void 0:l.icon}/>}}export{F as SSButton,we as SSCallout,pe as SSChip,Ee as SSDataTable,Fe as SSDropdown,Oe as SSExpandable,C as SSForm,Ve as SSHeader,$ as SSModal,Et as SSModalsProvider,R as SSShell,Jt as SSSurface,_e as SSTile,Bt as createSSTile,et as useSSModals};