edit css
This commit is contained in:
parent
b61998b9e3
commit
868b5e1c36
6 changed files with 126 additions and 61 deletions
|
@ -49,30 +49,32 @@ export const Delete = () => {
|
||||||
<form
|
<form
|
||||||
onSubmit={handleSubmit(onSubmit)}
|
onSubmit={handleSubmit(onSubmit)}
|
||||||
className=
|
className=
|
||||||
'flex flex-col m-auto w-10/12 h-[80dvh] px-10 gap-3 pb-3 md:w-[50dvw]'
|
'flex flex-col m-auto w-10/12 px-6 py-8 gap-6 rounded-lg shadow-xl bg-white md:w-[50dvw]'
|
||||||
>
|
>
|
||||||
{/* register your input into the hook by invoking the "register" function */}
|
<h1 className="text-4xl font-bold text-gray-800 mb-4 text-center">刪除匿名</h1>
|
||||||
<h1 className="text-3xl my-3">刪除匿名</h1>
|
<label htmlFor='id' className='text-lg font-medium text-gray-700'>
|
||||||
<label htmlFor='id' className='text-left'>
|
|
||||||
ID:
|
ID:
|
||||||
</label>
|
</label>
|
||||||
<input id="id" type="search" {...register("id", { required: true })} className='border-b-4 border-zinc-500 w-full text-end outline-none focus:border-zinc-200' list="saved_id" />
|
<input id="id" type="search" {...register("id", { required: true })} className='border-b-4 border-zinc-500 w-full text-end outline-none focus:border-gray-600 text-black' list="saved_id" />
|
||||||
<datalist id="saved_id">
|
<datalist id="saved_id">
|
||||||
{saved.map((e, it) => <option value={e.id} key={it}></option>)}
|
{saved.map((e, it) => <option value={e.id} key={it}></option>)}
|
||||||
</datalist>
|
</datalist>
|
||||||
|
|
||||||
<label htmlFor='hash'>
|
<label htmlFor='hash' className='text-lg font-medium text-gray-700'>
|
||||||
Hash:
|
Hash:
|
||||||
</label>
|
</label>
|
||||||
<input id="hash" type="text" {...register("hash", { required: true })} className='border-b-4 border-zinc-500 w-full text-end outline-none focus:border-zinc-200' />
|
<input id="hash" type="text" {...register("hash", { required: true })} className='border-b-4 border-zinc-500 w-full text-end outline-none focus:border-gray-600 text-black' />
|
||||||
|
|
||||||
{/* errors will return when field validation fails */}
|
{/* errors will return when field validation fails */}
|
||||||
{errors.id && <span>This field is required</span>}
|
{errors.id && <span>This field is required</span>}
|
||||||
{errors.hash && <span>This field is required</span>}
|
{errors.hash && <span>This field is required</span>}
|
||||||
|
|
||||||
<button type="submit" className="ring-3 w-full mx-auto rounded-md ">
|
<button
|
||||||
刪除
|
type="submit"
|
||||||
</button>
|
className="mt-6 w-full py-3 bg-gray-800 text-white font-semibold rounded-lg hover:bg-gray-700 transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed" // Consistent button styling
|
||||||
|
>
|
||||||
|
刪除
|
||||||
|
</button>
|
||||||
</form>
|
</form>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|
|
@ -12,29 +12,30 @@ export const Fetch = () => {
|
||||||
setFetched(e)
|
setFetched(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}, [])
|
}, [])
|
||||||
return (<div className="h-dvh w-dvw flex flex-col justify-items-center overflow-scroll gap-10 pt-10 pb-10">
|
return (<div className="min-h-dvh w-dvw flex flex-col items-center gap-6 py-8 px-4">
|
||||||
{fetched.map(post => <div className="w-[87%] h-fit mx-auto border-1 border-slate-700 rounded">
|
{fetched.map(post => <div className="w-full max-w-2xl bg-gray-200 rounded-lg shadow-md p-6 flex flex-col gap-4">
|
||||||
<p className="text-left mx-2 mt-2">{post.content}</p>
|
<p className="text-gray-800 text-base leading-relaxed">{post.content}</p>
|
||||||
{post.enclosure.length > 0 && post.enclosure[0] != null && <div className="flex flex-row overflow-scroll w-full h-30 mx-2">
|
{post.enclosure.length > 0 && post.enclosure[0] != null && <div className="flex flex-row overflow-x-auto gap-3 py-2 -mx-2 px-2 border-t border-gray-200 pt-4">
|
||||||
{post.enclosure.map(enc => {
|
{post.enclosure.map((enc, index) => {
|
||||||
if (enc) {
|
if (enc) {
|
||||||
return <img src={MediaPathTrc(enc)} className="rounded h-full w-auto" />
|
return <img
|
||||||
|
key={index}
|
||||||
|
src={MediaPathTrc(enc)}
|
||||||
|
alt={`Post media ${post.id}-${index}`}
|
||||||
|
className="rounded-md h-32 object-cover aspect-video flex-shrink-0"
|
||||||
|
/>
|
||||||
} else {
|
} else {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
</div>}
|
</div>}
|
||||||
{post.signing && <p className="text-right mr-3">by: {post.signing}</p>}
|
{post.signing && <p className="text-right text-sm text-gray-600 italic mt-2">by: {post.signing}</p>}
|
||||||
<div className="h-fit flex flex-row text-sm mx-3">
|
<div className="flex justify-between items-center text-xs text-gray-500 border-t border-gray-100 pt-3">
|
||||||
<p className="self-start">PID: {post.id}</p>
|
<p className="self-start">PID: {post.id}</p>
|
||||||
<p className="self-end ml-auto">發文於: {post.post_at.toLocaleTimeString()}</p>
|
<p className="self-end">發文於: {post.post_at.toLocaleTimeString()}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</div>)}
|
</div>)}
|
||||||
|
|
||||||
</div>)
|
</div>)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ const ImageWithPreview = ({
|
||||||
<>
|
<>
|
||||||
<img
|
<img
|
||||||
src={src}
|
src={src}
|
||||||
alt="kkk"
|
alt="tyts"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setShow(true)
|
setShow(true)
|
||||||
}}
|
}}
|
||||||
|
@ -25,15 +25,15 @@ const ImageWithPreview = ({
|
||||||
}
|
}
|
||||||
onClick={() => setShow(false)}
|
onClick={() => setShow(false)}
|
||||||
>
|
>
|
||||||
<div className="z-20 w-full fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 md:w-[50dvw]">
|
<div className="z-20 w-full md:w-fit fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 md:w-[50dvw]">
|
||||||
<img src={src} className="w-[90dvw] h-auto m-auto" />
|
<img src={src} className="w-[90dvw] h-auto m-auto md:w-auto md:max-h-[80dvh]" />
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="border w-full"
|
className="mt-6 w-full py-3 bg-gray-800 text-white font-semibold rounded-lg hover:bg-gray-700 transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed" // Consistent button styling
|
||||||
onClick={() => remove()}
|
onClick={() => remove()}
|
||||||
>
|
>
|
||||||
刪除
|
刪除
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|
30
src/Post.tsx
30
src/Post.tsx
|
@ -1,4 +1,4 @@
|
||||||
import { useState, type ReactElement, } from 'react'
|
import { useState, type ReactElement } from 'react'
|
||||||
import { useForm, type SubmitHandler } from 'react-hook-form'
|
import { useForm, type SubmitHandler } from 'react-hook-form'
|
||||||
import ImageWithPreview from './ImageWithPrview'
|
import ImageWithPreview from './ImageWithPrview'
|
||||||
import { Post } from './api'
|
import { Post } from './api'
|
||||||
|
@ -77,20 +77,20 @@ export default function PostForm() {
|
||||||
<form
|
<form
|
||||||
onSubmit={handleSubmit(onSubmit)}
|
onSubmit={handleSubmit(onSubmit)}
|
||||||
className={
|
className={
|
||||||
'flex flex-col m-auto w-10/12 h-[80dvh] px-10 gap-3 pb-3 md:w-[50dvw]' +
|
'flex flex-col m-auto w-10/12 h-[80dvh] px-6 py-8 gap-6 rounded-lg shadow-xl bg-white ' + // Added background, padding, shadow, rounded corners
|
||||||
(cred !== undefined ? ' hidden' : '')
|
(cred !== undefined ? ' hidden' : '')
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{/* register your input into the hook by invoking the "register" function */}
|
<h1 className="text-4xl font-bold text-gray-800 mb-4 text-center">發佈匿名</h1> {/* Larger, bolder title, centered */}
|
||||||
<h1 className="text-3xl my-3">發佈匿名</h1>
|
|
||||||
<textarea
|
<textarea
|
||||||
className="ring-3 ring-zinc-500 focus:ring-zinc-200 outline-none rounded-xl h-5/12 px-3 w-full"
|
className="p-4 border border-gray-300 rounded-lg h-5/12 w-full resize-y focus:outline-none focus:border-gray-500 transition-colors duration-200 text-black" // Modernized textarea
|
||||||
placeholder="匿名文章"
|
placeholder="匿名文章"
|
||||||
{...register('content', { required: true })}
|
{...register('content', { required: true })}
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor="files" className="text-xl">
|
<label htmlFor="files" className="block text-lg font-medium text-gray-700 mb-2 cursor-pointer"> {/* Styled label */}
|
||||||
圖片
|
圖片
|
||||||
|
<span className="ml-2 px-3 py-1 bg-gray-100 text-gray-700 rounded-full text-sm hover:bg-gray-200 transition-colors duration-200">選擇圖片</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
multiple
|
multiple
|
||||||
|
@ -102,7 +102,7 @@ export default function PostForm() {
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
className={
|
className={
|
||||||
'h-20 w-full gap-3 overflow-scroll flex flex-row ' +
|
'h-24 w-full gap-3 overflow-x-auto flex flex-row items-center border border-gray-200 rounded-lg p-2 ' + // Adjusted image preview container
|
||||||
(files_list && files_list.length > 0 ? '' : 'hidden')
|
(files_list && files_list.length > 0 ? '' : 'hidden')
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
@ -110,20 +110,22 @@ export default function PostForm() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* include validation with required or other standard HTML validation rules */}
|
<div className="ml-auto flex items-center gap-3"> {/* Aligned items, added items-center */}
|
||||||
<div className="ml-auto flex gap-3">
|
<label className="w-fit text-lg font-medium text-gray-700 break-keep">署名</label>
|
||||||
<label className="w-fit break-keep">署名</label>
|
|
||||||
<input
|
<input
|
||||||
className="border-b-4 border-zinc-500 w-full text-end outline-none focus:border-zinc-200"
|
className="border-b-2 border-gray-400 w-full text-right outline-none focus:border-gray-600 transition-colors duration-200 p-1" // Modernized signing input
|
||||||
{...register('signing', {
|
{...register('signing', {
|
||||||
maxLength: 20,
|
maxLength: 20,
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/* errors will return when field validation fails */}
|
{errors.content && <span className="text-red-500 text-sm">此欄位為必填</span>} {/* Styled error message */}
|
||||||
{errors.content && <span>This field is required</span>}
|
|
||||||
|
|
||||||
<button type="submit" className="ring-3 w-full mx-auto rounded-md " disabled={posting}>
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="mt-6 w-full py-3 bg-gray-800 text-white font-semibold rounded-lg hover:bg-gray-700 transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed" // Modernized button
|
||||||
|
disabled={posting}
|
||||||
|
>
|
||||||
發送
|
發送
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
80
src/Zone.tsx
80
src/Zone.tsx
|
@ -2,21 +2,81 @@ import { useState } from 'react'
|
||||||
|
|
||||||
export const Zone = ({ setZone }: { setZone: (zone_name: string) => void }) => {
|
export const Zone = ({ setZone }: { setZone: (zone_name: string) => void }) => {
|
||||||
const [toggle, setToggle] = useState(false)
|
const [toggle, setToggle] = useState(false)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
{/* Backdrop with fade animation */}
|
||||||
|
<div
|
||||||
|
className={`fixed inset-0 z-10 backdrop-blur-sm transition-all duration-300 ${
|
||||||
|
toggle
|
||||||
|
? 'bg-black/50 opacity-100 pointer-events-auto'
|
||||||
|
: 'bg-black/0 opacity-0 pointer-events-none'
|
||||||
|
}`}
|
||||||
|
onClick={() => setToggle(false)}
|
||||||
|
/>
|
||||||
|
|
||||||
{toggle && <div className="w-full fixed h-dvh top-0 left-0 z-10 backdrop-brightness-50" >
|
{/* Main drawer */}
|
||||||
</div>}
|
<div className={`
|
||||||
|
fixed bottom-0 left-1/2 -translate-x-1/2 z-20
|
||||||
|
w-[min(80dvw,400px)] bg-zinc-800
|
||||||
|
rounded-t-3xl shadow-2xl border-t border-zinc-600
|
||||||
|
transition-all duration-500 ease-out
|
||||||
|
${toggle ? 'h-[45dvh]' : 'h-16'}
|
||||||
|
`}>
|
||||||
|
|
||||||
<div className={"fixed bottom-0 left-1/2 -translate-x-1/2 z-20 w-[80dvw] bg-zinc-700 rounded transition-all duration-300" + (toggle ? " h-[40dvh]" : "h-fit")}>
|
{/* Drawer handle/button */}
|
||||||
<button onClick={() => setToggle(!toggle)}>Drawer</button>
|
<div className="relative">
|
||||||
{toggle && <div className="h-[40dvh] w-full flex flex-col pt-10 gap-8 text-xl">
|
{/* Visual handle indicator */}
|
||||||
<button onClick={() => { setZone("post"); setToggle(false) }}>PO文</button>
|
<div className="w-12 h-1 bg-zinc-500 rounded-full mx-auto mt-3 mb-2" />
|
||||||
<button onClick={() => { setZone("delete"); setToggle(false) }}>刪文</button>
|
|
||||||
<button onClick={() => { setZone("view"); setToggle(false) }}>查看</button>
|
<button
|
||||||
</div>}
|
onClick={() => setToggle(!toggle)}
|
||||||
|
className="w-full py-3 px-6 text-white font-medium text-lg
|
||||||
|
hover:bg-zinc-700 transition-colors duration-200
|
||||||
|
flex items-center justify-center gap-2"
|
||||||
|
>
|
||||||
|
<span>選擇功能</span>
|
||||||
|
<span className={`transition-transform duration-300 ${toggle ? 'rotate-180' : ''}`}>
|
||||||
|
▲
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Menu content with staggered animations */}
|
||||||
|
<div className={`
|
||||||
|
px-6 pb-6 overflow-hidden
|
||||||
|
transition-all duration-500 ease-out
|
||||||
|
${toggle ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-4 pointer-events-none'}
|
||||||
|
`}>
|
||||||
|
<div className="grid gap-4 pt-4">
|
||||||
|
{[
|
||||||
|
{ key: 'post', label: 'PO文', icon: '✏️', delay: '100ms' },
|
||||||
|
{ key: 'delete', label: '刪文', icon: '🗑️', delay: '200ms' },
|
||||||
|
{ key: 'view', label: '查看', icon: '👁️', delay: '300ms' }
|
||||||
|
].map(({ key, label, icon, delay }) => (
|
||||||
|
<button
|
||||||
|
key={key}
|
||||||
|
onClick={() => { setZone(key); setToggle(false) }}
|
||||||
|
className={`
|
||||||
|
w-full py-4 px-6 bg-zinc-700 hover:bg-zinc-600
|
||||||
|
rounded-xl text-white text-lg font-medium
|
||||||
|
flex items-center gap-4
|
||||||
|
transform transition-all duration-300 ease-out
|
||||||
|
hover:scale-105 hover:shadow-lg
|
||||||
|
active:scale-95
|
||||||
|
${toggle ? 'translate-y-0 opacity-100' : 'translate-y-4 opacity-0'}
|
||||||
|
`}
|
||||||
|
style={{
|
||||||
|
transitionDelay: toggle ? delay : '0ms'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span className="text-2xl">{icon}</span>
|
||||||
|
<span>{label}</span>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
import { z } from 'zod/v4'
|
import { z } from 'zod/v4'
|
||||||
let rooturl = 'http://10.16.20.19:3000'
|
let rooturl = 'http://172.16.20.145:3000'
|
||||||
|
|
||||||
type Inputs = {
|
type Inputs = {
|
||||||
content: string
|
content: string
|
||||||
|
|
Loading…
Add table
Reference in a new issue