@vss-software/ui 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1255 -0
- package/dist/index.d.ts +73 -0
- package/dist/lumen-ui.css +1 -0
- package/dist/lumen-ui.js +3965 -0
- package/package.json +62 -0
package/README.md
ADDED
|
@@ -0,0 +1,1255 @@
|
|
|
1
|
+
# @vss-software/ui
|
|
2
|
+
|
|
3
|
+
Produktuebergreifendes Design-System mit Vue 3 Komponenten fuer die lumen.hr Plattform.
|
|
4
|
+
|
|
5
|
+
Stil: **Professional Calm** — dunkle Sidebar (Navy `#0B1929`), helle Arbeitsflaechen (Gray-50 `#F8FAFC`), Teal-Akzentfarbe (`#00BFA6`), Inter Schrift, WCAG 2.1 AA konform.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @vss-software/ui
|
|
11
|
+
# or
|
|
12
|
+
pnpm add @vss-software/ui
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### Peer Dependencies
|
|
16
|
+
|
|
17
|
+
```json
|
|
18
|
+
{
|
|
19
|
+
"vue": "^3.5.0",
|
|
20
|
+
"vue-router": "^4",
|
|
21
|
+
"tailwindcss": "^4.0.0"
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Setup
|
|
26
|
+
|
|
27
|
+
### Option 1: Vue Plugin (alle Komponenten global registrieren)
|
|
28
|
+
|
|
29
|
+
```js
|
|
30
|
+
import { createApp } from 'vue'
|
|
31
|
+
import LumenUI from '@vss-software/ui'
|
|
32
|
+
import '@vss-software/ui/styles'
|
|
33
|
+
|
|
34
|
+
const app = createApp(App)
|
|
35
|
+
app.use(LumenUI)
|
|
36
|
+
app.mount('#app')
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Option 2: Einzelimport (Tree-Shaking)
|
|
40
|
+
|
|
41
|
+
```js
|
|
42
|
+
import { LuButton, LuCard, LuInput } from '@vss-software/ui'
|
|
43
|
+
import '@vss-software/ui/styles'
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Tailwind Integration
|
|
47
|
+
|
|
48
|
+
Da `@vss-software/ui` auf Tailwind CSS v4 basiert, muss das konsumierende Projekt ebenfalls Tailwind v4 verwenden. Die Styles werden ueber `@vss-software/ui/styles` eingebunden.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Design Tokens
|
|
53
|
+
|
|
54
|
+
### Farben
|
|
55
|
+
|
|
56
|
+
| Token | Hex | Verwendung |
|
|
57
|
+
| ------------ | --------- | ------------------------------------- |
|
|
58
|
+
| `navy-900` | `#0B1929` | Sidebar-Hintergrund, Login-BG |
|
|
59
|
+
| `navy-800` | `#0F2438` | Sidebar Hover-States |
|
|
60
|
+
| `teal-500` | `#00BFA6` | Primaere CTAs, Links, aktive Elemente |
|
|
61
|
+
| `teal-400` | `#26EDCA` | Hover auf dunklem Hintergrund |
|
|
62
|
+
| `teal-700` | `#00897B` | Pressed-States, dunkle Akzente |
|
|
63
|
+
| `lu-bg` | `#F8FAFC` | Seiten-Hintergrund (gray-50) |
|
|
64
|
+
| `lu-surface` | `#FFFFFF` | Karten, Modals, Tabellen |
|
|
65
|
+
| `lu-border` | `#E2E8F0` | Trennlinien, Karten-Raender |
|
|
66
|
+
|
|
67
|
+
### Typografie
|
|
68
|
+
|
|
69
|
+
| Token | Groesse | Verwendung |
|
|
70
|
+
| -------------------- | ------- | ------------------------- |
|
|
71
|
+
| `--lu-text-display` | 30px | Display-Headlines |
|
|
72
|
+
| `--lu-text-h1` | 24px | Seiten-Ueberschriften |
|
|
73
|
+
| `--lu-text-h2` | 20px | Abschnitts-Ueberschriften |
|
|
74
|
+
| `--lu-text-h3` | 16px | Karten-Ueberschriften |
|
|
75
|
+
| `--lu-text-body` | 14px | Fliesstext |
|
|
76
|
+
| `--lu-text-body-sm` | 13px | Kompakter Text |
|
|
77
|
+
| `--lu-text-caption` | 12px | Beschriftungen |
|
|
78
|
+
| `--lu-text-overline` | 11px | Labels, Overlines |
|
|
79
|
+
|
|
80
|
+
Schriftarten: `Inter` (sans-serif), `JetBrains Mono` (monospace).
|
|
81
|
+
|
|
82
|
+
### Spacing
|
|
83
|
+
|
|
84
|
+
4px-Basisraster: `1=4px`, `2=8px`, `3=12px`, `4=16px`, `5=20px`, `6=24px`, `8=32px`, `10=40px`, `12=48px`.
|
|
85
|
+
|
|
86
|
+
### Z-Index Skala
|
|
87
|
+
|
|
88
|
+
| Token | Wert | Verwendung |
|
|
89
|
+
| ------------------ | ---- | -------------------- |
|
|
90
|
+
| `z-header` | 30 | LuHeader |
|
|
91
|
+
| `z-sidebar` | 40 | LuSidebar |
|
|
92
|
+
| `z-dropdown` | 50 | LuDropdown, LuSelect |
|
|
93
|
+
| `z-modal-backdrop` | 60 | Modal-Backdrop |
|
|
94
|
+
| `z-modal` | 70 | LuModal |
|
|
95
|
+
| `z-toast` | 80 | LuToast |
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Komponenten
|
|
100
|
+
|
|
101
|
+
### Layout
|
|
102
|
+
|
|
103
|
+
#### LuAppLayout
|
|
104
|
+
|
|
105
|
+
Haupt-Application-Shell mit Sidebar, Header und scrollbarem Content-Bereich.
|
|
106
|
+
|
|
107
|
+
```vue
|
|
108
|
+
<LuAppLayout
|
|
109
|
+
:nav-items="navGroups"
|
|
110
|
+
:user="{ name: 'Max Mustermann', role: 'HR' }"
|
|
111
|
+
:notification-count="3"
|
|
112
|
+
:current-user-roles="['HR']"
|
|
113
|
+
v-model:sidebar-collapsed="collapsed"
|
|
114
|
+
:user-menu-items="[
|
|
115
|
+
{ label: 'Profil', icon: 'User', action: 'profile' },
|
|
116
|
+
{ label: 'Abmelden', icon: 'LogOut', action: 'logout' },
|
|
117
|
+
]"
|
|
118
|
+
@notifications-click="openNotifications"
|
|
119
|
+
@user-menu-action="handleMenuAction"
|
|
120
|
+
>
|
|
121
|
+
<template #sidebar-logo>
|
|
122
|
+
<img src="/logo.svg" alt="Logo" />
|
|
123
|
+
</template>
|
|
124
|
+
|
|
125
|
+
<template #breadcrumb>
|
|
126
|
+
<LuBreadcrumb :items="[{ label: 'Dashboard', to: '/' }, { label: 'Mitarbeiter' }]" />
|
|
127
|
+
</template>
|
|
128
|
+
|
|
129
|
+
<RouterView />
|
|
130
|
+
</LuAppLayout>
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**Props:**
|
|
134
|
+
|
|
135
|
+
| Prop | Typ | Default | Beschreibung |
|
|
136
|
+
| ------------------- | ----------------------------------------- | ---------------------- | ------------------------------------- |
|
|
137
|
+
| `navItems` | `NavGroup[]` | `[]` | Navigationsgruppen fuer die Sidebar |
|
|
138
|
+
| `user` | `{ name, role?, roleLabel?, avatarSrc? }` | `{ name: 'Benutzer' }` | Aktuelle Benutzerinfo |
|
|
139
|
+
| `notificationCount` | `Number` | `0` | Anzahl ungelesener Benachrichtigungen |
|
|
140
|
+
| `currentUserRoles` | `String[]` | `[]` | Benutzerrollen fuer RBAC-Filterung |
|
|
141
|
+
| `sidebarCollapsed` | `Boolean` | `false` | Sidebar eingeklappt (v-model) |
|
|
142
|
+
| `userMenuItems` | `{ label, icon?, action }[]` | `[]` | Eintraege im Benutzer-Dropdown |
|
|
143
|
+
|
|
144
|
+
**Events:** `notifications-click`, `user-menu-action(action)`, `update:sidebarCollapsed(boolean)`
|
|
145
|
+
|
|
146
|
+
**Slots:** `default` (Content), `sidebar-logo`, `sidebar-bottom`, `breadcrumb`
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
#### LuSidebar
|
|
151
|
+
|
|
152
|
+
Navigations-Sidebar mit RBAC-gefilterter Navigation, Collapse-Modus und responsivem Overlay.
|
|
153
|
+
|
|
154
|
+
```vue
|
|
155
|
+
<LuSidebar
|
|
156
|
+
:items="[
|
|
157
|
+
{
|
|
158
|
+
label: 'Hauptmenue',
|
|
159
|
+
items: [
|
|
160
|
+
{ icon: 'LayoutDashboard', label: 'Dashboard', to: '/' },
|
|
161
|
+
{ icon: 'Users', label: 'Mitarbeiter', to: '/employees', roles: ['HR', 'GF'] },
|
|
162
|
+
{ icon: 'Clock', label: 'Zeiterfassung', to: '/time-tracking' },
|
|
163
|
+
],
|
|
164
|
+
},
|
|
165
|
+
]"
|
|
166
|
+
v-model:open="sidebarOpen"
|
|
167
|
+
v-model:collapsed="sidebarCollapsed"
|
|
168
|
+
:current-user-roles="['HR']"
|
|
169
|
+
>
|
|
170
|
+
<template #logo>
|
|
171
|
+
<img src="/logo.svg" alt="Logo" />
|
|
172
|
+
</template>
|
|
173
|
+
</LuSidebar>
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
**Props:**
|
|
177
|
+
|
|
178
|
+
| Prop | Typ | Default | Beschreibung |
|
|
179
|
+
| ------------------ | ------------ | ------- | ---------------------------------------- |
|
|
180
|
+
| `items` | `NavGroup[]` | `[]` | Navigationsgruppen mit Items |
|
|
181
|
+
| `open` | `Boolean` | `false` | Sichtbarkeit auf Mobile/Tablet (v-model) |
|
|
182
|
+
| `collapsed` | `Boolean` | `false` | Icons-Only-Modus auf Desktop (v-model) |
|
|
183
|
+
| `currentUserRoles` | `String[]` | `[]` | Rollen fuer RBAC-Filterung |
|
|
184
|
+
|
|
185
|
+
**Events:** `update:open(boolean)`, `update:collapsed(boolean)`
|
|
186
|
+
|
|
187
|
+
**Slots:** `logo`, `bottom`
|
|
188
|
+
|
|
189
|
+
**NavGroup-Format:**
|
|
190
|
+
|
|
191
|
+
```js
|
|
192
|
+
{
|
|
193
|
+
label: 'Gruppenname',
|
|
194
|
+
items: [
|
|
195
|
+
{ icon: 'LucideIconName', label: 'Link-Text', to: '/route', roles: ['HR'] }
|
|
196
|
+
]
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Items mit `roles`-Array werden nur angezeigt, wenn der Benutzer mindestens eine passende Rolle hat.
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
#### LuHeader
|
|
205
|
+
|
|
206
|
+
Applikations-Header (64px) mit Hamburger-Toggle, Benachrichtigungen und Benutzer-Dropdown.
|
|
207
|
+
|
|
208
|
+
```vue
|
|
209
|
+
<LuHeader
|
|
210
|
+
:user="{ name: 'Max Mustermann', roleLabel: 'HR-Manager', avatarSrc: '/avatar.jpg' }"
|
|
211
|
+
:notification-count="5"
|
|
212
|
+
:pending-corrections-count="2"
|
|
213
|
+
:user-menu-items="menuItems"
|
|
214
|
+
@toggle-sidebar="toggleSidebar"
|
|
215
|
+
@notifications-click="openNotifications"
|
|
216
|
+
@corrections-click="openCorrections"
|
|
217
|
+
@user-menu-action="handleAction"
|
|
218
|
+
>
|
|
219
|
+
<template #breadcrumb>
|
|
220
|
+
<LuBreadcrumb />
|
|
221
|
+
</template>
|
|
222
|
+
</LuHeader>
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
**Props:**
|
|
226
|
+
|
|
227
|
+
| Prop | Typ | Default | Beschreibung |
|
|
228
|
+
| ------------------------- | ----------------------------------------- | ---------------------- | ------------------------------------- |
|
|
229
|
+
| `user` | `{ name, role?, roleLabel?, avatarSrc? }` | `{ name: 'Benutzer' }` | Benutzerinfo fuer Avatar und Dropdown |
|
|
230
|
+
| `notificationCount` | `Number` | `0` | Badge-Zaehler (max "99+") |
|
|
231
|
+
| `pendingCorrectionsCount` | `Number` | `0` | Amber Badge fuer offene Korrekturen |
|
|
232
|
+
| `userMenuItems` | `{ label, icon?, action }[]` | `[]` | Dropdown-Eintraege |
|
|
233
|
+
|
|
234
|
+
**Events:** `toggle-sidebar`, `notifications-click`, `corrections-click`, `user-menu-action(action)`
|
|
235
|
+
|
|
236
|
+
**Slots:** `breadcrumb`
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
#### LuBreadcrumb
|
|
241
|
+
|
|
242
|
+
Breadcrumb-Navigation — liest Items aus Props oder automatisch aus `route.meta.breadcrumb`.
|
|
243
|
+
|
|
244
|
+
```vue
|
|
245
|
+
<LuBreadcrumb
|
|
246
|
+
:items="[
|
|
247
|
+
{ label: 'Dashboard', to: '/' },
|
|
248
|
+
{ label: 'Mitarbeiter', to: '/employees' },
|
|
249
|
+
{ label: 'Max Mustermann' },
|
|
250
|
+
]"
|
|
251
|
+
/>
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
**Props:**
|
|
255
|
+
|
|
256
|
+
| Prop | Typ | Default | Beschreibung |
|
|
257
|
+
| ------- | ------------------ | ----------- | ----------------------------------------------------------------- |
|
|
258
|
+
| `items` | `{ label, to? }[]` | `undefined` | Breadcrumb-Pfad. Ohne Prop wird `route.meta.breadcrumb` verwendet |
|
|
259
|
+
|
|
260
|
+
Das letzte Item wird als aktuelle Seite dargestellt (`aria-current="page"`).
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
### Formulare
|
|
265
|
+
|
|
266
|
+
#### LuInput
|
|
267
|
+
|
|
268
|
+
Vielseitiges Textfeld mit Label, Fehler/Hinweis, Prefix/Suffix und Passwort-Toggle.
|
|
269
|
+
|
|
270
|
+
```vue
|
|
271
|
+
<LuInput
|
|
272
|
+
v-model="email"
|
|
273
|
+
type="email"
|
|
274
|
+
label="E-Mail-Adresse"
|
|
275
|
+
placeholder="name@firma.de"
|
|
276
|
+
prefix="@"
|
|
277
|
+
required
|
|
278
|
+
:error="errors.email"
|
|
279
|
+
hint="Ihre geschaeftliche E-Mail-Adresse"
|
|
280
|
+
/>
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
**Props:**
|
|
284
|
+
|
|
285
|
+
| Prop | Typ | Default | Beschreibung |
|
|
286
|
+
| ------------- | --------------------------------------------------------- | -------- | ------------------------------------ |
|
|
287
|
+
| `modelValue` | `String \| Number` | `''` | Eingabewert (v-model) |
|
|
288
|
+
| `type` | `'text' \| 'password' \| 'number' \| 'email' \| 'search'` | `'text'` | Input-Typ |
|
|
289
|
+
| `label` | `String` | `''` | Label-Text |
|
|
290
|
+
| `placeholder` | `String` | `''` | Platzhaltertext |
|
|
291
|
+
| `error` | `String` | `''` | Fehlermeldung (aktiviert roten Rand) |
|
|
292
|
+
| `hint` | `String` | `''` | Hinweistext (versteckt bei Fehler) |
|
|
293
|
+
| `prefix` | `String` | `''` | Prefix-Text links im Input |
|
|
294
|
+
| `suffix` | `String` | `''` | Suffix-Text rechts im Input |
|
|
295
|
+
| `disabled` | `Boolean` | `false` | Deaktiviert |
|
|
296
|
+
| `readonly` | `Boolean` | `false` | Schreibgeschuetzt |
|
|
297
|
+
| `required` | `Boolean` | `false` | Pflichtfeld (roter Stern) |
|
|
298
|
+
|
|
299
|
+
**Expose:** `{ focus }` — Fokus programmatisch setzen.
|
|
300
|
+
|
|
301
|
+
Zusaetzliche HTML-Attribute (`min`, `max`, `step`, `pattern`, `maxlength`) werden an das native `<input>` weitergeleitet.
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
#### LuTextarea
|
|
306
|
+
|
|
307
|
+
Mehrzeiliges Textfeld mit Label, Fehler/Hinweis und konfigurierbarer Groessenanpassung.
|
|
308
|
+
|
|
309
|
+
```vue
|
|
310
|
+
<LuTextarea
|
|
311
|
+
v-model="notes"
|
|
312
|
+
label="Notizen"
|
|
313
|
+
placeholder="Optionale Anmerkungen..."
|
|
314
|
+
:rows="5"
|
|
315
|
+
resize="vertical"
|
|
316
|
+
:error="errors.notes"
|
|
317
|
+
/>
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
**Props:**
|
|
321
|
+
|
|
322
|
+
| Prop | Typ | Default | Beschreibung |
|
|
323
|
+
| ------------- | -------------------------------- | ------------ | -------------------- |
|
|
324
|
+
| `modelValue` | `String` | `''` | Textwert (v-model) |
|
|
325
|
+
| `label` | `String` | `''` | Label-Text |
|
|
326
|
+
| `placeholder` | `String` | `''` | Platzhaltertext |
|
|
327
|
+
| `error` | `String` | `''` | Fehlermeldung |
|
|
328
|
+
| `hint` | `String` | `''` | Hinweistext |
|
|
329
|
+
| `disabled` | `Boolean` | `false` | Deaktiviert |
|
|
330
|
+
| `readonly` | `Boolean` | `false` | Schreibgeschuetzt |
|
|
331
|
+
| `required` | `Boolean` | `false` | Pflichtfeld |
|
|
332
|
+
| `rows` | `Number \| String` | `3` | Sichtbare Textzeilen |
|
|
333
|
+
| `resize` | `'none' \| 'vertical' \| 'both'` | `'vertical'` | CSS-Resize-Verhalten |
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
#### LuSelect
|
|
338
|
+
|
|
339
|
+
Dropdown-Select mit Suche, Mehrfachauswahl und gruppierten Optionen.
|
|
340
|
+
|
|
341
|
+
```vue
|
|
342
|
+
<!-- Einfache Auswahl -->
|
|
343
|
+
<LuSelect
|
|
344
|
+
v-model="department"
|
|
345
|
+
label="Abteilung"
|
|
346
|
+
:options="[
|
|
347
|
+
{ value: 'hr', label: 'Personal' },
|
|
348
|
+
{ value: 'it', label: 'IT' },
|
|
349
|
+
{ value: 'sales', label: 'Vertrieb' },
|
|
350
|
+
]"
|
|
351
|
+
searchable
|
|
352
|
+
/>
|
|
353
|
+
|
|
354
|
+
<!-- Mehrfachauswahl mit Gruppen -->
|
|
355
|
+
<LuSelect
|
|
356
|
+
v-model="selectedRoles"
|
|
357
|
+
label="Rollen"
|
|
358
|
+
multiple
|
|
359
|
+
searchable
|
|
360
|
+
:options="[
|
|
361
|
+
{
|
|
362
|
+
group: 'Management',
|
|
363
|
+
items: [
|
|
364
|
+
{ value: 'GF', label: 'Geschaeftsfuehrung' },
|
|
365
|
+
{ value: 'HR', label: 'Personal' },
|
|
366
|
+
],
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
group: 'Operativ',
|
|
370
|
+
items: [
|
|
371
|
+
{ value: 'TEAMLEITUNG', label: 'Teamleitung' },
|
|
372
|
+
{ value: 'MITARBEITER', label: 'Mitarbeiter' },
|
|
373
|
+
],
|
|
374
|
+
},
|
|
375
|
+
]"
|
|
376
|
+
/>
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
**Props:**
|
|
380
|
+
|
|
381
|
+
| Prop | Typ | Default | Beschreibung |
|
|
382
|
+
| ------------- | --------- | -------------------- | ---------------------------------------------------------------- |
|
|
383
|
+
| `modelValue` | `any` | `null` | Ausgewaehlter Wert (v-model) |
|
|
384
|
+
| `options` | `Array` | `[]` | Flach: `[{ value, label }]` oder gruppiert: `[{ group, items }]` |
|
|
385
|
+
| `label` | `String` | `''` | Label-Text |
|
|
386
|
+
| `error` | `String` | `''` | Fehlermeldung |
|
|
387
|
+
| `placeholder` | `String` | `'Bitte waehlen...'` | Platzhaltertext |
|
|
388
|
+
| `searchable` | `Boolean` | `false` | Suchfeld aktivieren |
|
|
389
|
+
| `multiple` | `Boolean` | `false` | Mehrfachauswahl mit Chips |
|
|
390
|
+
| `disabled` | `Boolean` | `false` | Deaktiviert |
|
|
391
|
+
|
|
392
|
+
Multi-Select zeigt ausgewaehlte Werte als Teal-Chips mit Entfernen-Button. Suche ist akzent-unabhaengig (NFD-Normalisierung).
|
|
393
|
+
|
|
394
|
+
---
|
|
395
|
+
|
|
396
|
+
#### LuDatePicker
|
|
397
|
+
|
|
398
|
+
Datumseingabe mit Kalender-Overlay, Monatsnavigation und optionalem Range-Highlighting.
|
|
399
|
+
|
|
400
|
+
```vue
|
|
401
|
+
<LuDatePicker
|
|
402
|
+
v-model="startDate"
|
|
403
|
+
label="Startdatum"
|
|
404
|
+
placeholder="TT.MM.JJJJ"
|
|
405
|
+
required
|
|
406
|
+
:range-start="rangeStart"
|
|
407
|
+
:range-end="rangeEnd"
|
|
408
|
+
/>
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
**Props:**
|
|
412
|
+
|
|
413
|
+
| Prop | Typ | Default | Beschreibung |
|
|
414
|
+
| ------------- | --------- | -------------- | --------------------------------------- |
|
|
415
|
+
| `modelValue` | `String` | `''` | ISO-Datumsstring YYYY-MM-DD (v-model) |
|
|
416
|
+
| `label` | `String` | `''` | Label-Text |
|
|
417
|
+
| `placeholder` | `String` | `'TT.MM.JJJJ'` | Platzhaltertext |
|
|
418
|
+
| `error` | `String` | `''` | Fehlermeldung |
|
|
419
|
+
| `disabled` | `Boolean` | `false` | Deaktiviert |
|
|
420
|
+
| `required` | `Boolean` | `false` | Pflichtfeld |
|
|
421
|
+
| `rangeStart` | `String` | `''` | ISO-Datum fuer Range-Start-Hervorhebung |
|
|
422
|
+
| `rangeEnd` | `String` | `''` | ISO-Datum fuer Range-Ende-Hervorhebung |
|
|
423
|
+
|
|
424
|
+
Deutsche Lokalisierung (date-fns `de`). Woche beginnt am Montag. Anzeige als `dd.MM.yyyy`.
|
|
425
|
+
|
|
426
|
+
---
|
|
427
|
+
|
|
428
|
+
#### LuCheckbox
|
|
429
|
+
|
|
430
|
+
Checkbox mit Boolean- und Array-Modus sowie Indeterminate-State.
|
|
431
|
+
|
|
432
|
+
```vue
|
|
433
|
+
<!-- Boolean-Modus -->
|
|
434
|
+
<LuCheckbox v-model="accepted" label="AGB akzeptieren" />
|
|
435
|
+
|
|
436
|
+
<!-- Array-Modus (Mehrfachauswahl) -->
|
|
437
|
+
<LuCheckbox v-model="selectedDays" value="MO" label="Montag" />
|
|
438
|
+
<LuCheckbox v-model="selectedDays" value="DI" label="Dienstag" />
|
|
439
|
+
|
|
440
|
+
<!-- Indeterminate ("Alle auswaehlen") -->
|
|
441
|
+
<LuCheckbox v-model="allSelected" :indeterminate="partiallySelected" label="Alle" />
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
**Props:**
|
|
445
|
+
|
|
446
|
+
| Prop | Typ | Default | Beschreibung |
|
|
447
|
+
| --------------- | ------------------ | ----------- | --------------------------- |
|
|
448
|
+
| `modelValue` | `Boolean \| Array` | `false` | Checked-Status (v-model) |
|
|
449
|
+
| `value` | `String \| Number` | `undefined` | Wert im Array-Modus |
|
|
450
|
+
| `label` | `String` | `''` | Label-Text |
|
|
451
|
+
| `disabled` | `Boolean` | `false` | Deaktiviert |
|
|
452
|
+
| `indeterminate` | `Boolean` | `false` | Zeigt Strich statt Haekchen |
|
|
453
|
+
|
|
454
|
+
---
|
|
455
|
+
|
|
456
|
+
#### LuRadio
|
|
457
|
+
|
|
458
|
+
Radio-Button fuer Einzelauswahl in Gruppen.
|
|
459
|
+
|
|
460
|
+
```vue
|
|
461
|
+
<LuRadio v-model="status" value="active" label="Aktiv" name="status" />
|
|
462
|
+
<LuRadio v-model="status" value="inactive" label="Inaktiv" name="status" />
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
**Props:**
|
|
466
|
+
|
|
467
|
+
| Prop | Typ | Default | Beschreibung |
|
|
468
|
+
| ------------ | ----------------------------- | ------- | --------------------------------------- |
|
|
469
|
+
| `modelValue` | `String \| Number \| Boolean` | `''` | Ausgewaehlter Wert der Gruppe (v-model) |
|
|
470
|
+
| `value` | `String \| Number \| Boolean` | — | Wert dieser Radio-Option |
|
|
471
|
+
| `label` | `String` | `''` | Label-Text |
|
|
472
|
+
| `name` | `String` | `''` | Name-Attribut fuer Gruppierung |
|
|
473
|
+
| `disabled` | `Boolean` | `false` | Deaktiviert |
|
|
474
|
+
|
|
475
|
+
---
|
|
476
|
+
|
|
477
|
+
#### LuToggle
|
|
478
|
+
|
|
479
|
+
Toggle-Switch mit Slide-Animation.
|
|
480
|
+
|
|
481
|
+
```vue
|
|
482
|
+
<LuToggle v-model="darkMode" label="Dunkelmodus" />
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
**Props:**
|
|
486
|
+
|
|
487
|
+
| Prop | Typ | Default | Beschreibung |
|
|
488
|
+
| ------------ | --------- | ------- | ----------------------- |
|
|
489
|
+
| `modelValue` | `Boolean` | `false` | Toggle-Status (v-model) |
|
|
490
|
+
| `label` | `String` | `''` | Label-Text |
|
|
491
|
+
| `disabled` | `Boolean` | `false` | Deaktiviert |
|
|
492
|
+
|
|
493
|
+
---
|
|
494
|
+
|
|
495
|
+
#### LuFormGroup
|
|
496
|
+
|
|
497
|
+
Container fuer konsistentes Label-, Fehler- und Hinweis-Layout um Formularfelder.
|
|
498
|
+
|
|
499
|
+
```vue
|
|
500
|
+
<LuFormGroup label="Abteilung" error="Bitte Abteilung waehlen" required>
|
|
501
|
+
<LuSelect v-model="department" :options="departments" />
|
|
502
|
+
</LuFormGroup>
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
**Props:**
|
|
506
|
+
|
|
507
|
+
| Prop | Typ | Default | Beschreibung |
|
|
508
|
+
| ---------- | --------- | ------- | ------------------------------------- |
|
|
509
|
+
| `label` | `String` | `''` | Label-Text (uppercase, tracking-wide) |
|
|
510
|
+
| `error` | `String` | `''` | Fehlermeldung mit AlertCircle-Icon |
|
|
511
|
+
| `hint` | `String` | `''` | Hinweistext (versteckt bei Fehler) |
|
|
512
|
+
| `for` | `String` | `''` | HTML `for`-Attribut |
|
|
513
|
+
| `required` | `Boolean` | `false` | Roter Stern am Label |
|
|
514
|
+
|
|
515
|
+
**Slots:** `default` — Formularfeld(er)
|
|
516
|
+
|
|
517
|
+
---
|
|
518
|
+
|
|
519
|
+
### Datendarstellung
|
|
520
|
+
|
|
521
|
+
#### LuTable
|
|
522
|
+
|
|
523
|
+
Datentabelle mit Sortierung, Zebra-Striping, Loading-Skeleton und Scoped Slots.
|
|
524
|
+
|
|
525
|
+
```vue
|
|
526
|
+
<LuTable
|
|
527
|
+
:columns="[
|
|
528
|
+
{ key: 'name', label: 'Name', sortable: true },
|
|
529
|
+
{ key: 'department', label: 'Abteilung', sortable: true, hideOnMobile: true },
|
|
530
|
+
{ key: 'status', label: 'Status' },
|
|
531
|
+
{ key: 'actions', label: '', width: '80px' },
|
|
532
|
+
]"
|
|
533
|
+
:data="employees"
|
|
534
|
+
:loading="isLoading"
|
|
535
|
+
:sort-field="sortField"
|
|
536
|
+
:sort-dir="sortDir"
|
|
537
|
+
@sort="handleSort"
|
|
538
|
+
@row-click="openEmployee"
|
|
539
|
+
>
|
|
540
|
+
<template #cell-status="{ value }">
|
|
541
|
+
<LuBadge :variant="value === 'active' ? 'success' : 'neutral'">
|
|
542
|
+
{{ value === 'active' ? 'Aktiv' : 'Inaktiv' }}
|
|
543
|
+
</LuBadge>
|
|
544
|
+
</template>
|
|
545
|
+
|
|
546
|
+
<template #cell-actions="{ row }">
|
|
547
|
+
<LuButton variant="ghost" size="sm" @click.stop="editEmployee(row)">
|
|
548
|
+
<template #icon-left><LuIcon name="Pencil" :size="16" /></template>
|
|
549
|
+
</LuButton>
|
|
550
|
+
</template>
|
|
551
|
+
|
|
552
|
+
<template #empty>Keine Mitarbeiter gefunden.</template>
|
|
553
|
+
</LuTable>
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
**Props:**
|
|
557
|
+
|
|
558
|
+
| Prop | Typ | Default | Beschreibung |
|
|
559
|
+
| ----------- | ------------------ | ------- | -------------------------------------------------------- |
|
|
560
|
+
| `columns` | `Column[]` | — | Spaltendefinitionen |
|
|
561
|
+
| `data` | `Object[]` | `[]` | Zeilendaten |
|
|
562
|
+
| `loading` | `Boolean` | `false` | Zeigt 3 Skeleton-Zeilen |
|
|
563
|
+
| `sortField` | `String` | `''` | Aktives Sortierfeld |
|
|
564
|
+
| `sortDir` | `'asc' \| 'desc'` | `'asc'` | Sortierrichtung |
|
|
565
|
+
| `rowKey` | `String` | `'id'` | Property fuer Zeilen-Key (Fallback: `_id`, dann Index) |
|
|
566
|
+
| `rowClass` | `Function \| null` | `null` | `(row, index) => string` fuer individuelle Zeilenklassen |
|
|
567
|
+
|
|
568
|
+
**Column-Definition:**
|
|
569
|
+
|
|
570
|
+
```js
|
|
571
|
+
{ key: 'name', label: 'Name', sortable: true, hideOnMobile: true, width: '200px' }
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
**Events:** `sort(field)`, `row-click(row, index)`
|
|
575
|
+
|
|
576
|
+
**Slots:** `cell-{key}({ row, value })`, `header-{key}({ column })`, `empty`, `row({ row, index })`
|
|
577
|
+
|
|
578
|
+
---
|
|
579
|
+
|
|
580
|
+
#### LuPagination
|
|
581
|
+
|
|
582
|
+
Seitennavigation mit Seiten-Links, Vor/Zurueck und Items-pro-Seite-Auswahl.
|
|
583
|
+
|
|
584
|
+
```vue
|
|
585
|
+
<LuPagination
|
|
586
|
+
:current-page="page"
|
|
587
|
+
:total-pages="totalPages"
|
|
588
|
+
:total-items="487"
|
|
589
|
+
:items-per-page="25"
|
|
590
|
+
@change="page = $event"
|
|
591
|
+
@change-per-page="perPage = $event"
|
|
592
|
+
/>
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
**Props:**
|
|
596
|
+
|
|
597
|
+
| Prop | Typ | Default | Beschreibung |
|
|
598
|
+
| -------------- | -------- | ------- | -------------------------- |
|
|
599
|
+
| `currentPage` | `Number` | — | Aktuelle Seite (1-basiert) |
|
|
600
|
+
| `totalPages` | `Number` | — | Gesamtseitenanzahl |
|
|
601
|
+
| `totalItems` | `Number` | `0` | Gesamtanzahl Eintraege |
|
|
602
|
+
| `itemsPerPage` | `Number` | `10` | Eintraege pro Seite |
|
|
603
|
+
|
|
604
|
+
**Events:** `change(page)`, `change-per-page(perPage)`
|
|
605
|
+
|
|
606
|
+
Zeigt Items-pro-Seite-Selektor (10/25/50), intelligente Seitenwindows mit Ellipsis, und Eintrags-Bereich (z.B. "1-25 von 487 Eintraegen").
|
|
607
|
+
|
|
608
|
+
---
|
|
609
|
+
|
|
610
|
+
### Feedback & Overlay
|
|
611
|
+
|
|
612
|
+
#### LuModal
|
|
613
|
+
|
|
614
|
+
Modaler Dialog mit Backdrop, ESC-Schliessen und Bottom-Sheet auf Mobile.
|
|
615
|
+
|
|
616
|
+
```vue
|
|
617
|
+
<LuModal v-model="showModal" title="Mitarbeiter bearbeiten" size="lg" persistent>
|
|
618
|
+
<form @submit.prevent="save">
|
|
619
|
+
<LuInput v-model="name" label="Name" />
|
|
620
|
+
</form>
|
|
621
|
+
|
|
622
|
+
<template #footer>
|
|
623
|
+
<LuButton variant="secondary" @click="showModal = false">Abbrechen</LuButton>
|
|
624
|
+
<LuButton @click="save" :loading="saving">Speichern</LuButton>
|
|
625
|
+
</template>
|
|
626
|
+
</LuModal>
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
**Props:**
|
|
630
|
+
|
|
631
|
+
| Prop | Typ | Default | Beschreibung |
|
|
632
|
+
| ------------ | ------------------------------ | ------- | ------------------------------------------ |
|
|
633
|
+
| `modelValue` | `Boolean` | — | Sichtbarkeit (v-model) |
|
|
634
|
+
| `title` | `String` | `''` | Titel in der Kopfzeile |
|
|
635
|
+
| `size` | `'sm' \| 'md' \| 'lg' \| 'xl'` | `'md'` | sm=400px, md=560px, lg=768px, xl=1024px |
|
|
636
|
+
| `persistent` | `Boolean` | `false` | Verhindert Schliessen durch Backdrop-Klick |
|
|
637
|
+
|
|
638
|
+
**Slots:** `default` (Body, scrollbar), `footer` (Aktionen)
|
|
639
|
+
|
|
640
|
+
Features: Focus-Trap (Tab/Shift+Tab), Body-Scroll-Lock, Bottom-Sheet unter 768px, `prefers-reduced-motion` Support.
|
|
641
|
+
|
|
642
|
+
Empfehlung: `useModal()` Composable fuer einfache Zustandsverwaltung.
|
|
643
|
+
|
|
644
|
+
---
|
|
645
|
+
|
|
646
|
+
#### LuAlert
|
|
647
|
+
|
|
648
|
+
Inline-Alert fuer kontextbezogene Rueckmeldungen.
|
|
649
|
+
|
|
650
|
+
```vue
|
|
651
|
+
<LuAlert
|
|
652
|
+
variant="warning"
|
|
653
|
+
title="Achtung"
|
|
654
|
+
message="Ihre Sitzung laeuft in 5 Minuten ab."
|
|
655
|
+
dismissible
|
|
656
|
+
@dismiss="hideWarning"
|
|
657
|
+
/>
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
**Props:**
|
|
661
|
+
|
|
662
|
+
| Prop | Typ | Default | Beschreibung |
|
|
663
|
+
| ------------- | --------------------------------------------- | -------- | -------------------------- |
|
|
664
|
+
| `variant` | `'info' \| 'warning' \| 'error' \| 'success'` | `'info'` | Semantische Variante |
|
|
665
|
+
| `title` | `String` | `''` | Optionale fette Titelzeile |
|
|
666
|
+
| `message` | `String` | `''` | Nachrichtentext |
|
|
667
|
+
| `dismissible` | `Boolean` | `false` | Zeigt Schliessen-Button |
|
|
668
|
+
|
|
669
|
+
**Events:** `dismiss`
|
|
670
|
+
|
|
671
|
+
Icons pro Variante: success=CircleCheck, warning=TriangleAlert, error=XCircle, info=Info.
|
|
672
|
+
|
|
673
|
+
---
|
|
674
|
+
|
|
675
|
+
#### LuToast
|
|
676
|
+
|
|
677
|
+
Toast-Benachrichtigungs-Container. Einmal im App-Root einbinden, Steuerung via `useToast()`.
|
|
678
|
+
|
|
679
|
+
```vue
|
|
680
|
+
<!-- In App.vue (einmalig einbinden) -->
|
|
681
|
+
<template>
|
|
682
|
+
<LuAppLayout>
|
|
683
|
+
<RouterView />
|
|
684
|
+
</LuAppLayout>
|
|
685
|
+
<LuToast />
|
|
686
|
+
</template>
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
```js
|
|
690
|
+
// In beliebiger Komponente
|
|
691
|
+
import { useToast } from '@vss-software/ui'
|
|
692
|
+
|
|
693
|
+
const { addToast } = useToast()
|
|
694
|
+
|
|
695
|
+
addToast({
|
|
696
|
+
variant: 'success',
|
|
697
|
+
title: 'Gespeichert',
|
|
698
|
+
message: 'Mitarbeiterdaten wurden aktualisiert.',
|
|
699
|
+
duration: 5000,
|
|
700
|
+
})
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
Teleportiert zu `<body>`. Fixiert unten rechts. Auto-Dismiss nach `duration` (Standard: 5000ms, `0` = kein Auto-Dismiss).
|
|
704
|
+
|
|
705
|
+
---
|
|
706
|
+
|
|
707
|
+
#### LuDropdown
|
|
708
|
+
|
|
709
|
+
Dropdown-Menue mit selektierbaren Optionen, Trennlinien und optionalen Beschreibungen.
|
|
710
|
+
|
|
711
|
+
```vue
|
|
712
|
+
<LuDropdown
|
|
713
|
+
label="Exportieren"
|
|
714
|
+
:options="[
|
|
715
|
+
{ value: 'csv', label: 'CSV-Export', description: 'Komma-getrennte Werte' },
|
|
716
|
+
{ value: 'xlsx', label: 'Excel-Export' },
|
|
717
|
+
{ divider: true },
|
|
718
|
+
{ value: 'datev', label: 'DATEV-Export', icon: 'FileSpreadsheet' },
|
|
719
|
+
]"
|
|
720
|
+
@select="handleExport"
|
|
721
|
+
/>
|
|
722
|
+
```
|
|
723
|
+
|
|
724
|
+
**Props:**
|
|
725
|
+
|
|
726
|
+
| Prop | Typ | Default | Beschreibung |
|
|
727
|
+
| --------- | ------------------------------------- | ------------- | ------------------ |
|
|
728
|
+
| `label` | `String` | — | Button-Label |
|
|
729
|
+
| `options` | `DropdownOption[]` | `[]` | Menue-Optionen |
|
|
730
|
+
| `icon` | `String \| Object` | `null` | Icon vor dem Label |
|
|
731
|
+
| `variant` | `'primary' \| 'secondary' \| 'ghost'` | `'secondary'` | Button-Variante |
|
|
732
|
+
| `size` | `'sm' \| 'md' \| 'lg'` | `'sm'` | Button-Groesse |
|
|
733
|
+
|
|
734
|
+
**DropdownOption:**
|
|
735
|
+
|
|
736
|
+
```js
|
|
737
|
+
{ value: 'id', label: 'Text', description?: 'Zusatzinfo', disabled?: false, icon?: 'LucideName', divider?: false }
|
|
738
|
+
```
|
|
739
|
+
|
|
740
|
+
**Events:** `select(value)`
|
|
741
|
+
|
|
742
|
+
---
|
|
743
|
+
|
|
744
|
+
### Allgemeine UI
|
|
745
|
+
|
|
746
|
+
#### LuButton
|
|
747
|
+
|
|
748
|
+
Vielseitiger Button fuer primaere, sekundaere, Danger-, Ghost- und Icon-Aktionen.
|
|
749
|
+
|
|
750
|
+
```vue
|
|
751
|
+
<LuButton @click="save" :loading="saving">
|
|
752
|
+
<template #icon-left><LuIcon name="Save" :size="16" /></template>
|
|
753
|
+
Speichern
|
|
754
|
+
</LuButton>
|
|
755
|
+
|
|
756
|
+
<LuButton variant="danger" size="sm">Loeschen</LuButton>
|
|
757
|
+
|
|
758
|
+
<LuButton variant="ghost">Abbrechen</LuButton>
|
|
759
|
+
|
|
760
|
+
<LuButton variant="icon" aria-label="Bearbeiten">
|
|
761
|
+
<LuIcon name="Pencil" :size="18" />
|
|
762
|
+
</LuButton>
|
|
763
|
+
```
|
|
764
|
+
|
|
765
|
+
**Props:**
|
|
766
|
+
|
|
767
|
+
| Prop | Typ | Default | Beschreibung |
|
|
768
|
+
| ------------- | ----------------------------------------------------------- | ------------------- | ------------------------------------------- |
|
|
769
|
+
| `variant` | `'primary' \| 'secondary' \| 'danger' \| 'ghost' \| 'icon'` | `'primary'` | Visuelle Variante |
|
|
770
|
+
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | sm=32px, md=40px, lg=48px Hoehe |
|
|
771
|
+
| `disabled` | `Boolean` | `false` | Deaktiviert |
|
|
772
|
+
| `loading` | `Boolean` | `false` | Zeigt Lade-Spinner, deaktiviert Interaktion |
|
|
773
|
+
| `type` | `'button' \| 'submit' \| 'reset'` | `'button'` | HTML button type |
|
|
774
|
+
| `loadingText` | `String` | `'Wird geladen...'` | Screenreader-Ansage bei Loading |
|
|
775
|
+
|
|
776
|
+
**Slots:** `default` (Label), `icon-left`, `icon-right`
|
|
777
|
+
|
|
778
|
+
---
|
|
779
|
+
|
|
780
|
+
#### LuCard
|
|
781
|
+
|
|
782
|
+
Flexible Karte mit Standard- und KPI-Variante.
|
|
783
|
+
|
|
784
|
+
```vue
|
|
785
|
+
<!-- Standard -->
|
|
786
|
+
<LuCard>
|
|
787
|
+
<template #header>
|
|
788
|
+
<h2>Mitarbeiterliste</h2>
|
|
789
|
+
</template>
|
|
790
|
+
<LuTable :columns="cols" :data="rows" />
|
|
791
|
+
<template #footer>
|
|
792
|
+
<LuPagination :current-page="1" :total-pages="5" />
|
|
793
|
+
</template>
|
|
794
|
+
</LuCard>
|
|
795
|
+
|
|
796
|
+
<!-- KPI -->
|
|
797
|
+
<LuCard variant="kpi" value="42" label="Offene Antraege" trend="+12%" trend-direction="up" />
|
|
798
|
+
```
|
|
799
|
+
|
|
800
|
+
**Props:**
|
|
801
|
+
|
|
802
|
+
| Prop | Typ | Default | Beschreibung |
|
|
803
|
+
| ---------------- | ----------------------------- | ------------ | ---------------------------------------- |
|
|
804
|
+
| `variant` | `'standard' \| 'kpi'` | `'standard'` | Darstellungsvariante |
|
|
805
|
+
| `value` | `String \| Number` | `undefined` | KPI-Wert (nur kpi-Variante) |
|
|
806
|
+
| `label` | `String` | `''` | KPI-Label (nur kpi-Variante) |
|
|
807
|
+
| `trend` | `String` | `''` | Trend-Text z.B. "+5%" (nur kpi-Variante) |
|
|
808
|
+
| `trendDirection` | `'up' \| 'down' \| 'neutral'` | `'neutral'` | Trend-Pfeil und Farbe |
|
|
809
|
+
|
|
810
|
+
**Slots (Standard):** `default` (Body), `header`, `footer`
|
|
811
|
+
|
|
812
|
+
---
|
|
813
|
+
|
|
814
|
+
#### LuKpiCard
|
|
815
|
+
|
|
816
|
+
Kompakte KPI-Karte fuer Dashboards mit Status-Farbgebung und Trend-Indikator.
|
|
817
|
+
|
|
818
|
+
```vue
|
|
819
|
+
<LuKpiCard
|
|
820
|
+
label="Krankenquote"
|
|
821
|
+
value="4.2%"
|
|
822
|
+
status="warning"
|
|
823
|
+
:trend="-0.3"
|
|
824
|
+
subtext="vs. Vormonat"
|
|
825
|
+
/>
|
|
826
|
+
```
|
|
827
|
+
|
|
828
|
+
**Props:**
|
|
829
|
+
|
|
830
|
+
| Prop | Typ | Default | Beschreibung |
|
|
831
|
+
| --------- | ------------------------------------------------ | ----------- | -------------------------------------------------------- |
|
|
832
|
+
| `label` | `String` | — | Label (uppercase, xs) |
|
|
833
|
+
| `value` | `String \| Number` | — | KPI-Wert (gross dargestellt) |
|
|
834
|
+
| `status` | `'success' \| 'warning' \| 'error' \| 'default'` | `'default'` | Wert-Textfarbe |
|
|
835
|
+
| `trend` | `Number` | `undefined` | Trend-Prozent (positiv=TrendingUp, negativ=TrendingDown) |
|
|
836
|
+
| `subtext` | `String` | `undefined` | Zusatztext unter dem Wert |
|
|
837
|
+
|
|
838
|
+
---
|
|
839
|
+
|
|
840
|
+
#### LuBadge
|
|
841
|
+
|
|
842
|
+
Pill-foermiges Badge fuer Status- und Kategorie-Anzeigen.
|
|
843
|
+
|
|
844
|
+
```vue
|
|
845
|
+
<LuBadge variant="success">Aktiv</LuBadge>
|
|
846
|
+
<LuBadge variant="warning" size="sm">Ausstehend</LuBadge>
|
|
847
|
+
<LuBadge variant="error">Abgelehnt</LuBadge>
|
|
848
|
+
```
|
|
849
|
+
|
|
850
|
+
**Props:**
|
|
851
|
+
|
|
852
|
+
| Prop | Typ | Default | Beschreibung |
|
|
853
|
+
| --------- | ---------------------------------------------------------- | ----------- | ------------ |
|
|
854
|
+
| `variant` | `'success' \| 'warning' \| 'error' \| 'info' \| 'neutral'` | `'neutral'` | Farbvariante |
|
|
855
|
+
| `size` | `'sm' \| 'md'` | `'md'` | Groesse |
|
|
856
|
+
|
|
857
|
+
**Slots:** `default` (Label-Text)
|
|
858
|
+
|
|
859
|
+
---
|
|
860
|
+
|
|
861
|
+
#### LuAvatar
|
|
862
|
+
|
|
863
|
+
Avatar mit Initialen-Fallback und optionalem Bild. Farbe wird deterministisch aus dem Namen berechnet.
|
|
864
|
+
|
|
865
|
+
```vue
|
|
866
|
+
<LuAvatar name="Max Mustermann" size="lg" />
|
|
867
|
+
<LuAvatar name="Anna Schmidt" src="/avatars/anna.jpg" size="md" />
|
|
868
|
+
```
|
|
869
|
+
|
|
870
|
+
**Props:**
|
|
871
|
+
|
|
872
|
+
| Prop | Typ | Default | Beschreibung |
|
|
873
|
+
| ------ | ------------------------------ | ------- | ------------------------------------------------------- |
|
|
874
|
+
| `name` | `String` | — | Vollstaendiger Name (fuer Initialen und Farbberechnung) |
|
|
875
|
+
| `src` | `String` | `''` | Optionale Bild-URL |
|
|
876
|
+
| `size` | `'sm' \| 'md' \| 'lg' \| 'xl'` | `'md'` | sm=32px, md=40px, lg=48px, xl=64px |
|
|
877
|
+
|
|
878
|
+
Bild wird vorab geladen. Bei Fehler werden 2-Buchstaben-Initialen auf farbigem Hintergrund angezeigt (6 deterministische Farben).
|
|
879
|
+
|
|
880
|
+
---
|
|
881
|
+
|
|
882
|
+
#### LuIcon
|
|
883
|
+
|
|
884
|
+
Dynamischer Lucide-Icon-Wrapper mit Lazy-Loading und Caching.
|
|
885
|
+
|
|
886
|
+
```vue
|
|
887
|
+
<LuIcon name="Clock" :size="24" color="#00BFA6" />
|
|
888
|
+
<LuIcon name="Users" />
|
|
889
|
+
<LuIcon name="AlertTriangle" :stroke-width="2" />
|
|
890
|
+
```
|
|
891
|
+
|
|
892
|
+
**Props:**
|
|
893
|
+
|
|
894
|
+
| Prop | Typ | Default | Beschreibung |
|
|
895
|
+
| ------------- | ------------------ | ---------------- | ------------------------------ |
|
|
896
|
+
| `name` | `String` | — | Lucide-Icon-Name in PascalCase |
|
|
897
|
+
| `size` | `Number \| String` | `20` | Groesse in Pixeln |
|
|
898
|
+
| `color` | `String` | `'currentColor'` | CSS-Farbwert |
|
|
899
|
+
| `strokeWidth` | `Number \| String` | `1.5` | SVG Strichstaerke |
|
|
900
|
+
|
|
901
|
+
Icons werden on-demand geladen und im Cache gehalten. Vollstaendige Icon-Liste: [lucide.dev/icons](https://lucide.dev/icons)
|
|
902
|
+
|
|
903
|
+
---
|
|
904
|
+
|
|
905
|
+
#### LuSkeleton
|
|
906
|
+
|
|
907
|
+
Lade-Platzhalter mit Shimmer-Animation.
|
|
908
|
+
|
|
909
|
+
```vue
|
|
910
|
+
<LuSkeleton width="200px" height="20px" />
|
|
911
|
+
<LuSkeleton width="48px" height="48px" rounded />
|
|
912
|
+
<LuSkeleton height="120px" />
|
|
913
|
+
```
|
|
914
|
+
|
|
915
|
+
**Props:**
|
|
916
|
+
|
|
917
|
+
| Prop | Typ | Default | Beschreibung |
|
|
918
|
+
| --------- | --------- | -------- | -------------------------- |
|
|
919
|
+
| `width` | `String` | `'100%'` | CSS-Breite |
|
|
920
|
+
| `height` | `String` | `'16px'` | CSS-Hoehe |
|
|
921
|
+
| `rounded` | `Boolean` | `false` | Komplett rund (Kreis/Pill) |
|
|
922
|
+
|
|
923
|
+
Respektiert `prefers-reduced-motion`.
|
|
924
|
+
|
|
925
|
+
---
|
|
926
|
+
|
|
927
|
+
#### LuEmptyState
|
|
928
|
+
|
|
929
|
+
Zentrierter Platzhalter fuer leere Ansichten.
|
|
930
|
+
|
|
931
|
+
```vue
|
|
932
|
+
<LuEmptyState
|
|
933
|
+
icon="Users"
|
|
934
|
+
title="Keine Mitarbeiter gefunden"
|
|
935
|
+
description="Passen Sie Ihre Filterkriterien an oder legen Sie einen neuen Mitarbeiter an."
|
|
936
|
+
action-label="Mitarbeiter anlegen"
|
|
937
|
+
:action-handler="createEmployee"
|
|
938
|
+
/>
|
|
939
|
+
```
|
|
940
|
+
|
|
941
|
+
**Props:**
|
|
942
|
+
|
|
943
|
+
| Prop | Typ | Default | Beschreibung |
|
|
944
|
+
| --------------- | ---------- | ------- | -------------------- |
|
|
945
|
+
| `icon` | `String` | `''` | Lucide-Icon-Name |
|
|
946
|
+
| `title` | `String` | — | Ueberschrift |
|
|
947
|
+
| `description` | `String` | `''` | Beschreibungstext |
|
|
948
|
+
| `actionLabel` | `String` | `''` | Button-Text |
|
|
949
|
+
| `actionHandler` | `Function` | `null` | Button-Click-Handler |
|
|
950
|
+
|
|
951
|
+
Button nur sichtbar wenn `actionLabel` und `actionHandler` gesetzt sind.
|
|
952
|
+
|
|
953
|
+
---
|
|
954
|
+
|
|
955
|
+
#### LuActionBar
|
|
956
|
+
|
|
957
|
+
Fixierte Aktionsleiste am unteren Bildschirmrand fuer Bulk-Operationen.
|
|
958
|
+
|
|
959
|
+
```vue
|
|
960
|
+
<LuActionBar
|
|
961
|
+
:selected-count="selectedIds.length"
|
|
962
|
+
:loading="processing"
|
|
963
|
+
:show-delete="canDelete"
|
|
964
|
+
:show-export="true"
|
|
965
|
+
:show-status="true"
|
|
966
|
+
:show-department="false"
|
|
967
|
+
@cancel="clearSelection"
|
|
968
|
+
@action-delete="deleteSelected"
|
|
969
|
+
@action-export="exportSelected"
|
|
970
|
+
@action-status="changeStatus"
|
|
971
|
+
>
|
|
972
|
+
<template #actions>
|
|
973
|
+
<LuButton variant="secondary" size="sm" @click="customAction">
|
|
974
|
+
Eigene Aktion
|
|
975
|
+
</LuButton>
|
|
976
|
+
</template>
|
|
977
|
+
</LuActionBar>
|
|
978
|
+
```
|
|
979
|
+
|
|
980
|
+
**Props:**
|
|
981
|
+
|
|
982
|
+
| Prop | Typ | Default | Beschreibung |
|
|
983
|
+
| ---------------- | --------- | ------- | ----------------------------- |
|
|
984
|
+
| `selectedCount` | `Number` | — | Anzahl ausgewaehlter Elemente |
|
|
985
|
+
| `loading` | `Boolean` | `false` | Ladezustand |
|
|
986
|
+
| `showDelete` | `Boolean` | `true` | Loeschen-Button anzeigen |
|
|
987
|
+
| `showExport` | `Boolean` | `true` | Export-Button anzeigen |
|
|
988
|
+
| `showStatus` | `Boolean` | `true` | Status-Button anzeigen |
|
|
989
|
+
| `showDepartment` | `Boolean` | `true` | Abteilungs-Button anzeigen |
|
|
990
|
+
|
|
991
|
+
**Events:** `cancel`, `action-status`, `action-department`, `action-export`, `action-delete`
|
|
992
|
+
|
|
993
|
+
**Slots:** `actions` — zusaetzliche eigene Aktionsbuttons
|
|
994
|
+
|
|
995
|
+
Wird nur angezeigt wenn `selectedCount > 0`. Slide-Up-Animation. Dunkler Hintergrund (gray-900).
|
|
996
|
+
|
|
997
|
+
---
|
|
998
|
+
|
|
999
|
+
### Charts
|
|
1000
|
+
|
|
1001
|
+
Alle Chart-Komponenten nutzen Chart.js via vue-chartjs und wenden automatisch die lumen.hr Design-Defaults an.
|
|
1002
|
+
|
|
1003
|
+
#### LuLineChart
|
|
1004
|
+
|
|
1005
|
+
```vue
|
|
1006
|
+
<LuLineChart
|
|
1007
|
+
:data="{
|
|
1008
|
+
labels: ['Jan', 'Feb', 'Mrz', 'Apr', 'Mai', 'Jun'],
|
|
1009
|
+
datasets: [{ label: 'Ueberstunden', data: [12, 19, 3, 5, 2, 3] }],
|
|
1010
|
+
}"
|
|
1011
|
+
height="300px"
|
|
1012
|
+
aria-label="Ueberstunden pro Monat"
|
|
1013
|
+
/>
|
|
1014
|
+
```
|
|
1015
|
+
|
|
1016
|
+
**Props:**
|
|
1017
|
+
|
|
1018
|
+
| Prop | Typ | Default | Beschreibung |
|
|
1019
|
+
| ----------- | ---------------------- | ------------------ | ----------------------------------- |
|
|
1020
|
+
| `data` | `ChartData<'line'>` | — | Chart.js Daten |
|
|
1021
|
+
| `options` | `ChartOptions<'line'>` | `{}` | Optionen (deep-merged mit Defaults) |
|
|
1022
|
+
| `height` | `String` | `'100%'` | CSS-Hoehe |
|
|
1023
|
+
| `ariaLabel` | `String` | `'Liniendiagramm'` | Barrierefreie Beschriftung |
|
|
1024
|
+
|
|
1025
|
+
Defaults: Teal-500 Linie, 10% Teal-Fuellung, 3px Punkte, 0.3 Kurvenspannung.
|
|
1026
|
+
|
|
1027
|
+
---
|
|
1028
|
+
|
|
1029
|
+
#### LuBarChart
|
|
1030
|
+
|
|
1031
|
+
```vue
|
|
1032
|
+
<LuBarChart
|
|
1033
|
+
:data="{
|
|
1034
|
+
labels: ['Mo', 'Di', 'Mi', 'Do', 'Fr'],
|
|
1035
|
+
datasets: [{ label: 'Anwesenheit', data: [45, 42, 48, 43, 40] }],
|
|
1036
|
+
}"
|
|
1037
|
+
height="250px"
|
|
1038
|
+
/>
|
|
1039
|
+
```
|
|
1040
|
+
|
|
1041
|
+
**Props:**
|
|
1042
|
+
|
|
1043
|
+
| Prop | Typ | Default | Beschreibung |
|
|
1044
|
+
| ----------- | --------------------- | ------------------ | ----------------------------------- |
|
|
1045
|
+
| `data` | `ChartData<'bar'>` | — | Chart.js Daten |
|
|
1046
|
+
| `options` | `ChartOptions<'bar'>` | `{}` | Optionen (deep-merged mit Defaults) |
|
|
1047
|
+
| `height` | `String` | `'100%'` | CSS-Hoehe |
|
|
1048
|
+
| `ariaLabel` | `String` | `'Balkendiagramm'` | Barrierefreie Beschriftung |
|
|
1049
|
+
|
|
1050
|
+
Defaults: Teal-500 Hintergrund, 4px abgerundete Ecken.
|
|
1051
|
+
|
|
1052
|
+
---
|
|
1053
|
+
|
|
1054
|
+
#### LuDonutChart
|
|
1055
|
+
|
|
1056
|
+
```vue
|
|
1057
|
+
<LuDonutChart
|
|
1058
|
+
:data="{
|
|
1059
|
+
labels: ['Anwesend', 'Urlaub', 'Krank', 'Abwesend'],
|
|
1060
|
+
datasets: [{ data: [35, 5, 3, 2] }],
|
|
1061
|
+
}"
|
|
1062
|
+
height="300px"
|
|
1063
|
+
/>
|
|
1064
|
+
```
|
|
1065
|
+
|
|
1066
|
+
**Props:**
|
|
1067
|
+
|
|
1068
|
+
| Prop | Typ | Default | Beschreibung |
|
|
1069
|
+
| ----------- | -------------------------- | ----------------- | ----------------------------------- |
|
|
1070
|
+
| `data` | `ChartData<'doughnut'>` | — | Chart.js Daten |
|
|
1071
|
+
| `options` | `ChartOptions<'doughnut'>` | `{}` | Optionen (deep-merged mit Defaults) |
|
|
1072
|
+
| `height` | `String` | `'300px'` | CSS-Hoehe |
|
|
1073
|
+
| `ariaLabel` | `String` | `'Kreisdiagramm'` | Barrierefreie Beschriftung |
|
|
1074
|
+
|
|
1075
|
+
Defaults: 65% Cutout (Donut), 10-Farben-Palette im "Professional Calm"-Stil, Legende unten. Tooltip zeigt Label, Wert und berechneten Prozentsatz.
|
|
1076
|
+
|
|
1077
|
+
---
|
|
1078
|
+
|
|
1079
|
+
### Tabs (Compound Pattern)
|
|
1080
|
+
|
|
1081
|
+
Barrierefreies Tab-System nach WAI-ARIA Tabs Pattern mit Tastaturnavigation (Pfeiltasten, Home, End).
|
|
1082
|
+
|
|
1083
|
+
```vue
|
|
1084
|
+
<LuTabs v-model="activeTab">
|
|
1085
|
+
<LuTabList>
|
|
1086
|
+
<LuTab value="overview">Uebersicht</LuTab>
|
|
1087
|
+
<LuTab value="details">Details</LuTab>
|
|
1088
|
+
<LuTab value="history">Verlauf</LuTab>
|
|
1089
|
+
</LuTabList>
|
|
1090
|
+
|
|
1091
|
+
<LuTabPanel value="overview">
|
|
1092
|
+
<p>Uebersichts-Inhalt...</p>
|
|
1093
|
+
</LuTabPanel>
|
|
1094
|
+
<LuTabPanel value="details">
|
|
1095
|
+
<p>Detail-Inhalt...</p>
|
|
1096
|
+
</LuTabPanel>
|
|
1097
|
+
<LuTabPanel value="history">
|
|
1098
|
+
<p>Verlaufs-Inhalt...</p>
|
|
1099
|
+
</LuTabPanel>
|
|
1100
|
+
</LuTabs>
|
|
1101
|
+
```
|
|
1102
|
+
|
|
1103
|
+
#### LuTabs
|
|
1104
|
+
|
|
1105
|
+
Root-Container. Stellt aktiven Tab-Status via provide/inject bereit.
|
|
1106
|
+
|
|
1107
|
+
| Prop | Typ | Default | Beschreibung |
|
|
1108
|
+
| ------------ | -------- | ------- | -------------------------- |
|
|
1109
|
+
| `modelValue` | `String` | `''` | Aktiver Tab-Wert (v-model) |
|
|
1110
|
+
|
|
1111
|
+
#### LuTabList
|
|
1112
|
+
|
|
1113
|
+
Horizontaler Container fuer Tab-Buttons mit WAI-ARIA Tastaturnavigation.
|
|
1114
|
+
|
|
1115
|
+
#### LuTab
|
|
1116
|
+
|
|
1117
|
+
Einzelner Tab-Button. `role="tab"`, `aria-selected`, `aria-controls`.
|
|
1118
|
+
|
|
1119
|
+
| Prop | Typ | Default | Beschreibung |
|
|
1120
|
+
| ------- | -------- | ------- | --------------------------------------------------- |
|
|
1121
|
+
| `value` | `String` | — | Eindeutige ID (muss mit LuTabPanel uebereinstimmen) |
|
|
1122
|
+
|
|
1123
|
+
**Slots:** `default` (Tab-Label)
|
|
1124
|
+
|
|
1125
|
+
#### LuTabPanel
|
|
1126
|
+
|
|
1127
|
+
Content-Panel. Wird nur gerendert wenn der zugehoerige Tab aktiv ist (`v-if`).
|
|
1128
|
+
|
|
1129
|
+
| Prop | Typ | Default | Beschreibung |
|
|
1130
|
+
| ------- | -------- | ------- | ----------------------------------------------------------- |
|
|
1131
|
+
| `value` | `String` | — | Muss mit dem `value` des zugehoerigen LuTab uebereinstimmen |
|
|
1132
|
+
|
|
1133
|
+
**Slots:** `default` (Panel-Inhalt)
|
|
1134
|
+
|
|
1135
|
+
---
|
|
1136
|
+
|
|
1137
|
+
## Composables
|
|
1138
|
+
|
|
1139
|
+
### useToast()
|
|
1140
|
+
|
|
1141
|
+
Globaler Toast-Zustand. Wird von `<LuToast>` fuer die Darstellung genutzt.
|
|
1142
|
+
|
|
1143
|
+
```js
|
|
1144
|
+
import { useToast } from '@vss-software/ui'
|
|
1145
|
+
|
|
1146
|
+
const { toasts, addToast, removeToast } = useToast()
|
|
1147
|
+
|
|
1148
|
+
// Toast hinzufuegen
|
|
1149
|
+
const id = addToast({
|
|
1150
|
+
variant: 'success', // 'success' | 'warning' | 'error' | 'info'
|
|
1151
|
+
title: 'Gespeichert', // Optional
|
|
1152
|
+
message: 'Aenderungen uebernommen.',
|
|
1153
|
+
duration: 5000, // ms, 0 = kein Auto-Dismiss
|
|
1154
|
+
})
|
|
1155
|
+
|
|
1156
|
+
// Toast manuell entfernen
|
|
1157
|
+
removeToast(id)
|
|
1158
|
+
```
|
|
1159
|
+
|
|
1160
|
+
| Return | Typ | Beschreibung |
|
|
1161
|
+
| ------------- | --------------------- | ---------------------------------- |
|
|
1162
|
+
| `toasts` | `Toast[]` (reactive) | Alle aktiven Toasts |
|
|
1163
|
+
| `addToast` | `(options) => number` | Toast hinzufuegen, gibt ID zurueck |
|
|
1164
|
+
| `removeToast` | `(id) => void` | Toast entfernen |
|
|
1165
|
+
|
|
1166
|
+
---
|
|
1167
|
+
|
|
1168
|
+
### useModal()
|
|
1169
|
+
|
|
1170
|
+
Einfache reaktive Zustandsverwaltung fuer Modals.
|
|
1171
|
+
|
|
1172
|
+
```js
|
|
1173
|
+
import { useModal } from '@vss-software/ui'
|
|
1174
|
+
|
|
1175
|
+
const { isOpen, open, close, toggle } = useModal()
|
|
1176
|
+
```
|
|
1177
|
+
|
|
1178
|
+
```vue
|
|
1179
|
+
<LuButton @click="open">Dialog oeffnen</LuButton>
|
|
1180
|
+
<LuModal v-model="isOpen" title="Mein Dialog">
|
|
1181
|
+
Inhalt...
|
|
1182
|
+
</LuModal>
|
|
1183
|
+
```
|
|
1184
|
+
|
|
1185
|
+
| Return | Typ | Beschreibung |
|
|
1186
|
+
| -------- | -------------- | ----------------------------- |
|
|
1187
|
+
| `isOpen` | `Ref<boolean>` | Reaktiver Sichtbarkeitsstatus |
|
|
1188
|
+
| `open` | `() => void` | Oeffnen |
|
|
1189
|
+
| `close` | `() => void` | Schliessen |
|
|
1190
|
+
| `toggle` | `() => void` | Umschalten |
|
|
1191
|
+
|
|
1192
|
+
---
|
|
1193
|
+
|
|
1194
|
+
### useClickOutside()
|
|
1195
|
+
|
|
1196
|
+
Erkennt Klicks ausserhalb eines Elements.
|
|
1197
|
+
|
|
1198
|
+
```js
|
|
1199
|
+
import { useClickOutside } from '@vss-software/ui'
|
|
1200
|
+
import { ref } from 'vue'
|
|
1201
|
+
|
|
1202
|
+
const dropdownRef = ref(null)
|
|
1203
|
+
useClickOutside(dropdownRef, () => {
|
|
1204
|
+
isOpen.value = false
|
|
1205
|
+
})
|
|
1206
|
+
```
|
|
1207
|
+
|
|
1208
|
+
| Parameter | Typ | Beschreibung |
|
|
1209
|
+
| ----------- | -------------------------- | ------------------------------------ |
|
|
1210
|
+
| `targetRef` | `Ref<HTMLElement \| null>` | Ref zum Begrenzungselement |
|
|
1211
|
+
| `callback` | `() => void` | Wird bei Klick ausserhalb aufgerufen |
|
|
1212
|
+
|
|
1213
|
+
Registriert `mousedown` und `touchstart` Listener. Bereinigt automatisch bei Unmount.
|
|
1214
|
+
|
|
1215
|
+
---
|
|
1216
|
+
|
|
1217
|
+
## Utilities
|
|
1218
|
+
|
|
1219
|
+
### Chart Defaults
|
|
1220
|
+
|
|
1221
|
+
```js
|
|
1222
|
+
import {
|
|
1223
|
+
baseChartOptions,
|
|
1224
|
+
lineDatasetDefaults,
|
|
1225
|
+
barDatasetDefaults,
|
|
1226
|
+
deepMerge,
|
|
1227
|
+
} from '@vss-software/ui'
|
|
1228
|
+
```
|
|
1229
|
+
|
|
1230
|
+
| Export | Beschreibung |
|
|
1231
|
+
| --------------------------- | ---------------------------------------------------------------------------------- |
|
|
1232
|
+
| `baseChartOptions` | Gemeinsame Chart.js Optionen (responsive, Tooltip-Stil, Achsen-Formatierung) |
|
|
1233
|
+
| `lineDatasetDefaults` | Linien-Defaults: Teal-500, 10% Fuellung, 3px Punkte, 0.3 Spannung |
|
|
1234
|
+
| `barDatasetDefaults` | Balken-Defaults: Teal-500, 4px Border-Radius |
|
|
1235
|
+
| `deepMerge(target, source)` | Deep-Merge zweier Objekte. Arrays werden ersetzt. Schutz gegen Prototype-Pollution |
|
|
1236
|
+
|
|
1237
|
+
---
|
|
1238
|
+
|
|
1239
|
+
## Barrierefreiheit (a11y)
|
|
1240
|
+
|
|
1241
|
+
Alle Komponenten folgen WCAG 2.1 AA Richtlinien:
|
|
1242
|
+
|
|
1243
|
+
- **ARIA-Rollen und -Attribute** auf allen interaktiven Elementen
|
|
1244
|
+
- **Tastaturnavigation** (Tab, Enter, Space, Escape, Pfeiltasten)
|
|
1245
|
+
- **Focus-Management** mit sichtbarem Fokusring (Teal-500, 2px)
|
|
1246
|
+
- **Focus-Trap** in Modals
|
|
1247
|
+
- **Screen-Reader-Unterstuetzung** via `aria-label`, `aria-live`, `role`
|
|
1248
|
+
- **Reduzierte Bewegung** (`prefers-reduced-motion`) wird respektiert
|
|
1249
|
+
- **Farbkontraste** eingehalten (mindestens 4.5:1)
|
|
1250
|
+
|
|
1251
|
+
---
|
|
1252
|
+
|
|
1253
|
+
## Lizenz
|
|
1254
|
+
|
|
1255
|
+
Proprietaer. Alle Rechte vorbehalten.
|