@wellingtonhlc/shared-ui 0.29.0 → 0.29.2

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.
Files changed (153) hide show
  1. package/README.md +25 -10
  2. package/dist/cli/check-page-actions.d.ts +9 -0
  3. package/dist/cli/check-page-actions.d.ts.map +1 -0
  4. package/dist/cli/check-page-actions.js +108 -0
  5. package/dist/components/ActionBar.d.ts +26 -0
  6. package/dist/components/ActionBar.d.ts.map +1 -0
  7. package/dist/components/ActionBar.js +29 -0
  8. package/dist/components/ActionPrimitives.d.ts +31 -0
  9. package/dist/components/ActionPrimitives.d.ts.map +1 -0
  10. package/dist/components/ActionPrimitives.js +27 -0
  11. package/dist/components/AppShell.d.ts +65 -0
  12. package/dist/components/AppShell.d.ts.map +1 -0
  13. package/dist/components/AppShell.js +50 -0
  14. package/dist/components/AppShellActions.d.ts +24 -0
  15. package/dist/components/AppShellActions.d.ts.map +1 -0
  16. package/dist/components/AppShellActions.js +15 -0
  17. package/dist/components/Badge.d.ts +16 -0
  18. package/dist/components/Badge.d.ts.map +1 -0
  19. package/dist/components/Badge.js +36 -0
  20. package/dist/components/Banner.d.ts +13 -0
  21. package/dist/components/Banner.d.ts.map +1 -0
  22. package/dist/components/Banner.js +29 -0
  23. package/dist/components/Button.d.ts +14 -0
  24. package/dist/components/Button.d.ts.map +1 -0
  25. package/dist/components/Button.js +39 -0
  26. package/dist/components/Card.d.ts +36 -0
  27. package/dist/components/Card.d.ts.map +1 -0
  28. package/dist/components/Card.js +56 -0
  29. package/dist/components/ConfirmationDialog.d.ts +74 -0
  30. package/dist/components/ConfirmationDialog.d.ts.map +1 -0
  31. package/dist/components/ConfirmationDialog.js +234 -0
  32. package/dist/components/CopyableField.d.ts +10 -0
  33. package/dist/components/CopyableField.d.ts.map +1 -0
  34. package/dist/components/CopyableField.js +60 -0
  35. package/dist/components/DateField.d.ts +20 -0
  36. package/dist/components/DateField.d.ts.map +1 -0
  37. package/dist/components/DateField.js +172 -0
  38. package/dist/components/DecimalField.d.ts +21 -0
  39. package/dist/components/DecimalField.d.ts.map +1 -0
  40. package/dist/components/DecimalField.js +137 -0
  41. package/dist/components/DismissiblePopover.d.ts +14 -0
  42. package/dist/components/DismissiblePopover.d.ts.map +1 -0
  43. package/dist/components/DismissiblePopover.js +51 -0
  44. package/dist/components/EmptyState.d.ts +15 -0
  45. package/dist/components/EmptyState.d.ts.map +1 -0
  46. package/dist/components/EmptyState.js +32 -0
  47. package/dist/components/FieldControl.d.ts +20 -0
  48. package/dist/components/FieldControl.d.ts.map +1 -0
  49. package/dist/components/FieldControl.js +73 -0
  50. package/dist/components/FieldGroup.d.ts +11 -0
  51. package/dist/components/FieldGroup.d.ts.map +1 -0
  52. package/dist/components/FieldGroup.js +11 -0
  53. package/dist/components/FieldSkeleton.d.ts +14 -0
  54. package/dist/components/FieldSkeleton.d.ts.map +1 -0
  55. package/dist/components/FieldSkeleton.js +23 -0
  56. package/dist/components/FieldValidationError.d.ts +8 -0
  57. package/dist/components/FieldValidationError.d.ts.map +1 -0
  58. package/dist/components/FieldValidationError.js +7 -0
  59. package/dist/components/Filter.d.ts +51 -0
  60. package/dist/components/Filter.d.ts.map +1 -0
  61. package/dist/components/Filter.js +68 -0
  62. package/dist/components/Loading.d.ts +17 -0
  63. package/dist/components/Loading.d.ts.map +1 -0
  64. package/dist/components/Loading.js +15 -0
  65. package/dist/components/LoadingOverlay.d.ts +11 -0
  66. package/dist/components/LoadingOverlay.d.ts.map +1 -0
  67. package/dist/components/LoadingOverlay.js +8 -0
  68. package/dist/components/Modal.d.ts +49 -0
  69. package/dist/components/Modal.d.ts.map +1 -0
  70. package/dist/components/Modal.js +108 -0
  71. package/dist/components/MultiSelectField.d.ts +22 -0
  72. package/dist/components/MultiSelectField.d.ts.map +1 -0
  73. package/dist/components/MultiSelectField.js +84 -0
  74. package/dist/components/NavCard.d.ts +15 -0
  75. package/dist/components/NavCard.d.ts.map +1 -0
  76. package/dist/components/NavCard.js +13 -0
  77. package/dist/components/Page.d.ts +89 -0
  78. package/dist/components/Page.d.ts.map +1 -0
  79. package/dist/components/Page.js +347 -0
  80. package/dist/components/PageMessage.d.ts +12 -0
  81. package/dist/components/PageMessage.d.ts.map +1 -0
  82. package/dist/components/PageMessage.js +36 -0
  83. package/dist/components/Pagination.d.ts +15 -0
  84. package/dist/components/Pagination.d.ts.map +1 -0
  85. package/dist/components/Pagination.js +57 -0
  86. package/dist/components/PasswordInput.d.ts +8 -0
  87. package/dist/components/PasswordInput.d.ts.map +1 -0
  88. package/dist/components/PasswordInput.js +17 -0
  89. package/dist/components/RadioGroup.d.ts +32 -0
  90. package/dist/components/RadioGroup.d.ts.map +1 -0
  91. package/dist/components/RadioGroup.js +86 -0
  92. package/dist/components/RenderCase.d.ts +20 -0
  93. package/dist/components/RenderCase.d.ts.map +1 -0
  94. package/dist/components/RenderCase.js +31 -0
  95. package/dist/components/RenderIf.d.ts +10 -0
  96. package/dist/components/RenderIf.d.ts.map +1 -0
  97. package/dist/components/RenderIf.js +9 -0
  98. package/dist/components/SearchField.d.ts +22 -0
  99. package/dist/components/SearchField.d.ts.map +1 -0
  100. package/dist/components/SearchField.js +43 -0
  101. package/dist/components/SelectField.d.ts +31 -0
  102. package/dist/components/SelectField.d.ts.map +1 -0
  103. package/dist/components/SelectField.js +26 -0
  104. package/dist/components/SelectionField.d.ts +39 -0
  105. package/dist/components/SelectionField.d.ts.map +1 -0
  106. package/dist/components/SelectionField.js +105 -0
  107. package/dist/components/Sidebar.d.ts +104 -0
  108. package/dist/components/Sidebar.d.ts.map +1 -0
  109. package/dist/components/Sidebar.js +146 -0
  110. package/dist/components/StatCard.d.ts +16 -0
  111. package/dist/components/StatCard.d.ts.map +1 -0
  112. package/dist/components/StatCard.js +62 -0
  113. package/dist/components/Switch.d.ts +20 -0
  114. package/dist/components/Switch.d.ts.map +1 -0
  115. package/dist/components/Switch.js +63 -0
  116. package/dist/components/Table.d.ts +65 -0
  117. package/dist/components/Table.d.ts.map +1 -0
  118. package/dist/components/Table.js +118 -0
  119. package/dist/components/TabsUnderlined.d.ts +23 -0
  120. package/dist/components/TabsUnderlined.d.ts.map +1 -0
  121. package/dist/components/TabsUnderlined.js +29 -0
  122. package/dist/components/TextField.d.ts +25 -0
  123. package/dist/components/TextField.d.ts.map +1 -0
  124. package/dist/components/TextField.js +71 -0
  125. package/dist/components/TextareaField.d.ts +14 -0
  126. package/dist/components/TextareaField.d.ts.map +1 -0
  127. package/dist/components/TextareaField.js +34 -0
  128. package/dist/components/ThemePreferencesSelector.d.ts +29 -0
  129. package/dist/components/ThemePreferencesSelector.d.ts.map +1 -0
  130. package/dist/components/ThemePreferencesSelector.js +37 -0
  131. package/dist/components/Tooltip.d.ts +13 -0
  132. package/dist/components/Tooltip.d.ts.map +1 -0
  133. package/dist/components/Tooltip.js +40 -0
  134. package/dist/components/Workspace.d.ts +22 -0
  135. package/dist/components/Workspace.d.ts.map +1 -0
  136. package/dist/components/Workspace.js +33 -0
  137. package/dist/components/WorkspaceContext.d.ts +2 -0
  138. package/dist/components/WorkspaceContext.d.ts.map +1 -0
  139. package/dist/components/WorkspaceContext.js +2 -0
  140. package/dist/components/form-control-helpers.d.ts +24 -0
  141. package/dist/components/form-control-helpers.d.ts.map +1 -0
  142. package/dist/components/form-control-helpers.js +20 -0
  143. package/dist/index.d.ts +45 -0
  144. package/dist/index.d.ts.map +1 -0
  145. package/dist/index.js +44 -0
  146. package/dist/styles.css +26 -43
  147. package/dist/tailwind-preset.d.ts +23 -0
  148. package/dist/tailwind-preset.d.ts.map +1 -0
  149. package/dist/tailwind-preset.js +22 -0
  150. package/dist/utils/cn.d.ts +2 -0
  151. package/dist/utils/cn.d.ts.map +1 -0
  152. package/dist/utils/cn.js +33 -0
  153. package/package.json +1 -1
@@ -0,0 +1,146 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { forwardRef, useEffect, useMemo, useRef, useState } from 'react';
3
+ import { cn } from '../utils/cn';
4
+ const Root = forwardRef(function SidebarRoot({ ariaLabel = 'Menu lateral', children, className, desktopWidthClass = 'lg:w-72', isOpen, mobileWidthClass = 'w-72', }, ref) {
5
+ return (_jsx("aside", { ref: ref, "aria-label": ariaLabel, "data-open": isOpen, className: cn('border-app-border bg-[var(--sidebar-background)] fixed inset-y-0 left-0 z-[var(--z-navigation)] flex h-full w-20 shrink-0 -translate-x-full flex-col justify-between overflow-visible border-r px-2 transition-[width,transform] duration-[var(--duration-slow)] ease-in-out data-[open=true]:w-4/5 data-[open=true]:translate-x-0 md:static md:z-[var(--z-navigation-desktop)] md:w-20 md:translate-x-0 md:shadow-none md:data-[open=true]:w-72', isOpen && `${mobileWidthClass} ${desktopWidthClass}`, className), children: children }));
6
+ });
7
+ function Content({ ariaLabel = 'Navegação principal', children, className }) {
8
+ return (_jsx("nav", { "aria-label": ariaLabel, className: cn('flex min-h-0 w-full flex-1 flex-col', className), children: children }));
9
+ }
10
+ function Header({ children, className }) {
11
+ return _jsx("div", { className: cn('flex h-16 w-full flex-none items-center', className), children: children });
12
+ }
13
+ function List({ children, className, isOpen }) {
14
+ const listRef = useRef(null);
15
+ useEffect(() => {
16
+ if (!isOpen)
17
+ return;
18
+ const activeItem = listRef.current?.querySelector('[data-sidebar-active="true"]');
19
+ activeItem?.scrollIntoView({ block: 'nearest' });
20
+ }, [children, isOpen]);
21
+ return (_jsx("ul", { ref: listRef, className: cn('flex min-h-0 w-full flex-1 flex-col gap-2', isOpen ? 'overflow-y-auto overflow-x-hidden' : 'overflow-visible', className), children: children }));
22
+ }
23
+ function Footer({ children, className }) {
24
+ return _jsx("footer", { className: cn('border-app-border flex w-full flex-col border-t py-2', className), children: children });
25
+ }
26
+ function Separator({ className, flush = false }) {
27
+ return (_jsx("div", { "aria-hidden": "true", className: cn('border-app-border w-full border-t', flush && 'my-0', className) }));
28
+ }
29
+ const ToggleButton = forwardRef(function SidebarToggleButton({ className, closedLabel = 'Abrir menu lateral', icon, isOpen, onToggle, openLabel = 'Recolher menu lateral' }, ref) {
30
+ return (_jsx("button", { ref: ref, type: "button", onClick: onToggle, "aria-label": isOpen ? openLabel : closedLabel, "aria-expanded": isOpen, "data-open": isOpen, className: cn('text-foreground-muted focus-visible:ring-brand/35 flex w-full flex-none cursor-pointer items-center rounded-xl bg-transparent transition-[background-color,color] focus-visible:ring-2 focus-visible:outline-none hover:bg-[var(--sidebar-item-hover)] hover:text-foreground', className), children: _jsx("span", { className: "grid w-16 min-w-16 flex-none place-items-center py-2", children: icon }) }));
31
+ });
32
+ const ActionButton = forwardRef(function SidebarActionButton({ active = false, className, expanded = false, icon, isOpen, label, labelClassName, showChevron = false, size = 'default', title, tone = 'nav', type = 'button', ...props }, ref) {
33
+ return (_jsxs("button", { ref: ref, type: type, title: title ? (typeof label === 'string' && !isOpen ? label : undefined) : undefined, "data-open": isOpen, "data-sidebar-active": active ? 'true' : undefined, className: cn(sidebarButtonClasses(active, size, tone), showChevron && isOpen && 'pr-2', className), ...props, children: [_jsx("span", { className: cn('grid w-16 min-w-16 flex-none place-items-center transition-colors', size === 'compact' ? 'h-9' : 'h-11', active ? 'text-current' : 'text-current'), children: icon }), _jsx("span", { className: cn('min-w-0 flex-1 truncate text-left font-medium whitespace-nowrap transition-opacity duration-[var(--duration-normal)]', size === 'compact' ? 'text-xs max-md:text-base' : 'text-[length:var(--font-size-compact)] max-md:text-base', isOpen ? 'opacity-100' : 'pointer-events-none w-0 opacity-0', labelClassName), children: label }), _jsx("span", { className: cn('flex-none place-items-center', isOpen ? 'grid' : 'hidden', size === 'compact' ? 'h-9 w-6' : 'h-11 w-7', showChevron && isOpen ? 'opacity-100' : 'opacity-0'), children: _jsx(ChevronIcon, { expanded: expanded }) })] }));
34
+ });
35
+ const SubActionButton = forwardRef(function SidebarSubActionButton({ active = false, className, isOpen, label, size = 'compact', title, type = 'button', ...props }, ref) {
36
+ return (_jsx("button", { ref: ref, type: type, title: title ? (typeof label === 'string' && !isOpen ? label : undefined) : undefined, "data-sidebar-active": active ? 'true' : undefined, className: cn('focus-visible:ring-brand/35 flex w-full cursor-pointer items-center rounded-xl font-medium transition-colors focus-visible:ring-2 focus-visible:outline-none', size === 'compact' ? 'h-9 min-h-9 text-xs' : 'h-11 min-h-11 text-[length:var(--font-size-compact)]', isOpen ? 'justify-start pl-16' : 'justify-center', subActionButtonTone(active, size), className), ...props, children: _jsx("span", { className: cn('truncate', isOpen ? 'opacity-100' : 'sr-only'), children: label }) }));
37
+ });
38
+ function NavGroup({ active = false, className, collapsible = false, defaultExpanded, icon, isOpen, items, subItemSize = 'compact', title, }) {
39
+ const itemByKey = useMemo(() => new Map(items.map((item) => [item.key, item])), [items]);
40
+ const [expanded, setExpanded] = useState(defaultExpanded ?? (active ? !collapsible : false));
41
+ const [hovered, setHovered] = useState(false);
42
+ const [labelVisible, setLabelVisible] = useState(isOpen);
43
+ const openHoverTimerRef = useRef(null);
44
+ const closeHoverTimerRef = useRef(null);
45
+ const groupActive = active || items.some((item) => item.active);
46
+ const singleItem = !collapsible && items.length === 1 ? items[0] : null;
47
+ useEffect(() => {
48
+ if (isOpen) {
49
+ setLabelVisible(true);
50
+ return undefined;
51
+ }
52
+ setLabelVisible(false);
53
+ return undefined;
54
+ }, [isOpen]);
55
+ useEffect(() => {
56
+ if (groupActive) {
57
+ setExpanded(true);
58
+ }
59
+ }, [groupActive]);
60
+ useEffect(() => () => {
61
+ clearHoverTimer(openHoverTimerRef);
62
+ clearHoverTimer(closeHoverTimerRef);
63
+ }, []);
64
+ function handleSingleItemClick(event) {
65
+ singleItem?.onSelect?.(event);
66
+ }
67
+ function handleGroupClick(event) {
68
+ if (!isOpen) {
69
+ items[0]?.onSelect?.(event);
70
+ return;
71
+ }
72
+ if (collapsible) {
73
+ setExpanded((current) => !current);
74
+ }
75
+ }
76
+ function handleMouseEnter() {
77
+ clearHoverTimer(closeHoverTimerRef);
78
+ clearHoverTimer(openHoverTimerRef);
79
+ openHoverTimerRef.current = setTimeout(() => setHovered(true), 280);
80
+ }
81
+ function handleMouseLeave() {
82
+ clearHoverTimer(openHoverTimerRef);
83
+ clearHoverTimer(closeHoverTimerRef);
84
+ closeHoverTimerRef.current = setTimeout(() => setHovered(false), 350);
85
+ }
86
+ function handleItemSelect(event) {
87
+ const key = event.currentTarget.dataset.sidebarItemKey;
88
+ const item = key ? itemByKey.get(key) : undefined;
89
+ item?.onSelect?.(event);
90
+ setHovered(false);
91
+ }
92
+ if (singleItem) {
93
+ return (_jsx("li", { className: cn('w-full', className), children: _jsx(ActionButton, { active: singleItem.active, icon: singleItem.icon ?? icon, isOpen: isOpen, label: singleItem.label, onClick: handleSingleItemClick, title: singleItem.title, tone: "subtle" }) }));
94
+ }
95
+ return (_jsxs("li", { className: cn('relative w-full', className), onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, children: [_jsx(ActionButton, { active: groupActive, "aria-expanded": isOpen ? expanded : undefined, icon: icon, isOpen: isOpen, label: title, labelClassName: labelVisible ? 'opacity-100' : 'opacity-0', onClick: handleGroupClick, expanded: expanded, showChevron: collapsible && isOpen, tone: "subtle" }), isOpen ? (_jsx("div", { className: "grid grid-rows-[0fr] opacity-0 transition-[grid-template-rows,opacity] duration-[var(--duration-normal)] ease-out data-[expanded=true]:grid-rows-[1fr] data-[expanded=true]:opacity-100", "data-expanded": expanded, children: _jsx("div", { className: "relative min-h-0 overflow-hidden", children: _jsx("ul", { className: "flex w-full flex-col", children: items.map((item, index) => (_jsxs("li", { className: "relative w-full", children: [_jsx("span", { "aria-hidden": "true", className: cn('bg-app-border absolute left-8 top-0 w-px', index === items.length - 1 ? 'h-1/2' : 'bottom-0') }), _jsx("span", { "aria-hidden": "true", className: "bg-app-border absolute left-8 top-1/2 h-px w-5" }), _jsx(SubActionButton, { active: item.active, "data-sidebar-item-key": item.key, isOpen: isOpen, label: item.label, onClick: handleItemSelect, size: subItemSize, title: item.title })] }, item.key))) }) }) })) : (_jsx(Flyout, { open: hovered, title: title, items: items, onItemSelect: handleItemSelect }))] }));
96
+ }
97
+ function Flyout({ items, onItemSelect, open, title }) {
98
+ return (_jsxs("div", { className: "border-app-border bg-[var(--sidebar-flyout-background)] invisible pointer-events-none absolute left-[calc(100%+0.375rem)] top-0 z-[var(--z-flyout)] min-w-52 -translate-x-2 overflow-hidden rounded-xl border p-2 opacity-0 shadow-[var(--shadow-flyout)] transition-[opacity,transform,visibility] duration-[var(--duration-fast)] data-[open=true]:visible data-[open=true]:pointer-events-auto data-[open=true]:translate-x-0 data-[open=true]:opacity-100", "data-open": open, children: [_jsx("span", { "aria-hidden": "true", className: "absolute right-full top-0 h-full w-2.5" }), _jsx("p", { className: "text-foreground-muted mb-2 text-[length:var(--font-size-caption)] font-semibold uppercase tracking-[var(--letter-spacing-caps)]", children: title }), _jsx("div", { className: "flex max-h-[calc(100vh-5rem)] flex-col overflow-y-auto", children: items.map((item) => (_jsxs("button", { type: "button", "data-sidebar-item-key": item.key, onClick: onItemSelect, className: cn('group focus-visible:ring-brand/35 flex h-9 min-h-9 w-full cursor-pointer items-center gap-2 rounded-lg text-left text-xs font-medium leading-none transition-colors focus-visible:ring-2 focus-visible:outline-none', item.active
99
+ ? '!bg-transparent text-foreground font-semibold'
100
+ : '!bg-transparent text-foreground-muted hover:text-foreground hover:font-semibold'), children: [_jsx("span", { className: "grid h-3.5 w-3.5 flex-none place-items-center text-current transition-colors [&>svg]:h-3.5 [&>svg]:w-3.5", children: item.icon }), _jsx("span", { className: "flex min-h-4 items-center leading-none", children: item.label })] }, item.key))) })] }));
101
+ }
102
+ function subActionButtonTone(active, size) {
103
+ if (size === 'compact') {
104
+ return active
105
+ ? '!bg-transparent text-foreground font-semibold'
106
+ : '!bg-transparent text-foreground-muted hover:text-foreground hover:font-semibold';
107
+ }
108
+ return active
109
+ ? 'bg-transparent text-foreground font-semibold'
110
+ : 'text-foreground-muted hover:text-foreground hover:font-semibold';
111
+ }
112
+ function sidebarButtonClasses(active, size, tone) {
113
+ return cn('group focus-visible:ring-brand/35 flex w-full cursor-pointer items-center rounded-xl bg-transparent text-left transition-colors focus-visible:ring-2 focus-visible:outline-none', size === 'compact' ? 'h-9 min-h-9' : 'h-11 min-h-11', actionButtonTone(active, tone));
114
+ }
115
+ function actionButtonTone(active, tone) {
116
+ if (tone === 'subtle') {
117
+ return active
118
+ ? '!bg-[var(--sidebar-item-active)] text-foreground font-semibold hover:!bg-[var(--sidebar-item-hover)]'
119
+ : '!bg-transparent text-foreground-muted hover:!bg-[var(--sidebar-item-hover)] hover:text-foreground hover:font-semibold';
120
+ }
121
+ return active
122
+ ? 'bg-[var(--sidebar-neutral-active)] text-foreground font-semibold'
123
+ : 'text-foreground-muted hover:bg-[var(--sidebar-neutral-hover)] hover:text-foreground';
124
+ }
125
+ function clearHoverTimer(timerRef) {
126
+ if (timerRef.current) {
127
+ clearTimeout(timerRef.current);
128
+ timerRef.current = null;
129
+ }
130
+ }
131
+ function ChevronIcon({ expanded }) {
132
+ return (_jsx("svg", { "aria-hidden": "true", viewBox: "0 0 20 20", className: cn('h-4 w-4 transition-transform duration-[var(--duration-normal)]', expanded && 'rotate-180'), fill: "none", stroke: "currentColor", strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "1.8", children: _jsx("path", { d: "m6 8 4 4 4-4" }) }));
133
+ }
134
+ export const Sidebar = {
135
+ Root,
136
+ Content,
137
+ Header,
138
+ List,
139
+ Footer,
140
+ Separator,
141
+ ToggleButton,
142
+ ActionButton,
143
+ SubActionButton,
144
+ NavGroup,
145
+ Flyout,
146
+ };
@@ -0,0 +1,16 @@
1
+ import { type ReactNode } from 'react';
2
+ export type StatCardVariant = 'default' | 'success' | 'info' | 'warning' | 'danger';
3
+ export type StatCardSize = 'sm' | 'md' | 'lg';
4
+ export interface StatCardProps<TValue> {
5
+ className?: string;
6
+ description?: ReactNode;
7
+ format?: (value: TValue) => ReactNode;
8
+ icon?: ReactNode;
9
+ label: ReactNode;
10
+ size?: StatCardSize;
11
+ value: TValue;
12
+ variant?: StatCardVariant;
13
+ valueClassName?: string;
14
+ }
15
+ export declare function StatCard<TValue>({ className, description, format, icon, label, size, value, variant, valueClassName, }: StatCardProps<TValue>): import("react").JSX.Element;
16
+ //# sourceMappingURL=StatCard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StatCard.d.ts","sourceRoot":"","sources":["../../src/components/StatCard.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAIvC,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAC;AACpF,MAAM,MAAM,YAAY,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAE9C,MAAM,WAAW,aAAa,CAAC,MAAM;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,SAAS,CAAC;IACtC,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,KAAK,EAAE,SAAS,CAAC;IACjB,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAmED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,EAC/B,SAAS,EACT,WAAW,EACX,MAAM,EACN,IAAI,EACJ,KAAK,EACL,IAAW,EACX,KAAK,EACL,OAAmB,EACnB,cAAc,GACf,EAAE,aAAa,CAAC,MAAM,CAAC,+BAiCvB"}
@@ -0,0 +1,62 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn } from '../utils/cn';
3
+ const variantClasses = {
4
+ default: {
5
+ container: 'app-stat-card--default shadow-sm',
6
+ label: 'app-stat-card__label--default',
7
+ value: 'app-stat-card__value--default',
8
+ description: 'app-stat-card__description--default',
9
+ icon: 'app-stat-card__icon--default',
10
+ },
11
+ success: {
12
+ container: 'app-stat-card--success shadow-sm',
13
+ label: 'app-stat-card__label--success',
14
+ value: 'app-stat-card__value--success',
15
+ description: 'app-stat-card__description--success',
16
+ icon: 'app-stat-card__icon--success',
17
+ },
18
+ info: {
19
+ container: 'app-stat-card--info shadow-sm',
20
+ label: 'app-stat-card__label--info',
21
+ value: 'app-stat-card__value--info',
22
+ description: 'app-stat-card__description--info',
23
+ icon: 'app-stat-card__icon--info',
24
+ },
25
+ warning: {
26
+ container: 'app-stat-card--warning shadow-sm',
27
+ label: 'app-stat-card__label--warning',
28
+ value: 'app-stat-card__value--warning',
29
+ description: 'app-stat-card__description--warning',
30
+ icon: 'app-stat-card__icon--warning',
31
+ },
32
+ danger: {
33
+ container: 'app-stat-card--danger shadow-sm',
34
+ label: 'app-stat-card__label--danger',
35
+ value: 'app-stat-card__value--danger',
36
+ description: 'app-stat-card__description--danger',
37
+ icon: 'app-stat-card__icon--danger',
38
+ },
39
+ };
40
+ const sizeClasses = {
41
+ sm: {
42
+ container: 'px-3 py-3',
43
+ icon: 'size-4',
44
+ value: 'text-lg',
45
+ },
46
+ md: {
47
+ container: 'px-4 py-4',
48
+ icon: 'size-5',
49
+ value: 'text-2xl',
50
+ },
51
+ lg: {
52
+ container: 'px-4 py-4',
53
+ icon: 'size-5',
54
+ value: 'text-3xl',
55
+ },
56
+ };
57
+ export function StatCard({ className, description, format, icon, label, size = 'md', value, variant = 'default', valueClassName, }) {
58
+ const variantStyle = variantClasses[variant];
59
+ const sizeStyle = sizeClasses[size];
60
+ const formattedValue = format ? format(value) : String(value ?? '-');
61
+ return (_jsxs("div", { className: cn('app-stat-card rounded-xl border', variantStyle.container, sizeStyle.container, className), children: [_jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsx("div", { className: cn('min-w-0 text-xs font-medium tracking-wide uppercase', variantStyle.label), children: label }), icon && (_jsx("div", { className: cn('shrink-0 [&>svg]:size-full', sizeStyle.icon, variantStyle.icon), children: icon }))] }), _jsx("div", { className: cn('mt-1 font-semibold', sizeStyle.value, variantStyle.value, valueClassName), children: formattedValue }), description && (_jsx("div", { className: cn('mt-1 text-xs', size === 'lg' && 'min-h-8 leading-4', variantStyle.description), children: description }))] }));
62
+ }
@@ -0,0 +1,20 @@
1
+ import React from 'react';
2
+ import { type FieldValues } from 'react-hook-form';
3
+ export interface SwitchProps<TFieldValues extends FieldValues = FieldValues> extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'onChange'> {
4
+ checked?: boolean;
5
+ onChange?: (checked: boolean) => void;
6
+ label?: string;
7
+ errorMessage?: string;
8
+ wrapperClassName?: string;
9
+ labelClassName?: string;
10
+ labelPosition?: 'top' | 'right' | 'bottom' | 'left';
11
+ display?: 'field' | 'inline';
12
+ size?: 'xs' | 'sm' | 'md' | 'lg';
13
+ control?: unknown;
14
+ rules?: unknown;
15
+ }
16
+ export declare function Switch<TFieldValues extends FieldValues = FieldValues>(props: SwitchProps<TFieldValues>): React.JSX.Element;
17
+ export declare namespace Switch {
18
+ var displayName: string;
19
+ }
20
+ //# sourceMappingURL=Switch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Switch.d.ts","sourceRoot":"","sources":["../../src/components/Switch.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAGL,KAAK,WAAW,EAGjB,MAAM,iBAAiB,CAAC;AAKzB,MAAM,WAAW,WAAW,CAAC,YAAY,SAAS,WAAW,GAAG,WAAW,CACzE,SAAQ,IAAI,CAAC,KAAK,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,UAAU,CAAC;IACvE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;IACpD,OAAO,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC7B,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IACjC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAyJD,wBAAgB,MAAM,CAAC,YAAY,SAAS,WAAW,GAAG,WAAW,EAAE,KAAK,EAAE,WAAW,CAAC,YAAY,CAAC,qBAMtG;yBANe,MAAM"}
@@ -0,0 +1,63 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useController, } from 'react-hook-form';
3
+ import { cn } from '../utils/cn';
4
+ import { FieldControl } from './FieldControl';
5
+ const switchSizeClasses = {
6
+ xs: {
7
+ root: 'h-4 w-7 min-w-7 max-w-7 p-0.5',
8
+ thumb: 'size-3',
9
+ offset: 12,
10
+ },
11
+ sm: {
12
+ root: 'h-5 w-9 min-w-9 max-w-9 p-0.5',
13
+ thumb: 'size-4',
14
+ offset: 16,
15
+ },
16
+ md: {
17
+ root: 'h-5 w-10 min-w-10 max-w-10 p-0.5',
18
+ thumb: 'size-4',
19
+ offset: 20,
20
+ },
21
+ lg: {
22
+ root: 'h-6 w-11 min-w-11 max-w-11 p-0.5',
23
+ thumb: 'size-5',
24
+ offset: 20,
25
+ },
26
+ };
27
+ function SwitchBase({ checked, className, errorMessage, id, inputChecked, inputDisabled, inputName, inputOnBlur, label, labelClassName = '', labelPosition = 'left', display = 'field', name, onChange, onCheckedChange, size = 'md', wrapperClassName, ...props }) {
28
+ const resolvedChecked = inputChecked ?? checked;
29
+ const resolvedName = inputName ?? name;
30
+ const resolvedDisabled = inputDisabled ?? props.disabled;
31
+ const componentId = id || resolvedName;
32
+ const isHorizontal = labelPosition === 'left' || labelPosition === 'right';
33
+ const sizeClasses = switchSizeClasses[size];
34
+ function handleClick() {
35
+ const nextChecked = !resolvedChecked;
36
+ onCheckedChange?.(nextChecked);
37
+ onChange?.(nextChecked);
38
+ }
39
+ const button = (controlId, describedBy, disabled = resolvedDisabled) => (_jsx("button", { type: "button", role: "switch", "aria-checked": !!resolvedChecked, "aria-label": label, "aria-describedby": describedBy, id: controlId, name: resolvedName, onClick: handleClick, onBlur: inputOnBlur, disabled: disabled, "data-checked": !!resolvedChecked, className: cn('app-switch focus:ring-brand/50 inline-flex shrink-0 flex-none self-center rounded-full transition-colors duration-[var(--duration-normal)] focus:ring-2 focus:ring-offset-2 focus:outline-none', sizeClasses.root, disabled ? 'cursor-not-allowed opacity-60' : 'cursor-pointer', className), ...props, children: _jsx("span", { className: cn('app-switch__thumb block rounded-full shadow-sm transition-transform duration-[var(--duration-normal)]', sizeClasses.thumb), style: { transform: `translateX(${resolvedChecked ? sizeClasses.offset : 0}px)` } }) }));
40
+ if (display === 'inline') {
41
+ const labelElement = label ? (_jsx("span", { className: cn('min-w-0 flex-1 text-sm font-medium leading-snug text-foreground', labelClassName), children: label })) : null;
42
+ const isLabelAfter = labelPosition === 'right' || labelPosition === 'bottom';
43
+ const isVertical = labelPosition === 'top' || labelPosition === 'bottom';
44
+ return (_jsxs("div", { className: cn('flex gap-3', isVertical ? 'flex-col items-start' : 'items-center justify-between', resolvedDisabled && 'opacity-70', wrapperClassName), "data-disabled": resolvedDisabled || undefined, children: [!isLabelAfter && labelElement, button(componentId, undefined, resolvedDisabled), isLabelAfter && labelElement] }));
45
+ }
46
+ return (_jsx(FieldControl, { label: label, id: componentId, labelClassName: cn('whitespace-nowrap leading-none', isHorizontal && 'self-center', labelClassName), labelPosition: labelPosition, wrapperClassName: wrapperClassName, errorMessage: errorMessage, size: size, disabled: resolvedDisabled, children: ({ controlId, describedBy, disabled }) => (button(controlId, describedBy, disabled)) }));
47
+ }
48
+ function HookFormSwitch(props) {
49
+ const { control, name, rules, ...rest } = props;
50
+ const { field } = useController({
51
+ control: control,
52
+ name: name,
53
+ rules: rules,
54
+ });
55
+ return _jsx(SwitchBase, { ...rest, name: name, inputName: field.name, inputChecked: !!field.value, inputDisabled: field.disabled, inputOnBlur: () => field.onBlur(), onCheckedChange: (nextChecked) => field.onChange(nextChecked) });
56
+ }
57
+ export function Switch(props) {
58
+ if (props.control && props.name) {
59
+ return _jsx(HookFormSwitch, { ...props });
60
+ }
61
+ return _jsx(SwitchBase, { ...props });
62
+ }
63
+ Switch.displayName = 'Switch';
@@ -0,0 +1,65 @@
1
+ import { type ButtonHTMLAttributes, type ColHTMLAttributes, type HTMLAttributes, type TableHTMLAttributes, type TdHTMLAttributes, type ThHTMLAttributes } from 'react';
2
+ import { Pagination } from './Pagination';
3
+ interface TableRootProps extends TableHTMLAttributes<HTMLTableElement> {
4
+ chrome?: 'default' | 'flat';
5
+ density?: 'default' | 'compact';
6
+ fill?: boolean;
7
+ layout?: 'auto' | 'fixed';
8
+ isFramed?: boolean;
9
+ viewportClassName?: string;
10
+ wrapperClassName?: string;
11
+ wrapperProps?: HTMLAttributes<HTMLDivElement>;
12
+ }
13
+ interface TableRowProps extends HTMLAttributes<HTMLTableRowElement> {
14
+ isInteractive?: boolean;
15
+ }
16
+ export declare const TABLE_ACTION_CELL_WIDTH_PX: number;
17
+ /**
18
+ * O rotulo "Ações" ocupa ~45px. Um grupo com um unico botao (28px) nao o comporta,
19
+ * entao o grupo inteiro respeita este minimo — sem nunca absorver a sobra da tabela.
20
+ */
21
+ export declare const TABLE_ACTION_GROUP_MIN_WIDTH_PX = 56;
22
+ /**
23
+ * Folga entre o icone da ponta e a borda da tabela: sem ela o botao encosta na
24
+ * moldura. Entra como padding, entao o valor inteiro vira folga na borda
25
+ * (centralizar o botao mandaria metade dela para o vao entre icones).
26
+ */
27
+ export declare const TABLE_ACTION_EDGE_GUTTER_PX = 12;
28
+ export declare function getTableActionColumnWidth(actionCount?: number, hasEdgeGutter?: boolean): number;
29
+ export type TableActionColumnSize = 'sm' | 'md' | 'lg';
30
+ interface TableActionHeadProps extends ThHTMLAttributes<HTMLTableCellElement> {
31
+ size?: TableActionColumnSize;
32
+ }
33
+ interface TableActionColProps extends ColHTMLAttributes<HTMLTableColElement> {
34
+ actionCount?: number;
35
+ hasEdgeGutter?: boolean;
36
+ }
37
+ interface TableActionCellProps extends TdHTMLAttributes<HTMLTableCellElement> {
38
+ }
39
+ /**
40
+ * Tonalidade semantica do icone. Todas mantem o corpo do `ghost` (sem preenchimento
41
+ * nem borda) e so trocam a cor: icone tonalizado em repouso e hover com a mesma cor
42
+ * suavizada, igual ao default faz com a `--brand`.
43
+ */
44
+ export type TableActionTone = 'default' | 'success' | 'danger';
45
+ interface TableActionProps extends ButtonHTMLAttributes<HTMLButtonElement> {
46
+ size?: 'xs' | 'sm';
47
+ tone?: TableActionTone;
48
+ tooltip?: string;
49
+ }
50
+ export declare const Table: {
51
+ Root: import("react").ForwardRefExoticComponent<TableRootProps & import("react").RefAttributes<HTMLTableElement>>;
52
+ Header: import("react").ForwardRefExoticComponent<HTMLAttributes<HTMLTableSectionElement> & import("react").RefAttributes<HTMLTableSectionElement>>;
53
+ Body: import("react").ForwardRefExoticComponent<HTMLAttributes<HTMLTableSectionElement> & import("react").RefAttributes<HTMLTableSectionElement>>;
54
+ Footer: import("react").ForwardRefExoticComponent<HTMLAttributes<HTMLTableSectionElement> & import("react").RefAttributes<HTMLTableSectionElement>>;
55
+ Row: import("react").ForwardRefExoticComponent<TableRowProps & import("react").RefAttributes<HTMLTableRowElement>>;
56
+ Head: import("react").ForwardRefExoticComponent<ThHTMLAttributes<HTMLTableCellElement> & import("react").RefAttributes<HTMLTableCellElement>>;
57
+ ActionHead: import("react").ForwardRefExoticComponent<TableActionHeadProps & import("react").RefAttributes<HTMLTableCellElement>>;
58
+ ActionCol: import("react").ForwardRefExoticComponent<TableActionColProps & import("react").RefAttributes<HTMLTableColElement>>;
59
+ Cell: import("react").ForwardRefExoticComponent<TdHTMLAttributes<HTMLTableCellElement> & import("react").RefAttributes<HTMLTableCellElement>>;
60
+ ActionCell: import("react").ForwardRefExoticComponent<TableActionCellProps & import("react").RefAttributes<HTMLTableCellElement>>;
61
+ Action: import("react").ForwardRefExoticComponent<TableActionProps & import("react").RefAttributes<HTMLButtonElement>>;
62
+ Pagination: typeof Pagination;
63
+ };
64
+ export {};
65
+ //# sourceMappingURL=Table.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Table.d.ts","sourceRoot":"","sources":["../../src/components/Table.tsx"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EAEtB,KAAK,cAAc,EACnB,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACtB,MAAM,OAAO,CAAC;AAIf,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,UAAU,cAAe,SAAQ,mBAAmB,CAAC,gBAAgB,CAAC;IACpE,MAAM,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;IAC5B,OAAO,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAChC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,cAAc,CAAC,cAAc,CAAC,CAAC;CAC/C;AA6ED,UAAU,aAAc,SAAQ,cAAc,CAAC,mBAAmB,CAAC;IACjE,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AA4CD,eAAO,MAAM,0BAA0B,QAAqD,CAAC;AAE7F;;;GAGG;AACH,eAAO,MAAM,+BAA+B,KAAK,CAAC;AAElD;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,KAAK,CAAC;AAyB9C,wBAAgB,yBAAyB,CAAC,WAAW,SAAI,EAAE,aAAa,UAAQ,UAQ/E;AAED,MAAM,MAAM,qBAAqB,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AACvD,UAAU,oBAAqB,SAAQ,gBAAgB,CAAC,oBAAoB,CAAC;IAC3E,IAAI,CAAC,EAAE,qBAAqB,CAAC;CAC9B;AA+BD,UAAU,mBAAoB,SAAQ,iBAAiB,CAAC,mBAAmB,CAAC;IAC1E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAYD,UAAU,oBAAqB,SAAQ,gBAAgB,CAAC,oBAAoB,CAAC;CAAG;AA8BhF;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;AAiB/D,UAAU,gBAAiB,SAAQ,oBAAoB,CAAC,iBAAiB,CAAC;IACxE,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACnB,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAoBD,eAAO,MAAM,KAAK;;;;;;;;;;;;;CAajB,CAAC"}
@@ -0,0 +1,118 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Children, forwardRef, isValidElement, } from 'react';
3
+ import { cn } from '../utils/cn';
4
+ import { Button } from './Button';
5
+ import { Pagination } from './Pagination';
6
+ const Root = forwardRef(({ children, chrome = 'default', className, density = 'default', fill = true, layout = 'auto', isFramed = true, viewportClassName, wrapperClassName, wrapperProps, ...props }, ref) => {
7
+ const childNodes = Children.toArray(children);
8
+ const tableChildren = childNodes.filter((child) => !isValidElement(child) || child.type !== Pagination);
9
+ const paginationChildren = childNodes.filter((child) => isValidElement(child) && child.type === Pagination);
10
+ const { className: wrapperPropsClassName, ...resolvedWrapperProps } = wrapperProps ?? {};
11
+ return (_jsxs("div", { ...resolvedWrapperProps, "data-slot": "table-root", className: cn('bg-surface flex w-full min-w-0 flex-col overflow-hidden', fill && 'h-full min-h-0 grow', isFramed && 'border-app-border rounded-[var(--radius)] border', isFramed && chrome === 'default' && 'shadow-[var(--shadow-sm)]', density === 'compact' && '[&_td]:py-2 [&_th]:py-2', wrapperPropsClassName, wrapperClassName), children: [_jsx("div", { className: cn('min-h-0 flex-1 overflow-auto', viewportClassName), children: _jsx("table", { ref: ref, className: cn('w-full border-separate border-spacing-0 caption-bottom text-sm text-foreground', layout === 'fixed' && 'table-fixed', className), ...props, children: tableChildren }) }), paginationChildren.length > 0 && (_jsx("div", { "data-slot": "table-pagination", className: cn('border-app-border bg-background-secondary shrink-0 border-t px-3 py-2', density === 'compact' && 'px-3 py-2'), children: paginationChildren }))] }));
12
+ });
13
+ Root.displayName = 'Table.Root';
14
+ const Header = forwardRef(({ className, ...props }, ref) => _jsx("thead", { ref: ref, className: cn('bg-background-secondary', className), ...props }));
15
+ Header.displayName = 'Table.Header';
16
+ const Body = forwardRef(({ className, ...props }, ref) => _jsx("tbody", { ref: ref, className: className, ...props }));
17
+ Body.displayName = 'Table.Body';
18
+ const Footer = forwardRef(({ className, ...props }, ref) => (_jsx("tfoot", { ref: ref, className: cn('border-app-border bg-background border-t font-medium', className), ...props })));
19
+ Footer.displayName = 'Table.Footer';
20
+ const Row = forwardRef(({ className, isInteractive = true, ...props }, ref) => (_jsx("tr", { ref: ref, className: cn('transition-colors hover:bg-[color-mix(in_srgb,var(--brand)_5%,transparent)] has-[td[data-slot=table-action-cell]:hover]:bg-transparent last:[&>td]:border-b-0', isInteractive && 'cursor-pointer', className), ...props })));
21
+ Row.displayName = 'Table.Row';
22
+ const Head = forwardRef(({ className, ...props }, ref) => (_jsx("th", { ref: ref, className: cn('border-app-border bg-background-secondary sticky top-0 z-[var(--z-table-header)] h-10 whitespace-nowrap border-b px-3 py-2.5 text-left align-middle text-xs font-extrabold tracking-[var(--letter-spacing-label)] text-foreground-muted uppercase', className), ...props })));
23
+ Head.displayName = 'Table.Head';
24
+ const Cell = forwardRef(({ className, ...props }, ref) => (_jsx("td", { ref: ref, className: cn('border-app-border border-b px-3 py-2.5 align-middle text-foreground', className), ...props })));
25
+ Cell.displayName = 'Table.Cell';
26
+ /** Tamanho `xs` do Button (min-h-7): nao encolher abaixo disso. */
27
+ const ACTION_BUTTON_SIZE_PX = 28;
28
+ const ACTION_CELL_PADDING_PX = 2;
29
+ export const TABLE_ACTION_CELL_WIDTH_PX = ACTION_BUTTON_SIZE_PX + ACTION_CELL_PADDING_PX * 2;
30
+ /**
31
+ * O rotulo "Ações" ocupa ~45px. Um grupo com um unico botao (28px) nao o comporta,
32
+ * entao o grupo inteiro respeita este minimo — sem nunca absorver a sobra da tabela.
33
+ */
34
+ export const TABLE_ACTION_GROUP_MIN_WIDTH_PX = 56;
35
+ /**
36
+ * Folga entre o icone da ponta e a borda da tabela: sem ela o botao encosta na
37
+ * moldura. Entra como padding, entao o valor inteiro vira folga na borda
38
+ * (centralizar o botao mandaria metade dela para o vao entre icones).
39
+ */
40
+ export const TABLE_ACTION_EDGE_GUTTER_PX = 12;
41
+ /**
42
+ * A folga vale para a celula que encosta na moldura — `:first-child`/`:last-child`
43
+ * dizem isso sem prop nenhuma, entao qualquer tabela que use `Table.ActionCell`
44
+ * ganha o comportamento (e o `ActionHead` recebe o mesmo recuo, o que mantem o
45
+ * rotulo centralizado sobre os icones, e nao sobre o grupo + folga).
46
+ */
47
+ const ACTION_EDGE_PAD_VAR = '--table-action-edge-pad';
48
+ const actionEdgeStyle = {
49
+ [ACTION_EDGE_PAD_VAR]: `${ACTION_CELL_PADDING_PX + TABLE_ACTION_EDGE_GUTTER_PX}px`,
50
+ };
51
+ const actionEdgeClasses = 'first:[padding-inline-start:var(--table-action-edge-pad)] last:[padding-inline-end:var(--table-action-edge-pad)]';
52
+ /**
53
+ * O Head traz `px-3`, entao o ActionHead precisa de `!` para reduzir o padding —
54
+ * e `!important` ganha de regra normal mesmo com especificidade menor. Logo a
55
+ * folga da ponta tambem precisa ser `!` aqui, senao o rotulo fica fora de centro.
56
+ */
57
+ const actionEdgeImportantClasses = 'first:[padding-inline-start:var(--table-action-edge-pad)]! last:[padding-inline-end:var(--table-action-edge-pad)]!';
58
+ export function getTableActionColumnWidth(actionCount = 1, hasEdgeGutter = false) {
59
+ const columnCount = Math.max(actionCount, 1);
60
+ const groupWidth = Math.max(columnCount * TABLE_ACTION_CELL_WIDTH_PX, TABLE_ACTION_GROUP_MIN_WIDTH_PX);
61
+ return groupWidth / columnCount + (hasEdgeGutter ? TABLE_ACTION_EDGE_GUTTER_PX : 0);
62
+ }
63
+ const actionColumnWidthClasses = {
64
+ sm: 'w-16',
65
+ md: 'w-20',
66
+ lg: 'w-28',
67
+ };
68
+ const ActionHead = forwardRef(({ children = 'Ações', className, colSpan, size = 'sm', style, ...props }, ref) => (_jsx(Head, { ref: ref, colSpan: colSpan,
69
+ // O sufixo `!` vence o `px-3` do Head: o cn do pacote nao usa tailwind-merge.
70
+ className: cn(!colSpan && actionColumnWidthClasses[size], 'px-0.5!', actionEdgeImportantClasses, 'text-center', className), style: { ...actionEdgeStyle, ...style }, ...props, children: children })));
71
+ ActionHead.displayName = 'Table.ActionHead';
72
+ const ActionCol = forwardRef(({ actionCount = 1, hasEdgeGutter = false, style, ...props }, ref) => {
73
+ const width = `${getTableActionColumnWidth(actionCount, hasEdgeGutter)}px`;
74
+ return _jsx("col", { ref: ref, style: { ...style, width, minWidth: width, maxWidth: width }, ...props });
75
+ });
76
+ ActionCol.displayName = 'Table.ActionCol';
77
+ const ActionCell = forwardRef(({ children, className, style, ...props }, ref) => (_jsx("td", { ref: ref, "data-slot": "table-action-cell", className: cn('border-app-border border-b px-0.5 py-0.5 text-center align-middle', actionEdgeClasses, className),
78
+ // A largura e obrigatoria: em layout `auto` (tabela sem colgroup) uma celula
79
+ // sem width volta a absorver a sobra da tabela. Em `fixed` quem manda e o
80
+ // <col>, e na ponta o proprio padding faz a celula crescer o necessario.
81
+ style: {
82
+ ...actionEdgeStyle,
83
+ ...style,
84
+ width: `${TABLE_ACTION_CELL_WIDTH_PX}px`,
85
+ minWidth: `${TABLE_ACTION_CELL_WIDTH_PX}px`,
86
+ }, ...props, children: _jsx("div", { className: "flex min-h-7 items-center justify-center", children: children }) })));
87
+ ActionCell.displayName = 'Table.ActionCell';
88
+ /**
89
+ * Os tons usam os tokens semanticos de icone, que preservam saturacao e contraste
90
+ * nos temas claro e escuro sem repetir hexadecimais no componente.
91
+ *
92
+ * `!` obrigatorio: o cn do pacote nao usa tailwind-merge, entao sem ele o
93
+ * `text-foreground-muted`/`hover:bg` do ghost venceria pela ordem do CSS.
94
+ */
95
+ const actionToneClasses = {
96
+ default: '',
97
+ success: 'text-[var(--status-success-icon)]! hover:bg-[color-mix(in_srgb,var(--status-success-icon)_10%,transparent)]! hover:text-[var(--status-success-icon)]! dark:hover:bg-[color-mix(in_srgb,var(--status-success-icon)_16%,transparent)]!',
98
+ danger: 'text-[var(--status-danger-icon)]! hover:bg-[color-mix(in_srgb,var(--status-danger-icon)_10%,transparent)]! hover:text-[var(--status-danger-icon)]! dark:hover:bg-[color-mix(in_srgb,var(--status-danger-icon)_16%,transparent)]!',
99
+ };
100
+ const Action = forwardRef(({ className, size = 'xs', tone = 'default', tooltip, ...props }, ref) => (
101
+ // `props` vem primeiro: o corpo (variant/size/className) e do Table.Action, e nao
102
+ // pode ser sobrescrito por quem chama — o tom e a unica alavanca de cor.
103
+ _jsx(Button, { ...props, ref: ref, "aria-label": props['aria-label'] ?? tooltip, className: cn('size-7 min-h-7 min-w-7 p-0', actionToneClasses[tone], className), size: size, variant: "ghost", tooltip: tooltip })));
104
+ Action.displayName = 'Table.Action';
105
+ export const Table = {
106
+ Root,
107
+ Header,
108
+ Body,
109
+ Footer,
110
+ Row,
111
+ Head,
112
+ ActionHead,
113
+ ActionCol,
114
+ Cell,
115
+ ActionCell,
116
+ Action,
117
+ Pagination,
118
+ };
@@ -0,0 +1,23 @@
1
+ import { type CSSProperties, type ReactNode } from 'react';
2
+ export interface UnderlinedTabItem {
3
+ id: string;
4
+ label: string;
5
+ visible?: boolean;
6
+ children: ReactNode;
7
+ }
8
+ export interface TabsUnderlinedProps {
9
+ items: UnderlinedTabItem[];
10
+ defaultValue?: string;
11
+ value?: string;
12
+ onValueChange?: (value: string) => void;
13
+ className?: string;
14
+ listClassName?: string;
15
+ triggerClassName?: string;
16
+ contentClassName?: string;
17
+ tabWidth?: CSSProperties['width'];
18
+ tabMinWidth?: CSSProperties['minWidth'];
19
+ tabMaxWidth?: CSSProperties['maxWidth'];
20
+ truncateLabels?: boolean;
21
+ }
22
+ export declare function TabsUnderlined({ className, contentClassName, defaultValue, items, listClassName, onValueChange, tabMaxWidth, tabMinWidth, tabWidth, triggerClassName, truncateLabels, value, }: TabsUnderlinedProps): import("react").JSX.Element;
23
+ //# sourceMappingURL=TabsUnderlined.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TabsUnderlined.d.ts","sourceRoot":"","sources":["../../src/components/TabsUnderlined.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,aAAa,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAW9E,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,iBAAiB,EAAE,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;IAClC,WAAW,CAAC,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;IACxC,WAAW,CAAC,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;IACxC,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,wBAAgB,cAAc,CAAC,EAC7B,SAAS,EACT,gBAAgB,EAChB,YAAY,EACZ,KAAK,EACL,aAAa,EACb,aAAa,EACb,WAAW,EACX,WAAW,EACX,QAAQ,EACR,gBAAgB,EAChB,cAAsB,EACtB,KAAK,GACN,EAAE,mBAAmB,+BA4DrB"}
@@ -0,0 +1,29 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useMemo, useState } from 'react';
3
+ import * as Tabs from '@radix-ui/react-tabs';
4
+ import { cn } from '../utils/cn';
5
+ const DEFAULT_TAB_MIN_WIDTH = '8.75rem';
6
+ const TAB_BAR_BACKGROUND = 'bg-[var(--tabs-background)]';
7
+ const TAB_INACTIVE_BACKGROUND = 'bg-[var(--tabs-background)]';
8
+ const TAB_INACTIVE_HOVER_BACKGROUND = 'hover:bg-[color-mix(in_srgb,var(--foreground-muted)_8%,var(--tabs-background))]';
9
+ export function TabsUnderlined({ className, contentClassName, defaultValue, items, listClassName, onValueChange, tabMaxWidth, tabMinWidth, tabWidth, triggerClassName, truncateLabels = false, value, }) {
10
+ const visibleItems = useMemo(() => items.filter((item) => item.visible ?? true), [items]);
11
+ const initialValue = defaultValue && visibleItems.some((item) => item.id === defaultValue)
12
+ ? defaultValue
13
+ : visibleItems[0]?.id || '';
14
+ const [internalValue, setInternalValue] = useState(initialValue);
15
+ const resolvedValue = value ?? internalValue;
16
+ function handleValueChange(nextValue) {
17
+ setInternalValue(nextValue);
18
+ onValueChange?.(nextValue);
19
+ }
20
+ return (_jsx("div", { className: cn('border-app-border bg-surface flex h-full min-w-0 flex-col overflow-hidden rounded-xl border', className), children: _jsxs(Tabs.Root, { value: resolvedValue, onValueChange: handleValueChange, className: "flex min-w-0 flex-1 flex-col", children: [_jsx(Tabs.List, { className: cn("flex min-w-0 overflow-x-auto overflow-y-hidden rounded-t-[calc(var(--radius-xl)-1px)] text-sm after:min-w-0 after:flex-1 after:border-b after:border-app-border after:bg-[var(--tabs-background)] after:content-['']", TAB_BAR_BACKGROUND, listClassName), children: visibleItems.map((item) => (_jsx(TabTrigger, { value: item.id, className: triggerClassName, maxWidth: tabMaxWidth, minWidth: tabMinWidth, width: tabWidth, truncate: truncateLabels, children: item.label }, item.id))) }), visibleItems.map((item) => (_jsx(Tabs.Content, { value: item.id, className: cn('flex h-full min-w-0 flex-1 flex-col overflow-hidden', contentClassName), children: item.children }, item.id)))] }) }));
21
+ }
22
+ function TabTrigger({ children, className, value, maxWidth, minWidth, width, truncate, }) {
23
+ const style = {
24
+ maxWidth,
25
+ minWidth: minWidth ?? DEFAULT_TAB_MIN_WIDTH,
26
+ width,
27
+ };
28
+ return (_jsx(Tabs.Trigger, { value: value, style: style, className: cn('text-foreground-muted hover:text-foreground focus-visible:ring-brand/50 border-b-app-border data-[state=active]:border-app-border data-[state=active]:!border-b-transparent data-[state=active]:bg-surface data-[state=active]:text-foreground inline-flex shrink-0 cursor-pointer items-center justify-center border border-t-0 border-x-transparent px-4 py-2 text-sm font-medium transition-colors first:border-l-0 focus:outline-none focus-visible:ring-2', TAB_INACTIVE_BACKGROUND, TAB_INACTIVE_HOVER_BACKGROUND, className), children: _jsx("span", { className: cn('min-w-0', truncate && 'overflow-hidden text-ellipsis whitespace-nowrap'), children: children }) }));
29
+ }
@@ -0,0 +1,25 @@
1
+ import React from 'react';
2
+ import { type FormControlVariant } from './form-control-helpers';
3
+ export type TextFieldVariant = FormControlVariant;
4
+ export type TextFieldIconPosition = 'left' | 'right';
5
+ export interface TextFieldProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size'> {
6
+ wrapperClassName?: string;
7
+ labelClassName?: string;
8
+ label?: string;
9
+ errorMessage?: string;
10
+ error?: string;
11
+ helperText?: string;
12
+ hasValidation?: boolean;
13
+ roundedFull?: boolean;
14
+ size?: 'xs' | 'sm' | 'md' | 'lg';
15
+ labelPosition?: 'top' | 'right' | 'bottom' | 'left';
16
+ variant?: TextFieldVariant;
17
+ icon?: React.ReactNode;
18
+ iconPosition?: TextFieldIconPosition;
19
+ children?: React.ReactNode;
20
+ startAdornment?: React.ReactNode;
21
+ endAdornment?: React.ReactNode;
22
+ adornmentClassName?: string;
23
+ }
24
+ export declare const TextField: React.ForwardRefExoticComponent<TextFieldProps & React.RefAttributes<HTMLInputElement>>;
25
+ //# sourceMappingURL=TextField.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TextField.d.ts","sourceRoot":"","sources":["../../src/components/TextField.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,EAAkB,KAAK,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAGjF,MAAM,MAAM,gBAAgB,GAAG,kBAAkB,CAAC;AAClD,MAAM,MAAM,qBAAqB,GAAG,MAAM,GAAG,OAAO,CAAC;AAErD,MAAM,WAAW,cAAe,SAAQ,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC/F,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IACjC,aAAa,CAAC,EAAE,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;IACpD,OAAO,CAAC,EAAE,gBAAgB,CAAC;IAC3B,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,YAAY,CAAC,EAAE,qBAAqB,CAAC;IACrC,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,cAAc,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACjC,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC/B,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,eAAO,MAAM,SAAS,yFA+KrB,CAAC"}