Его 2019 и многое изменилось.Это очень грубая и хакерская реализация того, как реализован твиттер.Это довольно просто.
Рабочая демонстрационная ссылка
Для людей, которые хотят просто взглянуть на код, перейдите к codesandbox
Примечание : это было сделано очень быстро, чтобы продемонстрировать, что твиттер сделал под капотом.
Редактор в основном состоит из <textarea />
, в который добавляется текст для твитов.Под текстовой областью находится расширяющийся блок div, который зацикливает файлы изображений, выбранные из файловой системы.
Что касается смайликов, то для выбора смайликов используется стандартный смайлик, а для добавления смайликов - простой старый javascript.текущее положение курсора в текстовой области.
Я пропустил сборщик картинок, так как он похож на сборщик изображений, с той лишь разницей, что модал открыт для заполнения картинок из giphy.API от Giphy.может быть легко интегрирован для достижения этой цели.
Ниже перечислены компоненты, написанные
FileInput Component
Компонент FileInput используется в качестве кнопки и средства выбора файлов для выбораизображений.Это равнина <input type="file" />
.Собственные стили скрыты, и отображается пользовательский значок
const FileInput = ({ onChange, children }) => {
const fileRef = useRef();
const onPickFile = event => {
onChange([...event.target.files]);
};
return (
<div
style={{
width: "35px",
height: "35px",
borderRadius: "3px"
}}
onClick={() => fileRef.current.click()}
>
{children}
<input
multiple
ref={fileRef}
onChange={onPickFile}
type="file"
style={{ visibility: "hidden" }}
/>
</div>
);
};
Img Component
Компонент Img отображает изображения, выбранные из входных данных.Он использует URL.createObjectURL для создания временного локального URL-адреса, который можно заполнить внутри тега img.Это предварительный просмотр изображения, который можно увидеть в листе твита под текстовой областью.
const Img = ({ file, onRemove, index }) => {
const [fileUrl, setFileUrl] = useState(null);
useEffect(() => {
if (file) {
setFileUrl(URL.createObjectURL(file));
}
}, [file]);
return fileUrl ? (
<div style={{ position: "relative", maxWidth: "230px", maxHeight: "95px" }}>
<img
style={{
display: "block",
maxWidth: "230px",
maxHeight: "95px",
width: "auto",
height: "auto"
}}
alt="pic"
src={fileUrl}
/>
<div
onClick={() => onRemove(index)}
style={{
position: "absolute",
right: 0,
top: 0,
width: "20px",
height: "20px",
borderRadius: "50%",
background: "black",
color: "white",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
x
</div>
</div>
) : null;
};
Приложение (Tweet Sheet)
Это корневой компонент, который объединяет все вместе.
function App() {
const [text, setText] = useState("");
const [pics, setPics] = useState([]);
const textAreaRef = useRef();
const insertAtPos = value => {
const { current: taRef } = textAreaRef;
let startPos = taRef.selectionStart;
let endPos = taRef.selectionEnd;
taRef.value =
taRef.value.substring(0, startPos) +
value.native +
taRef.value.substring(endPos, taRef.value.length);
};
return (
<div
style={{
display: "flex",
flexDirection: "column",
border: "3px solid",
borderRadius: "5px",
width: "600px",
minHeight: "200px",
padding: "20px"
}}
>
<div
style={{
display: "flex",
flexDirection: "column",
flex: 1,
border: "1px solid",
borderRadius: "5px",
margin: "0px"
}}
>
<textarea
ref={textAreaRef}
value={text}
style={{ flex: 1, border: "none", minHeight: "150px" }}
onChange={e => setText(e.target.value)}
/>
<div
style={{
display: "flex",
flexDirection: "row",
flexWrap: "wrap",
background: "fbfbfb"
}}
>
{pics.map((picFile, index) => (
<Img
key={index}
index={index}
file={picFile}
onRemove={rmIndx =>
setPics(pics.filter((pic, index) => index !== rmIndx))
}
/>
))}
</div>
</div>
<div
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
marginTop: "20px"
}}
>
<div style={{ marginRight: "20px" }}>
<FileInput onChange={pics => setPics(pics)}>
<ImgIcon />
</FileInput>
</div>
<EmojiPicker onSelect={insertAtPos} />
</div>
</div>
);
}
EmojiPickerModal
const EmojiPicker = ({ onSelect }) => {
const [show, setShow] = useState(false);
return (
<>
<button
onClick={() => setShow(oldState => !oldState)}
style={{
width: "30px",
height: "30px",
borderRadius: "4px",
border: "3px solid",
display: "flex",
alignItems: "center",
justifyContent: "center",
background: "transparent"
}}
>
ej
</button>
{ReactDOM.createPortal(
show && <Picker onSelect={onSelect} />,
document.body
)}
</>
);
};
Примечание. Для средства выбора смайликов использовался популярный компонент средства выбора смайликов с открытым исходным кодом emoji-mart