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