@hauktui/registry 0.0.1

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 (139) hide show
  1. package/components/accordion/accordion.tsx +146 -0
  2. package/components/accordion/index.ts +2 -0
  3. package/components/alert/alert.tsx +69 -0
  4. package/components/alert/index.ts +2 -0
  5. package/components/alert-dialog/alert-dialog.tsx +185 -0
  6. package/components/alert-dialog/index.ts +2 -0
  7. package/components/avatar/avatar.tsx +57 -0
  8. package/components/avatar/index.ts +2 -0
  9. package/components/avatar-group/avatar-group.tsx +144 -0
  10. package/components/avatar-group/index.ts +2 -0
  11. package/components/badge/badge.tsx +52 -0
  12. package/components/badge/index.ts +2 -0
  13. package/components/banner/banner.tsx +407 -0
  14. package/components/banner/index.ts +2 -0
  15. package/components/breadcrumb/breadcrumb.tsx +58 -0
  16. package/components/breadcrumb/index.ts +2 -0
  17. package/components/button/button.tsx +114 -0
  18. package/components/button/index.ts +2 -0
  19. package/components/calendar/calendar.tsx +250 -0
  20. package/components/calendar/index.ts +2 -0
  21. package/components/card/card.tsx +88 -0
  22. package/components/card/index.ts +2 -0
  23. package/components/carousel/carousel.tsx +185 -0
  24. package/components/carousel/index.ts +2 -0
  25. package/components/chart/chart.tsx +189 -0
  26. package/components/chart/index.ts +2 -0
  27. package/components/checkbox/checkbox.tsx +98 -0
  28. package/components/checkbox/index.ts +2 -0
  29. package/components/code-block/code-block.tsx +214 -0
  30. package/components/code-block/index.ts +2 -0
  31. package/components/collapsible/collapsible.tsx +123 -0
  32. package/components/collapsible/index.ts +2 -0
  33. package/components/color-picker/color-picker.tsx +211 -0
  34. package/components/color-picker/index.ts +2 -0
  35. package/components/combobox/combobox.tsx +275 -0
  36. package/components/combobox/index.ts +2 -0
  37. package/components/command/command.tsx +304 -0
  38. package/components/command/index.ts +2 -0
  39. package/components/confirm-dialog/confirm-dialog.tsx +140 -0
  40. package/components/confirm-dialog/index.ts +2 -0
  41. package/components/context-menu/context-menu.tsx +188 -0
  42. package/components/context-menu/index.ts +2 -0
  43. package/components/countdown/countdown.tsx +165 -0
  44. package/components/countdown/index.ts +2 -0
  45. package/components/data-table/data-table.tsx +256 -0
  46. package/components/data-table/index.ts +2 -0
  47. package/components/date-picker/date-picker.tsx +280 -0
  48. package/components/date-picker/index.ts +2 -0
  49. package/components/dialog/dialog.tsx +84 -0
  50. package/components/dialog/index.ts +2 -0
  51. package/components/drawer/drawer.tsx +141 -0
  52. package/components/drawer/index.ts +2 -0
  53. package/components/dropdown-menu/dropdown-menu.tsx +188 -0
  54. package/components/dropdown-menu/index.ts +2 -0
  55. package/components/empty/empty.tsx +107 -0
  56. package/components/empty/index.ts +2 -0
  57. package/components/field/field.tsx +83 -0
  58. package/components/field/index.ts +2 -0
  59. package/components/form/form.tsx +202 -0
  60. package/components/form/index.ts +8 -0
  61. package/components/hover-card/hover-card.tsx +72 -0
  62. package/components/hover-card/index.ts +2 -0
  63. package/components/input-otp/index.ts +2 -0
  64. package/components/input-otp/input-otp.tsx +176 -0
  65. package/components/kbd/index.ts +2 -0
  66. package/components/kbd/kbd.tsx +30 -0
  67. package/components/label/index.ts +2 -0
  68. package/components/label/label.tsx +56 -0
  69. package/components/list/index.ts +2 -0
  70. package/components/list/list.tsx +247 -0
  71. package/components/menubar/index.ts +2 -0
  72. package/components/menubar/menubar.tsx +220 -0
  73. package/components/navigation-menu/index.ts +6 -0
  74. package/components/navigation-menu/navigation-menu.tsx +216 -0
  75. package/components/pagination/index.ts +2 -0
  76. package/components/pagination/pagination.tsx +158 -0
  77. package/components/password-input/index.ts +2 -0
  78. package/components/password-input/password-input.tsx +198 -0
  79. package/components/popover/index.ts +2 -0
  80. package/components/popover/popover.tsx +102 -0
  81. package/components/progress/index.ts +2 -0
  82. package/components/progress/progress.tsx +73 -0
  83. package/components/radio-group/index.ts +2 -0
  84. package/components/radio-group/radio-group.tsx +167 -0
  85. package/components/resizable/index.ts +2 -0
  86. package/components/resizable/resizable.tsx +141 -0
  87. package/components/scroll-area/index.ts +2 -0
  88. package/components/scroll-area/scroll-area.tsx +133 -0
  89. package/components/select/index.ts +2 -0
  90. package/components/select/select.tsx +185 -0
  91. package/components/separator/index.ts +2 -0
  92. package/components/separator/separator.tsx +63 -0
  93. package/components/sheet/index.ts +2 -0
  94. package/components/sheet/sheet.tsx +137 -0
  95. package/components/sidebar/index.ts +2 -0
  96. package/components/sidebar/sidebar.tsx +225 -0
  97. package/components/skeleton/index.ts +2 -0
  98. package/components/skeleton/skeleton.tsx +64 -0
  99. package/components/slider/index.ts +2 -0
  100. package/components/slider/slider.tsx +128 -0
  101. package/components/spinner/index.ts +2 -0
  102. package/components/spinner/spinner.tsx +57 -0
  103. package/components/stat/index.ts +2 -0
  104. package/components/stat/stat.tsx +138 -0
  105. package/components/stepper/index.ts +2 -0
  106. package/components/stepper/stepper.tsx +219 -0
  107. package/components/switch/index.ts +2 -0
  108. package/components/switch/switch.tsx +102 -0
  109. package/components/table/index.ts +2 -0
  110. package/components/table/table.tsx +242 -0
  111. package/components/tabs/index.ts +2 -0
  112. package/components/tabs/tabs.tsx +240 -0
  113. package/components/tag-input/index.ts +2 -0
  114. package/components/tag-input/tag-input.tsx +180 -0
  115. package/components/terminal/index.ts +2 -0
  116. package/components/terminal/terminal.tsx +162 -0
  117. package/components/text-input/index.ts +2 -0
  118. package/components/text-input/text-input.tsx +179 -0
  119. package/components/textarea/index.ts +2 -0
  120. package/components/textarea/textarea.tsx +206 -0
  121. package/components/timeline/index.ts +2 -0
  122. package/components/timeline/timeline.tsx +167 -0
  123. package/components/toast/index.ts +2 -0
  124. package/components/toast/toast.tsx +93 -0
  125. package/components/toggle/index.ts +2 -0
  126. package/components/toggle/toggle.tsx +114 -0
  127. package/components/toggle-group/index.ts +2 -0
  128. package/components/toggle-group/toggle-group.tsx +176 -0
  129. package/components/tooltip/index.ts +2 -0
  130. package/components/tooltip/tooltip.tsx +65 -0
  131. package/components/tree-view/index.ts +2 -0
  132. package/components/tree-view/tree-view.tsx +245 -0
  133. package/components/typography/index.ts +12 -0
  134. package/components/typography/typography.tsx +154 -0
  135. package/dist/index.d.ts +102 -0
  136. package/dist/index.js +938 -0
  137. package/dist/index.js.map +1 -0
  138. package/package.json +41 -0
  139. package/registry.json +923 -0
@@ -0,0 +1,158 @@
1
+ import React, { useCallback } from "react";
2
+ import { Box, Text, useInput } from "ink";
3
+ import type { Tokens } from "@hauktui/tokens";
4
+ import { useTokens, useFocusable } from "@hauktui/primitives-ink";
5
+ import { stableId, clamp } from "@hauktui/core";
6
+
7
+ export interface PaginationProps {
8
+ /** Current page (1-indexed) */
9
+ page: number;
10
+ /** Total number of pages */
11
+ totalPages: number;
12
+ /** Callback when page changes */
13
+ onChange: (page: number) => void;
14
+ /** Whether the pagination is disabled */
15
+ disabled?: boolean;
16
+ /** Custom tokens override */
17
+ tokens?: Tokens;
18
+ /** Focus ID for focus management */
19
+ focusId?: string;
20
+ /** Number of page buttons to show */
21
+ siblingCount?: number;
22
+ /** Show first/last buttons */
23
+ showBoundaryButtons?: boolean;
24
+ }
25
+
26
+ export function Pagination({
27
+ page,
28
+ totalPages,
29
+ onChange,
30
+ disabled = false,
31
+ tokens: propTokens,
32
+ focusId,
33
+ siblingCount = 1,
34
+ showBoundaryButtons = true,
35
+ }: PaginationProps): React.ReactElement {
36
+ const contextTokens = useTokens();
37
+ const tokens = propTokens ?? contextTokens;
38
+ const id = focusId ?? stableId("pagination");
39
+ const { isFocused } = useFocusable(id);
40
+
41
+ const goToPage = useCallback(
42
+ (newPage: number) => {
43
+ const clampedPage = clamp(newPage, 1, totalPages);
44
+ if (clampedPage !== page) {
45
+ onChange(clampedPage);
46
+ }
47
+ },
48
+ [page, totalPages, onChange]
49
+ );
50
+
51
+ useInput(
52
+ (input, key) => {
53
+ if (!isFocused || disabled) return;
54
+
55
+ if (key.leftArrow || input === "h") {
56
+ goToPage(page - 1);
57
+ } else if (key.rightArrow || input === "l") {
58
+ goToPage(page + 1);
59
+ } else if (input === "g" && !key.shift) {
60
+ goToPage(1);
61
+ } else if (input === "G") {
62
+ goToPage(totalPages);
63
+ } else {
64
+ const num = parseInt(input, 10);
65
+ if (num >= 1 && num <= 9 && num <= totalPages) {
66
+ goToPage(num);
67
+ }
68
+ }
69
+ },
70
+ { isActive: isFocused }
71
+ );
72
+
73
+ // Calculate visible page numbers
74
+ const getPageNumbers = (): (number | "...")[] => {
75
+ const pages: (number | "...")[] = [];
76
+
77
+ const leftSibling = Math.max(page - siblingCount, 1);
78
+ const rightSibling = Math.min(page + siblingCount, totalPages);
79
+
80
+ const showLeftEllipsis = leftSibling > 2;
81
+ const showRightEllipsis = rightSibling < totalPages - 1;
82
+
83
+ if (showBoundaryButtons && leftSibling > 1) {
84
+ pages.push(1);
85
+ if (showLeftEllipsis) pages.push("...");
86
+ }
87
+
88
+ for (let i = leftSibling; i <= rightSibling; i++) {
89
+ pages.push(i);
90
+ }
91
+
92
+ if (showBoundaryButtons && rightSibling < totalPages) {
93
+ if (showRightEllipsis) pages.push("...");
94
+ pages.push(totalPages);
95
+ }
96
+
97
+ return pages;
98
+ };
99
+
100
+ const pageNumbers = getPageNumbers();
101
+
102
+ return React.createElement(
103
+ Box,
104
+ {
105
+ gap: 1,
106
+ borderStyle: isFocused ? "round" : "single",
107
+ borderColor: isFocused ? tokens.colors.focus : tokens.colors.border,
108
+ paddingX: 1,
109
+ },
110
+ // Previous button
111
+ React.createElement(
112
+ Text,
113
+ {
114
+ color: page > 1 ? tokens.colors.accent : tokens.colors.disabled,
115
+ dimColor: page <= 1,
116
+ },
117
+ "←"
118
+ ),
119
+ // Page numbers
120
+ ...pageNumbers.map((pageNum, index) => {
121
+ if (pageNum === "...") {
122
+ return React.createElement(
123
+ Text,
124
+ { key: `ellipsis-${index}`, color: tokens.colors.muted },
125
+ "..."
126
+ );
127
+ }
128
+
129
+ const isCurrent = pageNum === page;
130
+ return React.createElement(
131
+ Text,
132
+ {
133
+ key: pageNum,
134
+ color: isCurrent ? tokens.colors.accent : tokens.colors.fg,
135
+ bold: isCurrent,
136
+ inverse: isCurrent && isFocused,
137
+ },
138
+ String(pageNum)
139
+ );
140
+ }),
141
+ // Next button
142
+ React.createElement(
143
+ Text,
144
+ {
145
+ color:
146
+ page < totalPages ? tokens.colors.accent : tokens.colors.disabled,
147
+ dimColor: page >= totalPages,
148
+ },
149
+ "→"
150
+ ),
151
+ // Page indicator
152
+ React.createElement(
153
+ Text,
154
+ { color: tokens.colors.muted, dimColor: true },
155
+ `(${page}/${totalPages})`
156
+ )
157
+ );
158
+ }
@@ -0,0 +1,2 @@
1
+ export { PasswordInput } from "./password-input.js";
2
+ export type { PasswordInputProps } from "./password-input.js";
@@ -0,0 +1,198 @@
1
+ import React, { useState, useCallback } from "react";
2
+ import { Box, Text, useInput } from "ink";
3
+ import type { Tokens } from "@hauktui/tokens";
4
+ import { useTokens, useFocusable } from "@hauktui/primitives-ink";
5
+ import { stableId } from "@hauktui/core";
6
+
7
+ export interface PasswordInputProps {
8
+ /** Current input value (controlled) */
9
+ value?: string;
10
+ /** Default value (uncontrolled) */
11
+ defaultValue?: string;
12
+ /** Callback when value changes */
13
+ onChange?: (value: string) => void;
14
+ /** Callback when Enter is pressed */
15
+ onSubmit?: (value: string) => void;
16
+ /** Placeholder text */
17
+ placeholder?: string;
18
+ /** Input label */
19
+ label?: string;
20
+ /** Whether the input is disabled */
21
+ disabled?: boolean;
22
+ /** Maximum input length */
23
+ maxLength?: number;
24
+ /** Custom tokens override */
25
+ tokens?: Tokens;
26
+ /** Focus ID for focus management */
27
+ focusId?: string;
28
+ /** Mask character (default: "*") */
29
+ mask?: string;
30
+ /** Show password reveal hint */
31
+ showHint?: boolean;
32
+ }
33
+
34
+ export function PasswordInput({
35
+ value: controlledValue,
36
+ defaultValue = "",
37
+ onChange,
38
+ onSubmit,
39
+ placeholder = "Enter password",
40
+ label,
41
+ disabled = false,
42
+ maxLength,
43
+ tokens: propTokens,
44
+ focusId,
45
+ mask = "*",
46
+ showHint = true,
47
+ }: PasswordInputProps): React.ReactElement {
48
+ const contextTokens = useTokens();
49
+ const tokens = propTokens ?? contextTokens;
50
+ const id = focusId ?? stableId("password-input");
51
+ const { isFocused } = useFocusable(id);
52
+
53
+ const [internalValue, setInternalValue] = useState(defaultValue);
54
+ const [cursorPosition, setCursorPosition] = useState(defaultValue.length);
55
+ const [showPassword, setShowPassword] = useState(false);
56
+
57
+ const isControlled = controlledValue !== undefined;
58
+ const currentValue = isControlled ? controlledValue : internalValue;
59
+
60
+ const updateValue = useCallback(
61
+ (newValue: string) => {
62
+ if (maxLength !== undefined && newValue.length > maxLength) {
63
+ newValue = newValue.slice(0, maxLength);
64
+ }
65
+ if (!isControlled) {
66
+ setInternalValue(newValue);
67
+ }
68
+ onChange?.(newValue);
69
+ },
70
+ [isControlled, maxLength, onChange]
71
+ );
72
+
73
+ useInput(
74
+ (input, key) => {
75
+ if (!isFocused || disabled) return;
76
+
77
+ if (key.return) {
78
+ onSubmit?.(currentValue);
79
+ return;
80
+ }
81
+
82
+ // Toggle password visibility with Ctrl+R
83
+ if (key.ctrl && input === "r") {
84
+ setShowPassword((prev) => !prev);
85
+ return;
86
+ }
87
+
88
+ if (key.backspace || key.delete) {
89
+ if (cursorPosition > 0) {
90
+ const newValue =
91
+ currentValue.slice(0, cursorPosition - 1) +
92
+ currentValue.slice(cursorPosition);
93
+ updateValue(newValue);
94
+ setCursorPosition(Math.max(0, cursorPosition - 1));
95
+ }
96
+ return;
97
+ }
98
+
99
+ if (key.leftArrow) {
100
+ setCursorPosition(Math.max(0, cursorPosition - 1));
101
+ return;
102
+ }
103
+
104
+ if (key.rightArrow) {
105
+ setCursorPosition(Math.min(currentValue.length, cursorPosition + 1));
106
+ return;
107
+ }
108
+
109
+ // Home key
110
+ if (key.ctrl && input === "a") {
111
+ setCursorPosition(0);
112
+ return;
113
+ }
114
+
115
+ // End key
116
+ if (key.ctrl && input === "e") {
117
+ setCursorPosition(currentValue.length);
118
+ return;
119
+ }
120
+
121
+ // Regular character input
122
+ if (input && !key.ctrl && !key.meta) {
123
+ const newValue =
124
+ currentValue.slice(0, cursorPosition) +
125
+ input +
126
+ currentValue.slice(cursorPosition);
127
+ updateValue(newValue);
128
+ setCursorPosition(cursorPosition + input.length);
129
+ }
130
+ },
131
+ { isActive: isFocused }
132
+ );
133
+
134
+ // Display value (masked or plain)
135
+ const displayValue = showPassword
136
+ ? currentValue
137
+ : mask.repeat(currentValue.length);
138
+
139
+ // Render with cursor
140
+ const renderValue = () => {
141
+ if (!isFocused) {
142
+ if (displayValue.length === 0) {
143
+ return React.createElement(Text, { color: tokens.colors.muted }, placeholder);
144
+ }
145
+ return React.createElement(Text, { color: tokens.colors.fg }, displayValue);
146
+ }
147
+
148
+ const before = displayValue.slice(0, cursorPosition);
149
+ const cursor = displayValue[cursorPosition] ?? " ";
150
+ const after = displayValue.slice(cursorPosition + 1);
151
+
152
+ return React.createElement(
153
+ Text,
154
+ null,
155
+ React.createElement(Text, { color: tokens.colors.fg }, before),
156
+ React.createElement(
157
+ Text,
158
+ { backgroundColor: tokens.colors.accent, color: tokens.colors.bg },
159
+ cursor
160
+ ),
161
+ React.createElement(Text, { color: tokens.colors.fg }, after)
162
+ );
163
+ };
164
+
165
+ return React.createElement(
166
+ Box,
167
+ { flexDirection: "column" },
168
+ label
169
+ ? React.createElement(
170
+ Text,
171
+ { color: isFocused ? tokens.colors.accent : tokens.colors.muted },
172
+ label
173
+ )
174
+ : null,
175
+ React.createElement(
176
+ Box,
177
+ {
178
+ borderStyle: "round",
179
+ borderColor: disabled
180
+ ? tokens.colors.disabled
181
+ : isFocused
182
+ ? tokens.colors.focus
183
+ : tokens.colors.border,
184
+ paddingX: 1,
185
+ },
186
+ React.createElement(Text, { color: tokens.colors.muted }, showPassword ? "* " : " "),
187
+ renderValue()
188
+ ),
189
+ showHint && isFocused
190
+ ? React.createElement(
191
+ Text,
192
+ { color: tokens.colors.muted, dimColor: true },
193
+ "Ctrl+R to ",
194
+ showPassword ? "hide" : "reveal"
195
+ )
196
+ : null
197
+ );
198
+ }
@@ -0,0 +1,2 @@
1
+ export { Popover } from "./popover.js";
2
+ export type { PopoverProps } from "./popover.js";
@@ -0,0 +1,102 @@
1
+ import React from "react";
2
+ import { Box, Text } from "ink";
3
+ import type { Tokens } from "@hauktui/tokens";
4
+ import { useTokens, useFocusable } from "@hauktui/primitives-ink";
5
+ import { stableId } from "@hauktui/core";
6
+
7
+ export interface PopoverProps {
8
+ /** Content to display in the popover */
9
+ content: React.ReactNode;
10
+ /** Trigger element */
11
+ children: React.ReactNode;
12
+ /** Whether the popover is open */
13
+ open: boolean;
14
+ /** Position relative to trigger */
15
+ position?: "top" | "bottom" | "left" | "right";
16
+ /** Popover width */
17
+ width?: number;
18
+ /** Custom tokens override */
19
+ tokens?: Tokens;
20
+ /** Focus ID for focus management */
21
+ focusId?: string;
22
+ /** Optional title */
23
+ title?: string;
24
+ }
25
+
26
+ export function Popover({
27
+ content,
28
+ children,
29
+ open,
30
+ position = "bottom",
31
+ width = 30,
32
+ tokens: propTokens,
33
+ focusId,
34
+ title,
35
+ }: PopoverProps): React.ReactElement {
36
+ const contextTokens = useTokens();
37
+ const tokens = propTokens ?? contextTokens;
38
+ const id = focusId ?? stableId("popover");
39
+ const { isFocused } = useFocusable(id);
40
+
41
+ const popoverContent = React.createElement(
42
+ Box,
43
+ {
44
+ flexDirection: "column",
45
+ borderStyle: "round",
46
+ borderColor: isFocused ? tokens.colors.focus : tokens.colors.border,
47
+ width,
48
+ paddingX: 1,
49
+ paddingY: 0,
50
+ },
51
+ title
52
+ ? React.createElement(
53
+ Box,
54
+ { marginBottom: 0 },
55
+ React.createElement(
56
+ Text,
57
+ { bold: true, color: tokens.colors.fg },
58
+ title
59
+ )
60
+ )
61
+ : null,
62
+ title
63
+ ? React.createElement(
64
+ Box,
65
+ { marginBottom: 0 },
66
+ React.createElement(
67
+ Text,
68
+ { color: tokens.colors.border },
69
+ "─".repeat(width - 4)
70
+ )
71
+ )
72
+ : null,
73
+ React.createElement(Box, { flexDirection: "column" }, content)
74
+ );
75
+
76
+ if (!open) {
77
+ return React.createElement(Box, null, children);
78
+ }
79
+
80
+ const isVertical = position === "top" || position === "bottom";
81
+
82
+ const containerProps: Record<string, unknown> = {
83
+ flexDirection: isVertical ? "column" : "row",
84
+ alignItems: isVertical ? "flex-start" : "center",
85
+ gap: 0,
86
+ };
87
+
88
+ if (position === "top") {
89
+ return React.createElement(Box, containerProps, popoverContent, children);
90
+ }
91
+
92
+ if (position === "bottom") {
93
+ return React.createElement(Box, containerProps, children, popoverContent);
94
+ }
95
+
96
+ if (position === "left") {
97
+ return React.createElement(Box, containerProps, popoverContent, children);
98
+ }
99
+
100
+ // right
101
+ return React.createElement(Box, containerProps, children, popoverContent);
102
+ }
@@ -0,0 +1,2 @@
1
+ export { Progress } from "./progress.js";
2
+ export type { ProgressProps } from "./progress.js";
@@ -0,0 +1,73 @@
1
+ import React from "react";
2
+ import { Box, Text } from "ink";
3
+ import type { Tokens } from "@hauktui/tokens";
4
+ import { useTokens } from "@hauktui/primitives-ink";
5
+
6
+ export interface ProgressProps {
7
+ /** Progress value (0-100) */
8
+ value: number;
9
+ /** Progress bar width in characters */
10
+ width?: number;
11
+ /** Show percentage label */
12
+ showLabel?: boolean;
13
+ /** Label position */
14
+ labelPosition?: "left" | "right" | "inside";
15
+ /** Custom tokens override */
16
+ tokens?: Tokens;
17
+ /** Fill character */
18
+ fillChar?: string;
19
+ /** Empty character */
20
+ emptyChar?: string;
21
+ }
22
+
23
+ export function Progress({
24
+ value,
25
+ width = 20,
26
+ showLabel = true,
27
+ labelPosition = "right",
28
+ tokens: propTokens,
29
+ fillChar = "█",
30
+ emptyChar = "░",
31
+ }: ProgressProps): React.ReactElement {
32
+ const contextTokens = useTokens();
33
+ const tokens = propTokens ?? contextTokens;
34
+
35
+ // Clamp value between 0 and 100
36
+ const clampedValue = Math.max(0, Math.min(100, value));
37
+ const filled = Math.round((clampedValue / 100) * width);
38
+ const empty = width - filled;
39
+
40
+ const bar = fillChar.repeat(filled) + emptyChar.repeat(empty);
41
+ const label = `${Math.round(clampedValue)}%`;
42
+
43
+ // Determine color based on progress
44
+ let color = tokens.colors.accent;
45
+ if (clampedValue >= 100) {
46
+ color = tokens.colors.success;
47
+ } else if (clampedValue < 30) {
48
+ color = tokens.colors.warning;
49
+ }
50
+
51
+ const labelElement = showLabel
52
+ ? React.createElement(Text, { color: tokens.colors.muted }, label)
53
+ : null;
54
+
55
+ if (labelPosition === "inside") {
56
+ return React.createElement(
57
+ Box,
58
+ null,
59
+ React.createElement(Text, { color }, "["),
60
+ React.createElement(Text, { color }, bar),
61
+ React.createElement(Text, { color }, "] "),
62
+ labelElement
63
+ );
64
+ }
65
+
66
+ return React.createElement(
67
+ Box,
68
+ { gap: 1 },
69
+ labelPosition === "left" ? labelElement : null,
70
+ React.createElement(Text, { color }, bar),
71
+ labelPosition === "right" ? labelElement : null
72
+ );
73
+ }
@@ -0,0 +1,2 @@
1
+ export { RadioGroup } from "./radio-group.js";
2
+ export type { RadioGroupProps, RadioOption } from "./radio-group.js";