v3: inline edit forms and the narrow-viewport search row ease open instead of popping in
All checks were successful
Build and Publish Docker Image / build-and-push (push) Successful in 41s

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
khannurien
2026-09-19 20:26:18 +00:00
parent 5e5f984d87
commit 8c3efe6c30
15 changed files with 202 additions and 28 deletions

View File

@@ -30,6 +30,7 @@ import { useAuth } from "../hooks/useAuth.ts";
import { useCategories } from "../hooks/useCategories.ts";
import { useDocumentTitle } from "../hooks/useDocumentTitle.ts";
import { relativeTime } from "../utils/relativeTime.ts";
import { useHeightTransition } from "../hooks/useHeightTransition.ts";
import { useWS } from "../hooks/useWS.ts";
import { Avatar } from "../components/Avatar.tsx";
import RichContentCard from "../components/RichContentCard.tsx";
@@ -74,6 +75,7 @@ export function Dump() {
const [collections, setCollections] = useState<Playlist[]>([]);
const [titleEditing, setTitleEditing] = useState(false);
const headerRightRef = useHeightTransition<HTMLDivElement>(titleEditing);
const [titleDraft, setTitleDraft] = useState("");
const [titleSaving, setTitleSaving] = useState(false);
const [titleError, setTitleError] = useState<string | null>(null);
@@ -364,10 +366,10 @@ export function Dump() {
</button>
)}
</div>
<div className="dump-header-right">
<div className="dump-header-right" ref={headerRightRef}>
{titleEditing
? (
<div className="dump-title-editor">
<div className="dump-title-editor animate-in">
<input
type="text"
className="dump-title-input"

View File

@@ -28,6 +28,7 @@ import { playlistUrl } from "../utils/urls.ts";
import { can } from "../utils/permissions.ts";
import { useAuth } from "../hooks/useAuth.ts";
import { useDocumentTitle } from "../hooks/useDocumentTitle.ts";
import { useHeightTransition } from "../hooks/useHeightTransition.ts";
import { useWS } from "../hooks/useWS.ts";
import { relativeTime } from "../utils/relativeTime.ts";
import { DumpCard } from "../components/DumpCard.tsx";
@@ -105,6 +106,7 @@ export function PlaylistDetail() {
const [dragOverIndex, setDragOverIndex] = useState<number | null>(null);
const [editOpen, setEditOpen] = useState(false);
const headerRef = useHeightTransition<HTMLDivElement>(editOpen);
const [confirmDelete, setConfirmDelete] = useState(false);
// Mirrors activeDumpIds for use in effects without adding it as a dep.
@@ -587,7 +589,7 @@ export function PlaylistDetail() {
return (
<PageShell>
<div className="playlist-detail-header">
<div className="playlist-detail-header" ref={headerRef}>
<div className="playlist-detail-header-top">
{editOpen
? (
@@ -842,6 +844,7 @@ function PlaylistEditForm(
: null)}
alt="Cover"
size={72}
className="animate-in"
onChange={(file) => {
field.onChange(file);
setImagePreview(URL.createObjectURL(file));
@@ -850,7 +853,7 @@ function PlaylistEditForm(
)}
/>
<div className="playlist-detail-content playlist-edit-content">
<div className="playlist-detail-content playlist-edit-content animate-in">
<Controller
name="title"
control={form.control}

View File

@@ -11,6 +11,7 @@ import {
} from "../model.ts";
import { useAuth } from "../hooks/useAuth.ts";
import { useDocumentTitle } from "../hooks/useDocumentTitle.ts";
import { useHeightTransition } from "../hooks/useHeightTransition.ts";
import { PageShell } from "../components/PageShell.tsx";
import {
expectOk,
@@ -35,6 +36,7 @@ export function UserLogin() {
const navigate = useNavigate();
const { login } = useAuth();
const [showReset, setShowReset] = useState(false);
const cardRef = useHeightTransition<HTMLDivElement>(showReset);
const form = useApiForm<LoginValues>({
defaultValues: { username: "", password: "" },
@@ -54,7 +56,7 @@ export function UserLogin() {
return (
<PageShell centered>
<div className="auth-card">
<div className="auth-card" ref={cardRef}>
<h1 className="auth-card-title">
<Trans>Log in</Trans>
</h1>
@@ -115,7 +117,7 @@ function ResetRequestPanel() {
if (sent) {
return (
<div className="auth-reset-panel">
<div className="auth-reset-panel animate-in">
<p className="auth-reset-sent">
<Trans>
If that address is registered you'll receive a reset link shortly.
@@ -126,7 +128,7 @@ function ResetRequestPanel() {
}
return (
<div className="auth-reset-panel">
<div className="auth-reset-panel animate-in">
<FormProvider {...form}>
<form onSubmit={onSubmit} className="form auth-reset-form">
<FormError title={t`Request failed`} />

View File

@@ -45,6 +45,7 @@ import { useDefaultFeedTab } from "../hooks/useDefaultFeedTab.ts";
import { useWS } from "../hooks/useWS.ts";
import { useDumpListSync } from "../hooks/useDumpListSync.ts";
import { useFading } from "../hooks/useFading.ts";
import { useHeightTransition } from "../hooks/useHeightTransition.ts";
import { usePlaylistListSync } from "../hooks/usePlaylistListSync.ts";
import { usePositionAwareSync } from "../hooks/usePositionAwareSync.ts";
import type { Playlist, RawPlaylist } from "../model.ts";
@@ -327,6 +328,8 @@ export function UserPublicProfile() {
const [descEditing, setDescEditing] = useState(false);
const [emailEditing, setEmailEditing] = useState(false);
const infoRef = useHeightTransition<HTMLDivElement>(emailEditing);
const descRef = useHeightTransition<HTMLDivElement>(descEditing);
const prevMyVotesRef = useRef<Set<string> | null>(null);
const [changePasswordOpen, setChangePasswordOpen] = useState(false);
@@ -786,7 +789,7 @@ export function UserPublicProfile() {
</label>
)}
</div>
<div className="profile-info">
<div className="profile-info" ref={infoRef}>
<div className="profile-info-scroll">
<div className="profile-username-row">
<h1 className="profile-username">{profileUser.username}</h1>
@@ -856,7 +859,7 @@ export function UserPublicProfile() {
</div>
{(profileUser.description || isOwnProfile) && (
<div className="profile-description">
<div className="profile-description" ref={descRef}>
{descEditing
? (
<DescriptionEditor
@@ -1646,7 +1649,7 @@ function EmailEditor({ initialEmail, onCancel, onSaved }: EmailEditorProps) {
return (
<FormProvider {...form}>
<form className="profile-email-editor" onSubmit={onSubmit}>
<form className="profile-email-editor animate-in" onSubmit={onSubmit}>
<input
type="email"
className="profile-email-input"
@@ -1712,7 +1715,10 @@ function DescriptionEditor(
return (
<FormProvider {...form}>
<form className="profile-description-editor" onSubmit={onSubmit}>
<form
className="profile-description-editor animate-in"
onSubmit={onSubmit}
>
<Controller
name="description"
control={form.control}