@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/dist/index.js ADDED
@@ -0,0 +1,544 @@
1
+ "use client";
2
+ import { __export } from './chunk-MLKGABMK.js';
3
+ import React, { useState, useRef, useEffect, useCallback } from 'react';
4
+ import clsx11 from 'clsx';
5
+ import Link from 'next/link';
6
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
7
+ import Image from 'next/image';
8
+
9
+ var baseStyles = "inline-flex items-center justify-center font-medium rounded-full focus:outline-none focus:ring-2 focus:ring-offset-2 transition-colors duration-150 disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer whitespace-nowrap";
10
+ var variantStyles = {
11
+ primary: "bg-brand-primary text-text-on-brand hover:bg-brand-primary-hover focus:ring-ring-focus",
12
+ secondary: "border border-brand-primary text-brand-primary bg-transparent hover:bg-brand-primary-muted focus:ring-ring-focus",
13
+ subtle: "bg-ghost-hover text-text-primary hover:bg-ghost-active focus:ring-ring-focus",
14
+ danger: "bg-danger-bg text-text-on-danger hover:bg-danger-hover focus:ring-ring-focus"
15
+ };
16
+ var sizeStyles = {
17
+ sm: "px-3 py-1.5 text-sm",
18
+ md: "px-4 py-2 text-base",
19
+ lg: "px-6 py-3 text-lg"
20
+ };
21
+ function getButtonClasses({
22
+ variant,
23
+ size,
24
+ fullWidth,
25
+ className
26
+ }) {
27
+ return clsx11(baseStyles, variantStyles[variant], sizeStyles[size], fullWidth && "w-full", className);
28
+ }
29
+ function ButtonContent({
30
+ loading,
31
+ icon,
32
+ children
33
+ }) {
34
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
35
+ children,
36
+ loading ? /* @__PURE__ */ jsx("span", { className: "mr-2 inline-block h-4 w-4 animate-spin rounded-full border-2 border-white border-t-transparent align-middle" }) : icon && /* @__PURE__ */ jsx("span", { className: "ml-2 inline-flex items-center", children: icon })
37
+ ] });
38
+ }
39
+ var Button = React.forwardRef(
40
+ ({
41
+ children,
42
+ variant = "primary",
43
+ size = "md",
44
+ disabled = false,
45
+ loading = false,
46
+ fullWidth = false,
47
+ icon,
48
+ type = "button",
49
+ className,
50
+ onClick,
51
+ ...props
52
+ }, ref) => /* @__PURE__ */ jsx(
53
+ "button",
54
+ {
55
+ ref,
56
+ type,
57
+ disabled: disabled || loading,
58
+ className: getButtonClasses({ variant, size, fullWidth, className }),
59
+ onClick,
60
+ "aria-disabled": disabled || loading,
61
+ ...props,
62
+ children: /* @__PURE__ */ jsx(ButtonContent, { loading, icon, children })
63
+ }
64
+ )
65
+ );
66
+ Button.displayName = "Button";
67
+ var LinkButton = React.forwardRef(
68
+ ({
69
+ children,
70
+ href,
71
+ variant = "primary",
72
+ size = "md",
73
+ disabled = false,
74
+ loading = false,
75
+ fullWidth = false,
76
+ icon,
77
+ className,
78
+ onClick,
79
+ ...props
80
+ }, ref) => /* @__PURE__ */ jsx(
81
+ Link,
82
+ {
83
+ href,
84
+ ref,
85
+ className: getButtonClasses({ variant, size, fullWidth, className }),
86
+ "aria-disabled": disabled || loading,
87
+ tabIndex: disabled ? -1 : 0,
88
+ onClick,
89
+ ...props,
90
+ children: /* @__PURE__ */ jsx(ButtonContent, { loading, icon, children })
91
+ }
92
+ )
93
+ );
94
+ LinkButton.displayName = "LinkButton";
95
+ var Input = React.forwardRef(
96
+ ({ label, error, helperText, fullWidth = false, className, id, type, showPasswordToggle = false, ...props }, ref) => {
97
+ const [showPassword, setShowPassword] = useState(false);
98
+ const inputId = id || label?.toLowerCase().replace(/\s+/g, "-");
99
+ const inputType = showPasswordToggle && type === "password" ? showPassword ? "text" : "password" : type;
100
+ return /* @__PURE__ */ jsxs("div", { className: clsx11("flex flex-col gap-1", fullWidth && "w-full", className), children: [
101
+ label && /* @__PURE__ */ jsx("label", { htmlFor: inputId, className: "block text-sm font-medium text-text-primary", children: label }),
102
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
103
+ /* @__PURE__ */ jsx(
104
+ "input",
105
+ {
106
+ ref,
107
+ id: inputId,
108
+ type: inputType,
109
+ className: clsx11(
110
+ "block w-full rounded-md border border-border-input bg-surface-input px-3 py-2 text-text-primary placeholder:text-text-muted focus:border-border-focus focus:outline-none focus:ring-1 focus:ring-ring-focus",
111
+ error && "border-border-error focus:border-border-error focus:ring-error",
112
+ showPasswordToggle && type === "password" && "pr-10"
113
+ ),
114
+ ...props
115
+ }
116
+ ),
117
+ showPasswordToggle && type === "password" && /* @__PURE__ */ jsx(
118
+ "button",
119
+ {
120
+ type: "button",
121
+ className: "absolute top-0 right-0 bottom-0 flex h-full w-10 shrink-0 items-center justify-center p-0 text-text-muted hover:text-text-secondary",
122
+ onClick: () => setShowPassword(!showPassword),
123
+ tabIndex: -1,
124
+ children: showPassword ? /* @__PURE__ */ jsx("svg", { className: "h-4 w-4 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.878 9.878L3 3m6.878 6.878L21 21" }) }) : /* @__PURE__ */ jsxs("svg", { className: "h-4 w-4 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: [
125
+ /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M15 12a3 3 0 11-6 0 3 3 0 016 0z" }),
126
+ /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" })
127
+ ] })
128
+ }
129
+ )
130
+ ] }),
131
+ error && /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-text-error", children: error }),
132
+ helperText && !error && /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-text-muted", children: helperText })
133
+ ] });
134
+ }
135
+ );
136
+ Input.displayName = "Input";
137
+ var Checkbox = React.forwardRef(
138
+ ({ label, error, className, id, ...props }, ref) => {
139
+ const checkboxId = id || (typeof label === "string" && label ? label.toLowerCase().replace(/\s+/g, "-") : void 0);
140
+ return /* @__PURE__ */ jsxs("div", { className: clsx11("flex flex-col gap-1", className), children: [
141
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start", children: [
142
+ /* @__PURE__ */ jsx("div", { className: "flex h-5 items-center", children: /* @__PURE__ */ jsx(
143
+ "input",
144
+ {
145
+ ref,
146
+ id: checkboxId,
147
+ type: "checkbox",
148
+ className: clsx11(
149
+ "h-4 w-4 rounded border-border-input text-brand-primary focus:ring-ring-focus",
150
+ error && "border-border-error"
151
+ ),
152
+ ...props
153
+ }
154
+ ) }),
155
+ /* @__PURE__ */ jsx("div", { className: "ml-3 text-sm", children: /* @__PURE__ */ jsx(
156
+ "label",
157
+ {
158
+ htmlFor: checkboxId,
159
+ className: clsx11("font-medium text-text-secondary", error && "text-text-error"),
160
+ children: label
161
+ }
162
+ ) })
163
+ ] }),
164
+ error && /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-text-error", children: error })
165
+ ] });
166
+ }
167
+ );
168
+ Checkbox.displayName = "Checkbox";
169
+ var Select = React.forwardRef(
170
+ ({ label, error, helperText, fullWidth = false, className, id, options, placeholder, ...props }, ref) => {
171
+ const selectId = id || label?.toLowerCase().replace(/\s+/g, "-");
172
+ return /* @__PURE__ */ jsxs("div", { className: clsx11("flex flex-col gap-1", fullWidth && "w-full", className), children: [
173
+ label && /* @__PURE__ */ jsx("label", { htmlFor: selectId, className: "block text-sm font-medium text-text-primary", children: label }),
174
+ /* @__PURE__ */ jsxs(
175
+ "select",
176
+ {
177
+ ref,
178
+ id: selectId,
179
+ className: clsx11(
180
+ "block w-full rounded-md border border-border-input bg-surface-input px-3 py-2 text-text-primary focus:border-border-focus focus:outline-none focus:ring-1 focus:ring-ring-focus",
181
+ error && "border-border-error focus:border-border-error focus:ring-error"
182
+ ),
183
+ ...props,
184
+ children: [
185
+ placeholder !== void 0 && /* @__PURE__ */ jsx("option", { value: "", disabled: true, children: placeholder }),
186
+ options.map((option) => /* @__PURE__ */ jsx("option", { value: option.value, children: option.label }, option.value))
187
+ ]
188
+ }
189
+ ),
190
+ error && /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-text-error", children: error }),
191
+ helperText && !error && /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-text-muted", children: helperText })
192
+ ] });
193
+ }
194
+ );
195
+ Select.displayName = "Select";
196
+ var Textarea = React.forwardRef(
197
+ ({ label, error, helperText, fullWidth = false, className, id, ...props }, ref) => {
198
+ const textareaId = id || label?.toLowerCase().replace(/\s+/g, "-");
199
+ return /* @__PURE__ */ jsxs("div", { className: clsx11("flex flex-col gap-1", fullWidth && "w-full", className), children: [
200
+ label && /* @__PURE__ */ jsx("label", { htmlFor: textareaId, className: "block text-sm font-medium text-text-primary", children: label }),
201
+ /* @__PURE__ */ jsx(
202
+ "textarea",
203
+ {
204
+ ref,
205
+ id: textareaId,
206
+ className: clsx11(
207
+ "block w-full rounded-md border border-border-input bg-surface-input px-3 py-2 text-text-primary placeholder:text-text-muted focus:border-border-focus focus:outline-none focus:ring-1 focus:ring-ring-focus",
208
+ error && "border-border-error focus:border-border-error focus:ring-error"
209
+ ),
210
+ ...props
211
+ }
212
+ ),
213
+ error && /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-text-error", children: error }),
214
+ helperText && !error && /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-text-muted", children: helperText })
215
+ ] });
216
+ }
217
+ );
218
+ Textarea.displayName = "Textarea";
219
+ var variantStyles2 = {
220
+ success: "bg-success-muted text-text-success",
221
+ warning: "bg-warning-muted text-text-warning",
222
+ error: "bg-error-muted text-text-error",
223
+ neutral: "bg-surface-hover text-text-secondary",
224
+ info: "bg-info-muted text-text-info"
225
+ };
226
+ var Badge = ({ children, variant = "neutral", className }) => /* @__PURE__ */ jsx(
227
+ "span",
228
+ {
229
+ className: clsx11(
230
+ "inline-flex items-center gap-1.5 rounded-md px-2.5 py-0.5 text-xs font-medium",
231
+ variantStyles2[variant],
232
+ className
233
+ ),
234
+ children
235
+ }
236
+ );
237
+ Badge.displayName = "Badge";
238
+ var sizeStyles2 = {
239
+ sm: "h-8 w-8 text-xs",
240
+ md: "h-10 w-10 text-sm",
241
+ lg: "h-12 w-12 text-base",
242
+ xl: "h-16 w-16 text-lg"
243
+ };
244
+ function getInitials(name) {
245
+ return name.split(" ").map((word) => word[0]).filter(Boolean).slice(0, 2).join("").toUpperCase();
246
+ }
247
+ var Avatar = ({ src, alt = "", name, size = "md", className }) => {
248
+ if (src) {
249
+ const pixelSize = { sm: 32, md: 40, lg: 48, xl: 64 }[size];
250
+ return /* @__PURE__ */ jsx(
251
+ Image,
252
+ {
253
+ src,
254
+ alt: alt || name || "",
255
+ width: pixelSize,
256
+ height: pixelSize,
257
+ className: clsx11("rounded-full object-cover", sizeStyles2[size], className)
258
+ }
259
+ );
260
+ }
261
+ const initials = name ? getInitials(name) : "?";
262
+ return /* @__PURE__ */ jsx(
263
+ "div",
264
+ {
265
+ className: clsx11(
266
+ "flex items-center justify-center rounded-full bg-avatar-bg font-medium text-avatar-text",
267
+ sizeStyles2[size],
268
+ className
269
+ ),
270
+ role: "img",
271
+ "aria-label": alt || name || "avatar",
272
+ children: initials
273
+ }
274
+ );
275
+ };
276
+ Avatar.displayName = "Avatar";
277
+ var Divider = ({ children, className }) => {
278
+ if (!children) {
279
+ return /* @__PURE__ */ jsx("hr", { className: clsx11("border-t border-border-divider", className) });
280
+ }
281
+ return /* @__PURE__ */ jsxs("div", { className: clsx11("relative", className), children: [
282
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center", "aria-hidden": "true", children: /* @__PURE__ */ jsx("div", { className: "w-full border-t border-border-divider" }) }),
283
+ /* @__PURE__ */ jsx("div", { className: "relative flex justify-center", children: /* @__PURE__ */ jsx("span", { className: "bg-surface-card px-4 text-sm text-text-muted", children }) })
284
+ ] });
285
+ };
286
+ Divider.displayName = "Divider";
287
+ var sizeStyles3 = {
288
+ sm: "h-4 w-4 border-2",
289
+ md: "h-6 w-6 border-2",
290
+ lg: "h-8 w-8 border-[3px]"
291
+ };
292
+ var Spinner = ({ size = "md", className, label = "Loading" }) => /* @__PURE__ */ jsx(
293
+ "span",
294
+ {
295
+ className: clsx11(
296
+ "inline-block animate-spin rounded-full border-brand-primary border-t-transparent",
297
+ sizeStyles3[size],
298
+ className
299
+ ),
300
+ role: "status",
301
+ "aria-label": label
302
+ }
303
+ );
304
+ Spinner.displayName = "Spinner";
305
+ var Table = ({ className, children }) => /* @__PURE__ */ jsx("div", { className: clsx11("overflow-x-auto", className), children: /* @__PURE__ */ jsx("table", { className: "min-w-full text-left rounded-lg text-base leading-relaxed overflow-hidden", children }) });
306
+ var TableHead = ({ children, className }) => /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { className: clsx11("font-bold bg-table-header-bg", className), children }) });
307
+ var TableBody = ({ children }) => /* @__PURE__ */ jsx("tbody", { children });
308
+ var TableRow = ({ children, className }) => /* @__PURE__ */ jsx(
309
+ "tr",
310
+ {
311
+ className: clsx11("bg-surface-card border-b border-table-border hover:bg-table-row-hover", className),
312
+ children
313
+ }
314
+ );
315
+ var TableHeaderCell = ({
316
+ children,
317
+ className
318
+ }) => /* @__PURE__ */ jsx("th", { className: clsx11("py-2 px-4 text-table-header-text font-semibold whitespace-nowrap", className), children });
319
+ var TableCell = ({ children, className }) => /* @__PURE__ */ jsx("td", { className: clsx11("px-4 py-2 align-middle text-text-primary", className), children });
320
+ var JsonLd = ({ data }) => /* @__PURE__ */ jsx("script", { type: "application/ld+json", dangerouslySetInnerHTML: { __html: JSON.stringify(data) } });
321
+ JsonLd.displayName = "JsonLd";
322
+
323
+ // src/components/primitives/Text.tsx
324
+ var Text_exports = {};
325
+ __export(Text_exports, {
326
+ Blockquote: () => Blockquote,
327
+ Cite: () => Cite,
328
+ H1: () => H1,
329
+ H2: () => H2,
330
+ H3: () => H3,
331
+ H4: () => H4,
332
+ Paragraph: () => Paragraph,
333
+ TextLink: () => TextLink
334
+ });
335
+ var H1 = ({ children, as: Component = "h1", noMargin = false, className }) => /* @__PURE__ */ jsx(Component, { className: clsx11("text-4xl font-bold leading-tight text-text-primary", !noMargin && "mb-4", className), children });
336
+ var H2 = ({ children, as: Component = "h2", noMargin = false, className }) => /* @__PURE__ */ jsx(Component, { className: clsx11("text-3xl font-bold leading-tight text-text-primary", !noMargin && "mb-2", className), children });
337
+ var H3 = ({ children, as: Component = "h3", noMargin = false, className }) => /* @__PURE__ */ jsx(Component, { className: clsx11("text-2xl font-bold leading-tight text-text-primary", !noMargin && "mb-1", className), children });
338
+ var H4 = ({ children, as: Component = "h4", noMargin = false, className }) => /* @__PURE__ */ jsx(Component, { className: clsx11("text-xl font-bold text-text-primary", !noMargin && "mb-1", className), children });
339
+ var Paragraph = ({
340
+ children,
341
+ noMargin = false,
342
+ className
343
+ }) => /* @__PURE__ */ jsx("p", { className: clsx11("text-base leading-relaxed text-text-secondary", !noMargin && "mb-4", className), children });
344
+ var TextLink = ({ href, children, className, target, rel }) => /* @__PURE__ */ jsx(
345
+ "a",
346
+ {
347
+ href,
348
+ target,
349
+ rel,
350
+ className: clsx11(
351
+ "text-base font-medium text-text-link hover:text-text-link-hover hover:underline transition-colors duration-150 whitespace-nowrap",
352
+ className
353
+ ),
354
+ children
355
+ }
356
+ );
357
+ var Cite = ({ children, className }) => /* @__PURE__ */ jsxs("cite", { className: clsx11("not-italic text-text-primary block", className), children: [
358
+ "\u2014 ",
359
+ children
360
+ ] });
361
+ var Blockquote = ({
362
+ children,
363
+ noMargin = false,
364
+ className
365
+ }) => /* @__PURE__ */ jsx(
366
+ "blockquote",
367
+ {
368
+ className: clsx11("text-lg italic border-l-4 border-border-default pl-4 text-text-secondary", !noMargin && "mb-4", className),
369
+ children: /* @__PURE__ */ jsx("div", { className: "mb-2", children })
370
+ }
371
+ );
372
+ var paddingStyles = {
373
+ none: "",
374
+ sm: "p-4",
375
+ md: "p-6",
376
+ lg: "p-8"
377
+ };
378
+ var Card = ({ children, className, padding = "md" }) => /* @__PURE__ */ jsx(
379
+ "div",
380
+ {
381
+ className: clsx11(
382
+ "rounded-xl border border-border-default bg-surface-card shadow-xs",
383
+ paddingStyles[padding],
384
+ className
385
+ ),
386
+ children
387
+ }
388
+ );
389
+ Card.displayName = "Card";
390
+ var CardHeader = ({ children, className }) => /* @__PURE__ */ jsx("div", { className: clsx11("border-b border-border-divider px-6 py-4", className), children });
391
+ CardHeader.displayName = "CardHeader";
392
+ var CardBody = ({ children, className }) => /* @__PURE__ */ jsx("div", { className: clsx11("px-6 py-4", className), children });
393
+ CardBody.displayName = "CardBody";
394
+ var CardFooter = ({ children, className }) => /* @__PURE__ */ jsx("div", { className: clsx11("border-t border-border-divider px-6 py-4", className), children });
395
+ CardFooter.displayName = "CardFooter";
396
+ var Modal = ({ open, onClose, title, children, className }) => {
397
+ const dialogRef = useRef(null);
398
+ useEffect(() => {
399
+ const dialog = dialogRef.current;
400
+ if (!dialog) return;
401
+ if (open && !dialog.open) {
402
+ dialog.showModal();
403
+ } else if (!open && dialog.open) {
404
+ dialog.close();
405
+ }
406
+ }, [open]);
407
+ const handleBackdropClick = useCallback(
408
+ (e) => {
409
+ if (e.target === dialogRef.current) {
410
+ onClose();
411
+ }
412
+ },
413
+ [onClose]
414
+ );
415
+ return /* @__PURE__ */ jsx(
416
+ "dialog",
417
+ {
418
+ ref: dialogRef,
419
+ onClose,
420
+ onClick: handleBackdropClick,
421
+ "aria-labelledby": "modal-title",
422
+ className: "backdrop:bg-surface-overlay bg-transparent p-0 m-auto max-w-md w-full open:flex open:items-center open:justify-center",
423
+ children: /* @__PURE__ */ jsxs("div", { className: clsx11("bg-surface-card rounded-xl shadow-xl p-6 w-full", className), children: [
424
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-4", children: [
425
+ /* @__PURE__ */ jsx("h2", { id: "modal-title", className: "text-lg font-semibold text-text-primary", children: title }),
426
+ /* @__PURE__ */ jsx(
427
+ "button",
428
+ {
429
+ onClick: onClose,
430
+ "aria-label": "Close",
431
+ className: "rounded-full p-1.5 text-text-muted hover:text-text-secondary hover:bg-ghost-hover transition-colors",
432
+ children: /* @__PURE__ */ jsx("svg", { className: "h-4 w-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) })
433
+ }
434
+ )
435
+ ] }),
436
+ /* @__PURE__ */ jsx("div", { className: "text-sm text-text-secondary leading-relaxed", children })
437
+ ] })
438
+ }
439
+ );
440
+ };
441
+ Modal.displayName = "Modal";
442
+ var variantStyles3 = {
443
+ success: "border-border-success bg-success-muted text-text-success",
444
+ error: "border-border-error bg-error-muted text-text-error",
445
+ warning: "border-l-warning bg-warning-muted text-text-warning",
446
+ info: "border-l-info bg-info-muted text-text-info"
447
+ };
448
+ var iconByVariant = {
449
+ success: "M5 13l4 4L19 7",
450
+ error: "M6 18L18 6M6 6l12 12",
451
+ warning: "M12 9v2m0 4h.01M12 3l9.66 16.5H2.34L12 3z",
452
+ info: "M13 16h-1v-4h-1m1-4h.01M12 2a10 10 0 100 20 10 10 0 000-20z"
453
+ };
454
+ var Toast = ({
455
+ message,
456
+ variant = "info",
457
+ duration = 5e3,
458
+ onDismiss,
459
+ className
460
+ }) => {
461
+ const [visible, setVisible] = useState(true);
462
+ const dismiss = useCallback(() => {
463
+ setVisible(false);
464
+ onDismiss?.();
465
+ }, [onDismiss]);
466
+ useEffect(() => {
467
+ if (duration <= 0) return;
468
+ const timer = setTimeout(dismiss, duration);
469
+ return () => clearTimeout(timer);
470
+ }, [duration, dismiss]);
471
+ if (!visible) return null;
472
+ return /* @__PURE__ */ jsxs(
473
+ "div",
474
+ {
475
+ role: "alert",
476
+ className: clsx11(
477
+ "flex items-center gap-3 rounded-lg border-l-4 px-4 py-3 shadow-md",
478
+ variantStyles3[variant],
479
+ className
480
+ ),
481
+ children: [
482
+ /* @__PURE__ */ jsx("svg", { className: "h-5 w-5 shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: iconByVariant[variant] }) }),
483
+ /* @__PURE__ */ jsx("p", { className: "flex-1 text-sm font-medium", children: message }),
484
+ /* @__PURE__ */ jsx("button", { onClick: dismiss, className: "shrink-0 rounded p-1 hover:opacity-70 transition-opacity", "aria-label": "Dismiss", children: /* @__PURE__ */ jsx("svg", { className: "h-4 w-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 18L18 6M6 6l12 12" }) }) })
485
+ ]
486
+ }
487
+ );
488
+ };
489
+ Toast.displayName = "Toast";
490
+ var strengthColors = {
491
+ Strong: "text-text-success",
492
+ Medium: "text-text-warning",
493
+ Weak: "text-text-error"
494
+ };
495
+ var PasswordStrengthIndicator = ({
496
+ strength,
497
+ requirements,
498
+ className
499
+ }) => /* @__PURE__ */ jsxs("div", { className: clsx11("space-y-2", className), children: [
500
+ /* @__PURE__ */ jsxs("div", { className: "text-xs text-text-secondary", children: [
501
+ "Password strength: ",
502
+ /* @__PURE__ */ jsx("span", { className: strengthColors[strength], children: strength })
503
+ ] }),
504
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-2 text-xs", children: [
505
+ [requirements.length, "8-100 characters"],
506
+ [requirements.lowercase, "Lowercase letter"],
507
+ [requirements.uppercase, "Uppercase letter"],
508
+ [requirements.number, "Number"],
509
+ [requirements.special, "Special character"],
510
+ [requirements.noPassword, 'No "password"'],
511
+ [requirements.noSequential, 'No "123"']
512
+ ].map(([met, label]) => /* @__PURE__ */ jsxs("div", { className: clsx11(met ? "text-text-success" : "text-text-muted"), children: [
513
+ "\u2713 ",
514
+ label
515
+ ] }, label)) })
516
+ ] });
517
+ PasswordStrengthIndicator.displayName = "PasswordStrengthIndicator";
518
+ var EmptyState = ({ icon, title, description, action, className }) => /* @__PURE__ */ jsxs("div", { className: clsx11("flex flex-col items-center justify-center py-12 px-4 text-center", className), children: [
519
+ icon && /* @__PURE__ */ jsx("div", { className: "mb-4 text-text-muted", children: icon }),
520
+ /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-text-primary mb-1", children: title }),
521
+ description && /* @__PURE__ */ jsx("p", { className: "text-sm text-text-secondary mb-6 max-w-sm", children: description }),
522
+ action && /* @__PURE__ */ jsx("div", { children: action })
523
+ ] });
524
+ EmptyState.displayName = "EmptyState";
525
+ var FormField = ({
526
+ label,
527
+ error,
528
+ helperText,
529
+ required,
530
+ children,
531
+ className,
532
+ htmlFor
533
+ }) => /* @__PURE__ */ jsxs("div", { className: clsx11("flex flex-col gap-1", className), children: [
534
+ label && /* @__PURE__ */ jsxs("label", { htmlFor, className: "block text-sm font-medium text-text-primary", children: [
535
+ label,
536
+ required && /* @__PURE__ */ jsx("span", { className: "text-text-error ml-0.5", children: "*" })
537
+ ] }),
538
+ children,
539
+ error && /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-text-error", children: error }),
540
+ helperText && !error && /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-text-muted", children: helperText })
541
+ ] });
542
+ FormField.displayName = "FormField";
543
+
544
+ export { Avatar, Badge, Button, Card, CardBody, CardFooter, CardHeader, Checkbox, Divider, EmptyState, FormField, Input, JsonLd, LinkButton, Modal, PasswordStrengthIndicator, Select, Spinner, Table, TableBody, TableCell, TableHead, TableHeaderCell, TableRow, Text_exports as Text, Textarea, Toast };
package/package.json ADDED
@@ -0,0 +1,72 @@
1
+ {
2
+ "name": "@mars-stack/ui",
3
+ "version": "1.0.0",
4
+ "license": "MIT",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/greaveselliott/mars.git"
8
+ },
9
+ "homepage": "https://github.com/greaveselliott/mars#readme",
10
+ "keywords": [
11
+ "saas",
12
+ "nextjs",
13
+ "prisma",
14
+ "auth",
15
+ "fullstack",
16
+ "ui"
17
+ ],
18
+ "type": "module",
19
+ "exports": {
20
+ ".": {
21
+ "types": "./dist/index.d.ts",
22
+ "import": "./dist/index.js"
23
+ },
24
+ "./hooks": {
25
+ "types": "./dist/hooks/index.d.ts",
26
+ "import": "./dist/hooks/index.js"
27
+ },
28
+ "./styles/*": "./styles/*"
29
+ },
30
+ "sideEffects": false,
31
+ "publishConfig": {
32
+ "access": "restricted"
33
+ },
34
+ "files": [
35
+ "dist",
36
+ "styles"
37
+ ],
38
+ "scripts": {
39
+ "build": "tsup",
40
+ "dev": "tsup --watch",
41
+ "test": "vitest run",
42
+ "test:watch": "vitest",
43
+ "prepublishOnly": "yarn build"
44
+ },
45
+ "peerDependencies": {
46
+ "@mars-stack/core": ">=0.4.0",
47
+ "clsx": ">=2",
48
+ "next": ">=14",
49
+ "react": ">=18"
50
+ },
51
+ "peerDependenciesMeta": {
52
+ "@mars-stack/core": {
53
+ "optional": true
54
+ }
55
+ },
56
+ "devDependencies": {
57
+ "@testing-library/dom": "^10.4.1",
58
+ "@testing-library/jest-dom": "^6.9.1",
59
+ "@testing-library/react": "^16.3.2",
60
+ "@types/react": "^19.0.0",
61
+ "@types/react-dom": "^19.2.3",
62
+ "clsx": "^2.1.1",
63
+ "jsdom": "^28.1.0",
64
+ "next": "^16.0.0",
65
+ "react": "^19.0.0",
66
+ "react-dom": "^19.2.4",
67
+ "tsup": "^8.0.0",
68
+ "typescript": "^5.7.0",
69
+ "vitest": "^4.0.18",
70
+ "zod": "^4.3.6"
71
+ }
72
+ }
@@ -0,0 +1,11 @@
1
+ /* MARS Design System: Responsive Breakpoints
2
+ * Mobile-first breakpoint system.
3
+ * Use min-width media queries: sm, md, lg, xl, 2xl. */
4
+
5
+ :root {
6
+ --breakpoint-sm: 640px;
7
+ --breakpoint-md: 768px;
8
+ --breakpoint-lg: 1024px;
9
+ --breakpoint-xl: 1280px;
10
+ --breakpoint-2xl: 1536px;
11
+ }
@@ -0,0 +1,8 @@
1
+ /* MARS Design System: Single entry point
2
+ * Import this in your app's globals.css.
3
+ * Override --brand-* in a separate brand.css for project-specific colours. */
4
+
5
+ @import './primitives.css';
6
+ @import './tokens.css';
7
+ @import './theme.css';
8
+ @import './breakpoints.css';