@martinsura/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/dist/index.js ADDED
@@ -0,0 +1,4197 @@
1
+ import { cva } from 'class-variance-authority';
2
+ import { twMerge } from 'tailwind-merge';
3
+ import { IconDownload, IconPaperclip, IconFileSpreadsheet, IconX, IconTrashX, IconPlus, IconPencil, IconDotsVertical, IconEyeOff, IconEye, IconCheck, IconDroplet, IconDatabaseOff, IconChevronUp, IconChevronDown, IconSelector, IconCalendar, IconUsers, IconInfoCircle, IconAlertTriangle, IconCircleX, IconBold, IconItalic, IconUnderline, IconList, IconListNumbers, IconLink, IconUnlink, IconClearFormatting, IconUpload, IconAlertCircle, IconTrash, IconLoader2, IconChevronLeft, IconChevronRight, IconSearch } from '@tabler/icons-react';
4
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
5
+ import { createContext, useRef, useState, useLayoutEffect, useEffect, useMemo, useContext, useCallback, useId } from 'react';
6
+ import { createPortal } from 'react-dom';
7
+ import dayjs from 'dayjs';
8
+
9
+ // src/button/button.tsx
10
+ var defaultIcons = {
11
+ dots: IconDotsVertical,
12
+ edit: IconPencil,
13
+ create: IconPlus,
14
+ delete: IconTrashX,
15
+ remove: IconX,
16
+ excel: IconFileSpreadsheet,
17
+ file: IconPaperclip,
18
+ download: IconDownload
19
+ };
20
+ var iconRegistry = { ...defaultIcons };
21
+ function registerIcons(icons) {
22
+ iconRegistry = { ...iconRegistry, ...icons };
23
+ }
24
+ function getIcon(type) {
25
+ return iconRegistry[type];
26
+ }
27
+
28
+ // src/theme.ts
29
+ var black = { base: "#000000", hover: "#1f2937" };
30
+ var red = { base: "#dc2626", hover: "#b91c1c" };
31
+ var green = { base: "#16a34a", hover: "#15803d" };
32
+ var amber = { base: "#d97706", hover: "#b45309" };
33
+ var blue = { base: "#2563eb", hover: "#1d4ed8" };
34
+ var gray = {
35
+ 50: "#f9fafb",
36
+ 100: "#f3f4f6",
37
+ 200: "#e5e7eb",
38
+ 300: "#d1d5db",
39
+ 400: "#9ca3af",
40
+ 500: "#6b7280",
41
+ 600: "#4b5563",
42
+ 700: "#374151",
43
+ 800: "#1f2937",
44
+ 900: "#111827"
45
+ };
46
+ var uiTheme = {
47
+ colors: {
48
+ primary: { bg: black.base, hover: black.hover, text: "#fff" },
49
+ danger: { bg: red.base, hover: red.hover, text: "#fff" },
50
+ success: { bg: green.base, hover: green.hover, text: "#fff" },
51
+ warning: { bg: amber.base, hover: amber.hover, text: "#fff" },
52
+ info: { bg: blue.base, hover: blue.hover, text: "#fff" }
53
+ },
54
+ size: {
55
+ small: { height: "28px", px: "10px", text: "12px", radius: "4px" },
56
+ middle: { height: "32px", px: "12px", text: "13px", radius: "6px" },
57
+ large: { height: "40px", px: "16px", text: "15px", radius: "8px" }
58
+ },
59
+ radius: { sm: "4px", md: "6px", lg: "8px", full: "9999px" },
60
+ border: {
61
+ color: "#e5e7eb",
62
+ input: "#d1d5db",
63
+ focus: "#6b7280",
64
+ width: "1px"
65
+ },
66
+ shadow: {
67
+ sm: "0 1px 2px rgba(0,0,0,0.05)",
68
+ md: "0 4px 6px rgba(0,0,0,0.07)"
69
+ },
70
+ font: { family: "sans-serif", size: { sm: "12px", md: "13px", lg: "15px" } },
71
+ gray,
72
+ layout: {
73
+ panel: {
74
+ headerHeight: "40px",
75
+ headerPx: "12px",
76
+ bodyPadding: "12px",
77
+ footerPx: "12px",
78
+ footerPy: "8px"
79
+ },
80
+ drawer: {
81
+ headerHeight: "48px",
82
+ headerPx: "16px",
83
+ bodyPadding: "24px",
84
+ footerPx: "12px",
85
+ footerPy: "10px"
86
+ },
87
+ popup: {
88
+ panelPadding: "12px",
89
+ compactPaddingX: "12px",
90
+ compactPaddingY: "8px",
91
+ compactGap: "8px",
92
+ text: "13px"
93
+ },
94
+ table: {
95
+ small: { cellPx: "8px", thPy: "6px", tdPy: "4px", emptyPy: "24px" },
96
+ middle: { cellPx: "12px", thPy: "8px", tdPy: "6px", emptyPy: "32px" },
97
+ large: { cellPx: "16px", thPy: "10px", tdPy: "8px", emptyPy: "40px" }
98
+ },
99
+ empty: {
100
+ defaultPy: "48px",
101
+ compactPy: "32px",
102
+ defaultIconBox: "56px",
103
+ compactIconBox: "44px",
104
+ defaultIcon: "28px",
105
+ compactIcon: "22px",
106
+ compactText: "13px",
107
+ compactDescription: "11px"
108
+ }
109
+ }
110
+ };
111
+ var inputSizeClasses = {
112
+ small: "h-(--ui-h-sm) px-(--ui-px-sm) [font-size:var(--ui-text-sm)] rounded-(--ui-radius-sm)",
113
+ middle: "h-(--ui-h-md) px-(--ui-px-md) [font-size:var(--ui-text-md)] rounded-(--ui-radius-md)",
114
+ large: "h-(--ui-h-lg) px-(--ui-px-lg) [font-size:var(--ui-text-lg)] rounded-(--ui-radius-lg)"
115
+ };
116
+ var triggerSizeClasses = {
117
+ small: "min-h-(--ui-h-sm) px-(--ui-px-sm) [font-size:var(--ui-text-sm)] rounded-(--ui-radius-sm)",
118
+ middle: "min-h-(--ui-h-md) px-(--ui-px-md) [font-size:var(--ui-text-md)] rounded-(--ui-radius-md)",
119
+ large: "min-h-(--ui-h-lg) px-(--ui-px-lg) [font-size:var(--ui-text-lg)] rounded-(--ui-radius-lg)"
120
+ };
121
+ var multiTriggerSizeClasses = {
122
+ small: "min-h-(--ui-h-sm) px-(--ui-px-sm) [font-size:var(--ui-text-sm)] rounded-(--ui-radius-sm)",
123
+ middle: "min-h-(--ui-h-md) px-(--ui-px-md) [font-size:var(--ui-text-md)] rounded-(--ui-radius-md)",
124
+ large: "min-h-(--ui-h-lg) px-(--ui-px-lg) [font-size:var(--ui-text-lg)] rounded-(--ui-radius-lg)"
125
+ };
126
+ var spinnerIconSize = {
127
+ small: 14,
128
+ middle: 18,
129
+ large: 22
130
+ };
131
+ var spinnerTextSizeClasses = {
132
+ small: "[font-size:var(--ui-text-sm)]",
133
+ middle: "[font-size:var(--ui-text-md)]",
134
+ large: "[font-size:var(--ui-text-lg)]"
135
+ };
136
+ var componentTitleClasses = {
137
+ default: "font-medium [font-size:var(--ui-text-lg)] text-(--ui-text-strong)",
138
+ inverse: "font-medium [font-size:var(--ui-text-lg)] text-(--ui-primary-text)"
139
+ };
140
+ var panelLayoutClasses = {
141
+ header: "min-h-(--ui-panel-header-h) px-(--ui-panel-header-px)",
142
+ body: "p-(--ui-panel-body-p)",
143
+ footer: "px-(--ui-panel-footer-px) py-(--ui-panel-footer-py)"
144
+ };
145
+ var drawerLayoutClasses = {
146
+ header: "h-(--ui-drawer-header-h) px-(--ui-drawer-header-px)",
147
+ body: "p-(--ui-drawer-body-p)",
148
+ footer: "px-(--ui-drawer-footer-px) py-(--ui-drawer-footer-py)"
149
+ };
150
+ var popupLayoutClasses = {
151
+ panel: "p-(--ui-popup-panel-p)",
152
+ compactRow: "px-(--ui-popup-row-px) py-(--ui-popup-row-py) gap-(--ui-popup-gap) [font-size:var(--ui-popup-text)]",
153
+ compactHeader: "px-(--ui-popup-row-px) py-(--ui-popup-row-py)",
154
+ tooltip: "px-(--ui-popup-row-px) py-(--ui-popup-row-py) [font-size:var(--ui-text-sm)]"
155
+ };
156
+ var emptyLayoutClasses = {
157
+ default: "py-(--ui-empty-py)",
158
+ compact: "py-(--ui-empty-compact-py)",
159
+ defaultIconBox: "h-(--ui-empty-icon-box) w-(--ui-empty-icon-box)",
160
+ compactIconBox: "h-(--ui-empty-compact-icon-box) w-(--ui-empty-compact-icon-box)",
161
+ compactText: "[font-size:var(--ui-empty-compact-text)]",
162
+ compactDescription: "[font-size:var(--ui-empty-compact-description)]"
163
+ };
164
+ var neutralTextClasses = {
165
+ strong: "text-(--ui-text-strong)",
166
+ default: "text-(--ui-text)",
167
+ muted: "text-(--ui-text-muted)",
168
+ soft: "text-(--ui-text-soft)"};
169
+ var neutralSurfaceClasses = {
170
+ subtle: "bg-(--ui-surface-subtle)"};
171
+ var neutralIconClasses = {
172
+ default: "text-(--ui-text-soft)",
173
+ hover: "hover:text-(--ui-text)"
174
+ };
175
+ var inputBaseClass = "w-full border border-(--ui-border-input) bg-white text-(--ui-text-strong) outline-none transition-colors placeholder:text-(--ui-text-soft) focus:border-(--ui-border-focus) disabled:bg-(--ui-surface-muted) disabled:text-(--ui-text-disabled) disabled:cursor-not-allowed";
176
+ var triggerBorderClasses = {
177
+ default: "border-(--ui-border-input) hover:border-(--ui-border-focus)",
178
+ open: "border-(--ui-border-focus)",
179
+ error: "border-(--ui-danger) hover:border-(--ui-danger)",
180
+ disabled: "bg-(--ui-surface-muted) cursor-not-allowed opacity-60 hover:border-(--ui-border-input)"
181
+ };
182
+ var spinnerColorStyle = {
183
+ primary: { color: "var(--ui-primary)" },
184
+ danger: { color: "var(--ui-danger)" },
185
+ success: { color: "var(--ui-success)" },
186
+ warning: { color: "var(--ui-warning)" },
187
+ info: { color: "var(--ui-info)" }
188
+ };
189
+ var Spinner = ({
190
+ size = "middle",
191
+ color = "current",
192
+ label,
193
+ center = false,
194
+ className
195
+ }) => {
196
+ const icon = /* @__PURE__ */ jsx(
197
+ IconLoader2,
198
+ {
199
+ size: typeof size === "number" ? size : spinnerIconSize[size],
200
+ strokeWidth: 1.5,
201
+ className: "animate-spin shrink-0"
202
+ }
203
+ );
204
+ return /* @__PURE__ */ jsxs(
205
+ "span",
206
+ {
207
+ className: twMerge(
208
+ "inline-flex items-center",
209
+ label && "gap-2",
210
+ center && "w-full justify-center",
211
+ typeof size === "number" ? "text-sm" : spinnerTextSizeClasses[size],
212
+ className
213
+ ),
214
+ style: color === "current" ? void 0 : spinnerColorStyle[color],
215
+ children: [
216
+ icon,
217
+ label && /* @__PURE__ */ jsx("span", { children: label })
218
+ ]
219
+ }
220
+ );
221
+ };
222
+ var Popconfirm = ({
223
+ children,
224
+ onConfirm,
225
+ onCancel,
226
+ open: controlledOpen,
227
+ onOpenChange,
228
+ anchorRect,
229
+ classNames,
230
+ ...confirm
231
+ }) => {
232
+ const triggerRef = useRef(null);
233
+ const [uncontrolledOpen, setUncontrolledOpen] = useState(false);
234
+ const open = controlledOpen ?? uncontrolledOpen;
235
+ const setOpen = (next) => {
236
+ if (controlledOpen === void 0) {
237
+ setUncontrolledOpen(next);
238
+ }
239
+ onOpenChange?.(next);
240
+ };
241
+ const rect = anchorRect ?? triggerRef.current?.getBoundingClientRect();
242
+ const top = rect ? rect.bottom + 4 : 0;
243
+ const left = rect ? rect.left : 0;
244
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
245
+ children && /* @__PURE__ */ jsx(
246
+ "span",
247
+ {
248
+ ref: triggerRef,
249
+ className: twMerge("inline-flex", classNames?.trigger),
250
+ onClick: (event) => {
251
+ event.preventDefault();
252
+ event.stopPropagation();
253
+ setOpen(true);
254
+ },
255
+ children
256
+ }
257
+ ),
258
+ open && createPortal(
259
+ /* @__PURE__ */ jsxs(Fragment, { children: [
260
+ /* @__PURE__ */ jsx(
261
+ "div",
262
+ {
263
+ className: twMerge("fixed inset-0 z-999", classNames?.overlay),
264
+ onClick: () => {
265
+ setOpen(false);
266
+ onCancel?.();
267
+ }
268
+ }
269
+ ),
270
+ /* @__PURE__ */ jsxs(
271
+ "div",
272
+ {
273
+ className: twMerge("fixed z-1000 min-w-55 rounded-lg border border-(--ui-border) bg-white shadow-lg", popupLayoutClasses.panel, classNames?.panel),
274
+ style: { top, left: Math.max(left, 8) },
275
+ children: [
276
+ /* @__PURE__ */ jsx("p", { className: twMerge("mb-3 text-sm text-(--ui-text)", classNames?.message), children: confirm.message }),
277
+ /* @__PURE__ */ jsxs("div", { className: twMerge("flex justify-end gap-2", classNames?.actions), children: [
278
+ /* @__PURE__ */ jsx(
279
+ "button",
280
+ {
281
+ type: "button",
282
+ className: twMerge("h-7 rounded-(--ui-radius-sm) border border-(--ui-border-strong) bg-white px-2.5 text-xs text-(--ui-text) cursor-pointer hover:bg-(--ui-surface-subtle)", classNames?.cancelButton),
283
+ onClick: () => {
284
+ setOpen(false);
285
+ onCancel?.();
286
+ },
287
+ children: confirm.confirmCancel ?? "Zru\u0161it"
288
+ }
289
+ ),
290
+ /* @__PURE__ */ jsx(
291
+ "button",
292
+ {
293
+ type: "button",
294
+ className: twMerge(
295
+ "h-7 rounded-(--ui-radius-sm) px-2.5 text-xs cursor-pointer",
296
+ confirm.confirmColor === "red" ? "bg-(--ui-danger) text-(--ui-danger-text) hover:bg-(--ui-danger-hover)" : "bg-(--ui-primary) text-(--ui-primary-text) hover:bg-(--ui-primary-hover)",
297
+ classNames?.confirmButton
298
+ ),
299
+ onClick: () => {
300
+ setOpen(false);
301
+ onConfirm();
302
+ },
303
+ children: confirm.confirmOk ?? "Potvrdit"
304
+ }
305
+ )
306
+ ] })
307
+ ]
308
+ }
309
+ )
310
+ ] }),
311
+ document.body
312
+ )
313
+ ] });
314
+ };
315
+ var buttonVariants = cva(
316
+ "inline-flex items-center justify-center gap-1.5 font-medium cursor-pointer transition-colors disabled:opacity-50 disabled:cursor-not-allowed select-none",
317
+ {
318
+ variants: {
319
+ variant: {
320
+ primary: "bg-(--ui-primary) text-(--ui-primary-text) hover:bg-(--ui-primary-hover) active:bg-(--ui-primary-active)",
321
+ danger: "bg-(--ui-danger) text-(--ui-danger-text) hover:bg-(--ui-danger-hover) active:bg-(--ui-danger-hover)",
322
+ success: "bg-(--ui-success) text-(--ui-success-text) hover:bg-(--ui-success-hover) active:bg-(--ui-success-hover)",
323
+ default: "bg-white text-(--ui-text) border border-(--ui-border-strong) hover:bg-(--ui-surface-subtle) active:bg-(--ui-surface-muted)",
324
+ ghost: "text-(--ui-text) hover:bg-(--ui-surface-muted) active:bg-(--ui-surface-subtle)",
325
+ link: "text-(--ui-primary) hover:underline p-0! h-auto!"
326
+ },
327
+ size: {
328
+ small: "h-(--ui-h-sm) px-(--ui-px-sm) [font-size:var(--ui-text-sm)] rounded-(--ui-radius-sm)",
329
+ middle: "h-(--ui-h-md) px-(--ui-px-md) [font-size:var(--ui-text-md)] rounded-(--ui-radius-md)",
330
+ large: "h-(--ui-h-lg) px-(--ui-px-lg) [font-size:var(--ui-text-lg)] rounded-(--ui-radius-lg)"
331
+ },
332
+ block: {
333
+ true: "w-full",
334
+ false: ""
335
+ },
336
+ shape: {
337
+ default: "",
338
+ circle: "rounded-full aspect-square px-0!",
339
+ round: "rounded-full"
340
+ }
341
+ },
342
+ defaultVariants: {
343
+ variant: "primary",
344
+ size: "middle",
345
+ block: false,
346
+ shape: "default"
347
+ }
348
+ }
349
+ );
350
+ var iconSizeMap = {
351
+ small: 14,
352
+ middle: 16,
353
+ large: 18
354
+ };
355
+ var Button = ({
356
+ variant = "primary",
357
+ size = "middle",
358
+ shape = "default",
359
+ block = false,
360
+ iconPosition = "left",
361
+ ...props
362
+ }) => {
363
+ const iconSize = props.iconSize ?? iconSizeMap[size];
364
+ const IconComponent = props.icon ?? (props.iconType ? getIcon(props.iconType) : void 0);
365
+ const iconNode = IconComponent ? /* @__PURE__ */ jsx(IconComponent, { size: iconSize, strokeWidth: 1.5, className: props.classNames?.icon }) : void 0;
366
+ const handleClick = () => {
367
+ if (props.disabled || props.loading) {
368
+ return;
369
+ }
370
+ props.onClick?.();
371
+ };
372
+ const button = /* @__PURE__ */ jsxs(
373
+ "button",
374
+ {
375
+ type: props.htmlType ?? "button",
376
+ disabled: props.disabled || props.loading,
377
+ className: twMerge(buttonVariants({ variant, size, block, shape }), props.className),
378
+ onClick: props.confirm ? void 0 : handleClick,
379
+ children: [
380
+ props.loading ? /* @__PURE__ */ jsx(Spinner, { size: iconSize, color: "current", className: props.classNames?.spinner }) : iconPosition === "left" && iconNode,
381
+ /* @__PURE__ */ jsx("span", { className: props.classNames?.content, children: props.text ?? props.children }),
382
+ !props.loading && iconPosition === "right" && iconNode
383
+ ]
384
+ }
385
+ );
386
+ if (!props.confirm) {
387
+ return button;
388
+ }
389
+ return /* @__PURE__ */ jsx(
390
+ Popconfirm,
391
+ {
392
+ message: props.confirm.message,
393
+ confirmOk: props.confirm.confirmOk,
394
+ confirmCancel: props.confirm.confirmCancel,
395
+ confirmColor: props.confirm.confirmColor,
396
+ onConfirm: handleClick,
397
+ children: button
398
+ }
399
+ );
400
+ };
401
+ var InputField = ({ noMargin, className, children }) => /* @__PURE__ */ jsx("div", { className: twMerge("flex flex-col gap-1", !noMargin && "mb-3", className), children });
402
+ var InputLabel = ({ label, required, className, requiredMarkClassName }) => /* @__PURE__ */ jsxs("label", { className: twMerge("text-sm text-(--ui-text)", className), children: [
403
+ label,
404
+ required && /* @__PURE__ */ jsx("span", { className: twMerge("ml-0.5 text-(--ui-danger)", requiredMarkClassName), children: "*" })
405
+ ] });
406
+ var InputError = ({ error, className }) => /* @__PURE__ */ jsx("span", { className: twMerge("text-xs text-(--ui-danger)", className), children: error });
407
+ var ErrorContext = createContext({
408
+ resolveInputError: () => [],
409
+ resolveServerError: () => null
410
+ });
411
+ var ErrorProvider = ({ resolveInputError, resolveServerError, children }) => /* @__PURE__ */ jsx(ErrorContext.Provider, { value: { resolveInputError, resolveServerError }, children });
412
+ var useErrorResolver = () => useContext(ErrorContext).resolveInputError;
413
+ var useServerError = () => useContext(ErrorContext).resolveServerError;
414
+ var TextInput = ({
415
+ size = "middle",
416
+ password = false,
417
+ newPassword = false,
418
+ ...props
419
+ }) => {
420
+ const [showPassword, setShowPassword] = useState(false);
421
+ const resolveError = useErrorResolver();
422
+ const resolvedErrors = props.errorName ? resolveError(props.errorName) : [];
423
+ const errorDisplay = props.error ?? props.customError ?? (resolvedErrors.length > 0 ? resolvedErrors.join(", ") : void 0);
424
+ const hasError = !!errorDisplay;
425
+ const inputType = !password ? "text" : showPassword ? "text" : "password";
426
+ return /* @__PURE__ */ jsxs(InputField, { noMargin: props.noMargin, className: props.className, children: [
427
+ props.label && /* @__PURE__ */ jsx(
428
+ InputLabel,
429
+ {
430
+ label: props.label,
431
+ required: props.required,
432
+ className: props.classNames?.label,
433
+ requiredMarkClassName: props.classNames?.requiredMark
434
+ }
435
+ ),
436
+ /* @__PURE__ */ jsxs("div", { className: twMerge("relative", props.classNames?.inputWrapper), children: [
437
+ /* @__PURE__ */ jsx(
438
+ "input",
439
+ {
440
+ type: inputType,
441
+ autoComplete: newPassword ? "new-password" : void 0,
442
+ placeholder: props.placeholder,
443
+ value: props.value ?? "",
444
+ disabled: props.disabled,
445
+ onChange: (e) => props.onChange?.(e.target.value),
446
+ className: twMerge(
447
+ inputBaseClass,
448
+ inputSizeClasses[size],
449
+ password && "pr-8",
450
+ hasError && "border-(--ui-danger)",
451
+ props.classNames?.input
452
+ )
453
+ }
454
+ ),
455
+ password && /* @__PURE__ */ jsx(
456
+ "button",
457
+ {
458
+ type: "button",
459
+ tabIndex: -1,
460
+ onClick: () => setShowPassword((v) => !v),
461
+ className: twMerge(
462
+ "absolute right-2 top-1/2 -translate-y-1/2 cursor-pointer",
463
+ neutralIconClasses.default,
464
+ neutralIconClasses.hover,
465
+ props.classNames?.passwordToggle
466
+ ),
467
+ children: showPassword ? /* @__PURE__ */ jsx(IconEyeOff, { size: 14, strokeWidth: 1.5 }) : /* @__PURE__ */ jsx(IconEye, { size: 14, strokeWidth: 1.5 })
468
+ }
469
+ )
470
+ ] }),
471
+ errorDisplay && /* @__PURE__ */ jsx(InputError, { error: String(errorDisplay), className: props.classNames?.error })
472
+ ] });
473
+ };
474
+ var numberInputClass = inputBaseClass + " [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none";
475
+ var NumberInput = ({
476
+ size = "middle",
477
+ ...props
478
+ }) => {
479
+ const resolveError = useErrorResolver();
480
+ const resolvedErrors = props.errorName ? resolveError(props.errorName) : [];
481
+ const errorDisplay = props.error ?? props.customError ?? (resolvedErrors.length > 0 ? resolvedErrors.join(", ") : void 0);
482
+ const hasError = !!errorDisplay;
483
+ const handleChange = (e) => {
484
+ const raw = e.target.value;
485
+ if (raw === "" || raw === "-") {
486
+ props.onChange?.(null);
487
+ return;
488
+ }
489
+ const num = parseFloat(raw);
490
+ if (!isNaN(num)) {
491
+ props.onChange?.(num);
492
+ }
493
+ };
494
+ return /* @__PURE__ */ jsxs(InputField, { noMargin: props.noMargin, className: props.className, children: [
495
+ props.label && /* @__PURE__ */ jsx(
496
+ InputLabel,
497
+ {
498
+ label: props.label,
499
+ required: props.required,
500
+ className: props.classNames?.label,
501
+ requiredMarkClassName: props.classNames?.requiredMark
502
+ }
503
+ ),
504
+ /* @__PURE__ */ jsx(
505
+ "input",
506
+ {
507
+ type: "number",
508
+ placeholder: props.placeholder,
509
+ value: props.value ?? "",
510
+ disabled: props.disabled,
511
+ min: props.min,
512
+ max: props.max,
513
+ onChange: handleChange,
514
+ className: twMerge(
515
+ numberInputClass,
516
+ inputSizeClasses[size],
517
+ hasError && "border-(--ui-danger)",
518
+ props.classNames?.input
519
+ )
520
+ }
521
+ ),
522
+ errorDisplay && /* @__PURE__ */ jsx(InputError, { error: String(errorDisplay), className: props.classNames?.error })
523
+ ] });
524
+ };
525
+ var CheckboxInput = ({
526
+ value = false,
527
+ ...props
528
+ }) => {
529
+ const resolveError = useErrorResolver();
530
+ const resolvedErrors = props.errorName ? resolveError(props.errorName) : [];
531
+ const errorDisplay = props.error ?? props.customError ?? (resolvedErrors.length > 0 ? resolvedErrors.join(", ") : void 0);
532
+ const hasError = !!errorDisplay;
533
+ const checked = value ?? false;
534
+ return /* @__PURE__ */ jsxs(InputField, { noMargin: props.noMargin, className: props.className, children: [
535
+ /* @__PURE__ */ jsxs(
536
+ "label",
537
+ {
538
+ className: twMerge(
539
+ "inline-flex items-center gap-2",
540
+ props.disabled ? "cursor-not-allowed opacity-50" : "cursor-pointer",
541
+ props.classNames?.label
542
+ ),
543
+ children: [
544
+ /* @__PURE__ */ jsx(
545
+ "input",
546
+ {
547
+ type: "checkbox",
548
+ checked,
549
+ disabled: props.disabled,
550
+ onChange: (e) => props.onChange?.(e.target.checked),
551
+ className: "sr-only peer"
552
+ }
553
+ ),
554
+ /* @__PURE__ */ jsx(
555
+ "span",
556
+ {
557
+ className: twMerge(
558
+ "relative shrink-0 w-4 h-4 rounded border transition-colors",
559
+ "peer-focus-visible:ring-2 peer-focus-visible:ring-(--ui-border-focus) peer-focus-visible:ring-offset-2",
560
+ checked ? "bg-(--ui-primary) border-(--ui-primary)" : "bg-white border-(--ui-border-strong)",
561
+ hasError && "border-(--ui-danger)",
562
+ props.classNames?.control
563
+ ),
564
+ children: checked && /* @__PURE__ */ jsx(
565
+ IconCheck,
566
+ {
567
+ size: 12,
568
+ strokeWidth: 2.5,
569
+ className: twMerge("absolute inset-0 m-auto text-white", props.classNames?.indicator)
570
+ }
571
+ )
572
+ }
573
+ ),
574
+ props.label && /* @__PURE__ */ jsx("span", { className: twMerge("text-sm", neutralTextClasses.default, props.classNames?.text), children: props.label })
575
+ ]
576
+ }
577
+ ),
578
+ errorDisplay && /* @__PURE__ */ jsx(InputError, { error: String(errorDisplay), className: props.classNames?.error })
579
+ ] });
580
+ };
581
+ var SwitchInput = ({
582
+ value = false,
583
+ size = "middle",
584
+ ...props
585
+ }) => {
586
+ const resolveError = useErrorResolver();
587
+ const resolvedErrors = props.errorName ? resolveError(props.errorName) : [];
588
+ const errorDisplay = props.error ?? props.customError ?? (resolvedErrors.length > 0 ? resolvedErrors.join(", ") : void 0);
589
+ const hasError = !!errorDisplay;
590
+ const checked = value ?? false;
591
+ const trackClasses = size === "small" ? "h-5 w-9 p-0.5" : "h-6 w-11 p-0.5";
592
+ const thumbSizeClass = size === "small" ? "h-4 w-4" : "h-5 w-5";
593
+ const thumbPositionClass = size === "small" ? "left-[1px] top-1/2 -translate-y-1/2 peer-checked:translate-x-4" : "left-[1px] top-1/2 -translate-y-1/2 peer-checked:translate-x-5";
594
+ return /* @__PURE__ */ jsxs(InputField, { noMargin: props.noMargin, className: props.className, children: [
595
+ /* @__PURE__ */ jsxs(
596
+ "label",
597
+ {
598
+ className: twMerge(
599
+ "inline-flex items-center gap-3",
600
+ props.disabled ? "cursor-not-allowed opacity-50" : "cursor-pointer",
601
+ props.classNames?.label
602
+ ),
603
+ children: [
604
+ /* @__PURE__ */ jsx(
605
+ "input",
606
+ {
607
+ type: "checkbox",
608
+ checked,
609
+ disabled: props.disabled,
610
+ onChange: (e) => props.onChange?.(e.target.checked),
611
+ className: "sr-only peer"
612
+ }
613
+ ),
614
+ /* @__PURE__ */ jsx(
615
+ "span",
616
+ {
617
+ className: twMerge(
618
+ "relative shrink-0 rounded-full border transition-colors",
619
+ "peer-focus-visible:ring-2 peer-focus-visible:ring-(--ui-border-focus) peer-focus-visible:ring-offset-2",
620
+ trackClasses,
621
+ checked ? "border-(--ui-primary) bg-(--ui-primary)" : "border-(--ui-border-strong) bg-(--ui-surface-muted)",
622
+ hasError && "border-(--ui-danger)",
623
+ props.classNames?.track
624
+ ),
625
+ children: /* @__PURE__ */ jsx(
626
+ "span",
627
+ {
628
+ className: twMerge(
629
+ "absolute rounded-full bg-white shadow-sm transition-transform",
630
+ thumbSizeClass,
631
+ thumbPositionClass,
632
+ props.classNames?.thumb
633
+ )
634
+ }
635
+ )
636
+ }
637
+ ),
638
+ (props.label || props.checkedLabel || props.uncheckedLabel) && /* @__PURE__ */ jsx("span", { className: twMerge("text-sm", neutralTextClasses.default, props.classNames?.text), children: props.label ?? (checked ? props.checkedLabel : props.uncheckedLabel) })
639
+ ]
640
+ }
641
+ ),
642
+ errorDisplay && /* @__PURE__ */ jsx(InputError, { error: String(errorDisplay), className: props.classNames?.error })
643
+ ] });
644
+ };
645
+ var GAP = 6;
646
+ var VIEWPORT_MARGIN = 8;
647
+ var FLOATING_ROOT_SELECTOR = "[data-ui-floating-root]";
648
+ function calcPosition(triggerRect, panelRect, placement) {
649
+ const spaceBelow = window.innerHeight - triggerRect.bottom - VIEWPORT_MARGIN;
650
+ const spaceAbove = triggerRect.top - VIEWPORT_MARGIN;
651
+ const openAbove = spaceBelow < panelRect.height && spaceAbove > spaceBelow;
652
+ const preferEnd = placement === "bottom-end";
653
+ const startLeft = triggerRect.left;
654
+ const endLeft = triggerRect.right - panelRect.width;
655
+ const overflowsRightWithStart = startLeft + panelRect.width > window.innerWidth - VIEWPORT_MARGIN;
656
+ const overflowsLeftWithEnd = endLeft < VIEWPORT_MARGIN;
657
+ const startFits = !overflowsRightWithStart;
658
+ const endFits = !overflowsLeftWithEnd;
659
+ let unclampedLeft = preferEnd ? endLeft : startLeft;
660
+ if (placement === "auto") {
661
+ if (endFits && (!startFits || triggerRect.right > window.innerWidth / 2)) {
662
+ unclampedLeft = endLeft;
663
+ } else {
664
+ unclampedLeft = startLeft;
665
+ }
666
+ }
667
+ if (placement !== "auto" && !preferEnd && overflowsRightWithStart && endLeft >= VIEWPORT_MARGIN) {
668
+ unclampedLeft = endLeft;
669
+ } else if (placement !== "auto" && preferEnd && overflowsLeftWithEnd && startLeft + panelRect.width <= window.innerWidth - VIEWPORT_MARGIN) {
670
+ unclampedLeft = startLeft;
671
+ }
672
+ return {
673
+ top: openAbove ? Math.max(triggerRect.top - panelRect.height - GAP, VIEWPORT_MARGIN) : Math.min(triggerRect.bottom + GAP, window.innerHeight - panelRect.height - VIEWPORT_MARGIN),
674
+ left: Math.min(
675
+ Math.max(unclampedLeft, VIEWPORT_MARGIN),
676
+ Math.max(window.innerWidth - panelRect.width - VIEWPORT_MARGIN, VIEWPORT_MARGIN)
677
+ ),
678
+ minWidth: triggerRect.width,
679
+ maxHeight: Math.max((openAbove ? spaceAbove : spaceBelow) - GAP, 120)
680
+ };
681
+ }
682
+ var isAction = (option) => !option.divider;
683
+ var Dropdown = ({
684
+ children,
685
+ options,
686
+ content,
687
+ open: controlledOpen,
688
+ onOpenChange,
689
+ placement = "auto",
690
+ panelClassName,
691
+ className,
692
+ matchTriggerWidth = true,
693
+ classNames
694
+ }) => {
695
+ const triggerRef = useRef(null);
696
+ const popupRef = useRef(null);
697
+ const [uncontrolledOpen, setUncontrolledOpen] = useState(false);
698
+ const [pos, setPos] = useState(null);
699
+ const [pendingConfirm, setPendingConfirm] = useState(null);
700
+ const open = controlledOpen ?? uncontrolledOpen;
701
+ const setOpen = (next) => {
702
+ if (next) {
703
+ setPos(null);
704
+ }
705
+ if (controlledOpen === void 0) {
706
+ setUncontrolledOpen(next);
707
+ }
708
+ onOpenChange?.(next);
709
+ };
710
+ useLayoutEffect(() => {
711
+ if (!open || !triggerRef.current) {
712
+ return;
713
+ }
714
+ const triggerEl = triggerRef.current;
715
+ let frame = 0;
716
+ let resizeObserver = null;
717
+ const updatePosition = () => {
718
+ if (!triggerEl || !popupRef.current) {
719
+ return;
720
+ }
721
+ setPos(calcPosition(
722
+ triggerEl.getBoundingClientRect(),
723
+ popupRef.current.getBoundingClientRect(),
724
+ placement
725
+ ));
726
+ };
727
+ frame = requestAnimationFrame(updatePosition);
728
+ if (typeof ResizeObserver !== "undefined" && popupRef.current) {
729
+ resizeObserver = new ResizeObserver(() => updatePosition());
730
+ resizeObserver.observe(popupRef.current);
731
+ }
732
+ window.addEventListener("resize", updatePosition);
733
+ window.addEventListener("scroll", updatePosition, true);
734
+ return () => {
735
+ cancelAnimationFrame(frame);
736
+ resizeObserver?.disconnect();
737
+ window.removeEventListener("resize", updatePosition);
738
+ window.removeEventListener("scroll", updatePosition, true);
739
+ };
740
+ }, [open, placement]);
741
+ useEffect(() => {
742
+ if (!open) {
743
+ return;
744
+ }
745
+ const handleMouseDown = (e) => {
746
+ const target = e.target;
747
+ const targetElement = target instanceof Element ? target : null;
748
+ const isInsideAnotherFloatingRoot = !!targetElement?.closest(FLOATING_ROOT_SELECTOR);
749
+ if (!isInsideAnotherFloatingRoot && popupRef.current && !popupRef.current.contains(target) && triggerRef.current && !triggerRef.current.contains(target)) {
750
+ setOpen(false);
751
+ }
752
+ };
753
+ const handleKeyDown = (e) => {
754
+ if (e.key === "Escape") {
755
+ setOpen(false);
756
+ }
757
+ };
758
+ document.addEventListener("mousedown", handleMouseDown);
759
+ document.addEventListener("keydown", handleKeyDown);
760
+ return () => {
761
+ document.removeEventListener("mousedown", handleMouseDown);
762
+ document.removeEventListener("keydown", handleKeyDown);
763
+ };
764
+ }, [open]);
765
+ const visibleOptions = (options ?? []).filter((option) => option.visible !== false);
766
+ const renderedContent = typeof content === "function" ? content({ close: () => setOpen(false) }) : content;
767
+ const panel = renderedContent ?? /* @__PURE__ */ jsx("div", { className: twMerge("py-[calc(var(--ui-popup-row-py)/2)]", classNames?.content), children: visibleOptions.map((option, index) => {
768
+ if (!isAction(option)) {
769
+ return /* @__PURE__ */ jsx("div", { className: twMerge("my-1 border-t border-(--ui-border)", classNames?.divider) }, `divider-${index}`);
770
+ }
771
+ const Icon = option.icon ?? (option.iconType ? getIcon(option.iconType) : void 0);
772
+ const handleSelect = () => {
773
+ if (option.disabled) {
774
+ return;
775
+ }
776
+ const shouldClose = option.closeOnSelect ?? true;
777
+ if (shouldClose) {
778
+ setOpen(false);
779
+ }
780
+ if (option.confirm) {
781
+ const target = popupRef.current?.getBoundingClientRect() ?? triggerRef.current?.getBoundingClientRect();
782
+ if (target) {
783
+ setPendingConfirm({ option, anchorRect: target });
784
+ }
785
+ return;
786
+ }
787
+ option.onSelect?.();
788
+ };
789
+ if (option.custom) {
790
+ return /* @__PURE__ */ jsx(
791
+ "div",
792
+ {
793
+ className: twMerge("w-full", popupLayoutClasses.compactHeader, option.className, classNames?.content),
794
+ style: option.color ? { color: option.color } : void 0,
795
+ children: option.label
796
+ },
797
+ `option-${index}`
798
+ );
799
+ }
800
+ return /* @__PURE__ */ jsxs(
801
+ "button",
802
+ {
803
+ type: "button",
804
+ disabled: option.disabled,
805
+ onClick: handleSelect,
806
+ className: twMerge(
807
+ "w-full flex items-center text-left transition-colors",
808
+ popupLayoutClasses.compactRow,
809
+ "disabled:opacity-50 disabled:cursor-not-allowed",
810
+ option.disabled ? "" : "cursor-pointer hover:bg-gray-50",
811
+ option.className,
812
+ classNames?.option
813
+ ),
814
+ style: option.color ? { color: option.color } : void 0,
815
+ children: [
816
+ Icon && /* @__PURE__ */ jsx(Icon, { size: 14, strokeWidth: 1.5, className: twMerge("shrink-0", classNames?.optionIcon) }),
817
+ /* @__PURE__ */ jsx("span", { className: twMerge("min-w-0 truncate", classNames?.optionLabel), children: option.label })
818
+ ]
819
+ },
820
+ `option-${index}`
821
+ );
822
+ }) });
823
+ const confirmAnchorRect = useMemo(() => {
824
+ if (!pendingConfirm) return null;
825
+ const rect = pendingConfirm.anchorRect;
826
+ if (placement === "bottom-end") {
827
+ return {
828
+ ...rect,
829
+ left: rect.right - 220,
830
+ right: rect.right
831
+ };
832
+ }
833
+ return rect;
834
+ }, [pendingConfirm, placement]);
835
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
836
+ /* @__PURE__ */ jsx(
837
+ "span",
838
+ {
839
+ ref: triggerRef,
840
+ onClick: () => setOpen(!open),
841
+ className: twMerge("inline-flex", className, classNames?.trigger),
842
+ children
843
+ }
844
+ ),
845
+ open && createPortal(
846
+ /* @__PURE__ */ jsx(
847
+ "div",
848
+ {
849
+ ref: popupRef,
850
+ "data-ui-floating-root": "",
851
+ className: twMerge(
852
+ "fixed z-1000 min-w-40 overflow-auto rounded-(--ui-radius-lg) border border-(--ui-border) bg-white shadow-lg",
853
+ !pos && "invisible",
854
+ panelClassName,
855
+ classNames?.panel
856
+ ),
857
+ style: {
858
+ top: pos?.top ?? 0,
859
+ left: pos?.left ?? 0,
860
+ minWidth: matchTriggerWidth ? pos?.minWidth ?? triggerRef.current?.getBoundingClientRect().width : void 0,
861
+ maxHeight: pos?.maxHeight
862
+ },
863
+ children: panel
864
+ }
865
+ ),
866
+ document.body
867
+ ),
868
+ pendingConfirm && /* @__PURE__ */ jsx(
869
+ Popconfirm,
870
+ {
871
+ open: true,
872
+ anchorRect: confirmAnchorRect,
873
+ message: pendingConfirm.option.confirm?.message ?? "",
874
+ confirmOk: pendingConfirm.option.confirm?.confirmOk,
875
+ confirmCancel: pendingConfirm.option.confirm?.confirmCancel,
876
+ confirmColor: pendingConfirm.option.confirm?.confirmColor,
877
+ onOpenChange: (next) => {
878
+ if (!next) setPendingConfirm(null);
879
+ },
880
+ onCancel: () => setPendingConfirm(null),
881
+ onConfirm: () => {
882
+ pendingConfirm.option.onSelect?.();
883
+ setPendingConfirm(null);
884
+ }
885
+ }
886
+ )
887
+ ] });
888
+ };
889
+ function clamp(value, min, max) {
890
+ return Math.min(max, Math.max(min, value));
891
+ }
892
+ function normalizeHex(value) {
893
+ if (!value) return null;
894
+ const raw = value.trim().replace(/^#/, "");
895
+ if (/^[0-9a-fA-F]{3}$/.test(raw)) {
896
+ return `#${raw.split("").map((char) => char + char).join("").toUpperCase()}`;
897
+ }
898
+ if (/^[0-9a-fA-F]{4}$/.test(raw)) {
899
+ return `#${raw.split("").map((char) => char + char).join("").toUpperCase()}`;
900
+ }
901
+ if (/^[0-9a-fA-F]{6}$/.test(raw) || /^[0-9a-fA-F]{8}$/.test(raw)) {
902
+ return `#${raw.toUpperCase()}`;
903
+ }
904
+ return null;
905
+ }
906
+ function hexToRgba(value) {
907
+ const normalized = normalizeHex(value);
908
+ if (!normalized) {
909
+ return { r: 37, g: 99, b: 235, a: 1 };
910
+ }
911
+ const raw = normalized.slice(1);
912
+ const hasAlpha = raw.length === 8;
913
+ return {
914
+ r: parseInt(raw.slice(0, 2), 16),
915
+ g: parseInt(raw.slice(2, 4), 16),
916
+ b: parseInt(raw.slice(4, 6), 16),
917
+ a: hasAlpha ? parseInt(raw.slice(6, 8), 16) / 255 : 1
918
+ };
919
+ }
920
+ function rgbaToHex(r, g, b, a = 1) {
921
+ const toHex = (value) => clamp(Math.round(value), 0, 255).toString(16).padStart(2, "0").toUpperCase();
922
+ const rgb = `#${toHex(r)}${toHex(g)}${toHex(b)}`;
923
+ if (a >= 0.999) return rgb;
924
+ return `${rgb}${toHex(a * 255)}`;
925
+ }
926
+ function rgbToHsv(r, g, b) {
927
+ const rn = r / 255;
928
+ const gn = g / 255;
929
+ const bn = b / 255;
930
+ const max = Math.max(rn, gn, bn);
931
+ const min = Math.min(rn, gn, bn);
932
+ const delta = max - min;
933
+ let h = 0;
934
+ if (delta !== 0) {
935
+ if (max === rn) h = 60 * ((gn - bn) / delta % 6);
936
+ else if (max === gn) h = 60 * ((bn - rn) / delta + 2);
937
+ else h = 60 * ((rn - gn) / delta + 4);
938
+ }
939
+ return {
940
+ h: h < 0 ? h + 360 : h,
941
+ s: max === 0 ? 0 : delta / max,
942
+ v: max,
943
+ a: 1
944
+ };
945
+ }
946
+ function hsvToRgb(h, s, v) {
947
+ const c = v * s;
948
+ const x = c * (1 - Math.abs(h / 60 % 2 - 1));
949
+ const m = v - c;
950
+ let r1 = 0;
951
+ let g1 = 0;
952
+ let b1 = 0;
953
+ if (h >= 0 && h < 60) [r1, g1, b1] = [c, x, 0];
954
+ else if (h < 120) [r1, g1, b1] = [x, c, 0];
955
+ else if (h < 180) [r1, g1, b1] = [0, c, x];
956
+ else if (h < 240) [r1, g1, b1] = [0, x, c];
957
+ else if (h < 300) [r1, g1, b1] = [x, 0, c];
958
+ else [r1, g1, b1] = [c, 0, x];
959
+ return {
960
+ r: Math.round((r1 + m) * 255),
961
+ g: Math.round((g1 + m) * 255),
962
+ b: Math.round((b1 + m) * 255)
963
+ };
964
+ }
965
+ function hexToHsva(value) {
966
+ const rgba = hexToRgba(value);
967
+ const hsv = rgbToHsv(rgba.r, rgba.g, rgba.b);
968
+ return { ...hsv, a: rgba.a };
969
+ }
970
+ function hsvaToHex(value) {
971
+ const rgb = hsvToRgb(value.h, value.s, value.v);
972
+ return rgbaToHex(rgb.r, rgb.g, rgb.b, value.a);
973
+ }
974
+ var ColorInput = ({
975
+ size = "middle",
976
+ placeholder = "Vyberte barvu",
977
+ presets,
978
+ allowClear = true,
979
+ variant = "default",
980
+ showAlpha = false,
981
+ ...props
982
+ }) => {
983
+ const resolveError = useErrorResolver();
984
+ const resolvedErrors = props.errorName ? resolveError(props.errorName) : [];
985
+ const errorDisplay = props.error ?? props.customError ?? (resolvedErrors.length > 0 ? resolvedErrors.join(", ") : void 0);
986
+ const hasError = !!errorDisplay;
987
+ const [open, setOpen] = useState(false);
988
+ const normalizedValue = normalizeHex(props.value);
989
+ const [draft, setDraft] = useState(normalizedValue ?? "");
990
+ const [pickerValue, setPickerValue] = useState(() => hexToHsva(props.value));
991
+ const saturationRef = useRef(null);
992
+ useEffect(() => {
993
+ setDraft(normalizedValue ?? "");
994
+ setPickerValue(hexToHsva(normalizedValue));
995
+ }, [normalizedValue]);
996
+ const normalizedPresets = useMemo(
997
+ () => (presets ?? []).map(normalizeHex).filter((value) => !!value),
998
+ [presets]
999
+ );
1000
+ const commitHex = (next) => {
1001
+ setDraft(next ?? "");
1002
+ setPickerValue(hexToHsva(next));
1003
+ props.onChange?.(next);
1004
+ };
1005
+ const commitDraft = () => {
1006
+ const next = normalizeHex(draft);
1007
+ if (!draft.trim()) {
1008
+ commitHex(null);
1009
+ return;
1010
+ }
1011
+ if (next) {
1012
+ commitHex(next);
1013
+ } else {
1014
+ setDraft(normalizedValue ?? "");
1015
+ }
1016
+ };
1017
+ const updatePicker = (next) => {
1018
+ const merged = {
1019
+ ...pickerValue,
1020
+ ...next,
1021
+ h: clamp(next.h ?? pickerValue.h, 0, 360),
1022
+ s: clamp(next.s ?? pickerValue.s, 0, 1),
1023
+ v: clamp(next.v ?? pickerValue.v, 0, 1),
1024
+ a: clamp(next.a ?? pickerValue.a, 0, 1)
1025
+ };
1026
+ setPickerValue(merged);
1027
+ const result = hsvaToHex(showAlpha ? merged : { ...merged, a: 1 });
1028
+ setDraft(result);
1029
+ props.onChange?.(result);
1030
+ };
1031
+ const hueColor = hsvaToHex({ h: pickerValue.h, s: 1, v: 1, a: 1 });
1032
+ const triggerText = normalizedValue ?? placeholder;
1033
+ const updateSaturationFromPointer = (clientX, clientY) => {
1034
+ if (!saturationRef.current) return;
1035
+ const rect = saturationRef.current.getBoundingClientRect();
1036
+ const s = clamp((clientX - rect.left) / rect.width, 0, 1);
1037
+ const v = clamp(1 - (clientY - rect.top) / rect.height, 0, 1);
1038
+ updatePicker({ s, v });
1039
+ };
1040
+ const startSaturationDrag = (clientX, clientY) => {
1041
+ updateSaturationFromPointer(clientX, clientY);
1042
+ const handleMove = (e) => updateSaturationFromPointer(e.clientX, e.clientY);
1043
+ const handleUp = () => {
1044
+ window.removeEventListener("mousemove", handleMove);
1045
+ window.removeEventListener("mouseup", handleUp);
1046
+ };
1047
+ window.addEventListener("mousemove", handleMove);
1048
+ window.addEventListener("mouseup", handleUp);
1049
+ };
1050
+ return /* @__PURE__ */ jsxs(InputField, { noMargin: props.noMargin, className: props.className, children: [
1051
+ props.label && /* @__PURE__ */ jsx(
1052
+ InputLabel,
1053
+ {
1054
+ label: props.label,
1055
+ required: props.required,
1056
+ className: props.classNames?.label,
1057
+ requiredMarkClassName: props.classNames?.requiredMark
1058
+ }
1059
+ ),
1060
+ /* @__PURE__ */ jsx(
1061
+ Dropdown,
1062
+ {
1063
+ open,
1064
+ onOpenChange: (next) => !props.disabled && setOpen(next),
1065
+ placement: "bottom-start",
1066
+ matchTriggerWidth: false,
1067
+ panelClassName: variant === "picker" ? "w-[320px] max-w-[320px]" : "w-[256px] max-w-[256px]",
1068
+ content: () => /* @__PURE__ */ jsxs("div", { className: twMerge("grid gap-3", popupLayoutClasses.panel, props.classNames?.panel), children: [
1069
+ variant === "picker" ? /* @__PURE__ */ jsxs(Fragment, { children: [
1070
+ /* @__PURE__ */ jsxs("div", { className: "grid gap-3", children: [
1071
+ /* @__PURE__ */ jsxs(
1072
+ "div",
1073
+ {
1074
+ ref: saturationRef,
1075
+ onMouseDown: (e) => {
1076
+ e.preventDefault();
1077
+ startSaturationDrag(e.clientX, e.clientY);
1078
+ },
1079
+ className: twMerge("relative h-44 rounded-(--ui-radius-lg) border border-(--ui-border) cursor-crosshair overflow-hidden", props.classNames?.saturation),
1080
+ style: { backgroundColor: hueColor },
1081
+ children: [
1082
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-linear-to-r from-white to-transparent" }),
1083
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-linear-to-t from-black to-transparent" }),
1084
+ /* @__PURE__ */ jsx(
1085
+ "span",
1086
+ {
1087
+ className: "absolute h-4 w-4 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-white shadow-sm",
1088
+ style: {
1089
+ left: `${pickerValue.s * 100}%`,
1090
+ top: `${(1 - pickerValue.v) * 100}%`
1091
+ }
1092
+ }
1093
+ )
1094
+ ]
1095
+ }
1096
+ ),
1097
+ /* @__PURE__ */ jsxs("div", { className: "grid gap-2", children: [
1098
+ /* @__PURE__ */ jsx(
1099
+ "input",
1100
+ {
1101
+ type: "range",
1102
+ min: 0,
1103
+ max: 360,
1104
+ value: pickerValue.h,
1105
+ onChange: (e) => updatePicker({ h: Number(e.target.value) }),
1106
+ className: twMerge("h-2 w-full cursor-pointer appearance-none rounded-full border border-(--ui-border)", props.classNames?.hueSlider),
1107
+ style: {
1108
+ background: "linear-gradient(90deg, #FF0000 0%, #FFFF00 17%, #00FF00 33%, #00FFFF 50%, #0000FF 67%, #FF00FF 83%, #FF0000 100%)"
1109
+ }
1110
+ }
1111
+ ),
1112
+ showAlpha && /* @__PURE__ */ jsx(
1113
+ "input",
1114
+ {
1115
+ type: "range",
1116
+ min: 0,
1117
+ max: 100,
1118
+ value: Math.round(pickerValue.a * 100),
1119
+ onChange: (e) => updatePicker({ a: Number(e.target.value) / 100 }),
1120
+ className: twMerge("h-2 w-full cursor-pointer appearance-none rounded-full border border-(--ui-border)", props.classNames?.alphaSlider),
1121
+ style: {
1122
+ background: `linear-gradient(90deg, transparent 0%, ${hsvaToHex({ ...pickerValue, a: 1 })} 100%)`
1123
+ }
1124
+ }
1125
+ )
1126
+ ] })
1127
+ ] }),
1128
+ /* @__PURE__ */ jsxs("div", { className: twMerge("grid grid-cols-[1fr_auto] gap-2", props.classNames?.hexRow), children: [
1129
+ /* @__PURE__ */ jsx(
1130
+ "input",
1131
+ {
1132
+ type: "text",
1133
+ value: draft,
1134
+ disabled: props.disabled,
1135
+ placeholder: showAlpha ? "#000000FF" : "#000000",
1136
+ onChange: (e) => setDraft(e.target.value),
1137
+ onBlur: commitDraft,
1138
+ onKeyDown: (e) => {
1139
+ if (e.key === "Enter") {
1140
+ e.preventDefault();
1141
+ commitDraft();
1142
+ }
1143
+ },
1144
+ className: twMerge(inputBaseClass, inputSizeClasses.small, props.classNames?.hexInput)
1145
+ }
1146
+ ),
1147
+ /* @__PURE__ */ jsx(
1148
+ "div",
1149
+ {
1150
+ className: twMerge("h-(--ui-h-sm) w-11 rounded-(--ui-radius-md) border border-(--ui-border)", props.classNames?.preview),
1151
+ style: { backgroundColor: normalizedValue ?? "transparent" }
1152
+ }
1153
+ )
1154
+ ] })
1155
+ ] }) : /* @__PURE__ */ jsxs("div", { className: "grid gap-2", children: [
1156
+ /* @__PURE__ */ jsxs("div", { className: twMerge("flex items-center gap-3", props.classNames?.hexRow), children: [
1157
+ /* @__PURE__ */ jsxs(
1158
+ "label",
1159
+ {
1160
+ className: twMerge(
1161
+ "relative block h-11 w-11 shrink-0 overflow-hidden rounded-(--ui-radius-md) border border-(--ui-border) cursor-pointer",
1162
+ props.disabled && "pointer-events-none opacity-60"
1163
+ ),
1164
+ children: [
1165
+ /* @__PURE__ */ jsx(
1166
+ "input",
1167
+ {
1168
+ type: "color",
1169
+ value: normalizedValue ?? "#000000",
1170
+ disabled: props.disabled,
1171
+ onChange: (e) => {
1172
+ const next = normalizeHex(e.target.value);
1173
+ commitHex(next);
1174
+ },
1175
+ className: "absolute inset-0 h-full w-full cursor-pointer opacity-0"
1176
+ }
1177
+ ),
1178
+ /* @__PURE__ */ jsx(
1179
+ "span",
1180
+ {
1181
+ className: "absolute inset-0",
1182
+ style: { backgroundColor: normalizedValue ?? "var(--ui-surface-subtle)" }
1183
+ }
1184
+ )
1185
+ ]
1186
+ }
1187
+ ),
1188
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx(
1189
+ "input",
1190
+ {
1191
+ type: "text",
1192
+ value: draft,
1193
+ disabled: props.disabled,
1194
+ placeholder: "#000000",
1195
+ onChange: (e) => setDraft(e.target.value),
1196
+ onBlur: commitDraft,
1197
+ onKeyDown: (e) => {
1198
+ if (e.key === "Enter") {
1199
+ e.preventDefault();
1200
+ commitDraft();
1201
+ }
1202
+ },
1203
+ className: twMerge(inputBaseClass, inputSizeClasses.small, props.classNames?.hexInput)
1204
+ }
1205
+ ) })
1206
+ ] }),
1207
+ /* @__PURE__ */ jsx("p", { className: twMerge("text-xs", neutralTextClasses.muted), children: "Zadejte HEX barvu nebo ji vyberte z pickeru." })
1208
+ ] }),
1209
+ normalizedPresets.length > 0 && /* @__PURE__ */ jsxs("div", { className: twMerge("grid gap-2", props.classNames?.presets), children: [
1210
+ /* @__PURE__ */ jsx("div", { className: twMerge("text-xs font-medium", neutralTextClasses.muted), children: "Rychl\xE9 volby" }),
1211
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: normalizedPresets.map((color) => /* @__PURE__ */ jsx(
1212
+ "button",
1213
+ {
1214
+ type: "button",
1215
+ onClick: () => commitHex(color),
1216
+ className: twMerge(
1217
+ "h-7 w-7 rounded-(--ui-radius-sm) border border-(--ui-border) transition-transform cursor-pointer hover:scale-105",
1218
+ normalizedValue === color && "ring-2 ring-(--ui-border-focus) ring-offset-2 ring-offset-white",
1219
+ props.classNames?.presetButton
1220
+ ),
1221
+ style: { backgroundColor: color },
1222
+ "aria-label": `Vybrat barvu ${color}`
1223
+ },
1224
+ color
1225
+ )) })
1226
+ ] })
1227
+ ] }),
1228
+ children: /* @__PURE__ */ jsxs(
1229
+ "div",
1230
+ {
1231
+ className: twMerge(
1232
+ "flex items-center gap-2 border bg-white cursor-pointer transition-colors select-none",
1233
+ inputSizeClasses[size],
1234
+ open ? triggerBorderClasses.open : triggerBorderClasses.default,
1235
+ props.disabled && triggerBorderClasses.disabled,
1236
+ hasError && triggerBorderClasses.error,
1237
+ props.classNames?.trigger
1238
+ ),
1239
+ children: [
1240
+ /* @__PURE__ */ jsx(
1241
+ "span",
1242
+ {
1243
+ className: twMerge(
1244
+ "h-4 w-4 shrink-0 rounded-(--ui-radius-sm) border border-(--ui-border)",
1245
+ !normalizedValue && neutralSurfaceClasses.subtle,
1246
+ props.classNames?.swatch
1247
+ ),
1248
+ style: normalizedValue ? { backgroundColor: normalizedValue } : void 0
1249
+ }
1250
+ ),
1251
+ /* @__PURE__ */ jsx("span", { className: twMerge("min-w-0 flex-1 truncate", normalizedValue ? neutralTextClasses.strong : neutralTextClasses.soft, props.classNames?.value), children: triggerText }),
1252
+ allowClear && normalizedValue && !props.disabled && /* @__PURE__ */ jsx(
1253
+ "button",
1254
+ {
1255
+ type: "button",
1256
+ onClick: (e) => {
1257
+ e.stopPropagation();
1258
+ commitHex(null);
1259
+ },
1260
+ className: twMerge("shrink-0 cursor-pointer", neutralIconClasses.default, neutralIconClasses.hover, props.classNames?.clearButton),
1261
+ children: /* @__PURE__ */ jsx(IconX, { size: 12, strokeWidth: 2 })
1262
+ }
1263
+ ),
1264
+ /* @__PURE__ */ jsx(IconDroplet, { size: 14, strokeWidth: 1.5, className: twMerge("shrink-0", neutralIconClasses.default, props.classNames?.icon) })
1265
+ ]
1266
+ }
1267
+ )
1268
+ }
1269
+ ),
1270
+ errorDisplay && /* @__PURE__ */ jsx(InputError, { error: String(errorDisplay), className: props.classNames?.error })
1271
+ ] });
1272
+ };
1273
+ var Empty = ({
1274
+ text = "\u017D\xE1dn\xE9 z\xE1znamy k dispozici",
1275
+ description,
1276
+ icon: Icon = IconDatabaseOff,
1277
+ className,
1278
+ size = "default"
1279
+ }) => /* @__PURE__ */ jsxs(
1280
+ "div",
1281
+ {
1282
+ className: twMerge(
1283
+ "flex flex-col items-center justify-center text-center",
1284
+ size === "default" ? emptyLayoutClasses.default : emptyLayoutClasses.compact,
1285
+ className
1286
+ ),
1287
+ children: [
1288
+ /* @__PURE__ */ jsx(
1289
+ "div",
1290
+ {
1291
+ className: twMerge(
1292
+ "mb-3 flex items-center justify-center rounded-full border border-dashed border-(--ui-border) bg-(--ui-surface-subtle)",
1293
+ size === "default" ? emptyLayoutClasses.defaultIconBox : twMerge("mb-2.5", emptyLayoutClasses.compactIconBox)
1294
+ ),
1295
+ children: /* @__PURE__ */ jsx(
1296
+ Icon,
1297
+ {
1298
+ size: "1em",
1299
+ strokeWidth: 1.5,
1300
+ className: twMerge(
1301
+ neutralTextClasses.soft,
1302
+ size === "default" ? "[font-size:var(--ui-empty-icon)]" : "[font-size:var(--ui-empty-compact-icon)]"
1303
+ )
1304
+ }
1305
+ )
1306
+ }
1307
+ ),
1308
+ /* @__PURE__ */ jsx("p", { className: twMerge("font-medium text-(--ui-text-muted)", size === "default" ? "text-sm" : emptyLayoutClasses.compactText), children: text }),
1309
+ description && /* @__PURE__ */ jsx("p", { className: twMerge("mt-1 max-w-xs text-(--ui-text-soft)", size === "default" ? "text-xs" : emptyLayoutClasses.compactDescription), children: description })
1310
+ ]
1311
+ }
1312
+ );
1313
+ var tableSizeConfig = {
1314
+ small: { th: "px-(--ui-table-cell-x-sm) py-(--ui-table-th-y-sm) [font-size:var(--ui-text-sm)]", td: "px-(--ui-table-cell-x-sm) py-(--ui-table-td-y-sm) [font-size:var(--ui-text-sm)]", emptyPy: "py-(--ui-table-empty-y-sm)" },
1315
+ middle: { th: "px-(--ui-table-cell-x-md) py-(--ui-table-th-y-md) [font-size:var(--ui-text-md)]", td: "px-(--ui-table-cell-x-md) py-(--ui-table-td-y-md) [font-size:var(--ui-text-md)]", emptyPy: "py-(--ui-table-empty-y-md)" },
1316
+ large: { th: "px-(--ui-table-cell-x-lg) py-(--ui-table-th-y-lg) [font-size:var(--ui-text-lg)]", td: "px-(--ui-table-cell-x-lg) py-(--ui-table-td-y-lg) [font-size:var(--ui-text-lg)]", emptyPy: "py-(--ui-table-empty-y-lg)" }
1317
+ };
1318
+ var tableHeaderVerticalBorderClass = "border-r border-(--ui-primary-text)/10 last:border-r-0";
1319
+ var tableCellVerticalBorderClass = "border-r border-(--ui-border) last:border-r-0";
1320
+ var columnTypeWidth = {
1321
+ date: 100,
1322
+ datetime: 130,
1323
+ dot: 36
1324
+ };
1325
+ function formatCellValue(value, type) {
1326
+ if (value === null || value === void 0) return "-";
1327
+ switch (type) {
1328
+ case "date":
1329
+ return dayjs(value).format("D.M.YYYY");
1330
+ case "datetime":
1331
+ return dayjs(value).format("D.M.YYYY H:mm");
1332
+ case "boolean":
1333
+ return /* @__PURE__ */ jsx("input", { type: "checkbox", readOnly: true, checked: value, className: "cursor-default" });
1334
+ case "yes/no":
1335
+ return value ? "Ano" : "Ne";
1336
+ default:
1337
+ return String(value);
1338
+ }
1339
+ }
1340
+ function getKey(item, items, rowKey) {
1341
+ return rowKey ? String(item[rowKey]) : items.indexOf(item);
1342
+ }
1343
+ var Table = ({
1344
+ loading = false,
1345
+ emptyText = "\u017D\xE1dn\xE9 z\xE1znamy",
1346
+ size = "middle",
1347
+ verticalBorders = false,
1348
+ ...props
1349
+ }) => {
1350
+ const sc = tableSizeConfig[size];
1351
+ const isEmpty = !loading && props.items.length === 0;
1352
+ return /* @__PURE__ */ jsxs("div", { className: twMerge("relative overflow-x-auto border border-(--ui-border) rounded-(--ui-radius-lg)", props.className), children: [
1353
+ loading && /* @__PURE__ */ jsx("div", { className: twMerge("absolute inset-0 z-10 bg-white/60 flex items-center justify-center", props.classNames?.overlay), children: /* @__PURE__ */ jsx(Spinner, { size: "large", color: "primary" }) }),
1354
+ /* @__PURE__ */ jsxs("table", { className: twMerge("w-full border-collapse", props.classNames?.table), children: [
1355
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { className: twMerge("bg-(--ui-primary) text-(--ui-primary-text)", props.classNames?.headerRow), children: props.columns.map((col, i) => /* @__PURE__ */ jsx(
1356
+ "th",
1357
+ {
1358
+ style: { width: col.width ?? columnTypeWidth[col.type] },
1359
+ className: twMerge(
1360
+ sc.th,
1361
+ "font-normal text-left whitespace-nowrap",
1362
+ verticalBorders && tableHeaderVerticalBorderClass,
1363
+ col.align === "center" && "text-center",
1364
+ col.align === "right" && "text-right",
1365
+ props.classNames?.headerCell
1366
+ ),
1367
+ children: col.title
1368
+ },
1369
+ i
1370
+ )) }) }),
1371
+ /* @__PURE__ */ jsx("tbody", { children: isEmpty ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
1372
+ "td",
1373
+ {
1374
+ colSpan: props.columns.length,
1375
+ className: twMerge("px-3", props.classNames?.emptyCell),
1376
+ children: /* @__PURE__ */ jsx(Empty, { size: "compact", text: emptyText, className: sc.emptyPy })
1377
+ }
1378
+ ) }) : props.items.map((item, rowIdx) => /* @__PURE__ */ jsx(
1379
+ "tr",
1380
+ {
1381
+ onClick: props.onRowClick ? () => props.onRowClick(item) : void 0,
1382
+ className: twMerge(
1383
+ "border-t border-(--ui-border) transition-colors",
1384
+ rowIdx % 2 === 1 && neutralSurfaceClasses.subtle,
1385
+ props.onRowClick && "cursor-pointer hover:bg-blue-50",
1386
+ props.classNames?.bodyRow
1387
+ ),
1388
+ children: props.columns.map((col, colIdx) => {
1389
+ const content = col.render ? col.render(item) : col.dataField ? formatCellValue(item[col.dataField], col.type) : null;
1390
+ const centeredContent = col.align === "center";
1391
+ const rightAlignedContent = col.align === "right";
1392
+ return /* @__PURE__ */ jsx(
1393
+ "td",
1394
+ {
1395
+ onClick: col.type === "dot" ? (e) => e.stopPropagation() : void 0,
1396
+ className: twMerge(
1397
+ sc.td,
1398
+ "align-middle",
1399
+ verticalBorders && tableCellVerticalBorderClass,
1400
+ !col.wrap && "max-w-60 truncate",
1401
+ col.wrap && "whitespace-normal",
1402
+ col.align === "center" && "text-center",
1403
+ col.align === "right" && "text-right",
1404
+ col.type === "dot" && "w-9",
1405
+ props.classNames?.bodyCell
1406
+ ),
1407
+ children: centeredContent || rightAlignedContent ? /* @__PURE__ */ jsx(
1408
+ "div",
1409
+ {
1410
+ className: twMerge(
1411
+ "flex w-full items-center",
1412
+ centeredContent ? "justify-center" : "justify-end"
1413
+ ),
1414
+ children: content
1415
+ }
1416
+ ) : content
1417
+ },
1418
+ colIdx
1419
+ );
1420
+ })
1421
+ },
1422
+ getKey(item, props.items, props.rowKey)
1423
+ )) })
1424
+ ] })
1425
+ ] });
1426
+ };
1427
+ var SortDirection = {
1428
+ Asc: "Asc",
1429
+ Desc: "Desc"
1430
+ };
1431
+ var ISO_REGEX = /^\d{4}-\d{2}-\d{2}T/;
1432
+ function reviveDates(obj) {
1433
+ if (typeof obj === "string" && ISO_REGEX.test(obj)) {
1434
+ return new Date(obj);
1435
+ }
1436
+ if (obj && typeof obj === "object" && !Array.isArray(obj)) {
1437
+ return Object.fromEntries(
1438
+ Object.entries(obj).map(([k, v]) => [k, reviveDates(v)])
1439
+ );
1440
+ }
1441
+ return obj;
1442
+ }
1443
+ function loadFilter(storageKey) {
1444
+ try {
1445
+ const raw = sessionStorage.getItem(`gridFilters:${storageKey}`);
1446
+ if (!raw) return {};
1447
+ return reviveDates(JSON.parse(raw));
1448
+ } catch {
1449
+ return {};
1450
+ }
1451
+ }
1452
+ function saveFilter(storageKey, filter) {
1453
+ try {
1454
+ sessionStorage.setItem(`gridFilters:${storageKey}`, JSON.stringify(filter));
1455
+ } catch {
1456
+ }
1457
+ }
1458
+ function useGrid(defaults, storageKey) {
1459
+ const [state, setState] = useState(() => {
1460
+ const persisted = storageKey ? loadFilter(storageKey) : {};
1461
+ return {
1462
+ pageNumber: 1,
1463
+ pageSize: 15,
1464
+ filter: { ...persisted },
1465
+ ...defaults
1466
+ };
1467
+ });
1468
+ const onChange = (changes) => {
1469
+ setState((s) => {
1470
+ const next = {
1471
+ ...s,
1472
+ pageNumber: "filter" in changes ? 1 : s.pageNumber,
1473
+ ...changes
1474
+ };
1475
+ if ("filter" in changes && storageKey) {
1476
+ saveFilter(storageKey, next.filter);
1477
+ }
1478
+ return next;
1479
+ });
1480
+ };
1481
+ const apiQuery = {
1482
+ pageNumber: state.pageNumber,
1483
+ pageSize: state.pageSize,
1484
+ sortBy: state.sortBy,
1485
+ direction: state.direction,
1486
+ ...state.filter
1487
+ };
1488
+ return { ...state, onChange, apiQuery };
1489
+ }
1490
+ var columnTypeWidth2 = {
1491
+ date: 100,
1492
+ datetime: 130,
1493
+ dot: 36
1494
+ };
1495
+ function getKey2(item, items, rowKey) {
1496
+ return rowKey ? String(item[rowKey]) : items.indexOf(item);
1497
+ }
1498
+ var PAGE_SIZES = [15, 25, 50];
1499
+ function buildPageNumbers(current, total) {
1500
+ if (total <= 7) {
1501
+ return Array.from({ length: total }, (_, i) => i + 1);
1502
+ }
1503
+ const pages = [1];
1504
+ if (current > 3) pages.push("...");
1505
+ for (let p = Math.max(2, current - 1); p <= Math.min(total - 1, current + 1); p++) {
1506
+ pages.push(p);
1507
+ }
1508
+ if (current < total - 2) pages.push("...");
1509
+ pages.push(total);
1510
+ return pages;
1511
+ }
1512
+ var Pagination = ({ current, pageSize, total, onChange, showPageNumberChanger, showPageSizeChanger, classNames }) => {
1513
+ const totalPages = Math.max(1, Math.ceil(total / pageSize));
1514
+ const from = Math.min((current - 1) * pageSize + 1, total);
1515
+ const to = Math.min(current * pageSize, total);
1516
+ const pages = buildPageNumbers(current, totalPages);
1517
+ const btnBase = "h-7 min-w-7 px-1.5 text-xs rounded border border-(--ui-border) flex items-center justify-center transition-colors cursor-pointer select-none";
1518
+ return /* @__PURE__ */ jsxs("div", { className: twMerge("flex items-center justify-between gap-4 px-3 py-2 border-t border-(--ui-border) rounded-b-lg", neutralSurfaceClasses.subtle, classNames?.root), children: [
1519
+ /* @__PURE__ */ jsx("span", { className: twMerge("text-xs text-gray-500 whitespace-nowrap", classNames?.summary), children: total > 0 ? `${from}\u2013${to} z ${total} z\xE1znam\u016F` : "0 z\xE1znam\u016F" }),
1520
+ /* @__PURE__ */ jsxs("div", { className: twMerge("flex items-center gap-1.5", classNames?.controls), children: [
1521
+ showPageSizeChanger && /* @__PURE__ */ jsx(
1522
+ "select",
1523
+ {
1524
+ value: pageSize,
1525
+ onChange: (e) => onChange(1, Number(e.target.value)),
1526
+ className: twMerge("h-7 px-1.5 text-xs border border-(--ui-border) rounded-(--ui-radius-sm) bg-white cursor-pointer outline-none", classNames?.pageSizeSelect),
1527
+ children: PAGE_SIZES.map((s) => /* @__PURE__ */ jsxs("option", { value: s, children: [
1528
+ s,
1529
+ " / str."
1530
+ ] }, s))
1531
+ }
1532
+ ),
1533
+ /* @__PURE__ */ jsx(
1534
+ "button",
1535
+ {
1536
+ type: "button",
1537
+ disabled: current <= 1,
1538
+ onClick: () => onChange(current - 1, pageSize),
1539
+ className: twMerge(btnBase, "bg-white hover:bg-gray-50 disabled:opacity-40 disabled:cursor-not-allowed", classNames?.button),
1540
+ children: /* @__PURE__ */ jsx(IconChevronLeft, { size: 13, strokeWidth: 1.5 })
1541
+ }
1542
+ ),
1543
+ showPageNumberChanger && /* @__PURE__ */ jsx(Fragment, { children: pages.map(
1544
+ (p, i) => p === "..." ? /* @__PURE__ */ jsx("span", { className: "text-xs text-gray-400 px-0.5", children: "\u2026" }, `ellipsis-${i}`) : /* @__PURE__ */ jsx(
1545
+ "button",
1546
+ {
1547
+ type: "button",
1548
+ onClick: () => onChange(p, pageSize),
1549
+ className: twMerge(
1550
+ btnBase,
1551
+ p === current ? "bg-(--ui-primary) text-(--ui-primary-text) border-(--ui-primary)" : "bg-white hover:bg-gray-50",
1552
+ classNames?.button,
1553
+ p === current && classNames?.activeButton
1554
+ ),
1555
+ children: p
1556
+ },
1557
+ p
1558
+ )
1559
+ ) }),
1560
+ /* @__PURE__ */ jsx(
1561
+ "button",
1562
+ {
1563
+ type: "button",
1564
+ disabled: current >= totalPages,
1565
+ onClick: () => onChange(current + 1, pageSize),
1566
+ className: twMerge(btnBase, "bg-white hover:bg-gray-50 disabled:opacity-40 disabled:cursor-not-allowed", classNames?.button),
1567
+ children: /* @__PURE__ */ jsx(IconChevronRight, { size: 13, strokeWidth: 1.5 })
1568
+ }
1569
+ )
1570
+ ] })
1571
+ ] });
1572
+ };
1573
+ var Grid = (props) => {
1574
+ const { grid } = props;
1575
+ const sc = tableSizeConfig[props.size ?? "middle"];
1576
+ const verticalBorders = props.verticalBorders ?? false;
1577
+ const isInitialLoading = props.loading && props.items.length === 0;
1578
+ const isEmpty = !props.loading && props.items.length === 0;
1579
+ const handleSort = (col) => {
1580
+ if (col.sortBy === void 0) return;
1581
+ const isCurrent = grid.sortBy === col.sortBy;
1582
+ grid.onChange({
1583
+ sortBy: col.sortBy,
1584
+ direction: isCurrent && grid.direction === SortDirection.Asc ? SortDirection.Desc : SortDirection.Asc
1585
+ });
1586
+ };
1587
+ props.items.map((item) => getKey2(item, props.items, props.rowKey));
1588
+ const selectedKeys = new Set(
1589
+ (props.selection?.selectedItems ?? []).map((item) => getKey2(item, props.items, props.rowKey))
1590
+ );
1591
+ const allSelected = props.items.length > 0 && props.items.every((item) => selectedKeys.has(getKey2(item, props.items, props.rowKey)));
1592
+ const someSelected = !allSelected && props.items.some((item) => selectedKeys.has(getKey2(item, props.items, props.rowKey)));
1593
+ const toggleAll = () => {
1594
+ if (!props.selection) return;
1595
+ props.selection.onChange(allSelected ? [] : [...props.items]);
1596
+ };
1597
+ const toggleRow = (item) => {
1598
+ if (!props.selection) return;
1599
+ const key = getKey2(item, props.items, props.rowKey);
1600
+ const next = selectedKeys.has(key) ? props.selection.selectedItems.filter((s) => getKey2(s, props.items, props.rowKey) !== key) : [...props.selection.selectedItems, item];
1601
+ props.selection.onChange(next);
1602
+ };
1603
+ return /* @__PURE__ */ jsxs("div", { className: twMerge("relative border border-(--ui-border) rounded-(--ui-radius-lg) overflow-hidden", props.className), children: [
1604
+ props.loading && !isInitialLoading && /* @__PURE__ */ jsx("div", { className: twMerge("absolute inset-0 z-10 bg-white/60 flex items-center justify-center", props.classNames?.overlay), children: /* @__PURE__ */ jsx(Spinner, { size: "large", color: "primary" }) }),
1605
+ /* @__PURE__ */ jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxs("table", { className: twMerge("w-full border-collapse", props.classNames?.table), children: [
1606
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: twMerge("bg-(--ui-primary) text-(--ui-primary-text)", props.classNames?.headerRow), children: [
1607
+ props.selection && /* @__PURE__ */ jsx("th", { className: twMerge(sc.th, "w-9", verticalBorders && tableHeaderVerticalBorderClass, props.classNames?.headerCell), children: /* @__PURE__ */ jsx(
1608
+ "input",
1609
+ {
1610
+ type: "checkbox",
1611
+ checked: allSelected,
1612
+ ref: (el) => {
1613
+ if (el) el.indeterminate = someSelected;
1614
+ },
1615
+ onChange: toggleAll,
1616
+ className: "cursor-pointer accent-white"
1617
+ }
1618
+ ) }),
1619
+ props.columns.map((col, i) => {
1620
+ const isSortable = col.sortBy !== void 0;
1621
+ const isCurrent = isSortable && grid.sortBy === col.sortBy;
1622
+ return /* @__PURE__ */ jsx(
1623
+ "th",
1624
+ {
1625
+ style: { width: col.width ?? columnTypeWidth2[col.type] },
1626
+ onClick: isSortable ? () => handleSort(col) : void 0,
1627
+ className: twMerge(
1628
+ sc.th,
1629
+ "font-normal text-left whitespace-nowrap",
1630
+ verticalBorders && tableHeaderVerticalBorderClass,
1631
+ isSortable && "cursor-pointer select-none hover:bg-white/10",
1632
+ col.align === "center" && "text-center",
1633
+ col.align === "right" && "text-right",
1634
+ props.classNames?.headerCell
1635
+ ),
1636
+ children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
1637
+ col.title,
1638
+ isSortable && /* @__PURE__ */ jsx("span", { className: "opacity-70", children: isCurrent ? grid.direction === SortDirection.Asc ? /* @__PURE__ */ jsx(IconChevronUp, { size: 12, strokeWidth: 2 }) : /* @__PURE__ */ jsx(IconChevronDown, { size: 12, strokeWidth: 2 }) : /* @__PURE__ */ jsx(IconSelector, { size: 12, strokeWidth: 2 }) })
1639
+ ] })
1640
+ },
1641
+ i
1642
+ );
1643
+ })
1644
+ ] }) }),
1645
+ /* @__PURE__ */ jsx("tbody", { children: isInitialLoading ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
1646
+ "td",
1647
+ {
1648
+ colSpan: props.columns.length + (props.selection ? 1 : 0),
1649
+ className: twMerge("px-3 py-0", props.classNames?.loadingCell),
1650
+ children: /* @__PURE__ */ jsx("div", { className: "flex min-h-28 items-center justify-center", children: /* @__PURE__ */ jsx(Spinner, { size: "large", color: "primary" }) })
1651
+ }
1652
+ ) }) : isEmpty ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
1653
+ "td",
1654
+ {
1655
+ colSpan: props.columns.length + (props.selection ? 1 : 0),
1656
+ className: twMerge("px-3", props.classNames?.emptyCell),
1657
+ children: /* @__PURE__ */ jsx(Empty, { size: "compact", text: props.emptyText ?? "\u017D\xE1dn\xE9 z\xE1znamy", className: sc.emptyPy })
1658
+ }
1659
+ ) }) : props.items.map((item, rowIdx) => {
1660
+ const key = getKey2(item, props.items, props.rowKey);
1661
+ const isSelected = selectedKeys.has(key);
1662
+ return /* @__PURE__ */ jsxs(
1663
+ "tr",
1664
+ {
1665
+ onClick: props.onRowClick ? () => props.onRowClick(item) : void 0,
1666
+ className: twMerge(
1667
+ "border-t border-(--ui-border) transition-colors",
1668
+ rowIdx % 2 === 1 && !isSelected && neutralSurfaceClasses.subtle,
1669
+ isSelected && "bg-blue-50",
1670
+ props.onRowClick && "cursor-pointer hover:bg-blue-50",
1671
+ props.classNames?.bodyRow
1672
+ ),
1673
+ children: [
1674
+ props.selection && /* @__PURE__ */ jsx(
1675
+ "td",
1676
+ {
1677
+ className: twMerge(sc.td, "w-9 align-middle", verticalBorders && tableCellVerticalBorderClass, props.classNames?.bodyCell),
1678
+ onClick: (e) => e.stopPropagation(),
1679
+ children: /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center", children: /* @__PURE__ */ jsx(
1680
+ "input",
1681
+ {
1682
+ type: "checkbox",
1683
+ checked: isSelected,
1684
+ onChange: () => toggleRow(item),
1685
+ className: "cursor-pointer"
1686
+ }
1687
+ ) })
1688
+ }
1689
+ ),
1690
+ props.columns.map((col, colIdx) => {
1691
+ const content = col.render ? col.render(item) : col.dataField ? formatCellValue(item[col.dataField], col.type) : null;
1692
+ const centeredContent = col.align === "center";
1693
+ const rightAlignedContent = col.align === "right";
1694
+ return /* @__PURE__ */ jsx(
1695
+ "td",
1696
+ {
1697
+ onClick: col.type === "dot" ? (e) => e.stopPropagation() : void 0,
1698
+ className: twMerge(
1699
+ sc.td,
1700
+ "align-middle",
1701
+ verticalBorders && tableCellVerticalBorderClass,
1702
+ !col.wrap && "max-w-60 truncate",
1703
+ col.wrap && "whitespace-normal",
1704
+ col.align === "center" && "text-center",
1705
+ col.align === "right" && "text-right",
1706
+ col.type === "dot" && "w-9",
1707
+ props.classNames?.bodyCell
1708
+ ),
1709
+ children: centeredContent || rightAlignedContent ? /* @__PURE__ */ jsx(
1710
+ "div",
1711
+ {
1712
+ className: twMerge(
1713
+ "flex w-full items-center",
1714
+ centeredContent ? "justify-center" : "justify-end"
1715
+ ),
1716
+ children: content
1717
+ }
1718
+ ) : content
1719
+ },
1720
+ colIdx
1721
+ );
1722
+ })
1723
+ ]
1724
+ },
1725
+ key
1726
+ );
1727
+ }) })
1728
+ ] }) }),
1729
+ /* @__PURE__ */ jsx(
1730
+ Pagination,
1731
+ {
1732
+ current: grid.pageNumber,
1733
+ pageSize: grid.pageSize,
1734
+ total: props.totalCount,
1735
+ showPageNumberChanger: props.showPageNumberChanger ?? false,
1736
+ showPageSizeChanger: props.showPageSizeChanger ?? false,
1737
+ onChange: (page, pageSize) => grid.onChange({ pageNumber: page, pageSize }),
1738
+ classNames: props.classNames?.pagination
1739
+ }
1740
+ )
1741
+ ] });
1742
+ };
1743
+ function isValueSet(v) {
1744
+ if (Array.isArray(v)) return v.length > 0;
1745
+ return v !== null && v !== void 0 && v !== "";
1746
+ }
1747
+ var GridFilters = (props) => {
1748
+ const active = (props.descriptors ?? []).filter(
1749
+ (d) => d.keys.some((k) => isValueSet(props.grid.filter[k]))
1750
+ );
1751
+ const clearDescriptor = (d) => {
1752
+ const cleared = d.keys.reduce((acc, k) => ({ ...acc, [k]: void 0 }), {});
1753
+ props.grid.onChange({ filter: { ...props.grid.filter, ...cleared } });
1754
+ };
1755
+ const clearAll = () => {
1756
+ props.grid.onChange({ filter: {} });
1757
+ };
1758
+ return /* @__PURE__ */ jsxs("div", { className: props.className, children: [
1759
+ /* @__PURE__ */ jsx("div", { className: twMerge("p-2 border border-b-0 border-(--ui-border) bg-(--ui-surface-subtle) rounded-t-lg", props.classNames?.inputs), children: props.children }),
1760
+ active.length > 0 && /* @__PURE__ */ jsxs("div", { className: twMerge("flex flex-wrap items-center gap-1.5 px-2 py-1.5 border border-b-0 border-(--ui-border) bg-(--ui-surface-subtle)", props.classNames?.activeFilters), children: [
1761
+ active.map((d) => {
1762
+ const value = d.formatValue?.(props.grid.filter);
1763
+ return /* @__PURE__ */ jsxs(
1764
+ "span",
1765
+ {
1766
+ className: twMerge("inline-flex items-center gap-1 bg-blue-50 text-blue-700 border border-blue-200 rounded-full px-2 h-6 text-xs", props.classNames?.chip),
1767
+ children: [
1768
+ value ? `${d.label}: ${value}` : d.label,
1769
+ /* @__PURE__ */ jsx(
1770
+ "button",
1771
+ {
1772
+ type: "button",
1773
+ onClick: () => clearDescriptor(d),
1774
+ className: twMerge("text-blue-400 hover:text-blue-700 cursor-pointer", props.classNames?.chipRemove),
1775
+ children: /* @__PURE__ */ jsx(IconX, { size: 10, strokeWidth: 2.5 })
1776
+ }
1777
+ )
1778
+ ]
1779
+ },
1780
+ d.keys.join(",")
1781
+ );
1782
+ }),
1783
+ /* @__PURE__ */ jsx(
1784
+ "button",
1785
+ {
1786
+ type: "button",
1787
+ onClick: clearAll,
1788
+ className: twMerge("ml-1 text-xs text-(--ui-text-soft) hover:text-(--ui-text) cursor-pointer", props.classNames?.clearAll),
1789
+ children: "Vymazat v\u0161e"
1790
+ }
1791
+ )
1792
+ ] })
1793
+ ] });
1794
+ };
1795
+ var FilterTextInput = ({
1796
+ debounce = 400,
1797
+ ...props
1798
+ }) => {
1799
+ const [local, setLocal] = useState(String(props.grid.filter[props.filterKey] ?? ""));
1800
+ const timer = useRef(null);
1801
+ useEffect(() => {
1802
+ setLocal(String(props.grid.filter[props.filterKey] ?? ""));
1803
+ }, [props.grid.filter[props.filterKey]]);
1804
+ const handleChange = (value) => {
1805
+ setLocal(value);
1806
+ if (timer.current) clearTimeout(timer.current);
1807
+ timer.current = setTimeout(() => {
1808
+ props.grid.onChange({ filter: { ...props.grid.filter, [props.filterKey]: value } });
1809
+ }, debounce);
1810
+ };
1811
+ return /* @__PURE__ */ jsx(
1812
+ TextInput,
1813
+ {
1814
+ size: "small",
1815
+ noMargin: true,
1816
+ label: props.label,
1817
+ placeholder: props.placeholder,
1818
+ value: local,
1819
+ onChange: handleChange
1820
+ }
1821
+ );
1822
+ };
1823
+ var FilterNumberInput = ({
1824
+ debounce = 300,
1825
+ ...props
1826
+ }) => {
1827
+ const rawValue = props.grid.filter[props.filterKey];
1828
+ const [local, setLocal] = useState(typeof rawValue === "number" ? rawValue : null);
1829
+ const timer = useRef(null);
1830
+ useEffect(() => {
1831
+ const next = props.grid.filter[props.filterKey];
1832
+ setLocal(typeof next === "number" ? next : null);
1833
+ }, [props.grid.filter[props.filterKey]]);
1834
+ const handleChange = (value) => {
1835
+ setLocal(value);
1836
+ if (timer.current) clearTimeout(timer.current);
1837
+ timer.current = setTimeout(() => {
1838
+ props.grid.onChange({
1839
+ filter: {
1840
+ ...props.grid.filter,
1841
+ [props.filterKey]: value ?? void 0
1842
+ }
1843
+ });
1844
+ }, debounce);
1845
+ };
1846
+ return /* @__PURE__ */ jsx(
1847
+ NumberInput,
1848
+ {
1849
+ size: "small",
1850
+ noMargin: true,
1851
+ label: props.label,
1852
+ placeholder: props.placeholder,
1853
+ value: local,
1854
+ onChange: handleChange,
1855
+ min: props.min,
1856
+ max: props.max
1857
+ }
1858
+ );
1859
+ };
1860
+ var FilterCheckboxInput = (props) => {
1861
+ const currentValue = props.grid.filter[props.filterKey];
1862
+ const checked = currentValue === true;
1863
+ const handleChange = (value) => {
1864
+ props.grid.onChange({
1865
+ filter: {
1866
+ ...props.grid.filter,
1867
+ [props.filterKey]: value ? true : void 0
1868
+ }
1869
+ });
1870
+ };
1871
+ return /* @__PURE__ */ jsx("div", { className: "flex h-(--ui-h-sm) items-center", children: /* @__PURE__ */ jsx(
1872
+ CheckboxInput,
1873
+ {
1874
+ noMargin: true,
1875
+ label: props.label,
1876
+ value: checked,
1877
+ onChange: handleChange
1878
+ }
1879
+ ) });
1880
+ };
1881
+ function calcPosition2(el) {
1882
+ const rect = el.getBoundingClientRect();
1883
+ const maxDropdownHeight = 280;
1884
+ const spaceBelow = window.innerHeight - rect.bottom;
1885
+ const openAbove = spaceBelow < maxDropdownHeight && rect.top > spaceBelow;
1886
+ return {
1887
+ top: openAbove ? rect.top - maxDropdownHeight - 2 : rect.bottom + 2,
1888
+ left: rect.left,
1889
+ width: rect.width,
1890
+ openAbove
1891
+ };
1892
+ }
1893
+ var SelectDropdown = (props) => {
1894
+ const [search, setSearch] = useState("");
1895
+ const [pos, setPos] = useState(null);
1896
+ const searchRef = useRef(null);
1897
+ const popupRef = useRef(null);
1898
+ useEffect(() => {
1899
+ if (props.isOpen && props.anchorRef.current) {
1900
+ setPos(calcPosition2(props.anchorRef.current));
1901
+ setSearch("");
1902
+ if (props.showSearch !== false) {
1903
+ setTimeout(() => searchRef.current?.focus(), 0);
1904
+ }
1905
+ }
1906
+ }, [props.isOpen, props.anchorRef, props.showSearch]);
1907
+ const handleMouseDown = useCallback(
1908
+ (e) => {
1909
+ if (popupRef.current && !popupRef.current.contains(e.target) && props.anchorRef.current && !props.anchorRef.current.contains(e.target)) {
1910
+ props.onClose();
1911
+ }
1912
+ },
1913
+ [props]
1914
+ );
1915
+ useEffect(() => {
1916
+ if (props.isOpen) {
1917
+ document.addEventListener("mousedown", handleMouseDown);
1918
+ }
1919
+ return () => document.removeEventListener("mousedown", handleMouseDown);
1920
+ }, [props.isOpen, handleMouseDown]);
1921
+ if (!props.isOpen || !pos) {
1922
+ return null;
1923
+ }
1924
+ const filtered = search.trim() === "" ? props.options : props.options.filter((o) => o.label.toLowerCase().includes(search.toLowerCase()));
1925
+ return createPortal(
1926
+ /* @__PURE__ */ jsxs(
1927
+ "div",
1928
+ {
1929
+ ref: popupRef,
1930
+ "data-ui-floating-root": "",
1931
+ style: { top: pos.top, left: pos.left, minWidth: pos.width },
1932
+ className: "fixed z-1002 bg-white border border-(--ui-border) rounded-(--ui-radius-lg) shadow-lg flex flex-col overflow-hidden",
1933
+ children: [
1934
+ props.showSearch !== false && /* @__PURE__ */ jsxs(
1935
+ "div",
1936
+ {
1937
+ className: twMerge(
1938
+ "flex items-center",
1939
+ !props.multiMode && "border-b border-(--ui-border)",
1940
+ popupLayoutClasses.compactHeader,
1941
+ "gap-(--ui-popup-gap)"
1942
+ ),
1943
+ children: [
1944
+ /* @__PURE__ */ jsx(IconSearch, { size: 13, strokeWidth: 1.5, className: twMerge("shrink-0", neutralIconClasses.default) }),
1945
+ /* @__PURE__ */ jsx(
1946
+ "input",
1947
+ {
1948
+ ref: searchRef,
1949
+ type: "text",
1950
+ value: search,
1951
+ onChange: (e) => setSearch(e.target.value),
1952
+ placeholder: "Hledat...",
1953
+ className: "flex-1 bg-transparent outline-none [font-size:var(--ui-popup-text)] placeholder:text-(--ui-text-soft)"
1954
+ }
1955
+ )
1956
+ ]
1957
+ }
1958
+ ),
1959
+ props.multiMode && props.onSelectAll && props.onClearAll && /* @__PURE__ */ jsxs(
1960
+ "div",
1961
+ {
1962
+ className: twMerge(
1963
+ "flex items-center justify-between border-t border-b border-(--ui-border) bg-(--ui-surface-subtle)",
1964
+ popupLayoutClasses.compactHeader
1965
+ ),
1966
+ children: [
1967
+ /* @__PURE__ */ jsx("span", { className: twMerge("text-[11px] font-medium uppercase tracking-[0.08em]", neutralTextClasses.soft), children: "Hromadn\xE9 akce" }),
1968
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1969
+ /* @__PURE__ */ jsx(
1970
+ "button",
1971
+ {
1972
+ type: "button",
1973
+ onMouseDown: (e) => e.preventDefault(),
1974
+ onClick: props.onSelectAll,
1975
+ className: twMerge(
1976
+ "cursor-pointer rounded-md px-2 py-1 text-xs transition-colors hover:bg-white",
1977
+ neutralTextClasses.muted,
1978
+ "hover:text-(--ui-text-strong)"
1979
+ ),
1980
+ children: "Vybrat v\u0161e"
1981
+ }
1982
+ ),
1983
+ /* @__PURE__ */ jsx(
1984
+ "button",
1985
+ {
1986
+ type: "button",
1987
+ onMouseDown: (e) => e.preventDefault(),
1988
+ onClick: props.onClearAll,
1989
+ className: twMerge(
1990
+ "cursor-pointer rounded-md px-2 py-1 text-xs transition-colors hover:bg-white",
1991
+ neutralTextClasses.muted,
1992
+ "hover:text-(--ui-text-strong)"
1993
+ ),
1994
+ children: "Zru\u0161it v\u0161e"
1995
+ }
1996
+ )
1997
+ ] })
1998
+ ]
1999
+ }
2000
+ ),
2001
+ /* @__PURE__ */ jsx("div", { className: "overflow-y-auto max-h-60", children: props.loading ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-6", children: /* @__PURE__ */ jsx(Spinner, { size: "middle", color: "primary" }) }) : filtered.length === 0 ? /* @__PURE__ */ jsx("div", { className: twMerge("py-4 text-center text-xs", neutralTextClasses.soft), children: "\u017D\xE1dn\xE9 v\xFDsledky" }) : filtered.map((opt, i) => {
2002
+ const isSelected = props.selectedValues.has(opt.value);
2003
+ return /* @__PURE__ */ jsxs(
2004
+ "div",
2005
+ {
2006
+ onMouseDown: (e) => e.preventDefault(),
2007
+ onClick: () => props.onSelect(opt.value),
2008
+ className: twMerge(
2009
+ "flex items-center justify-between cursor-pointer select-none",
2010
+ popupLayoutClasses.compactRow,
2011
+ isSelected ? "font-medium text-(--ui-text-strong)" : "text-(--ui-text) hover:bg-(--ui-surface-subtle)"
2012
+ ),
2013
+ children: [
2014
+ /* @__PURE__ */ jsx("span", { children: opt.label }),
2015
+ isSelected && /* @__PURE__ */ jsx(IconCheck, { size: 13, strokeWidth: 2, className: "shrink-0 text-(--ui-text)" })
2016
+ ]
2017
+ },
2018
+ i
2019
+ );
2020
+ }) })
2021
+ ]
2022
+ }
2023
+ ),
2024
+ document.body
2025
+ );
2026
+ };
2027
+ var SelectInput = ({
2028
+ options,
2029
+ labelKey = "label",
2030
+ valueKey = "value",
2031
+ size = "middle",
2032
+ showSearch = true,
2033
+ ...props
2034
+ }) => {
2035
+ const [isOpen, setIsOpen] = useState(false);
2036
+ const triggerRef = useRef(null);
2037
+ const allOptions = (options ?? []).map((opt) => ({
2038
+ label: String(opt[labelKey]),
2039
+ value: opt[valueKey]
2040
+ }));
2041
+ const effectiveValue = props.treatZeroAsEmpty && props.value === 0 ? null : props.value ?? null;
2042
+ const selectedOption = allOptions.find((o) => o.value === effectiveValue) ?? null;
2043
+ const hasValue = effectiveValue !== null && effectiveValue !== void 0;
2044
+ const allowClear = props.allowClear ?? false;
2045
+ const resolveError = useErrorResolver();
2046
+ const resolvedErrors = props.errorName ? resolveError(props.errorName) : [];
2047
+ const errorDisplay = props.error ?? props.customError ?? (resolvedErrors.length > 0 ? resolvedErrors.join(", ") : void 0);
2048
+ const hasError = !!errorDisplay;
2049
+ const handleSelect = (value) => {
2050
+ props.onChange?.(value);
2051
+ setIsOpen(false);
2052
+ };
2053
+ const handleClear = (e) => {
2054
+ e.stopPropagation();
2055
+ props.onChange?.(null);
2056
+ };
2057
+ const handleTriggerClick = () => {
2058
+ if (!props.disabled) {
2059
+ setIsOpen((v) => !v);
2060
+ }
2061
+ };
2062
+ return /* @__PURE__ */ jsxs(InputField, { noMargin: props.noMargin, className: props.className, children: [
2063
+ props.label && /* @__PURE__ */ jsx(
2064
+ InputLabel,
2065
+ {
2066
+ label: props.label,
2067
+ required: props.required,
2068
+ className: props.classNames?.label,
2069
+ requiredMarkClassName: props.classNames?.requiredMark
2070
+ }
2071
+ ),
2072
+ /* @__PURE__ */ jsxs(
2073
+ "div",
2074
+ {
2075
+ ref: triggerRef,
2076
+ onClick: handleTriggerClick,
2077
+ className: twMerge(
2078
+ "flex items-center gap-1 border bg-white cursor-pointer transition-colors select-none",
2079
+ triggerSizeClasses[size],
2080
+ isOpen ? triggerBorderClasses.open : triggerBorderClasses.default,
2081
+ props.disabled && triggerBorderClasses.disabled,
2082
+ props.classNames?.trigger
2083
+ ),
2084
+ style: hasError ? { borderColor: "var(--ui-danger)" } : void 0,
2085
+ children: [
2086
+ /* @__PURE__ */ jsx(
2087
+ "span",
2088
+ {
2089
+ className: twMerge(
2090
+ "flex min-w-0 flex-1 items-center truncate leading-none",
2091
+ !selectedOption && neutralTextClasses.soft,
2092
+ props.classNames?.value
2093
+ ),
2094
+ children: selectedOption ? selectedOption.label : props.placeholder ?? ""
2095
+ }
2096
+ ),
2097
+ allowClear && hasValue && !props.disabled && /* @__PURE__ */ jsx(
2098
+ "button",
2099
+ {
2100
+ type: "button",
2101
+ onMouseDown: (e) => e.preventDefault(),
2102
+ onClick: handleClear,
2103
+ className: twMerge(
2104
+ "flex h-4 w-4 shrink-0 items-center justify-center self-center cursor-pointer",
2105
+ neutralIconClasses.default,
2106
+ neutralIconClasses.hover,
2107
+ props.classNames?.clearButton
2108
+ ),
2109
+ children: /* @__PURE__ */ jsx(IconX, { size: 12, strokeWidth: 2 })
2110
+ }
2111
+ ),
2112
+ /* @__PURE__ */ jsx(
2113
+ IconChevronDown,
2114
+ {
2115
+ size: 13,
2116
+ strokeWidth: 1.5,
2117
+ className: twMerge(
2118
+ "shrink-0 self-center transition-transform",
2119
+ neutralIconClasses.default,
2120
+ isOpen && "rotate-180",
2121
+ props.classNames?.icon
2122
+ )
2123
+ }
2124
+ )
2125
+ ]
2126
+ }
2127
+ ),
2128
+ /* @__PURE__ */ jsx(
2129
+ SelectDropdown,
2130
+ {
2131
+ anchorRef: triggerRef,
2132
+ isOpen,
2133
+ options: allOptions,
2134
+ selectedValues: new Set(hasValue ? [effectiveValue] : []),
2135
+ onSelect: handleSelect,
2136
+ onClose: () => setIsOpen(false),
2137
+ loading: props.loading,
2138
+ showSearch,
2139
+ multiMode: false
2140
+ }
2141
+ ),
2142
+ errorDisplay && /* @__PURE__ */ jsx(InputError, { error: String(errorDisplay), className: props.classNames?.error })
2143
+ ] });
2144
+ };
2145
+ var FilterSelectInput = (props) => {
2146
+ const currentValue = props.grid.filter[props.filterKey] ?? null;
2147
+ const handleChange = (value) => {
2148
+ props.grid.onChange({
2149
+ filter: { ...props.grid.filter, [props.filterKey]: value ?? void 0 }
2150
+ });
2151
+ };
2152
+ return /* @__PURE__ */ jsx(
2153
+ SelectInput,
2154
+ {
2155
+ size: "small",
2156
+ noMargin: true,
2157
+ label: props.label,
2158
+ placeholder: props.placeholder,
2159
+ options: props.options,
2160
+ labelKey: props.labelKey,
2161
+ valueKey: props.valueKey,
2162
+ value: currentValue,
2163
+ onChange: handleChange,
2164
+ allowClear: true
2165
+ }
2166
+ );
2167
+ };
2168
+ var MONTHS_CS = [
2169
+ "Leden",
2170
+ "\xDAnor",
2171
+ "B\u0159ezen",
2172
+ "Duben",
2173
+ "Kv\u011Bten",
2174
+ "\u010Cerven",
2175
+ "\u010Cervenec",
2176
+ "Srpen",
2177
+ "Z\xE1\u0159\xED",
2178
+ "\u0158\xEDjen",
2179
+ "Listopad",
2180
+ "Prosinec"
2181
+ ];
2182
+ var DAYS_CS = ["Po", "\xDAt", "St", "\u010Ct", "P\xE1", "So", "Ne"];
2183
+ function toMondayFirst(jsDay) {
2184
+ return (jsDay + 6) % 7;
2185
+ }
2186
+ function buildDays(year, month) {
2187
+ const first = new Date(year, month, 1);
2188
+ const last = new Date(year, month + 1, 0);
2189
+ const startOffset = toMondayFirst(first.getDay());
2190
+ const days = [];
2191
+ for (let i = startOffset; i > 0; i--) {
2192
+ const d = new Date(year, month, 1 - i);
2193
+ days.push({ day: d.getDate(), month: d.getMonth(), year: d.getFullYear(), isCurrentMonth: false });
2194
+ }
2195
+ for (let d = 1; d <= last.getDate(); d++) {
2196
+ days.push({ day: d, month, year, isCurrentMonth: true });
2197
+ }
2198
+ let next = 1;
2199
+ while (days.length < 42) {
2200
+ const nd = new Date(year, month + 1, next++);
2201
+ days.push({ day: nd.getDate(), month: nd.getMonth(), year: nd.getFullYear(), isCurrentMonth: false });
2202
+ }
2203
+ return days;
2204
+ }
2205
+ function sameDay(a, b) {
2206
+ return a.getFullYear() === b.year && a.getMonth() === b.month && a.getDate() === b.day;
2207
+ }
2208
+ function calcPos(el, calHeight) {
2209
+ const rect = el.getBoundingClientRect();
2210
+ const spaceBelow = window.innerHeight - rect.bottom;
2211
+ const openAbove = spaceBelow < calHeight && rect.top > spaceBelow;
2212
+ return {
2213
+ top: openAbove ? rect.top - calHeight - 2 : rect.bottom + 2,
2214
+ left: rect.left
2215
+ };
2216
+ }
2217
+ function atStartOfDay(date) {
2218
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime();
2219
+ }
2220
+ function isDayDisabled(date, minDate, maxDate, isDateDisabled) {
2221
+ const day = atStartOfDay(date);
2222
+ if (minDate && day < atStartOfDay(minDate)) return true;
2223
+ if (maxDate && day > atStartOfDay(maxDate)) return true;
2224
+ return isDateDisabled?.(date) ?? false;
2225
+ }
2226
+ var CalendarPopup = (props) => {
2227
+ const today = /* @__PURE__ */ new Date();
2228
+ const [pos, setPos] = useState(null);
2229
+ const [viewYear, setViewYear] = useState(today.getFullYear());
2230
+ const [viewMonth, setViewMonth] = useState(today.getMonth());
2231
+ const [pending, setPending] = useState(null);
2232
+ const [hours, setHours] = useState(0);
2233
+ const [minutes, setMinutes] = useState(0);
2234
+ const popupRef = useRef(null);
2235
+ const calHeight = props.showTime ? 360 : 310;
2236
+ useEffect(() => {
2237
+ if (!props.isOpen || !props.anchorRef.current) {
2238
+ return;
2239
+ }
2240
+ setPos(calcPos(props.anchorRef.current, calHeight));
2241
+ const base = props.value;
2242
+ if (base) {
2243
+ setViewYear(base.getFullYear());
2244
+ setViewMonth(base.getMonth());
2245
+ setPending(base);
2246
+ setHours(base.getHours());
2247
+ setMinutes(base.getMinutes());
2248
+ } else {
2249
+ setViewYear(today.getFullYear());
2250
+ setViewMonth(today.getMonth());
2251
+ setPending(null);
2252
+ setHours(0);
2253
+ setMinutes(0);
2254
+ }
2255
+ }, [props.isOpen]);
2256
+ const handleMouseDown = useCallback(
2257
+ (e) => {
2258
+ if (popupRef.current && !popupRef.current.contains(e.target) && props.anchorRef.current && !props.anchorRef.current.contains(e.target)) {
2259
+ props.onClose();
2260
+ }
2261
+ },
2262
+ [props]
2263
+ );
2264
+ useEffect(() => {
2265
+ if (props.isOpen) {
2266
+ document.addEventListener("mousedown", handleMouseDown);
2267
+ }
2268
+ return () => document.removeEventListener("mousedown", handleMouseDown);
2269
+ }, [props.isOpen, handleMouseDown]);
2270
+ if (!props.isOpen || !pos) {
2271
+ return null;
2272
+ }
2273
+ const days = buildDays(viewYear, viewMonth);
2274
+ const prevMonth = () => {
2275
+ if (viewMonth === 0) {
2276
+ setViewMonth(11);
2277
+ setViewYear((y) => y - 1);
2278
+ } else {
2279
+ setViewMonth((m) => m - 1);
2280
+ }
2281
+ };
2282
+ const nextMonth = () => {
2283
+ if (viewMonth === 11) {
2284
+ setViewMonth(0);
2285
+ setViewYear((y) => y + 1);
2286
+ } else {
2287
+ setViewMonth((m) => m + 1);
2288
+ }
2289
+ };
2290
+ const handleDayClick = (day) => {
2291
+ const newDate = new Date(day.year, day.month, day.day, hours, minutes, 0, 0);
2292
+ if (isDayDisabled(newDate, props.minDate, props.maxDate, props.isDateDisabled)) {
2293
+ return;
2294
+ }
2295
+ if (!props.showTime) {
2296
+ props.onSelect(newDate);
2297
+ props.onClose();
2298
+ } else {
2299
+ setPending(newDate);
2300
+ }
2301
+ };
2302
+ const handleConfirm = () => {
2303
+ if (!pending) {
2304
+ return;
2305
+ }
2306
+ props.onSelect(
2307
+ new Date(pending.getFullYear(), pending.getMonth(), pending.getDate(), hours, minutes, 0, 0)
2308
+ );
2309
+ props.onClose();
2310
+ };
2311
+ const clamp2 = (val, min, max) => Math.min(max, Math.max(min, val));
2312
+ return createPortal(
2313
+ /* @__PURE__ */ jsxs(
2314
+ "div",
2315
+ {
2316
+ ref: popupRef,
2317
+ "data-ui-floating-root": "",
2318
+ style: { top: pos.top, left: pos.left },
2319
+ className: "fixed z-1000 bg-white border border-(--ui-border) rounded-(--ui-radius-lg) shadow-lg w-68",
2320
+ children: [
2321
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-3 py-2 border-b border-(--ui-border)", children: [
2322
+ /* @__PURE__ */ jsx(
2323
+ "button",
2324
+ {
2325
+ type: "button",
2326
+ onMouseDown: (e) => e.preventDefault(),
2327
+ onClick: prevMonth,
2328
+ className: twMerge("p-1 rounded cursor-pointer hover:bg-(--ui-surface-muted)", neutralTextClasses.muted),
2329
+ children: /* @__PURE__ */ jsx(IconChevronLeft, { size: 14, strokeWidth: 1.5 })
2330
+ }
2331
+ ),
2332
+ /* @__PURE__ */ jsxs("span", { className: "text-[13px] font-medium text-(--ui-text-strong)", children: [
2333
+ MONTHS_CS[viewMonth],
2334
+ " ",
2335
+ viewYear
2336
+ ] }),
2337
+ /* @__PURE__ */ jsx(
2338
+ "button",
2339
+ {
2340
+ type: "button",
2341
+ onMouseDown: (e) => e.preventDefault(),
2342
+ onClick: nextMonth,
2343
+ className: twMerge("p-1 rounded cursor-pointer hover:bg-(--ui-surface-muted)", neutralTextClasses.muted),
2344
+ children: /* @__PURE__ */ jsx(IconChevronRight, { size: 14, strokeWidth: 1.5 })
2345
+ }
2346
+ )
2347
+ ] }),
2348
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-7 px-2 pt-2 pb-1", children: DAYS_CS.map((d) => /* @__PURE__ */ jsx("div", { className: "text-center text-[11px] text-(--ui-text-soft) font-medium", children: d }, d)) }),
2349
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-7 px-2 pb-2", children: days.map((day, i) => {
2350
+ const dayDate = new Date(day.year, day.month, day.day, hours, minutes, 0, 0);
2351
+ const disabled = isDayDisabled(dayDate, props.minDate, props.maxDate, props.isDateDisabled);
2352
+ const isSelected = pending !== null && sameDay(pending, day);
2353
+ const isTodayDay = today.getDate() === day.day && today.getMonth() === day.month && today.getFullYear() === day.year;
2354
+ return /* @__PURE__ */ jsx(
2355
+ "button",
2356
+ {
2357
+ type: "button",
2358
+ onMouseDown: (e) => e.preventDefault(),
2359
+ onClick: () => handleDayClick(day),
2360
+ disabled,
2361
+ className: twMerge(
2362
+ "h-8 w-full text-[13px] rounded transition-colors leading-none",
2363
+ disabled && "cursor-not-allowed text-(--ui-text-disabled) opacity-55",
2364
+ !disabled && "cursor-pointer",
2365
+ !day.isCurrentMonth && !disabled && "text-(--ui-text-disabled)",
2366
+ day.isCurrentMonth && !isSelected && !disabled && "text-(--ui-text) hover:bg-(--ui-surface-muted)",
2367
+ isSelected && !disabled && "bg-(--ui-primary) text-(--ui-primary-text) hover:bg-(--ui-primary-hover)",
2368
+ isTodayDay && !isSelected && "font-semibold"
2369
+ ),
2370
+ children: day.day
2371
+ },
2372
+ i
2373
+ );
2374
+ }) }),
2375
+ props.showTime && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-3 py-2.5 border-t border-(--ui-border)", children: [
2376
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-(--ui-text-muted) mr-1", children: "\u010Cas:" }),
2377
+ /* @__PURE__ */ jsx(
2378
+ "input",
2379
+ {
2380
+ type: "number",
2381
+ min: 0,
2382
+ max: 23,
2383
+ value: hours,
2384
+ onChange: (e) => setHours(clamp2(parseInt(e.target.value) || 0, 0, 23)),
2385
+ className: "w-11 h-7 text-center text-[13px] border border-(--ui-border-input) rounded-(--ui-radius-md) outline-none focus:border-(--ui-border-focus) [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none"
2386
+ }
2387
+ ),
2388
+ /* @__PURE__ */ jsx("span", { className: "text-(--ui-text-muted) font-medium text-sm", children: ":" }),
2389
+ /* @__PURE__ */ jsx(
2390
+ "input",
2391
+ {
2392
+ type: "number",
2393
+ min: 0,
2394
+ max: 59,
2395
+ value: String(minutes).padStart(2, "0"),
2396
+ onChange: (e) => setMinutes(clamp2(parseInt(e.target.value) || 0, 0, 59)),
2397
+ className: "w-11 h-7 text-center text-[13px] border border-(--ui-border-input) rounded-(--ui-radius-md) outline-none focus:border-(--ui-border-focus) [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none"
2398
+ }
2399
+ ),
2400
+ /* @__PURE__ */ jsx(
2401
+ "button",
2402
+ {
2403
+ type: "button",
2404
+ onMouseDown: (e) => e.preventDefault(),
2405
+ onClick: handleConfirm,
2406
+ disabled: !pending,
2407
+ className: "ml-auto h-7 px-3 text-xs bg-(--ui-primary) text-(--ui-primary-text) rounded-(--ui-radius-md) hover:bg-(--ui-primary-hover) disabled:opacity-40 cursor-pointer disabled:cursor-not-allowed",
2408
+ children: "Potvrdit"
2409
+ }
2410
+ )
2411
+ ] })
2412
+ ]
2413
+ }
2414
+ ),
2415
+ document.body
2416
+ );
2417
+ };
2418
+ var DateInput = ({ size = "middle", showTime = false, ...props }) => {
2419
+ const [isOpen, setIsOpen] = useState(false);
2420
+ const triggerRef = useRef(null);
2421
+ const resolveError = useErrorResolver();
2422
+ const hasValue = props.value != null;
2423
+ const resolvedErrors = props.errorName ? resolveError(props.errorName) : [];
2424
+ const errorDisplay = props.error ?? props.customError ?? (resolvedErrors.length > 0 ? resolvedErrors.join(", ") : void 0);
2425
+ const hasError = !!errorDisplay;
2426
+ const displayValue = hasValue ? showTime ? dayjs(props.value).format("D.M.YYYY H:mm") : dayjs(props.value).format("D.M.YYYY") : null;
2427
+ const defaultPlaceholder2 = showTime ? "Vyberte datum a \u010Das..." : "Vyberte datum...";
2428
+ const handleClear = (e) => {
2429
+ e.stopPropagation();
2430
+ props.onChange?.(null);
2431
+ };
2432
+ return /* @__PURE__ */ jsxs(InputField, { noMargin: props.noMargin, className: props.className, children: [
2433
+ props.label && /* @__PURE__ */ jsx(
2434
+ InputLabel,
2435
+ {
2436
+ label: props.label,
2437
+ required: props.required,
2438
+ className: props.classNames?.label,
2439
+ requiredMarkClassName: props.classNames?.requiredMark
2440
+ }
2441
+ ),
2442
+ /* @__PURE__ */ jsxs(
2443
+ "div",
2444
+ {
2445
+ ref: triggerRef,
2446
+ onClick: () => !props.disabled && setIsOpen((v) => !v),
2447
+ className: twMerge(
2448
+ "flex items-center gap-1.5 border bg-white cursor-pointer transition-colors select-none",
2449
+ inputSizeClasses[size],
2450
+ isOpen ? triggerBorderClasses.open : triggerBorderClasses.default,
2451
+ props.disabled && triggerBorderClasses.disabled,
2452
+ props.classNames?.trigger
2453
+ ),
2454
+ style: hasError ? { borderColor: "var(--ui-danger)" } : void 0,
2455
+ children: [
2456
+ /* @__PURE__ */ jsx("span", { className: twMerge("flex-1 truncate", !displayValue && neutralTextClasses.soft, props.classNames?.value), children: displayValue ?? (props.placeholder ?? defaultPlaceholder2) }),
2457
+ hasValue && !props.disabled && /* @__PURE__ */ jsx(
2458
+ "button",
2459
+ {
2460
+ type: "button",
2461
+ onMouseDown: (e) => e.preventDefault(),
2462
+ onClick: handleClear,
2463
+ className: twMerge("shrink-0 cursor-pointer", neutralIconClasses.default, neutralIconClasses.hover, props.classNames?.clearButton),
2464
+ children: /* @__PURE__ */ jsx(IconX, { size: 12, strokeWidth: 2 })
2465
+ }
2466
+ ),
2467
+ /* @__PURE__ */ jsx(IconCalendar, { size: 13, strokeWidth: 1.5, className: twMerge("shrink-0", neutralIconClasses.default, props.classNames?.icon) })
2468
+ ]
2469
+ }
2470
+ ),
2471
+ /* @__PURE__ */ jsx(
2472
+ CalendarPopup,
2473
+ {
2474
+ anchorRef: triggerRef,
2475
+ isOpen,
2476
+ value: props.value ?? null,
2477
+ minDate: props.minDate,
2478
+ maxDate: props.maxDate,
2479
+ isDateDisabled: props.isDateDisabled,
2480
+ onSelect: (v) => props.onChange?.(v),
2481
+ onClose: () => setIsOpen(false),
2482
+ showTime
2483
+ }
2484
+ ),
2485
+ errorDisplay && /* @__PURE__ */ jsx(InputError, { error: String(errorDisplay), className: props.classNames?.error })
2486
+ ] });
2487
+ };
2488
+ var FilterDateInput = (props) => {
2489
+ const rawValue = props.grid.filter[props.filterKey];
2490
+ const [local, setLocal] = useState(rawValue instanceof Date ? rawValue : null);
2491
+ useEffect(() => {
2492
+ const v = props.grid.filter[props.filterKey];
2493
+ setLocal(v instanceof Date ? v : null);
2494
+ }, [props.grid.filter[props.filterKey]]);
2495
+ const handleChange = (value) => {
2496
+ setLocal(value);
2497
+ props.grid.onChange({ filter: { ...props.grid.filter, [props.filterKey]: value } });
2498
+ };
2499
+ return /* @__PURE__ */ jsx(
2500
+ DateInput,
2501
+ {
2502
+ size: "small",
2503
+ noMargin: true,
2504
+ label: props.label,
2505
+ placeholder: props.placeholder,
2506
+ showTime: props.showTime,
2507
+ value: local,
2508
+ onChange: handleChange
2509
+ }
2510
+ );
2511
+ };
2512
+ var Badge = ({
2513
+ count = 0,
2514
+ size = "default",
2515
+ showZero = false,
2516
+ ...props
2517
+ }) => {
2518
+ const visible = count > 0 || showZero;
2519
+ const label = count > 99 ? "99+" : String(count);
2520
+ return /* @__PURE__ */ jsxs("div", { className: twMerge("relative inline-flex", props.className), children: [
2521
+ props.children,
2522
+ visible && /* @__PURE__ */ jsx(
2523
+ "span",
2524
+ {
2525
+ className: twMerge(
2526
+ "absolute top-0 right-0 translate-x-1/2 -translate-y-1/2 bg-red-500 text-white font-medium rounded-full flex items-center justify-center leading-none",
2527
+ size === "small" ? "min-w-4 h-4 px-1 text-[10px]" : "min-w-4.5 h-4.5 px-1 text-xs",
2528
+ props.classNames?.badge
2529
+ ),
2530
+ children: label
2531
+ }
2532
+ )
2533
+ ] });
2534
+ };
2535
+ var FilterSelectGroupPopover = (props) => {
2536
+ const activeCount = props.groups.filter((g) => {
2537
+ const value = props.grid.filter[g.filterKey];
2538
+ return value !== null && value !== void 0 && value !== "";
2539
+ }).length;
2540
+ const handleReset = () => {
2541
+ const cleared = props.groups.reduce((acc, group) => {
2542
+ acc[group.filterKey] = void 0;
2543
+ return acc;
2544
+ }, {});
2545
+ props.grid.onChange({ filter: { ...props.grid.filter, ...cleared } });
2546
+ };
2547
+ const Icon = props.icon ?? IconUsers;
2548
+ return /* @__PURE__ */ jsx(
2549
+ Dropdown,
2550
+ {
2551
+ className: props.className,
2552
+ content: () => /* @__PURE__ */ jsxs("div", { className: "grid min-w-70 gap-3 p-3", children: [
2553
+ props.groups.map((group) => /* @__PURE__ */ jsxs("div", { className: "grid gap-1", children: [
2554
+ /* @__PURE__ */ jsx("div", { className: "text-xs font-medium text-gray-500", children: group.label }),
2555
+ /* @__PURE__ */ jsx(
2556
+ SelectInput,
2557
+ {
2558
+ size: "small",
2559
+ noMargin: true,
2560
+ placeholder: group.placeholder ?? group.label,
2561
+ options: group.options,
2562
+ labelKey: group.labelKey,
2563
+ valueKey: group.valueKey,
2564
+ value: props.grid.filter[group.filterKey] ?? null,
2565
+ onChange: (value) => {
2566
+ props.grid.onChange({
2567
+ filter: { ...props.grid.filter, [group.filterKey]: value ?? void 0 }
2568
+ });
2569
+ },
2570
+ allowClear: true
2571
+ }
2572
+ )
2573
+ ] }, String(group.filterKey))),
2574
+ activeCount > 0 && /* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx(
2575
+ Button,
2576
+ {
2577
+ variant: "default",
2578
+ size: "small",
2579
+ text: "Resetovat",
2580
+ onClick: handleReset
2581
+ }
2582
+ ) })
2583
+ ] }),
2584
+ children: /* @__PURE__ */ jsx(Badge, { count: activeCount, size: "small", children: /* @__PURE__ */ jsx(
2585
+ Button,
2586
+ {
2587
+ variant: "default",
2588
+ size: "small",
2589
+ text: props.label ?? "U\u017Eivatel",
2590
+ icon: Icon,
2591
+ block: props.block
2592
+ }
2593
+ ) })
2594
+ }
2595
+ );
2596
+ };
2597
+ var FilterDateRangePopover = (props) => {
2598
+ const activeCount = props.groups.filter((group) => {
2599
+ const from = props.grid.filter[group.fromKey];
2600
+ const to = props.grid.filter[group.toKey];
2601
+ return from instanceof Date || to instanceof Date;
2602
+ }).length;
2603
+ const handleDateChange = (key, value) => {
2604
+ props.grid.onChange({
2605
+ filter: { ...props.grid.filter, [key]: value ?? void 0 }
2606
+ });
2607
+ };
2608
+ const handleReset = () => {
2609
+ const cleared = props.groups.reduce((acc, group) => {
2610
+ acc[group.fromKey] = void 0;
2611
+ acc[group.toKey] = void 0;
2612
+ return acc;
2613
+ }, {});
2614
+ props.grid.onChange({ filter: { ...props.grid.filter, ...cleared } });
2615
+ };
2616
+ return /* @__PURE__ */ jsx(
2617
+ Dropdown,
2618
+ {
2619
+ className: props.className,
2620
+ content: () => /* @__PURE__ */ jsxs("div", { className: "grid min-w-85 gap-3 p-3", children: [
2621
+ props.groups.map((group) => {
2622
+ const fromValue = props.grid.filter[group.fromKey];
2623
+ const toValue = props.grid.filter[group.toKey];
2624
+ return /* @__PURE__ */ jsxs("div", { className: "grid gap-1", children: [
2625
+ /* @__PURE__ */ jsx("div", { className: "text-xs font-medium text-gray-500", children: group.label }),
2626
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[1fr_auto_1fr] items-center gap-1", children: [
2627
+ /* @__PURE__ */ jsx(
2628
+ DateInput,
2629
+ {
2630
+ size: "small",
2631
+ noMargin: true,
2632
+ placeholder: "Od",
2633
+ value: fromValue instanceof Date ? fromValue : null,
2634
+ onChange: (value) => handleDateChange(group.fromKey, value)
2635
+ }
2636
+ ),
2637
+ /* @__PURE__ */ jsx("span", { className: "px-1 text-gray-400", children: "\u2192" }),
2638
+ /* @__PURE__ */ jsx(
2639
+ DateInput,
2640
+ {
2641
+ size: "small",
2642
+ noMargin: true,
2643
+ placeholder: "Do",
2644
+ value: toValue instanceof Date ? toValue : null,
2645
+ onChange: (value) => handleDateChange(group.toKey, value)
2646
+ }
2647
+ )
2648
+ ] })
2649
+ ] }, String(group.fromKey));
2650
+ }),
2651
+ activeCount > 0 && /* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx(
2652
+ Button,
2653
+ {
2654
+ variant: "default",
2655
+ size: "small",
2656
+ text: "Resetovat",
2657
+ onClick: handleReset
2658
+ }
2659
+ ) })
2660
+ ] }),
2661
+ children: /* @__PURE__ */ jsx(Badge, { count: activeCount, size: "small", children: /* @__PURE__ */ jsx(
2662
+ Button,
2663
+ {
2664
+ variant: "default",
2665
+ size: "small",
2666
+ text: props.label ?? "Datum",
2667
+ icon: IconCalendar,
2668
+ block: props.block
2669
+ }
2670
+ ) })
2671
+ }
2672
+ );
2673
+ };
2674
+ var DrawerContext = createContext(null);
2675
+ function useDrawer() {
2676
+ const ctx = useContext(DrawerContext);
2677
+ if (!ctx) {
2678
+ throw new Error("useDrawer must be used inside Drawer");
2679
+ }
2680
+ return ctx;
2681
+ }
2682
+ var feedbackTypeConfig = {
2683
+ success: { icon: IconCheck, borderColorVar: "--ui-success", iconColor: "text-(--ui-success)" },
2684
+ error: { icon: IconCircleX, borderColorVar: "--ui-danger", iconColor: "text-(--ui-danger)" },
2685
+ warning: { icon: IconAlertTriangle, borderColorVar: "--ui-warning", iconColor: "text-(--ui-warning)" },
2686
+ info: { icon: IconInfoCircle, borderColorVar: "--ui-info", iconColor: "text-(--ui-info)" }
2687
+ };
2688
+ var _add = null;
2689
+ var notification = {
2690
+ success: (message, options) => _add?.("success", message, options),
2691
+ error: (message, options) => _add?.("error", message, options),
2692
+ warning: (message, options) => _add?.("warning", message, options),
2693
+ info: (message, options) => _add?.("info", message, options)
2694
+ };
2695
+ var NotificationContext = createContext(null);
2696
+ var useNotificationContext = () => {
2697
+ const ctx = useContext(NotificationContext);
2698
+ if (!ctx) {
2699
+ throw new Error("useNotification must be used within NotificationProvider");
2700
+ }
2701
+ return ctx;
2702
+ };
2703
+ var Toast = ({ toast, onClose, classNames }) => {
2704
+ const { icon: Icon, borderColorVar, iconColor } = feedbackTypeConfig[toast.type];
2705
+ return /* @__PURE__ */ jsxs(
2706
+ "div",
2707
+ {
2708
+ className: twMerge(
2709
+ "flex items-start gap-3 w-80 bg-white border border-(--ui-border) rounded-lg shadow-md px-4 py-3",
2710
+ "transition-all duration-300 ease-out",
2711
+ toast.visible ? "opacity-100 translate-x-0" : "opacity-0 translate-x-6"
2712
+ ),
2713
+ style: { borderLeftWidth: "4px", borderLeftColor: `var(${borderColorVar})` },
2714
+ children: [
2715
+ /* @__PURE__ */ jsx(Icon, { size: 18, strokeWidth: 2, className: twMerge("shrink-0 mt-0.5", iconColor, classNames?.icon) }),
2716
+ /* @__PURE__ */ jsxs("div", { className: twMerge("flex-1 min-w-0", classNames?.content), children: [
2717
+ /* @__PURE__ */ jsx("p", { className: twMerge("text-sm font-medium text-(--ui-text-strong) leading-snug", classNames?.message), children: toast.message }),
2718
+ toast.description && /* @__PURE__ */ jsx("p", { className: twMerge("mt-0.5 text-xs text-(--ui-text-muted) leading-snug", classNames?.description), children: toast.description })
2719
+ ] }),
2720
+ /* @__PURE__ */ jsx(
2721
+ "button",
2722
+ {
2723
+ type: "button",
2724
+ onClick: () => onClose(toast.id),
2725
+ className: twMerge("shrink-0 transition-colors cursor-pointer mt-0.5", neutralIconClasses.default, neutralIconClasses.hover, classNames?.closeButton),
2726
+ children: /* @__PURE__ */ jsx(IconX, { size: 14, strokeWidth: 2 })
2727
+ }
2728
+ )
2729
+ ]
2730
+ }
2731
+ );
2732
+ };
2733
+ var counter = 0;
2734
+ var MAX_TOASTS = 6;
2735
+ var NotificationProvider = ({ children }) => {
2736
+ const [toasts, setToasts] = useState([]);
2737
+ const timers = useRef(/* @__PURE__ */ new Map());
2738
+ const remove = useCallback((id) => {
2739
+ timers.current.delete(id);
2740
+ setToasts((prev) => prev.map((t) => t.id === id ? { ...t, visible: false } : t));
2741
+ setTimeout(() => {
2742
+ setToasts((prev) => prev.filter((t) => t.id !== id));
2743
+ }, 320);
2744
+ }, []);
2745
+ const add = useCallback(
2746
+ (type, message, options) => {
2747
+ const id = ++counter;
2748
+ const duration = options?.duration ?? 4e3;
2749
+ setToasts((prev) => {
2750
+ const next = [
2751
+ ...prev,
2752
+ { id, type, message, description: options?.description, duration, visible: false }
2753
+ ];
2754
+ return next.length > MAX_TOASTS ? next.slice(next.length - MAX_TOASTS) : next;
2755
+ });
2756
+ requestAnimationFrame(() => {
2757
+ requestAnimationFrame(() => {
2758
+ setToasts((prev) => prev.map((t) => t.id === id ? { ...t, visible: true } : t));
2759
+ });
2760
+ });
2761
+ if (duration > 0) {
2762
+ const timer = setTimeout(() => remove(id), duration);
2763
+ timers.current.set(id, timer);
2764
+ }
2765
+ },
2766
+ [remove]
2767
+ );
2768
+ useEffect(() => {
2769
+ _add = add;
2770
+ return () => {
2771
+ _add = null;
2772
+ };
2773
+ }, [add]);
2774
+ useEffect(() => {
2775
+ const t = timers.current;
2776
+ return () => {
2777
+ t.forEach(clearTimeout);
2778
+ };
2779
+ }, []);
2780
+ return /* @__PURE__ */ jsxs(NotificationContext.Provider, { value: { add }, children: [
2781
+ children,
2782
+ createPortal(
2783
+ /* @__PURE__ */ jsx("div", { className: "fixed top-4 right-4 z-9999 flex flex-col gap-2 pointer-events-none", children: toasts.map((toast) => /* @__PURE__ */ jsx("div", { className: "pointer-events-auto", children: /* @__PURE__ */ jsx(Toast, { toast, onClose: remove }) }, toast.id)) }),
2784
+ document.body
2785
+ )
2786
+ ] });
2787
+ };
2788
+ var TRANSITION_MS = 220;
2789
+ var panelBase = {
2790
+ top: "fixed top-0 left-0 right-0 z-1001 flex flex-col",
2791
+ bottom: "fixed bottom-0 left-0 right-0 z-1001 flex flex-col",
2792
+ left: "fixed top-0 left-0 bottom-0 z-1001 flex flex-col w-120 max-w-[95vw]",
2793
+ right: "fixed top-0 right-0 bottom-0 z-1001 flex flex-col w-120 max-w-[95vw]"
2794
+ };
2795
+ var panelHidden = {
2796
+ top: "-translate-y-full",
2797
+ bottom: "translate-y-full",
2798
+ left: "-translate-x-full",
2799
+ right: "translate-x-full"
2800
+ };
2801
+ var panelVisible = {
2802
+ top: "translate-y-0",
2803
+ bottom: "translate-y-0",
2804
+ left: "translate-x-0",
2805
+ right: "translate-x-0"
2806
+ };
2807
+ var bodyMaxHeight = {
2808
+ withFooter: "max-h-[calc(90dvh-120px)]",
2809
+ withoutFooter: "max-h-[calc(90dvh-65px)]"
2810
+ };
2811
+ var Drawer = ({ placement = "top", ...props }) => {
2812
+ const [rendered, setRendered] = useState(false);
2813
+ const [visible, setVisible] = useState(false);
2814
+ const [title, setTitle] = useState("");
2815
+ const [footer, setFooter] = useState(null);
2816
+ const [loading, setLoading] = useState(false);
2817
+ const frameRef = useRef(0);
2818
+ useEffect(() => {
2819
+ if (props.isOpen) {
2820
+ setRendered(true);
2821
+ frameRef.current = requestAnimationFrame(() => {
2822
+ frameRef.current = requestAnimationFrame(() => setVisible(true));
2823
+ });
2824
+ } else {
2825
+ setVisible(false);
2826
+ const t = setTimeout(() => setRendered(false), TRANSITION_MS);
2827
+ return () => clearTimeout(t);
2828
+ }
2829
+ return () => cancelAnimationFrame(frameRef.current);
2830
+ }, [props.isOpen]);
2831
+ useEffect(() => {
2832
+ if (!rendered) {
2833
+ return;
2834
+ }
2835
+ const handleKeyDown = (e) => {
2836
+ if (e.key === "Escape") {
2837
+ props.onClose(false);
2838
+ }
2839
+ };
2840
+ document.body.style.overflow = "hidden";
2841
+ document.addEventListener("keydown", handleKeyDown);
2842
+ return () => {
2843
+ document.body.style.overflow = "";
2844
+ document.removeEventListener("keydown", handleKeyDown);
2845
+ };
2846
+ }, [rendered, props.onClose]);
2847
+ if (!rendered) {
2848
+ return null;
2849
+ }
2850
+ const close = () => props.onClose(false);
2851
+ const saveAndClose = (message) => {
2852
+ if (message) {
2853
+ notification.success(message);
2854
+ }
2855
+ props.onClose(true);
2856
+ };
2857
+ const isVertical = placement === "top" || placement === "bottom";
2858
+ const hasFooter = footer !== null;
2859
+ return createPortal(
2860
+ /* @__PURE__ */ jsxs(DrawerContext.Provider, { value: { close, saveAndClose, setTitle, setFooter, setLoading }, children: [
2861
+ /* @__PURE__ */ jsx(
2862
+ "div",
2863
+ {
2864
+ className: twMerge(
2865
+ "fixed inset-0 z-1000 bg-black/40 transition-opacity duration-220",
2866
+ visible ? "opacity-100" : "opacity-0",
2867
+ props.classNames?.overlay
2868
+ ),
2869
+ onClick: close
2870
+ }
2871
+ ),
2872
+ /* @__PURE__ */ jsxs(
2873
+ "div",
2874
+ {
2875
+ className: twMerge(
2876
+ panelBase[placement],
2877
+ "bg-white shadow-xl transition-transform duration-220 ease-out",
2878
+ visible ? panelVisible[placement] : panelHidden[placement],
2879
+ props.className,
2880
+ props.classNames?.panel
2881
+ ),
2882
+ children: [
2883
+ /* @__PURE__ */ jsxs("div", { className: twMerge("flex items-center justify-between gap-2 bg-(--ui-primary) shrink-0", drawerLayoutClasses.header, props.classNames?.header), children: [
2884
+ /* @__PURE__ */ jsx("span", { className: twMerge(componentTitleClasses.inverse, "truncate", props.classNames?.title), children: title }),
2885
+ /* @__PURE__ */ jsx(
2886
+ "button",
2887
+ {
2888
+ type: "button",
2889
+ onClick: close,
2890
+ className: twMerge("shrink-0 text-(--ui-primary-text)/70 hover:text-(--ui-primary-text) cursor-pointer transition-colors", props.classNames?.closeButton),
2891
+ children: /* @__PURE__ */ jsx(IconX, { size: 18, strokeWidth: 1.5 })
2892
+ }
2893
+ )
2894
+ ] }),
2895
+ /* @__PURE__ */ jsx(
2896
+ "div",
2897
+ {
2898
+ className: twMerge(
2899
+ "overflow-y-auto flex-1",
2900
+ drawerLayoutClasses.body,
2901
+ isVertical && (hasFooter ? bodyMaxHeight.withFooter : bodyMaxHeight.withoutFooter),
2902
+ props.classNames?.body
2903
+ ),
2904
+ children: loading ? /* @__PURE__ */ jsx("div", { className: twMerge("flex items-center justify-center py-16", props.classNames?.loading), children: /* @__PURE__ */ jsx(Spinner, { size: "large", color: "primary" }) }) : props.children
2905
+ }
2906
+ ),
2907
+ hasFooter && /* @__PURE__ */ jsx("div", { className: twMerge("shrink-0 border-t border-(--ui-border)", neutralSurfaceClasses.subtle, drawerLayoutClasses.footer, props.classNames?.footer), children: footer })
2908
+ ]
2909
+ }
2910
+ )
2911
+ ] }),
2912
+ document.body
2913
+ );
2914
+ };
2915
+ var DrawerTitle = ({ children }) => {
2916
+ const { setTitle } = useDrawer();
2917
+ useEffect(() => {
2918
+ setTitle(String(children));
2919
+ return () => setTitle("");
2920
+ }, [children]);
2921
+ return null;
2922
+ };
2923
+ var alignClass = {
2924
+ left: "justify-start",
2925
+ center: "justify-center",
2926
+ right: "justify-end"
2927
+ };
2928
+ var DrawerFooter = ({ children, align = "right" }) => {
2929
+ const { setFooter } = useDrawer();
2930
+ useEffect(() => {
2931
+ setFooter(
2932
+ /* @__PURE__ */ jsx("div", { className: twMerge("flex items-center gap-2", alignClass[align]), children })
2933
+ );
2934
+ return () => setFooter(null);
2935
+ }, []);
2936
+ return null;
2937
+ };
2938
+ var Skeleton = ({
2939
+ active = false,
2940
+ title = true,
2941
+ paragraph,
2942
+ className
2943
+ }) => {
2944
+ const rows = paragraph?.rows ?? 3;
2945
+ return /* @__PURE__ */ jsxs("div", { className: twMerge("space-y-3", active && "animate-pulse", className), children: [
2946
+ title && /* @__PURE__ */ jsx("div", { className: "h-4 w-1/3 rounded bg-(--ui-surface-muted)" }),
2947
+ /* @__PURE__ */ jsx("div", { className: "space-y-2", children: Array.from({ length: rows }, (_, index) => /* @__PURE__ */ jsx(
2948
+ "div",
2949
+ {
2950
+ className: twMerge(
2951
+ "h-4 rounded bg-(--ui-surface-muted)",
2952
+ index === rows - 1 ? "w-4/5" : "w-full"
2953
+ )
2954
+ },
2955
+ index
2956
+ )) })
2957
+ ] });
2958
+ };
2959
+ var DrawerSkeleton = () => /* @__PURE__ */ jsxs("div", { className: "animate-pulse space-y-3", children: [
2960
+ /* @__PURE__ */ jsx("div", { className: "h-4 bg-(--ui-surface-muted) rounded w-1/3" }),
2961
+ /* @__PURE__ */ jsx("div", { className: "h-8 bg-(--ui-surface-muted) rounded" }),
2962
+ /* @__PURE__ */ jsx("div", { className: "h-4 bg-(--ui-surface-muted) rounded w-1/3 mt-5" }),
2963
+ /* @__PURE__ */ jsx("div", { className: "h-8 bg-(--ui-surface-muted) rounded" }),
2964
+ /* @__PURE__ */ jsx("div", { className: "h-4 bg-(--ui-surface-muted) rounded w-1/3 mt-5" }),
2965
+ /* @__PURE__ */ jsx("div", { className: "h-8 bg-(--ui-surface-muted) rounded" }),
2966
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4 mt-5", children: [
2967
+ /* @__PURE__ */ jsxs("div", { children: [
2968
+ /* @__PURE__ */ jsx("div", { className: "h-4 bg-(--ui-surface-muted) rounded w-1/2 mb-1" }),
2969
+ /* @__PURE__ */ jsx("div", { className: "h-8 bg-(--ui-surface-muted) rounded" })
2970
+ ] }),
2971
+ /* @__PURE__ */ jsxs("div", { children: [
2972
+ /* @__PURE__ */ jsx("div", { className: "h-4 bg-(--ui-surface-muted) rounded w-1/2 mb-1" }),
2973
+ /* @__PURE__ */ jsx("div", { className: "h-8 bg-(--ui-surface-muted) rounded" })
2974
+ ] })
2975
+ ] })
2976
+ ] });
2977
+ var DrawerContent = ({ loading = false, children }) => {
2978
+ if (loading) {
2979
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-5", children: [
2980
+ /* @__PURE__ */ jsx(DrawerSkeleton, {}),
2981
+ /* @__PURE__ */ jsx(Skeleton, { active: true, title: false, paragraph: { rows: 2 } })
2982
+ ] });
2983
+ }
2984
+ return /* @__PURE__ */ jsx(Fragment, { children });
2985
+ };
2986
+ var Alert = ({ type = "info", message, description, closable = false, className, classNames }) => {
2987
+ const [closed, setClosed] = useState(false);
2988
+ if (closed) {
2989
+ return null;
2990
+ }
2991
+ const { icon: Icon, borderColorVar, iconColor } = feedbackTypeConfig[type];
2992
+ return /* @__PURE__ */ jsxs(
2993
+ "div",
2994
+ {
2995
+ className: twMerge(
2996
+ "flex items-start gap-3 bg-white border border-(--ui-border) rounded-lg px-4 py-3",
2997
+ className
2998
+ ),
2999
+ style: { borderLeftWidth: "4px", borderLeftColor: `var(${borderColorVar})` },
3000
+ children: [
3001
+ /* @__PURE__ */ jsx(Icon, { size: 17, strokeWidth: 2, className: twMerge("shrink-0 mt-0.5", iconColor, classNames?.icon) }),
3002
+ /* @__PURE__ */ jsxs("div", { className: twMerge("flex-1 min-w-0", classNames?.content), children: [
3003
+ /* @__PURE__ */ jsx("p", { className: twMerge("text-sm font-medium text-(--ui-text-strong) leading-snug", classNames?.message), children: message }),
3004
+ description && /* @__PURE__ */ jsx("p", { className: twMerge("mt-1 text-xs text-(--ui-text-muted) leading-relaxed", classNames?.description), children: description })
3005
+ ] }),
3006
+ closable && /* @__PURE__ */ jsx(
3007
+ "button",
3008
+ {
3009
+ type: "button",
3010
+ onClick: () => setClosed(true),
3011
+ className: twMerge("shrink-0 transition-colors cursor-pointer mt-0.5", neutralIconClasses.default, neutralIconClasses.hover, classNames?.closeButton),
3012
+ children: /* @__PURE__ */ jsx(IconX, { size: 14, strokeWidth: 2 })
3013
+ }
3014
+ )
3015
+ ]
3016
+ }
3017
+ );
3018
+ };
3019
+ var ServerError = ({ className }) => {
3020
+ const error = useServerError()();
3021
+ if (!error) return null;
3022
+ const description = error.detail && error.detail !== error.title ? error.detail : void 0;
3023
+ return /* @__PURE__ */ jsx("div", { className: className ?? "mb-3", children: /* @__PURE__ */ jsx(Alert, { type: "error", message: error.title, description }) });
3024
+ };
3025
+ var Panel = ({
3026
+ variant = "outlined",
3027
+ inner = false,
3028
+ ...props
3029
+ }) => {
3030
+ const hasHeader = !!props.title || !!props.headerExtra;
3031
+ return /* @__PURE__ */ jsxs(
3032
+ "div",
3033
+ {
3034
+ className: twMerge(
3035
+ "bg-white",
3036
+ variant === "outlined" && "border border-(--ui-border) rounded-(--ui-radius-lg)",
3037
+ inner && `${neutralSurfaceClasses.subtle} rounded-md`,
3038
+ props.className
3039
+ ),
3040
+ children: [
3041
+ hasHeader && /* @__PURE__ */ jsxs(
3042
+ "div",
3043
+ {
3044
+ className: twMerge(
3045
+ "flex items-center justify-between gap-2",
3046
+ panelLayoutClasses.header,
3047
+ variant === "outlined" && `border-b border-(--ui-border) ${neutralSurfaceClasses.subtle} rounded-t-lg`,
3048
+ inner && "border-b border-(--ui-border)",
3049
+ props.classNames?.header
3050
+ ),
3051
+ children: [
3052
+ props.title && /* @__PURE__ */ jsx("span", { className: twMerge(componentTitleClasses.default, props.classNames?.title), children: props.title }),
3053
+ props.headerExtra && /* @__PURE__ */ jsx("div", { className: twMerge("ml-auto flex items-center gap-2", props.classNames?.headerExtra), children: props.headerExtra })
3054
+ ]
3055
+ }
3056
+ ),
3057
+ /* @__PURE__ */ jsxs("div", { className: twMerge(panelLayoutClasses.body, props.classNames?.body), children: [
3058
+ props.showServerError && /* @__PURE__ */ jsx(ServerError, { className: props.classNames?.serverError }),
3059
+ props.children
3060
+ ] }),
3061
+ props.footer && /* @__PURE__ */ jsx(
3062
+ "div",
3063
+ {
3064
+ className: twMerge(
3065
+ "flex items-center justify-end gap-2 border-t border-(--ui-border)",
3066
+ panelLayoutClasses.footer,
3067
+ variant === "outlined" && `${neutralSurfaceClasses.subtle} rounded-b-lg`,
3068
+ props.classNames?.footer
3069
+ ),
3070
+ children: props.footer
3071
+ }
3072
+ )
3073
+ ]
3074
+ }
3075
+ );
3076
+ };
3077
+ var Tabs = ({
3078
+ block = true,
3079
+ ...props
3080
+ }) => /* @__PURE__ */ jsx(
3081
+ "div",
3082
+ {
3083
+ className: twMerge(
3084
+ "inline-flex gap-0 rounded-(--ui-radius-lg) p-1",
3085
+ neutralSurfaceClasses.subtle,
3086
+ block && "flex w-full",
3087
+ props.className,
3088
+ props.classNames?.list
3089
+ ),
3090
+ children: props.options.map((opt) => /* @__PURE__ */ jsx(
3091
+ "button",
3092
+ {
3093
+ type: "button",
3094
+ onClick: () => props.onChange(opt.value),
3095
+ className: twMerge(
3096
+ "flex-1 h-9 px-4 text-[14px] rounded-[calc(var(--ui-radius-lg)-4px)] transition-all duration-150 cursor-pointer select-none whitespace-nowrap",
3097
+ props.value === opt.value ? "bg-white text-(--ui-text-strong) shadow-sm font-semibold" : "text-(--ui-text-muted) hover:text-(--ui-text)",
3098
+ props.classNames?.tab,
3099
+ props.value === opt.value ? props.classNames?.activeTab : props.classNames?.inactiveTab
3100
+ ),
3101
+ children: opt.label
3102
+ },
3103
+ opt.value
3104
+ ))
3105
+ }
3106
+ );
3107
+ var colorClasses = {
3108
+ geekblue: {
3109
+ outlined: "bg-blue-50 text-blue-700 border border-blue-200",
3110
+ filled: "bg-blue-600 text-white border border-blue-600"
3111
+ },
3112
+ blue: {
3113
+ outlined: "bg-blue-50 text-blue-700 border border-blue-200",
3114
+ filled: "bg-blue-600 text-white border border-blue-600"
3115
+ },
3116
+ green: {
3117
+ outlined: "bg-green-50 text-green-700 border border-green-200",
3118
+ filled: "bg-green-600 text-white border border-green-600"
3119
+ },
3120
+ cyan: {
3121
+ outlined: "bg-cyan-50 text-cyan-700 border border-cyan-200",
3122
+ filled: "bg-cyan-600 text-white border border-cyan-600"
3123
+ },
3124
+ red: {
3125
+ outlined: "bg-red-50 text-red-700 border border-red-200",
3126
+ filled: "bg-red-600 text-white border border-red-600"
3127
+ },
3128
+ volcano: {
3129
+ outlined: "bg-orange-50 text-orange-700 border border-orange-200",
3130
+ filled: "bg-orange-600 text-white border border-orange-600"
3131
+ },
3132
+ orange: {
3133
+ outlined: "bg-orange-50 text-orange-700 border border-orange-200",
3134
+ filled: "bg-orange-600 text-white border border-orange-600"
3135
+ },
3136
+ gold: {
3137
+ outlined: "bg-yellow-50 text-yellow-700 border border-yellow-200",
3138
+ filled: "bg-yellow-500 text-white border border-yellow-500"
3139
+ },
3140
+ purple: {
3141
+ outlined: "bg-purple-50 text-purple-700 border border-purple-200",
3142
+ filled: "bg-purple-600 text-white border border-purple-600"
3143
+ },
3144
+ magenta: {
3145
+ outlined: "bg-pink-50 text-pink-700 border border-pink-200",
3146
+ filled: "bg-pink-600 text-white border border-pink-600"
3147
+ },
3148
+ gray: {
3149
+ outlined: "bg-gray-50 text-gray-600 border border-gray-200",
3150
+ filled: "bg-gray-500 text-white border border-gray-500"
3151
+ },
3152
+ default: {
3153
+ outlined: "bg-gray-50 text-gray-600 border border-gray-200",
3154
+ filled: "bg-gray-500 text-white border border-gray-500"
3155
+ }
3156
+ };
3157
+ var Tag = ({
3158
+ color = "geekblue",
3159
+ variant = "outlined",
3160
+ ...props
3161
+ }) => /* @__PURE__ */ jsx(
3162
+ "span",
3163
+ {
3164
+ className: twMerge(
3165
+ "inline-flex items-center px-2 h-5 text-xs rounded-full whitespace-nowrap",
3166
+ colorClasses[color][variant],
3167
+ props.className
3168
+ ),
3169
+ children: /* @__PURE__ */ jsx("span", { className: props.contentClassName, children: props.children })
3170
+ }
3171
+ );
3172
+ var ActiveTag = ({
3173
+ value,
3174
+ trueLabel = "Aktivn\xED",
3175
+ falseLabel = "Neaktivn\xED"
3176
+ }) => /* @__PURE__ */ jsx(Tag, { color: value ? "green" : "red", children: value ? trueLabel : falseLabel });
3177
+ var FlagTag = ({ value, label, color = "geekblue", variant }) => {
3178
+ if (!value) {
3179
+ return null;
3180
+ }
3181
+ return /* @__PURE__ */ jsx(Tag, { color, variant, children: label });
3182
+ };
3183
+ var AVATAR_COLORS = [
3184
+ "bg-blue-500",
3185
+ "bg-green-500",
3186
+ "bg-purple-500",
3187
+ "bg-orange-500",
3188
+ "bg-pink-500",
3189
+ "bg-teal-500",
3190
+ "bg-red-500",
3191
+ "bg-indigo-500"
3192
+ ];
3193
+ function colorFromString(str) {
3194
+ let hash = 0;
3195
+ for (let i = 0; i < str.length; i++) {
3196
+ hash = str.charCodeAt(i) + ((hash << 5) - hash);
3197
+ }
3198
+ return AVATAR_COLORS[Math.abs(hash) % AVATAR_COLORS.length];
3199
+ }
3200
+ function getInitials(name) {
3201
+ const parts = name.trim().split(/\s+/);
3202
+ if (parts.length === 1) {
3203
+ return parts[0].slice(0, 2).toUpperCase();
3204
+ }
3205
+ return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
3206
+ }
3207
+ var sizeClasses = {
3208
+ xs: "w-6 h-6 text-[10px]",
3209
+ sm: "w-7 h-7 text-xs",
3210
+ md: "w-8 h-8 text-sm",
3211
+ lg: "w-10 h-10 text-base",
3212
+ xl: "w-12 h-12 text-lg"
3213
+ };
3214
+ var Avatar = ({ size = "md", ...props }) => {
3215
+ const initials = props.name ? getInitials(props.name) : "?";
3216
+ const bgColor = props.color ?? (props.name ? colorFromString(props.name) : "bg-gray-400");
3217
+ if (props.src) {
3218
+ return /* @__PURE__ */ jsx(
3219
+ "img",
3220
+ {
3221
+ src: props.src,
3222
+ alt: props.name ?? "avatar",
3223
+ className: twMerge(
3224
+ "rounded-full object-cover shrink-0",
3225
+ sizeClasses[size],
3226
+ props.className
3227
+ )
3228
+ }
3229
+ );
3230
+ }
3231
+ return /* @__PURE__ */ jsx(
3232
+ "span",
3233
+ {
3234
+ title: props.name,
3235
+ className: twMerge(
3236
+ "inline-flex items-center justify-center rounded-full text-white font-medium shrink-0 select-none",
3237
+ sizeClasses[size],
3238
+ bgColor,
3239
+ props.className
3240
+ ),
3241
+ children: /* @__PURE__ */ jsx("span", { className: props.initialsClassName, children: initials })
3242
+ }
3243
+ );
3244
+ };
3245
+ var defaultToolbar = [
3246
+ "bold",
3247
+ "italic",
3248
+ "underline",
3249
+ "bullet",
3250
+ "numbered",
3251
+ "link",
3252
+ "unlink",
3253
+ "clear"
3254
+ ];
3255
+ var emptyHtml = "<p></p>";
3256
+ function normalizeHtml(html) {
3257
+ const trimmed = html.replace(/<div><br><\/div>/gi, "").replace(/<p><br><\/p>/gi, "").trim();
3258
+ return trimmed === "" ? emptyHtml : html;
3259
+ }
3260
+ function isHtmlEmpty(html) {
3261
+ if (!html) {
3262
+ return true;
3263
+ }
3264
+ const textOnly = html.replace(/<br\s*\/?>/gi, "").replace(/&nbsp;/gi, " ").replace(/<[^>]+>/g, "").trim();
3265
+ return textOnly === "";
3266
+ }
3267
+ var HtmlInput = ({
3268
+ placeholder = "Zadejte text...",
3269
+ value,
3270
+ disabled = false,
3271
+ minHeight = 180,
3272
+ toolbar = defaultToolbar,
3273
+ ...props
3274
+ }) => {
3275
+ const editorRef = useRef(null);
3276
+ const [focused, setFocused] = useState(false);
3277
+ const [selectionVersion, setSelectionVersion] = useState(0);
3278
+ const resolveError = useErrorResolver();
3279
+ const resolvedErrors = props.errorName ? resolveError(props.errorName) : [];
3280
+ const errorDisplay = props.error ?? props.customError ?? (resolvedErrors.length > 0 ? resolvedErrors.join(", ") : void 0);
3281
+ const hasError = !!errorDisplay;
3282
+ const normalizedValue = value ?? "";
3283
+ const showPlaceholder = isHtmlEmpty(normalizedValue);
3284
+ useEffect(() => {
3285
+ if (!editorRef.current) {
3286
+ return;
3287
+ }
3288
+ if (editorRef.current.innerHTML !== normalizedValue) {
3289
+ editorRef.current.innerHTML = normalizedValue;
3290
+ }
3291
+ }, [normalizedValue]);
3292
+ useEffect(() => {
3293
+ const handleSelectionChange = () => {
3294
+ if (focused) {
3295
+ setSelectionVersion((v) => v + 1);
3296
+ }
3297
+ };
3298
+ document.addEventListener("selectionchange", handleSelectionChange);
3299
+ return () => document.removeEventListener("selectionchange", handleSelectionChange);
3300
+ }, [focused]);
3301
+ const activeState = useMemo(() => ({
3302
+ bold: document.queryCommandState?.("bold") ?? false,
3303
+ italic: document.queryCommandState?.("italic") ?? false,
3304
+ underline: document.queryCommandState?.("underline") ?? false,
3305
+ bullet: document.queryCommandState?.("insertUnorderedList") ?? false,
3306
+ numbered: document.queryCommandState?.("insertOrderedList") ?? false
3307
+ }), [selectionVersion]);
3308
+ const emitChange = () => {
3309
+ if (!editorRef.current) {
3310
+ return;
3311
+ }
3312
+ props.onChange?.(normalizeHtml(editorRef.current.innerHTML));
3313
+ };
3314
+ const focusEditor = () => {
3315
+ editorRef.current?.focus();
3316
+ };
3317
+ const runCommand = (action) => {
3318
+ if (disabled) {
3319
+ return;
3320
+ }
3321
+ focusEditor();
3322
+ switch (action) {
3323
+ case "bullet":
3324
+ document.execCommand("insertUnorderedList");
3325
+ break;
3326
+ case "numbered":
3327
+ document.execCommand("insertOrderedList");
3328
+ break;
3329
+ case "link": {
3330
+ const url = window.prompt("Zadejte URL", "https://");
3331
+ if (url) {
3332
+ document.execCommand("createLink", false, url);
3333
+ }
3334
+ break;
3335
+ }
3336
+ case "unlink":
3337
+ document.execCommand("unlink");
3338
+ break;
3339
+ case "clear":
3340
+ document.execCommand("removeFormat");
3341
+ document.execCommand("formatBlock", false, "p");
3342
+ break;
3343
+ default:
3344
+ document.execCommand(action);
3345
+ break;
3346
+ }
3347
+ emitChange();
3348
+ setSelectionVersion((v) => v + 1);
3349
+ };
3350
+ const toolbarButtons = [
3351
+ { key: "bold", icon: IconBold, title: "Tu\u010Dn\u011B", active: activeState.bold },
3352
+ { key: "italic", icon: IconItalic, title: "Kurz\xEDva", active: activeState.italic },
3353
+ { key: "underline", icon: IconUnderline, title: "Podtr\u017Een\xED", active: activeState.underline },
3354
+ { key: "bullet", icon: IconList, title: "Odr\xE1\u017Eky", active: activeState.bullet },
3355
+ { key: "numbered", icon: IconListNumbers, title: "\u010C\xEDslov\xE1n\xED", active: activeState.numbered },
3356
+ { key: "link", icon: IconLink, title: "Odkaz", active: false },
3357
+ { key: "unlink", icon: IconUnlink, title: "Zru\u0161it odkaz", active: false },
3358
+ { key: "clear", icon: IconClearFormatting, title: "Vy\u010Distit form\xE1t", active: false }
3359
+ ].filter((button) => toolbar.includes(button.key));
3360
+ return /* @__PURE__ */ jsxs(InputField, { noMargin: props.noMargin, className: props.className, children: [
3361
+ props.label && /* @__PURE__ */ jsx(
3362
+ InputLabel,
3363
+ {
3364
+ label: props.label,
3365
+ required: props.required,
3366
+ className: props.classNames?.label,
3367
+ requiredMarkClassName: props.classNames?.requiredMark
3368
+ }
3369
+ ),
3370
+ /* @__PURE__ */ jsxs(
3371
+ "div",
3372
+ {
3373
+ className: twMerge(
3374
+ "overflow-hidden rounded-(--ui-radius-md) border bg-white",
3375
+ focused ? triggerBorderClasses.open : triggerBorderClasses.default,
3376
+ disabled && triggerBorderClasses.disabled,
3377
+ hasError && triggerBorderClasses.error,
3378
+ props.classNames?.shell
3379
+ ),
3380
+ children: [
3381
+ /* @__PURE__ */ jsx("div", { className: twMerge("flex flex-wrap items-center gap-1 border-b border-(--ui-border) bg-(--ui-surface-subtle) px-2 py-1.5", props.classNames?.toolbar), children: toolbarButtons.map((button) => {
3382
+ const Icon = button.icon;
3383
+ return /* @__PURE__ */ jsx(
3384
+ "button",
3385
+ {
3386
+ type: "button",
3387
+ title: button.title,
3388
+ disabled,
3389
+ onMouseDown: (e) => e.preventDefault(),
3390
+ onClick: () => runCommand(button.key),
3391
+ className: twMerge(
3392
+ "inline-flex h-8 w-8 items-center justify-center rounded-md border transition-colors disabled:opacity-40 disabled:cursor-not-allowed",
3393
+ button.active ? "border-(--ui-border-focus) bg-white text-(--ui-text-strong)" : "border-transparent bg-transparent",
3394
+ !button.active && neutralIconClasses.default,
3395
+ !disabled && "hover:border-(--ui-border) hover:bg-white",
3396
+ props.classNames?.toolbarButton
3397
+ ),
3398
+ children: /* @__PURE__ */ jsx(Icon, { size: 15, strokeWidth: 1.8 })
3399
+ },
3400
+ button.key
3401
+ );
3402
+ }) }),
3403
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
3404
+ showPlaceholder && /* @__PURE__ */ jsx(
3405
+ "span",
3406
+ {
3407
+ className: twMerge(
3408
+ "pointer-events-none absolute left-3 top-2.5 z-1 text-sm",
3409
+ neutralTextClasses.soft,
3410
+ props.classNames?.placeholder
3411
+ ),
3412
+ children: placeholder
3413
+ }
3414
+ ),
3415
+ /* @__PURE__ */ jsx(
3416
+ "div",
3417
+ {
3418
+ ref: editorRef,
3419
+ contentEditable: !disabled,
3420
+ suppressContentEditableWarning: true,
3421
+ onInput: emitChange,
3422
+ onFocus: () => setFocused(true),
3423
+ onBlur: () => {
3424
+ setFocused(false);
3425
+ emitChange();
3426
+ },
3427
+ className: twMerge(
3428
+ inputBaseClass,
3429
+ "relative z-2 min-h-0 border-0! rounded-none! bg-transparent! px-3 py-2.5 focus:border-transparent!",
3430
+ "overflow-y-auto [&_a]:text-(--ui-primary) [&_a]:underline [&_blockquote]:border-l-2 [&_blockquote]:border-(--ui-border) [&_blockquote]:pl-3 [&_ol]:list-decimal [&_ol]:pl-5 [&_p]:min-h-[1.5em] [&_ul]:list-disc [&_ul]:pl-5",
3431
+ neutralTextClasses.strong,
3432
+ !disabled && "cursor-text",
3433
+ props.classNames?.editor
3434
+ ),
3435
+ style: { minHeight }
3436
+ }
3437
+ )
3438
+ ] })
3439
+ ]
3440
+ }
3441
+ ),
3442
+ errorDisplay && /* @__PURE__ */ jsx(InputError, { error: String(errorDisplay), className: props.classNames?.error })
3443
+ ] });
3444
+ };
3445
+ var UploadContext = createContext(null);
3446
+ var UploadProvider = ({ config, children }) => /* @__PURE__ */ jsx(UploadContext.Provider, { value: config, children });
3447
+ function useUploadConfig() {
3448
+ return useContext(UploadContext);
3449
+ }
3450
+ var defaultPlaceholder = "P\u0159et\xE1hn\u011Bte soubory sem nebo je vyberte z po\u010D\xEDta\u010De.";
3451
+ var defaultBrowseText = "Vybrat soubor";
3452
+ var defaultHelperMultiple = "M\u016F\u017Eete nahr\xE1t v\xEDce soubor\u016F.";
3453
+ function createId() {
3454
+ if (typeof crypto !== "undefined" && "randomUUID" in crypto) {
3455
+ return crypto.randomUUID();
3456
+ }
3457
+ return `upload-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
3458
+ }
3459
+ function formatBytes(size) {
3460
+ if (!size || Number.isNaN(size)) {
3461
+ return null;
3462
+ }
3463
+ if (size < 1024) return `${size} B`;
3464
+ if (size < 1024 * 1024) return `${(size / 1024).toFixed(1)} KB`;
3465
+ return `${(size / (1024 * 1024)).toFixed(1)} MB`;
3466
+ }
3467
+ function normalizeUploadedItem(file, item) {
3468
+ const next = { ...item };
3469
+ return {
3470
+ ...next,
3471
+ id: next.id || createId(),
3472
+ name: next.name || file.name,
3473
+ size: next.size ?? file.size,
3474
+ mimeType: next.mimeType ?? file.type,
3475
+ status: next.status ?? "uploaded"
3476
+ };
3477
+ }
3478
+ var UploadInput = ({
3479
+ defaultValue = [],
3480
+ multiple = false,
3481
+ autoUpload = true,
3482
+ variant = "dropzone",
3483
+ placeholder = defaultPlaceholder,
3484
+ browseText = defaultBrowseText,
3485
+ ...props
3486
+ }) => {
3487
+ const providerConfig = useUploadConfig();
3488
+ const inputRef = useRef(null);
3489
+ const itemsRef = useRef(props.value ?? defaultValue);
3490
+ const [uncontrolledItems, setUncontrolledItems] = useState(defaultValue);
3491
+ const [dragActive, setDragActive] = useState(false);
3492
+ const [removingIds, setRemovingIds] = useState([]);
3493
+ const [downloadingIds, setDownloadingIds] = useState([]);
3494
+ const resolveError = useErrorResolver();
3495
+ const items = props.value ?? uncontrolledItems;
3496
+ const uploadFile = props.onUpload ?? providerConfig?.uploadFile;
3497
+ const onRemoveClick = props.onRemoveClick ?? providerConfig?.onRemoveClick;
3498
+ const removeFile = props.onRemove ?? providerConfig?.removeFile;
3499
+ const downloadFile = props.onDownload ?? providerConfig?.downloadFile;
3500
+ useEffect(() => {
3501
+ itemsRef.current = items;
3502
+ }, [items]);
3503
+ const resolvedErrors = props.errorName ? resolveError(props.errorName) : [];
3504
+ const errorDisplay = props.error ?? props.customError ?? (resolvedErrors.length > 0 ? resolvedErrors.join(", ") : void 0);
3505
+ const hasError = !!errorDisplay;
3506
+ const setItems = (next) => {
3507
+ const resolved = typeof next === "function" ? next(itemsRef.current) : next;
3508
+ itemsRef.current = resolved;
3509
+ if (props.value === void 0) {
3510
+ setUncontrolledItems(resolved);
3511
+ }
3512
+ props.onChange?.(resolved);
3513
+ };
3514
+ const remainingSlots = props.maxFiles ? Math.max(props.maxFiles - items.length, 0) : void 0;
3515
+ const addFiles = async (fileList) => {
3516
+ if (props.disabled) {
3517
+ return;
3518
+ }
3519
+ const incoming = Array.from(fileList);
3520
+ const limited = remainingSlots !== void 0 ? incoming.slice(0, remainingSlots) : incoming;
3521
+ if (limited.length === 0) {
3522
+ return;
3523
+ }
3524
+ if (!autoUpload || !uploadFile) {
3525
+ const localItems = limited.map((file) => ({
3526
+ id: createId(),
3527
+ name: file.name,
3528
+ size: file.size,
3529
+ mimeType: file.type,
3530
+ file,
3531
+ status: "idle"
3532
+ }));
3533
+ setItems((current) => [...current, ...localItems]);
3534
+ return;
3535
+ }
3536
+ const optimisticItems = limited.map((file) => ({
3537
+ id: createId(),
3538
+ name: file.name,
3539
+ size: file.size,
3540
+ mimeType: file.type,
3541
+ file,
3542
+ status: "uploading"
3543
+ }));
3544
+ setItems((current) => [...current, ...optimisticItems]);
3545
+ await Promise.all(optimisticItems.map(async (optimisticItem) => {
3546
+ try {
3547
+ const uploaded = await uploadFile(optimisticItem.file);
3548
+ setItems((current) => current.map(
3549
+ (item) => item.id === optimisticItem.id ? normalizeUploadedItem(optimisticItem.file, uploaded) : item
3550
+ ));
3551
+ } catch (error) {
3552
+ const message = error instanceof Error ? error.message : "Nahr\xE1n\xED se nepoda\u0159ilo.";
3553
+ setItems((current) => current.map(
3554
+ (item) => item.id === optimisticItem.id ? { ...item, status: "error", error: message } : item
3555
+ ));
3556
+ }
3557
+ }));
3558
+ };
3559
+ const handleRemove = async (item) => {
3560
+ if (props.disabled || removingIds.includes(item.id)) {
3561
+ return;
3562
+ }
3563
+ setRemovingIds((current) => [...current, item.id]);
3564
+ try {
3565
+ await onRemoveClick?.(item);
3566
+ await removeFile?.(item);
3567
+ setItems((current) => current.filter((existing) => existing.id !== item.id));
3568
+ } catch (error) {
3569
+ const message = error instanceof Error ? error.message : "Soubor se nepoda\u0159ilo odebrat.";
3570
+ setItems((current) => current.map(
3571
+ (existing) => existing.id === item.id ? { ...existing, error: message, status: "error" } : existing
3572
+ ));
3573
+ } finally {
3574
+ setRemovingIds((current) => current.filter((id) => id !== item.id));
3575
+ }
3576
+ };
3577
+ const handleDownload = async (item) => {
3578
+ if (!downloadFile || props.disabled || downloadingIds.includes(item.id)) {
3579
+ return;
3580
+ }
3581
+ setDownloadingIds((current) => [...current, item.id]);
3582
+ try {
3583
+ await downloadFile(item);
3584
+ } catch (error) {
3585
+ const message = error instanceof Error ? error.message : "Soubor se nepoda\u0159ilo st\xE1hnout.";
3586
+ setItems((current) => current.map(
3587
+ (existing) => existing.id === item.id ? { ...existing, error: message, status: existing.status === "uploaded" ? "uploaded" : "error" } : existing
3588
+ ));
3589
+ } finally {
3590
+ setDownloadingIds((current) => current.filter((id) => id !== item.id));
3591
+ }
3592
+ };
3593
+ const effectiveTexts = {
3594
+ placeholder: props.texts?.placeholder ?? placeholder,
3595
+ browseButton: props.texts?.browseButton ?? browseText,
3596
+ helperSingle: props.texts?.helperSingle ?? "",
3597
+ helperMultiple: props.texts?.helperMultiple ?? defaultHelperMultiple
3598
+ };
3599
+ const helperText = useMemo(() => {
3600
+ if (props.maxFiles) {
3601
+ return multiple ? `Maxim\xE1ln\u011B ${props.maxFiles} soubor\u016F.` : "Lze vybrat pouze jeden soubor.";
3602
+ }
3603
+ return multiple ? effectiveTexts.helperMultiple : effectiveTexts.helperSingle;
3604
+ }, [effectiveTexts.helperMultiple, effectiveTexts.helperSingle, multiple, props.maxFiles]);
3605
+ const openPicker = () => inputRef.current?.click();
3606
+ const triggerApi = {
3607
+ open: openPicker,
3608
+ disabled: !!props.disabled || remainingSlots === 0,
3609
+ dragActive,
3610
+ itemCount: items.length,
3611
+ remainingSlots
3612
+ };
3613
+ const defaultTrigger = variant === "button" ? /* @__PURE__ */ jsx(
3614
+ Button,
3615
+ {
3616
+ variant: "default",
3617
+ size: "small",
3618
+ text: effectiveTexts.browseButton,
3619
+ disabled: triggerApi.disabled,
3620
+ onClick: openPicker
3621
+ }
3622
+ ) : /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center gap-2 py-1.5 text-center", children: [
3623
+ /* @__PURE__ */ jsx("div", { className: twMerge("flex h-12 w-12 items-center justify-center rounded-full border border-(--ui-border) bg-white", dragActive && "border-(--ui-border-focus)"), children: /* @__PURE__ */ jsx(IconUpload, { size: 22, strokeWidth: 1.7, className: neutralIconClasses.default }) }),
3624
+ /* @__PURE__ */ jsxs("div", { className: "space-y-0.5", children: [
3625
+ /* @__PURE__ */ jsx("div", { className: twMerge("text-sm font-semibold", neutralTextClasses.strong), children: effectiveTexts.placeholder }),
3626
+ helperText && /* @__PURE__ */ jsx("div", { className: twMerge("text-xs", neutralTextClasses.muted), children: helperText })
3627
+ ] }),
3628
+ /* @__PURE__ */ jsx(
3629
+ Button,
3630
+ {
3631
+ variant: "default",
3632
+ size: "small",
3633
+ text: effectiveTexts.browseButton,
3634
+ disabled: triggerApi.disabled,
3635
+ onClick: openPicker
3636
+ }
3637
+ )
3638
+ ] });
3639
+ const renderedTrigger = props.renderTrigger?.(triggerApi) ?? defaultTrigger;
3640
+ return /* @__PURE__ */ jsxs(InputField, { noMargin: props.noMargin, className: props.className, children: [
3641
+ props.label && /* @__PURE__ */ jsx(
3642
+ InputLabel,
3643
+ {
3644
+ label: props.label,
3645
+ required: props.required,
3646
+ className: props.classNames?.label,
3647
+ requiredMarkClassName: props.classNames?.requiredMark
3648
+ }
3649
+ ),
3650
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
3651
+ /* @__PURE__ */ jsxs(
3652
+ "div",
3653
+ {
3654
+ onDragOver: (e) => {
3655
+ e.preventDefault();
3656
+ if (!props.disabled) {
3657
+ setDragActive(true);
3658
+ }
3659
+ },
3660
+ onDragLeave: (e) => {
3661
+ e.preventDefault();
3662
+ setDragActive(false);
3663
+ },
3664
+ onDrop: async (e) => {
3665
+ e.preventDefault();
3666
+ setDragActive(false);
3667
+ await addFiles(e.dataTransfer.files);
3668
+ },
3669
+ className: twMerge(
3670
+ variant === "button" ? "border-0 bg-transparent p-0" : "rounded-(--ui-radius-md) border border-dashed bg-white px-4 py-3.5 transition-colors",
3671
+ dragActive ? "border-(--ui-border-focus) bg-(--ui-surface-subtle)" : variant === "button" ? "" : triggerBorderClasses.default,
3672
+ props.disabled && triggerBorderClasses.disabled,
3673
+ hasError && triggerBorderClasses.error,
3674
+ props.classNames?.dropzone
3675
+ ),
3676
+ children: [
3677
+ /* @__PURE__ */ jsx(
3678
+ "input",
3679
+ {
3680
+ ref: inputRef,
3681
+ type: "file",
3682
+ className: "hidden",
3683
+ disabled: props.disabled,
3684
+ multiple,
3685
+ accept: props.accept,
3686
+ onChange: async (e) => {
3687
+ if (!e.target.files) {
3688
+ return;
3689
+ }
3690
+ await addFiles(e.target.files);
3691
+ e.target.value = "";
3692
+ }
3693
+ }
3694
+ ),
3695
+ /* @__PURE__ */ jsx("div", { className: props.classNames?.trigger, children: renderedTrigger })
3696
+ ]
3697
+ }
3698
+ ),
3699
+ items.length > 0 && /* @__PURE__ */ jsx("div", { className: twMerge("rounded-(--ui-radius-md) border border-(--ui-border) bg-white", props.classNames?.fileList), children: /* @__PURE__ */ jsx("div", { className: "divide-y divide-(--ui-border)", children: items.map((item) => {
3700
+ const removing = removingIds.includes(item.id);
3701
+ const downloading = downloadingIds.includes(item.id);
3702
+ const canDownload = !!downloadFile && item.status !== "uploading" && !removing;
3703
+ const meta = formatBytes(item.size);
3704
+ return /* @__PURE__ */ jsxs("div", { className: twMerge("flex items-start gap-3 px-3 py-2.5", props.classNames?.fileItem), children: [
3705
+ /* @__PURE__ */ jsx("div", { className: twMerge("mt-0.5 rounded-full p-2", neutralSurfaceClasses.subtle, props.classNames?.fileIcon), children: item.status === "uploading" ? /* @__PURE__ */ jsx(Spinner, { size: 14, color: "current" }) : item.status === "error" ? /* @__PURE__ */ jsx(IconAlertCircle, { size: 16, strokeWidth: 1.7, className: "text-(--ui-danger)" }) : /* @__PURE__ */ jsx(IconPaperclip, { size: 16, strokeWidth: 1.7, className: neutralIconClasses.default }) }),
3706
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
3707
+ canDownload ? /* @__PURE__ */ jsx(
3708
+ "button",
3709
+ {
3710
+ type: "button",
3711
+ disabled: downloading,
3712
+ onClick: () => handleDownload(item),
3713
+ className: twMerge(
3714
+ "max-w-full truncate text-left text-sm font-medium underline-offset-2 transition-colors cursor-pointer disabled:cursor-not-allowed disabled:opacity-50",
3715
+ neutralTextClasses.strong,
3716
+ "hover:text-(--ui-primary) hover:underline",
3717
+ props.classNames?.fileName
3718
+ ),
3719
+ title: item.name,
3720
+ children: item.name
3721
+ }
3722
+ ) : /* @__PURE__ */ jsx("div", { className: twMerge("truncate text-sm font-medium", neutralTextClasses.strong, props.classNames?.fileName), children: item.name }),
3723
+ /* @__PURE__ */ jsx("div", { className: twMerge("mt-0.5 text-xs", item.status === "error" ? "text-(--ui-danger)" : neutralTextClasses.muted, props.classNames?.fileMeta), children: item.error ?? (item.status === "uploading" ? "Nahr\xE1v\xE1 se..." : meta || "P\u0159ipraveno") })
3724
+ ] }),
3725
+ /* @__PURE__ */ jsxs("div", { className: twMerge("flex items-center gap-1", props.classNames?.fileActions), children: [
3726
+ canDownload && /* @__PURE__ */ jsx(
3727
+ "button",
3728
+ {
3729
+ type: "button",
3730
+ disabled: downloading,
3731
+ onClick: () => handleDownload(item),
3732
+ className: twMerge(
3733
+ "inline-flex h-8 w-8 items-center justify-center rounded-md transition-colors cursor-pointer disabled:cursor-not-allowed disabled:opacity-40",
3734
+ neutralIconClasses.default,
3735
+ "hover:bg-(--ui-surface-subtle) hover:text-(--ui-text)",
3736
+ props.classNames?.downloadButton
3737
+ ),
3738
+ title: "St\xE1hnout",
3739
+ children: downloading ? /* @__PURE__ */ jsx(Spinner, { size: 14, color: "current" }) : /* @__PURE__ */ jsx(IconDownload, { size: 15, strokeWidth: 1.7 })
3740
+ }
3741
+ ),
3742
+ /* @__PURE__ */ jsx(
3743
+ "button",
3744
+ {
3745
+ type: "button",
3746
+ disabled: props.disabled || removing,
3747
+ onClick: () => handleRemove(item),
3748
+ className: twMerge(
3749
+ "inline-flex h-8 w-8 items-center justify-center rounded-md transition-colors cursor-pointer disabled:cursor-not-allowed disabled:opacity-40",
3750
+ "text-(--ui-danger) hover:bg-red-50 hover:text-red-700",
3751
+ props.classNames?.removeButton
3752
+ ),
3753
+ title: "Odebrat",
3754
+ children: removing ? /* @__PURE__ */ jsx(Spinner, { size: 14, color: "current" }) : /* @__PURE__ */ jsx(IconTrash, { size: 15, strokeWidth: 1.7 })
3755
+ }
3756
+ )
3757
+ ] })
3758
+ ] }, item.id);
3759
+ }) }) })
3760
+ ] }),
3761
+ errorDisplay && /* @__PURE__ */ jsx(InputError, { error: String(errorDisplay), className: props.classNames?.error })
3762
+ ] });
3763
+ };
3764
+ var tagSizeClass = {
3765
+ small: "h-5 px-1.5 text-[11px] rounded",
3766
+ middle: "h-5 px-1.5 text-xs rounded",
3767
+ large: "h-6 px-2 text-xs rounded"
3768
+ };
3769
+ var MultiSelectInput = ({
3770
+ options,
3771
+ labelKey = "label",
3772
+ valueKey = "value",
3773
+ size = "middle",
3774
+ showSearch = true,
3775
+ maxTagsVisible = 3,
3776
+ ...props
3777
+ }) => {
3778
+ const [isOpen, setIsOpen] = useState(false);
3779
+ const triggerRef = useRef(null);
3780
+ const allOptions = (options ?? []).map((opt) => ({
3781
+ label: String(opt[labelKey]),
3782
+ value: opt[valueKey]
3783
+ }));
3784
+ const selectedValues = props.value ?? [];
3785
+ const hasValue = selectedValues.length > 0;
3786
+ const resolveError = useErrorResolver();
3787
+ const resolvedErrors = props.errorName ? resolveError(props.errorName) : [];
3788
+ const errorDisplay = props.error ?? props.customError ?? (resolvedErrors.length > 0 ? resolvedErrors.join(", ") : void 0);
3789
+ const hasError = !!errorDisplay;
3790
+ const selectedOptions = selectedValues.map((v) => allOptions.find((o) => o.value === v)).filter((o) => o !== void 0);
3791
+ const visibleTags = selectedOptions.slice(0, maxTagsVisible);
3792
+ const overflowCount = selectedOptions.length - maxTagsVisible;
3793
+ const handleToggle = (value) => {
3794
+ const v = value;
3795
+ const next = selectedValues.includes(v) ? selectedValues.filter((x) => x !== v) : [...selectedValues, v];
3796
+ props.onChange?.(next);
3797
+ };
3798
+ const handleRemoveTag = (e, value) => {
3799
+ e.stopPropagation();
3800
+ props.onChange?.(selectedValues.filter((v) => v !== value));
3801
+ };
3802
+ const handleTriggerClick = () => {
3803
+ if (!props.disabled) {
3804
+ setIsOpen((v) => !v);
3805
+ }
3806
+ };
3807
+ const handleSelectAll = () => {
3808
+ props.onChange?.(allOptions.map((o) => o.value));
3809
+ };
3810
+ const handleClearAllDropdown = () => {
3811
+ props.onChange?.([]);
3812
+ };
3813
+ return /* @__PURE__ */ jsxs(InputField, { noMargin: props.noMargin, className: props.className, children: [
3814
+ props.label && /* @__PURE__ */ jsx(
3815
+ InputLabel,
3816
+ {
3817
+ label: props.label,
3818
+ required: props.required,
3819
+ className: props.classNames?.label,
3820
+ requiredMarkClassName: props.classNames?.requiredMark
3821
+ }
3822
+ ),
3823
+ /* @__PURE__ */ jsxs(
3824
+ "div",
3825
+ {
3826
+ ref: triggerRef,
3827
+ onClick: handleTriggerClick,
3828
+ className: twMerge(
3829
+ "flex items-center gap-1 border bg-white cursor-pointer transition-colors select-none",
3830
+ multiTriggerSizeClasses[size],
3831
+ isOpen ? triggerBorderClasses.open : triggerBorderClasses.default,
3832
+ props.disabled && triggerBorderClasses.disabled,
3833
+ props.classNames?.trigger
3834
+ ),
3835
+ style: hasError ? { borderColor: "var(--ui-danger)" } : void 0,
3836
+ children: [
3837
+ /* @__PURE__ */ jsx("div", { className: twMerge("flex min-w-0 flex-1 flex-wrap items-center gap-1 py-0.5", props.classNames?.tags), children: hasValue ? /* @__PURE__ */ jsxs(Fragment, { children: [
3838
+ visibleTags.map((opt) => /* @__PURE__ */ jsxs(
3839
+ "span",
3840
+ {
3841
+ className: twMerge(
3842
+ "inline-flex shrink-0 items-center gap-0.5 border border-(--ui-border) bg-white text-(--ui-text)",
3843
+ tagSizeClass[size],
3844
+ props.classNames?.tag
3845
+ ),
3846
+ children: [
3847
+ /* @__PURE__ */ jsx("span", { className: "max-w-30 truncate", children: opt.label }),
3848
+ !props.disabled && /* @__PURE__ */ jsx(
3849
+ "button",
3850
+ {
3851
+ type: "button",
3852
+ onMouseDown: (e) => e.preventDefault(),
3853
+ onClick: (e) => handleRemoveTag(e, opt.value),
3854
+ className: twMerge("shrink-0 cursor-pointer ml-0.5", neutralIconClasses.default, neutralIconClasses.hover, props.classNames?.tagRemove),
3855
+ children: /* @__PURE__ */ jsx(IconX, { size: 10, strokeWidth: 2.5 })
3856
+ }
3857
+ )
3858
+ ]
3859
+ },
3860
+ String(opt.value)
3861
+ )),
3862
+ overflowCount > 0 && /* @__PURE__ */ jsxs(
3863
+ "span",
3864
+ {
3865
+ className: twMerge(
3866
+ "inline-flex shrink-0 items-center border border-(--ui-border) bg-white text-(--ui-text-muted)",
3867
+ tagSizeClass[size],
3868
+ props.classNames?.overflowTag
3869
+ ),
3870
+ children: [
3871
+ "+",
3872
+ overflowCount
3873
+ ]
3874
+ }
3875
+ )
3876
+ ] }) : /* @__PURE__ */ jsx("span", { className: twMerge("flex min-h-5 items-center leading-none", neutralTextClasses.soft, props.classNames?.placeholder), children: props.placeholder ?? "" }) }),
3877
+ /* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center gap-1 self-center", children: /* @__PURE__ */ jsx(
3878
+ IconChevronDown,
3879
+ {
3880
+ size: 13,
3881
+ strokeWidth: 1.5,
3882
+ className: twMerge(neutralIconClasses.default, "shrink-0 self-center transition-transform", isOpen && "rotate-180", props.classNames?.icon)
3883
+ }
3884
+ ) })
3885
+ ]
3886
+ }
3887
+ ),
3888
+ /* @__PURE__ */ jsx(
3889
+ SelectDropdown,
3890
+ {
3891
+ anchorRef: triggerRef,
3892
+ isOpen,
3893
+ options: allOptions,
3894
+ selectedValues: new Set(selectedValues),
3895
+ onSelect: handleToggle,
3896
+ onClose: () => setIsOpen(false),
3897
+ loading: props.loading,
3898
+ showSearch,
3899
+ multiMode: true,
3900
+ onSelectAll: handleSelectAll,
3901
+ onClearAll: handleClearAllDropdown
3902
+ }
3903
+ ),
3904
+ errorDisplay && /* @__PURE__ */ jsx(InputError, { error: String(errorDisplay), className: props.classNames?.error })
3905
+ ] });
3906
+ };
3907
+
3908
+ // src/notification/useNotification.ts
3909
+ var useNotification = () => {
3910
+ const { add } = useNotificationContext();
3911
+ return {
3912
+ success: (message, options) => add("success", message, options),
3913
+ error: (message, options) => add("error", message, options),
3914
+ warning: (message, options) => add("warning", message, options),
3915
+ info: (message, options) => add("info", message, options)
3916
+ };
3917
+ };
3918
+ var Pretty = ({ data, className }) => /* @__PURE__ */ jsx(
3919
+ "pre",
3920
+ {
3921
+ className: twMerge(
3922
+ "overflow-auto rounded-(--ui-radius-lg) border border-(--ui-border) bg-(--ui-surface-subtle) p-3 text-xs text-(--ui-text) whitespace-pre-wrap wrap-anywhere",
3923
+ className
3924
+ ),
3925
+ children: JSON.stringify(data, null, 2)
3926
+ }
3927
+ );
3928
+ var GAP2 = 10;
3929
+ var VIEWPORT_MARGIN2 = 8;
3930
+ function calcPosition3(trigger, tooltip, placement) {
3931
+ let resolvedPlacement = placement;
3932
+ if (placement === "top" && trigger.top < tooltip.height + GAP2 + VIEWPORT_MARGIN2) {
3933
+ resolvedPlacement = "bottom";
3934
+ } else if (placement === "bottom" && window.innerHeight - trigger.bottom < tooltip.height + GAP2 + VIEWPORT_MARGIN2) {
3935
+ resolvedPlacement = "top";
3936
+ } else if (placement === "left" && trigger.left < tooltip.width + GAP2 + VIEWPORT_MARGIN2) {
3937
+ resolvedPlacement = "right";
3938
+ } else if (placement === "right" && window.innerWidth - trigger.right < tooltip.width + GAP2 + VIEWPORT_MARGIN2) {
3939
+ resolvedPlacement = "left";
3940
+ }
3941
+ let top = 0;
3942
+ let left = 0;
3943
+ switch (resolvedPlacement) {
3944
+ case "bottom":
3945
+ top = trigger.bottom + GAP2;
3946
+ left = trigger.left + (trigger.width - tooltip.width) / 2;
3947
+ break;
3948
+ case "left":
3949
+ top = trigger.top + (trigger.height - tooltip.height) / 2;
3950
+ left = trigger.left - tooltip.width - GAP2;
3951
+ break;
3952
+ case "right":
3953
+ top = trigger.top + (trigger.height - tooltip.height) / 2;
3954
+ left = trigger.right + GAP2;
3955
+ break;
3956
+ case "top":
3957
+ default:
3958
+ top = trigger.top - tooltip.height - GAP2;
3959
+ left = trigger.left + (trigger.width - tooltip.width) / 2;
3960
+ break;
3961
+ }
3962
+ return {
3963
+ top: Math.min(
3964
+ Math.max(top, VIEWPORT_MARGIN2),
3965
+ Math.max(window.innerHeight - tooltip.height - VIEWPORT_MARGIN2, VIEWPORT_MARGIN2)
3966
+ ),
3967
+ left: Math.min(
3968
+ Math.max(left, VIEWPORT_MARGIN2),
3969
+ Math.max(window.innerWidth - tooltip.width - VIEWPORT_MARGIN2, VIEWPORT_MARGIN2)
3970
+ ),
3971
+ placement: resolvedPlacement
3972
+ };
3973
+ }
3974
+ var arrowClasses = {
3975
+ top: "left-1/2 -bottom-1.5 -translate-x-1/2 border-l-transparent border-r-transparent border-b-0 border-t-white",
3976
+ bottom: "left-1/2 -top-1.5 -translate-x-1/2 border-l-transparent border-r-transparent border-t-0 border-b-white",
3977
+ left: "top-1/2 -right-1.5 -translate-y-1/2 border-t-transparent border-b-transparent border-r-0 border-l-white",
3978
+ right: "top-1/2 -left-1.5 -translate-y-1/2 border-t-transparent border-b-transparent border-l-0 border-r-white"
3979
+ };
3980
+ var Tooltip = ({
3981
+ title,
3982
+ children,
3983
+ placement = "top",
3984
+ open: controlledOpen,
3985
+ onOpenChange,
3986
+ maxWidth = 320,
3987
+ className,
3988
+ contentClassName,
3989
+ arrow = true,
3990
+ classNames
3991
+ }) => {
3992
+ const tooltipId = useId();
3993
+ const triggerRef = useRef(null);
3994
+ const tooltipRef = useRef(null);
3995
+ const [uncontrolledOpen, setUncontrolledOpen] = useState(false);
3996
+ const [position, setPosition] = useState(null);
3997
+ const open = controlledOpen ?? uncontrolledOpen;
3998
+ const setOpen = (next) => {
3999
+ if (controlledOpen === void 0) {
4000
+ setUncontrolledOpen(next);
4001
+ }
4002
+ onOpenChange?.(next);
4003
+ };
4004
+ useEffect(() => {
4005
+ if (!open || !triggerRef.current || !tooltipRef.current) {
4006
+ return;
4007
+ }
4008
+ const updatePosition = () => {
4009
+ if (!triggerRef.current || !tooltipRef.current) {
4010
+ return;
4011
+ }
4012
+ const triggerRect = triggerRef.current.getBoundingClientRect();
4013
+ const tooltipRect = tooltipRef.current.getBoundingClientRect();
4014
+ const next = calcPosition3(triggerRect, tooltipRect, placement);
4015
+ setPosition(next);
4016
+ };
4017
+ updatePosition();
4018
+ window.addEventListener("resize", updatePosition);
4019
+ window.addEventListener("scroll", updatePosition, true);
4020
+ return () => {
4021
+ window.removeEventListener("resize", updatePosition);
4022
+ window.removeEventListener("scroll", updatePosition, true);
4023
+ };
4024
+ }, [open, placement, title]);
4025
+ if (!title) {
4026
+ return /* @__PURE__ */ jsx(Fragment, { children });
4027
+ }
4028
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
4029
+ /* @__PURE__ */ jsx(
4030
+ "span",
4031
+ {
4032
+ ref: triggerRef,
4033
+ "aria-describedby": open ? tooltipId : void 0,
4034
+ className: twMerge("inline-flex", className, classNames?.trigger),
4035
+ onMouseEnter: () => setOpen(true),
4036
+ onMouseLeave: () => setOpen(false),
4037
+ onFocus: () => setOpen(true),
4038
+ onBlur: () => setOpen(false),
4039
+ children
4040
+ }
4041
+ ),
4042
+ open && createPortal(
4043
+ /* @__PURE__ */ jsxs(
4044
+ "div",
4045
+ {
4046
+ id: tooltipId,
4047
+ ref: tooltipRef,
4048
+ "data-ui-floating-root": "",
4049
+ role: "tooltip",
4050
+ className: twMerge(
4051
+ "fixed z-1100 rounded-(--ui-radius-md) border border-(--ui-border) bg-white text-gray-700 shadow-md",
4052
+ popupLayoutClasses.tooltip,
4053
+ contentClassName,
4054
+ classNames?.panel
4055
+ ),
4056
+ style: {
4057
+ top: position?.top ?? -9999,
4058
+ left: position?.left ?? -9999,
4059
+ maxWidth
4060
+ },
4061
+ onMouseEnter: () => setOpen(true),
4062
+ onMouseLeave: () => setOpen(false),
4063
+ children: [
4064
+ title,
4065
+ arrow && /* @__PURE__ */ jsx(
4066
+ "span",
4067
+ {
4068
+ className: twMerge(
4069
+ "absolute h-0 w-0 border-[6px]",
4070
+ arrowClasses[position?.placement ?? placement],
4071
+ classNames?.arrow
4072
+ )
4073
+ }
4074
+ )
4075
+ ]
4076
+ }
4077
+ ),
4078
+ document.body
4079
+ )
4080
+ ] });
4081
+ };
4082
+
4083
+ // src/initUI.ts
4084
+ function initUI(config) {
4085
+ const d = uiTheme;
4086
+ const c = config ?? {};
4087
+ const colors = {
4088
+ primary: { ...d.colors.primary, ...c.colors?.primary },
4089
+ danger: { ...d.colors.danger, ...c.colors?.danger },
4090
+ success: { ...d.colors.success, ...c.colors?.success },
4091
+ warning: { ...d.colors.warning, ...c.colors?.warning },
4092
+ info: { ...d.colors.info, ...c.colors?.info }
4093
+ };
4094
+ const border = { ...d.border, ...c.border };
4095
+ const radius = { ...d.radius, ...c.radius };
4096
+ const gray2 = { ...d.gray, ...c.gray };
4097
+ const size = {
4098
+ small: { ...d.size.small, ...c.size?.small },
4099
+ middle: { ...d.size.middle, ...c.size?.middle },
4100
+ large: { ...d.size.large, ...c.size?.large }
4101
+ };
4102
+ const layout = {
4103
+ panel: { ...d.layout.panel, ...c.layout?.panel },
4104
+ drawer: { ...d.layout.drawer, ...c.layout?.drawer },
4105
+ popup: { ...d.layout.popup, ...c.layout?.popup },
4106
+ empty: { ...d.layout.empty, ...c.layout?.empty },
4107
+ table: {
4108
+ small: { ...d.layout.table.small, ...c.layout?.table?.small },
4109
+ middle: { ...d.layout.table.middle, ...c.layout?.table?.middle },
4110
+ large: { ...d.layout.table.large, ...c.layout?.table?.large }
4111
+ }
4112
+ };
4113
+ if (c.icons) {
4114
+ registerIcons(c.icons);
4115
+ }
4116
+ const r = document.documentElement;
4117
+ r.style.setProperty("--ui-primary", colors.primary.bg);
4118
+ r.style.setProperty("--ui-primary-hover", colors.primary.hover);
4119
+ r.style.setProperty("--ui-primary-active", colors.primary.hover);
4120
+ r.style.setProperty("--ui-primary-text", colors.primary.text);
4121
+ r.style.setProperty("--ui-danger", colors.danger.bg);
4122
+ r.style.setProperty("--ui-danger-hover", colors.danger.hover);
4123
+ r.style.setProperty("--ui-danger-text", colors.danger.text);
4124
+ r.style.setProperty("--ui-success", colors.success.bg);
4125
+ r.style.setProperty("--ui-success-hover", colors.success.hover);
4126
+ r.style.setProperty("--ui-success-text", colors.success.text);
4127
+ r.style.setProperty("--ui-warning", colors.warning.bg);
4128
+ r.style.setProperty("--ui-warning-hover", colors.warning.hover);
4129
+ r.style.setProperty("--ui-warning-text", colors.warning.text);
4130
+ r.style.setProperty("--ui-info", colors.info.bg);
4131
+ r.style.setProperty("--ui-info-hover", colors.info.hover);
4132
+ r.style.setProperty("--ui-info-text", colors.info.text);
4133
+ r.style.setProperty("--ui-border", border.color);
4134
+ r.style.setProperty("--ui-border-input", border.input);
4135
+ r.style.setProperty("--ui-border-focus", border.focus);
4136
+ r.style.setProperty("--ui-border-width", border.width);
4137
+ r.style.setProperty("--ui-border-strong", gray2[300]);
4138
+ r.style.setProperty("--ui-surface-subtle", gray2[50]);
4139
+ r.style.setProperty("--ui-surface-muted", gray2[100]);
4140
+ r.style.setProperty("--ui-surface-selected", "#eff6ff");
4141
+ r.style.setProperty("--ui-text-strong", gray2[800]);
4142
+ r.style.setProperty("--ui-text", gray2[700]);
4143
+ r.style.setProperty("--ui-text-muted", gray2[500]);
4144
+ r.style.setProperty("--ui-text-soft", gray2[400]);
4145
+ r.style.setProperty("--ui-text-disabled", gray2[400]);
4146
+ r.style.setProperty("--ui-radius-sm", radius.sm);
4147
+ r.style.setProperty("--ui-radius-md", radius.md);
4148
+ r.style.setProperty("--ui-radius-lg", radius.lg);
4149
+ r.style.setProperty("--ui-h-sm", size.small.height);
4150
+ r.style.setProperty("--ui-px-sm", size.small.px);
4151
+ r.style.setProperty("--ui-text-sm", size.small.text);
4152
+ r.style.setProperty("--ui-h-md", size.middle.height);
4153
+ r.style.setProperty("--ui-px-md", size.middle.px);
4154
+ r.style.setProperty("--ui-text-md", size.middle.text);
4155
+ r.style.setProperty("--ui-h-lg", size.large.height);
4156
+ r.style.setProperty("--ui-px-lg", size.large.px);
4157
+ r.style.setProperty("--ui-text-lg", size.large.text);
4158
+ r.style.setProperty("--ui-panel-header-h", layout.panel.headerHeight);
4159
+ r.style.setProperty("--ui-panel-header-px", layout.panel.headerPx);
4160
+ r.style.setProperty("--ui-panel-body-p", layout.panel.bodyPadding);
4161
+ r.style.setProperty("--ui-panel-footer-px", layout.panel.footerPx);
4162
+ r.style.setProperty("--ui-panel-footer-py", layout.panel.footerPy);
4163
+ r.style.setProperty("--ui-drawer-header-h", layout.drawer.headerHeight);
4164
+ r.style.setProperty("--ui-drawer-header-px", layout.drawer.headerPx);
4165
+ r.style.setProperty("--ui-drawer-body-p", layout.drawer.bodyPadding);
4166
+ r.style.setProperty("--ui-drawer-footer-px", layout.drawer.footerPx);
4167
+ r.style.setProperty("--ui-drawer-footer-py", layout.drawer.footerPy);
4168
+ r.style.setProperty("--ui-popup-panel-p", layout.popup.panelPadding);
4169
+ r.style.setProperty("--ui-popup-row-px", layout.popup.compactPaddingX);
4170
+ r.style.setProperty("--ui-popup-row-py", layout.popup.compactPaddingY);
4171
+ r.style.setProperty("--ui-popup-gap", layout.popup.compactGap);
4172
+ r.style.setProperty("--ui-popup-text", layout.popup.text);
4173
+ r.style.setProperty("--ui-table-cell-x-sm", layout.table.small.cellPx);
4174
+ r.style.setProperty("--ui-table-th-y-sm", layout.table.small.thPy);
4175
+ r.style.setProperty("--ui-table-td-y-sm", layout.table.small.tdPy);
4176
+ r.style.setProperty("--ui-table-empty-y-sm", layout.table.small.emptyPy);
4177
+ r.style.setProperty("--ui-table-cell-x-md", layout.table.middle.cellPx);
4178
+ r.style.setProperty("--ui-table-th-y-md", layout.table.middle.thPy);
4179
+ r.style.setProperty("--ui-table-td-y-md", layout.table.middle.tdPy);
4180
+ r.style.setProperty("--ui-table-empty-y-md", layout.table.middle.emptyPy);
4181
+ r.style.setProperty("--ui-table-cell-x-lg", layout.table.large.cellPx);
4182
+ r.style.setProperty("--ui-table-th-y-lg", layout.table.large.thPy);
4183
+ r.style.setProperty("--ui-table-td-y-lg", layout.table.large.tdPy);
4184
+ r.style.setProperty("--ui-table-empty-y-lg", layout.table.large.emptyPy);
4185
+ r.style.setProperty("--ui-empty-py", layout.empty.defaultPy);
4186
+ r.style.setProperty("--ui-empty-compact-py", layout.empty.compactPy);
4187
+ r.style.setProperty("--ui-empty-icon-box", layout.empty.defaultIconBox);
4188
+ r.style.setProperty("--ui-empty-compact-icon-box", layout.empty.compactIconBox);
4189
+ r.style.setProperty("--ui-empty-icon", layout.empty.defaultIcon);
4190
+ r.style.setProperty("--ui-empty-compact-icon", layout.empty.compactIcon);
4191
+ r.style.setProperty("--ui-empty-compact-text", layout.empty.compactText);
4192
+ r.style.setProperty("--ui-empty-compact-description", layout.empty.compactDescription);
4193
+ }
4194
+
4195
+ export { ActiveTag, Alert, Avatar, Badge, Button, CheckboxInput, ColorInput, DateInput, Drawer, DrawerContent, DrawerFooter, DrawerTitle, Dropdown, Empty, ErrorProvider, FilterCheckboxInput, FilterDateInput, FilterDateRangePopover, FilterNumberInput, FilterSelectGroupPopover, FilterSelectInput, FilterTextInput, FlagTag, Grid, GridFilters, HtmlInput, InputError, InputField, InputLabel, MultiSelectInput, NotificationProvider, NumberInput, Panel, Popconfirm, Pretty, SelectInput, ServerError, Skeleton, SortDirection, Spinner, SwitchInput, Table, Tabs, Tag, TextInput, Tooltip, UploadInput, UploadProvider, getIcon, initUI, notification, registerIcons, uiTheme, useDrawer, useGrid, useNotification, useUploadConfig };
4196
+ //# sourceMappingURL=index.js.map
4197
+ //# sourceMappingURL=index.js.map