Files
lucko-paste/src/components/EditorControls.tsx

173 lines
4.2 KiB
TypeScript
Raw Normal View History

2021-03-26 22:00:12 +00:00
import copy from 'copy-to-clipboard';
2022-11-06 22:46:13 +00:00
import history from 'history/browser';
2024-12-08 22:11:46 +00:00
import { RefObject, useCallback, useEffect, useState } from 'react';
2022-11-06 22:46:13 +00:00
import styled from 'styled-components';
2021-03-26 22:00:12 +00:00
2022-11-06 22:46:13 +00:00
import themes, { Themes } from '../style/themes';
2021-10-28 21:05:35 +01:00
import { languages } from '../util/highlighting';
2022-11-06 22:46:13 +00:00
import { saveToBytebin } from '../util/storage';
2025-06-29 12:47:24 +03:00
import { useQueryRouting } from '../util/constants';
2022-11-06 22:46:13 +00:00
import Button from './Button';
2023-01-08 20:33:17 +00:00
import { ResetFunction } from './Editor';
2022-11-06 22:46:13 +00:00
import MenuButton from './MenuButton';
export interface EditorControlsProps {
actualContent: string;
2024-12-08 22:11:46 +00:00
resetFunction: RefObject<ResetFunction | null>;
2022-11-06 22:46:13 +00:00
language: string;
setLanguage: (value: string) => void;
readOnly: boolean;
setReadOnly: (value: boolean) => void;
theme: keyof Themes;
setTheme: (value: keyof Themes) => void;
2025-03-15 13:36:29 +00:00
wordWrap: boolean;
setWordWrap: (value: boolean) => void;
2022-11-06 22:46:13 +00:00
zoom: (delta: number) => void;
2025-06-20 08:35:23 +01:00
setShowAbout: (value: boolean) => void;
2022-11-06 22:46:13 +00:00
}
2021-03-27 11:49:46 +00:00
2021-04-02 13:05:15 +01:00
export default function EditorControls({
2022-01-08 19:10:27 +00:00
actualContent,
2023-01-08 20:33:17 +00:00
resetFunction,
2021-04-02 13:05:15 +01:00
language,
setLanguage,
2022-01-27 23:26:16 +00:00
readOnly,
setReadOnly,
2021-04-02 13:05:15 +01:00
theme,
setTheme,
2025-03-15 13:36:29 +00:00
wordWrap,
setWordWrap,
2021-04-02 13:05:15 +01:00
zoom,
2025-06-20 08:35:23 +01:00
setShowAbout,
2022-11-06 22:46:13 +00:00
}: EditorControlsProps) {
const [saving, setSaving] = useState<boolean>(false);
const [recentlySaved, setRecentlySaved] = useState<boolean>(false);
2021-03-26 22:00:12 +00:00
useEffect(() => {
setRecentlySaved(false);
2022-01-08 19:10:27 +00:00
}, [actualContent, language]);
2021-03-26 22:00:12 +00:00
2025-06-20 08:35:23 +01:00
const showAbout = useCallback(() => {
setShowAbout(true);
}, [setShowAbout]);
2021-03-27 14:17:28 +00:00
const save = useCallback(() => {
2022-01-08 19:10:27 +00:00
if (!actualContent || recentlySaved) {
2021-03-26 22:00:12 +00:00
return;
}
setSaving(true);
2022-01-08 19:10:27 +00:00
saveToBytebin(actualContent, language).then(pasteId => {
2021-03-26 22:00:12 +00:00
setSaving(false);
setRecentlySaved(true);
2022-01-08 19:10:27 +00:00
if (pasteId) {
2025-06-29 12:47:24 +03:00
if (useQueryRouting) {
history.replace({
search: `?id=${pasteId}`,
});
} else {
history.replace({
pathname: pasteId,
});
}
2022-01-08 19:10:27 +00:00
copy(window.location.href);
document.title = 'paste | ' + pasteId;
}
2021-03-26 22:00:12 +00:00
});
2022-01-08 19:10:27 +00:00
}, [actualContent, language, recentlySaved]);
2021-03-27 14:17:28 +00:00
useEffect(() => {
2022-11-06 22:46:13 +00:00
const listener = (e: KeyboardEvent) => {
2021-04-02 13:05:15 +01:00
if (e.ctrlKey || e.metaKey) {
2021-03-27 14:17:28 +00:00
if (e.key === 's' || e.key === 'S') {
e.preventDefault();
save();
}
if (e.key === '=' || e.key === '-') {
e.preventDefault();
zoom(e.key === '=' ? 1 : -1);
}
}
2021-04-02 13:05:15 +01:00
};
2021-03-27 14:17:28 +00:00
window.addEventListener('keydown', listener);
return () => window.removeEventListener('keydown', listener);
}, [save, zoom]);
2021-03-26 22:00:12 +00:00
2021-03-27 11:59:56 +00:00
function reset() {
2023-01-08 20:33:17 +00:00
if (!resetFunction.current) {
throw new Error();
}
resetFunction.current();
2021-03-27 11:59:56 +00:00
setLanguage('plain');
history.replace({
2021-04-02 13:05:15 +01:00
pathname: '/',
hash: '',
2021-03-27 11:59:56 +00:00
});
document.title = 'paste';
}
2022-01-27 23:26:16 +00:00
function unsetReadOnly() {
setReadOnly(false);
}
2021-03-27 11:49:46 +00:00
return (
<Header>
<Section>
2021-03-27 11:59:56 +00:00
<Button onClick={reset}>[new]</Button>
2021-03-27 11:49:46 +00:00
<Button onClick={save}>
2021-04-02 13:05:15 +01:00
{recentlySaved ? '[link copied!]' : saving ? '[saving...]' : '[save]'}
2021-03-27 11:49:46 +00:00
</Button>
2021-04-02 13:05:15 +01:00
<MenuButton
label="language"
value={language}
setValue={setLanguage}
2021-10-28 21:05:35 +01:00
ids={languages}
2021-04-02 13:05:15 +01:00
/>
2022-01-27 23:26:16 +00:00
{readOnly && <Button onClick={unsetReadOnly}>[edit]</Button>}
2021-03-27 11:49:46 +00:00
</Section>
<Section>
2021-03-27 14:17:28 +00:00
<Button onClick={() => zoom(1)}>[+ </Button>
2021-03-27 17:01:20 +00:00
<Button onClick={() => zoom(-1)}> -]</Button>
2025-03-15 13:36:29 +00:00
<Button onClick={() => setWordWrap(!wordWrap)}>
[wrap:{wordWrap ? 'on' : 'off'}]
</Button>
2021-04-02 13:05:15 +01:00
<MenuButton
label="theme"
value={theme}
setValue={setTheme}
2022-11-06 22:46:13 +00:00
ids={Object.keys(themes) as (keyof Themes)[]}
2021-04-02 13:05:15 +01:00
/>
2025-06-20 08:35:23 +01:00
<Button onClick={showAbout}>[about]</Button>
2021-03-27 11:49:46 +00:00
</Section>
</Header>
2021-04-02 13:05:15 +01:00
);
2021-03-27 11:49:46 +00:00
}
const Header = styled.header`
position: fixed;
2022-01-08 19:10:27 +00:00
top: 0;
2024-08-24 11:05:21 +01:00
z-index: 10;
2021-03-27 11:49:46 +00:00
width: 100%;
height: 2em;
color: ${props => props.theme.primary};
background: ${props => props.theme.secondary};
display: flex;
justify-content: space-between;
2021-03-27 14:17:28 +00:00
user-select: none;
2025-03-15 13:36:29 +00:00
overflow-x: auto;
white-space: nowrap;
2021-03-27 11:49:46 +00:00
`;
const Section = styled.div`
display: flex;
align-items: center;
2021-03-27 20:05:15 +00:00
2025-03-15 13:36:29 +00:00
@media (max-width: 850px) {
2021-03-27 20:05:15 +00:00
.optional {
display: none;
}
}
2021-03-27 11:49:46 +00:00
`;