@hellboy/ds 0.1.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 (137) hide show
  1. package/README.md +111 -0
  2. package/dist/index.css +3699 -0
  3. package/dist/index.css.map +1 -0
  4. package/dist/index.d.mts +1087 -0
  5. package/dist/index.d.ts +1087 -0
  6. package/dist/index.js +3391 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/index.mjs +3287 -0
  9. package/dist/index.mjs.map +1 -0
  10. package/dist/theme.css +55 -0
  11. package/hellboy-ds-0.1.2.tgz +0 -0
  12. package/package.json +42 -0
  13. package/src/components/badge/Badge.tsx +29 -0
  14. package/src/components/badge/index.ts +1 -0
  15. package/src/components/banner/Banner.tsx +48 -0
  16. package/src/components/banner/banner.css +44 -0
  17. package/src/components/banner/index.ts +1 -0
  18. package/src/components/button/button.tsx +127 -0
  19. package/src/components/button/index.ts +1 -0
  20. package/src/components/card/card.tsx +57 -0
  21. package/src/components/card/index.ts +1 -0
  22. package/src/components/checkbox/Checkbox.tsx +98 -0
  23. package/src/components/checkbox/index.ts +1 -0
  24. package/src/components/code-block/code-block.tsx +44 -0
  25. package/src/components/code-block/index.ts +1 -0
  26. package/src/components/color-control/color-control.tsx +322 -0
  27. package/src/components/color-control/index.ts +1 -0
  28. package/src/components/drag-handle/DragHandle.tsx +78 -0
  29. package/src/components/drag-handle/index.ts +1 -0
  30. package/src/components/drawer/drawer.tsx +82 -0
  31. package/src/components/drawer/index.ts +1 -0
  32. package/src/components/floating-bar/floating-bar.tsx +52 -0
  33. package/src/components/floating-bar/index.ts +2 -0
  34. package/src/components/footer/footer.tsx +28 -0
  35. package/src/components/footer/index.ts +1 -0
  36. package/src/components/grid/Grid.tsx +53 -0
  37. package/src/components/grid/index.ts +1 -0
  38. package/src/components/header/header.tsx +57 -0
  39. package/src/components/header/index.ts +1 -0
  40. package/src/components/icons/icons.tsx +44 -0
  41. package/src/components/icons/index.ts +1 -0
  42. package/src/components/index.ts +29 -0
  43. package/src/components/input/DatePicker.tsx +133 -0
  44. package/src/components/input/Input.tsx +220 -0
  45. package/src/components/input/InputDate.tsx +10 -0
  46. package/src/components/input/InputDateTime.tsx +10 -0
  47. package/src/components/input/InputEmail.tsx +10 -0
  48. package/src/components/input/InputField.tsx +137 -0
  49. package/src/components/input/InputNumber.tsx +10 -0
  50. package/src/components/input/InputPassword.tsx +10 -0
  51. package/src/components/input/InputSearch.tsx +10 -0
  52. package/src/components/input/InputTel.tsx +10 -0
  53. package/src/components/input/InputText.tsx +10 -0
  54. package/src/components/input/InputTime.tsx +10 -0
  55. package/src/components/input/InputUrl.tsx +10 -0
  56. package/src/components/input/TimePicker.tsx +151 -0
  57. package/src/components/input/index.ts +11 -0
  58. package/src/components/layout/Layout.tsx +244 -0
  59. package/src/components/layout/index.ts +1 -0
  60. package/src/components/list/List.tsx +159 -0
  61. package/src/components/list/index.ts +1 -0
  62. package/src/components/navbar/MenuCategory.tsx +20 -0
  63. package/src/components/navbar/MenuGroup.tsx +288 -0
  64. package/src/components/navbar/MenuItem.tsx +65 -0
  65. package/src/components/navbar/Navbar.tsx +23 -0
  66. package/src/components/navbar/index.ts +4 -0
  67. package/src/components/page/index.ts +1 -0
  68. package/src/components/page/page.tsx +46 -0
  69. package/src/components/page-index/PageIndex.tsx +275 -0
  70. package/src/components/page-index/index.ts +1 -0
  71. package/src/components/popover/index.ts +1 -0
  72. package/src/components/popover/popover.tsx +199 -0
  73. package/src/components/radio/Radio.tsx +176 -0
  74. package/src/components/radio/index.ts +1 -0
  75. package/src/components/section/index.ts +1 -0
  76. package/src/components/section/section.tsx +66 -0
  77. package/src/components/select/Select.tsx +212 -0
  78. package/src/components/select/index.ts +1 -0
  79. package/src/components/slider/Slider.tsx +267 -0
  80. package/src/components/slider/index.ts +1 -0
  81. package/src/components/switch/index.ts +1 -0
  82. package/src/components/switch/switch.tsx +99 -0
  83. package/src/components/table/Table.tsx +147 -0
  84. package/src/components/table/index.ts +1 -0
  85. package/src/components/theme-control/index.ts +1 -0
  86. package/src/components/theme-control/theme-control.tsx +78 -0
  87. package/src/components/tooltip/index.ts +1 -0
  88. package/src/components/tooltip/tooltip.tsx +207 -0
  89. package/src/contexts/NavbarTooltipContext.tsx +48 -0
  90. package/src/contexts/index.ts +1 -0
  91. package/src/foundations/motion.md +136 -0
  92. package/src/index.ts +40 -0
  93. package/src/style/_shared/field.css +69 -0
  94. package/src/style/components/badge/badge.css +74 -0
  95. package/src/style/components/button/button.css +244 -0
  96. package/src/style/components/card/card.css +69 -0
  97. package/src/style/components/checkbox.css +142 -0
  98. package/src/style/components/code-block/code-block.css +34 -0
  99. package/src/style/components/color-control/color-control.css +126 -0
  100. package/src/style/components/drag-handle/drag-handle.css +68 -0
  101. package/src/style/components/drawer/drawer.css +210 -0
  102. package/src/style/components/floating-bar/floating-bar.css +39 -0
  103. package/src/style/components/footer/footer.css +108 -0
  104. package/src/style/components/grid/grid.css +33 -0
  105. package/src/style/components/header/header.css +44 -0
  106. package/src/style/components/icons/icons.css +44 -0
  107. package/src/style/components/input/input.css +393 -0
  108. package/src/style/components/layout/layout.css +205 -0
  109. package/src/style/components/list/list.css +140 -0
  110. package/src/style/components/navbar/navbar.css +342 -0
  111. package/src/style/components/page/page.css +46 -0
  112. package/src/style/components/page-index/page-index.css +158 -0
  113. package/src/style/components/popover/popover.css +44 -0
  114. package/src/style/components/radio.css +178 -0
  115. package/src/style/components/section/section.css +67 -0
  116. package/src/style/components/select/select.css +143 -0
  117. package/src/style/components/slider/slider.css +159 -0
  118. package/src/style/components/switch/switch.css +267 -0
  119. package/src/style/components/table/table.css +108 -0
  120. package/src/style/components/theme-control/theme-control.css +35 -0
  121. package/src/style/components/tooltip/tooltip.css +52 -0
  122. package/src/style/foundations/global.css +316 -0
  123. package/src/style/foundations/motion.css +164 -0
  124. package/src/style/foundations/spacing.css +51 -0
  125. package/src/style/foundations/typography.css +39 -0
  126. package/src/style/foundations/z-index.css +81 -0
  127. package/src/style/modes/dark.css +146 -0
  128. package/src/style/modes/light.css +147 -0
  129. package/src/style/semantic.css +52 -0
  130. package/src/style/styles.css +51 -0
  131. package/src/style/themes/theme.json +37 -0
  132. package/src/utils/README.md +305 -0
  133. package/src/utils/USER_PREFERENCES.md +558 -0
  134. package/src/utils/theme.ts +127 -0
  135. package/src/utils/user-preferences.ts +577 -0
  136. package/tsconfig.json +25 -0
  137. package/tsup.config.ts +52 -0
@@ -0,0 +1 @@
1
+ export { Header, type HeaderProps } from './header';
@@ -0,0 +1,44 @@
1
+ import React from 'react';
2
+ import { Icon as IconifyIcon } from '@iconify/react';
3
+ import '../../style/components/icons/icons.css';
4
+
5
+ export type IconName = string; // Allow any string for famicons icons
6
+
7
+ export interface IconProps {
8
+ name: IconName;
9
+ size?: number;
10
+ className?: string;
11
+ /** Render a skeleton placeholder instead of the real icon (useful while downloading/loading) */
12
+ loading?: boolean;
13
+ /** aria-label for icon-only buttons etc. */
14
+ 'aria-label'?: string;
15
+ }
16
+
17
+ export const Icon: React.FC<IconProps> = ({ name, size = 24, className, loading = false, ...rest }) => {
18
+ // If name already has famicons: prefix, use it as-is, otherwise add the prefix
19
+ const iconName = name.startsWith('famicons:') ? name : `famicons:${name}`;
20
+
21
+ const classes = ['icon', className].filter(Boolean).join(' ');
22
+
23
+ if (loading) {
24
+ const style: React.CSSProperties = { width: size, height: size };
25
+ return (
26
+ <span
27
+ className={`${classes} icon--skeleton`}
28
+ style={style}
29
+ aria-hidden={rest['aria-label'] ? 'false' : 'true'}
30
+ {...(rest['aria-label'] ? { 'aria-label': rest['aria-label'] } : {})}
31
+ />
32
+ );
33
+ }
34
+
35
+ return (
36
+ <IconifyIcon
37
+ icon={iconName}
38
+ width={size}
39
+ height={size}
40
+ className={classes}
41
+ {...rest}
42
+ />
43
+ );
44
+ };
@@ -0,0 +1 @@
1
+ export { Icon, type IconProps, type IconName } from './icons';
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Design System Components
3
+ * Export all available components
4
+ */
5
+
6
+ export * from './button';
7
+ export * from './badge';
8
+ export * from './checkbox';
9
+ export * from './switch';
10
+ export * from './layout';
11
+ export * from './page';
12
+ export * from './header';
13
+ export * from './section';
14
+ export * from './code-block';
15
+ export * from './navbar';
16
+ export * from './icons';
17
+ export * from './theme-control';
18
+ export * from './card';
19
+ export * from './color-control';
20
+ export * from './grid';
21
+ export * from './slider';
22
+ export * from './drag-handle';
23
+ export * from './footer';
24
+ export * from './input';
25
+ export * from './list';
26
+ export * from './radio';
27
+ export * from './select';
28
+ export * from './banner';
29
+ export * from './floating-bar';
@@ -0,0 +1,133 @@
1
+ import * as React from 'react';
2
+
3
+ interface DatePickerProps {
4
+ value: string;
5
+ onChange: (value: string) => void;
6
+ disabled?: boolean;
7
+ }
8
+
9
+ const MONTHS = [
10
+ 'January', 'February', 'March', 'April', 'May', 'June',
11
+ 'July', 'August', 'September', 'October', 'November', 'December'
12
+ ];
13
+
14
+ const DAYS_OF_WEEK = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
15
+
16
+ export const DatePicker: React.FC<DatePickerProps> = ({ value, onChange, disabled }) => {
17
+ const parseDate = (dateStr: string) => {
18
+ if (!dateStr) {
19
+ const today = new Date();
20
+ return { year: today.getFullYear(), month: today.getMonth(), day: today.getDate() };
21
+ }
22
+ const [year, month, day] = dateStr.split('-').map(Number);
23
+ return { year, month: month - 1, day };
24
+ };
25
+
26
+ const { year: initialYear, month: initialMonth, day: selectedDay } = parseDate(value);
27
+ const [currentYear, setCurrentYear] = React.useState(initialYear);
28
+ const [currentMonth, setCurrentMonth] = React.useState(initialMonth);
29
+
30
+ const getDaysInMonth = (year: number, month: number) => {
31
+ return new Date(year, month + 1, 0).getDate();
32
+ };
33
+
34
+ const getFirstDayOfMonth = (year: number, month: number) => {
35
+ return new Date(year, month, 1).getDay();
36
+ };
37
+
38
+ const handleDateSelect = (day: number) => {
39
+ if (disabled) return;
40
+ const formattedDate = `${currentYear}-${String(currentMonth + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
41
+ onChange(formattedDate);
42
+ };
43
+
44
+ const handlePrevMonth = () => {
45
+ if (currentMonth === 0) {
46
+ setCurrentMonth(11);
47
+ setCurrentYear(currentYear - 1);
48
+ } else {
49
+ setCurrentMonth(currentMonth - 1);
50
+ }
51
+ };
52
+
53
+ const handleNextMonth = () => {
54
+ if (currentMonth === 11) {
55
+ setCurrentMonth(0);
56
+ setCurrentYear(currentYear + 1);
57
+ } else {
58
+ setCurrentMonth(currentMonth + 1);
59
+ }
60
+ };
61
+
62
+ const daysInMonth = getDaysInMonth(currentYear, currentMonth);
63
+ const firstDay = getFirstDayOfMonth(currentYear, currentMonth);
64
+ const days: (number | null)[] = [
65
+ ...Array(firstDay).fill(null),
66
+ ...Array.from({ length: daysInMonth }, (_, i) => i + 1)
67
+ ];
68
+
69
+ return (
70
+ <div className="date-picker">
71
+ <div className="date-picker__header">
72
+ <button
73
+ type="button"
74
+ className="date-picker__nav"
75
+ onClick={handlePrevMonth}
76
+ disabled={disabled}
77
+ aria-label="Previous month"
78
+ >
79
+
80
+ </button>
81
+ <div className="date-picker__title">
82
+ <select
83
+ className="date-picker__select"
84
+ value={currentMonth}
85
+ onChange={(e) => setCurrentMonth(Number(e.target.value))}
86
+ disabled={disabled}
87
+ >
88
+ {MONTHS.map((month, idx) => (
89
+ <option key={month} value={idx}>{month}</option>
90
+ ))}
91
+ </select>
92
+ <select
93
+ className="date-picker__select"
94
+ value={currentYear}
95
+ onChange={(e) => setCurrentYear(Number(e.target.value))}
96
+ disabled={disabled}
97
+ >
98
+ {Array.from({ length: 100 }, (_, i) => currentYear - 50 + i).map(year => (
99
+ <option key={year} value={year}>{year}</option>
100
+ ))}
101
+ </select>
102
+ </div>
103
+ <button
104
+ type="button"
105
+ className="date-picker__nav"
106
+ onClick={handleNextMonth}
107
+ disabled={disabled}
108
+ aria-label="Next month"
109
+ >
110
+
111
+ </button>
112
+ </div>
113
+ <div className="date-picker__weekdays">
114
+ {DAYS_OF_WEEK.map(day => (
115
+ <div key={day} className="date-picker__weekday">{day}</div>
116
+ ))}
117
+ </div>
118
+ <div className="date-picker__days">
119
+ {days.map((day, idx) => (
120
+ <button
121
+ key={idx}
122
+ type="button"
123
+ className={`date-picker__day ${day === selectedDay ? 'date-picker__day--selected' : ''} ${!day ? 'date-picker__day--empty' : ''}`}
124
+ onClick={() => day && handleDateSelect(day)}
125
+ disabled={!day || disabled}
126
+ >
127
+ {day || ''}
128
+ </button>
129
+ ))}
130
+ </div>
131
+ </div>
132
+ );
133
+ };
@@ -0,0 +1,220 @@
1
+ import * as React from 'react';
2
+ import { InputField, InputFieldProps } from './InputField';
3
+ import { Popover } from '../popover/popover';
4
+ import { DatePicker } from './DatePicker';
5
+ import { TimePicker } from './TimePicker';
6
+ import '../../style/components/input/input.css';
7
+
8
+ export type InputType = 'text' | 'email' | 'password' | 'number' | 'tel' | 'url' | 'search' | 'date' | 'time' | 'datetime-local';
9
+
10
+ export interface InputProps extends Omit<InputFieldProps, 'type' | 'iconRight' | 'onRightIconClick'> {
11
+ /**
12
+ * Input type with extended support for date/time pickers
13
+ * @default 'text'
14
+ */
15
+ type?: InputType;
16
+
17
+ /**
18
+ * Show milliseconds in time picker (only for time type)
19
+ * @default false
20
+ */
21
+ showMilliseconds?: boolean;
22
+
23
+ /**
24
+ * Minute step in time picker (only for time type)
25
+ * @default 1
26
+ */
27
+ minuteStep?: 1 | 5 | 15 | 30;
28
+
29
+ /**
30
+ * Show clear button for search inputs when they have value
31
+ * @default true
32
+ */
33
+ showClear?: boolean;
34
+ }
35
+
36
+ const TYPE_ICON_MAP: Record<string, string> = {
37
+ email: 'mail',
38
+ password: 'lock-closed',
39
+ search: 'search',
40
+ date: 'calendar',
41
+ time: 'time',
42
+ 'datetime-local': 'calendar',
43
+ tel: 'call',
44
+ url: 'link',
45
+ };
46
+
47
+ const formatDisplayValue = (val: string, inputType: InputType) => {
48
+ if (!val) return '';
49
+ if (inputType === 'date') {
50
+ const [year, month, day] = val.split('-');
51
+ return `${day}/${month}/${year}`;
52
+ }
53
+ if (inputType === 'time') {
54
+ return val; // Já vem formatado como HH:MM:SS
55
+ }
56
+ if (inputType === 'datetime-local') {
57
+ const [datePart, timePart] = val.split('T');
58
+ if (datePart && timePart) {
59
+ const [year, month, day] = datePart.split('-');
60
+ return `${day}/${month}/${year} ${timePart}`;
61
+ }
62
+ return val;
63
+ }
64
+ return val;
65
+ };
66
+
67
+ export const Input = React.forwardRef<HTMLInputElement, InputProps>(
68
+ (
69
+ {
70
+ type = 'text',
71
+ label,
72
+ helperText,
73
+ error,
74
+ icon,
75
+ fullWidth = false,
76
+ size = 'md',
77
+ className = '',
78
+ disabled,
79
+ id,
80
+ showMilliseconds = false,
81
+ minuteStep = 1,
82
+ showClear = true,
83
+ ...props
84
+ },
85
+ ref
86
+ ) => {
87
+ const [showPassword, setShowPassword] = React.useState(false);
88
+ const [showDateTimePicker, setShowDateTimePicker] = React.useState(false);
89
+ const inputRef = React.useRef<HTMLInputElement>(null);
90
+
91
+ // Determine icons
92
+ const leftIcon = icon || TYPE_ICON_MAP[type];
93
+ const isPasswordType = type === 'password';
94
+ const actualType = isPasswordType && showPassword ? 'text' : (type !== 'date' && type !== 'time' && type !== 'datetime-local' ? type : 'text');
95
+
96
+ // Right icon - either custom, password toggle, date/time picker, clear button, or type-based
97
+ const isDateTimeType = type === 'date' || type === 'time' || type === 'datetime-local';
98
+ const isSearchType = type === 'search';
99
+ const hasValue = props.value && String(props.value).length > 0;
100
+
101
+ const rightIconName = isPasswordType
102
+ ? showPassword
103
+ ? 'eye-off'
104
+ : 'eye'
105
+ : isDateTimeType
106
+ ? 'chevron-down'
107
+ : isSearchType && hasValue && showClear
108
+ ? 'x'
109
+ : undefined;
110
+
111
+ const handleRightIconClick = () => {
112
+ if (isPasswordType) {
113
+ setShowPassword(!showPassword);
114
+ } else if (isDateTimeType) {
115
+ setShowDateTimePicker(!showDateTimePicker);
116
+ } else if (isSearchType && hasValue && showClear) {
117
+ if (props.onChange) {
118
+ props.onChange({ target: { value: '' } } as any);
119
+ }
120
+ }
121
+ };
122
+
123
+ const handleDateTimeChange = (newValue: string) => {
124
+ if (props.onChange) {
125
+ props.onChange({ target: { value: newValue } } as any);
126
+ }
127
+ };
128
+
129
+ if (!isDateTimeType) {
130
+ return (
131
+ <InputField
132
+ ref={ref || inputRef}
133
+ type={actualType}
134
+ label={label}
135
+ helperText={helperText}
136
+ error={error}
137
+ icon={leftIcon}
138
+ iconRight={rightIconName}
139
+ onRightIconClick={handleRightIconClick}
140
+ fullWidth={fullWidth}
141
+ size={size}
142
+ className={className}
143
+ disabled={disabled}
144
+ id={id}
145
+ {...props}
146
+ />
147
+ );
148
+ }
149
+
150
+ // Date/Time picker rendering
151
+ return (
152
+ <div className={`input-container ${fullWidth ? 'input-container--full-width' : ''} ${className}`.trim()}>
153
+ {label && (
154
+ <label htmlFor={id} className="input__label">
155
+ {label}
156
+ </label>
157
+ )}
158
+ <Popover
159
+ trigger={
160
+ <InputField
161
+ ref={ref || inputRef}
162
+ type="text"
163
+ icon={leftIcon}
164
+ iconRight={rightIconName}
165
+ onRightIconClick={() => setShowDateTimePicker(!showDateTimePicker)}
166
+ fullWidth={fullWidth}
167
+ size={size}
168
+ disabled={disabled}
169
+ id={id}
170
+ value={formatDisplayValue(String(props.value || ''), type)}
171
+ placeholder={props.placeholder}
172
+ readOnly
173
+ />
174
+ }
175
+ isOpen={showDateTimePicker}
176
+ onToggle={() => setShowDateTimePicker(!showDateTimePicker)}
177
+ placement="bottom"
178
+ >
179
+ <div style={{ padding: '1rem' }}>
180
+ {(type === 'date' || type === 'datetime-local') && (
181
+ <DatePicker
182
+ value={(String(props.value || '')).split('T')[0]}
183
+ onChange={(newDate) => {
184
+ if (type === 'datetime-local') {
185
+ const time = (String(props.value || '')).split('T')[1] || '00:00:00';
186
+ handleDateTimeChange(`${newDate}T${time}`);
187
+ } else {
188
+ handleDateTimeChange(newDate);
189
+ setShowDateTimePicker(false);
190
+ }
191
+ }}
192
+ disabled={disabled}
193
+ />
194
+ )}
195
+ {(type === 'time' || type === 'datetime-local') && (
196
+ <TimePicker
197
+ value={(String(props.value || '')).split('T')[1] || '00:00:00'}
198
+ onChange={(newTime) => {
199
+ if (type === 'datetime-local') {
200
+ const date = (String(props.value || '')).split('T')[0] || new Date().toISOString().split('T')[0];
201
+ handleDateTimeChange(`${date}T${newTime}`);
202
+ } else {
203
+ handleDateTimeChange(newTime);
204
+ }
205
+ }}
206
+ disabled={disabled}
207
+ showMilliseconds={showMilliseconds}
208
+ minuteStep={minuteStep}
209
+ />
210
+ )}
211
+ </div>
212
+ </Popover>
213
+ {error && <p className="input__message input__message--error">{error}</p>}
214
+ {helperText && !error && <p className="input__message">{helperText}</p>}
215
+ </div>
216
+ );
217
+ }
218
+ );
219
+
220
+ Input.displayName = 'Input';
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import { Input, InputProps } from './Input';
3
+
4
+ export interface InputDateProps extends Omit<InputProps, 'type'> {}
5
+
6
+ export const InputDate = React.forwardRef<HTMLInputElement, InputDateProps>((props, ref) => {
7
+ return <Input ref={ref} {...props} type="date" />;
8
+ });
9
+
10
+ InputDate.displayName = 'InputDate';
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import { Input, InputProps } from './Input';
3
+
4
+ export interface InputDateTimeProps extends Omit<InputProps, 'type'> {}
5
+
6
+ export const InputDateTime = React.forwardRef<HTMLInputElement, InputDateTimeProps>((props, ref) => {
7
+ return <Input ref={ref} {...props} type="datetime-local" />;
8
+ });
9
+
10
+ InputDateTime.displayName = 'InputDateTime';
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import { Input, InputProps } from './Input';
3
+
4
+ export interface InputEmailProps extends Omit<InputProps, 'type'> {}
5
+
6
+ export const InputEmail = React.forwardRef<HTMLInputElement, InputEmailProps>((props, ref) => {
7
+ return <Input ref={ref} {...props} type="email" />;
8
+ });
9
+
10
+ InputEmail.displayName = 'InputEmail';
@@ -0,0 +1,137 @@
1
+ import * as React from 'react';
2
+ import { Icon } from '../icons/icons';
3
+ import '../../style/components/input/input.css';
4
+
5
+ export interface InputFieldProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size' | 'type'> {
6
+ /**
7
+ * Input native type (text, email, password, etc.)
8
+ */
9
+ type?: string;
10
+
11
+ /**
12
+ * Label text
13
+ */
14
+ label?: string;
15
+
16
+ /**
17
+ * Helper text shown below the input
18
+ */
19
+ helperText?: string;
20
+
21
+ /**
22
+ * Error message - when present, input is in error state
23
+ */
24
+ error?: string;
25
+
26
+ /**
27
+ * Icon name from our icon library (shown on the left)
28
+ */
29
+ icon?: string;
30
+
31
+ /**
32
+ * Icon name for the right side
33
+ */
34
+ iconRight?: string;
35
+
36
+ /**
37
+ * Full width input
38
+ * @default false
39
+ */
40
+ fullWidth?: boolean;
41
+
42
+ /**
43
+ * Size variant
44
+ * @default 'md'
45
+ */
46
+ size?: 'sm' | 'md' | 'lg';
47
+
48
+ /**
49
+ * Custom class name
50
+ */
51
+ className?: string;
52
+
53
+ /**
54
+ * Right icon click handler
55
+ */
56
+ onRightIconClick?: () => void;
57
+ }
58
+
59
+ export const InputField = React.forwardRef<HTMLInputElement, InputFieldProps>(
60
+ (
61
+ {
62
+ type = 'text',
63
+ label,
64
+ helperText,
65
+ error,
66
+ icon,
67
+ iconRight,
68
+ fullWidth = false,
69
+ size = 'md',
70
+ className = '',
71
+ disabled,
72
+ id,
73
+ onRightIconClick,
74
+ ...props
75
+ },
76
+ ref
77
+ ) => {
78
+ const inputId = id || `input-${React.useId()}`;
79
+
80
+ const containerClasses = [
81
+ 'input-container',
82
+ fullWidth && 'input-container--full-width',
83
+ className,
84
+ ]
85
+ .filter(Boolean)
86
+ .join(' ');
87
+
88
+ const inputClasses = [
89
+ 'input',
90
+ `input--${size}`,
91
+ error && 'input--error',
92
+ disabled && 'input--disabled',
93
+ icon && 'input--has-icon-left',
94
+ iconRight && 'input--has-icon-right',
95
+ ]
96
+ .filter(Boolean)
97
+ .join(' ');
98
+
99
+ return (
100
+ <div className={containerClasses}>
101
+ {label && (
102
+ <label htmlFor={inputId} className="input__label">
103
+ {label}
104
+ </label>
105
+ )}
106
+ <div className="input__wrapper">
107
+ {icon && (
108
+ <span className="input__icon input__icon--left" aria-hidden="true">
109
+ <Icon name={icon as any} size={16} />
110
+ </span>
111
+ )}
112
+ <input
113
+ ref={ref}
114
+ type={type}
115
+ id={inputId}
116
+ className={inputClasses}
117
+ disabled={disabled}
118
+ {...props}
119
+ />
120
+ {iconRight && (
121
+ <button
122
+ type="button"
123
+ className="input__icon input__icon--right"
124
+ onClick={onRightIconClick}
125
+ tabIndex={-1}
126
+ aria-label="Icon action"
127
+ >
128
+ <Icon name={iconRight as any} size={16} />
129
+ </button>
130
+ )}
131
+ </div>
132
+ {error && <p className="input__message input__message--error">{error}</p>}
133
+ {helperText && !error && <p className="input__message">{helperText}</p>}
134
+ </div>
135
+ );
136
+ }
137
+ );
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import { Input, InputProps } from './Input';
3
+
4
+ export interface InputNumberProps extends Omit<InputProps, 'type'> {}
5
+
6
+ export const InputNumber = React.forwardRef<HTMLInputElement, InputNumberProps>((props, ref) => {
7
+ return <Input ref={ref} {...props} type="number" />;
8
+ });
9
+
10
+ InputNumber.displayName = 'InputNumber';
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import { Input, InputProps } from './Input';
3
+
4
+ export interface InputPasswordProps extends Omit<InputProps, 'type'> {}
5
+
6
+ export const InputPassword = React.forwardRef<HTMLInputElement, InputPasswordProps>((props, ref) => {
7
+ return <Input ref={ref} {...props} type="password" />;
8
+ });
9
+
10
+ InputPassword.displayName = 'InputPassword';
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import { Input, InputProps } from './Input';
3
+
4
+ export interface InputSearchProps extends Omit<InputProps, 'type'> {}
5
+
6
+ export const InputSearch = React.forwardRef<HTMLInputElement, InputSearchProps>((props, ref) => {
7
+ return <Input ref={ref} {...props} type="search" />;
8
+ });
9
+
10
+ InputSearch.displayName = 'InputSearch';
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import { Input, InputProps } from './Input';
3
+
4
+ export interface InputTelProps extends Omit<InputProps, 'type'> {}
5
+
6
+ export const InputTel = React.forwardRef<HTMLInputElement, InputTelProps>((props, ref) => {
7
+ return <Input ref={ref} {...props} type="tel" />;
8
+ });
9
+
10
+ InputTel.displayName = 'InputTel';
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import { Input, InputProps } from './Input';
3
+
4
+ export interface InputTextProps extends Omit<InputProps, 'type'> {}
5
+
6
+ export const InputText = React.forwardRef<HTMLInputElement, InputTextProps>((props, ref) => {
7
+ return <Input ref={ref} {...props} type="text" />;
8
+ });
9
+
10
+ InputText.displayName = 'InputText';
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import { Input, InputProps } from './Input';
3
+
4
+ export interface InputTimeProps extends Omit<InputProps, 'type'> {}
5
+
6
+ export const InputTime = React.forwardRef<HTMLInputElement, InputTimeProps>((props, ref) => {
7
+ return <Input ref={ref} {...props} type="time" />;
8
+ });
9
+
10
+ InputTime.displayName = 'InputTime';
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import { Input, InputProps } from './Input';
3
+
4
+ export interface InputUrlProps extends Omit<InputProps, 'type'> {}
5
+
6
+ export const InputUrl = React.forwardRef<HTMLInputElement, InputUrlProps>((props, ref) => {
7
+ return <Input ref={ref} {...props} type="url" />;
8
+ });
9
+
10
+ InputUrl.displayName = 'InputUrl';