Enhanced mobile version UI UX
This commit is contained in:
+42
-4
@@ -174,11 +174,24 @@ function PromptToast(props: {
|
|||||||
|
|
||||||
function useSubmitHandler() {
|
function useSubmitHandler() {
|
||||||
const config = useAppConfig();
|
const config = useAppConfig();
|
||||||
|
const isMobileScreen = useMobileScreen();
|
||||||
const submitKey = config.submitKey;
|
const submitKey = config.submitKey;
|
||||||
|
const mobileSubmitKey = config.mobileSubmitKey;
|
||||||
|
|
||||||
const shouldSubmit = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
const shouldSubmit = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
||||||
if (e.key !== "Enter") return false;
|
if (e.key !== "Enter") return false;
|
||||||
if (e.key === "Enter" && e.nativeEvent.isComposing) return false;
|
if (e.key === "Enter" && e.nativeEvent.isComposing) return false;
|
||||||
|
if (isMobileScreen) return (
|
||||||
|
(mobileSubmitKey === SubmitKey.AltEnter && e.altKey) ||
|
||||||
|
(mobileSubmitKey === SubmitKey.CtrlEnter && e.ctrlKey) ||
|
||||||
|
(mobileSubmitKey === SubmitKey.ShiftEnter && e.shiftKey) ||
|
||||||
|
(mobileSubmitKey === SubmitKey.MetaEnter && e.metaKey) ||
|
||||||
|
(mobileSubmitKey === SubmitKey.Enter &&
|
||||||
|
!e.altKey &&
|
||||||
|
!e.ctrlKey &&
|
||||||
|
!e.shiftKey &&
|
||||||
|
!e.metaKey)
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
(submitKey === SubmitKey.AltEnter && e.altKey) ||
|
(submitKey === SubmitKey.AltEnter && e.altKey) ||
|
||||||
(submitKey === SubmitKey.CtrlEnter && e.ctrlKey) ||
|
(submitKey === SubmitKey.CtrlEnter && e.ctrlKey) ||
|
||||||
@@ -316,9 +329,31 @@ function ChatAction(props: {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// useEffect(() => {
|
||||||
|
// // updateWidth();
|
||||||
|
// setTimeout(updateWidth, 600); // 手机优化,给DOM元素一些加载的时间
|
||||||
|
// }, []);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// updateWidth();
|
let resizeObserver: ResizeObserver;
|
||||||
setTimeout(updateWidth, 600); // 手机优化,给DOM元素一些加载的时间
|
if (window.ResizeObserver) {
|
||||||
|
resizeObserver = new ResizeObserver(() => {
|
||||||
|
updateWidth();
|
||||||
|
});
|
||||||
|
if (iconRef.current && textRef.current) {
|
||||||
|
resizeObserver.observe(iconRef.current);
|
||||||
|
resizeObserver.observe(textRef.current);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setTimeout(updateWidth, 600); // 增加600ms延迟,给DOM元素一些加载的时间
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (resizeObserver) {
|
||||||
|
if (iconRef.current) resizeObserver.unobserve(iconRef.current);
|
||||||
|
if (textRef.current) resizeObserver.unobserve(textRef.current);
|
||||||
|
resizeObserver.disconnect();
|
||||||
|
}
|
||||||
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -751,10 +786,11 @@ export function Chat() {
|
|||||||
>
|
>
|
||||||
{!session.topic ? DEFAULT_TOPIC : session.topic}
|
{!session.topic ? DEFAULT_TOPIC : session.topic}
|
||||||
</div>
|
</div>
|
||||||
|
{!isMobileScreen && (
|
||||||
<div className="window-header-sub-title">
|
<div className="window-header-sub-title">
|
||||||
{Locale.Chat.SubTitle(session.messages.length)}
|
{Locale.Chat.SubTitle(session.messages.length)}
|
||||||
</div>
|
</div>
|
||||||
{/* todo */}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="window-actions">
|
<div className="window-actions">
|
||||||
<div className={"window-action-button" + " " + styles.mobile}>
|
<div className={"window-action-button" + " " + styles.mobile}>
|
||||||
@@ -765,6 +801,7 @@ export function Chat() {
|
|||||||
onClick={() => navigate(Path.Home)}
|
onClick={() => navigate(Path.Home)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
{!isMobileScreen && (
|
||||||
<div className="window-action-button">
|
<div className="window-action-button">
|
||||||
<IconButton
|
<IconButton
|
||||||
icon={<RenameIcon />}
|
icon={<RenameIcon />}
|
||||||
@@ -772,6 +809,7 @@ export function Chat() {
|
|||||||
onClick={renameSession}
|
onClick={renameSession}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
<div className="window-action-button">
|
<div className="window-action-button">
|
||||||
<IconButton
|
<IconButton
|
||||||
icon={<ExportIcon />}
|
icon={<ExportIcon />}
|
||||||
@@ -936,7 +974,7 @@ export function Chat() {
|
|||||||
<textarea
|
<textarea
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
className={styles["chat-input"]}
|
className={styles["chat-input"]}
|
||||||
placeholder={Locale.Chat.Input(submitKey)}
|
placeholder={isMobileScreen ? '' : Locale.Chat.Input(submitKey)}
|
||||||
onInput={(e) => onInput(e.currentTarget.value)}
|
onInput={(e) => onInput(e.currentTarget.value)}
|
||||||
value={userInput}
|
value={userInput}
|
||||||
onKeyDown={onInputKeyDown}
|
onKeyDown={onInputKeyDown}
|
||||||
|
|||||||
@@ -183,42 +183,55 @@
|
|||||||
animation: slide-in ease 0.3s;
|
animation: slide-in ease 0.3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-item-rename {
|
.chat-item-rename,
|
||||||
position: absolute;
|
|
||||||
top: 10px;
|
|
||||||
right: 30px;
|
|
||||||
transition: all ease 0.3s;
|
|
||||||
opacity: 0;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-item:hover > .chat-item-rename {
|
|
||||||
opacity: 0.5;
|
|
||||||
transform: translateX(-10px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-item:hover > .chat-item-rename:hover {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-item-delete {
|
.chat-item-delete {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 10px;
|
top: 10px;
|
||||||
right: 0;
|
|
||||||
transition: all ease 0.3s;
|
transition: all ease 0.3s;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.chat-item-rename {
|
||||||
|
right: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-item-delete {
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-item:hover > .chat-item-rename,
|
||||||
.chat-item:hover > .chat-item-delete {
|
.chat-item:hover > .chat-item-delete {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
transform: translateX(-10px);
|
transform: translateX(-10px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.chat-item:hover > .chat-item-rename:hover,
|
||||||
.chat-item:hover > .chat-item-delete:hover {
|
.chat-item:hover > .chat-item-delete:hover {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.chat-item-rename,
|
||||||
|
.chat-item-delete {
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-item-rename {
|
||||||
|
right: 35px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-item-delete {
|
||||||
|
right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-item:hover > .chat-item-rename,
|
||||||
|
.chat-item:hover > .chat-item-delete {
|
||||||
|
opacity: 0.5;
|
||||||
|
transform: translateX(0px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.chat-item-info {
|
.chat-item-info {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
@@ -301,25 +314,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-item-rename {
|
.chat-item-rename,
|
||||||
top: 15px;
|
|
||||||
right: 35px;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-item:hover > .chat-item-rename {
|
|
||||||
opacity: 0.5;
|
|
||||||
right: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-item-delete {
|
.chat-item-delete {
|
||||||
top: 15px;
|
top: 15px;
|
||||||
opacity: 1; // 手机模式下始终显示
|
transition: all ease 0.3s;
|
||||||
}
|
|
||||||
|
|
||||||
.chat-item:hover > .chat-item-delete {
|
|
||||||
opacity: 0.5;
|
|
||||||
right: 5px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-tail {
|
.sidebar-tail {
|
||||||
@@ -580,12 +578,33 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (max-width: 600px) {
|
@media only screen and (max-width: 600px) {
|
||||||
|
.chat-input-panel-inner {
|
||||||
|
justify-content: space-between; // 将子元素分开
|
||||||
|
align-items: center; // 使得子元素在一条平面上
|
||||||
|
}
|
||||||
|
|
||||||
.chat-input {
|
.chat-input {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
|
height: 20px; // 设置发送框的高度
|
||||||
|
width: calc(100% - 58px); // 给出了 58px 的空间给发送按钮
|
||||||
|
padding: 10px 0px 10px 10px; // 调整padding使得文字显示位置更符合人类审美
|
||||||
|
overflow-y: auto; // 只有滚动的时候才显示滚动条
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
&::-webkit-scrollbar-thumb {
|
||||||
|
background: #888;
|
||||||
|
}
|
||||||
|
&::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: #555;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-input-send {
|
.chat-input-send {
|
||||||
bottom: 30px;
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
margin-left: 10px; // 设置了10px的间隔
|
||||||
|
position: static; // 移除了定位属性
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export enum Theme {
|
|||||||
export const DEFAULT_CONFIG = {
|
export const DEFAULT_CONFIG = {
|
||||||
// submitKey: SubmitKey.CtrlEnter as SubmitKey,
|
// submitKey: SubmitKey.CtrlEnter as SubmitKey,
|
||||||
submitKey: SubmitKey.Enter,
|
submitKey: SubmitKey.Enter,
|
||||||
|
mobileSubmitKey: SubmitKey.CtrlEnter,
|
||||||
avatar: "1f603",
|
avatar: "1f603",
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
theme: Theme.Auto as Theme,
|
theme: Theme.Auto as Theme,
|
||||||
|
|||||||
@@ -34,4 +34,9 @@
|
|||||||
|
|
||||||
.window-action-button {
|
.window-action-button {
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
// width: 20px;
|
||||||
|
// height: 20px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user