v3: dump edit balances its two columns — the dump itself (preview, refresh, thumbnail, URL or replacement file) on the left, its presentation (title, why, categories, visibility) on the right, actions in a full-width footer bar so both columns end level; "Why?" stretches to absorb a taller preview on desktop
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
64
src/App.css
64
src/App.css
@@ -95,8 +95,8 @@
|
|||||||
.dump-post-header,
|
.dump-post-header,
|
||||||
.dump-rich-content,
|
.dump-rich-content,
|
||||||
.form-page-header,
|
.form-page-header,
|
||||||
.dump-edit-preview,
|
.dump-edit-panel,
|
||||||
.form-page .form,
|
.dump-edit-footer,
|
||||||
.playlist-detail-header,
|
.playlist-detail-header,
|
||||||
.comment-node-inner {
|
.comment-node-inner {
|
||||||
box-shadow: var(--panel-ring), var(--card-shadow);
|
box-shadow: var(--panel-ring), var(--card-shadow);
|
||||||
@@ -3032,20 +3032,38 @@ body.has-player .chat-fab {
|
|||||||
animation: page-enter 0.2s ease both;
|
animation: page-enter 0.2s ease both;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The edit form spans the page grid and lays out its own panels: the dump
|
||||||
|
itself (preview, thumbnail, source) beside its presentation (title, why,
|
||||||
|
categories, visibility), with a full-width action bar underneath so both
|
||||||
|
columns end on the same line. Only the header and footer are rounded. */
|
||||||
|
.dump-edit-form {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
@media (min-width: 860px) {
|
@media (min-width: 860px) {
|
||||||
.form-page--two-col {
|
.form-page--two-col,
|
||||||
|
.dump-edit-form {
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr 1fr;
|
||||||
}
|
}
|
||||||
.form-page--two-col .form-page-header {
|
.form-page--two-col .form-page-header,
|
||||||
|
.dump-edit-form,
|
||||||
|
.dump-edit-footer {
|
||||||
grid-column: 1 / -1;
|
grid-column: 1 / -1;
|
||||||
}
|
}
|
||||||
.form-page--two-col .dump-edit-preview {
|
/* "Why?" soaks up whatever height the source column adds, so a tall
|
||||||
border-radius: 0 0 0 var(--radius-xl);
|
preview doesn't leave a hole under the details. */
|
||||||
|
.dump-edit-panel--details .form-field:has(.mention-textarea-wrap) {
|
||||||
|
flex: 1 1 auto;
|
||||||
}
|
}
|
||||||
/* Higher specificity than the generic `.form-page .form` below so the
|
.dump-edit-panel--details .mention-textarea-wrap {
|
||||||
form's bottom-left stays square where it meets the preview. */
|
flex: 1 1 auto;
|
||||||
.form-page.form-page--two-col .form {
|
display: flex;
|
||||||
border-radius: 0 0 var(--radius-xl) 0;
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.dump-edit-panel--details .mention-textarea-wrap textarea {
|
||||||
|
flex: 1 1 auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3074,13 +3092,31 @@ body.has-player .chat-fab {
|
|||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dump-edit-preview {
|
.dump-edit-panel {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
min-width: 0;
|
||||||
padding: 1.25rem;
|
padding: 1.25rem;
|
||||||
background: var(--color-surface);
|
background: var(--color-surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dump-edit-preview {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dump-edit-footer {
|
||||||
|
padding: 0.875rem 1.25rem;
|
||||||
|
background: var(--color-surface);
|
||||||
border-radius: 0 0 var(--radius-xl) var(--radius-xl);
|
border-radius: 0 0 var(--radius-xl) var(--radius-xl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dump-edit-footer .form-actions {
|
||||||
|
padding-top: 0;
|
||||||
|
border-top: 0;
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.dump-edit-refresh {
|
.dump-edit-refresh {
|
||||||
margin-top: 0.75rem;
|
margin-top: 0.75rem;
|
||||||
}
|
}
|
||||||
@@ -3097,12 +3133,6 @@ body.has-player .chat-fab {
|
|||||||
gap: 0.75rem;
|
gap: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-page .form {
|
|
||||||
background: var(--color-surface);
|
|
||||||
border-radius: 0 0 var(--radius-xl) var(--radius-xl);
|
|
||||||
padding: 1.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-actions {
|
.form-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -214,6 +214,10 @@ export function DumpEdit() {
|
|||||||
<h1 className="form-page-title">{dump.title}</h1>
|
<h1 className="form-page-title">{dump.title}</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<DumpEditForm
|
||||||
|
key={dump.id}
|
||||||
|
dump={dump}
|
||||||
|
preview={
|
||||||
<div className="dump-edit-preview">
|
<div className="dump-edit-preview">
|
||||||
{dump.kind === "file"
|
{dump.kind === "file"
|
||||||
? <FilePreview dump={dump} />
|
? <FilePreview dump={dump} />
|
||||||
@@ -245,10 +249,7 @@ export function DumpEdit() {
|
|||||||
<p className="dump-edit-refresh-error">{refreshError}</p>
|
<p className="dump-edit-refresh-error">{refreshError}</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
<DumpEditForm
|
|
||||||
key={dump.id}
|
|
||||||
dump={dump}
|
|
||||||
currentThumbnailSrc={currentThumbnailSrc}
|
currentThumbnailSrc={currentThumbnailSrc}
|
||||||
thumbUploading={thumbUploading}
|
thumbUploading={thumbUploading}
|
||||||
onThumbnailChange={handleThumbnailChange}
|
onThumbnailChange={handleThumbnailChange}
|
||||||
@@ -272,6 +273,7 @@ export function DumpEdit() {
|
|||||||
|
|
||||||
interface DumpEditFormProps {
|
interface DumpEditFormProps {
|
||||||
dump: Dump;
|
dump: Dump;
|
||||||
|
preview: React.ReactNode;
|
||||||
currentThumbnailSrc: string | null;
|
currentThumbnailSrc: string | null;
|
||||||
thumbUploading: boolean;
|
thumbUploading: boolean;
|
||||||
onThumbnailChange: (file: File) => void;
|
onThumbnailChange: (file: File) => void;
|
||||||
@@ -290,6 +292,7 @@ interface EditValues {
|
|||||||
|
|
||||||
function DumpEditForm({
|
function DumpEditForm({
|
||||||
dump,
|
dump,
|
||||||
|
preview,
|
||||||
currentThumbnailSrc,
|
currentThumbnailSrc,
|
||||||
thumbUploading,
|
thumbUploading,
|
||||||
onThumbnailChange,
|
onThumbnailChange,
|
||||||
@@ -367,8 +370,13 @@ function DumpEditForm({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<FormProvider {...form}>
|
<FormProvider {...form}>
|
||||||
<form className="form" onSubmit={onSubmit}>
|
<form className="form dump-edit-form" onSubmit={onSubmit}>
|
||||||
<FormError title={t`Could not save`} />
|
{
|
||||||
|
/* Left: the dumped thing itself — what it looks like and where it
|
||||||
|
comes from. Right: how it's presented and shared. */
|
||||||
|
}
|
||||||
|
<div className="dump-edit-panel dump-edit-panel--source">
|
||||||
|
{preview}
|
||||||
|
|
||||||
<div className="form-field">
|
<div className="form-field">
|
||||||
<span className="form-label">
|
<span className="form-label">
|
||||||
@@ -394,26 +402,6 @@ function DumpEditForm({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<TextField<EditValues>
|
|
||||||
name="title"
|
|
||||||
label={t`Title`}
|
|
||||||
labelAction={metaTitle
|
|
||||||
? (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="form-label-action"
|
|
||||||
onClick={() =>
|
|
||||||
form.setValue("title", metaTitle, { shouldDirty: true })}
|
|
||||||
disabled={currentTitle.trim() === metaTitle}
|
|
||||||
>
|
|
||||||
<Trans>Reset to default</Trans>
|
|
||||||
</button>
|
|
||||||
)
|
|
||||||
: undefined}
|
|
||||||
maxLength={VALIDATION.DUMP_TITLE_MAX}
|
|
||||||
rules={{ required: true }}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{dump.kind === "url"
|
{dump.kind === "url"
|
||||||
? (
|
? (
|
||||||
<TextField<EditValues>
|
<TextField<EditValues>
|
||||||
@@ -440,6 +428,30 @@ function DumpEditForm({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="dump-edit-panel dump-edit-panel--details">
|
||||||
|
<FormError title={t`Could not save`} />
|
||||||
|
|
||||||
|
<TextField<EditValues>
|
||||||
|
name="title"
|
||||||
|
label={t`Title`}
|
||||||
|
labelAction={metaTitle
|
||||||
|
? (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="form-label-action"
|
||||||
|
onClick={() =>
|
||||||
|
form.setValue("title", metaTitle, { shouldDirty: true })}
|
||||||
|
disabled={currentTitle.trim() === metaTitle}
|
||||||
|
>
|
||||||
|
<Trans>Reset to default</Trans>
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
: undefined}
|
||||||
|
maxLength={VALIDATION.DUMP_TITLE_MAX}
|
||||||
|
rules={{ required: true }}
|
||||||
|
/>
|
||||||
|
|
||||||
<RichTextField<EditValues>
|
<RichTextField<EditValues>
|
||||||
name="comment"
|
name="comment"
|
||||||
@@ -456,7 +468,9 @@ function DumpEditForm({
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<VisibilityToggle<EditValues> name="isPublic" />
|
<VisibilityToggle<EditValues> name="isPublic" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="dump-edit-footer">
|
||||||
<div className="form-actions">
|
<div className="form-actions">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -474,6 +488,7 @@ function DumpEditForm({
|
|||||||
</SubmitButton>
|
</SubmitButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</FormProvider>
|
</FormProvider>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user