@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 +38 -0
- package/dist/chunk-MLKGABMK.js +8 -0
- package/dist/hooks/index.d.ts +34 -0
- package/dist/hooks/index.js +151 -0
- package/dist/index.d.ts +294 -0
- package/dist/index.js +544 -0
- package/package.json +72 -0
- package/styles/breakpoints.css +11 -0
- package/styles/index.css +8 -0
- package/styles/primitives.css +126 -0
- package/styles/theme.css +116 -0
- package/styles/tokens.css +240 -0
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
|
+
}
|
package/styles/index.css
ADDED
|
@@ -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';
|