feat: enhance Messages and ThreadChat components with improved textarea handling and styling
This commit is contained in:
parent
49d6b242d4
commit
3ad1cb3c58
|
@ -1,30 +1,71 @@
|
|||
import React, { useState } from "react";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { getAvatarColor } from "../../../modules/collaboration/functions/getAvatarColor";
|
||||
import { KebabIcon } from "../../icons/ExportCommonIcons";
|
||||
import { adjustHeight } from "./function/textAreaHeightAdjust";
|
||||
|
||||
interface MessageProps {
|
||||
val: Reply;
|
||||
val: Reply | CommentSchema;
|
||||
i: number;
|
||||
}
|
||||
|
||||
const Messages: React.FC<MessageProps> = ({ val, i }) => {
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const [openOptions, setOpenOptions] = useState(false);
|
||||
|
||||
// input
|
||||
const [value, setValue] = useState<string>(
|
||||
"reply" in val ? val.reply : val.comment
|
||||
);
|
||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
const currentUser = "1";
|
||||
|
||||
const UserName = "username";
|
||||
|
||||
useEffect(() => {
|
||||
if (textareaRef.current) adjustHeight(textareaRef.current);
|
||||
}, [value]);
|
||||
|
||||
function handleCancelAction() {
|
||||
setIsEditing(false);
|
||||
}
|
||||
|
||||
function handleSaveAction() {
|
||||
setIsEditing(false);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{isEditing ? (
|
||||
<div className="edit-container">
|
||||
<div className="input-container">
|
||||
<textarea />
|
||||
<textarea
|
||||
placeholder="type here"
|
||||
ref={textareaRef}
|
||||
autoFocus
|
||||
value={value}
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
style={{ resize: "none" }}
|
||||
/>
|
||||
</div>
|
||||
<div className="actions-container">
|
||||
<div className="options"></div>
|
||||
<div className="actions">
|
||||
<button className="cancel-button">Cancel</button>
|
||||
<button className="save-button">Save</button>
|
||||
<button
|
||||
className="cancel-button"
|
||||
onClick={() => {
|
||||
handleCancelAction();
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
className="save-button"
|
||||
onClick={() => {
|
||||
handleSaveAction();
|
||||
}}
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -66,7 +107,9 @@ const Messages: React.FC<MessageProps> = ({ val, i }) => {
|
|||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className="message">{val.reply}</div>
|
||||
<div className="message">
|
||||
{"reply" in val ? val.reply : val.comment}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
@ -1,37 +1,49 @@
|
|||
import React, { useState } from "react";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { CloseIcon, KebabIcon } from "../../icons/ExportCommonIcons";
|
||||
import Messages from "./Messages";
|
||||
import { ExpandIcon } from "../../icons/SimulationIcons";
|
||||
import { adjustHeight } from "./function/textAreaHeightAdjust";
|
||||
|
||||
const ThreadChat: React.FC = () => {
|
||||
const [openThreadOptions, setOpenThreadOptions] = useState(false);
|
||||
|
||||
const [inputActive, setInputActive] = useState(false);
|
||||
|
||||
const [value, setValue] = useState<string>("");
|
||||
|
||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
|
||||
const messages = [
|
||||
{
|
||||
replyId: "user 1",
|
||||
creatorId: "1",
|
||||
createdAt: "hello, thread check",
|
||||
lastUpdatedAt: "2 hrs ago",
|
||||
reply: "true",
|
||||
reply: "reply 1",
|
||||
},
|
||||
{
|
||||
replyId: "user 2",
|
||||
creatorId: "2",
|
||||
createdAt: "hello, thread check",
|
||||
lastUpdatedAt: "2 hrs ago",
|
||||
reply: "true",
|
||||
reply: "reply 2",
|
||||
},
|
||||
];
|
||||
|
||||
useEffect(() => {
|
||||
if (textareaRef.current) adjustHeight(textareaRef.current);
|
||||
}, [value]);
|
||||
|
||||
return (
|
||||
<div className="thread-char-wrapper">
|
||||
<div className="thread-char-container">
|
||||
<div className="thread-chat-wrapper">
|
||||
<div className="thread-chat-container">
|
||||
<div className="header-wrapper">
|
||||
<div className="header">Comment</div>
|
||||
<div className="header-options">
|
||||
<button
|
||||
className="options-button"
|
||||
onClick={() => {
|
||||
setOpenThreadOptions(true);
|
||||
setOpenThreadOptions(!openThreadOptions);
|
||||
}}
|
||||
>
|
||||
<KebabIcon />
|
||||
|
@ -40,7 +52,7 @@ const ThreadChat: React.FC = () => {
|
|||
<div className="options-list">
|
||||
<div className="options">Mark as Unread</div>
|
||||
<div className="options">Mark as Resolved</div>
|
||||
<div className="options">Delete Thread</div>
|
||||
<div className="options delete">Delete Thread</div>
|
||||
</div>
|
||||
)}
|
||||
<button className="close-button">
|
||||
|
@ -54,9 +66,21 @@ const ThreadChat: React.FC = () => {
|
|||
))}
|
||||
</div>
|
||||
<div className="send-message-wrapper">
|
||||
<div className="input-container">
|
||||
<input type="text" />
|
||||
<div className="sent-button">
|
||||
<div className={`input-container ${inputActive ? "active" : ""}`}>
|
||||
<textarea
|
||||
placeholder="type something"
|
||||
ref={textareaRef}
|
||||
value={value}
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
onFocus={() => setInputActive(true)}
|
||||
onBlur={() => setInputActive(false)}
|
||||
style={{ resize: "none" }}
|
||||
/>
|
||||
<div
|
||||
className={`sent-button ${
|
||||
value === "" ? "disable-send-btn" : ""
|
||||
}`}
|
||||
>
|
||||
<ExpandIcon />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
export const adjustHeight = (textareaRef: HTMLTextAreaElement) => {
|
||||
const el = textareaRef;
|
||||
if (el) {
|
||||
el.style.height = "auto";
|
||||
el.style.height = "38px";
|
||||
|
||||
// Clamp to max height for 6 lines
|
||||
const lineHeight = 18; // px, adjust if needed
|
||||
const maxHeight = lineHeight * 6;
|
||||
if (el.scrollHeight > maxHeight) {
|
||||
el.style.overflowY = "auto";
|
||||
el.style.height = `${maxHeight}px`;
|
||||
} else {
|
||||
el.style.overflowY = "hidden";
|
||||
}
|
||||
}
|
||||
};
|
|
@ -5,7 +5,6 @@ import Controls from '../controls/controls';
|
|||
import { Environment } from '@react-three/drei'
|
||||
|
||||
import background from "../../../assets/textures/hdr/mudroadpuresky2k.hdr";
|
||||
import { MovingClouds } from '../clouds/clouds';
|
||||
|
||||
function Setup() {
|
||||
return (
|
||||
|
@ -18,7 +17,7 @@ function Setup() {
|
|||
|
||||
<PostProcessing />
|
||||
|
||||
<MovingClouds />
|
||||
{/* <MovingClouds /> */}
|
||||
|
||||
<Environment files={background} environmentIntensity={1.5} />
|
||||
</>
|
||||
|
|
|
@ -183,7 +183,7 @@ const Project: React.FC = () => {
|
|||
</>
|
||||
)}
|
||||
<VersionSaved />
|
||||
{/* <ThreadChat /> */}
|
||||
<ThreadChat />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
|
||||
// global input style
|
||||
|
||||
input {
|
||||
input,
|
||||
textarea {
|
||||
width: 100%;
|
||||
padding: 4px 8px;
|
||||
border-radius: #{$border-radius-large};
|
||||
|
|
|
@ -93,36 +93,86 @@
|
|||
}
|
||||
}
|
||||
|
||||
.thread-char-wrapper {
|
||||
.thread-chat-wrapper {
|
||||
position: absolute;
|
||||
// remove later
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
// ----
|
||||
z-index: 10000;
|
||||
.thread-char-container {
|
||||
z-index: #{$z-index-ui-highest};
|
||||
.thread-chat-container {
|
||||
background: var(--background-color);
|
||||
backdrop-filter: blur(14px);
|
||||
border-radius: #{$border-radius-extra-large};
|
||||
width: 20rem;
|
||||
.header-wrapper {
|
||||
.header {
|
||||
}
|
||||
padding: 12px;
|
||||
@include flex-space-between;
|
||||
.header-options {
|
||||
.options-button {
|
||||
}
|
||||
@include flex-center;
|
||||
position: relative;
|
||||
.options-list {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
transform: translate(-24px, 100%);
|
||||
background: var(--background-color);
|
||||
padding: 8px 4px;
|
||||
border-radius: #{$border-radius-medium};
|
||||
backdrop-filter: blur(10px);
|
||||
.options {
|
||||
text-wrap: nowrap;
|
||||
padding: 2px 4px;
|
||||
border-radius: #{$border-radius-medium};
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background: var(--background-color-accent);
|
||||
}
|
||||
&.delete {
|
||||
&:hover {
|
||||
color: var(--log-error-text-color);
|
||||
background: var(--log-error-background-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.options-button,
|
||||
.close-button {
|
||||
@include flex-center;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
border-radius: #{$border-radius-medium};
|
||||
&:hover {
|
||||
background: var(--background-color-solid);
|
||||
}
|
||||
}
|
||||
.close-button {
|
||||
svg {
|
||||
scale: 1.4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.messages-wrapper {
|
||||
padding: 12px;
|
||||
padding-top: 0;
|
||||
.edit-container {
|
||||
.input-container {
|
||||
}
|
||||
.actions-container {
|
||||
@include flex-space-between;
|
||||
width: 100%;
|
||||
margin: 8px 0;
|
||||
.actions {
|
||||
.cancel-button {
|
||||
@include flex-center;
|
||||
gap: 4px;
|
||||
.cancel-button,
|
||||
.save-button {
|
||||
padding: 4px 6px;
|
||||
border-radius: #{$border-radius-large};
|
||||
background: var(--background-color-solid);
|
||||
outline: 1px solid var(--border-color);
|
||||
}
|
||||
.save-button {
|
||||
}
|
||||
|
@ -151,8 +201,47 @@
|
|||
}
|
||||
}
|
||||
.send-message-wrapper {
|
||||
padding: 12px;
|
||||
padding-top: 8px;
|
||||
.input-container {
|
||||
position: relative;
|
||||
@include flex-space-between;
|
||||
background: var(--background-color);
|
||||
border-radius: #{$border-radius-extra-large};
|
||||
outline: 1px solid var(--border-color);
|
||||
textarea {
|
||||
background: transparent;
|
||||
outline: none;
|
||||
width: calc(100% - 36px);
|
||||
overflow: hidden;
|
||||
line-height: 28px;
|
||||
max-height: 108px;
|
||||
}
|
||||
.sent-button {
|
||||
position: absolute;
|
||||
right: 2px;
|
||||
bottom: 2px;
|
||||
@include flex-center;
|
||||
padding: 2px;
|
||||
svg {
|
||||
rotate: 45deg;
|
||||
}
|
||||
}
|
||||
.disable-send-btn {
|
||||
filter: saturate(0);
|
||||
}
|
||||
&.active {
|
||||
background: var(--background-color-solid);
|
||||
padding-top: 4px;
|
||||
flex-direction: column;
|
||||
align-items: end;
|
||||
textarea {
|
||||
width: 100%;
|
||||
line-height: 18px;
|
||||
}
|
||||
.sent-button {
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue