fiboui 1.0.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 (141) hide show
  1. package/README.md +101 -0
  2. package/dist/content/Alert/index.d.ts +878 -0
  3. package/dist/content/Avatar/index.d.ts +886 -0
  4. package/dist/content/Badge/index.d.ts +887 -0
  5. package/dist/content/Image/index.d.ts +893 -0
  6. package/dist/content/Label/index.d.ts +879 -0
  7. package/dist/content/Progress/index.d.ts +898 -0
  8. package/dist/content/Skeleton/index.d.ts +879 -0
  9. package/dist/content/Spinner/index.d.ts +880 -0
  10. package/dist/content/Text/index.d.ts +878 -0
  11. package/dist/content/index.d.ts +9 -0
  12. package/dist/core/StateWrapper.d.ts +28 -0
  13. package/dist/core/builder.d.ts +50 -0
  14. package/dist/core/component.d.ts +7 -0
  15. package/dist/core/effects.d.ts +3 -0
  16. package/dist/core/icons.d.ts +11 -0
  17. package/dist/core/index.d.ts +5 -0
  18. package/dist/core/state.d.ts +37 -0
  19. package/dist/core/types.d.ts +51 -0
  20. package/dist/demo.d.ts +7 -0
  21. package/dist/effects-Co9C_Dyx.mjs +9 -0
  22. package/dist/examples/ScaffoldExamples.d.ts +3 -0
  23. package/dist/examples/StateExamples.d.ts +5 -0
  24. package/dist/index.d.ts +6 -0
  25. package/dist/index.esm.js +4622 -0
  26. package/dist/index.umd.js +251 -0
  27. package/dist/interactive/Button/index.d.ts +897 -0
  28. package/dist/interactive/Checkbox/index.d.ts +884 -0
  29. package/dist/interactive/Combobox/index.d.ts +898 -0
  30. package/dist/interactive/RadioGroup/index.d.ts +888 -0
  31. package/dist/interactive/Select/index.d.ts +895 -0
  32. package/dist/interactive/Switch/index.d.ts +881 -0
  33. package/dist/interactive/TextField/index.d.ts +892 -0
  34. package/dist/interactive/Textarea/index.d.ts +890 -0
  35. package/dist/interactive/index.d.ts +8 -0
  36. package/dist/layout/Center/index.d.ts +869 -0
  37. package/dist/layout/Column/index.d.ts +881 -0
  38. package/dist/layout/Container/index.d.ts +870 -0
  39. package/dist/layout/DashboardLayout/index.d.ts +870 -0
  40. package/dist/layout/Header/index.d.ts +870 -0
  41. package/dist/layout/Line/index.d.ts +872 -0
  42. package/dist/layout/Row/index.d.ts +879 -0
  43. package/dist/layout/Scaffold/index.d.ts +878 -0
  44. package/dist/layout/ScrollView/index.d.ts +879 -0
  45. package/dist/layout/Sidebar/index.d.ts +870 -0
  46. package/dist/layout/Spacer/index.d.ts +875 -0
  47. package/dist/layout/StakentComponents.d.ts +4 -0
  48. package/dist/layout/index.d.ts +11 -0
  49. package/dist/structure/Banner/index.d.ts +879 -0
  50. package/dist/structure/Box/index.d.ts +869 -0
  51. package/dist/structure/Card/index.d.ts +881 -0
  52. package/dist/structure/Collapse/index.d.ts +884 -0
  53. package/dist/structure/Grid/index.d.ts +1757 -0
  54. package/dist/structure/List/index.d.ts +888 -0
  55. package/dist/structure/Table/index.d.ts +895 -0
  56. package/dist/structure/Tags/index.d.ts +876 -0
  57. package/dist/structure/cards/AssetCard/index.d.ts +884 -0
  58. package/dist/structure/cards/CollectionCard/index.d.ts +880 -0
  59. package/dist/structure/cards/ContentCard/index.d.ts +884 -0
  60. package/dist/structure/cards/ProfileCard/index.d.ts +892 -0
  61. package/dist/structure/cards/index.d.ts +5 -0
  62. package/dist/structure/cards/variants/index.d.ts +3490 -0
  63. package/dist/structure/index.d.ts +9 -0
  64. package/dist/style.css +1 -0
  65. package/dist/theme/cupertino.d.ts +3 -0
  66. package/dist/theme/index.d.ts +5 -0
  67. package/dist/theme/material.d.ts +3 -0
  68. package/dist/theme/provider.d.ts +16 -0
  69. package/dist/theme/types.d.ts +57 -0
  70. package/dist/theme/web.d.ts +3 -0
  71. package/package.json +54 -0
  72. package/src/content/Alert/index.ts +110 -0
  73. package/src/content/Avatar/index.ts +116 -0
  74. package/src/content/Badge/index.ts +162 -0
  75. package/src/content/Image/index.ts +137 -0
  76. package/src/content/Label/index.ts +66 -0
  77. package/src/content/Progress/index.ts +378 -0
  78. package/src/content/Skeleton/index.ts +71 -0
  79. package/src/content/Spinner/index.ts +139 -0
  80. package/src/content/Text/index.ts +74 -0
  81. package/src/content/index.ts +9 -0
  82. package/src/core/StateWrapper.tsx +105 -0
  83. package/src/core/builder.ts +259 -0
  84. package/src/core/component.ts +49 -0
  85. package/src/core/effects.ts +27 -0
  86. package/src/core/icons.tsx +57 -0
  87. package/src/core/index.ts +5 -0
  88. package/src/core/state.ts +233 -0
  89. package/src/core/types.ts +62 -0
  90. package/src/demo.tsx +1045 -0
  91. package/src/examples/ScaffoldExamples.tsx +299 -0
  92. package/src/examples/StateExamples.tsx +149 -0
  93. package/src/index.ts +8 -0
  94. package/src/interactive/Button/index.ts +218 -0
  95. package/src/interactive/Checkbox/index.ts +145 -0
  96. package/src/interactive/Combobox/index.ts +278 -0
  97. package/src/interactive/RadioGroup/index.ts +124 -0
  98. package/src/interactive/Select/index.ts +227 -0
  99. package/src/interactive/Switch/index.ts +123 -0
  100. package/src/interactive/TextField/index.ts +200 -0
  101. package/src/interactive/Textarea/index.ts +129 -0
  102. package/src/interactive/index.ts +8 -0
  103. package/src/layout/Center/index.ts +34 -0
  104. package/src/layout/Column/index.ts +101 -0
  105. package/src/layout/Container/index.ts +28 -0
  106. package/src/layout/DashboardLayout/index.ts +106 -0
  107. package/src/layout/Header/index.ts +50 -0
  108. package/src/layout/Line/index.ts +41 -0
  109. package/src/layout/Row/index.ts +93 -0
  110. package/src/layout/Scaffold/index.ts +133 -0
  111. package/src/layout/ScrollView/index.ts +67 -0
  112. package/src/layout/Sidebar/index.ts +56 -0
  113. package/src/layout/Spacer/index.ts +46 -0
  114. package/src/layout/StakentComponents.tsx +57 -0
  115. package/src/layout/index.ts +11 -0
  116. package/src/structure/Banner/index.ts +92 -0
  117. package/src/structure/Box/index.ts +45 -0
  118. package/src/structure/Card/index.ts +113 -0
  119. package/src/structure/Collapse/index.ts +122 -0
  120. package/src/structure/Grid/index.ts +197 -0
  121. package/src/structure/List/index.ts +185 -0
  122. package/src/structure/Table/index.ts +165 -0
  123. package/src/structure/Tags/index.ts +69 -0
  124. package/src/structure/cards/AssetCard/index.ts +158 -0
  125. package/src/structure/cards/CollectionCard/index.ts +95 -0
  126. package/src/structure/cards/ContentCard/index.ts +127 -0
  127. package/src/structure/cards/ProfileCard/index.ts +152 -0
  128. package/src/structure/cards/index.ts +5 -0
  129. package/src/structure/cards/variants/index.ts +301 -0
  130. package/src/structure/index.ts +10 -0
  131. package/src/styles/base.css +186 -0
  132. package/src/styles/core.css +52 -0
  133. package/src/styles/themes/cupertino.css +197 -0
  134. package/src/styles/themes/material.css +322 -0
  135. package/src/styles/themes/web.css +196 -0
  136. package/src/theme/cupertino.ts +85 -0
  137. package/src/theme/index.ts +5 -0
  138. package/src/theme/material.ts +86 -0
  139. package/src/theme/provider.tsx +76 -0
  140. package/src/theme/types.ts +62 -0
  141. package/src/theme/web.ts +82 -0
@@ -0,0 +1,145 @@
1
+ import React, { ReactElement } from 'react';
2
+ import { FiboBuilder, createBuilder } from '../../core/builder';
3
+ import { BaseProps } from '../../core/types';
4
+
5
+ const CheckboxInner = React.memo((props: any) => {
6
+ const {
7
+ checked,
8
+ onChange,
9
+ label,
10
+ disabled,
11
+ indeterminate,
12
+ className,
13
+ style
14
+ } = props;
15
+
16
+ const isCheckedOrIndeterminate = checked || indeterminate;
17
+
18
+ const baseStyle: React.CSSProperties = {
19
+ width: '20px',
20
+ height: '20px',
21
+ borderRadius: '6px',
22
+ border: isCheckedOrIndeterminate ? 'none' : '2px solid #D1D5DB',
23
+ backgroundColor: isCheckedOrIndeterminate ? 'var(--fibo-primary, #3B82F6)' : 'transparent',
24
+ display: 'flex',
25
+ alignItems: 'center',
26
+ justifyContent: 'center',
27
+ transition: 'all 0.2s',
28
+ cursor: disabled ? 'not-allowed' : 'pointer',
29
+ opacity: disabled ? 0.5 : 1,
30
+ ...style
31
+ };
32
+
33
+
34
+ let icon = null;
35
+ if (indeterminate) {
36
+
37
+ icon = React.createElement('div', { style: { width: '10px', height: '2px', backgroundColor: 'white', borderRadius: '1px' } });
38
+ } else if (checked) {
39
+
40
+ icon = React.createElement('svg', {
41
+ width: '12',
42
+ height: '12',
43
+ viewBox: '0 0 12 12',
44
+ fill: 'none',
45
+ xmlns: 'http://www.w3.org/2000/svg'
46
+ },
47
+ React.createElement('path', {
48
+ d: 'M10 3L4.5 8.5L2 6',
49
+ stroke: 'white',
50
+ strokeWidth: '2',
51
+ strokeLinecap: 'round',
52
+ strokeLinejoin: 'round'
53
+ }));
54
+ }
55
+
56
+ return React.createElement(
57
+ 'label',
58
+ {
59
+ className: `fibo-checkbox flex items-center gap-2 ${className}`
60
+ },
61
+ React.createElement(
62
+ 'div',
63
+ {
64
+ className: 'fibo-checkbox-box',
65
+ style: baseStyle,
66
+ onClick: (e: React.MouseEvent) => {
67
+ e.preventDefault();
68
+ if (!disabled && onChange) {
69
+ onChange(!checked);
70
+ }
71
+ }
72
+ },
73
+ icon
74
+ ),
75
+ label && React.createElement(
76
+ 'span',
77
+ { className: 'text-sm text-gray-700 select-none' },
78
+ label
79
+ ),
80
+ React.createElement('input', {
81
+ type: 'checkbox',
82
+ checked: checked,
83
+ disabled: disabled,
84
+ className: 'sr-only',
85
+ onChange: (e: React.ChangeEvent<HTMLInputElement>) => !disabled && onChange?.(e.target.checked)
86
+ })
87
+ );
88
+ });
89
+
90
+ class CheckboxBuilder extends FiboBuilder {
91
+ private _checked: boolean = false;
92
+ private _onChange?: (checked: boolean) => void;
93
+ private _label?: string;
94
+ private _disabled: boolean = false;
95
+ private _indeterminate: boolean = false;
96
+
97
+ checked(value: boolean): this {
98
+ this._checked = value;
99
+ return this;
100
+ }
101
+
102
+ indeterminate(value: boolean): this {
103
+ this._indeterminate = value;
104
+ return this;
105
+ }
106
+
107
+ onChange(handler: (checked: boolean) => void): this {
108
+ this._onChange = handler;
109
+ return this;
110
+ }
111
+
112
+ label(text: string): this {
113
+ this._label = text;
114
+ return this;
115
+ }
116
+
117
+ disabled(value: boolean = true): this {
118
+ this._disabled = value;
119
+ return this;
120
+ }
121
+
122
+ build(): ReactElement {
123
+ const props = {
124
+ checked: this._checked,
125
+ onChange: this._onChange,
126
+ label: this._label,
127
+ disabled: this._disabled,
128
+ indeterminate: this._indeterminate,
129
+ className: this.getClassName(),
130
+ style: this.getStyle()
131
+ };
132
+ return React.createElement(CheckboxInner, props);
133
+ }
134
+ }
135
+
136
+
137
+ export const Checkbox = createBuilder(CheckboxBuilder);
138
+
139
+ export interface CheckboxProps extends BaseProps {
140
+ checked: boolean;
141
+ onChange?: (checked: boolean) => void;
142
+ label?: string;
143
+ disabled?: boolean;
144
+ indeterminate?: boolean;
145
+ }
@@ -0,0 +1,278 @@
1
+ import React, { ReactElement, useState, useRef, useEffect, ChangeEvent } from 'react';
2
+ import { FiboBuilder, createBuilder } from '../../core/builder';
3
+ import { BaseProps } from '../../core/types';
4
+
5
+ interface Option {
6
+ label: string;
7
+ value: string;
8
+ }
9
+
10
+ const ComboboxInner = React.memo((props: any) => {
11
+ const uniqueId = React.useId();
12
+ const inputId = props.id || uniqueId;
13
+ const [isOpen, setIsOpen] = useState(false);
14
+ const [searchTerm, setSearchTerm] = useState('');
15
+ const containerRef = useRef<HTMLDivElement>(null);
16
+
17
+ const {
18
+ containerClass,
19
+ containerStyle,
20
+ label,
21
+ error,
22
+ value,
23
+ options = [],
24
+ placeholder,
25
+ onChange,
26
+ onAdd,
27
+ disabled,
28
+ multiple,
29
+ ...rest
30
+ } = props;
31
+
32
+ useEffect(() => {
33
+ if (!multiple) {
34
+ const selected = props.options.find((o: Option) => o.value === props.value);
35
+ if (selected && !isOpen && searchTerm !== selected.label) {
36
+ setSearchTerm(selected.label);
37
+ }
38
+ }
39
+ }, [props.value, props.options, isOpen, multiple]);
40
+
41
+ const filteredOptions = options.filter((opt: Option) =>
42
+ opt.label.toLowerCase().includes(searchTerm.toLowerCase())
43
+ );
44
+
45
+ useEffect(() => {
46
+ const handleClickOutside = (event: MouseEvent) => {
47
+ if (containerRef.current && !containerRef.current.contains(event.target as Node)) {
48
+ setIsOpen(false);
49
+ if (!multiple) {
50
+ const selected = options.find((o: Option) => o.value === value);
51
+ setSearchTerm(selected ? selected.label : '');
52
+ } else {
53
+ setSearchTerm('');
54
+ }
55
+ }
56
+ };
57
+ document.addEventListener('mousedown', handleClickOutside);
58
+ return () => document.removeEventListener('mousedown', handleClickOutside);
59
+ }, [value, options, multiple]);
60
+
61
+ const handleSelect = (opt: Option) => {
62
+ if (multiple) {
63
+ const currentValues = Array.isArray(value) ? [...value] : [];
64
+ if (!currentValues.includes(opt.value)) {
65
+ currentValues.push(opt.value);
66
+ onChange?.(currentValues);
67
+ }
68
+ setSearchTerm('');
69
+ } else {
70
+ setSearchTerm(opt.label);
71
+ onChange?.(opt.value);
72
+ setIsOpen(false);
73
+ }
74
+ };
75
+
76
+ const handleRemoveTag = (valToRemove: string) => {
77
+ if (multiple && Array.isArray(value)) {
78
+ const newValues = value.filter(v => v !== valToRemove);
79
+ onChange?.(newValues);
80
+ }
81
+ };
82
+
83
+ const handleAdd = () => {
84
+ onAdd?.(searchTerm);
85
+ if (multiple) {
86
+ setSearchTerm('');
87
+ } else {
88
+ setIsOpen(false);
89
+ }
90
+ };
91
+
92
+ const isSelected = (val: string) => {
93
+ if (multiple) {
94
+ return Array.isArray(value) && value.includes(val);
95
+ }
96
+ return value === val;
97
+ };
98
+
99
+ return React.createElement(
100
+ 'div',
101
+ {
102
+ ref: containerRef,
103
+ className: containerClass,
104
+ style: containerStyle
105
+ },
106
+ React.createElement('div', { className: 'relative w-full' }, [
107
+ React.createElement('div', {
108
+ className: `fibo-textfield-input flex flex-wrap items-center gap-1 ${isOpen ? 'ring-2 ring-[var(--fibo-primary-container)] border-[var(--fibo-primary)]' : ''}`,
109
+ style: { minHeight: '42px', padding: '4px 8px' }
110
+ }, [
111
+ multiple && Array.isArray(value) && value.map((val: string) => {
112
+ const opt = options.find((o: Option) => o.value === val);
113
+ const labelStr = opt ? opt.label : val;
114
+ return React.createElement('span', {
115
+ key: val,
116
+ className: 'bg-[var(--fibo-primary-container)] text-[var(--fibo-on-primary-container)] text-xs font-medium px-2 py-1 rounded-md flex items-center'
117
+ }, [
118
+ labelStr,
119
+ React.createElement('button', {
120
+ type: 'button',
121
+ className: 'ml-1 hover:text-[var(--fibo-primary)] focus:outline-none',
122
+ onClick: (e: any) => { e.stopPropagation(); handleRemoveTag(val); }
123
+ }, '×')
124
+ ]);
125
+ }),
126
+
127
+ React.createElement('input', {
128
+ key: 'input',
129
+ id: inputId,
130
+ className: 'bg-transparent outline-none flex-1 min-w-[60px] text-sm h-8',
131
+ value: searchTerm,
132
+ placeholder: (multiple && Array.isArray(value) && value.length > 0) ? '' : (placeholder || ' '),
133
+ disabled: disabled,
134
+ onChange: (e: ChangeEvent<HTMLInputElement>) => {
135
+ setSearchTerm(e.target.value);
136
+ if (!isOpen) setIsOpen(true);
137
+ },
138
+ onFocus: () => setIsOpen(true),
139
+ ...rest
140
+ })
141
+ ]),
142
+
143
+ label && React.createElement(
144
+ 'label',
145
+ {
146
+ key: 'label',
147
+ htmlFor: inputId,
148
+ className: 'fibo-textfield-label'
149
+ },
150
+ label
151
+ ),
152
+
153
+ isOpen && React.createElement('div', {
154
+ key: 'dropdown',
155
+ className: 'absolute z-50 w-full mt-1 bg-[var(--fibo-surface)] border border-[var(--fibo-outline)] rounded-md shadow-lg max-h-60 overflow-auto focus:outline-none left-0'
156
+ }, [
157
+ filteredOptions.length > 0 ?
158
+ filteredOptions.map((opt: Option) => {
159
+ const selected = isSelected(opt.value);
160
+ return React.createElement('div', {
161
+ key: opt.value,
162
+ className: `px-4 py-2 cursor-pointer text-sm hover:bg-[var(--fibo-primary-container)] hover:text-[var(--fibo-on-primary-container)] ${selected ? 'bg-[var(--fibo-primary-container)] text-[var(--fibo-on-primary-container)] font-medium opacity-50' : 'text-[var(--fibo-on-surface)]'}`,
163
+ onClick: () => !selected && handleSelect(opt)
164
+ }, opt.label);
165
+ }) :
166
+ React.createElement('div', { className: 'px-4 py-2 text-sm text-gray-500' }, 'No results found.'),
167
+
168
+ searchTerm && !filteredOptions.some((o: Option) => o.label.toLowerCase() === searchTerm.toLowerCase()) && onAdd && React.createElement('div', {
169
+ key: 'add-new',
170
+ className: 'border-t border-[var(--fibo-outline)] px-4 py-2 cursor-pointer text-sm text-[var(--fibo-primary)] hover:bg-[var(--fibo-primary-container)] font-medium flex items-center',
171
+ onClick: handleAdd
172
+ }, [
173
+ React.createElement('span', { className: 'mr-1' }, '+'),
174
+ `Add "${searchTerm}"`
175
+ ])
176
+ ])
177
+ ]),
178
+
179
+ error && React.createElement(
180
+ 'span',
181
+ { className: 'text-[var(--fibo-error)] text-xs mt-1 ml-1' },
182
+ error
183
+ )
184
+ );
185
+ });
186
+
187
+ class ComboboxBuilder extends FiboBuilder {
188
+ private _value: string | string[] = '';
189
+ private _label: string = '';
190
+ private _placeholder: string = '';
191
+ private _disabled: boolean = false;
192
+ private _error?: string;
193
+ private _multiple: boolean = false;
194
+ private _options: Option[] = [];
195
+ private _onChange?: (value: string | string[]) => void;
196
+ private _onAdd?: (newLabel: string) => void;
197
+
198
+ value(val: string | string[]): this {
199
+ this._value = val;
200
+ return this;
201
+ }
202
+
203
+ label(val: string): this {
204
+ this._label = val;
205
+ return this;
206
+ }
207
+
208
+ placeholder(val: string): this {
209
+ this._placeholder = val;
210
+ return this;
211
+ }
212
+
213
+ options(opts: Option[]): this {
214
+ this._options = opts;
215
+ return this;
216
+ }
217
+
218
+ multiple(val: boolean = true): this {
219
+ this._multiple = val;
220
+ return this;
221
+ }
222
+
223
+ disabled(val: boolean = true): this {
224
+ this._disabled = val;
225
+ return this;
226
+ }
227
+
228
+ error(val: string): this {
229
+ this._error = val;
230
+ return this;
231
+ }
232
+
233
+ onChange(handler: (value: string | string[]) => void): this {
234
+ this._onChange = handler;
235
+ return this;
236
+ }
237
+
238
+ onAdd(handler: (newLabel: string) => void): this {
239
+ this._onAdd = handler;
240
+ return this;
241
+ }
242
+
243
+ build(): ReactElement {
244
+ const containerClass = ['fibo-textfield-container relative', ...this._classNames]
245
+ .filter(Boolean)
246
+ .join(' ');
247
+
248
+ const props = {
249
+ containerClass,
250
+ containerStyle: this.getStyle(),
251
+ label: this._label,
252
+ error: this._error,
253
+ value: this._value,
254
+ placeholder: this._placeholder,
255
+ options: this._options,
256
+ disabled: this._disabled,
257
+ multiple: this._multiple,
258
+ onChange: this._onChange,
259
+ onAdd: this._onAdd,
260
+ ...this._props
261
+ };
262
+
263
+ return React.createElement(ComboboxInner, props);
264
+ }
265
+ }
266
+
267
+
268
+ export const Combobox = createBuilder(ComboboxBuilder);
269
+
270
+ export interface ComboboxProps extends BaseProps {
271
+ value: string | string[];
272
+ label?: string;
273
+ placeholder?: string;
274
+ multiple?: boolean;
275
+ options: Option[];
276
+ onChange?: (value: string | string[]) => void;
277
+ onAdd?: (newLabel: string) => void;
278
+ }
@@ -0,0 +1,124 @@
1
+ import React, { ReactElement } from 'react';
2
+ import { FiboBuilder, createBuilder } from '../../core/builder';
3
+ import { BaseProps } from '../../core/types';
4
+
5
+ interface RadioOption {
6
+ label: string;
7
+ value: string;
8
+ disabled?: boolean;
9
+ }
10
+
11
+ const RadioGroupInner = React.memo((props: any) => {
12
+ const {
13
+ value,
14
+ options,
15
+ onChange,
16
+ name,
17
+ direction,
18
+ className,
19
+ style
20
+ } = props;
21
+
22
+ return React.createElement(
23
+ 'div',
24
+ {
25
+ className: `fibo-radio-group flex ${direction === 'vertical' ? 'flex-col gap-2' : 'flex-row gap-4'} ${className}`,
26
+ style: style
27
+ },
28
+ options.map((option: any) => {
29
+ const isSelected = value === option.value;
30
+ const isDisabled = option.disabled;
31
+
32
+ return React.createElement(
33
+ 'label',
34
+ {
35
+ key: option.value,
36
+ className: `flex items-center gap-2 cursor-pointer ${isDisabled ? 'opacity-50 cursor-not-allowed' : ''}`
37
+ },
38
+ React.createElement(
39
+ 'div',
40
+ {
41
+ className: 'relative flex items-center justify-center',
42
+ style: {
43
+ width: '18px',
44
+ height: '18px',
45
+ borderRadius: '50%',
46
+ border: isSelected ? '5px solid var(--fibo-primary, #3B82F6)' : '2px solid #D1D5DB',
47
+ transition: 'all 0.2s',
48
+ }
49
+ },
50
+ React.createElement('input', {
51
+ type: 'radio',
52
+ name: name,
53
+ value: option.value,
54
+ checked: isSelected,
55
+ disabled: isDisabled,
56
+ className: 'sr-only',
57
+ onChange: () => !isDisabled && onChange?.(option.value)
58
+ })
59
+ ),
60
+ React.createElement(
61
+ 'span',
62
+ { className: 'text-sm text-gray-700' },
63
+ option.label
64
+ )
65
+ );
66
+ })
67
+ );
68
+ });
69
+
70
+ class RadioGroupBuilder extends FiboBuilder {
71
+ private _value: string = '';
72
+ private _options: RadioOption[] = [];
73
+ private _onChange?: (value: string) => void;
74
+ private _name: string = `radio-group-${Math.random().toString(36).substr(2, 9)}`;
75
+ private _direction: 'vertical' | 'horizontal' = 'vertical';
76
+
77
+ value(val: string): this {
78
+ this._value = val;
79
+ return this;
80
+ }
81
+
82
+ options(opts: RadioOption[]): this {
83
+ this._options = opts;
84
+ return this;
85
+ }
86
+
87
+ onChange(handler: (val: string) => void): this {
88
+ this._onChange = handler;
89
+ return this;
90
+ }
91
+
92
+ direction(dir: 'vertical' | 'horizontal'): this {
93
+ this._direction = dir;
94
+ return this;
95
+ }
96
+
97
+ name(val: string): this {
98
+ this._name = val;
99
+ return this;
100
+ }
101
+
102
+ build(): ReactElement {
103
+ const props = {
104
+ value: this._value,
105
+ options: this._options,
106
+ onChange: this._onChange,
107
+ name: this._name,
108
+ direction: this._direction,
109
+ className: this.getClassName(),
110
+ style: this.getStyle()
111
+ };
112
+ return React.createElement(RadioGroupInner, props);
113
+ }
114
+ }
115
+
116
+
117
+ export const RadioGroup = createBuilder(RadioGroupBuilder);
118
+
119
+ export interface RadioGroupProps extends BaseProps {
120
+ value: string;
121
+ options: RadioOption[];
122
+ onChange?: (value: string) => void;
123
+ direction?: 'vertical' | 'horizontal';
124
+ }