@bunnix/components 0.9.2 → 0.9.3

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/@types/index.d.ts CHANGED
@@ -11,6 +11,127 @@ export type LegacySize = "xs" | "sm" | "md" | "lg" | "xl" | "default";
11
11
  export type SizeValue = Size | LegacySize;
12
12
  export type SizeNoSmall = Exclude<SizeValue, "small" | "sm">;
13
13
  export type SizeRegularUp = Exclude<SizeValue, "xsmall" | "xs" | "small" | "sm">;
14
+ export type IconNameSlug =
15
+ | "add"
16
+ | "add-circle"
17
+ | "alt"
18
+ | "archive"
19
+ | "at"
20
+ | "attestation"
21
+ | "bell"
22
+ | "bookmark"
23
+ | "bot"
24
+ | "button"
25
+ | "calculate"
26
+ | "calendar"
27
+ | "chart"
28
+ | "check"
29
+ | "chevron-down"
30
+ | "chevron-left"
31
+ | "chevron-right"
32
+ | "clip"
33
+ | "clock"
34
+ | "close"
35
+ | "close-circle"
36
+ | "cloud"
37
+ | "cloud-download"
38
+ | "cloud-upload"
39
+ | "columns-layout"
40
+ | "command"
41
+ | "cube"
42
+ | "delete"
43
+ | "dollar"
44
+ | "download"
45
+ | "draw"
46
+ | "duplicate"
47
+ | "edit"
48
+ | "exclamation-mark"
49
+ | "eye"
50
+ | "eye-open"
51
+ | "file"
52
+ | "file-html"
53
+ | "finger"
54
+ | "flag"
55
+ | "folder"
56
+ | "function"
57
+ | "gear"
58
+ | "gift"
59
+ | "globe"
60
+ | "grid"
61
+ | "hand"
62
+ | "heart"
63
+ | "home"
64
+ | "image"
65
+ | "inbox"
66
+ | "info"
67
+ | "key"
68
+ | "lamp"
69
+ | "link"
70
+ | "location"
71
+ | "locker"
72
+ | "login"
73
+ | "logout"
74
+ | "mail"
75
+ | "map"
76
+ | "markup"
77
+ | "merge"
78
+ | "more-horizontal"
79
+ | "more-vertical"
80
+ | "mouse"
81
+ | "palette"
82
+ | "password"
83
+ | "pencil"
84
+ | "people"
85
+ | "person"
86
+ | "person-add"
87
+ | "person-remove"
88
+ | "pin"
89
+ | "question-circle"
90
+ | "remove-circle"
91
+ | "return-arrow"
92
+ | "save"
93
+ | "search"
94
+ | "sections"
95
+ | "send"
96
+ | "share"
97
+ | "shine"
98
+ | "sliders"
99
+ | "star"
100
+ | "storage"
101
+ | "success-circle"
102
+ | "swap"
103
+ | "switch"
104
+ | "sync"
105
+ | "table"
106
+ | "tag"
107
+ | "terminal"
108
+ | "text"
109
+ | "thumb-down"
110
+ | "thumb-up"
111
+ | "timer"
112
+ | "toggle"
113
+ | "trash"
114
+ | "update-page"
115
+ | "upload"
116
+ | "video"
117
+ | "wallet"
118
+ | "window";
119
+ export type IconName =
120
+ | IconNameSlug
121
+ | `icon-${IconNameSlug}`
122
+ | (string & {});
123
+ export type IconFill =
124
+ | "default"
125
+ | "base"
126
+ | "white"
127
+ | "secondary"
128
+ | "tertiary"
129
+ | "quaternary"
130
+ | "destructive"
131
+ | `icon-${string}`
132
+ | (string & {});
133
+ export type IconSizeClass = "icon-xs" | "icon-sm" | "icon-lg" | "icon-xl";
134
+ export type IconSize = SizeValue | IconSizeClass | (string & {});
14
135
 
15
136
  export interface ButtonProps extends BaseProps {
16
137
  type?: string;
@@ -23,9 +144,9 @@ export interface ButtonProps extends BaseProps {
23
144
  }
24
145
 
25
146
  export interface IconProps extends BaseProps {
26
- name?: string;
27
- fill?: string;
28
- size?: SizeValue | `icon-${string}`;
147
+ name?: IconName;
148
+ fill?: IconFill;
149
+ size?: IconSize;
29
150
  }
30
151
 
31
152
  export interface TextProps extends BaseProps {
@@ -48,6 +169,25 @@ export interface TextProps extends BaseProps {
48
169
  wrap?: "wrap" | "nowrap";
49
170
  }
50
171
 
172
+ export interface ProgressBarProps extends BaseProps {
173
+ value?: number | any;
174
+ min?: number;
175
+ max?: number;
176
+ size?: SizeValue;
177
+ color?: "default"
178
+ | "primary"
179
+ | "primary-dimmed"
180
+ | "secondary"
181
+ | "tertiary"
182
+ | "quaternary"
183
+ | "destructive"
184
+ | "destructive-dimmed"
185
+ | "accent"
186
+ | "accent-dimmed"
187
+ | "white"
188
+ | (string & {});
189
+ }
190
+
51
191
  export interface ContainerProps extends BaseProps {
52
192
  type?: "main" | "content" | "page" | (string & {});
53
193
  direction?: "row" | "column" | (string & {});
@@ -344,6 +484,7 @@ export const NavigationBar: Component<NavigationBarProps>;
344
484
  export const PageHeader: Component<PageHeaderProps>;
345
485
  export const PageSection: Component<PageSectionProps>;
346
486
  export const PopoverMenu: Component<PopoverMenuProps>;
487
+ export const ProgressBar: Component<ProgressBarProps>;
347
488
  export const RadioCheckbox: Component<RadioCheckboxProps>;
348
489
  export const SearchBox: Component<SearchBoxProps>;
349
490
  export const Sidebar: Component<SidebarProps>;
package/README.md CHANGED
@@ -101,7 +101,7 @@ You can compose your own UI using the same CSS utilities the components use:
101
101
  - Typography: `text-default|primary|secondary|tertiary|quaternary`, `text-accent`, `text-destructive`, `text-sm|base|lg|xl`, `text-mono`, `whitespace-nowrap`, `whitespace-pre-line`
102
102
  - Buttons: `btn`, `btn-flat`, `btn-outline`, `btn-destructive`, `btn-lg`, `btn-xl`, `btn-disabled`
103
103
  - Forms: `input-lg`, `input-xl`, `rounded-full` (useful for pill inputs)
104
- - Icons: `icon`, `icon-<name>`, `icon-xs|sm|lg|xl`, `icon-default|base|white|secondary|tertiary|quaternary`
104
+ - Icons: `icon`, `icon-<name>`, `icon-xs|sm|lg|xl`, `icon-default|base|white|secondary|tertiary|quaternary|destructive`
105
105
 
106
106
  Example:
107
107
 
@@ -115,6 +115,12 @@ div({ class: "card row-container gap-sm items-center" }, [
115
115
  ]);
116
116
  ```
117
117
 
118
+ ## Icon props
119
+
120
+ - `name`: icon slug (e.g. `star`) or a full class (e.g. `icon-star`). IDEs should autocomplete shipped icon names.
121
+ - `fill`: `default | base | white | secondary | tertiary | quaternary | destructive` or any `icon-*` utility.
122
+ - `size`: `xsmall | small | regular | large | xlarge` or `icon-xs | icon-sm | icon-lg | icon-xl`.
123
+
118
124
  ## Minimal webpack config
119
125
 
120
126
  This is a minimal setup that works with `@bunnix/components` (ESM, CSS, and SVG assets):
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bunnix/components",
3
- "version": "0.9.2",
3
+ "version": "0.9.3",
4
4
  "description": "Bunnix components: a set of bunnix ready components for modern web apps.",
5
5
  "keywords": [
6
6
  "bunnix",
@@ -136,7 +136,7 @@ export default function Dialog() {
136
136
  VStack({
137
137
  ref: panelRef,
138
138
  gap: "regular",
139
- class: "box-capsule dialog-panel shadow bg-base p-lg items-stretch dialog-appear"
139
+ class: "box-capsule dialog-panel shadow bg-base border-solid p-lg items-stretch dialog-appear"
140
140
  }, [
141
141
  HStack({ alignment: "leading", gap: "small", class: "items-center w-full" }, [
142
142
  Text({ type: "heading4", class: "no-margin" }, dialogState.map((value) => value.title)),
@@ -0,0 +1,81 @@
1
+ import Bunnix from "@bunnix/core";
2
+ import { clampSize, toSizeToken } from "../utils/sizeUtils.mjs";
3
+
4
+ const { div } = Bunnix;
5
+
6
+ export default function ProgressBar({
7
+ value = 0,
8
+ min = 0,
9
+ max = 100,
10
+ size,
11
+ color = "default",
12
+ class: className = "",
13
+ ...rest
14
+ } = {}) {
15
+ const isState = (val) => val && typeof val.map === "function";
16
+ const normalizeSize = (val) =>
17
+ clampSize(val, ["xsmall", "small", "regular", "large", "xlarge"], "regular");
18
+
19
+ const sizeState = isState(size) ? size : null;
20
+ const classState = isState(className) ? className : null;
21
+ const colorState = isState(color) ? color : null;
22
+ const valueState = isState(value) ? value : null;
23
+ const minState = isState(min) ? min : null;
24
+ const maxState = isState(max) ? max : null;
25
+
26
+ const buildClass = (sizeValue, classValue) => {
27
+ const normalizedSize = normalizeSize(sizeValue);
28
+ const sizeToken = toSizeToken(normalizedSize);
29
+ const sizeClass = sizeToken ? `progress-bar-${sizeToken}` : "";
30
+ return `progress-bar ${sizeClass} ${classValue || ""}`.trim();
31
+ };
32
+
33
+ const buildFillClass = (colorValue) => {
34
+ const resolvedColor = colorValue || "default";
35
+ return `progress-bar-fill text-${resolvedColor}`.trim();
36
+ };
37
+
38
+ const clampPercent = (val, minValue, maxValue) => {
39
+ const safeMin = Number.isFinite(Number(minValue)) ? Number(minValue) : 0;
40
+ const safeMax = Number.isFinite(Number(maxValue)) ? Number(maxValue) : 100;
41
+ const safeValue = Number.isFinite(Number(val)) ? Number(val) : 0;
42
+
43
+ if (safeMax <= safeMin) return 0;
44
+
45
+ const rawPercent = ((safeValue - safeMin) / (safeMax - safeMin)) * 100;
46
+ return Math.min(100, Math.max(0, rawPercent));
47
+ };
48
+
49
+ const buildFillStyle = (val, minValue, maxValue) =>
50
+ `width: ${clampPercent(val, minValue, maxValue)}%;`;
51
+
52
+ const combinedClass = sizeState
53
+ ? sizeState.map((value) => buildClass(value, classState ? classState.get() : className))
54
+ : classState
55
+ ? classState.map((value) => buildClass(size, value))
56
+ : buildClass(size, className);
57
+
58
+ const fillClass = colorState ? colorState.map((value) => buildFillClass(value)) : buildFillClass(color);
59
+
60
+ const fillStyle = valueState
61
+ ? valueState.map((val) =>
62
+ buildFillStyle(val, minState ? minState.get() : min, maxState ? maxState.get() : max)
63
+ )
64
+ : minState
65
+ ? minState.map((val) => buildFillStyle(value, val, maxState ? maxState.get() : max))
66
+ : maxState
67
+ ? maxState.map((val) => buildFillStyle(value, min, val))
68
+ : buildFillStyle(value, min, max);
69
+
70
+ return div(
71
+ {
72
+ class: combinedClass,
73
+ role: "progressbar",
74
+ "aria-valuemin": min,
75
+ "aria-valuemax": max,
76
+ "aria-valuenow": value,
77
+ ...rest,
78
+ },
79
+ [div({ class: fillClass, style: fillStyle })],
80
+ );
81
+ }
package/src/index.mjs CHANGED
@@ -17,6 +17,7 @@ export { default as NavigationBar } from "./components/NavigationBar.mjs";
17
17
  export { default as PageHeader } from "./components/PageHeader.mjs";
18
18
  export { default as PageSection } from "./components/PageSection.mjs";
19
19
  export { default as PopoverMenu } from "./components/PopoverMenu.mjs";
20
+ export { default as ProgressBar } from "./components/ProgressBar.mjs";
20
21
  export { default as RadioCheckbox } from "./components/RadioCheckbox.mjs";
21
22
  export { default as SearchBox } from "./components/SearchBox.mjs";
22
23
  export { default as Sidebar } from "./components/Sidebar.mjs";
@@ -201,6 +201,41 @@ textarea::placeholder {
201
201
  color: white;
202
202
  }
203
203
 
204
+ /* Progress Bar */
205
+ .progress-bar {
206
+ width: 100%;
207
+ background-color: var(--alternate-background-color);
208
+ border-radius: var(--min-control-radius);
209
+ overflow: hidden;
210
+ }
211
+
212
+ .progress-bar-fill {
213
+ height: 100%;
214
+ width: 0%;
215
+ background-color: currentColor;
216
+ transition: width 0.2s ease;
217
+ }
218
+
219
+ .progress-bar-xs {
220
+ height: 0.25rem;
221
+ }
222
+
223
+ .progress-bar-sm {
224
+ height: 0.375rem;
225
+ }
226
+
227
+ .progress-bar-md {
228
+ height: 0.5rem;
229
+ }
230
+
231
+ .progress-bar-lg {
232
+ height: 0.75rem;
233
+ }
234
+
235
+ .progress-bar-xl {
236
+ height: 1rem;
237
+ }
238
+
204
239
  .badge-solid.badge-dimmed {
205
240
  background-color: var(--highlight-background-color);
206
241
  color: var(--color-secondary);