Skip to content

Commit

Permalink
Resize article input box automatically
Browse files Browse the repository at this point in the history
Manually expanding the input box is no longer needed and the vertical resize handle is now useless (it can’t be dragged), thus the `resize` property is reverted to `none`.
  • Loading branch information
graphemecluster committed Dec 15, 2024
1 parent 375a3d4 commit cf51d28
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 26 deletions.
67 changes: 49 additions & 18 deletions src/Components/Main.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useCallback, useEffect, useReducer, useRef, useState } from "react";
import { useCallback, useEffect, useLayoutEffect, useReducer, useRef, useState } from "react";
import { createPortal } from "react-dom";

import styled from "@emotion/styled";
Expand All @@ -23,8 +23,10 @@ const dummyOutput = document.createElement("output");

const ArticleInput = styled.textarea`
line-height: 1.6;
resize: block;
resize: none;
width: 100%;
flex: 1;
overflow: hidden;
`;
const OutputContainer = styled.dialog`
transform: translateY(10%);
Expand Down Expand Up @@ -216,6 +218,37 @@ export default function Main({ evaluateHandlerRef }: { evaluateHandlerRef: Mutab
});
}, []);

const [articleInput, setArticleInput] = useState<HTMLTextAreaElement | null>(null);
useLayoutEffect(() => {
if (!articleInput) return;
const textArea = articleInput;
const container = textArea.parentElement!;
function resizeTextArea() {
const scrollTop = container.scrollTop;
// First measure without a scrollbar
textArea.style.minHeight = "";
textArea.style.flex = "unset";
const computedStyle = getComputedStyle(textArea);
const borderHeight = parseFloat(computedStyle.borderTopWidth) + parseFloat(computedStyle.borderBottomWidth);
textArea.style.minHeight = `max(9em, ${textArea.scrollHeight + borderHeight}px)`;
if (textArea.scrollHeight > textArea.getBoundingClientRect().height) {
// Remeasure if the input doesn’t actually fit due to the addition of scrollbar
textArea.style.minHeight = "";
container.style.overflowY = "scroll";
textArea.style.minHeight = `max(9em, ${textArea.scrollHeight + borderHeight}px)`;
container.style.overflowY = "";
}
textArea.style.flex = "";
container.scrollTop = scrollTop;
}
resizeTextArea();
const resizeObserver = new ResizeObserver(resizeTextArea);
resizeObserver.observe(container);
return () => {
resizeObserver.disconnect();
};
}, [article, articleInput]);

const resetArticle = useCallback(async () => {
if (
!article ||
Expand Down Expand Up @@ -245,7 +278,7 @@ export default function Main({ evaluateHandlerRef }: { evaluateHandlerRef: Mutab
setState={setState}
commonOptions={
<>
<p>
<div>
<label>
<select onChange={useHandle("option", event => event.target.value as Option)} value={option}>
{allOptions.map(([value, label]) => (
Expand Down Expand Up @@ -285,21 +318,19 @@ export default function Main({ evaluateHandlerRef }: { evaluateHandlerRef: Mutab
value="恢復成預設文本"
onClick={resetArticle}
/>
</p>
<p>
<ArticleInput
disabled={option !== "convertArticle"}
placeholder="輸入框"
rows={5}
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
required
onChange={useHandle("article", event => event.target.value)}
value={article}
/>
</p>
</div>
<ArticleInput
ref={setArticleInput}
disabled={option !== "convertArticle"}
placeholder="輸入框"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
required
onChange={useHandle("article", event => event.target.value)}
value={article}
/>
</>
}
evaluateHandlerRef={evaluateHandlerRef}
Expand Down
22 changes: 14 additions & 8 deletions src/Components/SchemaEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -180,11 +180,6 @@ const ParameterErrorHint = styled.p`
font-size: 0.875rem;
color: red;
`;
const Options = styled.form`
flex: 1;
padding: 0 1rem;
overflow-y: auto;
`;
const Divider = styled.div<{ isDragging: boolean }>`
background-color: #c4c6c8;
height: 0.2rem;
Expand Down Expand Up @@ -213,8 +208,19 @@ const DividerShadow = styled.div`
height: 6px;
box-shadow: #ddd 0 -6px 6px -6px inset;
`;
const Options = styled.form`
flex: 1;
display: flex;
flex-direction: column;
gap: 1.17rem;
padding: 1.17rem 1rem;
overflow-y: auto;
`;
const OptionsTitle = styled.h3`
margin: 0;
`;
const OptionsSeparator = styled.hr`
margin: 1rem -1rem;
margin: -0.17rem -1rem 0;
`;
const DropContainer = styled.div<{ isDragging: boolean }>`
position: fixed;
Expand Down Expand Up @@ -836,14 +842,14 @@ export default function SchemaEditor({ state, setState, commonOptions, evaluateH
onTouchStart={event => dividerDrag(event.touches[0])}
/>
<Options ref={setOptionPanel} className="pure-form">
<h3>
<OptionsTitle>
<span>選項</span>
{activeSchema?.parameters.size || activeSchema?.parameters.errors.length ? (
<ResetButton title="恢復成預設值" onClick={resetParameters}>
<FontAwesomeIcon icon={faRotateLeft} size="sm" />
</ResetButton>
) : null}
</h3>
</OptionsTitle>
{activeSchema?.parameters.size ? (
<Parameters>
{activeSchema.parameters.render(parameters =>
Expand Down

0 comments on commit cf51d28

Please sign in to comment.