@shalomormsby/ui 0.0.5

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/dist/hooks.mjs ADDED
@@ -0,0 +1,696 @@
1
+ "use client";
2
+
3
+ // src/lib/store/theme.ts
4
+ import { create } from "zustand";
5
+ import { persist } from "zustand/middleware";
6
+ var useThemeStore = create()(
7
+ persist(
8
+ (set, get) => ({
9
+ // Defaults
10
+ theme: "volt",
11
+ mode: "dark",
12
+ // Actions
13
+ setTheme: (theme) => set({ theme }),
14
+ setMode: (mode) => set({ mode }),
15
+ toggleMode: () => set((state) => ({ mode: state.mode === "light" ? "dark" : "light" })),
16
+ // Computed
17
+ get themeConfig() {
18
+ const state = get();
19
+ return { name: state.theme, mode: state.mode };
20
+ }
21
+ }),
22
+ {
23
+ name: "ecosystem-theme",
24
+ // Only persist theme and mode
25
+ partialize: (state) => ({
26
+ theme: state.theme,
27
+ mode: state.mode
28
+ })
29
+ }
30
+ )
31
+ );
32
+
33
+ // src/hooks/useTheme.ts
34
+ function useTheme() {
35
+ return useThemeStore();
36
+ }
37
+
38
+ // src/hooks/useMotionPreference.ts
39
+ import { useEffect } from "react";
40
+
41
+ // src/lib/store/customizer.ts
42
+ import { create as create2 } from "zustand";
43
+ import { persist as persist2 } from "zustand/middleware";
44
+ import { computeDerivedTokens } from "@sage/tokens";
45
+
46
+ // src/lib/colors.ts
47
+ var colorTokens = {
48
+ // Background colors
49
+ background: "var(--color-background)",
50
+ backgroundSecondary: "var(--color-background-secondary)",
51
+ backgroundTertiary: "var(--color-background-tertiary)",
52
+ surface: "var(--color-surface)",
53
+ // Foreground/Text colors
54
+ foreground: "var(--color-foreground)",
55
+ foregroundSecondary: "var(--color-foreground-secondary)",
56
+ foregroundTertiary: "var(--color-foreground-tertiary)",
57
+ textPrimary: "var(--color-text-primary)",
58
+ textSecondary: "var(--color-text-secondary)",
59
+ textMuted: "var(--color-text-muted)",
60
+ // Brand colors
61
+ primary: "var(--color-primary)",
62
+ primaryForeground: "var(--color-primary-foreground)",
63
+ secondary: "var(--color-secondary)",
64
+ secondaryForeground: "var(--color-secondary-foreground)",
65
+ accent: "var(--color-accent)",
66
+ accentForeground: "var(--color-accent-foreground)",
67
+ // Semantic colors
68
+ success: "var(--color-success)",
69
+ successForeground: "var(--color-success-foreground)",
70
+ warning: "var(--color-warning)",
71
+ warningForeground: "var(--color-warning-foreground)",
72
+ error: "var(--color-error)",
73
+ errorForeground: "var(--color-error-foreground)",
74
+ info: "var(--color-info)",
75
+ infoForeground: "var(--color-info-foreground)",
76
+ // Borders
77
+ border: "var(--color-border)",
78
+ borderSubtle: "var(--color-border-subtle)",
79
+ // Interactive states
80
+ hover: "var(--color-hover)",
81
+ active: "var(--color-active)",
82
+ focus: "var(--color-focus)",
83
+ // Links
84
+ link: "var(--color-link)",
85
+ linkHover: "var(--color-link-hover)",
86
+ linkHoverForeground: "var(--color-link-hover-foreground)"
87
+ };
88
+ var semanticColors = {
89
+ /**
90
+ * Status colors for indicating states
91
+ */
92
+ status: {
93
+ success: {
94
+ bg: colorTokens.success,
95
+ fg: colorTokens.successForeground
96
+ },
97
+ warning: {
98
+ bg: colorTokens.warning,
99
+ fg: colorTokens.warningForeground
100
+ },
101
+ error: {
102
+ bg: colorTokens.error,
103
+ fg: colorTokens.errorForeground
104
+ },
105
+ info: {
106
+ bg: colorTokens.info,
107
+ fg: colorTokens.infoForeground
108
+ }
109
+ },
110
+ /**
111
+ * Brand colors for primary UI elements
112
+ */
113
+ brand: {
114
+ primary: {
115
+ bg: colorTokens.primary,
116
+ fg: colorTokens.primaryForeground
117
+ },
118
+ secondary: {
119
+ bg: colorTokens.secondary,
120
+ fg: colorTokens.secondaryForeground
121
+ },
122
+ accent: {
123
+ bg: colorTokens.accent,
124
+ fg: colorTokens.accentForeground
125
+ }
126
+ },
127
+ /**
128
+ * Interactive state colors
129
+ */
130
+ interactive: {
131
+ default: {
132
+ bg: colorTokens.background,
133
+ fg: colorTokens.foreground
134
+ },
135
+ hover: {
136
+ bg: colorTokens.hover,
137
+ fg: colorTokens.foreground
138
+ },
139
+ active: {
140
+ bg: colorTokens.active,
141
+ fg: colorTokens.foreground
142
+ },
143
+ focus: {
144
+ border: colorTokens.focus
145
+ }
146
+ }
147
+ };
148
+ function hexToRgb(hex) {
149
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
150
+ return result ? {
151
+ r: parseInt(result[1], 16),
152
+ g: parseInt(result[2], 16),
153
+ b: parseInt(result[3], 16)
154
+ } : null;
155
+ }
156
+ function getLuminance(r, g, b) {
157
+ const [rs, gs, bs] = [r, g, b].map((c) => {
158
+ const srgb = c / 255;
159
+ return srgb <= 0.03928 ? srgb / 12.92 : Math.pow((srgb + 0.055) / 1.055, 2.4);
160
+ });
161
+ return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
162
+ }
163
+ function getContrastRatio(hex1, hex2) {
164
+ const rgb1 = hexToRgb(hex1);
165
+ const rgb2 = hexToRgb(hex2);
166
+ if (!rgb1 || !rgb2) return 0;
167
+ const lum1 = getLuminance(rgb1.r, rgb1.g, rgb1.b);
168
+ const lum2 = getLuminance(rgb2.r, rgb2.g, rgb2.b);
169
+ const lighter = Math.max(lum1, lum2);
170
+ const darker = Math.min(lum1, lum2);
171
+ return (lighter + 0.05) / (darker + 0.05);
172
+ }
173
+ function hexToHSL(hex) {
174
+ const rgb = hexToRgb(hex);
175
+ if (!rgb) return { h: 0, s: 0, l: 0 };
176
+ const r = rgb.r / 255;
177
+ const g = rgb.g / 255;
178
+ const b = rgb.b / 255;
179
+ const max = Math.max(r, g, b);
180
+ const min = Math.min(r, g, b);
181
+ let h = 0, s = 0, l = (max + min) / 2;
182
+ if (max !== min) {
183
+ const d = max - min;
184
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
185
+ switch (max) {
186
+ case r:
187
+ h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
188
+ break;
189
+ case g:
190
+ h = ((b - r) / d + 2) / 6;
191
+ break;
192
+ case b:
193
+ h = ((r - g) / d + 4) / 6;
194
+ break;
195
+ }
196
+ }
197
+ return {
198
+ h: Math.round(h * 360),
199
+ s: Math.round(s * 100),
200
+ l: Math.round(l * 100)
201
+ };
202
+ }
203
+ function hslToHex(h, s, l) {
204
+ h = h / 360;
205
+ s = s / 100;
206
+ l = l / 100;
207
+ let r, g, b;
208
+ if (s === 0) {
209
+ r = g = b = l;
210
+ } else {
211
+ const hue2rgb = (p2, q2, t) => {
212
+ if (t < 0) t += 1;
213
+ if (t > 1) t -= 1;
214
+ if (t < 1 / 6) return p2 + (q2 - p2) * 6 * t;
215
+ if (t < 1 / 2) return q2;
216
+ if (t < 2 / 3) return p2 + (q2 - p2) * (2 / 3 - t) * 6;
217
+ return p2;
218
+ };
219
+ const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
220
+ const p = 2 * l - q;
221
+ r = hue2rgb(p, q, h + 1 / 3);
222
+ g = hue2rgb(p, q, h);
223
+ b = hue2rgb(p, q, h - 1 / 3);
224
+ }
225
+ const toHex = (x) => {
226
+ const hex = Math.round(x * 255).toString(16);
227
+ return hex.length === 1 ? "0" + hex : hex;
228
+ };
229
+ return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
230
+ }
231
+ function getOptimalForeground(bgHex, whiteHex = "#ffffff", blackHex = "#000000") {
232
+ const whiteRatio = getContrastRatio(bgHex, whiteHex);
233
+ const blackRatio = getContrastRatio(bgHex, blackHex);
234
+ return whiteRatio > blackRatio ? whiteHex : blackHex;
235
+ }
236
+ function generateColorScale(baseHex) {
237
+ const hsl = hexToHSL(baseHex);
238
+ return {
239
+ 50: hslToHex(hsl.h, Math.max(hsl.s - 10, 20), 95),
240
+ 100: hslToHex(hsl.h, Math.max(hsl.s - 5, 30), 90),
241
+ 200: hslToHex(hsl.h, hsl.s, 80),
242
+ 300: hslToHex(hsl.h, hsl.s, 70),
243
+ 400: hslToHex(hsl.h, hsl.s, 60),
244
+ 500: baseHex,
245
+ // Base color
246
+ 600: hslToHex(hsl.h, Math.min(hsl.s + 5, 100), 45),
247
+ 700: hslToHex(hsl.h, Math.min(hsl.s + 10, 100), 35),
248
+ 800: hslToHex(hsl.h, Math.min(hsl.s + 15, 100), 25),
249
+ 900: hslToHex(hsl.h, Math.min(hsl.s + 20, 100), 15)
250
+ };
251
+ }
252
+
253
+ // src/lib/store/customizer.ts
254
+ var useCustomizer = create2()(
255
+ persist2(
256
+ (set, get) => ({
257
+ motion: 5,
258
+ prefersReducedMotion: false,
259
+ customizationMode: "simple",
260
+ customColors: {},
261
+ savedPalettes: [],
262
+ customFontThemes: {},
263
+ savedFontThemes: [],
264
+ setMotion: (level) => set({ motion: level }),
265
+ setPrefersReducedMotion: (value) => set({ prefersReducedMotion: value }),
266
+ setCustomizationMode: (mode) => set({ customizationMode: mode }),
267
+ setCustomPrimaryColor: (theme, mode, hexColor) => {
268
+ const state = get();
269
+ const currentPalette = state.customColors[theme]?.[mode];
270
+ const scale = generateColorScale(hexColor);
271
+ const primaryForeground = getOptimalForeground(hexColor);
272
+ const derivedTokens = computeDerivedTokens("--color-primary", hexColor, mode);
273
+ const isSimple = state.customizationMode === "simple";
274
+ const palette = {
275
+ primary: hexColor,
276
+ primaryForeground,
277
+ secondary: isSimple ? void 0 : currentPalette?.secondary,
278
+ secondaryForeground: isSimple ? void 0 : currentPalette?.secondaryForeground,
279
+ accent: isSimple ? void 0 : currentPalette?.accent,
280
+ accentForeground: isSimple ? void 0 : currentPalette?.accentForeground,
281
+ scale,
282
+ derivedTokens
283
+ };
284
+ set((state2) => ({
285
+ customColors: {
286
+ ...state2.customColors,
287
+ [theme]: {
288
+ ...state2.customColors[theme],
289
+ [mode]: palette
290
+ }
291
+ }
292
+ }));
293
+ },
294
+ setCustomSecondaryColor: (theme, mode, hexColor) => {
295
+ const state = get();
296
+ const currentPalette = state.customColors[theme]?.[mode];
297
+ if (!currentPalette) return;
298
+ const secondaryForeground = getOptimalForeground(hexColor);
299
+ const derivedTokens = computeDerivedTokens("--color-secondary", hexColor, mode);
300
+ set((state2) => ({
301
+ customColors: {
302
+ ...state2.customColors,
303
+ [theme]: {
304
+ ...state2.customColors[theme],
305
+ [mode]: {
306
+ ...currentPalette,
307
+ secondary: hexColor,
308
+ secondaryForeground,
309
+ derivedTokens: {
310
+ ...currentPalette.derivedTokens,
311
+ ...derivedTokens
312
+ }
313
+ }
314
+ }
315
+ }
316
+ }));
317
+ },
318
+ setCustomAccentColor: (theme, mode, hexColor) => {
319
+ const state = get();
320
+ const currentPalette = state.customColors[theme]?.[mode];
321
+ if (!currentPalette) return;
322
+ const accentForeground = getOptimalForeground(hexColor);
323
+ const derivedTokens = computeDerivedTokens("--color-accent", hexColor, mode);
324
+ set((state2) => ({
325
+ customColors: {
326
+ ...state2.customColors,
327
+ [theme]: {
328
+ ...state2.customColors[theme],
329
+ [mode]: {
330
+ ...currentPalette,
331
+ accent: hexColor,
332
+ accentForeground,
333
+ derivedTokens: {
334
+ ...currentPalette.derivedTokens,
335
+ ...derivedTokens
336
+ }
337
+ }
338
+ }
339
+ }
340
+ }));
341
+ },
342
+ applyColorPalette: (theme, mode, colors) => {
343
+ const scale = generateColorScale(colors.primary);
344
+ const primaryForeground = getOptimalForeground(colors.primary);
345
+ let derivedTokens = computeDerivedTokens("--color-primary", colors.primary, mode);
346
+ let secondary = colors.secondary;
347
+ let secondaryForeground = secondary ? getOptimalForeground(secondary) : void 0;
348
+ if (secondary) {
349
+ const secondaryDerived = computeDerivedTokens("--color-secondary", secondary, mode);
350
+ derivedTokens = { ...derivedTokens, ...secondaryDerived };
351
+ }
352
+ let accent = colors.accent;
353
+ let accentForeground = accent ? getOptimalForeground(accent) : void 0;
354
+ if (accent) {
355
+ const accentDerived = computeDerivedTokens("--color-accent", accent, mode);
356
+ derivedTokens = { ...derivedTokens, ...accentDerived };
357
+ }
358
+ const palette = {
359
+ name: colors.name,
360
+ description: colors.description,
361
+ primary: colors.primary,
362
+ primaryForeground,
363
+ secondary,
364
+ secondaryForeground,
365
+ accent,
366
+ accentForeground,
367
+ scale,
368
+ derivedTokens
369
+ };
370
+ set((state) => ({
371
+ customColors: {
372
+ ...state.customColors,
373
+ [theme]: {
374
+ ...state.customColors[theme],
375
+ [mode]: palette
376
+ }
377
+ }
378
+ }));
379
+ },
380
+ resetCustomColors: (theme, mode) => {
381
+ if (mode) {
382
+ set((state) => ({
383
+ customColors: {
384
+ ...state.customColors,
385
+ [theme]: {
386
+ ...state.customColors[theme],
387
+ [mode]: void 0
388
+ }
389
+ }
390
+ }));
391
+ } else {
392
+ set((state) => {
393
+ const { [theme]: _, ...rest } = state.customColors;
394
+ return { customColors: rest };
395
+ });
396
+ }
397
+ },
398
+ getActiveColorPalette: (theme, mode) => {
399
+ return get().customColors[theme]?.[mode] || null;
400
+ },
401
+ // Saved palette management
402
+ savePalette: (paletteData) => {
403
+ const id = `custom-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
404
+ const newPalette = {
405
+ ...paletteData,
406
+ id,
407
+ category: "custom",
408
+ createdAt: Date.now()
409
+ };
410
+ set((state) => ({
411
+ savedPalettes: [...state.savedPalettes, newPalette]
412
+ }));
413
+ },
414
+ updatePalette: (id, updates) => {
415
+ set((state) => ({
416
+ savedPalettes: state.savedPalettes.map(
417
+ (p) => p.id === id ? { ...p, ...updates } : p
418
+ )
419
+ }));
420
+ },
421
+ renamePalette: (id, newName) => {
422
+ set((state) => ({
423
+ savedPalettes: state.savedPalettes.map(
424
+ (p) => p.id === id ? { ...p, name: newName } : p
425
+ )
426
+ }));
427
+ },
428
+ deletePalette: (id) => {
429
+ set((state) => ({
430
+ savedPalettes: state.savedPalettes.filter((p) => p.id !== id)
431
+ }));
432
+ },
433
+ reorderPalettes: (palettes) => {
434
+ set({ savedPalettes: palettes });
435
+ },
436
+ getSavedPalettes: () => {
437
+ return get().savedPalettes;
438
+ },
439
+ // Font theme management
440
+ applyFontTheme: (theme, mode, fontTheme) => {
441
+ set((state) => ({
442
+ customFontThemes: {
443
+ ...state.customFontThemes,
444
+ [theme]: {
445
+ ...state.customFontThemes[theme],
446
+ [mode]: fontTheme
447
+ }
448
+ }
449
+ }));
450
+ },
451
+ resetCustomFonts: (theme, mode) => {
452
+ if (mode) {
453
+ set((state) => ({
454
+ customFontThemes: {
455
+ ...state.customFontThemes,
456
+ [theme]: {
457
+ ...state.customFontThemes[theme],
458
+ [mode]: void 0
459
+ }
460
+ }
461
+ }));
462
+ } else {
463
+ set((state) => {
464
+ const { [theme]: _, ...rest } = state.customFontThemes;
465
+ return { customFontThemes: rest };
466
+ });
467
+ }
468
+ },
469
+ getActiveFontTheme: (theme, mode) => {
470
+ return get().customFontThemes[theme]?.[mode] || null;
471
+ },
472
+ // Saved font theme management
473
+ saveFontTheme: (fontThemeData) => {
474
+ const id = `font-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
475
+ const newFontTheme = {
476
+ ...fontThemeData,
477
+ id,
478
+ category: "custom",
479
+ createdAt: Date.now(),
480
+ isCustom: true
481
+ };
482
+ set((state) => ({
483
+ savedFontThemes: [...state.savedFontThemes, newFontTheme]
484
+ }));
485
+ },
486
+ updateFontTheme: (id, updates) => {
487
+ set((state) => ({
488
+ savedFontThemes: state.savedFontThemes.map(
489
+ (ft) => ft.id === id ? { ...ft, ...updates } : ft
490
+ )
491
+ }));
492
+ },
493
+ renameFontTheme: (id, newName) => {
494
+ set((state) => ({
495
+ savedFontThemes: state.savedFontThemes.map(
496
+ (ft) => ft.id === id ? { ...ft, name: newName } : ft
497
+ )
498
+ }));
499
+ },
500
+ deleteFontTheme: (id) => {
501
+ set((state) => ({
502
+ savedFontThemes: state.savedFontThemes.filter((ft) => ft.id !== id)
503
+ }));
504
+ },
505
+ reorderFontThemes: (fontThemes) => {
506
+ set({ savedFontThemes: fontThemes });
507
+ },
508
+ getSavedFontThemes: () => {
509
+ return get().savedFontThemes;
510
+ }
511
+ }),
512
+ {
513
+ name: "ecosystem-customizer",
514
+ version: 4,
515
+ partialize: (state) => ({
516
+ motion: state.motion,
517
+ prefersReducedMotion: state.prefersReducedMotion,
518
+ customizationMode: state.customizationMode,
519
+ customColors: state.customColors,
520
+ savedPalettes: state.savedPalettes,
521
+ customFontThemes: state.customFontThemes,
522
+ savedFontThemes: state.savedFontThemes
523
+ })
524
+ }
525
+ )
526
+ );
527
+
528
+ // src/hooks/useMotionPreference.ts
529
+ function useMotionPreference() {
530
+ const { motion, prefersReducedMotion, setPrefersReducedMotion } = useCustomizer();
531
+ useEffect(() => {
532
+ if (typeof window === "undefined") return;
533
+ const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
534
+ setPrefersReducedMotion(mediaQuery.matches);
535
+ const handleChange = (e) => {
536
+ setPrefersReducedMotion(e.matches);
537
+ };
538
+ mediaQuery.addEventListener("change", handleChange);
539
+ return () => mediaQuery.removeEventListener("change", handleChange);
540
+ }, [setPrefersReducedMotion]);
541
+ return {
542
+ scale: motion,
543
+ shouldAnimate: motion > 0 && !prefersReducedMotion,
544
+ prefersReducedMotion
545
+ };
546
+ }
547
+
548
+ // src/hooks/useForm.ts
549
+ import { useState, useCallback } from "react";
550
+
551
+ // src/lib/validation.ts
552
+ function validateField(value, rules) {
553
+ if (rules.required) {
554
+ const isEmpty = value === void 0 || value === null || value === "" || Array.isArray(value) && value.length === 0;
555
+ if (isEmpty) {
556
+ return typeof rules.required === "string" ? rules.required : "This field is required";
557
+ }
558
+ }
559
+ if (!value && !rules.required) {
560
+ return void 0;
561
+ }
562
+ if (rules.minLength && value.length < rules.minLength.value) {
563
+ return rules.minLength.message;
564
+ }
565
+ if (rules.maxLength && value.length > rules.maxLength.value) {
566
+ return rules.maxLength.message;
567
+ }
568
+ if (rules.pattern && !rules.pattern.value.test(value)) {
569
+ return rules.pattern.message;
570
+ }
571
+ if (rules.custom) {
572
+ for (const rule of rules.custom) {
573
+ if (!rule.validate(value)) {
574
+ return rule.message;
575
+ }
576
+ }
577
+ }
578
+ return void 0;
579
+ }
580
+ function validateForm(values, validations) {
581
+ const errors = {};
582
+ for (const [field, rules] of Object.entries(validations)) {
583
+ const error = validateField(values[field], rules);
584
+ if (error) {
585
+ errors[field] = error;
586
+ }
587
+ }
588
+ return errors;
589
+ }
590
+
591
+ // src/hooks/useForm.ts
592
+ function useForm({
593
+ initialValues,
594
+ validations = {},
595
+ onSubmit,
596
+ validateOn = "onBlur"
597
+ }) {
598
+ const [values, setValues] = useState(initialValues);
599
+ const [errors, setErrors] = useState({});
600
+ const [isSubmitting, setIsSubmitting] = useState(false);
601
+ const [isDirty, setIsDirty] = useState(false);
602
+ const setValue = useCallback((name, value) => {
603
+ setValues((prev) => ({ ...prev, [name]: value }));
604
+ setIsDirty(true);
605
+ }, []);
606
+ const setError = useCallback((name, error) => {
607
+ setErrors((prev) => ({ ...prev, [name]: error }));
608
+ }, []);
609
+ const validateFieldByName = useCallback(
610
+ (name) => {
611
+ const fieldRules = validations[name];
612
+ if (!fieldRules) return;
613
+ const error = validateField(values[name], fieldRules);
614
+ setError(name, error);
615
+ return !error;
616
+ },
617
+ [values, validations, setError]
618
+ );
619
+ const handleChange = useCallback(
620
+ (e) => {
621
+ const { name, value, type } = e.target;
622
+ const fieldValue = type === "checkbox" ? e.target.checked : value;
623
+ setValue(name, fieldValue);
624
+ if (validateOn === "onChange") {
625
+ validateFieldByName(name);
626
+ }
627
+ },
628
+ [setValue, validateOn, validateFieldByName]
629
+ );
630
+ const handleBlur = useCallback(
631
+ (e) => {
632
+ const { name } = e.target;
633
+ if (validateOn === "onBlur") {
634
+ validateFieldByName(name);
635
+ }
636
+ },
637
+ [validateOn, validateFieldByName]
638
+ );
639
+ const validate = useCallback(() => {
640
+ const formErrors = validateForm(values, validations);
641
+ setErrors(formErrors);
642
+ return Object.keys(formErrors).length === 0;
643
+ }, [values, validations]);
644
+ const handleSubmit = useCallback(
645
+ async (e) => {
646
+ e?.preventDefault();
647
+ const isValid = validate();
648
+ if (!isValid) return;
649
+ if (onSubmit) {
650
+ setIsSubmitting(true);
651
+ try {
652
+ await onSubmit(values);
653
+ } finally {
654
+ setIsSubmitting(false);
655
+ }
656
+ }
657
+ },
658
+ [validate, onSubmit, values]
659
+ );
660
+ const reset = useCallback(() => {
661
+ setValues(initialValues);
662
+ setErrors({});
663
+ setIsDirty(false);
664
+ setIsSubmitting(false);
665
+ }, [initialValues]);
666
+ const getFieldProps = useCallback(
667
+ (name) => ({
668
+ name,
669
+ value: values[name] ?? "",
670
+ onChange: handleChange,
671
+ onBlur: handleBlur,
672
+ error: !!errors[name]
673
+ }),
674
+ [values, errors, handleChange, handleBlur]
675
+ );
676
+ return {
677
+ values,
678
+ errors,
679
+ isSubmitting,
680
+ isDirty,
681
+ setValue,
682
+ setError,
683
+ handleChange,
684
+ handleBlur,
685
+ handleSubmit,
686
+ reset,
687
+ validate,
688
+ getFieldProps
689
+ };
690
+ }
691
+ export {
692
+ useForm,
693
+ useMotionPreference,
694
+ useTheme
695
+ };
696
+ //# sourceMappingURL=hooks.mjs.map