Add per-card action buttons to Lumière project grid
Build and Deploy Verso / deploy (push) Successful in 14m46s
Build and Deploy Verso / deploy (push) Successful in 14m46s
Restructures ProjectCard so the card is a <div> container instead of <a> (buttons cannot be nested inside anchor elements). A .lumiere-card-link anchor wraps the thumb+body area; a .lumiere-card-actions strip sits below it and fades in on hover. Buttons added (reusing the same tooltip components as the classic table): - Copy project (opens CloneProjectModal) - Download project zip - Compile & download PDF - Archive project (skipped when already archived) - Trash project (skipped when already trashed) Action icons are coloured $lum-text-muted at rest and shift to $lum-teal on hover, matching the Lumière palette. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -20,6 +20,11 @@ import DashApiError from './dash-api-error'
|
||||
import { ProjectCheckbox } from './table/project-checkbox'
|
||||
import ProjectTools from './table/project-tools/project-tools'
|
||||
import OLFormCheckbox from '@/shared/components/ol/ol-form-checkbox'
|
||||
import { CopyProjectButtonTooltip } from './table/cells/action-buttons/copy-project-button'
|
||||
import { DownloadProjectButtonTooltip } from './table/cells/action-buttons/download-project-button'
|
||||
import { CompileAndDownloadProjectPDFButtonTooltip } from './table/cells/action-buttons/compile-and-download-project-pdf-button'
|
||||
import { ArchiveProjectButtonTooltip } from './table/cells/action-buttons/archive-project-button'
|
||||
import { TrashProjectButtonTooltip } from './table/cells/action-buttons/trash-project-button'
|
||||
|
||||
type FormatVariant = 'latex' | 'typst' | 'quarto' | 'quarto-slides'
|
||||
|
||||
@@ -62,9 +67,10 @@ const ProjectCard = memo(function ProjectCard({
|
||||
<div className="lumiere-card-checkbox">
|
||||
<ProjectCheckbox projectId={project.id} projectName={project.name} />
|
||||
</div>
|
||||
<div className={`lumiere-card lumiere-card--${variant}`} translate="no">
|
||||
<a
|
||||
href={`/project/${project.id}`}
|
||||
className={`lumiere-card lumiere-card--${variant}`}
|
||||
className="lumiere-card-link"
|
||||
translate="no"
|
||||
>
|
||||
<div className="lumiere-card-thumb">
|
||||
@@ -87,6 +93,14 @@ const ProjectCard = memo(function ProjectCard({
|
||||
<span className="lumiere-card-date">{date}</span>
|
||||
</div>
|
||||
</a>
|
||||
<div className="lumiere-card-actions">
|
||||
<CopyProjectButtonTooltip project={project} />
|
||||
<DownloadProjectButtonTooltip project={project} />
|
||||
<CompileAndDownloadProjectPDFButtonTooltip project={project} />
|
||||
<ArchiveProjectButtonTooltip project={project} />
|
||||
<TrashProjectButtonTooltip project={project} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
@@ -450,10 +450,10 @@ $lum-noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' wi
|
||||
|
||||
// ── Individual card ───────────────────────────────────────────────────────
|
||||
|
||||
// Card container is now a <div>; the clickable area is .lumiere-card-link
|
||||
.lumiere-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-decoration: none;
|
||||
border-radius: 10px;
|
||||
background: rgba(255, 255, 255, 0.82);
|
||||
backdrop-filter: blur(8px);
|
||||
@@ -462,17 +462,58 @@ $lum-noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' wi
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.07);
|
||||
transition: transform 0.18s ease, box-shadow 0.18s ease;
|
||||
overflow: hidden;
|
||||
|
||||
&:hover,
|
||||
&:focus-within {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
}
|
||||
|
||||
// The anchor covers thumb + body, with no underline or color bleed
|
||||
.lumiere-card-link {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
|
||||
&:hover,
|
||||
&:focus-visible {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
// Action strip — icon buttons that fade in when the card is hovered
|
||||
.lumiere-card-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 2px;
|
||||
padding: 3px 6px 5px;
|
||||
border-top: 1px solid rgba($lum-teal, 0.10);
|
||||
opacity: 0;
|
||||
transition: opacity 0.15s ease;
|
||||
|
||||
// Recolour OLIconButton's action-btn class for the Lumière palette
|
||||
.action-btn {
|
||||
color: $lum-text-muted !important;
|
||||
border-radius: 6px !important;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: $lum-teal !important;
|
||||
background: rgba($lum-teal, 0.09) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.lumiere-card:hover .lumiere-card-actions,
|
||||
.lumiere-card:focus-within .lumiere-card-actions {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
// ── Card thumbnail ────────────────────────────────────────────────────────
|
||||
|
||||
.lumiere-card-thumb {
|
||||
|
||||
Reference in New Issue
Block a user