Show four blog cards per row and let editors crop thumbnails as 1:1 or 3:2.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
c962fe524b
commit
d9bdfbbb12
@@ -1200,6 +1200,7 @@ const en = {
|
||||
'blog.form.uploadTitleImage': 'Upload title image',
|
||||
'blog.form.changeTitleImage': 'Change title image',
|
||||
'blog.form.cropHint': 'Click to select, then crop',
|
||||
'blog.form.aspectAria': 'Title image aspect ratio',
|
||||
'blog.form.type': 'Type',
|
||||
'blog.form.category': 'Category',
|
||||
'blog.form.categoryPlaceholder': 'Search and select category...',
|
||||
@@ -3512,6 +3513,7 @@ const fa: Record<MessageKey, string> = {
|
||||
'blog.form.uploadTitleImage': 'آپلود تصویر عنوان',
|
||||
'blog.form.changeTitleImage': 'تغییر تصویر عنوان',
|
||||
'blog.form.cropHint': 'برای انتخاب کلیک کنید، سپس برش دهید',
|
||||
'blog.form.aspectAria': 'نسبت تصویر عنوان',
|
||||
'blog.form.type': 'نوع',
|
||||
'blog.form.category': 'دستهبندی',
|
||||
'blog.form.categoryPlaceholder': 'جستجو و انتخاب دستهبندی...',
|
||||
|
||||
@@ -29,6 +29,13 @@ import { flattenCategories } from '../utils/categories'
|
||||
import pageStyles from '../components/PageContent.module.css'
|
||||
import styles from './AddNewProductPage.module.css'
|
||||
|
||||
type TitleImageAspectId = '1:1' | '3:2'
|
||||
|
||||
const TITLE_IMAGE_ASPECTS: { id: TitleImageAspectId; aspect: number }[] = [
|
||||
{ id: '1:1', aspect: 1 },
|
||||
{ id: '3:2', aspect: 3 / 2 },
|
||||
]
|
||||
|
||||
export function AddNewBlogPage() {
|
||||
const { id } = useParams()
|
||||
const navigate = useNavigate()
|
||||
@@ -43,6 +50,7 @@ export function AddNewBlogPage() {
|
||||
const [abstract, setAbstract] = useState('')
|
||||
const [mainTextHtml, setMainTextHtml] = useState('')
|
||||
const [titleImage, setTitleImage] = useState<string | null>(null)
|
||||
const [titleImageAspect, setTitleImageAspect] = useState<TitleImageAspectId>('3:2')
|
||||
const [tags, setTags] = useState<string[]>([])
|
||||
const [featuredMediaId, setFeaturedMediaId] = useState<string | null>(null)
|
||||
const [loaded, setLoaded] = useState(!isEdit)
|
||||
@@ -50,6 +58,8 @@ export function AddNewBlogPage() {
|
||||
const [error, setError] = useState('')
|
||||
|
||||
const categoryOptions = flattenCategories(categories)
|
||||
const selectedAspect =
|
||||
TITLE_IMAGE_ASPECTS.find((option) => option.id === titleImageAspect)?.aspect ?? 3 / 2
|
||||
|
||||
useEffect(() => {
|
||||
const controller = new AbortController()
|
||||
@@ -190,16 +200,46 @@ export function AddNewBlogPage() {
|
||||
|
||||
<form className={styles.form} onSubmit={handleSubmit}>
|
||||
<div className={styles.formGrid}>
|
||||
<div className={`${styles.field} ${styles.col3} ${styles.rowSpan3}`}>
|
||||
<div className={`${styles.field} ${styles.col3} ${styles.rowSpan3} ${styles.thumbnailField}`}>
|
||||
<label>{t('blog.form.titleImage')}</label>
|
||||
<ImageCropper
|
||||
value={titleImage}
|
||||
onChange={setTitleImage}
|
||||
aspect={3 / 2}
|
||||
aspect={selectedAspect}
|
||||
uploadLabel={t('blog.form.uploadTitleImage')}
|
||||
hint={t('blog.form.cropHint')}
|
||||
changeLabel={t('blog.form.changeTitleImage')}
|
||||
/>
|
||||
<div
|
||||
className={styles.aspectSwitch}
|
||||
role="radiogroup"
|
||||
aria-label={t('blog.form.aspectAria')}
|
||||
>
|
||||
{TITLE_IMAGE_ASPECTS.map((option) => (
|
||||
<button
|
||||
key={option.id}
|
||||
type="button"
|
||||
role="radio"
|
||||
aria-checked={titleImageAspect === option.id}
|
||||
className={`${styles.aspectSwitchBtn} ${
|
||||
titleImageAspect === option.id ? styles.aspectSwitchBtnSelected : ''
|
||||
}`}
|
||||
onClick={() => {
|
||||
if (option.id === titleImageAspect) return
|
||||
setTitleImageAspect(option.id)
|
||||
// Applied crop is locked to the old ratio — drop it so the user recrops.
|
||||
// An in-progress crop stays; ImageCropper updates via the aspect prop.
|
||||
if (titleImage) {
|
||||
setTitleImage(null)
|
||||
setFeaturedMediaId(null)
|
||||
}
|
||||
}}
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
{option.id}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={`${styles.field} ${styles.col3}`}>
|
||||
|
||||
@@ -283,6 +283,49 @@
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.aspectSwitch {
|
||||
display: inline-flex;
|
||||
align-self: center;
|
||||
margin-top: 4px;
|
||||
padding: 3px;
|
||||
gap: 2px;
|
||||
background: rgba(148, 163, 184, 0.14);
|
||||
border: 1px solid rgba(148, 163, 184, 0.28);
|
||||
border-radius: 50px;
|
||||
}
|
||||
|
||||
.aspectSwitchBtn {
|
||||
min-width: 52px;
|
||||
padding: 6px 12px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
line-height: 1.2;
|
||||
font-family: var(--font-en), var(--font-ui);
|
||||
color: var(--text-secondary);
|
||||
background: transparent;
|
||||
border-radius: 50px;
|
||||
transition: background 0.15s, color 0.15s, box-shadow 0.15s;
|
||||
}
|
||||
|
||||
.aspectSwitchBtn:hover:not(:disabled) {
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.aspectSwitchBtn:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.aspectSwitchBtnSelected {
|
||||
background: var(--primary);
|
||||
color: #fff;
|
||||
box-shadow: 0 1px 3px rgba(var(--primary-rgb) / 0.35);
|
||||
}
|
||||
|
||||
.aspectSwitchBtnSelected:hover:not(:disabled) {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.videoEmbedRow {
|
||||
grid-column: span 12;
|
||||
display: grid;
|
||||
|
||||
@@ -190,7 +190,7 @@ export function BlogListPage() {
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<div className={pageStyles.grid}>
|
||||
<div className={pageStyles.gridCols4}>
|
||||
{blogs.map((blog) => (
|
||||
<BlogCard
|
||||
key={blog.id}
|
||||
|
||||
Reference in New Issue
Block a user