mirror of
https://github.com/tabler/tabler.git
synced 2026-10-12 05:06:55 +04:00
Style the email-inbox message list with a list group (#2996)
This commit is contained in:
1 parent
b8e61dbea7
commit
d9ed9428de
5 files changed
+96
-100
No files matched your search
@@ -1,7 +1,6 @@
|
||||
# Classes used in the rendered html that no stylesheet defines on purpose:
|
||||
# JavaScript hooks and demo-only markers. One per line; delete a line when the hook goes.
|
||||
callout-note
|
||||
checkbox-wrapper-mail
|
||||
datagrid-content
|
||||
datagrid-item
|
||||
datatable
|
||||
@@ -11,17 +10,7 @@ docs-search
|
||||
docs-theme-toggle
|
||||
dropzone-msg-desc
|
||||
dropzone-msg-title
|
||||
email-action-icons
|
||||
email-action-icons-item
|
||||
email-border
|
||||
email-content
|
||||
email-date
|
||||
email-list
|
||||
email-sender-info
|
||||
email-subject
|
||||
email-title
|
||||
fallback
|
||||
file-offcanvas
|
||||
github-dark
|
||||
icon-tabler
|
||||
icon-tabler-pencil
|
||||
@@ -47,7 +36,6 @@ sort-score
|
||||
sort-status
|
||||
sort-tags
|
||||
sort-type
|
||||
star-toggle
|
||||
table-tbody
|
||||
tblr-illustrations-computer-fix-a
|
||||
tblr-illustrations-computer-fix-b
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@tabler/preview": patch
|
||||
---
|
||||
|
||||
Fixed the unstyled message list on the `email-inbox` page: a hoverable list group with label dots, dates and row actions.
|
||||
@@ -10,27 +10,33 @@ import Progress from '@ui/Progress.astro'
|
||||
import Modal from '@shared/components/modals/Modal.astro'
|
||||
import CaptureModal from '@shared/components/CaptureModal.astro'
|
||||
import NewEmailModalContent from '@shared/components/modals/NewEmailModalContent.astro'
|
||||
import ListGroup from '@ui/ListGroup.astro'
|
||||
import ListGroupItem from '@ui/ListGroupItem.astro'
|
||||
import { formatShortDate } from '@shared/lib/date-format'
|
||||
import mails from '@data/mails.json'
|
||||
|
||||
// Label colors shared by the folder sidebar and the dots in the message list.
|
||||
const labelColors: Record<string, string> = { Updates: 'info', Friends: 'warning', Family: 'success', Social: 'primary', Important: 'danger', Promotions: 'purple' }
|
||||
---
|
||||
|
||||
<DefaultLayout title="Email inbox" pageHeader="Inbox" pageMenu="extra.email-inbox" pageLibs={['hugerte']}>
|
||||
<Card>
|
||||
<div class="row g-0">
|
||||
<div class="col-xxl-3 email-border border-end">
|
||||
<Offcanvas responsive="xxl" direction="start" class="h-100 file-offcanvas" tabindex="-1" id="emailSidebaroffcanvas">
|
||||
<div class="col-xxl-3 border-end">
|
||||
<Offcanvas responsive="xxl" direction="start" class="h-100" tabindex="-1" id="emailSidebaroffcanvas">
|
||||
<div class="card-body h-100">
|
||||
<div>
|
||||
<Button icon="pencil" text="Compose" color="primary" class="d-none d-sm-block" modalId="new-email" />
|
||||
<Button icon="pencil" text="Compose" color="primary" block class="d-none d-sm-block" modalId="new-email" />
|
||||
</div>
|
||||
|
||||
<div class="mt-3 nav nav-vertical">
|
||||
<a href="#" class="nav-link text-danger fw-bold">
|
||||
<div class="mt-3 nav nav-vertical nav-pills">
|
||||
<a href="#" class="nav-link active">
|
||||
<Icon name="inbox" class="me-2" />
|
||||
Inbox<span class="badge bg-danger ms-auto">{mails.length}</span>
|
||||
Inbox<span class="badge bg-primary-lt ms-auto">{mails.length}</span>
|
||||
</a>
|
||||
<a href="#" class="nav-link"><Icon name="star" class="me-2" />Starred</a>
|
||||
<a href="#" class="nav-link"><Icon name="clock" class="me-2" />Snoozed</a>
|
||||
<a href="#" class="nav-link"><Icon name="file" class="me-2" />Draft<span class="badge bg-info ms-auto">32</span></a>
|
||||
<a href="#" class="nav-link"><Icon name="file" class="me-2" />Draft<span class="badge bg-info-lt ms-auto">32</span></a>
|
||||
<a href="#" class="nav-link"><Icon name="mail-up" class="me-2" />Sent Mail</a>
|
||||
<a href="#" class="nav-link"><Icon name="trash" class="me-2" />Trash</a>
|
||||
<a href="#" class="nav-link"><Icon name="tag" class="me-2" />Important</a>
|
||||
@@ -39,25 +45,14 @@ import mails from '@data/mails.json'
|
||||
|
||||
<div class="mt-4">
|
||||
<Subheader as="h6">Labels</Subheader>
|
||||
<div class="mt-2 nav nav-vertical">
|
||||
<a href="#" class="nav-link">
|
||||
<div class="badge bg-info me-2"></div> Updates
|
||||
</a>
|
||||
<a href="#" class="nav-link">
|
||||
<div class="badge bg-warning me-2"></div> Friends
|
||||
</a>
|
||||
<a href="#" class="nav-link">
|
||||
<div class="badge bg-success me-2"></div> Family
|
||||
</a>
|
||||
<a href="#" class="nav-link">
|
||||
<div class="badge bg-primary me-2"></div> Social
|
||||
</a>
|
||||
<a href="#" class="nav-link">
|
||||
<div class="badge bg-danger me-2"></div> Important
|
||||
</a>
|
||||
<a href="#" class="nav-link">
|
||||
<div class="badge bg-purple me-2"></div> Promotions
|
||||
</a>
|
||||
<div class="mt-2 nav nav-vertical nav-pills">
|
||||
{
|
||||
Object.entries(labelColors).map(([label, color]) => (
|
||||
<a href="#" class="nav-link">
|
||||
<span class={`badge bg-${color} me-2`} /> {label}
|
||||
</a>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -112,12 +107,11 @@ import mails from '@data/mails.json'
|
||||
</div>
|
||||
</ButtonGroup>
|
||||
|
||||
<ButtonGroup>
|
||||
<ButtonGroup class="ms-auto">
|
||||
<button type="button" class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<Icon name="dots" /> More
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<span class="dropdown-header">More Options :</span>
|
||||
<div class="dropdown-menu dropdown-menu-end">
|
||||
<a class="dropdown-item" href="#">Mark as Unread</a>
|
||||
<a class="dropdown-item" href="#">Add to Tasks</a>
|
||||
<a class="dropdown-item" href="#">Add Star</a>
|
||||
@@ -125,70 +119,66 @@ import mails from '@data/mails.json'
|
||||
</div>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<ul class="email-list">
|
||||
{
|
||||
mails && mails.length > 0 ? (
|
||||
mails.map((mail) => (
|
||||
<li>
|
||||
<div class="email-sender-info">
|
||||
<div class="checkbox-wrapper-mail">
|
||||
<div class="form-check">
|
||||
<input type="checkbox" class="form-check-input" id={`mail-${mail.id}`} />
|
||||
<label class="form-check-label" for={`mail-${mail.id}`} />
|
||||
</div>
|
||||
</div>
|
||||
<span class="star-toggle">
|
||||
<Icon name="star" />
|
||||
</span>
|
||||
<a href="#" class="email-title">
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<ListGroup flush hoverable>
|
||||
{
|
||||
mails.length > 0 ? (
|
||||
mails.map((mail) => (
|
||||
<ListGroupItem>
|
||||
<div class="row align-items-center g-2">
|
||||
<div class="col-auto">
|
||||
<input type="checkbox" class="form-check-input m-0" aria-label={`Select "${mail.subject}"`} />
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<a href="#" class:list={['list-group-item-actions', mail.starred && 'show']} aria-label={mail.starred ? 'Remove star' : 'Add star'}>
|
||||
<Icon name="star" color={mail.starred ? 'yellow' : 'muted'} />
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-3 col-lg-2 text-truncate">
|
||||
<a href="#" class="text-body">
|
||||
{mail.sender}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="email-content">
|
||||
<a href="#" class="email-subject">
|
||||
{mail.subject} –
|
||||
<span>{mail.preview}</span>
|
||||
<div class="col text-truncate">
|
||||
<a href="#" class="text-body">
|
||||
{mail.subject}
|
||||
</a>
|
||||
<div class="email-date">{mail.date}</div>
|
||||
<span class="text-secondary"> – {mail.preview}</span>
|
||||
</div>
|
||||
|
||||
<div class="email-action-icons">
|
||||
<ul class="list-inline">
|
||||
<li class="list-inline-item">
|
||||
<a href="#" aria-label="Archive">
|
||||
<Icon name="archive" />
|
||||
</a>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<a href="#" aria-label="Delete">
|
||||
<Icon name="trash" />
|
||||
</a>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<a href="#" aria-label="Mark as read">
|
||||
<Icon name="mail-opened" class="email-action-icons-item" />
|
||||
</a>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<a href="#" aria-label="Snooze">
|
||||
<Icon name="clock" />
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="col-auto d-none d-lg-block">
|
||||
{mail.labels.map((label) => (
|
||||
<span class={`badge bg-${labelColors[label] ?? 'secondary'}`} title={label} aria-label={label} />
|
||||
))}
|
||||
</div>
|
||||
</li>
|
||||
))
|
||||
) : (
|
||||
<li class="text-muted">No emails</li>
|
||||
)
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-auto d-none d-md-block">
|
||||
<div class="list-group-item-actions d-flex gap-2">
|
||||
<a href="#" class="text-secondary" aria-label="Archive">
|
||||
<Icon name="archive" />
|
||||
</a>
|
||||
<a href="#" class="text-secondary" aria-label="Delete">
|
||||
<Icon name="trash" />
|
||||
</a>
|
||||
<a href="#" class="text-secondary" aria-label="Mark as read">
|
||||
<Icon name="mail-opened" />
|
||||
</a>
|
||||
<a href="#" class="text-secondary" aria-label="Snooze">
|
||||
<Icon name="clock" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto text-secondary text-end">{formatShortDate(new Date(mail.date))}</div>
|
||||
</div>
|
||||
</ListGroupItem>
|
||||
))
|
||||
) : (
|
||||
<ListGroupItem class="text-secondary">No emails</ListGroupItem>
|
||||
)
|
||||
}
|
||||
</ListGroup>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-7 mt-1">
|
||||
Showing 1 - {mails.length} of {mails.length}
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { formatCommitDate, formatLongDate, formatUtcTimestamp, toUnixSeconds } from './date-format'
|
||||
import { formatCommitDate, formatShortDate, formatLongDate, formatUtcTimestamp, toUnixSeconds } from './date-format'
|
||||
|
||||
describe('formatCommitDate', () => {
|
||||
it('reformats a full timestamp to "D Mon YYYY"', () => {
|
||||
@@ -36,3 +36,10 @@ describe('formatUtcTimestamp', () => {
|
||||
expect(formatUtcTimestamp(date)).toBe('2025-11-28 08:05 +0000')
|
||||
})
|
||||
})
|
||||
|
||||
describe('formatShortDate', () => {
|
||||
it('formats the UTC day and short month', () => {
|
||||
expect(formatShortDate(new Date('2025-08-27T11:49:00Z'))).toBe('27 Aug')
|
||||
expect(formatShortDate(new Date('2025-01-01T00:30:00Z'))).toBe('1 Jan')
|
||||
})
|
||||
})
|
||||
@@ -1,6 +1,7 @@
|
||||
// Demo data date formatting quirks:
|
||||
// - unix seconds are shifted by local timezone offset (not the true epoch)
|
||||
// - long-date format uses UTC wall-clock components
|
||||
const SHORT_MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
|
||||
const MONTHS = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']
|
||||
|
||||
/** Unix seconds shifted by the local timezone offset (not the true epoch). */
|
||||
@@ -13,6 +14,11 @@ export function formatLongDate(date: Date): string {
|
||||
return `${MONTHS[date.getUTCMonth()]} ${String(date.getUTCDate()).padStart(2, '0')}, ${date.getUTCFullYear()}`
|
||||
}
|
||||
|
||||
/** Formats a date as "D Mon" using its UTC components, e.g. "27 Aug". */
|
||||
export function formatShortDate(date: Date): string {
|
||||
return `${date.getUTCDate()} ${SHORT_MONTHS[date.getUTCMonth()]}`
|
||||
}
|
||||
|
||||
/** Reformats a commit-style timestamp ("Thu Nov 28 08:48:33 2025 +0100") to "28 Nov 2025". */
|
||||
export function formatCommitDate(date: string): string {
|
||||
const m = date.match(/^\w+ (\w+) (\d+) [\d:]+ (\d+)/)
|
||||
|
||||
Reference in new issue
Block a user