@bunnix/components 0.9.0

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 (152) hide show
  1. package/@types/index.d.ts +269 -0
  2. package/LICENSE +7 -0
  3. package/README.md +184 -0
  4. package/package.json +53 -0
  5. package/src/components/AccordionGroup.mjs +37 -0
  6. package/src/components/Badge.mjs +49 -0
  7. package/src/components/Button.mjs +76 -0
  8. package/src/components/Checkbox.mjs +36 -0
  9. package/src/components/ComboBox.mjs +44 -0
  10. package/src/components/Container.mjs +27 -0
  11. package/src/components/DatePicker.mjs +251 -0
  12. package/src/components/Dialog.mjs +166 -0
  13. package/src/components/DropdownMenu.mjs +110 -0
  14. package/src/components/Grid.mjs +40 -0
  15. package/src/components/HStack.mjs +34 -0
  16. package/src/components/Icon.mjs +32 -0
  17. package/src/components/InputField.mjs +78 -0
  18. package/src/components/NavigationBar.mjs +47 -0
  19. package/src/components/PageHeader.mjs +13 -0
  20. package/src/components/PageSection.mjs +20 -0
  21. package/src/components/PopoverMenu.mjs +87 -0
  22. package/src/components/RadioCheckbox.mjs +36 -0
  23. package/src/components/SearchBox.mjs +207 -0
  24. package/src/components/Sidebar.mjs +187 -0
  25. package/src/components/Table.mjs +254 -0
  26. package/src/components/Text.mjs +38 -0
  27. package/src/components/TimePicker.mjs +172 -0
  28. package/src/components/ToastNotification.mjs +105 -0
  29. package/src/components/ToggleSwitch.mjs +26 -0
  30. package/src/components/VStack.mjs +35 -0
  31. package/src/icons/add-circle.svg +1 -0
  32. package/src/icons/add.svg +1 -0
  33. package/src/icons/alt.svg +1 -0
  34. package/src/icons/archive.svg +1 -0
  35. package/src/icons/at.svg +1 -0
  36. package/src/icons/attestation.svg +1 -0
  37. package/src/icons/bell.svg +4 -0
  38. package/src/icons/bookmark.svg +1 -0
  39. package/src/icons/bot.svg +1 -0
  40. package/src/icons/button.svg +1 -0
  41. package/src/icons/calculate.svg +1 -0
  42. package/src/icons/calendar.svg +1 -0
  43. package/src/icons/chart.svg +1 -0
  44. package/src/icons/check.svg +1 -0
  45. package/src/icons/chevron-down.svg +1 -0
  46. package/src/icons/chevron-left.svg +1 -0
  47. package/src/icons/chevron-right.svg +1 -0
  48. package/src/icons/clip.svg +1 -0
  49. package/src/icons/clock.svg +4 -0
  50. package/src/icons/close-circle.svg +4 -0
  51. package/src/icons/close.svg +1 -0
  52. package/src/icons/cloud-download.svg +1 -0
  53. package/src/icons/cloud-upload.svg +1 -0
  54. package/src/icons/cloud.svg +1 -0
  55. package/src/icons/columns-layout.svg +1 -0
  56. package/src/icons/command.svg +1 -0
  57. package/src/icons/cube.svg +1 -0
  58. package/src/icons/delete.svg +4 -0
  59. package/src/icons/dollar.svg +4 -0
  60. package/src/icons/download.svg +1 -0
  61. package/src/icons/draw.svg +1 -0
  62. package/src/icons/duplicate.svg +4 -0
  63. package/src/icons/edit.svg +1 -0
  64. package/src/icons/exclamation-mark.svg +1 -0
  65. package/src/icons/eye-open.svg +1 -0
  66. package/src/icons/eye.svg +1 -0
  67. package/src/icons/file-html.svg +1 -0
  68. package/src/icons/file.svg +4 -0
  69. package/src/icons/finger.svg +1 -0
  70. package/src/icons/flag.svg +1 -0
  71. package/src/icons/folder.svg +1 -0
  72. package/src/icons/function.svg +1 -0
  73. package/src/icons/gear.svg +1 -0
  74. package/src/icons/gift.svg +1 -0
  75. package/src/icons/globe.svg +4 -0
  76. package/src/icons/grid.svg +1 -0
  77. package/src/icons/hand.svg +1 -0
  78. package/src/icons/heart.svg +4 -0
  79. package/src/icons/home.svg +4 -0
  80. package/src/icons/image.svg +1 -0
  81. package/src/icons/inbox.svg +4 -0
  82. package/src/icons/info.svg +1 -0
  83. package/src/icons/key.svg +1 -0
  84. package/src/icons/lamp.svg +1 -0
  85. package/src/icons/link.svg +1 -0
  86. package/src/icons/location.svg +1 -0
  87. package/src/icons/locker.svg +1 -0
  88. package/src/icons/login.svg +1 -0
  89. package/src/icons/logout.svg +4 -0
  90. package/src/icons/mail.svg +4 -0
  91. package/src/icons/map.svg +4 -0
  92. package/src/icons/markup.svg +1 -0
  93. package/src/icons/merge.svg +1 -0
  94. package/src/icons/more-horizontal.svg +5 -0
  95. package/src/icons/more-vertical.svg +5 -0
  96. package/src/icons/mouse.svg +1 -0
  97. package/src/icons/palette.svg +1 -0
  98. package/src/icons/password.svg +1 -0
  99. package/src/icons/pencil.svg +1 -0
  100. package/src/icons/people.svg +4 -0
  101. package/src/icons/person-add.svg +1 -0
  102. package/src/icons/person-remove.svg +1 -0
  103. package/src/icons/person.svg +5 -0
  104. package/src/icons/pin.svg +1 -0
  105. package/src/icons/question-circle.svg +4 -0
  106. package/src/icons/remove-circle.svg +1 -0
  107. package/src/icons/return-arrow.svg +2 -0
  108. package/src/icons/save.svg +1 -0
  109. package/src/icons/search.svg +1 -0
  110. package/src/icons/sections.svg +1 -0
  111. package/src/icons/send.svg +1 -0
  112. package/src/icons/share.svg +1 -0
  113. package/src/icons/shine.svg +1 -0
  114. package/src/icons/sliders.svg +1 -0
  115. package/src/icons/star.svg +4 -0
  116. package/src/icons/storage.svg +1 -0
  117. package/src/icons/success-circle.svg +4 -0
  118. package/src/icons/swap.svg +1 -0
  119. package/src/icons/switch.svg +1 -0
  120. package/src/icons/sync.svg +4 -0
  121. package/src/icons/table.svg +4 -0
  122. package/src/icons/tag.svg +4 -0
  123. package/src/icons/terminal.svg +1 -0
  124. package/src/icons/text.svg +1 -0
  125. package/src/icons/thumb-down.svg +1 -0
  126. package/src/icons/thumb-up.svg +1 -0
  127. package/src/icons/timer.svg +4 -0
  128. package/src/icons/toggle.svg +1 -0
  129. package/src/icons/trash.svg +1 -0
  130. package/src/icons/update-page.svg +1 -0
  131. package/src/icons/upload.svg +1 -0
  132. package/src/icons/video.svg +1 -0
  133. package/src/icons/wallet.svg +1 -0
  134. package/src/icons/window.svg +1 -0
  135. package/src/index.mjs +29 -0
  136. package/src/styles/accordion.css +70 -0
  137. package/src/styles/buttons.css +118 -0
  138. package/src/styles/colors.css +131 -0
  139. package/src/styles/controls.css +504 -0
  140. package/src/styles/datepicker.css +140 -0
  141. package/src/styles/interactable.css +16 -0
  142. package/src/styles/layout.css +444 -0
  143. package/src/styles/links.css +38 -0
  144. package/src/styles/main.css +16 -0
  145. package/src/styles/media.css +155 -0
  146. package/src/styles/menu.css +168 -0
  147. package/src/styles/motion.css +66 -0
  148. package/src/styles/table.css +78 -0
  149. package/src/styles/timepicker.css +87 -0
  150. package/src/styles/typography.css +94 -0
  151. package/src/styles/variables.css +218 -0
  152. package/src/styles.css +1 -0
@@ -0,0 +1,269 @@
1
+ export type BunnixChild = any;
2
+ export type BunnixChildren = BunnixChild | BunnixChild[];
3
+
4
+ export interface BaseProps {
5
+ class?: string;
6
+ [key: string]: unknown;
7
+ }
8
+
9
+ export interface ButtonProps extends BaseProps {
10
+ type?: string;
11
+ variant?: string;
12
+ size?: "sm" | "md" | "lg" | "xl" | string;
13
+ href?: string;
14
+ disabled?: boolean;
15
+ onClick?: (event?: any) => void;
16
+ click?: (event?: any) => void;
17
+ }
18
+
19
+ export interface IconProps extends BaseProps {
20
+ name?: string;
21
+ fill?: string;
22
+ size?: "xs" | "sm" | "md" | "lg" | "xl" | string;
23
+ }
24
+
25
+ export interface TextProps extends BaseProps {
26
+ type?: "text" | "paragraph" | "heading1" | "heading2" | "heading3" | "heading4" | string;
27
+ color?: string;
28
+ design?: "regular" | "mono" | string;
29
+ }
30
+
31
+ export interface ContainerProps extends BaseProps {
32
+ type?: "main" | "content" | "page" | string;
33
+ direction?: "horizontal" | "vertical" | string;
34
+ }
35
+
36
+ export interface StackProps extends BaseProps {
37
+ alignment?: "leading" | "middle" | "trailing" | string;
38
+ gap?: "small" | "regular" | "large" | string;
39
+ }
40
+
41
+ export interface BadgeProps extends BaseProps {
42
+ tone?: "base" | "success" | "info" | "warning" | "danger" | "accent" | "dimmed" | string;
43
+ size?: "xs" | "sm" | "md" | string;
44
+ variant?: "solid" | "soft" | "outline" | string;
45
+ icon?: string;
46
+ overlap?: boolean;
47
+ shape?: "capsule" | "circle" | string;
48
+ }
49
+
50
+ export interface InputFieldProps extends BaseProps {
51
+ type?: string;
52
+ variant?: "regular" | "rounded" | string;
53
+ value?: string;
54
+ placeholder?: string;
55
+ label?: string;
56
+ disabled?: boolean;
57
+ suggestions?: string[];
58
+ onInput?: (event?: any) => void;
59
+ onChange?: (event?: any) => void;
60
+ onFocus?: (event?: any) => void;
61
+ onBlur?: (event?: any) => void;
62
+ onKeyDown?: (event?: any) => void;
63
+ input?: (event?: any) => void;
64
+ change?: (event?: any) => void;
65
+ focus?: (event?: any) => void;
66
+ blur?: (event?: any) => void;
67
+ keydown?: (event?: any) => void;
68
+ }
69
+
70
+ export interface ComboBoxOption {
71
+ value: string;
72
+ label?: string;
73
+ }
74
+
75
+ export interface ComboBoxProps extends BaseProps {
76
+ options?: Array<string | ComboBoxOption>;
77
+ selection?: any;
78
+ size?: "sm" | "md" | "lg" | "xl" | string;
79
+ onChange?: (event?: any) => void;
80
+ change?: (event?: any) => void;
81
+ }
82
+
83
+ export interface CheckboxProps extends BaseProps {
84
+ labelText?: string;
85
+ size?: "sm" | "md" | "lg" | "xl" | string;
86
+ onCheck?: (checked: boolean) => void;
87
+ check?: (checked: boolean) => void;
88
+ onChange?: (event?: any) => void;
89
+ }
90
+
91
+ export interface RadioCheckboxProps extends BaseProps {
92
+ labelText?: string;
93
+ size?: "sm" | "md" | "lg" | "xl" | string;
94
+ onCheck?: (checked: boolean) => void;
95
+ check?: (checked: boolean) => void;
96
+ onChange?: (event?: any) => void;
97
+ }
98
+
99
+ export interface ToggleSwitchProps extends BaseProps {
100
+ labelText?: string;
101
+ size?: "sm" | "md" | "lg" | "xl" | string;
102
+ onChange?: (event?: any) => void;
103
+ }
104
+
105
+ export interface SearchBoxItem {
106
+ title?: string;
107
+ snippet?: string;
108
+ icon?: string;
109
+ [key: string]: unknown;
110
+ }
111
+
112
+ export interface SearchBoxProps extends BaseProps {
113
+ data?: SearchBoxItem[];
114
+ value?: string | any;
115
+ placeholder?: string;
116
+ onInput?: (event?: any) => void;
117
+ input?: (event?: any) => void;
118
+ size?: "sm" | "md" | "lg" | "xl" | string;
119
+ variant?: "regular" | "rounded" | string;
120
+ onSelect?: (item?: SearchBoxItem) => void;
121
+ select?: (item?: SearchBoxItem) => void;
122
+ }
123
+
124
+ export interface SidebarItem {
125
+ id?: string;
126
+ label?: string;
127
+ icon?: string;
128
+ href?: string | null;
129
+ badge?: string | number | { value?: string | number; tone?: string; variant?: string; size?: string };
130
+ children?: SidebarItem[];
131
+ isExpanded?: boolean;
132
+ isHeader?: boolean;
133
+ isSeparator?: boolean;
134
+ [key: string]: unknown;
135
+ }
136
+
137
+ export interface SidebarProps extends BaseProps {
138
+ items?: SidebarItem[];
139
+ selection?: string;
140
+ onSelect?: (id?: string) => void;
141
+ onItemSelect?: (id?: string) => void;
142
+ searchable?: boolean;
143
+ searchProps?: Record<string, unknown>;
144
+ }
145
+
146
+ export interface NavigationBarProps extends BaseProps {
147
+ title?: string | (() => BunnixChildren);
148
+ leading?: BunnixChildren | (() => BunnixChildren);
149
+ trailing?: BunnixChildren | (() => BunnixChildren);
150
+ searchable?: boolean;
151
+ searchData?: SearchBoxItem[];
152
+ searchValue?: string | any;
153
+ onSearchInput?: (event?: any) => void;
154
+ onSearchSelect?: (item?: SearchBoxItem) => void;
155
+ searchProps?: Record<string, unknown>;
156
+ }
157
+
158
+ export interface DatePickerProps extends BaseProps {
159
+ id?: string;
160
+ placeholder?: string;
161
+ range?: boolean;
162
+ variant?: "regular" | "rounded" | string;
163
+ size?: "md" | "lg" | "xl" | string;
164
+ }
165
+
166
+ export interface TimePickerProps extends BaseProps {
167
+ id?: string;
168
+ placeholder?: string;
169
+ variant?: "regular" | "rounded" | string;
170
+ size?: "md" | "lg" | "xl" | string;
171
+ }
172
+
173
+ export interface DropdownMenuItem {
174
+ title?: string;
175
+ icon?: string;
176
+ destructive?: boolean;
177
+ isSeparator?: boolean;
178
+ selected?: boolean;
179
+ click?: () => void;
180
+ [key: string]: unknown;
181
+ }
182
+
183
+ export interface DropdownMenuProps extends BaseProps {
184
+ items?: DropdownMenuItem[];
185
+ id?: string;
186
+ align?: "left" | "right" | string;
187
+ placeholder?: string;
188
+ size?: "sm" | "md" | "lg" | "xl" | string;
189
+ onSelect?: (item?: DropdownMenuItem) => void;
190
+ }
191
+
192
+ export interface PopoverMenuProps extends BaseProps {
193
+ trigger?: BunnixChildren;
194
+ items?: DropdownMenuItem[];
195
+ id?: string;
196
+ align?: "left" | "right" | string;
197
+ size?: "sm" | "md" | "lg" | "xl" | string;
198
+ onSelect?: (item?: DropdownMenuItem) => void;
199
+ }
200
+
201
+ export interface DialogConfirmation {
202
+ text?: string;
203
+ action?: () => void;
204
+ variant?: string;
205
+ disabled?: boolean;
206
+ extra?: { text?: string; action?: () => void };
207
+ }
208
+
209
+ export interface ShowDialogOptions {
210
+ title?: string;
211
+ message?: string;
212
+ confirmation?: DialogConfirmation;
213
+ content?: (args: { setConfirmDisabled: (disabled: boolean) => void }) => BunnixChildren;
214
+ }
215
+
216
+ export interface ToastOptions {
217
+ message?: string;
218
+ duration?: number;
219
+ anchor?: "topRight" | "topLeft" | "bottomRight" | "bottomLeft" | string;
220
+ size?: "md" | "lg" | "xl" | string;
221
+ icon?: string;
222
+ }
223
+
224
+ export type Component<P = BaseProps> = (props?: P, children?: BunnixChildren) => any;
225
+
226
+ export const AccordionGroup: Component<BaseProps>;
227
+ export const Badge: Component<BadgeProps>;
228
+ export const Button: Component<ButtonProps>;
229
+ export const Checkbox: Component<CheckboxProps>;
230
+ export const ComboBox: Component<ComboBoxProps>;
231
+ export const Container: Component<ContainerProps>;
232
+ export const DatePicker: Component<DatePickerProps>;
233
+ export const Dialog: Component<BaseProps>;
234
+ export const DropdownMenu: Component<DropdownMenuProps>;
235
+ export const Grid: Component<BaseProps>;
236
+ export const HStack: Component<StackProps>;
237
+ export const Icon: Component<IconProps>;
238
+ export const InputField: Component<InputFieldProps>;
239
+ export const NavigationBar: Component<NavigationBarProps>;
240
+ export const PageHeader: Component<BaseProps>;
241
+ export const PageSection: Component<BaseProps>;
242
+ export const PopoverMenu: Component<PopoverMenuProps>;
243
+ export const RadioCheckbox: Component<RadioCheckboxProps>;
244
+ export const SearchBox: Component<SearchBoxProps>;
245
+ export const Sidebar: Component<SidebarProps>;
246
+ export const Table: Component<BaseProps>;
247
+ export const Text: Component<TextProps>;
248
+ export const TimePicker: Component<TimePickerProps>;
249
+ export const ToastNotification: Component<BaseProps>;
250
+ export const ToggleSwitch: Component<ToggleSwitchProps>;
251
+ export const VStack: Component<StackProps>;
252
+
253
+ export const dialogState: any;
254
+ export function showDialog(options?: ShowDialogOptions): void;
255
+ export function hideDialog(): void;
256
+
257
+ export const toastState: any;
258
+ export function showToast(options?: ToastOptions): void;
259
+ export function hideToast(): void;
260
+
261
+ declare module "@bunnix/components/styles.css" {
262
+ const content: string;
263
+ export default content;
264
+ }
265
+
266
+ declare module "@bunnix/components/styles" {
267
+ const content: string;
268
+ export default content;
269
+ }
package/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ ISC License
2
+
3
+ Copyright (c) 2025 Morisson Maciel
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,184 @@
1
+ # @bunnix/components
2
+
3
+ Design system + UI components for Bunnix projects. This package ships ESM source with CSS + icon assets and is intended for modern, module-based builds (webpack/vite/rollup/etc).
4
+
5
+ Disclaimer:
6
+ - This project is currently in **alpha release candidate**. APIs and components may change until the first stable release.
7
+ - Icon usage & attribution:
8
+ - COLLECTION: Framework7 Line Icons — LICENSE: MIT License — AUTHOR: framework7io
9
+ - COLLECTION: Iconcino Interface Icons — LICENSE: CC0 1.0 — AUTHOR: Gabriele Malaspina
10
+ - Sources (copy/paste): `https://framework7.io/icons/` and `https://iconcino.com/`
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ npm install @bunnix/components @bunnix/core
16
+ ```
17
+
18
+ `@bunnix/core` is a peer dependency (required by design).
19
+
20
+ ## Usage
21
+
22
+ Import the CSS once at your app entry:
23
+
24
+ ```js
25
+ import "@bunnix/components/styles.css";
26
+ ```
27
+
28
+ Then import components as needed:
29
+
30
+ ```js
31
+ import { Button, Icon, Text } from "@bunnix/components";
32
+
33
+ export default function Example() {
34
+ return Button({ variant: "regular" }, [
35
+ Icon({ name: "star", fill: "white" }),
36
+ Text({ type: "text" }, "Star")
37
+ ]);
38
+ }
39
+ ```
40
+
41
+ ## Component examples
42
+
43
+ Button with icon:
44
+
45
+ ```js
46
+ import { Button, Icon } from "@bunnix/components";
47
+
48
+ Button({ variant: "regular" }, [
49
+ Icon({ name: "star", fill: "white" }),
50
+ "Star"
51
+ ]);
52
+ ```
53
+
54
+ Search box with suggestions:
55
+
56
+ ```js
57
+ import { SearchBox } from "@bunnix/components";
58
+
59
+ SearchBox({
60
+ data: [
61
+ { title: "Users", snippet: "Manage users", icon: "person" },
62
+ { title: "Settings", snippet: "Configure app", icon: "gear" }
63
+ ]
64
+ });
65
+ ```
66
+
67
+ Dialog + toast helpers:
68
+
69
+ ```js
70
+ import { Dialog, showDialog, ToastNotification, showToast } from "@bunnix/components";
71
+
72
+ showDialog({ title: "Welcome", message: "Bunnix components ready." });
73
+ showToast({ message: "Saved successfully", icon: "success-circle" });
74
+ ```
75
+
76
+ ## Assets (icons)
77
+
78
+ SVG icons are shipped with the package and referenced by CSS variables. If your bundler rewrites asset URLs, make sure it processes CSS `url(...)` values from `@bunnix/components`.
79
+
80
+ ## Theming
81
+
82
+ Override CSS variables after importing the stylesheet:
83
+
84
+ ```css
85
+ :root {
86
+ --accent-color: #2563eb;
87
+ --background-color: #ffffff;
88
+ --border-color: #e5e7eb;
89
+ --base-padding: 0.75rem;
90
+ --base-gap: 0.6rem;
91
+ --font-family: "Inter", system-ui, sans-serif;
92
+ }
93
+ ```
94
+
95
+ ## CSS modifiers (utilities)
96
+
97
+ You can compose your own UI using the same CSS utilities the components use:
98
+
99
+ - Layout: `row-container`, `column-container`, `grid-flow`, `gap-xs|sm|md|lg`, `items-start|center|end|stretch`, `justify-start|center|end`, `w-full`, `h-full`, `spacer-h`, `spacer-v`
100
+ - Surfaces: `box`, `box-sm`, `box-control`, `box-capsule`, `card`, `shadow`, `rounded|rounded-sm|rounded-full`
101
+ - Typography: `text-primary|secondary|tertiary|quaternary`, `text-accent`, `text-destructive`, `text-sm|base|lg|xl`, `text-mono`, `whitespace-nowrap`, `whitespace-pre-line`
102
+ - Buttons: `btn`, `btn-flat`, `btn-outline`, `btn-destructive`, `btn-lg`, `btn-xl`, `btn-disabled`
103
+ - Forms: `input-lg`, `input-xl`, `rounded-full` (useful for pill inputs)
104
+ - Icons: `icon`, `icon-<name>`, `icon-xs|sm|lg|xl`, `icon-base|white|secondary|tertiary|quaternary`
105
+
106
+ Example:
107
+
108
+ ```js
109
+ import Bunnix from "@bunnix/core";
110
+ const { div, span } = Bunnix;
111
+
112
+ div({ class: "card row-container gap-sm items-center" }, [
113
+ span({ class: "icon icon-star icon-base icon-sm" }),
114
+ span({ class: "text-primary text-sm" }, "Custom card")
115
+ ]);
116
+ ```
117
+
118
+ ## Minimal webpack config
119
+
120
+ This is a minimal setup that works with `@bunnix/components` (ESM, CSS, and SVG assets):
121
+
122
+ ```js
123
+ import path from "path";
124
+ import { fileURLToPath } from "url";
125
+ import HtmlWebpackPlugin from "html-webpack-plugin";
126
+
127
+ const __filename = fileURLToPath(import.meta.url);
128
+ const __dirname = path.dirname(__filename);
129
+
130
+ export default {
131
+ entry: "./src/index.mjs",
132
+ output: {
133
+ filename: "main.js",
134
+ path: path.resolve(__dirname, "dist"),
135
+ clean: true,
136
+ assetModuleFilename: "images/[hash][ext][query]"
137
+ },
138
+ mode: "development",
139
+ module: {
140
+ rules: [
141
+ { test: /\\.css$/i, use: ["style-loader", "css-loader"] },
142
+ { test: /\\.(png|svg|jpg|jpeg|gif)$/i, type: "asset/resource" }
143
+ ]
144
+ },
145
+ resolve: {
146
+ extensions: [".mjs", ".js", ".json"]
147
+ },
148
+ plugins: [
149
+ new HtmlWebpackPlugin({ template: "./public/index.html" })
150
+ ],
151
+ devServer: {
152
+ static: { directory: path.join(__dirname, "public") },
153
+ compress: true,
154
+ port: 3000,
155
+ open: true
156
+ }
157
+ };
158
+ ```
159
+
160
+ ## Project structure
161
+
162
+ ```
163
+ src/
164
+ components/ # exported components
165
+ styles/ # design system CSS
166
+ icons/ # SVG assets
167
+ index.mjs # package exports
168
+ styles.css # CSS entry
169
+ playgrounds/ # local showcase (uses the package)
170
+ ```
171
+
172
+ ## Playground
173
+
174
+ The playground consumes `@bunnix/components` directly. Start it with your preferred dev server (whatever you use today), and ensure the CSS import stays at `playgrounds/src/index.mjs`.
175
+
176
+ ## Publishing notes
177
+
178
+ - This package targets ESM consumers and ships source files (no dist build).
179
+ - Ensure `files` and `exports` in `package.json` include `src/` and CSS entries.
180
+ - GitHub CI should run with Node that supports ESM (Node 16+ recommended).
181
+
182
+ ## License
183
+
184
+ ISC
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@bunnix/components",
3
+ "version": "0.9.0",
4
+ "description": "Bunnix components: a set of bunnix ready components for modern web apps.",
5
+ "keywords": [
6
+ "bunnix",
7
+ "componentes",
8
+ "reactive",
9
+ "stateful",
10
+ "web",
11
+ "app"
12
+ ],
13
+ "homepage": "https://github.com/bunnix-js/bunnix-components#readme",
14
+ "bugs": {
15
+ "url": "https://github.com/bunnix-js/bunnix-components/issues"
16
+ },
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/bunnix-js/bunnix-components.git"
20
+ },
21
+ "license": "ISC",
22
+ "author": "@bunnix",
23
+ "type": "module",
24
+ "main": "src/index.mjs",
25
+ "style": "src/styles.css",
26
+ "types": "@types/index.d.ts",
27
+ "exports": {
28
+ ".": {
29
+ "types": "./@types/index.d.ts",
30
+ "default": "./src/index.mjs"
31
+ },
32
+ "./styles": "./src/styles.css",
33
+ "./styles.css": "./src/styles.css",
34
+ "./icons/*": "./src/icons/*"
35
+ },
36
+ "sideEffects": [
37
+ "./src/styles.css",
38
+ "./src/styles/*.css"
39
+ ],
40
+ "files": [
41
+ "src",
42
+ "@types"
43
+ ],
44
+ "scripts": {
45
+ "test": "node --test"
46
+ },
47
+ "peerDependencies": {
48
+ "@bunnix/core": "^0.9.4"
49
+ },
50
+ "devDependencies": {
51
+ "@bunnix/core": "^0.9.4"
52
+ }
53
+ }
@@ -0,0 +1,37 @@
1
+ import Bunnix, { useState } from "@bunnix/core";
2
+ const { div, button, span, p } = Bunnix;
3
+
4
+ export default function AccordionGroup({ items = [], class: className = "", initialIndex } = {}) {
5
+ const resolvedInitial = Number.isInteger(initialIndex)
6
+ ? initialIndex
7
+ : items.findIndex((item) => item?.open);
8
+ const openIndex = useState(resolvedInitial >= 0 ? resolvedInitial : null);
9
+
10
+ const handleToggle = (index) => {
11
+ const current = openIndex.get();
12
+ openIndex.set(current === index ? null : index);
13
+ };
14
+
15
+ return div(
16
+ { class: `accordion-group ${className}`.trim() },
17
+ items.map((item, index) => {
18
+ const isOpen = openIndex.map((value) => value === index);
19
+ const iconClass = item.icon ? item.icon : "icon-add";
20
+ const description = item.description ?? item.content ?? "";
21
+
22
+ return div({ class: isOpen.map((open) => `accordion-item hoverable ${open ? "accordion-open" : ""}`) }, [
23
+ button({ class: "accordion-header", click: () => handleToggle(index) }, [
24
+ span({ class: isOpen.map((open) => `icon accordion-icon ${iconClass} ${open ? "bg-accent" : "bg-primary"}`) }),
25
+ span({ class: "accordion-title" }, item.title)
26
+ ]),
27
+ div({ class: "accordion-content" }, [
28
+ div({ class: "accordion-body" }, [
29
+ typeof description === "string"
30
+ ? p({ class: "accordion-description" }, description)
31
+ : description
32
+ ])
33
+ ])
34
+ ]);
35
+ })
36
+ );
37
+ }
@@ -0,0 +1,49 @@
1
+ import Bunnix from "@bunnix/core";
2
+ import Icon from "./Icon.mjs";
3
+
4
+ const { span } = Bunnix;
5
+
6
+ const toneClassMap = {
7
+ base: "badge-base",
8
+ success: "badge-success",
9
+ info: "badge-info",
10
+ warning: "badge-warning",
11
+ danger: "badge-danger",
12
+ accent: "badge-accent",
13
+ dimmed: "badge-dimmed"
14
+ };
15
+
16
+ const sizeClassMap = {
17
+ xs: "badge-xs",
18
+ sm: "badge-sm",
19
+ md: "badge-md"
20
+ };
21
+
22
+ const variantClassMap = {
23
+ solid: "badge-solid",
24
+ soft: "badge-soft",
25
+ outline: "badge-outline"
26
+ };
27
+
28
+ export default function Badge({
29
+ tone = "base",
30
+ size = "sm",
31
+ variant = "solid",
32
+ icon,
33
+ overlap = false,
34
+ shape = "capsule",
35
+ class: className = ""
36
+ } = {}, children) {
37
+ const toneClass = toneClassMap[tone] || toneClassMap.base;
38
+ const sizeClass = sizeClassMap[size] || sizeClassMap.sm;
39
+ const variantClass = variantClassMap[variant] || variantClassMap.solid;
40
+ const iconSize = size === "md" ? "lg" : size === "xs" ? "xs" : "sm";
41
+ const overlapClass = overlap ? "badge-overlap" : "";
42
+ const shapeClass = shape === "circle" ? "badge-circle" : "";
43
+ const combinedClass = `badge ${toneClass} ${sizeClass} ${variantClass} ${overlapClass} ${shapeClass} ${className}`.trim();
44
+
45
+ return span({ class: combinedClass }, [
46
+ icon ? Icon({ name: icon, fill: "current", size: iconSize }) : null,
47
+ children
48
+ ]);
49
+ }
@@ -0,0 +1,76 @@
1
+ import Bunnix from "@bunnix/core";
2
+ const { button, a } = Bunnix;
3
+
4
+ export default function Button({
5
+ type = "button",
6
+ variant = "regular",
7
+ size,
8
+ href,
9
+ disabled = false,
10
+ onClick,
11
+ click,
12
+ class: className = "",
13
+ ...rest
14
+ } = {}, children) {
15
+ const normalizeSize = (value) => {
16
+ if (!value || value === "default" || value === "regular" || value === "md") return "md";
17
+ if (value === "sm") return "sm";
18
+ if (value === "lg" || value === "xl") return value;
19
+ return value;
20
+ };
21
+
22
+ const variantState = variant && typeof variant.map === "function" ? variant : null;
23
+ const sizeState = size && typeof size.map === "function" ? size : null;
24
+ const disabledState = disabled && typeof disabled.map === "function" ? disabled : null;
25
+ const resolvedVariant = variantState ? variantState.get() : variant;
26
+ const isHyperlink = resolvedVariant === "hyperlink";
27
+ const handler = onClick ?? click;
28
+
29
+ const buildClass = (variantValue, sizeValue, disabledValue) => {
30
+ const normalizedSize = normalizeSize(sizeValue);
31
+ const baseClass = isHyperlink ? "" : "btn";
32
+ const variantClass = (isHyperlink || variantValue === "regular") ? "" : `btn-${variantValue}`;
33
+ const sizeClass = (!isHyperlink && normalizedSize && normalizedSize !== "md" && (normalizedSize === "lg" || normalizedSize === "xl"))
34
+ ? `btn-${normalizedSize}`
35
+ : "";
36
+ const disabledClass = disabledValue ? "btn-disabled" : "";
37
+ return `${baseClass} ${variantClass} ${sizeClass} ${disabledClass} ${className}`.trim();
38
+ };
39
+
40
+ const combinedClass = variantState
41
+ ? variantState.map((value) =>
42
+ buildClass(value, sizeState ? sizeState.get() : size, disabledState ? disabledState.get() : disabled)
43
+ )
44
+ : sizeState
45
+ ? sizeState.map((value) =>
46
+ buildClass(resolvedVariant, value, disabledState ? disabledState.get() : disabled)
47
+ )
48
+ : disabledState
49
+ ? disabledState.map((value) => buildClass(resolvedVariant, size, value))
50
+ : buildClass(resolvedVariant, size, disabled);
51
+
52
+ const props = {
53
+ class: combinedClass,
54
+ disabled,
55
+ ...rest
56
+ };
57
+
58
+ // Only attach click handler if not disabled
59
+ if (handler) {
60
+ props.click = (event) => {
61
+ const isDisabled = disabledState ? disabledState.get() : !!disabled;
62
+ if (isDisabled) return;
63
+ handler(event);
64
+ };
65
+ }
66
+
67
+ if (type === "link" || isHyperlink) {
68
+ // If it's a hyperlink variant, default to link type unless explicitly button
69
+ if (!disabled) {
70
+ props.href = href || "#";
71
+ }
72
+ return a(props, children);
73
+ }
74
+
75
+ return button(props, children);
76
+ }