@brightweblabs/ui 1.5.2 → 1.5.4
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/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brightweblabs/ui",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "1.5.
|
|
4
|
+
"version": "1.5.4",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
7
7
|
"files": [
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"./card": "./src/components/card.tsx",
|
|
31
31
|
"./chart": "./src/components/chart.tsx",
|
|
32
32
|
"./checkbox": "./src/components/checkbox.tsx",
|
|
33
|
+
"./cover-header": "./src/components/cover-header.tsx",
|
|
33
34
|
"./dropdown-menu": "./src/components/dropdown-menu.tsx",
|
|
34
35
|
"./email/invitation": "./src/email/invitation.ts",
|
|
35
36
|
"./empty-state": "./src/components/empty-state.tsx",
|
|
@@ -79,7 +80,7 @@
|
|
|
79
80
|
"recharts": "^2.15.4",
|
|
80
81
|
"sonner": "^2.0.7",
|
|
81
82
|
"tailwind-merge": "^3.6.0",
|
|
82
|
-
"@brightweblabs/theme": "0.8.
|
|
83
|
+
"@brightweblabs/theme": "0.8.3"
|
|
83
84
|
},
|
|
84
85
|
"peerDependencies": {
|
|
85
86
|
"lucide-react": "^1.8.0",
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { HTMLAttributes, ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
import { cn } from "../lib/utils";
|
|
4
|
+
|
|
5
|
+
export type CoverHeaderProps = HTMLAttributes<HTMLElement> & {
|
|
6
|
+
children: ReactNode;
|
|
7
|
+
coverClassName?: string;
|
|
8
|
+
contentClassName?: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export function CoverHeader({
|
|
12
|
+
children,
|
|
13
|
+
className,
|
|
14
|
+
coverClassName,
|
|
15
|
+
contentClassName,
|
|
16
|
+
...props
|
|
17
|
+
}: CoverHeaderProps) {
|
|
18
|
+
return (
|
|
19
|
+
<header
|
|
20
|
+
className={cn(
|
|
21
|
+
"overflow-hidden rounded-[var(--radius-panel)] border border-border/60 bg-card shadow-[var(--cover-header-shadow)]",
|
|
22
|
+
className,
|
|
23
|
+
)}
|
|
24
|
+
{...props}
|
|
25
|
+
>
|
|
26
|
+
<div
|
|
27
|
+
aria-hidden
|
|
28
|
+
className={cn("relative h-24 overflow-hidden sm:h-32", coverClassName)}
|
|
29
|
+
style={{ background: "var(--cover-header-surface)" }}
|
|
30
|
+
>
|
|
31
|
+
<span className="absolute -right-16 -top-24 size-72 rounded-full" style={{ background: "var(--cover-header-glow-strong)" }} />
|
|
32
|
+
<span className="absolute -bottom-24 left-[12%] size-56 rounded-full" style={{ background: "var(--cover-header-glow-soft)" }} />
|
|
33
|
+
</div>
|
|
34
|
+
<div className={cn("relative px-5 pb-6 sm:px-8 sm:pb-8", contentClassName)}>
|
|
35
|
+
{children}
|
|
36
|
+
</div>
|
|
37
|
+
</header>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
@@ -4,7 +4,7 @@ import { getInitials } from "../lib/patterns";
|
|
|
4
4
|
import { cn } from "../lib/utils";
|
|
5
5
|
import { Avatar, AvatarFallback } from "./avatar";
|
|
6
6
|
|
|
7
|
-
export type InitialsAvatarTone = "active" | "subtle";
|
|
7
|
+
export type InitialsAvatarTone = "active" | "subtle" | "client" | "team" | "inverse";
|
|
8
8
|
|
|
9
9
|
export type InitialsAvatarProps = Omit<ComponentProps<typeof Avatar>, "children"> & {
|
|
10
10
|
label?: string | null;
|
|
@@ -15,6 +15,9 @@ export type InitialsAvatarProps = Omit<ComponentProps<typeof Avatar>, "children"
|
|
|
15
15
|
const toneClasses: Record<InitialsAvatarTone, string> = {
|
|
16
16
|
active: "bg-primary/15 text-primary",
|
|
17
17
|
subtle: "bg-muted text-muted-foreground",
|
|
18
|
+
client: "bg-[color:var(--surface-account-client)] text-[color:var(--role-client-strong)]",
|
|
19
|
+
team: "bg-[color:var(--surface-account-team)] text-[color:var(--role-team-strong)]",
|
|
20
|
+
inverse: "bg-[color:var(--brand-panel-foreground)] text-[color:var(--project-hero-base)] ring-1 ring-[color:var(--brand-panel-border)]",
|
|
18
21
|
};
|
|
19
22
|
|
|
20
23
|
export function InitialsAvatar({ label, fallback, tone = "active", className, ...props }: InitialsAvatarProps) {
|
|
@@ -35,6 +35,25 @@ test("invitation email creates an accessible branded HTML and text pair", () =>
|
|
|
35
35
|
assert.doesNotMatch(email.html, /Verde & Filhos <Lda>/);
|
|
36
36
|
assert.match(email.text, /Organização: Verde & Filhos <Lda>/);
|
|
37
37
|
assert.match(email.text, /24 de agosto de 2026/);
|
|
38
|
+
assert.match(email.html, /Este convite foi enviado para ana@example\.com/);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("transactional email can use non-invitation recipient wording", () => {
|
|
42
|
+
const email = buildInvitationEmail({
|
|
43
|
+
brandName: "BeGreen Consulting",
|
|
44
|
+
preheader: "A sua conta foi atualizada.",
|
|
45
|
+
eyebrow: "Aviso de segurança",
|
|
46
|
+
title: "Alteração concluída",
|
|
47
|
+
introduction: "A operação foi concluída.",
|
|
48
|
+
details: [{ label: "Estado", value: "Concluído" }],
|
|
49
|
+
recipientEmail: "ana@example.com",
|
|
50
|
+
recipientLabel: "Esta mensagem foi enviada para",
|
|
51
|
+
closingNote: "Se não reconhece esta mensagem, pode ignorá-la em segurança.",
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
assert.match(email.html, /Esta mensagem foi enviada para ana@example\.com/);
|
|
55
|
+
assert.match(email.text, /Esta mensagem foi enviada para ana@example\.com/);
|
|
56
|
+
assert.doesNotMatch(email.html, /convite/i);
|
|
38
57
|
});
|
|
39
58
|
|
|
40
59
|
test("email brand name follows the configured sender display name", () => {
|
package/src/email/invitation.ts
CHANGED
|
@@ -90,6 +90,7 @@ export type TransactionalEmailContent = {
|
|
|
90
90
|
actionUrl?: string;
|
|
91
91
|
expiresLabel?: string | null;
|
|
92
92
|
recipientEmail?: string;
|
|
93
|
+
recipientLabel?: string;
|
|
93
94
|
closingNote?: string;
|
|
94
95
|
palette?: Partial<TransactionalEmailPalette>;
|
|
95
96
|
};
|
|
@@ -131,6 +132,7 @@ export function buildTransactionalEmail(content: TransactionalEmailContent): Tra
|
|
|
131
132
|
const hasAction = Boolean(actionLabel && actionUrl);
|
|
132
133
|
const expiresLabel = content.expiresLabel ? escapeHtml(content.expiresLabel) : null;
|
|
133
134
|
const recipientEmail = content.recipientEmail ? escapeHtml(content.recipientEmail) : null;
|
|
135
|
+
const recipientLabel = escapeHtml(content.recipientLabel ?? "Este convite foi enviado para");
|
|
134
136
|
const closingNote = escapeHtml(content.closingNote ?? "Se não reconhece este convite, pode ignorar esta mensagem em segurança.");
|
|
135
137
|
const detailRows = content.details.map((detail, index) => `
|
|
136
138
|
<tr>
|
|
@@ -198,7 +200,7 @@ export function buildTransactionalEmail(content: TransactionalEmailContent): Tra
|
|
|
198
200
|
</td>
|
|
199
201
|
</tr>
|
|
200
202
|
</table>
|
|
201
|
-
<p style="margin:18px auto 0;max-width:540px;color:${palette.mutedText};font-size:11px;line-height:1.6;text-align:center;">${closingNote}${recipientEmail ? `<br
|
|
203
|
+
<p style="margin:18px auto 0;max-width:540px;color:${palette.mutedText};font-size:11px;line-height:1.6;text-align:center;">${closingNote}${recipientEmail ? `<br>${recipientLabel} ${recipientEmail}.` : ""}</p>
|
|
202
204
|
</td>
|
|
203
205
|
</tr>
|
|
204
206
|
</table>
|
|
@@ -207,7 +209,7 @@ export function buildTransactionalEmail(content: TransactionalEmailContent): Tra
|
|
|
207
209
|
|
|
208
210
|
const detailText = content.details.map((detail) => `${detail.label}: ${detail.value}`).join("\n");
|
|
209
211
|
const actionText = hasAction ? `\n\n${content.actionLabel}:\n${content.actionUrl}` : "";
|
|
210
|
-
const text = `${content.brandName}\n\n${content.title}\n\n${content.introduction}\n\n${detailText}${actionText}${content.expiresLabel ? `\n\nEste convite é válido até ${content.expiresLabel}.` : ""}\n\n${content.closingNote ?? "Se não reconhece este convite, pode ignorar esta mensagem em segurança."}${content.recipientEmail ? `\
|
|
212
|
+
const text = `${content.brandName}\n\n${content.title}\n\n${content.introduction}\n\n${detailText}${actionText}${content.expiresLabel ? `\n\nEste convite é válido até ${content.expiresLabel}.` : ""}\n\n${content.closingNote ?? "Se não reconhece este convite, pode ignorar esta mensagem em segurança."}${content.recipientEmail ? `\n${content.recipientLabel ?? "Este convite foi enviado para"} ${content.recipientEmail}.` : ""}`;
|
|
211
213
|
|
|
212
214
|
return { html, text };
|
|
213
215
|
}
|
package/src/index.ts
CHANGED
|
@@ -32,6 +32,7 @@ export { TableRowsSkeleton } from "./components/skeleton-table";
|
|
|
32
32
|
export { Separator } from "./components/separator";
|
|
33
33
|
export * from "./components/alert-dialog";
|
|
34
34
|
export * from "./components/card";
|
|
35
|
+
export * from "./components/cover-header";
|
|
35
36
|
export * from "./components/chart";
|
|
36
37
|
export * from "./components/breadcrumb";
|
|
37
38
|
export * from "./components/calendar";
|