Port settings.

This commit is contained in:
Paul
2021-06-19 22:37:12 +01:00
parent b4bc2262ae
commit 31d8950ea1
48 changed files with 3056 additions and 106 deletions

View File

@@ -23,6 +23,14 @@ const CheckboxBase = styled.label`
input {
display: none;
}
&:hover {
background: var(--secondary-background);
.check {
background: var(--background);
}
}
`;
const CheckboxContent = styled.span`
@@ -46,6 +54,7 @@ const Checkmark = styled.div<{ checked: boolean }>`
display: grid;
border-radius: 4px;
place-items: center;
transition: 0.2s ease all;
background: var(--secondary-background);
svg {
@@ -56,7 +65,7 @@ const Checkmark = styled.div<{ checked: boolean }>`
${(props) =>
props.checked &&
css`
background: var(--accent);
background: var(--accent) !important;
`}
`;
@@ -71,7 +80,7 @@ export interface CheckboxProps {
export default function Checkbox(props: CheckboxProps) {
return (
<CheckboxBase disabled={props.disabled}>
<CheckboxBase disabled={props.disabled} className={props.className}>
<CheckboxContent>
<span>{props.children}</span>
{props.description && (
@@ -87,7 +96,7 @@ export default function Checkbox(props: CheckboxProps) {
!props.disabled && props.onChange(!props.checked)
}
/>
<Checkmark checked={props.checked}>
<Checkmark checked={props.checked} className="check">
<Check size={20} />
</Checkmark>
</CheckboxBase>

View File

@@ -47,7 +47,7 @@ const ModalContainer = styled.div`
animation-timing-function: cubic-bezier(.3,.3,.18,1.1);
`;
const ModalContent = styled.div<{ [key in 'attachment' | 'noBackground' | 'border']?: boolean }>`
const ModalContent = styled.div<{ [key in 'attachment' | 'noBackground' | 'border' | 'padding']?: boolean }>`
border-radius: 8px;
text-overflow: ellipsis;
@@ -56,10 +56,13 @@ const ModalContent = styled.div<{ [key in 'attachment' | 'noBackground' | 'borde
}
${ props => !props.noBackground && css`
padding: 1.5em;
background: var(--secondary-header);
` }
${ props => props.padding && css`
padding: 1.5em;
` }
${ props => props.attachment && css`
border-radius: 8px 8px 0 0;
` }
@@ -110,7 +113,8 @@ export default function Modal(props: Props) {
<ModalContent
attachment={!!props.actions}
noBackground={props.noBackground}
border={props.border}>
border={props.border}
padding={!props.dontModal}>
{props.title && <h3>{props.title}</h3>}
{props.children}
</ModalContent>

View File

@@ -0,0 +1,31 @@
.container {
font-size: 0.875rem;
line-height: 20px;
position: relative;
}
.textarea {
width: 100%;
white-space: pre-wrap;
textarea::placeholder {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
.hide {
width: 100%;
overflow: hidden;
position: relative;
}
.ghost {
width: 100%;
white-space: pre-wrap;
top: 0;
position: absolute;
visibility: hidden;
}

View File

@@ -0,0 +1,146 @@
// ! FIXME: temporarily here until re-written
// ! DO NOT IMRPOVE, JUST RE-WRITE
import classNames from "classnames";
import { memo } from "preact/compat";
import styles from "./TextArea.module.scss";
import { useState, useEffect, useRef, useLayoutEffect } from "preact/hooks";
export interface TextAreaProps {
id?: string;
value: string;
maxRows?: number;
padding?: number;
minHeight?: number;
disabled?: boolean;
maxLength?: number;
className?: string;
autoFocus?: boolean;
forceFocus?: boolean;
placeholder?: string;
onKeyDown?: (ev: KeyboardEvent) => void;
onKeyUp?: (ev: KeyboardEvent) => void;
onChange: (
value: string,
ev: JSX.TargetedEvent<HTMLTextAreaElement, Event>
) => void;
onFocus?: (current: HTMLTextAreaElement) => void;
onBlur?: () => void;
}
const lineHeight = 20;
export const TextArea = memo((props: TextAreaProps) => {
const padding = props.padding ? props.padding * 2 : 0;
const [height, setHeightState] = useState(
props.minHeight ?? lineHeight + padding
);
const ghost = useRef<HTMLDivElement>();
const ref = useRef<HTMLTextAreaElement>();
function setHeight(h: number = lineHeight) {
let newHeight = Math.min(
Math.max(
lineHeight,
props.maxRows ? Math.min(h, props.maxRows * lineHeight) : h
),
props.minHeight ?? Infinity
);
if (props.padding) newHeight += padding;
if (height !== newHeight) {
setHeightState(newHeight);
}
}
function onChange(ev: JSX.TargetedEvent<HTMLTextAreaElement, Event>) {
props.onChange(ev.currentTarget.value, ev);
}
useLayoutEffect(() => {
setHeight(ghost.current.clientHeight);
}, [ghost, props.value]);
useEffect(() => {
if (props.autoFocus) ref.current.focus();
}, [props.value]);
const inputSelected = () =>
["TEXTAREA", "INPUT"].includes(document.activeElement?.nodeName ?? "");
useEffect(() => {
if (props.forceFocus) {
ref.current.focus();
}
if (props.autoFocus && !inputSelected()) {
ref.current.focus();
}
// ? if you are wondering what this is
// ? it is a quick and dirty hack to fix
// ? value not setting correctly
// ? I have no clue what's going on
ref.current.value = props.value;
if (!props.autoFocus) return;
function keyDown(e: KeyboardEvent) {
if ((e.ctrlKey && e.key !== "v") || e.altKey || e.metaKey) return;
if (e.key.length !== 1) return;
if (ref && !inputSelected()) {
ref.current.focus();
}
}
document.body.addEventListener("keydown", keyDown);
return () => document.body.removeEventListener("keydown", keyDown);
}, [ref]);
useEffect(() => {
function focus(textarea_id: string) {
if (props.id === textarea_id) {
ref.current.focus();
}
}
// InternalEventEmitter.addListener("focus_textarea", focus);
// return () =>
// InternalEventEmitter.removeListener("focus_textarea", focus);
}, [ref]);
return (
<div className={classNames(styles.container, props.className)}>
<textarea
id={props.id}
name={props.id}
style={{ height }}
value={props.value}
onChange={onChange}
disabled={props.disabled}
maxLength={props.maxLength}
className={styles.textarea}
onKeyDown={props.onKeyDown}
placeholder={props.placeholder}
onContextMenu={e => e.stopPropagation()}
onKeyUp={ev => {
setHeight(ghost.current.clientHeight);
props.onKeyUp && props.onKeyUp(ev);
}}
ref={ref}
onFocus={() => props.onFocus && props.onFocus(ref.current)}
onBlur={props.onBlur}
/>
<div className={styles.hide}>
<div className={styles.ghost} ref={ghost}>
{props.value
? props.value
.split("\n")
.map(x => `${x}`)
.join("\n")
: undefined ?? "\n"}
</div>
</div>
</div>
);
});