@martinsura/ui 0.1.3 → 0.1.4

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,31 +3540,214 @@ 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(
3718
+ children: props.options.map((opt) => /* @__PURE__ */ jsxs(
3401
3719
  "button",
3402
3720
  {
3403
3721
  type: "button",
3404
- onClick: () => props.onChange(opt.value),
3722
+ role: "tab",
3723
+ "aria-selected": props.value === opt.value,
3724
+ disabled: opt.disabled,
3725
+ onClick: () => !opt.disabled && props.onChange(opt.value),
3405
3726
  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)",
3727
+ "flex-1 min-w-0 transition-all duration-150 cursor-pointer select-none whitespace-nowrap",
3728
+ "inline-flex items-center justify-center gap-2",
3729
+ tabSizeClass[size],
3730
+ tabVariantClass[variant],
3731
+ props.value === opt.value ? activeVariantClass[variant] : inactiveVariantClass[variant],
3732
+ opt.disabled && "cursor-not-allowed opacity-45 hover:border-(--ui-border) hover:text-(--ui-text-muted)",
3408
3733
  props.classNames?.tab,
3409
3734
  props.value === opt.value ? props.classNames?.activeTab : props.classNames?.inactiveTab
3410
3735
  ),
3411
- children: opt.label
3736
+ children: [
3737
+ opt.icon && /* @__PURE__ */ jsx("span", { className: twMerge("shrink-0", props.classNames?.icon), children: opt.icon }),
3738
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: opt.label }),
3739
+ opt.badge && /* @__PURE__ */ jsx(
3740
+ "span",
3741
+ {
3742
+ className: twMerge(
3743
+ "shrink-0 rounded-full px-2 py-0.5 text-[11px] leading-none",
3744
+ props.value === opt.value ? "bg-(--ui-primary) text-(--ui-primary-text)" : "bg-(--ui-surface-muted) text-(--ui-text-muted)",
3745
+ props.classNames?.badge
3746
+ ),
3747
+ children: opt.badge
3748
+ }
3749
+ )
3750
+ ]
3412
3751
  },
3413
3752
  opt.value
3414
3753
  ))
@@ -3490,68 +3829,6 @@ var FlagTag = ({ value, label, color = "geekblue", variant }) => {
3490
3829
  }
3491
3830
  return /* @__PURE__ */ jsx(Tag, { color, variant, children: label });
3492
3831
  };
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
3832
  var defaultToolbar = [
3556
3833
  "bold",
3557
3834
  "italic",
@@ -4225,6 +4502,88 @@ var useNotification = () => {
4225
4502
  info: (message, options) => add("info", message, options)
4226
4503
  };
4227
4504
  };
4505
+ var EmptyState = ({
4506
+ primaryActionText,
4507
+ onPrimaryAction,
4508
+ secondaryActionText,
4509
+ onSecondaryAction,
4510
+ extra,
4511
+ className,
4512
+ classNames,
4513
+ ...props
4514
+ }) => /* @__PURE__ */ jsxs("div", { className: twMerge("rounded-(--ui-radius-lg) border border-(--ui-border) bg-white", className), children: [
4515
+ /* @__PURE__ */ jsx(Empty, { className: "py-10", ...props }),
4516
+ (primaryActionText || secondaryActionText || extra) && /* @__PURE__ */ jsxs("div", { className: twMerge("flex flex-wrap items-center justify-center gap-2 px-4 pb-6", classNames?.actions), children: [
4517
+ secondaryActionText && /* @__PURE__ */ jsx(Button, { variant: "default", text: secondaryActionText, onClick: onSecondaryAction }),
4518
+ primaryActionText && /* @__PURE__ */ jsx(Button, { text: primaryActionText, onClick: onPrimaryAction }),
4519
+ extra && /* @__PURE__ */ jsx("div", { className: classNames?.extra, children: extra })
4520
+ ] })
4521
+ ] });
4522
+ var trendConfig = {
4523
+ up: {
4524
+ icon: IconArrowUpRight,
4525
+ className: "text-(--ui-success)"
4526
+ },
4527
+ down: {
4528
+ icon: IconArrowDownRight,
4529
+ className: "text-(--ui-danger)"
4530
+ },
4531
+ neutral: {
4532
+ icon: IconMinus,
4533
+ className: neutralTextClasses.soft
4534
+ }
4535
+ };
4536
+ var StatCard = ({ label, value, description, icon: Icon, trend, footer, className }) => {
4537
+ const trendDirection = trend?.direction ?? "neutral";
4538
+ const TrendIcon = trend ? trendConfig[trendDirection].icon : null;
4539
+ return /* @__PURE__ */ jsxs("div", { className: twMerge("rounded-(--ui-radius-lg) border border-(--ui-border) bg-white p-4 shadow-sm", className), children: [
4540
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3", children: [
4541
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
4542
+ /* @__PURE__ */ jsx("p", { className: twMerge("text-sm", neutralTextClasses.muted), children: label }),
4543
+ /* @__PURE__ */ jsx("p", { className: "mt-2 text-2xl font-semibold text-(--ui-text-strong)", children: value }),
4544
+ description && /* @__PURE__ */ jsx("p", { className: twMerge("mt-1 text-xs", neutralTextClasses.soft), children: description })
4545
+ ] }),
4546
+ 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 }) })
4547
+ ] }),
4548
+ trend && /* @__PURE__ */ jsxs("div", { className: "mt-4 flex items-center gap-2 text-sm", children: [
4549
+ TrendIcon && /* @__PURE__ */ jsx(TrendIcon, { size: 15, strokeWidth: 2, className: trendConfig[trendDirection].className }),
4550
+ /* @__PURE__ */ jsx("span", { className: twMerge("font-medium", trendConfig[trendDirection].className), children: trend.value }),
4551
+ trend.label && /* @__PURE__ */ jsx("span", { className: neutralTextClasses.soft, children: trend.label })
4552
+ ] }),
4553
+ footer && /* @__PURE__ */ jsx("div", { className: "mt-4 border-t border-(--ui-border) pt-3", children: footer })
4554
+ ] });
4555
+ };
4556
+ var DescriptionList = ({ items, columns = 2, className }) => /* @__PURE__ */ jsx(
4557
+ "div",
4558
+ {
4559
+ className: twMerge(
4560
+ "grid gap-x-6 gap-y-4 rounded-(--ui-radius-lg) border border-(--ui-border) bg-white p-4",
4561
+ columns === 1 && "grid-cols-1",
4562
+ columns === 2 && "grid-cols-1 md:grid-cols-2",
4563
+ columns === 3 && "grid-cols-1 md:grid-cols-3",
4564
+ className
4565
+ ),
4566
+ children: items.map((item, index) => /* @__PURE__ */ jsxs(
4567
+ "div",
4568
+ {
4569
+ className: twMerge(
4570
+ "min-w-0",
4571
+ item.span === 2 && columns > 1 && "md:col-span-2",
4572
+ item.span === 3 && columns > 2 && "md:col-span-3"
4573
+ ),
4574
+ children: [
4575
+ /* @__PURE__ */ jsx("dt", { className: twMerge("text-xs font-medium uppercase tracking-[0.08em]", neutralTextClasses.soft), children: item.label }),
4576
+ /* @__PURE__ */ jsx("dd", { className: "mt-1 text-sm text-(--ui-text)", children: item.value })
4577
+ ]
4578
+ },
4579
+ index
4580
+ ))
4581
+ }
4582
+ );
4583
+ 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: [
4584
+ /* @__PURE__ */ jsx("span", { className: twMerge("text-sm", neutralTextClasses.muted), children: label }),
4585
+ /* @__PURE__ */ jsx("span", { className: "text-right text-sm text-(--ui-text)", children: value })
4586
+ ] });
4228
4587
  var Pretty = ({ data, className }) => /* @__PURE__ */ jsx(
4229
4588
  "pre",
4230
4589
  {
@@ -4235,6 +4594,10 @@ var Pretty = ({ data, className }) => /* @__PURE__ */ jsx(
4235
4594
  children: JSON.stringify(data, null, 2)
4236
4595
  }
4237
4596
  );
4597
+ 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: [
4598
+ /* @__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)) }),
4599
+ /* @__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)) })
4600
+ ] }) });
4238
4601
  var GAP2 = 10;
4239
4602
  var VIEWPORT_MARGIN2 = 8;
4240
4603
  function calcPosition3(trigger, tooltip, placement) {
@@ -4389,6 +4752,47 @@ var Tooltip = ({
4389
4752
  )
4390
4753
  ] });
4391
4754
  };
4755
+ var Stepper = ({ items, className }) => /* @__PURE__ */ jsx("div", { className: twMerge("flex flex-wrap gap-4", className), children: items.map((item, index) => {
4756
+ const status = item.status ?? "upcoming";
4757
+ return /* @__PURE__ */ jsxs("div", { className: "flex min-w-40 flex-1 items-start gap-3", children: [
4758
+ /* @__PURE__ */ jsx(
4759
+ "div",
4760
+ {
4761
+ className: twMerge(
4762
+ "mt-0.5 flex h-7 w-7 shrink-0 items-center justify-center rounded-full border text-xs font-semibold",
4763
+ status === "complete" && "border-(--ui-success) bg-(--ui-success) text-white",
4764
+ status === "current" && "border-(--ui-primary) bg-(--ui-primary) text-white",
4765
+ status === "upcoming" && "border-(--ui-border) bg-white text-(--ui-text-soft)"
4766
+ ),
4767
+ children: status === "complete" ? /* @__PURE__ */ jsx(IconCheck, { size: 14, strokeWidth: 2.5 }) : index + 1
4768
+ }
4769
+ ),
4770
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
4771
+ /* @__PURE__ */ jsx("div", { className: twMerge("text-sm font-medium", status === "upcoming" ? neutralTextClasses.muted : "text-(--ui-text-strong)"), children: item.title }),
4772
+ item.description && /* @__PURE__ */ jsx("div", { className: twMerge("mt-1 text-xs", neutralTextClasses.soft), children: item.description })
4773
+ ] })
4774
+ ] }, index);
4775
+ }) });
4776
+ var colorClass = {
4777
+ primary: "bg-(--ui-primary)",
4778
+ success: "bg-(--ui-success)",
4779
+ danger: "bg-(--ui-danger)",
4780
+ warning: "bg-(--ui-warning)",
4781
+ neutral: "bg-(--ui-border-strong)"
4782
+ };
4783
+ 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: [
4784
+ /* @__PURE__ */ jsxs("div", { className: "relative flex flex-col items-center", children: [
4785
+ /* @__PURE__ */ jsx("span", { className: twMerge("mt-1 h-2.5 w-2.5 rounded-full", colorClass[item.color ?? "primary"]) }),
4786
+ index < items.length - 1 && /* @__PURE__ */ jsx("span", { className: "mt-1 h-full w-px bg-(--ui-border)" })
4787
+ ] }),
4788
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 pb-4", children: [
4789
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
4790
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-medium text-(--ui-text-strong)", children: item.title }),
4791
+ item.time && /* @__PURE__ */ jsx("div", { className: twMerge("text-xs", neutralTextClasses.soft), children: item.time })
4792
+ ] }),
4793
+ item.description && /* @__PURE__ */ jsx("div", { className: twMerge("mt-1 text-sm", neutralTextClasses.muted), children: item.description })
4794
+ ] })
4795
+ ] }, index)) });
4392
4796
 
4393
4797
  // src/initUI.ts
4394
4798
  function initUI(config) {
@@ -4502,6 +4906,6 @@ function initUI(config) {
4502
4906
  r.style.setProperty("--ui-empty-compact-description", layout.empty.compactDescription);
4503
4907
  }
4504
4908
 
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 };
4909
+ 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
4910
  //# sourceMappingURL=index.js.map
4507
4911
  //# sourceMappingURL=index.js.map