@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.
- package/README.md +111 -0
- package/dist/index.css +3699 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.mts +1087 -0
- package/dist/index.d.ts +1087 -0
- package/dist/index.js +3391 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +3287 -0
- package/dist/index.mjs.map +1 -0
- package/dist/theme.css +55 -0
- package/hellboy-ds-0.1.2.tgz +0 -0
- package/package.json +42 -0
- package/src/components/badge/Badge.tsx +29 -0
- package/src/components/badge/index.ts +1 -0
- package/src/components/banner/Banner.tsx +48 -0
- package/src/components/banner/banner.css +44 -0
- package/src/components/banner/index.ts +1 -0
- package/src/components/button/button.tsx +127 -0
- package/src/components/button/index.ts +1 -0
- package/src/components/card/card.tsx +57 -0
- package/src/components/card/index.ts +1 -0
- package/src/components/checkbox/Checkbox.tsx +98 -0
- package/src/components/checkbox/index.ts +1 -0
- package/src/components/code-block/code-block.tsx +44 -0
- package/src/components/code-block/index.ts +1 -0
- package/src/components/color-control/color-control.tsx +322 -0
- package/src/components/color-control/index.ts +1 -0
- package/src/components/drag-handle/DragHandle.tsx +78 -0
- package/src/components/drag-handle/index.ts +1 -0
- package/src/components/drawer/drawer.tsx +82 -0
- package/src/components/drawer/index.ts +1 -0
- package/src/components/floating-bar/floating-bar.tsx +52 -0
- package/src/components/floating-bar/index.ts +2 -0
- package/src/components/footer/footer.tsx +28 -0
- package/src/components/footer/index.ts +1 -0
- package/src/components/grid/Grid.tsx +53 -0
- package/src/components/grid/index.ts +1 -0
- package/src/components/header/header.tsx +57 -0
- package/src/components/header/index.ts +1 -0
- package/src/components/icons/icons.tsx +44 -0
- package/src/components/icons/index.ts +1 -0
- package/src/components/index.ts +29 -0
- package/src/components/input/DatePicker.tsx +133 -0
- package/src/components/input/Input.tsx +220 -0
- package/src/components/input/InputDate.tsx +10 -0
- package/src/components/input/InputDateTime.tsx +10 -0
- package/src/components/input/InputEmail.tsx +10 -0
- package/src/components/input/InputField.tsx +137 -0
- package/src/components/input/InputNumber.tsx +10 -0
- package/src/components/input/InputPassword.tsx +10 -0
- package/src/components/input/InputSearch.tsx +10 -0
- package/src/components/input/InputTel.tsx +10 -0
- package/src/components/input/InputText.tsx +10 -0
- package/src/components/input/InputTime.tsx +10 -0
- package/src/components/input/InputUrl.tsx +10 -0
- package/src/components/input/TimePicker.tsx +151 -0
- package/src/components/input/index.ts +11 -0
- package/src/components/layout/Layout.tsx +244 -0
- package/src/components/layout/index.ts +1 -0
- package/src/components/list/List.tsx +159 -0
- package/src/components/list/index.ts +1 -0
- package/src/components/navbar/MenuCategory.tsx +20 -0
- package/src/components/navbar/MenuGroup.tsx +288 -0
- package/src/components/navbar/MenuItem.tsx +65 -0
- package/src/components/navbar/Navbar.tsx +23 -0
- package/src/components/navbar/index.ts +4 -0
- package/src/components/page/index.ts +1 -0
- package/src/components/page/page.tsx +46 -0
- package/src/components/page-index/PageIndex.tsx +275 -0
- package/src/components/page-index/index.ts +1 -0
- package/src/components/popover/index.ts +1 -0
- package/src/components/popover/popover.tsx +199 -0
- package/src/components/radio/Radio.tsx +176 -0
- package/src/components/radio/index.ts +1 -0
- package/src/components/section/index.ts +1 -0
- package/src/components/section/section.tsx +66 -0
- package/src/components/select/Select.tsx +212 -0
- package/src/components/select/index.ts +1 -0
- package/src/components/slider/Slider.tsx +267 -0
- package/src/components/slider/index.ts +1 -0
- package/src/components/switch/index.ts +1 -0
- package/src/components/switch/switch.tsx +99 -0
- package/src/components/table/Table.tsx +147 -0
- package/src/components/table/index.ts +1 -0
- package/src/components/theme-control/index.ts +1 -0
- package/src/components/theme-control/theme-control.tsx +78 -0
- package/src/components/tooltip/index.ts +1 -0
- package/src/components/tooltip/tooltip.tsx +207 -0
- package/src/contexts/NavbarTooltipContext.tsx +48 -0
- package/src/contexts/index.ts +1 -0
- package/src/foundations/motion.md +136 -0
- package/src/index.ts +40 -0
- package/src/style/_shared/field.css +69 -0
- package/src/style/components/badge/badge.css +74 -0
- package/src/style/components/button/button.css +244 -0
- package/src/style/components/card/card.css +69 -0
- package/src/style/components/checkbox.css +142 -0
- package/src/style/components/code-block/code-block.css +34 -0
- package/src/style/components/color-control/color-control.css +126 -0
- package/src/style/components/drag-handle/drag-handle.css +68 -0
- package/src/style/components/drawer/drawer.css +210 -0
- package/src/style/components/floating-bar/floating-bar.css +39 -0
- package/src/style/components/footer/footer.css +108 -0
- package/src/style/components/grid/grid.css +33 -0
- package/src/style/components/header/header.css +44 -0
- package/src/style/components/icons/icons.css +44 -0
- package/src/style/components/input/input.css +393 -0
- package/src/style/components/layout/layout.css +205 -0
- package/src/style/components/list/list.css +140 -0
- package/src/style/components/navbar/navbar.css +342 -0
- package/src/style/components/page/page.css +46 -0
- package/src/style/components/page-index/page-index.css +158 -0
- package/src/style/components/popover/popover.css +44 -0
- package/src/style/components/radio.css +178 -0
- package/src/style/components/section/section.css +67 -0
- package/src/style/components/select/select.css +143 -0
- package/src/style/components/slider/slider.css +159 -0
- package/src/style/components/switch/switch.css +267 -0
- package/src/style/components/table/table.css +108 -0
- package/src/style/components/theme-control/theme-control.css +35 -0
- package/src/style/components/tooltip/tooltip.css +52 -0
- package/src/style/foundations/global.css +316 -0
- package/src/style/foundations/motion.css +164 -0
- package/src/style/foundations/spacing.css +51 -0
- package/src/style/foundations/typography.css +39 -0
- package/src/style/foundations/z-index.css +81 -0
- package/src/style/modes/dark.css +146 -0
- package/src/style/modes/light.css +147 -0
- package/src/style/semantic.css +52 -0
- package/src/style/styles.css +51 -0
- package/src/style/themes/theme.json +37 -0
- package/src/utils/README.md +305 -0
- package/src/utils/USER_PREFERENCES.md +558 -0
- package/src/utils/theme.ts +127 -0
- package/src/utils/user-preferences.ts +577 -0
- package/tsconfig.json +25 -0
- package/tsup.config.ts +52 -0
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
import React, { useState, useEffect, useCallback } from 'react';
|
|
2
|
+
import { Slider } from '../slider';
|
|
3
|
+
import '../../style/components/color-control/color-control.css';
|
|
4
|
+
|
|
5
|
+
interface ColorHue {
|
|
6
|
+
primary: number;
|
|
7
|
+
secondary: number;
|
|
8
|
+
accent: number;
|
|
9
|
+
success: number;
|
|
10
|
+
warning: number;
|
|
11
|
+
error: number;
|
|
12
|
+
info: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface ColorSaturation {
|
|
16
|
+
primary: number;
|
|
17
|
+
secondary: number;
|
|
18
|
+
accent: number;
|
|
19
|
+
success: number;
|
|
20
|
+
warning: number;
|
|
21
|
+
error: number;
|
|
22
|
+
info: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface ColorLightness {
|
|
26
|
+
primary: number;
|
|
27
|
+
secondary: number;
|
|
28
|
+
accent: number;
|
|
29
|
+
success: number;
|
|
30
|
+
warning: number;
|
|
31
|
+
error: number;
|
|
32
|
+
info: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface ColorConfig {
|
|
36
|
+
hues: ColorHue;
|
|
37
|
+
saturations: ColorSaturation;
|
|
38
|
+
lightnesses: ColorLightness;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Function to get CSS variable value as number
|
|
42
|
+
const getCSSVariable = (variable: string): number => {
|
|
43
|
+
if (typeof window === 'undefined') return 0;
|
|
44
|
+
const value = getComputedStyle(document.documentElement).getPropertyValue(variable).trim();
|
|
45
|
+
return value ? parseInt(value, 10) : 0;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// Load colors from CSS variables (generated from theme.json)
|
|
49
|
+
const loadColorsFromCSS = (): ColorConfig => {
|
|
50
|
+
return {
|
|
51
|
+
hues: {
|
|
52
|
+
primary: getCSSVariable('--primary-hue'),
|
|
53
|
+
secondary: getCSSVariable('--secondary-hue'),
|
|
54
|
+
accent: getCSSVariable('--accent-hue'),
|
|
55
|
+
success: getCSSVariable('--success-hue'),
|
|
56
|
+
warning: getCSSVariable('--warning-hue'),
|
|
57
|
+
error: getCSSVariable('--error-hue'),
|
|
58
|
+
info: getCSSVariable('--info-hue'),
|
|
59
|
+
},
|
|
60
|
+
saturations: {
|
|
61
|
+
primary: getCSSVariable('--primary-saturation'),
|
|
62
|
+
secondary: getCSSVariable('--secondary-saturation'),
|
|
63
|
+
accent: getCSSVariable('--accent-saturation'),
|
|
64
|
+
success: getCSSVariable('--success-saturation'),
|
|
65
|
+
warning: getCSSVariable('--warning-saturation'),
|
|
66
|
+
error: getCSSVariable('--error-saturation'),
|
|
67
|
+
info: getCSSVariable('--info-saturation'),
|
|
68
|
+
},
|
|
69
|
+
lightnesses: {
|
|
70
|
+
primary: getCSSVariable('--primary-lightness'),
|
|
71
|
+
secondary: getCSSVariable('--secondary-lightness'),
|
|
72
|
+
accent: getCSSVariable('--accent-lightness'),
|
|
73
|
+
success: getCSSVariable('--success-lightness'),
|
|
74
|
+
warning: getCSSVariable('--warning-lightness'),
|
|
75
|
+
error: getCSSVariable('--error-lightness'),
|
|
76
|
+
info: getCSSVariable('--info-lightness'),
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export const ColorControl: React.FC = () => {
|
|
82
|
+
const [colors, setColors] = useState<ColorConfig>(() => {
|
|
83
|
+
// Load saved colors from localStorage, fallback to CSS variables from theme.json
|
|
84
|
+
const saved = localStorage.getItem('color-config');
|
|
85
|
+
return saved ? JSON.parse(saved) : loadColorsFromCSS();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const [pendingColors, setPendingColors] = useState<ColorConfig>(colors);
|
|
89
|
+
|
|
90
|
+
// Initialize CSS on mount
|
|
91
|
+
useEffect(() => {
|
|
92
|
+
const root = document.documentElement;
|
|
93
|
+
// Set hues
|
|
94
|
+
root.style.setProperty('--hue-primary', colors.hues.primary.toString());
|
|
95
|
+
root.style.setProperty('--hue-secondary', colors.hues.secondary.toString());
|
|
96
|
+
root.style.setProperty('--hue-accent', colors.hues.accent.toString());
|
|
97
|
+
root.style.setProperty('--hue-success', colors.hues.success.toString());
|
|
98
|
+
root.style.setProperty('--hue-warning', colors.hues.warning.toString());
|
|
99
|
+
root.style.setProperty('--hue-error', colors.hues.error.toString());
|
|
100
|
+
root.style.setProperty('--hue-info', colors.hues.info.toString());
|
|
101
|
+
|
|
102
|
+
// Set saturations
|
|
103
|
+
root.style.setProperty('--saturation-primary', `${colors.saturations.primary}%`);
|
|
104
|
+
root.style.setProperty('--saturation-secondary', `${colors.saturations.secondary}%`);
|
|
105
|
+
root.style.setProperty('--saturation-accent', `${colors.saturations.accent}%`);
|
|
106
|
+
root.style.setProperty('--saturation-success', `${colors.saturations.success}%`);
|
|
107
|
+
root.style.setProperty('--saturation-warning', `${colors.saturations.warning}%`);
|
|
108
|
+
root.style.setProperty('--saturation-error', `${colors.saturations.error}%`);
|
|
109
|
+
root.style.setProperty('--saturation-info', `${colors.saturations.info}%`);
|
|
110
|
+
|
|
111
|
+
// Set lightnesses
|
|
112
|
+
root.style.setProperty('--lightness-primary', `${colors.lightnesses.primary}%`);
|
|
113
|
+
root.style.setProperty('--lightness-secondary', `${colors.lightnesses.secondary}%`);
|
|
114
|
+
root.style.setProperty('--lightness-accent', `${colors.lightnesses.accent}%`);
|
|
115
|
+
root.style.setProperty('--lightness-success', `${colors.lightnesses.success}%`);
|
|
116
|
+
root.style.setProperty('--lightness-warning', `${colors.lightnesses.warning}%`);
|
|
117
|
+
root.style.setProperty('--lightness-error', `${colors.lightnesses.error}%`);
|
|
118
|
+
root.style.setProperty('--lightness-info', `${colors.lightnesses.info}%`);
|
|
119
|
+
}, []);
|
|
120
|
+
|
|
121
|
+
const commitColorChange = useCallback((color: keyof ColorHue, property: 'hue' | 'saturation' | 'lightness', value: number) => {
|
|
122
|
+
setColors(prev => {
|
|
123
|
+
const newColors = {
|
|
124
|
+
...prev,
|
|
125
|
+
[property === 'hue' ? 'hues' : property === 'saturation' ? 'saturations' : 'lightnesses']: {
|
|
126
|
+
...prev[property === 'hue' ? 'hues' : property === 'saturation' ? 'saturations' : 'lightnesses'],
|
|
127
|
+
[color]: value
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
// Update CSS and localStorage immediately after drag ends
|
|
132
|
+
const root = document.documentElement;
|
|
133
|
+
const cssVar = property === 'hue' ? `--hue-${color}` : `--${property}-${color}`;
|
|
134
|
+
const cssValue = property === 'hue' ? value.toString() : `${value}%`;
|
|
135
|
+
root.style.setProperty(cssVar, cssValue);
|
|
136
|
+
localStorage.setItem('color-config', JSON.stringify(newColors));
|
|
137
|
+
|
|
138
|
+
return newColors;
|
|
139
|
+
});
|
|
140
|
+
}, []);
|
|
141
|
+
|
|
142
|
+
const resetToDefaults = useCallback(() => {
|
|
143
|
+
const cssColors = loadColorsFromCSS();
|
|
144
|
+
setColors(cssColors);
|
|
145
|
+
setPendingColors(cssColors);
|
|
146
|
+
}, []);
|
|
147
|
+
|
|
148
|
+
const renderColorControls = (colorName: keyof ColorHue) => {
|
|
149
|
+
const hue = pendingColors.hues[colorName];
|
|
150
|
+
const saturation = pendingColors.saturations[colorName];
|
|
151
|
+
const lightness = pendingColors.lightnesses[colorName];
|
|
152
|
+
|
|
153
|
+
return (
|
|
154
|
+
<div key={colorName} className="color-control__color-group">
|
|
155
|
+
<h4 className="color-control__color-title">{colorName.charAt(0).toUpperCase() + colorName.slice(1)}</h4>
|
|
156
|
+
|
|
157
|
+
<div className="color-control__controls-row">
|
|
158
|
+
<div className="color-control__control">
|
|
159
|
+
<label className="color-control__label">Hue</label>
|
|
160
|
+
<Slider
|
|
161
|
+
label=""
|
|
162
|
+
value={hue}
|
|
163
|
+
min={0}
|
|
164
|
+
max={360}
|
|
165
|
+
step={1}
|
|
166
|
+
onChange={(v) => setPendingColors(prev => ({
|
|
167
|
+
...prev,
|
|
168
|
+
hues: { ...prev.hues, [colorName]: v }
|
|
169
|
+
}))}
|
|
170
|
+
onChangeEnd={(v) => {
|
|
171
|
+
setPendingColors(prev => ({
|
|
172
|
+
...prev,
|
|
173
|
+
hues: { ...prev.hues, [colorName]: v }
|
|
174
|
+
}));
|
|
175
|
+
commitColorChange(colorName, 'hue', v);
|
|
176
|
+
}}
|
|
177
|
+
type="hue"
|
|
178
|
+
showValue={false}
|
|
179
|
+
/>
|
|
180
|
+
</div>
|
|
181
|
+
|
|
182
|
+
<div className="color-control__control">
|
|
183
|
+
<label className="color-control__label">Saturation</label>
|
|
184
|
+
<Slider
|
|
185
|
+
label=""
|
|
186
|
+
value={saturation}
|
|
187
|
+
min={0}
|
|
188
|
+
max={100}
|
|
189
|
+
step={1}
|
|
190
|
+
onChange={(v) => setPendingColors(prev => ({
|
|
191
|
+
...prev,
|
|
192
|
+
saturations: { ...prev.saturations, [colorName]: v }
|
|
193
|
+
}))}
|
|
194
|
+
onChangeEnd={(v) => {
|
|
195
|
+
setPendingColors(prev => ({
|
|
196
|
+
...prev,
|
|
197
|
+
saturations: { ...prev.saturations, [colorName]: v }
|
|
198
|
+
}));
|
|
199
|
+
commitColorChange(colorName, 'saturation', v);
|
|
200
|
+
}}
|
|
201
|
+
type="saturation"
|
|
202
|
+
baseHue={hue}
|
|
203
|
+
showValue={false}
|
|
204
|
+
/>
|
|
205
|
+
</div>
|
|
206
|
+
|
|
207
|
+
<div className="color-control__control">
|
|
208
|
+
<label className="color-control__label">Lightness</label>
|
|
209
|
+
<Slider
|
|
210
|
+
label=""
|
|
211
|
+
value={lightness}
|
|
212
|
+
min={0}
|
|
213
|
+
max={100}
|
|
214
|
+
step={1}
|
|
215
|
+
onChange={(v) => setPendingColors(prev => ({
|
|
216
|
+
...prev,
|
|
217
|
+
lightnesses: { ...prev.lightnesses, [colorName]: v }
|
|
218
|
+
}))}
|
|
219
|
+
onChangeEnd={(v) => {
|
|
220
|
+
setPendingColors(prev => ({
|
|
221
|
+
...prev,
|
|
222
|
+
lightnesses: { ...prev.lightnesses, [colorName]: v }
|
|
223
|
+
}));
|
|
224
|
+
commitColorChange(colorName, 'lightness', v);
|
|
225
|
+
}}
|
|
226
|
+
type="lightness"
|
|
227
|
+
baseHue={hue}
|
|
228
|
+
baseSaturation={saturation}
|
|
229
|
+
showValue={false}
|
|
230
|
+
/>
|
|
231
|
+
</div>
|
|
232
|
+
|
|
233
|
+
<div
|
|
234
|
+
className="color-control__preview"
|
|
235
|
+
style={{
|
|
236
|
+
backgroundColor: `hsl(${hue}, ${saturation}%, ${lightness}%)`,
|
|
237
|
+
border: '2px solid var(--color-neutral-300)'
|
|
238
|
+
}}
|
|
239
|
+
/>
|
|
240
|
+
</div>
|
|
241
|
+
</div>
|
|
242
|
+
);
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
return (
|
|
246
|
+
<div className="color-control">
|
|
247
|
+
<div className="color-control__header">
|
|
248
|
+
<h3 className="color-control__title">Paleta de Cores</h3>
|
|
249
|
+
<p className="color-control__description">
|
|
250
|
+
Ajuste o matiz, saturação e luminosidade das cores. As mudanças são aplicadas instantaneamente.
|
|
251
|
+
</p>
|
|
252
|
+
</div>
|
|
253
|
+
|
|
254
|
+
<div className="color-control__sliders">
|
|
255
|
+
{renderColorControls('primary')}
|
|
256
|
+
{renderColorControls('secondary')}
|
|
257
|
+
{renderColorControls('accent')}
|
|
258
|
+
{renderColorControls('success')}
|
|
259
|
+
{renderColorControls('warning')}
|
|
260
|
+
{renderColorControls('error')}
|
|
261
|
+
{renderColorControls('info')}
|
|
262
|
+
</div>
|
|
263
|
+
|
|
264
|
+
<div className="color-control__actions">
|
|
265
|
+
<button
|
|
266
|
+
onClick={resetToDefaults}
|
|
267
|
+
className="color-control__reset-btn"
|
|
268
|
+
>
|
|
269
|
+
Restaurar Padrões
|
|
270
|
+
</button>
|
|
271
|
+
</div>
|
|
272
|
+
|
|
273
|
+
<div className="color-control__preview-section">
|
|
274
|
+
<h4 className="color-control__preview-title">Prévia das Cores</h4>
|
|
275
|
+
<div className="color-control__color-grid">
|
|
276
|
+
<div
|
|
277
|
+
className="color-control__color-sample"
|
|
278
|
+
style={{ backgroundColor: `hsl(${pendingColors.hues.primary}, ${pendingColors.saturations.primary}%, ${pendingColors.lightnesses.primary}%)` }}
|
|
279
|
+
>
|
|
280
|
+
Primary
|
|
281
|
+
</div>
|
|
282
|
+
<div
|
|
283
|
+
className="color-control__color-sample"
|
|
284
|
+
style={{ backgroundColor: `hsl(${pendingColors.hues.secondary}, ${pendingColors.saturations.secondary}%, ${pendingColors.lightnesses.secondary}%)` }}
|
|
285
|
+
>
|
|
286
|
+
Secondary
|
|
287
|
+
</div>
|
|
288
|
+
<div
|
|
289
|
+
className="color-control__color-sample"
|
|
290
|
+
style={{ backgroundColor: `hsl(${pendingColors.hues.accent}, ${pendingColors.saturations.accent}%, ${pendingColors.lightnesses.accent}%)` }}
|
|
291
|
+
>
|
|
292
|
+
Accent
|
|
293
|
+
</div>
|
|
294
|
+
<div
|
|
295
|
+
className="color-control__color-sample"
|
|
296
|
+
style={{ backgroundColor: `hsl(${pendingColors.hues.success}, ${pendingColors.saturations.success}%, ${pendingColors.lightnesses.success}%)` }}
|
|
297
|
+
>
|
|
298
|
+
Success
|
|
299
|
+
</div>
|
|
300
|
+
<div
|
|
301
|
+
className="color-control__color-sample"
|
|
302
|
+
style={{ backgroundColor: `hsl(${pendingColors.hues.warning}, ${pendingColors.saturations.warning}%, ${pendingColors.lightnesses.warning}%)` }}
|
|
303
|
+
>
|
|
304
|
+
Warning
|
|
305
|
+
</div>
|
|
306
|
+
<div
|
|
307
|
+
className="color-control__color-sample"
|
|
308
|
+
style={{ backgroundColor: `hsl(${pendingColors.hues.error}, ${pendingColors.saturations.error}%, ${pendingColors.lightnesses.error}%)` }}
|
|
309
|
+
>
|
|
310
|
+
Error
|
|
311
|
+
</div>
|
|
312
|
+
<div
|
|
313
|
+
className="color-control__color-sample"
|
|
314
|
+
style={{ backgroundColor: `hsl(${pendingColors.hues.info}, ${pendingColors.saturations.info}%, ${pendingColors.lightnesses.info}%)` }}
|
|
315
|
+
>
|
|
316
|
+
Info
|
|
317
|
+
</div>
|
|
318
|
+
</div>
|
|
319
|
+
</div>
|
|
320
|
+
</div>
|
|
321
|
+
);
|
|
322
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './color-control';
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import React, { useState, useRef, useCallback } from 'react';
|
|
2
|
+
import '../../style/components/drag-handle/drag-handle.css';
|
|
3
|
+
|
|
4
|
+
export interface DragHandleProps {
|
|
5
|
+
onResize: (delta: number) => void;
|
|
6
|
+
onResizeEnd?: () => void;
|
|
7
|
+
orientation?: 'vertical' | 'horizontal';
|
|
8
|
+
className?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const DragHandle: React.FC<DragHandleProps> = ({
|
|
12
|
+
onResize,
|
|
13
|
+
onResizeEnd,
|
|
14
|
+
orientation = 'vertical',
|
|
15
|
+
className = '',
|
|
16
|
+
}) => {
|
|
17
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
18
|
+
const dragHandleRef = useRef<HTMLDivElement>(null);
|
|
19
|
+
const startPosRef = useRef<number>(0);
|
|
20
|
+
|
|
21
|
+
const handleMouseDown = useCallback((e: React.MouseEvent) => {
|
|
22
|
+
setIsDragging(true);
|
|
23
|
+
startPosRef.current = orientation === 'vertical' ? e.clientX : e.clientY;
|
|
24
|
+
e.preventDefault();
|
|
25
|
+
}, [orientation]);
|
|
26
|
+
|
|
27
|
+
const handleMouseMove = useCallback((e: MouseEvent) => {
|
|
28
|
+
if (!isDragging) return;
|
|
29
|
+
|
|
30
|
+
const currentPos = orientation === 'vertical' ? e.clientX : e.clientY;
|
|
31
|
+
const delta = currentPos - startPosRef.current;
|
|
32
|
+
onResize(delta);
|
|
33
|
+
startPosRef.current = currentPos;
|
|
34
|
+
}, [isDragging, onResize, orientation]);
|
|
35
|
+
|
|
36
|
+
const handleMouseUp = useCallback(() => {
|
|
37
|
+
setIsDragging(false);
|
|
38
|
+
onResizeEnd?.();
|
|
39
|
+
}, [onResizeEnd]);
|
|
40
|
+
|
|
41
|
+
React.useEffect(() => {
|
|
42
|
+
if (isDragging) {
|
|
43
|
+
document.addEventListener('mousemove', handleMouseMove);
|
|
44
|
+
document.addEventListener('mouseup', handleMouseUp);
|
|
45
|
+
document.body.style.cursor = orientation === 'vertical' ? 'col-resize' : 'row-resize';
|
|
46
|
+
document.body.style.userSelect = 'none';
|
|
47
|
+
} else {
|
|
48
|
+
document.removeEventListener('mousemove', handleMouseMove);
|
|
49
|
+
document.removeEventListener('mouseup', handleMouseUp);
|
|
50
|
+
document.body.style.cursor = '';
|
|
51
|
+
document.body.style.userSelect = '';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return () => {
|
|
55
|
+
document.removeEventListener('mousemove', handleMouseMove);
|
|
56
|
+
document.removeEventListener('mouseup', handleMouseUp);
|
|
57
|
+
document.body.style.cursor = '';
|
|
58
|
+
document.body.style.userSelect = '';
|
|
59
|
+
};
|
|
60
|
+
}, [isDragging, handleMouseMove, handleMouseUp, orientation]);
|
|
61
|
+
|
|
62
|
+
const classes = [
|
|
63
|
+
'drag-handle',
|
|
64
|
+
`drag-handle--${orientation}`,
|
|
65
|
+
isDragging && 'drag-handle--dragging',
|
|
66
|
+
className,
|
|
67
|
+
].filter(Boolean).join(' ');
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<div
|
|
71
|
+
ref={dragHandleRef}
|
|
72
|
+
className={classes}
|
|
73
|
+
onMouseDown={handleMouseDown}
|
|
74
|
+
>
|
|
75
|
+
<div className="drag-handle__indicator" />
|
|
76
|
+
</div>
|
|
77
|
+
);
|
|
78
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './DragHandle';
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Dialog, DialogProps, DialogHeading, useDialogStore } from "@ariakit/react";
|
|
3
|
+
import "../../style/components/drawer/drawer.css";
|
|
4
|
+
|
|
5
|
+
export type DrawerPlacement = "top" | "bottom" | "left" | "right";
|
|
6
|
+
|
|
7
|
+
export interface DrawerProps extends Omit<DialogProps, "children"> {
|
|
8
|
+
children: React.ReactNode;
|
|
9
|
+
title?: string;
|
|
10
|
+
placement?: DrawerPlacement;
|
|
11
|
+
size?: string | number;
|
|
12
|
+
store: ReturnType<typeof useDialogStore>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const Drawer: React.FC<DrawerProps> = ({
|
|
16
|
+
children,
|
|
17
|
+
title,
|
|
18
|
+
placement = "right",
|
|
19
|
+
size = "400px",
|
|
20
|
+
store,
|
|
21
|
+
...props
|
|
22
|
+
}) => {
|
|
23
|
+
const sizeValue = typeof size === "number" ? `${size}px` : size;
|
|
24
|
+
|
|
25
|
+
const getDrawerStyles = (): React.CSSProperties => {
|
|
26
|
+
const baseStyles: React.CSSProperties = {
|
|
27
|
+
zIndex: 1000,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
switch (placement) {
|
|
31
|
+
case "top":
|
|
32
|
+
return {
|
|
33
|
+
...baseStyles,
|
|
34
|
+
height: sizeValue,
|
|
35
|
+
};
|
|
36
|
+
case "bottom":
|
|
37
|
+
return {
|
|
38
|
+
...baseStyles,
|
|
39
|
+
height: sizeValue,
|
|
40
|
+
};
|
|
41
|
+
case "left":
|
|
42
|
+
return {
|
|
43
|
+
...baseStyles,
|
|
44
|
+
width: sizeValue,
|
|
45
|
+
};
|
|
46
|
+
case "right":
|
|
47
|
+
default:
|
|
48
|
+
return {
|
|
49
|
+
...baseStyles,
|
|
50
|
+
width: sizeValue,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<Dialog
|
|
57
|
+
store={store}
|
|
58
|
+
{...props}
|
|
59
|
+
className={`drawer drawer--${placement}`}
|
|
60
|
+
style={getDrawerStyles()}
|
|
61
|
+
backdrop={<div className="drawer__backdrop" />}
|
|
62
|
+
>
|
|
63
|
+
<div className="drawer__content">
|
|
64
|
+
{title && (
|
|
65
|
+
<div className="drawer__header">
|
|
66
|
+
<DialogHeading className="drawer__title">{title}</DialogHeading>
|
|
67
|
+
<button
|
|
68
|
+
className="drawer__close"
|
|
69
|
+
onClick={store.hide}
|
|
70
|
+
aria-label="Close drawer"
|
|
71
|
+
>
|
|
72
|
+
✕
|
|
73
|
+
</button>
|
|
74
|
+
</div>
|
|
75
|
+
)}
|
|
76
|
+
<div className="drawer__body">
|
|
77
|
+
{children}
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
</Dialog>
|
|
81
|
+
);
|
|
82
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Drawer, type DrawerProps, type DrawerPlacement } from './drawer';
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import '../../style/components/floating-bar/floating-bar.css';
|
|
3
|
+
|
|
4
|
+
export type FloatingBarPosition = 'top' | 'bottom';
|
|
5
|
+
|
|
6
|
+
export interface FloatingBarProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
7
|
+
/**
|
|
8
|
+
* Position of the floating bar
|
|
9
|
+
* @default 'bottom'
|
|
10
|
+
*/
|
|
11
|
+
position?: FloatingBarPosition;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Content of the floating bar
|
|
15
|
+
*/
|
|
16
|
+
children: React.ReactNode;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Additional CSS classes
|
|
20
|
+
*/
|
|
21
|
+
className?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* FloatingBar Component
|
|
26
|
+
* A floating bar that sticks to the top or bottom within the main layout container
|
|
27
|
+
*/
|
|
28
|
+
export const FloatingBar: React.FC<FloatingBarProps> = ({
|
|
29
|
+
position = 'bottom',
|
|
30
|
+
children,
|
|
31
|
+
className = '',
|
|
32
|
+
...props
|
|
33
|
+
}) => {
|
|
34
|
+
const classes = [
|
|
35
|
+
'floating-bar',
|
|
36
|
+
`floating-bar--${position}`,
|
|
37
|
+
className,
|
|
38
|
+
]
|
|
39
|
+
.filter(Boolean)
|
|
40
|
+
.join(' ');
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<div
|
|
44
|
+
className={classes}
|
|
45
|
+
{...props}
|
|
46
|
+
>
|
|
47
|
+
<div className="floating-bar__content">
|
|
48
|
+
{children}
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
);
|
|
52
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import '../../style/components/footer/footer.css';
|
|
3
|
+
|
|
4
|
+
export interface FooterProps extends React.HTMLAttributes<HTMLElement> {
|
|
5
|
+
children?: React.ReactNode;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const Footer: React.FC<FooterProps> = ({ className = '', children, ...props }) => {
|
|
9
|
+
const currentYear = new Date().getFullYear();
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<footer className={`footer ${className}`.trim()} {...props}>
|
|
13
|
+
<div className="footer__container">
|
|
14
|
+
{children ? (
|
|
15
|
+
children
|
|
16
|
+
) : (
|
|
17
|
+
<>
|
|
18
|
+
<div className="footer__bottom">
|
|
19
|
+
<p className="footer__copyright">
|
|
20
|
+
© {currentYear} <strong>HellboyDS</strong> is a design system package published on npm by user <strong>@hellboy</strong>. This project is not affiliated with any comic book, film, television, or other artistic works.
|
|
21
|
+
</p>
|
|
22
|
+
</div>
|
|
23
|
+
</>
|
|
24
|
+
)}
|
|
25
|
+
</div>
|
|
26
|
+
</footer>
|
|
27
|
+
);
|
|
28
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Footer, type FooterProps } from './footer';
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import "../../style/components/grid/grid.css";
|
|
3
|
+
|
|
4
|
+
export type GridColumns = number | "auto-fit" | "auto-fill";
|
|
5
|
+
|
|
6
|
+
export interface GridProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
7
|
+
/**
|
|
8
|
+
* Number of columns or auto-fit/auto-fill
|
|
9
|
+
* @default "auto-fit"
|
|
10
|
+
*/
|
|
11
|
+
columns?: GridColumns;
|
|
12
|
+
/**
|
|
13
|
+
* Minimum width for each column (when using auto-fit/auto-fill)
|
|
14
|
+
* @default "250px"
|
|
15
|
+
*/
|
|
16
|
+
minWidth?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Gap between grid items
|
|
19
|
+
* @default "1rem"
|
|
20
|
+
*/
|
|
21
|
+
gap?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Maximum number of columns (when columns is a number)
|
|
24
|
+
*/
|
|
25
|
+
maxColumns?: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const Grid: React.FC<GridProps> = ({
|
|
29
|
+
columns = "auto-fit",
|
|
30
|
+
minWidth = "250px",
|
|
31
|
+
gap = "1rem",
|
|
32
|
+
maxColumns,
|
|
33
|
+
className = "",
|
|
34
|
+
children,
|
|
35
|
+
...props
|
|
36
|
+
}) => {
|
|
37
|
+
const gridClass = [
|
|
38
|
+
"grid",
|
|
39
|
+
typeof columns === "number" ? `grid--cols-${Math.min(columns, maxColumns || columns)}` : `grid--${columns}`,
|
|
40
|
+
className
|
|
41
|
+
].filter(Boolean).join(" ");
|
|
42
|
+
|
|
43
|
+
const style = {
|
|
44
|
+
"--grid-min-width": minWidth,
|
|
45
|
+
"--grid-gap": gap,
|
|
46
|
+
} as React.CSSProperties;
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<div className={gridClass} style={style} {...props}>
|
|
50
|
+
{children}
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./Grid";
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import React, { ReactNode, useMemo } from 'react';
|
|
2
|
+
|
|
3
|
+
export interface HeaderProps {
|
|
4
|
+
/**
|
|
5
|
+
* Header title
|
|
6
|
+
*/
|
|
7
|
+
title?: ReactNode;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Header subtitle/description
|
|
11
|
+
*/
|
|
12
|
+
subtitle?: ReactNode;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Header content - overrides title/subtitle
|
|
16
|
+
*/
|
|
17
|
+
children?: ReactNode;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Custom class name
|
|
21
|
+
*/
|
|
22
|
+
className?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Header component for page headers
|
|
27
|
+
* Displays title, subtitle, or custom content
|
|
28
|
+
*/
|
|
29
|
+
export const Header: React.FC<HeaderProps> = ({ title, subtitle, children, className }) => {
|
|
30
|
+
// Generate ID from title for page index navigation
|
|
31
|
+
const headerId = useMemo(() => {
|
|
32
|
+
if (typeof title === 'string' && title.trim()) {
|
|
33
|
+
return title
|
|
34
|
+
.toLowerCase()
|
|
35
|
+
.replace(/[^\w\s-]/g, '') // Remove special characters
|
|
36
|
+
.replace(/\s+/g, '-') // Replace spaces with hyphens
|
|
37
|
+
.replace(/-+/g, '-') // Replace multiple hyphens with single
|
|
38
|
+
.trim();
|
|
39
|
+
}
|
|
40
|
+
return undefined;
|
|
41
|
+
}, [title]);
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<header className={`header ${className || ''}`} id={headerId}>
|
|
45
|
+
<div className="header__container">
|
|
46
|
+
{children ? (
|
|
47
|
+
children
|
|
48
|
+
) : (
|
|
49
|
+
<>
|
|
50
|
+
{title && <h1 className="header__title">{title}</h1>}
|
|
51
|
+
{subtitle && <p className="header__subtitle">{subtitle}</p>}
|
|
52
|
+
</>
|
|
53
|
+
)}
|
|
54
|
+
</div>
|
|
55
|
+
</header>
|
|
56
|
+
);
|
|
57
|
+
};
|