@mars-stack/ui 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.
package/README.md ADDED
@@ -0,0 +1,38 @@
1
+ # @mars-stack/ui
2
+
3
+ Design-token-driven UI components and styles for MARS apps.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ yarn add @mars-stack/ui
9
+ ```
10
+
11
+ ## Components
12
+
13
+ **Primitives:** Button, Input, Checkbox, Select, Textarea, Badge, Avatar, Spinner, Divider, Table, Text, JsonLd
14
+
15
+ **Patterns:** Card, Modal, Toast, FormField, EmptyState, PasswordStrengthIndicator
16
+
17
+ ## Styles
18
+
19
+ Import the full design system in your `globals.css`:
20
+
21
+ ```css
22
+ @import 'tailwindcss';
23
+ @import '@mars-stack/ui/styles/index.css';
24
+ @import './brand.css';
25
+ ```
26
+
27
+ Override `--brand-50` through `--brand-950` in `brand.css` to customise your brand colour.
28
+
29
+ ## Usage
30
+
31
+ ```tsx
32
+ import { Button, Card, Input, Text } from '@mars-stack/ui';
33
+ import { useZodForm } from '@mars-stack/ui/hooks';
34
+ ```
35
+
36
+ ## License
37
+
38
+ MIT
@@ -0,0 +1,8 @@
1
+ "use client";
2
+ var __defProp = Object.defineProperty;
3
+ var __export = (target, all) => {
4
+ for (var name in all)
5
+ __defProp(target, name, { get: all[name], enumerable: true });
6
+ };
7
+
8
+ export { __export };
@@ -0,0 +1,34 @@
1
+ import { z } from 'zod';
2
+
3
+ interface UseZodFormOptions<T extends z.ZodSchema<Record<string, unknown>>> {
4
+ schema: T;
5
+ initialValues?: Partial<z.infer<T>>;
6
+ mode?: 'onChange' | 'onBlur' | 'onSubmit';
7
+ }
8
+ interface UseZodFormResult<T extends z.ZodSchema<Record<string, unknown>>> {
9
+ values: z.infer<T>;
10
+ errors: Record<string, string>;
11
+ touched: Record<string, boolean>;
12
+ isValid: boolean;
13
+ isSubmitting: boolean;
14
+ setValue: (field: keyof z.infer<T>, value: string | boolean) => void;
15
+ setError: (field: keyof z.infer<T>, error: string) => void;
16
+ setTouched: (field: keyof z.infer<T>, touched?: boolean) => void;
17
+ setSubmitting: (submitting: boolean) => void;
18
+ validateField: (field: keyof z.infer<T>) => boolean;
19
+ validateForm: () => boolean;
20
+ handleChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
21
+ handleBlur: (e: React.FocusEvent<HTMLInputElement>) => void;
22
+ handleSubmit: (onSubmit: (values: z.infer<T>) => void | Promise<void>) => (e: React.FormEvent) => Promise<void>;
23
+ reset: (values?: Partial<z.infer<T>>) => void;
24
+ getFieldProps: (field: keyof z.infer<T>) => {
25
+ name: string;
26
+ value: string;
27
+ onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
28
+ onBlur: (e: React.FocusEvent<HTMLInputElement>) => void;
29
+ error?: string;
30
+ };
31
+ }
32
+ declare function useZodForm<T extends z.ZodSchema<Record<string, unknown>>>({ schema, initialValues, mode, }: UseZodFormOptions<T>): UseZodFormResult<T>;
33
+
34
+ export { type UseZodFormOptions, type UseZodFormResult, useZodForm };
@@ -0,0 +1,151 @@
1
+ "use client";
2
+ import '../chunk-MLKGABMK.js';
3
+ import { useState, useRef, useCallback } from 'react';
4
+
5
+ function useZodForm({
6
+ schema,
7
+ initialValues = {},
8
+ mode = "onChange"
9
+ }) {
10
+ const [values, setValues] = useState(() => ({ ...initialValues }));
11
+ const valuesRef = useRef(values);
12
+ valuesRef.current = values;
13
+ const [errors, setErrors] = useState({});
14
+ const [touched, setTouchedState] = useState({});
15
+ const [isSubmitting, setIsSubmitting] = useState(false);
16
+ const validateWithValues = useCallback(
17
+ (valuesToValidate) => {
18
+ const result = schema.safeParse(valuesToValidate);
19
+ if (result.success) return {};
20
+ const newErrors = {};
21
+ result.error.issues.forEach((issue) => {
22
+ const path = issue.path.join(".");
23
+ newErrors[path] = issue.message;
24
+ });
25
+ return newErrors;
26
+ },
27
+ [schema]
28
+ );
29
+ const validateForm = useCallback(() => {
30
+ const newErrors = validateWithValues(valuesRef.current);
31
+ setErrors(newErrors);
32
+ return Object.keys(newErrors).length === 0;
33
+ }, [validateWithValues]);
34
+ const validateField = useCallback(
35
+ (field) => {
36
+ const newErrors = validateWithValues(valuesRef.current);
37
+ setErrors(newErrors);
38
+ return !newErrors[field];
39
+ },
40
+ [validateWithValues]
41
+ );
42
+ const setValue = useCallback(
43
+ (field, value) => {
44
+ const fieldName = field;
45
+ const newValues = { ...valuesRef.current, [field]: value };
46
+ setValues(newValues);
47
+ valuesRef.current = newValues;
48
+ const shouldRevalidateOnChange = mode !== "onSubmit" && (touched[fieldName] || Boolean(errors[fieldName]));
49
+ if (shouldRevalidateOnChange) {
50
+ const newErrors = validateWithValues(newValues);
51
+ setErrors(newErrors);
52
+ }
53
+ },
54
+ [errors, mode, touched, validateWithValues]
55
+ );
56
+ const setError = useCallback((field, error) => {
57
+ setErrors((prev) => ({ ...prev, [field]: error }));
58
+ }, []);
59
+ const setTouched = useCallback((field, isTouched = true) => {
60
+ setTouchedState((prev) => ({ ...prev, [field]: isTouched }));
61
+ }, []);
62
+ const setSubmitting = useCallback((submitting) => {
63
+ setIsSubmitting(submitting);
64
+ }, []);
65
+ const handleChange = useCallback(
66
+ (e) => {
67
+ const { name, value, type, checked } = e.target;
68
+ const fieldValue = type === "checkbox" ? checked : value;
69
+ setValue(name, fieldValue);
70
+ },
71
+ [setValue]
72
+ );
73
+ const handleBlur = useCallback(
74
+ (e) => {
75
+ const { name } = e.target;
76
+ setTouched(name, true);
77
+ if (mode === "onBlur") validateField(name);
78
+ },
79
+ [mode, setTouched, validateField]
80
+ );
81
+ const handleSubmit = useCallback(
82
+ (onSubmit) => {
83
+ return async (e) => {
84
+ e.preventDefault();
85
+ const currentValues = valuesRef.current;
86
+ const allTouched = {};
87
+ Object.keys(currentValues).forEach((key) => {
88
+ allTouched[key] = true;
89
+ });
90
+ setTouchedState(allTouched);
91
+ if (!validateForm()) return;
92
+ setIsSubmitting(true);
93
+ try {
94
+ await onSubmit(currentValues);
95
+ } catch (error) {
96
+ console.error("Form submission error:", error);
97
+ } finally {
98
+ setIsSubmitting(false);
99
+ }
100
+ };
101
+ },
102
+ [validateForm]
103
+ );
104
+ const reset = useCallback(
105
+ (newValues) => {
106
+ if (newValues) {
107
+ setValues((prev) => ({ ...prev, ...newValues }));
108
+ } else {
109
+ setValues(initialValues);
110
+ }
111
+ setErrors({});
112
+ setTouchedState({});
113
+ setIsSubmitting(false);
114
+ },
115
+ [initialValues]
116
+ );
117
+ const getFieldProps = useCallback(
118
+ (field) => {
119
+ const fieldName = field;
120
+ return {
121
+ name: fieldName,
122
+ value: String(values[field] || ""),
123
+ onChange: handleChange,
124
+ onBlur: handleBlur,
125
+ error: touched[fieldName] ? errors[fieldName] : void 0
126
+ };
127
+ },
128
+ [values, handleChange, handleBlur, touched, errors]
129
+ );
130
+ const isValid = Object.keys(errors).length === 0;
131
+ return {
132
+ values,
133
+ errors,
134
+ touched,
135
+ isValid,
136
+ isSubmitting,
137
+ setValue,
138
+ setError,
139
+ setTouched,
140
+ setSubmitting,
141
+ validateField,
142
+ validateForm,
143
+ handleChange,
144
+ handleBlur,
145
+ handleSubmit,
146
+ reset,
147
+ getFieldProps
148
+ };
149
+ }
150
+
151
+ export { useZodForm };
@@ -0,0 +1,294 @@
1
+ import React from 'react';
2
+ import * as react_jsx_runtime from 'react/jsx-runtime';
3
+
4
+ type ButtonVariant = 'primary' | 'secondary' | 'subtle' | 'danger';
5
+ type ButtonSize = 'sm' | 'md' | 'lg';
6
+ type ButtonProps = {
7
+ children: React.ReactNode;
8
+ variant?: ButtonVariant;
9
+ size?: ButtonSize;
10
+ disabled?: boolean;
11
+ loading?: boolean;
12
+ fullWidth?: boolean;
13
+ icon?: React.ReactNode;
14
+ type?: 'button' | 'submit' | 'reset';
15
+ className?: string;
16
+ onClick?: React.MouseEventHandler<HTMLButtonElement>;
17
+ } & Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'type' | 'onClick'>;
18
+ declare const Button: React.ForwardRefExoticComponent<{
19
+ children: React.ReactNode;
20
+ variant?: ButtonVariant;
21
+ size?: ButtonSize;
22
+ disabled?: boolean;
23
+ loading?: boolean;
24
+ fullWidth?: boolean;
25
+ icon?: React.ReactNode;
26
+ type?: "button" | "submit" | "reset";
27
+ className?: string;
28
+ onClick?: React.MouseEventHandler<HTMLButtonElement>;
29
+ } & Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "type" | "onClick"> & React.RefAttributes<HTMLButtonElement>>;
30
+ declare const LinkButton: React.ForwardRefExoticComponent<{
31
+ children: React.ReactNode;
32
+ href: string;
33
+ variant?: ButtonVariant;
34
+ size?: ButtonSize;
35
+ disabled?: boolean;
36
+ loading?: boolean;
37
+ fullWidth?: boolean;
38
+ icon?: React.ReactNode;
39
+ className?: string;
40
+ onClick?: React.MouseEventHandler<HTMLAnchorElement>;
41
+ } & Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, "href"> & React.RefAttributes<HTMLAnchorElement>>;
42
+
43
+ type InputProps = {
44
+ label?: string;
45
+ error?: string;
46
+ helperText?: string;
47
+ fullWidth?: boolean;
48
+ className?: string;
49
+ showPasswordToggle?: boolean;
50
+ } & React.InputHTMLAttributes<HTMLInputElement>;
51
+ declare const Input: React.ForwardRefExoticComponent<{
52
+ label?: string;
53
+ error?: string;
54
+ helperText?: string;
55
+ fullWidth?: boolean;
56
+ className?: string;
57
+ showPasswordToggle?: boolean;
58
+ } & React.InputHTMLAttributes<HTMLInputElement> & React.RefAttributes<HTMLInputElement>>;
59
+
60
+ type CheckboxProps = {
61
+ label: React.ReactNode;
62
+ error?: string;
63
+ className?: string;
64
+ } & Omit<React.InputHTMLAttributes<HTMLInputElement>, 'type'>;
65
+ declare const Checkbox: React.ForwardRefExoticComponent<{
66
+ label: React.ReactNode;
67
+ error?: string;
68
+ className?: string;
69
+ } & Omit<React.InputHTMLAttributes<HTMLInputElement>, "type"> & React.RefAttributes<HTMLInputElement>>;
70
+
71
+ type SelectOption = {
72
+ value: string;
73
+ label: string;
74
+ };
75
+ type SelectProps = {
76
+ label?: string;
77
+ error?: string;
78
+ helperText?: string;
79
+ fullWidth?: boolean;
80
+ className?: string;
81
+ options: Array<SelectOption>;
82
+ placeholder?: string;
83
+ } & Omit<React.SelectHTMLAttributes<HTMLSelectElement>, 'children'>;
84
+ declare const Select: React.ForwardRefExoticComponent<{
85
+ label?: string;
86
+ error?: string;
87
+ helperText?: string;
88
+ fullWidth?: boolean;
89
+ className?: string;
90
+ options: Array<SelectOption>;
91
+ placeholder?: string;
92
+ } & Omit<React.SelectHTMLAttributes<HTMLSelectElement>, "children"> & React.RefAttributes<HTMLSelectElement>>;
93
+
94
+ type TextareaProps = {
95
+ label?: string;
96
+ error?: string;
97
+ helperText?: string;
98
+ fullWidth?: boolean;
99
+ className?: string;
100
+ } & React.TextareaHTMLAttributes<HTMLTextAreaElement>;
101
+ declare const Textarea: React.ForwardRefExoticComponent<{
102
+ label?: string;
103
+ error?: string;
104
+ helperText?: string;
105
+ fullWidth?: boolean;
106
+ className?: string;
107
+ } & React.TextareaHTMLAttributes<HTMLTextAreaElement> & React.RefAttributes<HTMLTextAreaElement>>;
108
+
109
+ type BadgeVariant = 'success' | 'warning' | 'error' | 'neutral' | 'info';
110
+ type BadgeProps = {
111
+ children: React.ReactNode;
112
+ variant?: BadgeVariant;
113
+ className?: string;
114
+ };
115
+ declare const Badge: React.FC<BadgeProps>;
116
+
117
+ type AvatarSize = 'sm' | 'md' | 'lg' | 'xl';
118
+ type AvatarProps = {
119
+ src?: string | null;
120
+ alt?: string;
121
+ name?: string;
122
+ size?: AvatarSize;
123
+ className?: string;
124
+ };
125
+ declare const Avatar: React.FC<AvatarProps>;
126
+
127
+ type DividerProps = {
128
+ children?: React.ReactNode;
129
+ className?: string;
130
+ };
131
+ declare const Divider: React.FC<DividerProps>;
132
+
133
+ type SpinnerSize = 'sm' | 'md' | 'lg';
134
+ type SpinnerProps = {
135
+ size?: SpinnerSize;
136
+ className?: string;
137
+ label?: string;
138
+ };
139
+ declare const Spinner: React.FC<SpinnerProps>;
140
+
141
+ type TableProps = {
142
+ className?: string;
143
+ children: React.ReactNode;
144
+ };
145
+ declare const Table: React.FC<TableProps>;
146
+ declare const TableHead: React.FC<{
147
+ children: React.ReactNode;
148
+ className?: string;
149
+ }>;
150
+ declare const TableBody: React.FC<{
151
+ children: React.ReactNode;
152
+ }>;
153
+ declare const TableRow: React.FC<{
154
+ children: React.ReactNode;
155
+ className?: string;
156
+ }>;
157
+ declare const TableHeaderCell: React.FC<{
158
+ children: React.ReactNode;
159
+ className?: string;
160
+ }>;
161
+ declare const TableCell: React.FC<{
162
+ children?: React.ReactNode;
163
+ className?: string;
164
+ }>;
165
+
166
+ type JsonLdProps = {
167
+ data: Record<string, unknown>;
168
+ };
169
+ declare const JsonLd: React.FC<JsonLdProps>;
170
+
171
+ type HeadingProps = {
172
+ children: React.ReactNode;
173
+ as?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' | 'div';
174
+ noMargin?: boolean;
175
+ className?: string;
176
+ };
177
+ declare const H1: ({ children, as: Component, noMargin, className }: HeadingProps) => react_jsx_runtime.JSX.Element;
178
+ declare const H2: ({ children, as: Component, noMargin, className }: HeadingProps) => react_jsx_runtime.JSX.Element;
179
+ declare const H3: ({ children, as: Component, noMargin, className }: HeadingProps) => react_jsx_runtime.JSX.Element;
180
+ declare const H4: ({ children, as: Component, noMargin, className }: HeadingProps) => react_jsx_runtime.JSX.Element;
181
+ declare const Paragraph: ({ children, noMargin, className, }: {
182
+ children: React.ReactNode;
183
+ noMargin?: boolean;
184
+ className?: string;
185
+ }) => react_jsx_runtime.JSX.Element;
186
+ interface TextLinkProps {
187
+ href: string;
188
+ children: React.ReactNode;
189
+ className?: string;
190
+ target?: string;
191
+ rel?: string;
192
+ }
193
+ declare const TextLink: ({ href, children, className, target, rel }: TextLinkProps) => react_jsx_runtime.JSX.Element;
194
+ declare const Cite: ({ children, className }: {
195
+ children: React.ReactNode;
196
+ className?: string;
197
+ }) => react_jsx_runtime.JSX.Element;
198
+ declare const Blockquote: ({ children, noMargin, className, }: {
199
+ children: React.ReactNode;
200
+ noMargin?: boolean;
201
+ className?: string;
202
+ }) => react_jsx_runtime.JSX.Element;
203
+
204
+ declare const Text_Blockquote: typeof Blockquote;
205
+ declare const Text_Cite: typeof Cite;
206
+ declare const Text_H1: typeof H1;
207
+ declare const Text_H2: typeof H2;
208
+ declare const Text_H3: typeof H3;
209
+ declare const Text_H4: typeof H4;
210
+ declare const Text_Paragraph: typeof Paragraph;
211
+ declare const Text_TextLink: typeof TextLink;
212
+ declare namespace Text {
213
+ export { Text_Blockquote as Blockquote, Text_Cite as Cite, Text_H1 as H1, Text_H2 as H2, Text_H3 as H3, Text_H4 as H4, Text_Paragraph as Paragraph, Text_TextLink as TextLink };
214
+ }
215
+
216
+ type CardProps = {
217
+ children: React.ReactNode;
218
+ className?: string;
219
+ padding?: 'none' | 'sm' | 'md' | 'lg';
220
+ };
221
+ declare const Card: React.FC<CardProps>;
222
+ type CardHeaderProps = {
223
+ children: React.ReactNode;
224
+ className?: string;
225
+ };
226
+ declare const CardHeader: React.FC<CardHeaderProps>;
227
+ type CardBodyProps = {
228
+ children: React.ReactNode;
229
+ className?: string;
230
+ };
231
+ declare const CardBody: React.FC<CardBodyProps>;
232
+ type CardFooterProps = {
233
+ children: React.ReactNode;
234
+ className?: string;
235
+ };
236
+ declare const CardFooter: React.FC<CardFooterProps>;
237
+
238
+ type ModalProps = {
239
+ open: boolean;
240
+ onClose: () => void;
241
+ title: string;
242
+ children: React.ReactNode;
243
+ className?: string;
244
+ };
245
+ declare const Modal: React.FC<ModalProps>;
246
+
247
+ type ToastVariant = 'success' | 'error' | 'warning' | 'info';
248
+ type ToastProps = {
249
+ message: string;
250
+ variant?: ToastVariant;
251
+ duration?: number;
252
+ onDismiss?: () => void;
253
+ className?: string;
254
+ };
255
+ declare const Toast: React.FC<ToastProps>;
256
+
257
+ type PasswordRequirements = {
258
+ length: boolean;
259
+ lowercase: boolean;
260
+ uppercase: boolean;
261
+ number: boolean;
262
+ special: boolean;
263
+ noPassword: boolean;
264
+ noSequential: boolean;
265
+ };
266
+ type PasswordStrength = 'Weak' | 'Medium' | 'Strong';
267
+ type PasswordStrengthIndicatorProps = {
268
+ strength: PasswordStrength;
269
+ requirements: PasswordRequirements;
270
+ className?: string;
271
+ };
272
+ declare const PasswordStrengthIndicator: React.FC<PasswordStrengthIndicatorProps>;
273
+
274
+ type EmptyStateProps = {
275
+ icon?: React.ReactNode;
276
+ title: string;
277
+ description?: string;
278
+ action?: React.ReactNode;
279
+ className?: string;
280
+ };
281
+ declare const EmptyState: React.FC<EmptyStateProps>;
282
+
283
+ type FormFieldProps = {
284
+ label?: string;
285
+ error?: string;
286
+ helperText?: string;
287
+ required?: boolean;
288
+ children: React.ReactNode;
289
+ className?: string;
290
+ htmlFor?: string;
291
+ };
292
+ declare const FormField: React.FC<FormFieldProps>;
293
+
294
+ export { Avatar, type AvatarProps, type AvatarSize, Badge, type BadgeProps, type BadgeVariant, Button, type ButtonProps, type ButtonSize, type ButtonVariant, Card, CardBody, type CardBodyProps, CardFooter, type CardFooterProps, CardHeader, type CardHeaderProps, type CardProps, Checkbox, type CheckboxProps, Divider, type DividerProps, EmptyState, type EmptyStateProps, FormField, type FormFieldProps, Input, type InputProps, JsonLd, type JsonLdProps, LinkButton, Modal, type ModalProps, type PasswordRequirements, type PasswordStrength, PasswordStrengthIndicator, type PasswordStrengthIndicatorProps, Select, type SelectOption, type SelectProps, Spinner, type SpinnerProps, type SpinnerSize, Table, TableBody, TableCell, TableHead, TableHeaderCell, type TableProps, TableRow, Text, Textarea, type TextareaProps, Toast, type ToastProps, type ToastVariant };