@martinsura/ui 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { cva } from 'class-variance-authority';
2
2
  import { twMerge } from 'tailwind-merge';
3
- import { IconEyeOff, IconEye, IconDownload, IconPaperclip, IconFileSpreadsheet, IconX, IconTrashX, IconPlus, IconPencil, IconDotsVertical, IconCheck, IconDroplet, IconDatabaseOff, IconChevronUp, IconChevronDown, IconSelector, IconCalendar, IconUsers, IconInfoCircle, IconAlertTriangle, IconCircleX, IconBold, IconItalic, IconUnderline, IconList, IconListNumbers, IconLink, IconUnlink, IconClearFormatting, IconUpload, IconAlertCircle, IconTrash, IconLoader2, IconChevronLeft, IconChevronRight, IconSearch } from '@tabler/icons-react';
3
+ import { IconEyeOff, IconEye, IconDownload, IconPaperclip, IconFileSpreadsheet, IconX, IconTrashX, IconPlus, IconPencil, IconDotsVertical, IconCheck, IconDroplet, IconSearch, IconDatabaseOff, IconChevronUp, IconChevronDown, IconSelector, IconCalendar, IconUsers, IconInfoCircle, IconAlertTriangle, IconCircleX, IconBold, IconItalic, IconUnderline, IconList, IconListNumbers, IconLink, IconUnlink, IconClearFormatting, IconUpload, IconAlertCircle, IconTrash, IconMinus, IconArrowDownRight, IconArrowUpRight, IconLoader2, IconChevronLeft, IconChevronRight } from '@tabler/icons-react';
4
4
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
5
5
  import { createContext, forwardRef, useState, useContext, useRef, useLayoutEffect, useEffect, useMemo, useCallback, useId } from 'react';
6
6
  import { createPortal } from 'react-dom';
@@ -321,7 +321,9 @@ var buttonVariants = cva(
321
321
  danger: "bg-(--ui-danger) text-(--ui-danger-text) hover:bg-(--ui-danger-hover) active:bg-(--ui-danger-hover)",
322
322
  success: "bg-(--ui-success) text-(--ui-success-text) hover:bg-(--ui-success-hover) active:bg-(--ui-success-hover)",
323
323
  default: "bg-white text-(--ui-text) border border-(--ui-border-strong) hover:bg-(--ui-surface-subtle) active:bg-(--ui-surface-muted)",
324
+ subtle: "bg-(--ui-surface-subtle) text-(--ui-text) border border-transparent hover:bg-(--ui-surface-muted) active:bg-(--ui-surface-muted)",
324
325
  ghost: "text-(--ui-text) hover:bg-(--ui-surface-muted) active:bg-(--ui-surface-subtle)",
326
+ text: "text-(--ui-text) hover:text-(--ui-text-strong) active:text-(--ui-text-strong) px-0! bg-transparent",
325
327
  link: "text-(--ui-primary) hover:underline p-0! h-auto!"
326
328
  },
327
329
  size: {
@@ -357,12 +359,14 @@ var Button = ({
357
359
  size = "middle",
358
360
  shape = "default",
359
361
  block = false,
362
+ fullWidthOnMobile = false,
360
363
  iconPosition = "left",
361
364
  ...props
362
365
  }) => {
363
366
  const iconSize = props.iconSize ?? iconSizeMap[size];
364
367
  const IconComponent = props.icon ?? (props.iconType ? getIcon(props.iconType) : void 0);
365
368
  const iconNode = IconComponent ? /* @__PURE__ */ jsx(IconComponent, { size: iconSize, strokeWidth: 1.5, className: props.classNames?.icon }) : void 0;
369
+ const content = props.text ?? props.children;
366
370
  const handleClick = () => {
367
371
  if (props.disabled || props.loading) {
368
372
  return;
@@ -374,11 +378,16 @@ var Button = ({
374
378
  {
375
379
  type: props.htmlType ?? "button",
376
380
  disabled: props.disabled || props.loading,
377
- className: twMerge(buttonVariants({ variant, size, block, shape }), props.className),
381
+ className: twMerge(
382
+ buttonVariants({ variant, size, block, shape }),
383
+ fullWidthOnMobile && !block && "w-full sm:w-auto",
384
+ props.iconOnly && "px-0!",
385
+ props.className
386
+ ),
378
387
  onClick: props.confirm ? void 0 : handleClick,
379
388
  children: [
380
389
  props.loading ? /* @__PURE__ */ jsx(Spinner, { size: iconSize, color: "current", className: props.classNames?.spinner }) : iconPosition === "left" && iconNode,
381
- /* @__PURE__ */ jsx("span", { className: props.classNames?.content, children: props.text ?? props.children }),
390
+ !props.iconOnly && content !== void 0 && content !== null && /* @__PURE__ */ jsx("span", { className: props.classNames?.content, children: content }),
382
391
  !props.loading && iconPosition === "right" && iconNode
383
392
  ]
384
393
  }
@@ -398,6 +407,21 @@ var Button = ({
398
407
  }
399
408
  );
400
409
  };
410
+ var ConfirmButton = (props) => /* @__PURE__ */ jsx(
411
+ Button,
412
+ {
413
+ text: props.text,
414
+ onClick: props.onClick,
415
+ disabled: props.disabled,
416
+ loading: props.loading,
417
+ variant: props.variant,
418
+ size: props.size,
419
+ icon: props.icon,
420
+ className: props.className,
421
+ confirm: props.confirm,
422
+ children: props.children
423
+ }
424
+ );
401
425
  var InputField = ({ noMargin, className, children }) => /* @__PURE__ */ jsx("div", { className: twMerge("flex flex-col gap-1", !noMargin && "mb-3", className), children });
402
426
  var InputLabel = ({ label, required, className, requiredMarkClassName }) => /* @__PURE__ */ jsxs("label", { className: twMerge("text-sm text-(--ui-text)", className), children: [
403
427
  label,
@@ -473,6 +497,26 @@ var TextInput = forwardRef(({
473
497
  ] });
474
498
  });
475
499
  TextInput.displayName = "TextInput";
500
+ var Textarea = ({ rows = 4, ...props }) => {
501
+ const resolveError = useErrorResolver();
502
+ const resolvedErrors = props.errorName ? resolveError(props.errorName) : [];
503
+ const errorDisplay = props.error ?? props.customError ?? (resolvedErrors.length > 0 ? resolvedErrors.join(", ") : void 0);
504
+ return /* @__PURE__ */ jsxs(InputField, { noMargin: props.noMargin, className: props.className, children: [
505
+ props.label && /* @__PURE__ */ jsx(InputLabel, { label: props.label, required: props.required }),
506
+ /* @__PURE__ */ jsx(
507
+ "textarea",
508
+ {
509
+ rows,
510
+ value: props.value ?? "",
511
+ placeholder: props.placeholder,
512
+ disabled: props.disabled,
513
+ onChange: (e) => props.onChange?.(e.target.value),
514
+ className: twMerge(inputBaseClass, "min-h-24 py-2", errorDisplay && "border-(--ui-danger)")
515
+ }
516
+ ),
517
+ errorDisplay && /* @__PURE__ */ jsx(InputError, { error: String(errorDisplay) })
518
+ ] });
519
+ };
476
520
  var numberInputClass = inputBaseClass + " [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none";
477
521
  var NumberInput = ({
478
522
  size = "middle",
@@ -580,6 +624,49 @@ var CheckboxInput = ({
580
624
  errorDisplay && /* @__PURE__ */ jsx(InputError, { error: String(errorDisplay), className: props.classNames?.error })
581
625
  ] });
582
626
  };
627
+ var RadioGroup = ({
628
+ direction = "column",
629
+ ...props
630
+ }) => {
631
+ const resolveError = useErrorResolver();
632
+ const resolvedErrors = props.errorName ? resolveError(props.errorName) : [];
633
+ const errorDisplay = props.error ?? props.customError ?? (resolvedErrors.length > 0 ? resolvedErrors.join(", ") : void 0);
634
+ return /* @__PURE__ */ jsxs(InputField, { noMargin: props.noMargin, className: props.className, children: [
635
+ props.label && /* @__PURE__ */ jsx(InputLabel, { label: props.label, required: props.required }),
636
+ /* @__PURE__ */ jsx("div", { className: twMerge("flex gap-2", direction === "column" ? "flex-col" : "flex-wrap items-stretch"), children: props.options.map((option) => {
637
+ const checked = props.value === option.value;
638
+ const isDisabled = props.disabled || option.disabled;
639
+ return /* @__PURE__ */ jsxs(
640
+ "label",
641
+ {
642
+ className: twMerge(
643
+ "flex items-start gap-3 rounded-(--ui-radius-md) border border-(--ui-border) bg-white px-3 py-2",
644
+ checked && "border-(--ui-border-focus) bg-(--ui-surface-subtle)",
645
+ isDisabled ? "cursor-not-allowed opacity-50" : "cursor-pointer"
646
+ ),
647
+ children: [
648
+ /* @__PURE__ */ jsx(
649
+ "input",
650
+ {
651
+ type: "radio",
652
+ className: "mt-1 h-4 w-4 accent-(--ui-primary)",
653
+ checked,
654
+ disabled: isDisabled,
655
+ onChange: () => props.onChange?.(option.value)
656
+ }
657
+ ),
658
+ /* @__PURE__ */ jsxs("span", { className: "min-w-0", children: [
659
+ /* @__PURE__ */ jsx("span", { className: "block text-sm text-(--ui-text)", children: option.label }),
660
+ option.description && /* @__PURE__ */ jsx("span", { className: twMerge("block text-xs", neutralTextClasses.soft), children: option.description })
661
+ ] })
662
+ ]
663
+ },
664
+ option.value
665
+ );
666
+ }) }),
667
+ errorDisplay && /* @__PURE__ */ jsx(InputError, { error: String(errorDisplay) })
668
+ ] });
669
+ };
583
670
  var SwitchInput = ({
584
671
  value = false,
585
672
  size = "middle",
@@ -1291,6 +1378,65 @@ var ColorInput = ({
1291
1378
  errorDisplay && /* @__PURE__ */ jsx(InputError, { error: String(errorDisplay), className: props.classNames?.error })
1292
1379
  ] });
1293
1380
  };
1381
+ var SearchInput = ({ ...props }) => {
1382
+ const resolveError = useErrorResolver();
1383
+ const resolvedErrors = props.errorName ? resolveError(props.errorName) : [];
1384
+ const errorDisplay = props.error ?? props.customError ?? (resolvedErrors.length > 0 ? resolvedErrors.join(", ") : void 0);
1385
+ const hasValue = !!props.value;
1386
+ return /* @__PURE__ */ jsxs(InputField, { noMargin: props.noMargin, className: props.className, children: [
1387
+ props.label && /* @__PURE__ */ jsx(InputLabel, { label: props.label }),
1388
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
1389
+ /* @__PURE__ */ jsx(IconSearch, { size: 15, strokeWidth: 1.75, className: twMerge("pointer-events-none absolute left-3 top-1/2 -translate-y-1/2", neutralIconClasses.default) }),
1390
+ /* @__PURE__ */ jsx(
1391
+ "input",
1392
+ {
1393
+ value: props.value ?? "",
1394
+ onChange: (e) => props.onChange?.(e.target.value),
1395
+ placeholder: props.placeholder ?? "Hledat...",
1396
+ className: twMerge(inputBaseClass, "pl-9 pr-9", errorDisplay && "border-(--ui-danger)")
1397
+ }
1398
+ ),
1399
+ hasValue && /* @__PURE__ */ jsx(
1400
+ "button",
1401
+ {
1402
+ type: "button",
1403
+ onClick: () => {
1404
+ props.onChange?.("");
1405
+ props.onClear?.();
1406
+ },
1407
+ className: twMerge("absolute right-3 top-1/2 -translate-y-1/2 cursor-pointer", neutralIconClasses.default, neutralIconClasses.hover),
1408
+ children: /* @__PURE__ */ jsx(IconX, { size: 14, strokeWidth: 2 })
1409
+ }
1410
+ )
1411
+ ] }),
1412
+ errorDisplay && /* @__PURE__ */ jsx(InputError, { error: String(errorDisplay) })
1413
+ ] });
1414
+ };
1415
+ var InlineEdit = ({ value, placeholder = "Klikn\u011Bte pro editaci", multiline = false, onSave, label, className }) => {
1416
+ const [editing, setEditing] = useState(false);
1417
+ const [draft, setDraft] = useState(value ?? "");
1418
+ const cancel = () => {
1419
+ setDraft(value ?? "");
1420
+ setEditing(false);
1421
+ };
1422
+ const save = () => {
1423
+ onSave?.(draft);
1424
+ setEditing(false);
1425
+ };
1426
+ return /* @__PURE__ */ jsxs("div", { className: twMerge("rounded-(--ui-radius-md) border border-(--ui-border) bg-white p-3", className), children: [
1427
+ label && /* @__PURE__ */ jsx("div", { className: "mb-2 text-xs font-medium uppercase tracking-[0.08em] text-(--ui-text-soft)", children: label }),
1428
+ editing ? /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
1429
+ multiline ? /* @__PURE__ */ jsx(Textarea, { value: draft, onChange: setDraft, noMargin: true, rows: 4 }) : /* @__PURE__ */ jsx(TextInput, { value: draft, onChange: setDraft, noMargin: true }),
1430
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1431
+ /* @__PURE__ */ jsx(Button, { size: "small", text: "Ulo\u017Eit", icon: IconCheck, onClick: save }),
1432
+ /* @__PURE__ */ jsx(Button, { size: "small", variant: "default", text: "Zru\u0161it", icon: IconX, onClick: cancel })
1433
+ ] })
1434
+ ] }) : /* @__PURE__ */ jsxs("button", { type: "button", onClick: () => setEditing(true), className: "flex w-full items-start justify-between gap-3 text-left cursor-pointer", children: [
1435
+ /* @__PURE__ */ jsx("span", { className: value ? "text-(--ui-text)" : twMerge("italic", neutralTextClasses.soft), children: value || placeholder }),
1436
+ /* @__PURE__ */ jsx(IconPencil, { size: 14, strokeWidth: 1.75, className: neutralTextClasses.soft })
1437
+ ] })
1438
+ ] });
1439
+ };
1294
1440
  var Empty = ({
1295
1441
  text = "\u017D\xE1dn\xE9 z\xE1znamy k dispozici",
1296
1442
  description,
@@ -1621,145 +1767,155 @@ var Grid = (props) => {
1621
1767
  const next = selectedKeys.has(key) ? props.selection.selectedItems.filter((s) => getKey2(s, props.items, props.rowKey) !== key) : [...props.selection.selectedItems, item];
1622
1768
  props.selection.onChange(next);
1623
1769
  };
1624
- return /* @__PURE__ */ jsxs("div", { className: twMerge("relative border border-(--ui-border) rounded-(--ui-radius-lg) overflow-hidden", props.className), children: [
1625
- props.loading && !isInitialLoading && /* @__PURE__ */ jsx("div", { className: twMerge("absolute inset-0 z-10 bg-white/60 flex items-center justify-center", props.classNames?.overlay), children: /* @__PURE__ */ jsx(Spinner, { size: "large", color: "primary" }) }),
1626
- /* @__PURE__ */ jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxs("table", { className: twMerge("w-full border-collapse", props.classNames?.table), children: [
1627
- /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: twMerge("bg-(--ui-primary) text-(--ui-primary-text)", props.classNames?.headerRow), children: [
1628
- props.selection && /* @__PURE__ */ jsx("th", { className: twMerge(sc.th, "w-9", verticalBorders && tableHeaderVerticalBorderClass, props.classNames?.headerCell), children: /* @__PURE__ */ jsx(
1629
- "input",
1630
- {
1631
- type: "checkbox",
1632
- checked: allSelected,
1633
- ref: (el) => {
1634
- if (el) el.indeterminate = someSelected;
1635
- },
1636
- onChange: toggleAll,
1637
- className: "cursor-pointer accent-white"
1638
- }
1639
- ) }),
1640
- props.columns.map((col, i) => {
1641
- const isSortable = col.sortBy !== void 0;
1642
- const isCurrent = isSortable && grid.sortBy === col.sortBy;
1643
- return /* @__PURE__ */ jsx(
1644
- "th",
1645
- {
1646
- style: { width: col.width ?? columnTypeWidth2[col.type] },
1647
- onClick: isSortable ? () => handleSort(col) : void 0,
1648
- className: twMerge(
1649
- sc.th,
1650
- "font-normal text-left whitespace-nowrap",
1651
- verticalBorders && tableHeaderVerticalBorderClass,
1652
- isSortable && "cursor-pointer select-none hover:bg-white/10",
1653
- col.align === "center" && "text-center",
1654
- col.align === "right" && "text-right",
1655
- props.classNames?.headerCell
1656
- ),
1657
- children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
1658
- col.title,
1659
- isSortable && /* @__PURE__ */ jsx("span", { className: "opacity-70", children: isCurrent ? grid.direction === SortDirection.Asc ? /* @__PURE__ */ jsx(IconChevronUp, { size: 12, strokeWidth: 2 }) : /* @__PURE__ */ jsx(IconChevronDown, { size: 12, strokeWidth: 2 }) : /* @__PURE__ */ jsx(IconSelector, { size: 12, strokeWidth: 2 }) })
1660
- ] })
1661
- },
1662
- i
1663
- );
1664
- })
1665
- ] }) }),
1666
- /* @__PURE__ */ jsx("tbody", { children: isInitialLoading ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
1667
- "td",
1668
- {
1669
- colSpan: props.columns.length + (props.selection ? 1 : 0),
1670
- className: twMerge("px-3 py-0", props.classNames?.loadingCell),
1671
- children: /* @__PURE__ */ jsx("div", { className: "flex min-h-28 items-center justify-center", children: /* @__PURE__ */ jsx(Spinner, { size: "large", color: "primary" }) })
1672
- }
1673
- ) }) : isEmpty ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
1674
- "td",
1675
- {
1676
- colSpan: props.columns.length + (props.selection ? 1 : 0),
1677
- className: twMerge("px-3", props.classNames?.emptyCell),
1678
- children: /* @__PURE__ */ jsx(Empty, { size: "compact", text: props.emptyText ?? "\u017D\xE1dn\xE9 z\xE1znamy", className: sc.emptyPy })
1679
- }
1680
- ) }) : props.items.map((item, rowIdx) => {
1681
- const key = getKey2(item, props.items, props.rowKey);
1682
- const isSelected = selectedKeys.has(key);
1683
- return /* @__PURE__ */ jsxs(
1684
- "tr",
1685
- {
1686
- onClick: props.onRowClick ? () => props.onRowClick(item) : void 0,
1687
- className: twMerge(
1688
- "border-t border-(--ui-border) transition-colors",
1689
- rowIdx % 2 === 1 && !isSelected && neutralSurfaceClasses.subtle,
1690
- isSelected && "bg-blue-50",
1691
- props.onRowClick && "cursor-pointer hover:bg-blue-50",
1692
- props.classNames?.bodyRow
1693
- ),
1694
- children: [
1695
- props.selection && /* @__PURE__ */ jsx(
1696
- "td",
1770
+ return /* @__PURE__ */ jsxs(
1771
+ "div",
1772
+ {
1773
+ className: twMerge(
1774
+ "relative border border-(--ui-border) rounded-(--ui-radius-lg) overflow-hidden",
1775
+ props.attachedTop && "rounded-t-none border-t-0",
1776
+ props.className
1777
+ ),
1778
+ children: [
1779
+ props.loading && !isInitialLoading && /* @__PURE__ */ jsx("div", { className: twMerge("absolute inset-0 z-10 bg-white/60 flex items-center justify-center", props.classNames?.overlay), children: /* @__PURE__ */ jsx(Spinner, { size: "large", color: "primary" }) }),
1780
+ /* @__PURE__ */ jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxs("table", { className: twMerge("w-full border-collapse", props.classNames?.table), children: [
1781
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: twMerge("bg-(--ui-primary) text-(--ui-primary-text)", props.classNames?.headerRow), children: [
1782
+ props.selection && /* @__PURE__ */ jsx("th", { className: twMerge(sc.th, "w-9", verticalBorders && tableHeaderVerticalBorderClass, props.classNames?.headerCell), children: /* @__PURE__ */ jsx(
1783
+ "input",
1784
+ {
1785
+ type: "checkbox",
1786
+ checked: allSelected,
1787
+ ref: (el) => {
1788
+ if (el) el.indeterminate = someSelected;
1789
+ },
1790
+ onChange: toggleAll,
1791
+ className: "cursor-pointer accent-white"
1792
+ }
1793
+ ) }),
1794
+ props.columns.map((col, i) => {
1795
+ const isSortable = col.sortBy !== void 0;
1796
+ const isCurrent = isSortable && grid.sortBy === col.sortBy;
1797
+ return /* @__PURE__ */ jsx(
1798
+ "th",
1697
1799
  {
1698
- className: twMerge(sc.td, "w-9 align-middle", verticalBorders && tableCellVerticalBorderClass, props.classNames?.bodyCell),
1699
- onClick: (e) => e.stopPropagation(),
1700
- children: /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center", children: /* @__PURE__ */ jsx(
1701
- "input",
1800
+ style: { width: col.width ?? columnTypeWidth2[col.type] },
1801
+ onClick: isSortable ? () => handleSort(col) : void 0,
1802
+ className: twMerge(
1803
+ sc.th,
1804
+ "font-normal text-left whitespace-nowrap",
1805
+ verticalBorders && tableHeaderVerticalBorderClass,
1806
+ isSortable && "cursor-pointer select-none hover:bg-white/10",
1807
+ col.align === "center" && "text-center",
1808
+ col.align === "right" && "text-right",
1809
+ props.classNames?.headerCell
1810
+ ),
1811
+ children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
1812
+ col.title,
1813
+ isSortable && /* @__PURE__ */ jsx("span", { className: "opacity-70", children: isCurrent ? grid.direction === SortDirection.Asc ? /* @__PURE__ */ jsx(IconChevronUp, { size: 12, strokeWidth: 2 }) : /* @__PURE__ */ jsx(IconChevronDown, { size: 12, strokeWidth: 2 }) : /* @__PURE__ */ jsx(IconSelector, { size: 12, strokeWidth: 2 }) })
1814
+ ] })
1815
+ },
1816
+ i
1817
+ );
1818
+ })
1819
+ ] }) }),
1820
+ /* @__PURE__ */ jsx("tbody", { children: isInitialLoading ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
1821
+ "td",
1822
+ {
1823
+ colSpan: props.columns.length + (props.selection ? 1 : 0),
1824
+ className: twMerge("px-3 py-0", props.classNames?.loadingCell),
1825
+ children: /* @__PURE__ */ jsx("div", { className: "flex min-h-28 items-center justify-center", children: /* @__PURE__ */ jsx(Spinner, { size: "large", color: "primary" }) })
1826
+ }
1827
+ ) }) : isEmpty ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
1828
+ "td",
1829
+ {
1830
+ colSpan: props.columns.length + (props.selection ? 1 : 0),
1831
+ className: twMerge("px-3", props.classNames?.emptyCell),
1832
+ children: /* @__PURE__ */ jsx(Empty, { size: "compact", text: props.emptyText ?? "\u017D\xE1dn\xE9 z\xE1znamy", className: sc.emptyPy })
1833
+ }
1834
+ ) }) : props.items.map((item, rowIdx) => {
1835
+ const key = getKey2(item, props.items, props.rowKey);
1836
+ const isSelected = selectedKeys.has(key);
1837
+ return /* @__PURE__ */ jsxs(
1838
+ "tr",
1839
+ {
1840
+ onClick: props.onRowClick ? () => props.onRowClick(item) : void 0,
1841
+ className: twMerge(
1842
+ "border-t border-(--ui-border) transition-colors",
1843
+ rowIdx % 2 === 1 && !isSelected && neutralSurfaceClasses.subtle,
1844
+ isSelected && "bg-blue-50",
1845
+ props.onRowClick && "cursor-pointer hover:bg-blue-50",
1846
+ props.classNames?.bodyRow
1847
+ ),
1848
+ children: [
1849
+ props.selection && /* @__PURE__ */ jsx(
1850
+ "td",
1702
1851
  {
1703
- type: "checkbox",
1704
- checked: isSelected,
1705
- onChange: () => toggleRow(item),
1706
- className: "cursor-pointer"
1852
+ className: twMerge(sc.td, "w-9 align-middle", verticalBorders && tableCellVerticalBorderClass, props.classNames?.bodyCell),
1853
+ onClick: (e) => e.stopPropagation(),
1854
+ children: /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center", children: /* @__PURE__ */ jsx(
1855
+ "input",
1856
+ {
1857
+ type: "checkbox",
1858
+ checked: isSelected,
1859
+ onChange: () => toggleRow(item),
1860
+ className: "cursor-pointer"
1861
+ }
1862
+ ) })
1707
1863
  }
1708
- ) })
1709
- }
1710
- ),
1711
- props.columns.map((col, colIdx) => {
1712
- const content = col.render ? col.render(item) : col.dataField ? formatCellValue(item[col.dataField], col.type) : null;
1713
- const centeredContent = col.align === "center";
1714
- const rightAlignedContent = col.align === "right";
1715
- return /* @__PURE__ */ jsx(
1716
- "td",
1717
- {
1718
- onClick: col.type === "dot" ? (e) => e.stopPropagation() : void 0,
1719
- className: twMerge(
1720
- sc.td,
1721
- "align-middle",
1722
- verticalBorders && tableCellVerticalBorderClass,
1723
- col.ellipsis && "max-w-60 truncate",
1724
- col.wrap && "whitespace-normal",
1725
- col.align === "center" && "text-center",
1726
- col.align === "right" && "text-right",
1727
- col.type === "dot" && "w-9",
1728
- props.classNames?.bodyCell
1729
- ),
1730
- children: centeredContent || rightAlignedContent ? /* @__PURE__ */ jsx(
1731
- "div",
1864
+ ),
1865
+ props.columns.map((col, colIdx) => {
1866
+ const content = col.render ? col.render(item) : col.dataField ? formatCellValue(item[col.dataField], col.type) : null;
1867
+ const centeredContent = col.align === "center";
1868
+ const rightAlignedContent = col.align === "right";
1869
+ return /* @__PURE__ */ jsx(
1870
+ "td",
1732
1871
  {
1872
+ onClick: col.type === "dot" ? (e) => e.stopPropagation() : void 0,
1733
1873
  className: twMerge(
1734
- "flex w-full items-center",
1735
- centeredContent ? "justify-center" : "justify-end"
1874
+ sc.td,
1875
+ "align-middle",
1876
+ verticalBorders && tableCellVerticalBorderClass,
1877
+ col.ellipsis && "max-w-60 truncate",
1878
+ col.wrap && "whitespace-normal",
1879
+ col.align === "center" && "text-center",
1880
+ col.align === "right" && "text-right",
1881
+ col.type === "dot" && "w-9",
1882
+ props.classNames?.bodyCell
1736
1883
  ),
1737
- children: content
1738
- }
1739
- ) : content
1740
- },
1741
- colIdx
1742
- );
1743
- })
1744
- ]
1745
- },
1746
- key
1747
- );
1748
- }) })
1749
- ] }) }),
1750
- /* @__PURE__ */ jsx(
1751
- Pagination,
1752
- {
1753
- current: grid.pageNumber,
1754
- pageSize: grid.pageSize,
1755
- total: props.totalCount,
1756
- showPageNumberChanger: props.showPageNumberChanger ?? false,
1757
- showPageSizeChanger: props.showPageSizeChanger ?? false,
1758
- onChange: (page, pageSize) => grid.onChange({ pageNumber: page, pageSize }),
1759
- classNames: props.classNames?.pagination
1760
- }
1761
- )
1762
- ] });
1884
+ children: centeredContent || rightAlignedContent ? /* @__PURE__ */ jsx(
1885
+ "div",
1886
+ {
1887
+ className: twMerge(
1888
+ "flex w-full items-center",
1889
+ centeredContent ? "justify-center" : "justify-end"
1890
+ ),
1891
+ children: content
1892
+ }
1893
+ ) : content
1894
+ },
1895
+ colIdx
1896
+ );
1897
+ })
1898
+ ]
1899
+ },
1900
+ key
1901
+ );
1902
+ }) })
1903
+ ] }) }),
1904
+ /* @__PURE__ */ jsx(
1905
+ Pagination,
1906
+ {
1907
+ current: grid.pageNumber,
1908
+ pageSize: grid.pageSize,
1909
+ total: props.totalCount,
1910
+ showPageNumberChanger: props.showPageNumberChanger ?? false,
1911
+ showPageSizeChanger: props.showPageSizeChanger ?? false,
1912
+ onChange: (page, pageSize) => grid.onChange({ pageNumber: page, pageSize }),
1913
+ classNames: props.classNames?.pagination
1914
+ }
1915
+ )
1916
+ ]
1917
+ }
1918
+ );
1763
1919
  };
1764
1920
  function isValueSet(v) {
1765
1921
  if (Array.isArray(v)) return v.length > 0;
@@ -3384,34 +3540,220 @@ var Panel = ({
3384
3540
  }
3385
3541
  );
3386
3542
  };
3543
+ var Toolbar = ({ children, start, end, wrapped = true, inset = false, className, classNames }) => /* @__PURE__ */ jsxs(
3544
+ "div",
3545
+ {
3546
+ className: twMerge(
3547
+ "flex items-center justify-between gap-3 rounded-(--ui-radius-lg) border border-(--ui-border)",
3548
+ neutralSurfaceClasses.subtle,
3549
+ wrapped && "flex-wrap",
3550
+ inset ? "px-3 py-2" : "px-4 py-3",
3551
+ className
3552
+ ),
3553
+ children: [
3554
+ /* @__PURE__ */ jsx("div", { className: twMerge("flex min-w-0 flex-1 items-center gap-2", wrapped && "flex-wrap", classNames?.start), children: start ?? children }),
3555
+ end && /* @__PURE__ */ jsx("div", { className: twMerge("flex items-center gap-2", wrapped && "flex-wrap justify-end", classNames?.end), children: end })
3556
+ ]
3557
+ }
3558
+ );
3559
+ var ActionBar = Toolbar;
3560
+ var SectionHeader = ({ title, subtitle, actions, className }) => /* @__PURE__ */ jsxs("div", { className: twMerge("flex items-start justify-between gap-4", className), children: [
3561
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
3562
+ /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-(--ui-text-strong)", children: title }),
3563
+ subtitle && /* @__PURE__ */ jsx("p", { className: twMerge("mt-1 text-sm", neutralTextClasses.muted), children: subtitle })
3564
+ ] }),
3565
+ actions && /* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center gap-2", children: actions })
3566
+ ] });
3567
+ var PageHeader = ({ title, subtitle, breadcrumbs, actions, extra, className }) => /* @__PURE__ */ jsxs("div", { className: twMerge("rounded-(--ui-radius-lg) border border-(--ui-border) bg-white p-5", className), children: [
3568
+ breadcrumbs && /* @__PURE__ */ jsx("div", { className: twMerge("mb-2 text-xs", neutralTextClasses.soft), children: breadcrumbs }),
3569
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-4", children: [
3570
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
3571
+ /* @__PURE__ */ jsx("h1", { className: "text-2xl font-semibold text-(--ui-text-strong)", children: title }),
3572
+ subtitle && /* @__PURE__ */ jsx("p", { className: twMerge("mt-1 text-sm", neutralTextClasses.muted), children: subtitle })
3573
+ ] }),
3574
+ actions && /* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center gap-2", children: actions })
3575
+ ] }),
3576
+ extra && /* @__PURE__ */ jsx("div", { className: "mt-4 border-t border-(--ui-border) pt-4", children: extra })
3577
+ ] });
3578
+ var AVATAR_COLORS = [
3579
+ "bg-blue-500",
3580
+ "bg-green-500",
3581
+ "bg-purple-500",
3582
+ "bg-orange-500",
3583
+ "bg-pink-500",
3584
+ "bg-teal-500",
3585
+ "bg-red-500",
3586
+ "bg-indigo-500"
3587
+ ];
3588
+ function colorFromString(str) {
3589
+ let hash = 0;
3590
+ for (let i = 0; i < str.length; i++) {
3591
+ hash = str.charCodeAt(i) + ((hash << 5) - hash);
3592
+ }
3593
+ return AVATAR_COLORS[Math.abs(hash) % AVATAR_COLORS.length];
3594
+ }
3595
+ function getInitials(name) {
3596
+ const parts = name.trim().split(/\s+/);
3597
+ if (parts.length === 1) {
3598
+ return parts[0].slice(0, 2).toUpperCase();
3599
+ }
3600
+ return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
3601
+ }
3602
+ var sizeClasses = {
3603
+ xs: "w-6 h-6 text-[10px]",
3604
+ sm: "w-7 h-7 text-xs",
3605
+ md: "w-8 h-8 text-sm",
3606
+ lg: "w-10 h-10 text-base",
3607
+ xl: "w-12 h-12 text-lg"
3608
+ };
3609
+ var Avatar = ({ size = "md", ...props }) => {
3610
+ const initials = props.name ? getInitials(props.name) : "?";
3611
+ const bgColor = props.color ?? (props.name ? colorFromString(props.name) : "bg-gray-400");
3612
+ if (props.src) {
3613
+ return /* @__PURE__ */ jsx(
3614
+ "img",
3615
+ {
3616
+ src: props.src,
3617
+ alt: props.name ?? "avatar",
3618
+ className: twMerge(
3619
+ "rounded-full object-cover shrink-0",
3620
+ sizeClasses[size],
3621
+ props.className
3622
+ )
3623
+ }
3624
+ );
3625
+ }
3626
+ return /* @__PURE__ */ jsx(
3627
+ "span",
3628
+ {
3629
+ title: props.name,
3630
+ className: twMerge(
3631
+ "inline-flex items-center justify-center rounded-full text-white font-medium shrink-0 select-none",
3632
+ sizeClasses[size],
3633
+ bgColor,
3634
+ props.className
3635
+ ),
3636
+ children: /* @__PURE__ */ jsx("span", { className: props.initialsClassName, children: initials })
3637
+ }
3638
+ );
3639
+ };
3640
+ var EntityHeader = ({ title, subtitle, avatarName, icon: Icon, meta, tags, actions, className }) => /* @__PURE__ */ jsx("div", { className: twMerge("rounded-(--ui-radius-lg) border border-(--ui-border) bg-white p-5", className), children: /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-4", children: [
3641
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-start gap-3", children: [
3642
+ avatarName ? /* @__PURE__ */ jsx(Avatar, { name: avatarName, size: "lg" }) : Icon ? /* @__PURE__ */ jsx("div", { className: "flex h-11 w-11 items-center justify-center rounded-(--ui-radius-lg) bg-(--ui-surface-subtle)", children: /* @__PURE__ */ jsx(Icon, { size: 20, strokeWidth: 1.8, className: "text-(--ui-text)" }) }) : null,
3643
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
3644
+ /* @__PURE__ */ jsx("div", { className: "text-xl font-semibold text-(--ui-text-strong)", children: title }),
3645
+ subtitle && /* @__PURE__ */ jsx("div", { className: twMerge("mt-1 text-sm", neutralTextClasses.muted), children: subtitle }),
3646
+ (meta || tags) && /* @__PURE__ */ jsxs("div", { className: "mt-3 flex flex-wrap items-center gap-2", children: [
3647
+ meta,
3648
+ tags
3649
+ ] })
3650
+ ] })
3651
+ ] }),
3652
+ actions && /* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center gap-2", children: actions })
3653
+ ] }) });
3654
+ var alignClass3 = {
3655
+ left: "justify-start",
3656
+ center: "justify-center",
3657
+ right: "justify-end",
3658
+ between: "justify-between"
3659
+ };
3660
+ var FormActions = ({ children, align = "right", className }) => /* @__PURE__ */ jsx("div", { className: twMerge("flex flex-wrap items-center gap-2 border-t border-(--ui-border) pt-4", alignClass3[align], className), children });
3661
+ var FormSection = ({ title, description, actions, children, className }) => /* @__PURE__ */ jsxs("fieldset", { className: twMerge("rounded-(--ui-radius-lg) border border-(--ui-border) bg-white p-4", className), children: [
3662
+ /* @__PURE__ */ jsxs("div", { className: "mb-4 flex items-start justify-between gap-4", children: [
3663
+ /* @__PURE__ */ jsxs("legend", { className: "min-w-0", children: [
3664
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-semibold text-(--ui-text-strong)", children: title }),
3665
+ description && /* @__PURE__ */ jsx("div", { className: twMerge("mt-1 text-xs", neutralTextClasses.soft), children: description })
3666
+ ] }),
3667
+ actions && /* @__PURE__ */ jsx("div", { className: "shrink-0", children: actions })
3668
+ ] }),
3669
+ /* @__PURE__ */ jsx("div", { className: "space-y-4", children })
3670
+ ] });
3671
+ var Fieldset = FormSection;
3672
+ var listVariantClass = {
3673
+ segmented: "inline-flex gap-0 rounded-(--ui-radius-lg) p-1",
3674
+ underline: "inline-flex gap-1 border-b border-(--ui-border-strong)",
3675
+ cards: "inline-flex gap-2"
3676
+ };
3677
+ var listSurfaceClass = {
3678
+ segmented: neutralSurfaceClasses.subtle,
3679
+ underline: "",
3680
+ cards: ""
3681
+ };
3682
+ var tabSizeClass = {
3683
+ small: "h-8 px-3 text-[12px]",
3684
+ middle: "h-9 px-4 text-[14px]",
3685
+ large: "h-10 px-5 text-[15px]"
3686
+ };
3687
+ var tabVariantClass = {
3688
+ segmented: "rounded-[calc(var(--ui-radius-lg)-4px)]",
3689
+ underline: "border-b-2 border-transparent rounded-none -mb-px",
3690
+ cards: "rounded-(--ui-radius-lg) border border-(--ui-border) bg-white shadow-sm"
3691
+ };
3692
+ var activeVariantClass = {
3693
+ segmented: "bg-white text-(--ui-text-strong) shadow-sm font-semibold",
3694
+ underline: "border-(--ui-primary) text-(--ui-text-strong) font-semibold",
3695
+ cards: "border-(--ui-border-focus) bg-(--ui-surface-subtle) text-(--ui-text-strong) font-semibold shadow-none"
3696
+ };
3697
+ var inactiveVariantClass = {
3698
+ segmented: "text-(--ui-text-muted) hover:text-(--ui-text)",
3699
+ underline: "text-(--ui-text-muted) hover:text-(--ui-text)",
3700
+ cards: "text-(--ui-text-muted) hover:border-(--ui-border-focus) hover:text-(--ui-text)"
3701
+ };
3387
3702
  var Tabs = ({
3388
3703
  block = true,
3704
+ variant = "segmented",
3705
+ size = "middle",
3389
3706
  ...props
3390
3707
  }) => /* @__PURE__ */ jsx(
3391
3708
  "div",
3392
3709
  {
3710
+ role: "tablist",
3393
3711
  className: twMerge(
3394
- "inline-flex gap-0 rounded-(--ui-radius-lg) p-1",
3395
- neutralSurfaceClasses.subtle,
3712
+ listVariantClass[variant],
3713
+ listSurfaceClass[variant],
3396
3714
  block && "flex w-full",
3397
3715
  props.className,
3398
3716
  props.classNames?.list
3399
3717
  ),
3400
- children: props.options.map((opt) => /* @__PURE__ */ jsx(
3401
- "button",
3402
- {
3403
- type: "button",
3404
- onClick: () => props.onChange(opt.value),
3405
- className: twMerge(
3406
- "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",
3407
- props.value === opt.value ? "bg-white text-(--ui-text-strong) shadow-sm font-semibold" : "text-(--ui-text-muted) hover:text-(--ui-text)",
3408
- props.classNames?.tab,
3409
- props.value === opt.value ? props.classNames?.activeTab : props.classNames?.inactiveTab
3410
- ),
3411
- children: opt.label
3412
- },
3413
- opt.value
3414
- ))
3718
+ children: props.options.map((opt) => {
3719
+ const isSimpleLabel = typeof opt.label === "string" || typeof opt.label === "number";
3720
+ return /* @__PURE__ */ jsxs(
3721
+ "button",
3722
+ {
3723
+ type: "button",
3724
+ role: "tab",
3725
+ "aria-selected": props.value === opt.value,
3726
+ disabled: opt.disabled,
3727
+ onClick: () => !opt.disabled && props.onChange(opt.value),
3728
+ className: twMerge(
3729
+ "flex-1 min-w-0 transition-all duration-150 cursor-pointer select-none whitespace-nowrap",
3730
+ "inline-flex items-center justify-center gap-2",
3731
+ tabSizeClass[size],
3732
+ tabVariantClass[variant],
3733
+ props.value === opt.value ? activeVariantClass[variant] : inactiveVariantClass[variant],
3734
+ opt.disabled && "cursor-not-allowed opacity-45 hover:border-(--ui-border) hover:text-(--ui-text-muted)",
3735
+ props.classNames?.tab,
3736
+ props.value === opt.value ? props.classNames?.activeTab : props.classNames?.inactiveTab
3737
+ ),
3738
+ children: [
3739
+ opt.icon && /* @__PURE__ */ jsx("span", { className: twMerge("shrink-0", props.classNames?.icon), children: opt.icon }),
3740
+ /* @__PURE__ */ jsx("span", { className: twMerge("min-w-0", isSimpleLabel && "truncate"), children: opt.label }),
3741
+ opt.badge && /* @__PURE__ */ jsx(
3742
+ "span",
3743
+ {
3744
+ className: twMerge(
3745
+ "shrink-0 rounded-full px-2 py-0.5 text-[11px] leading-none",
3746
+ props.value === opt.value ? "bg-(--ui-primary) text-(--ui-primary-text)" : "bg-(--ui-surface-muted) text-(--ui-text-muted)",
3747
+ props.classNames?.badge
3748
+ ),
3749
+ children: opt.badge
3750
+ }
3751
+ )
3752
+ ]
3753
+ },
3754
+ opt.value
3755
+ );
3756
+ })
3415
3757
  }
3416
3758
  );
3417
3759
  var colorClasses = {
@@ -3490,68 +3832,6 @@ var FlagTag = ({ value, label, color = "geekblue", variant }) => {
3490
3832
  }
3491
3833
  return /* @__PURE__ */ jsx(Tag, { color, variant, children: label });
3492
3834
  };
3493
- var AVATAR_COLORS = [
3494
- "bg-blue-500",
3495
- "bg-green-500",
3496
- "bg-purple-500",
3497
- "bg-orange-500",
3498
- "bg-pink-500",
3499
- "bg-teal-500",
3500
- "bg-red-500",
3501
- "bg-indigo-500"
3502
- ];
3503
- function colorFromString(str) {
3504
- let hash = 0;
3505
- for (let i = 0; i < str.length; i++) {
3506
- hash = str.charCodeAt(i) + ((hash << 5) - hash);
3507
- }
3508
- return AVATAR_COLORS[Math.abs(hash) % AVATAR_COLORS.length];
3509
- }
3510
- function getInitials(name) {
3511
- const parts = name.trim().split(/\s+/);
3512
- if (parts.length === 1) {
3513
- return parts[0].slice(0, 2).toUpperCase();
3514
- }
3515
- return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
3516
- }
3517
- var sizeClasses = {
3518
- xs: "w-6 h-6 text-[10px]",
3519
- sm: "w-7 h-7 text-xs",
3520
- md: "w-8 h-8 text-sm",
3521
- lg: "w-10 h-10 text-base",
3522
- xl: "w-12 h-12 text-lg"
3523
- };
3524
- var Avatar = ({ size = "md", ...props }) => {
3525
- const initials = props.name ? getInitials(props.name) : "?";
3526
- const bgColor = props.color ?? (props.name ? colorFromString(props.name) : "bg-gray-400");
3527
- if (props.src) {
3528
- return /* @__PURE__ */ jsx(
3529
- "img",
3530
- {
3531
- src: props.src,
3532
- alt: props.name ?? "avatar",
3533
- className: twMerge(
3534
- "rounded-full object-cover shrink-0",
3535
- sizeClasses[size],
3536
- props.className
3537
- )
3538
- }
3539
- );
3540
- }
3541
- return /* @__PURE__ */ jsx(
3542
- "span",
3543
- {
3544
- title: props.name,
3545
- className: twMerge(
3546
- "inline-flex items-center justify-center rounded-full text-white font-medium shrink-0 select-none",
3547
- sizeClasses[size],
3548
- bgColor,
3549
- props.className
3550
- ),
3551
- children: /* @__PURE__ */ jsx("span", { className: props.initialsClassName, children: initials })
3552
- }
3553
- );
3554
- };
3555
3835
  var defaultToolbar = [
3556
3836
  "bold",
3557
3837
  "italic",
@@ -4225,6 +4505,88 @@ var useNotification = () => {
4225
4505
  info: (message, options) => add("info", message, options)
4226
4506
  };
4227
4507
  };
4508
+ var EmptyState = ({
4509
+ primaryActionText,
4510
+ onPrimaryAction,
4511
+ secondaryActionText,
4512
+ onSecondaryAction,
4513
+ extra,
4514
+ className,
4515
+ classNames,
4516
+ ...props
4517
+ }) => /* @__PURE__ */ jsxs("div", { className: twMerge("rounded-(--ui-radius-lg) border border-(--ui-border) bg-white", className), children: [
4518
+ /* @__PURE__ */ jsx(Empty, { className: "py-10", ...props }),
4519
+ (primaryActionText || secondaryActionText || extra) && /* @__PURE__ */ jsxs("div", { className: twMerge("flex flex-wrap items-center justify-center gap-2 px-4 pb-6", classNames?.actions), children: [
4520
+ secondaryActionText && /* @__PURE__ */ jsx(Button, { variant: "default", text: secondaryActionText, onClick: onSecondaryAction }),
4521
+ primaryActionText && /* @__PURE__ */ jsx(Button, { text: primaryActionText, onClick: onPrimaryAction }),
4522
+ extra && /* @__PURE__ */ jsx("div", { className: classNames?.extra, children: extra })
4523
+ ] })
4524
+ ] });
4525
+ var trendConfig = {
4526
+ up: {
4527
+ icon: IconArrowUpRight,
4528
+ className: "text-(--ui-success)"
4529
+ },
4530
+ down: {
4531
+ icon: IconArrowDownRight,
4532
+ className: "text-(--ui-danger)"
4533
+ },
4534
+ neutral: {
4535
+ icon: IconMinus,
4536
+ className: neutralTextClasses.soft
4537
+ }
4538
+ };
4539
+ var StatCard = ({ label, value, description, icon: Icon, trend, footer, className }) => {
4540
+ const trendDirection = trend?.direction ?? "neutral";
4541
+ const TrendIcon = trend ? trendConfig[trendDirection].icon : null;
4542
+ return /* @__PURE__ */ jsxs("div", { className: twMerge("rounded-(--ui-radius-lg) border border-(--ui-border) bg-white p-4 shadow-sm", className), children: [
4543
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3", children: [
4544
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
4545
+ /* @__PURE__ */ jsx("p", { className: twMerge("text-sm", neutralTextClasses.muted), children: label }),
4546
+ /* @__PURE__ */ jsx("p", { className: "mt-2 text-2xl font-semibold text-(--ui-text-strong)", children: value }),
4547
+ description && /* @__PURE__ */ jsx("p", { className: twMerge("mt-1 text-xs", neutralTextClasses.soft), children: description })
4548
+ ] }),
4549
+ Icon && /* @__PURE__ */ jsx("div", { className: twMerge("flex h-10 w-10 items-center justify-center rounded-(--ui-radius-lg)", neutralSurfaceClasses.subtle), children: /* @__PURE__ */ jsx(Icon, { size: 18, strokeWidth: 1.75, className: neutralTextClasses.default }) })
4550
+ ] }),
4551
+ trend && /* @__PURE__ */ jsxs("div", { className: "mt-4 flex items-center gap-2 text-sm", children: [
4552
+ TrendIcon && /* @__PURE__ */ jsx(TrendIcon, { size: 15, strokeWidth: 2, className: trendConfig[trendDirection].className }),
4553
+ /* @__PURE__ */ jsx("span", { className: twMerge("font-medium", trendConfig[trendDirection].className), children: trend.value }),
4554
+ trend.label && /* @__PURE__ */ jsx("span", { className: neutralTextClasses.soft, children: trend.label })
4555
+ ] }),
4556
+ footer && /* @__PURE__ */ jsx("div", { className: "mt-4 border-t border-(--ui-border) pt-3", children: footer })
4557
+ ] });
4558
+ };
4559
+ var DescriptionList = ({ items, columns = 2, className }) => /* @__PURE__ */ jsx(
4560
+ "div",
4561
+ {
4562
+ className: twMerge(
4563
+ "grid gap-x-6 gap-y-4 rounded-(--ui-radius-lg) border border-(--ui-border) bg-white p-4",
4564
+ columns === 1 && "grid-cols-1",
4565
+ columns === 2 && "grid-cols-1 md:grid-cols-2",
4566
+ columns === 3 && "grid-cols-1 md:grid-cols-3",
4567
+ className
4568
+ ),
4569
+ children: items.map((item, index) => /* @__PURE__ */ jsxs(
4570
+ "div",
4571
+ {
4572
+ className: twMerge(
4573
+ "min-w-0",
4574
+ item.span === 2 && columns > 1 && "md:col-span-2",
4575
+ item.span === 3 && columns > 2 && "md:col-span-3"
4576
+ ),
4577
+ children: [
4578
+ /* @__PURE__ */ jsx("dt", { className: twMerge("text-xs font-medium uppercase tracking-[0.08em]", neutralTextClasses.soft), children: item.label }),
4579
+ /* @__PURE__ */ jsx("dd", { className: "mt-1 text-sm text-(--ui-text)", children: item.value })
4580
+ ]
4581
+ },
4582
+ index
4583
+ ))
4584
+ }
4585
+ );
4586
+ var KeyValue = ({ label, value, className }) => /* @__PURE__ */ jsxs("div", { className: twMerge("flex items-start justify-between gap-4 border-b border-(--ui-border) py-2 last:border-b-0", className), children: [
4587
+ /* @__PURE__ */ jsx("span", { className: twMerge("text-sm", neutralTextClasses.muted), children: label }),
4588
+ /* @__PURE__ */ jsx("span", { className: "text-right text-sm text-(--ui-text)", children: value })
4589
+ ] });
4228
4590
  var Pretty = ({ data, className }) => /* @__PURE__ */ jsx(
4229
4591
  "pre",
4230
4592
  {
@@ -4235,6 +4597,10 @@ var Pretty = ({ data, className }) => /* @__PURE__ */ jsx(
4235
4597
  children: JSON.stringify(data, null, 2)
4236
4598
  }
4237
4599
  );
4600
+ var SkeletonTable = ({ rows = 5, columns = 4, className }) => /* @__PURE__ */ jsx("div", { className: twMerge("overflow-hidden rounded-(--ui-radius-lg) border border-(--ui-border) bg-white", className), children: /* @__PURE__ */ jsxs("div", { className: "animate-pulse", children: [
4601
+ /* @__PURE__ */ jsx("div", { className: "grid bg-(--ui-primary) px-4 py-3", style: { gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))` }, children: Array.from({ length: columns }).map((_, index) => /* @__PURE__ */ jsx("div", { className: "h-3 w-20 rounded bg-white/30" }, index)) }),
4602
+ /* @__PURE__ */ jsx("div", { className: "divide-y divide-(--ui-border)", children: Array.from({ length: rows }).map((_, rowIndex) => /* @__PURE__ */ jsx("div", { className: "grid gap-4 px-4 py-3", style: { gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))` }, children: Array.from({ length: columns }).map((__, columnIndex) => /* @__PURE__ */ jsx("div", { className: "h-3 rounded bg-(--ui-surface-muted)" }, columnIndex)) }, rowIndex)) })
4603
+ ] }) });
4238
4604
  var GAP2 = 10;
4239
4605
  var VIEWPORT_MARGIN2 = 8;
4240
4606
  function calcPosition3(trigger, tooltip, placement) {
@@ -4389,6 +4755,47 @@ var Tooltip = ({
4389
4755
  )
4390
4756
  ] });
4391
4757
  };
4758
+ var Stepper = ({ items, className }) => /* @__PURE__ */ jsx("div", { className: twMerge("flex flex-wrap gap-4", className), children: items.map((item, index) => {
4759
+ const status = item.status ?? "upcoming";
4760
+ return /* @__PURE__ */ jsxs("div", { className: "flex min-w-40 flex-1 items-start gap-3", children: [
4761
+ /* @__PURE__ */ jsx(
4762
+ "div",
4763
+ {
4764
+ className: twMerge(
4765
+ "mt-0.5 flex h-7 w-7 shrink-0 items-center justify-center rounded-full border text-xs font-semibold",
4766
+ status === "complete" && "border-(--ui-success) bg-(--ui-success) text-white",
4767
+ status === "current" && "border-(--ui-primary) bg-(--ui-primary) text-white",
4768
+ status === "upcoming" && "border-(--ui-border) bg-white text-(--ui-text-soft)"
4769
+ ),
4770
+ children: status === "complete" ? /* @__PURE__ */ jsx(IconCheck, { size: 14, strokeWidth: 2.5 }) : index + 1
4771
+ }
4772
+ ),
4773
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
4774
+ /* @__PURE__ */ jsx("div", { className: twMerge("text-sm font-medium", status === "upcoming" ? neutralTextClasses.muted : "text-(--ui-text-strong)"), children: item.title }),
4775
+ item.description && /* @__PURE__ */ jsx("div", { className: twMerge("mt-1 text-xs", neutralTextClasses.soft), children: item.description })
4776
+ ] })
4777
+ ] }, index);
4778
+ }) });
4779
+ var colorClass = {
4780
+ primary: "bg-(--ui-primary)",
4781
+ success: "bg-(--ui-success)",
4782
+ danger: "bg-(--ui-danger)",
4783
+ warning: "bg-(--ui-warning)",
4784
+ neutral: "bg-(--ui-border-strong)"
4785
+ };
4786
+ var Timeline = ({ items, className }) => /* @__PURE__ */ jsx("div", { className: twMerge("space-y-4", className), children: items.map((item, index) => /* @__PURE__ */ jsxs("div", { className: "relative flex gap-3 pl-1", children: [
4787
+ /* @__PURE__ */ jsxs("div", { className: "relative flex flex-col items-center", children: [
4788
+ /* @__PURE__ */ jsx("span", { className: twMerge("mt-1 h-2.5 w-2.5 rounded-full", colorClass[item.color ?? "primary"]) }),
4789
+ index < items.length - 1 && /* @__PURE__ */ jsx("span", { className: "mt-1 h-full w-px bg-(--ui-border)" })
4790
+ ] }),
4791
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 pb-4", children: [
4792
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
4793
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-medium text-(--ui-text-strong)", children: item.title }),
4794
+ item.time && /* @__PURE__ */ jsx("div", { className: twMerge("text-xs", neutralTextClasses.soft), children: item.time })
4795
+ ] }),
4796
+ item.description && /* @__PURE__ */ jsx("div", { className: twMerge("mt-1 text-sm", neutralTextClasses.muted), children: item.description })
4797
+ ] })
4798
+ ] }, index)) });
4392
4799
 
4393
4800
  // src/initUI.ts
4394
4801
  function initUI(config) {
@@ -4502,6 +4909,6 @@ function initUI(config) {
4502
4909
  r.style.setProperty("--ui-empty-compact-description", layout.empty.compactDescription);
4503
4910
  }
4504
4911
 
4505
- export { ActiveTag, Alert, Avatar, Badge, Button, CheckboxInput, ColorInput, DateInput, Drawer, DrawerContent, DrawerFooter, DrawerTitle, Dropdown, Empty, ErrorProvider, FilterCheckboxInput, FilterDateInput, FilterDateRangePopover, FilterNumberInput, FilterSelectGroupPopover, FilterSelectInput, FilterTextInput, FlagTag, Grid, GridFilters, HtmlInput, InputError, InputField, InputLabel, Modal, ModalContent, ModalFooter, ModalTitle, MultiSelectInput, NotificationProvider, NumberInput, Panel, Popconfirm, Pretty, SelectInput, ServerError, Skeleton, SortDirection, Spinner, SwitchInput, Table, Tabs, Tag, TextInput, Tooltip, UploadInput, UploadProvider, getIcon, initUI, notification, registerIcons, uiTheme, useDrawer, useGrid, useModal, useNotification, useUploadConfig };
4912
+ export { ActionBar, ActiveTag, Alert, Avatar, Badge, Button, CheckboxInput, ColorInput, ConfirmButton, DateInput, DescriptionList, Drawer, DrawerContent, DrawerFooter, DrawerTitle, Dropdown, Empty, EmptyState, EntityHeader, ErrorProvider, Fieldset, FilterCheckboxInput, FilterDateInput, FilterDateRangePopover, FilterNumberInput, FilterSelectGroupPopover, FilterSelectInput, FilterTextInput, FlagTag, FormActions, FormSection, Grid, GridFilters, HtmlInput, InlineEdit, InputError, InputField, InputLabel, KeyValue, Modal, ModalContent, ModalFooter, ModalTitle, MultiSelectInput, NotificationProvider, NumberInput, PageHeader, Panel, Popconfirm, Pretty, RadioGroup, SearchInput, SectionHeader, SelectInput, ServerError, Skeleton, SkeletonTable, SortDirection, Spinner, StatCard, Stepper, SwitchInput, Table, Tabs, Tag, TextInput, Textarea, Timeline, Toolbar, Tooltip, UploadInput, UploadProvider, getIcon, initUI, notification, registerIcons, uiTheme, useDrawer, useGrid, useModal, useNotification, useUploadConfig };
4506
4913
  //# sourceMappingURL=index.js.map
4507
4914
  //# sourceMappingURL=index.js.map