feat: implement AutoTextarea component for dynamic height adjustment in text areas
This commit is contained in:
@@ -4,6 +4,7 @@ import { useState, useEffect } from 'react';
|
|||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { useParams } from 'next/navigation';
|
import { useParams } from 'next/navigation';
|
||||||
import { ArrowLeftIcon, CalendarIcon, HeartIcon, RepeatIcon, MessageIcon, FlagIcon } from '@/components/Icons';
|
import { ArrowLeftIcon, CalendarIcon, HeartIcon, RepeatIcon, MessageIcon, FlagIcon } from '@/components/Icons';
|
||||||
|
import AutoTextarea from '@/components/AutoTextarea';
|
||||||
|
|
||||||
interface User {
|
interface User {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -481,7 +482,7 @@ export default function ProfilePage() {
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label style={{ fontSize: '12px', color: 'var(--foreground-tertiary)' }}>Bio</label>
|
<label style={{ fontSize: '12px', color: 'var(--foreground-tertiary)' }}>Bio</label>
|
||||||
<textarea
|
<AutoTextarea
|
||||||
className="input"
|
className="input"
|
||||||
value={profileForm.bio}
|
value={profileForm.bio}
|
||||||
onChange={(e) => setProfileForm({ ...profileForm, bio: e.target.value })}
|
onChange={(e) => setProfileForm({ ...profileForm, bio: e.target.value })}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
import AutoTextarea from '@/components/AutoTextarea';
|
||||||
|
|
||||||
type AdminUser = {
|
type AdminUser = {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -494,7 +495,7 @@ export default function AdminPage() {
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label style={{ fontSize: '13px', fontWeight: 500, marginBottom: '4px', display: 'block' }}>Short Description</label>
|
<label style={{ fontSize: '13px', fontWeight: 500, marginBottom: '4px', display: 'block' }}>Short Description</label>
|
||||||
<textarea
|
<AutoTextarea
|
||||||
className="input"
|
className="input"
|
||||||
value={nodeSettings.description}
|
value={nodeSettings.description}
|
||||||
onChange={e => setNodeSettings({ ...nodeSettings, description: e.target.value })}
|
onChange={e => setNodeSettings({ ...nodeSettings, description: e.target.value })}
|
||||||
@@ -554,7 +555,7 @@ export default function AdminPage() {
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label style={{ fontSize: '13px', fontWeight: 500, marginBottom: '4px', display: 'block' }}>Long Description (About)</label>
|
<label style={{ fontSize: '13px', fontWeight: 500, marginBottom: '4px', display: 'block' }}>Long Description (About)</label>
|
||||||
<textarea
|
<AutoTextarea
|
||||||
className="input"
|
className="input"
|
||||||
value={nodeSettings.longDescription}
|
value={nodeSettings.longDescription}
|
||||||
onChange={e => setNodeSettings({ ...nodeSettings, longDescription: e.target.value })}
|
onChange={e => setNodeSettings({ ...nodeSettings, longDescription: e.target.value })}
|
||||||
@@ -565,7 +566,7 @@ export default function AdminPage() {
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label style={{ fontSize: '13px', fontWeight: 500, marginBottom: '4px', display: 'block' }}>Rules</label>
|
<label style={{ fontSize: '13px', fontWeight: 500, marginBottom: '4px', display: 'block' }}>Rules</label>
|
||||||
<textarea
|
<AutoTextarea
|
||||||
className="input"
|
className="input"
|
||||||
value={nodeSettings.rules}
|
value={nodeSettings.rules}
|
||||||
onChange={e => setNodeSettings({ ...nodeSettings, rules: e.target.value })}
|
onChange={e => setNodeSettings({ ...nodeSettings, rules: e.target.value })}
|
||||||
|
|||||||
@@ -1312,3 +1312,23 @@ a.btn-primary:visited {
|
|||||||
-webkit-line-clamp: 2;
|
-webkit-line-clamp: 2;
|
||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Scrollbar Styling */
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
background: var(--background);
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background: var(--border);
|
||||||
|
border-radius: var(--radius-full);
|
||||||
|
border: 2px solid var(--background); /* Creates a padding effect */
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: var(--border-hover);
|
||||||
|
}
|
||||||
+2
-1
@@ -3,6 +3,7 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { useAuth } from '@/lib/contexts/AuthContext';
|
import { useAuth } from '@/lib/contexts/AuthContext';
|
||||||
|
import AutoTextarea from '@/components/AutoTextarea';
|
||||||
|
|
||||||
interface User {
|
interface User {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -260,7 +261,7 @@ function Compose({ onPost }: { onPost: (content: string, mediaIds: string[]) =>
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="compose">
|
<div className="compose">
|
||||||
<textarea
|
<AutoTextarea
|
||||||
className="compose-input"
|
className="compose-input"
|
||||||
placeholder="What's happening?"
|
placeholder="What's happening?"
|
||||||
value={content}
|
value={content}
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useEffect, useRef, TextareaHTMLAttributes } from 'react';
|
||||||
|
|
||||||
|
export default function AutoTextarea(props: TextareaHTMLAttributes<HTMLTextAreaElement>) {
|
||||||
|
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||||
|
|
||||||
|
const adjustHeight = () => {
|
||||||
|
const textarea = textareaRef.current;
|
||||||
|
if (textarea) {
|
||||||
|
textarea.style.height = 'auto';
|
||||||
|
textarea.style.height = `${textarea.scrollHeight}px`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
adjustHeight();
|
||||||
|
}, [props.value]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<textarea
|
||||||
|
{...props}
|
||||||
|
ref={textareaRef}
|
||||||
|
// Ensure 1 row minimum and hide scrollbars to prevent jitter
|
||||||
|
rows={props.rows || 1}
|
||||||
|
onInput={(e) => {
|
||||||
|
adjustHeight();
|
||||||
|
props.onInput?.(e);
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
...props.style,
|
||||||
|
resize: 'none',
|
||||||
|
overflow: 'hidden',
|
||||||
|
minHeight: 'auto', // Override specific min-heights if they conflict
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user