mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-24 13:54:57 +00:00
fix(emailclient): 10-issue polish pass — visual feedback, state sync, UX
- Selected email card: add secondary-container bg + left accent border - Unread email cards: add primary left border accent - StarButton: sync local state with parent via useEffect([isStarred]) - Initial dark mode: useEffect applies data-theme on mount - Star active color: amber #f9a825 via .star-button--active CSS rule - Unread count badge: styled as pill with primary-container background - Empty state: add inbox/folder_open material icon - Folder toolbar label: capitalize + replace underscores with spaces - ComposeWindow: CC/BCC hidden by default, revealed via Cc/Bcc button - Email header: flex layout for .header-top, column gap for .header-details Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// fakemui/email/atoms/StarButton.tsx
|
||||
import React, { forwardRef, useState } from 'react'
|
||||
import React, { forwardRef, useEffect, useState } from 'react'
|
||||
import { MaterialIcon } from '../../../../icons/react/fakemui'
|
||||
import { useAccessible } from '../../../../hooks/useAccessible'
|
||||
|
||||
@@ -16,6 +16,8 @@ export const StarButton = forwardRef<
|
||||
>(({ isStarred = false, onToggleStar, testId, ...props }, ref) => {
|
||||
const [starred, setStarred] = useState(isStarred)
|
||||
|
||||
useEffect(() => { setStarred(isStarred) }, [isStarred])
|
||||
|
||||
const accessible = useAccessible({
|
||||
feature: 'email',
|
||||
component: 'star-button',
|
||||
|
||||
@@ -24,6 +24,7 @@ export const ComposeWindow = ({
|
||||
const [bcc, setBcc] = useState<string[]>([])
|
||||
const [subject, setSubject] = useState('')
|
||||
const [body, setBody] = useState('')
|
||||
const [showCcBcc, setShowCcBcc] = useState(false)
|
||||
const accessible = useAccessible({
|
||||
feature: 'email', component: 'compose',
|
||||
identifier: customTestId || 'compose'
|
||||
@@ -58,15 +59,37 @@ export const ComposeWindow = ({
|
||||
</button>
|
||||
</Box>
|
||||
<Box className="compose-body">
|
||||
<RecipientInput recipientType="to"
|
||||
recipients={to} onRecipientsChange={setTo}
|
||||
placeholder="To:" />
|
||||
<RecipientInput recipientType="cc"
|
||||
recipients={cc} onRecipientsChange={setCc}
|
||||
placeholder="Cc:" />
|
||||
<RecipientInput recipientType="bcc"
|
||||
recipients={bcc} onRecipientsChange={setBcc}
|
||||
placeholder="Bcc:" />
|
||||
<Box style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
|
||||
<RecipientInput recipientType="to"
|
||||
recipients={to} onRecipientsChange={setTo}
|
||||
placeholder="To:" />
|
||||
{!showCcBcc && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowCcBcc(true)}
|
||||
style={{
|
||||
fontSize: '0.75rem',
|
||||
color: 'var(--mat-sys-on-surface-variant)',
|
||||
whiteSpace: 'nowrap',
|
||||
flexShrink: 0,
|
||||
padding: '4px 6px',
|
||||
}}
|
||||
aria-label="Show Cc and Bcc fields"
|
||||
>
|
||||
Cc/Bcc
|
||||
</button>
|
||||
)}
|
||||
</Box>
|
||||
{showCcBcc && (
|
||||
<>
|
||||
<RecipientInput recipientType="cc"
|
||||
recipients={cc} onRecipientsChange={setCc}
|
||||
placeholder="Cc:" />
|
||||
<RecipientInput recipientType="bcc"
|
||||
recipients={bcc} onRecipientsChange={setBcc}
|
||||
placeholder="Bcc:" />
|
||||
</>
|
||||
)}
|
||||
<input type="text" placeholder="Subject"
|
||||
value={subject} id="compose-subject"
|
||||
aria-label="Subject"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import React from 'react'
|
||||
import React, { useEffect } from 'react'
|
||||
import {
|
||||
MailboxLayout,
|
||||
MailboxHeader,
|
||||
@@ -15,6 +15,13 @@ import { useEmailClient } from './hooks/useEmailClient'
|
||||
export default function EmailClientContent() {
|
||||
const { state, actions } = useEmailClient()
|
||||
|
||||
useEffect(() => {
|
||||
document.documentElement.setAttribute(
|
||||
'data-theme',
|
||||
state.isDarkMode ? 'dark' : 'light'
|
||||
)
|
||||
}, [state.isDarkMode])
|
||||
|
||||
const header = (
|
||||
<MailboxHeader
|
||||
searchQuery={state.searchQuery}
|
||||
@@ -53,7 +60,9 @@ export default function EmailClientContent() {
|
||||
variant="body2"
|
||||
className="mailbox-thread-folder-label"
|
||||
>
|
||||
{state.activeFolder}
|
||||
{state.activeFolder
|
||||
.replace(/_/g, ' ')
|
||||
.replace(/^\w/, c => c.toUpperCase())}
|
||||
{state.filteredEmails.length > 0 &&
|
||||
` (${state.filteredEmails.length})`}
|
||||
</Typography>
|
||||
@@ -66,6 +75,11 @@ export default function EmailClientContent() {
|
||||
</Box>
|
||||
{state.filteredEmails.length === 0 ? (
|
||||
<Box className="mailbox-empty-state">
|
||||
<span className="material-symbols-outlined mailbox-empty-icon">
|
||||
{state.activeFolder === 'inbox' || state.searchQuery
|
||||
? 'inbox'
|
||||
: 'folder_open'}
|
||||
</span>
|
||||
<Typography variant="body2">
|
||||
{state.activeFolder === 'inbox' &&
|
||||
state.searchQuery
|
||||
|
||||
@@ -339,10 +339,15 @@ input, textarea, select {
|
||||
}
|
||||
|
||||
.folder-nav-item .unread-count {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
background-color: var(--mat-sys-primary-container);
|
||||
color: var(--mat-sys-on-primary-container);
|
||||
border-radius: var(--mat-sys-corner-full);
|
||||
padding: 0 6px;
|
||||
font-size: 0.6875rem;
|
||||
font-weight: 700;
|
||||
min-width: 18px;
|
||||
text-align: center;
|
||||
margin-left: auto;
|
||||
color: var(--mat-sys-on-surface-variant);
|
||||
}
|
||||
|
||||
/* Active folder — filled tonal */
|
||||
@@ -616,8 +621,17 @@ input, textarea, select {
|
||||
border-color: var(--mat-sys-surface-variant);
|
||||
}
|
||||
|
||||
/* Issue 1 — selected card distinct highlight */
|
||||
.email-card[aria-current="true"] {
|
||||
background-color: var(--mat-sys-secondary-container) !important;
|
||||
border-left: 3px solid var(--mat-sys-primary);
|
||||
padding-left: 13px;
|
||||
}
|
||||
|
||||
.email-card--unread {
|
||||
background-color: var(--mat-sys-surface-container-lowest);
|
||||
border-left: 3px solid var(--mat-sys-primary);
|
||||
padding-left: 13px;
|
||||
}
|
||||
|
||||
.email-card--unread .email-from,
|
||||
@@ -698,6 +712,15 @@ input, textarea, select {
|
||||
background-color: var(--mat-sys-surface-container-high);
|
||||
}
|
||||
|
||||
/* Issue 4 — starred state amber color */
|
||||
.star-button--active {
|
||||
color: #f9a825;
|
||||
}
|
||||
|
||||
.star-button--active .material-symbols-outlined {
|
||||
color: #f9a825;
|
||||
}
|
||||
|
||||
/* ============================================ */
|
||||
/* Email Detail / Reading Pane */
|
||||
/* ============================================ */
|
||||
@@ -714,6 +737,21 @@ input, textarea, select {
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
/* Issue 9 — email header metadata layout */
|
||||
.header-top {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.header-details {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
/* ============================================ */
|
||||
/* Thread Panel (main area) */
|
||||
/* ============================================ */
|
||||
|
||||
Reference in New Issue
Block a user