mirror of
https://github.com/stoatchat/for-legacy-web.git
synced 2026-03-07 01:15:28 +00:00
Run prettier on all files.
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
import styled from "styled-components";
|
||||
|
||||
export default styled.div`
|
||||
padding: 8px;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
padding: 8px;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
|
||||
color: var(--accent);
|
||||
background: var(--primary-background);
|
||||
color: var(--accent);
|
||||
background: var(--primary-background);
|
||||
`;
|
||||
|
||||
@@ -1,71 +1,71 @@
|
||||
import styled, { css } from "styled-components";
|
||||
|
||||
interface Props {
|
||||
readonly contrast?: boolean;
|
||||
readonly error?: boolean;
|
||||
readonly contrast?: boolean;
|
||||
readonly error?: boolean;
|
||||
}
|
||||
|
||||
export default styled.button<Props>`
|
||||
z-index: 1;
|
||||
padding: 8px;
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
font-family: inherit;
|
||||
z-index: 1;
|
||||
padding: 8px;
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
font-family: inherit;
|
||||
|
||||
transition: 0.2s ease opacity;
|
||||
transition: 0.2s ease background-color;
|
||||
transition: 0.2s ease opacity;
|
||||
transition: 0.2s ease background-color;
|
||||
|
||||
background: var(--primary-background);
|
||||
color: var(--foreground);
|
||||
background: var(--primary-background);
|
||||
color: var(--foreground);
|
||||
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
|
||||
&:hover {
|
||||
background: var(--secondary-header);
|
||||
}
|
||||
&:hover {
|
||||
background: var(--secondary-header);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background: var(--primary-background);
|
||||
}
|
||||
&:disabled {
|
||||
background: var(--primary-background);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: var(--secondary-background);
|
||||
}
|
||||
&:active {
|
||||
background: var(--secondary-background);
|
||||
}
|
||||
|
||||
${(props) =>
|
||||
props.contrast &&
|
||||
css`
|
||||
padding: 4px 8px;
|
||||
background: var(--secondary-header);
|
||||
${(props) =>
|
||||
props.contrast &&
|
||||
css`
|
||||
padding: 4px 8px;
|
||||
background: var(--secondary-header);
|
||||
|
||||
&:hover {
|
||||
background: var(--primary-header);
|
||||
}
|
||||
&:hover {
|
||||
background: var(--primary-header);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background: var(--secondary-header);
|
||||
}
|
||||
&:disabled {
|
||||
background: var(--secondary-header);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: var(--secondary-background);
|
||||
}
|
||||
`}
|
||||
&:active {
|
||||
background: var(--secondary-background);
|
||||
}
|
||||
`}
|
||||
|
||||
${(props) =>
|
||||
props.error &&
|
||||
css`
|
||||
color: white;
|
||||
background: var(--error);
|
||||
${(props) =>
|
||||
props.error &&
|
||||
css`
|
||||
color: white;
|
||||
background: var(--error);
|
||||
|
||||
&:hover {
|
||||
filter: brightness(1.2);
|
||||
background: var(--error);
|
||||
}
|
||||
&:hover {
|
||||
filter: brightness(1.2);
|
||||
background: var(--error);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background: var(--error);
|
||||
}
|
||||
`}
|
||||
&:disabled {
|
||||
background: var(--error);
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
@@ -1,51 +1,55 @@
|
||||
import styled, { css } from "styled-components";
|
||||
import { Children } from "../../types/Preact";
|
||||
import { Plus } from "@styled-icons/boxicons-regular";
|
||||
import styled, { css } from "styled-components";
|
||||
|
||||
const CategoryBase = styled.div<Pick<Props, 'variant'>>`
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
import { Children } from "../../types/Preact";
|
||||
|
||||
margin-top: 4px;
|
||||
padding: 6px 0;
|
||||
margin-bottom: 4px;
|
||||
white-space: nowrap;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
const CategoryBase = styled.div<Pick<Props, "variant">>`
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
|
||||
svg {
|
||||
cursor: pointer;
|
||||
}
|
||||
margin-top: 4px;
|
||||
padding: 6px 0;
|
||||
margin-bottom: 4px;
|
||||
white-space: nowrap;
|
||||
|
||||
&:first-child {
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
|
||||
${ props => props.variant === 'uniform' && css`
|
||||
padding-top: 6px;
|
||||
` }
|
||||
svg {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
${(props) =>
|
||||
props.variant === "uniform" &&
|
||||
css`
|
||||
padding-top: 6px;
|
||||
`}
|
||||
`;
|
||||
|
||||
type Props = Omit<JSX.HTMLAttributes<HTMLDivElement>, 'children' | 'as' | 'action'> & {
|
||||
text: Children;
|
||||
action?: () => void;
|
||||
variant?: 'default' | 'uniform';
|
||||
}
|
||||
type Props = Omit<
|
||||
JSX.HTMLAttributes<HTMLDivElement>,
|
||||
"children" | "as" | "action"
|
||||
> & {
|
||||
text: Children;
|
||||
action?: () => void;
|
||||
variant?: "default" | "uniform";
|
||||
};
|
||||
|
||||
export default function Category(props: Props) {
|
||||
let { text, action, ...otherProps } = props;
|
||||
let { text, action, ...otherProps } = props;
|
||||
|
||||
return (
|
||||
<CategoryBase {...otherProps}>
|
||||
{text}
|
||||
{action && (
|
||||
<Plus size={16} onClick={action} />
|
||||
)}
|
||||
</CategoryBase>
|
||||
);
|
||||
};
|
||||
return (
|
||||
<CategoryBase {...otherProps}>
|
||||
{text}
|
||||
{action && <Plus size={16} onClick={action} />}
|
||||
</CategoryBase>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,107 +1,108 @@
|
||||
import { Check } from "@styled-icons/boxicons-regular";
|
||||
import { Children } from "../../types/Preact";
|
||||
import styled, { css } from "styled-components";
|
||||
|
||||
import { Children } from "../../types/Preact";
|
||||
|
||||
const CheckboxBase = styled.label`
|
||||
margin-top: 20px;
|
||||
gap: 4px;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
border-radius: 4px;
|
||||
align-items: center;
|
||||
margin-top: 20px;
|
||||
gap: 4px;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
border-radius: 4px;
|
||||
align-items: center;
|
||||
|
||||
cursor: pointer;
|
||||
font-size: 18px;
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
font-size: 18px;
|
||||
user-select: none;
|
||||
|
||||
transition: 0.2s ease all;
|
||||
transition: 0.2s ease all;
|
||||
|
||||
input {
|
||||
display: none;
|
||||
}
|
||||
input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.check {
|
||||
background: var(--background);
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
.check {
|
||||
background: var(--background);
|
||||
}
|
||||
}
|
||||
|
||||
&[disabled] {
|
||||
opacity: .5;
|
||||
cursor: not-allowed;
|
||||
&[disabled] {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
|
||||
&:hover {
|
||||
background: unset;
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
background: unset;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const CheckboxContent = styled.span`
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
flex-direction: column;
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
flex-direction: column;
|
||||
`;
|
||||
|
||||
const CheckboxDescription = styled.span`
|
||||
font-size: .75rem;
|
||||
font-weight: 400;
|
||||
color: var(--secondary-foreground);
|
||||
font-size: 0.75rem;
|
||||
font-weight: 400;
|
||||
color: var(--secondary-foreground);
|
||||
`;
|
||||
|
||||
const Checkmark = styled.div<{ checked: boolean }>`
|
||||
margin: 4px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
display: grid;
|
||||
flex-shrink: 0;
|
||||
border-radius: 4px;
|
||||
place-items: center;
|
||||
transition: 0.2s ease all;
|
||||
background: var(--secondary-background);
|
||||
margin: 4px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
display: grid;
|
||||
flex-shrink: 0;
|
||||
border-radius: 4px;
|
||||
place-items: center;
|
||||
transition: 0.2s ease all;
|
||||
background: var(--secondary-background);
|
||||
|
||||
svg {
|
||||
color: var(--secondary-background);
|
||||
}
|
||||
svg {
|
||||
color: var(--secondary-background);
|
||||
}
|
||||
|
||||
${(props) =>
|
||||
props.checked &&
|
||||
css`
|
||||
background: var(--accent) !important;
|
||||
`}
|
||||
${(props) =>
|
||||
props.checked &&
|
||||
css`
|
||||
background: var(--accent) !important;
|
||||
`}
|
||||
`;
|
||||
|
||||
export interface CheckboxProps {
|
||||
checked: boolean;
|
||||
disabled?: boolean;
|
||||
className?: string;
|
||||
children: Children;
|
||||
description?: Children;
|
||||
onChange: (state: boolean) => void;
|
||||
checked: boolean;
|
||||
disabled?: boolean;
|
||||
className?: string;
|
||||
children: Children;
|
||||
description?: Children;
|
||||
onChange: (state: boolean) => void;
|
||||
}
|
||||
|
||||
export default function Checkbox(props: CheckboxProps) {
|
||||
return (
|
||||
<CheckboxBase disabled={props.disabled} className={props.className}>
|
||||
<CheckboxContent>
|
||||
<span>{props.children}</span>
|
||||
{props.description && (
|
||||
<CheckboxDescription>
|
||||
{props.description}
|
||||
</CheckboxDescription>
|
||||
)}
|
||||
</CheckboxContent>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={props.checked}
|
||||
onChange={() =>
|
||||
!props.disabled && props.onChange(!props.checked)
|
||||
}
|
||||
/>
|
||||
<Checkmark checked={props.checked} className="check">
|
||||
<Check size={20} />
|
||||
</Checkmark>
|
||||
</CheckboxBase>
|
||||
);
|
||||
return (
|
||||
<CheckboxBase disabled={props.disabled} className={props.className}>
|
||||
<CheckboxContent>
|
||||
<span>{props.children}</span>
|
||||
{props.description && (
|
||||
<CheckboxDescription>
|
||||
{props.description}
|
||||
</CheckboxDescription>
|
||||
)}
|
||||
</CheckboxContent>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={props.checked}
|
||||
onChange={() =>
|
||||
!props.disabled && props.onChange(!props.checked)
|
||||
}
|
||||
/>
|
||||
<Checkmark checked={props.checked} className="check">
|
||||
<Check size={20} />
|
||||
</Checkmark>
|
||||
</CheckboxBase>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,130 +1,127 @@
|
||||
import { useRef } from "preact/hooks";
|
||||
import { Check } from "@styled-icons/boxicons-regular";
|
||||
import { Palette } from "@styled-icons/boxicons-solid";
|
||||
import styled, { css } from "styled-components";
|
||||
|
||||
import { useRef } from "preact/hooks";
|
||||
|
||||
interface Props {
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
}
|
||||
|
||||
const presets = [
|
||||
[
|
||||
"#7B68EE",
|
||||
"#3498DB",
|
||||
"#1ABC9C",
|
||||
"#F1C40F",
|
||||
"#FF7F50",
|
||||
"#FD6671",
|
||||
"#E91E63",
|
||||
"#D468EE",
|
||||
],
|
||||
[
|
||||
"#594CAD",
|
||||
"#206694",
|
||||
"#11806A",
|
||||
"#C27C0E",
|
||||
"#CD5B45",
|
||||
"#FF424F",
|
||||
"#AD1457",
|
||||
"#954AA8",
|
||||
],
|
||||
[
|
||||
"#7B68EE",
|
||||
"#3498DB",
|
||||
"#1ABC9C",
|
||||
"#F1C40F",
|
||||
"#FF7F50",
|
||||
"#FD6671",
|
||||
"#E91E63",
|
||||
"#D468EE",
|
||||
],
|
||||
[
|
||||
"#594CAD",
|
||||
"#206694",
|
||||
"#11806A",
|
||||
"#C27C0E",
|
||||
"#CD5B45",
|
||||
"#FF424F",
|
||||
"#AD1457",
|
||||
"#954AA8",
|
||||
],
|
||||
];
|
||||
|
||||
const SwatchesBase = styled.div`
|
||||
gap: 8px;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
display: flex;
|
||||
|
||||
input {
|
||||
opacity: 0;
|
||||
margin-top: 44px;
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
}
|
||||
input {
|
||||
opacity: 0;
|
||||
margin-top: 44px;
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
}
|
||||
`;
|
||||
|
||||
const Swatch = styled.div<{ type: "small" | "large"; colour: string }>`
|
||||
flex-shrink: 0;
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
background-color: ${(props) => props.colour};
|
||||
flex-shrink: 0;
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
background-color: ${(props) => props.colour};
|
||||
|
||||
display: grid;
|
||||
place-items: center;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
|
||||
&:hover {
|
||||
border: 3px solid var(--foreground);
|
||||
transition: border ease-in-out 0.07s;
|
||||
}
|
||||
&:hover {
|
||||
border: 3px solid var(--foreground);
|
||||
transition: border ease-in-out 0.07s;
|
||||
}
|
||||
|
||||
svg {
|
||||
color: white;
|
||||
}
|
||||
svg {
|
||||
color: white;
|
||||
}
|
||||
|
||||
${(props) =>
|
||||
props.type === "small"
|
||||
? css`
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
${(props) =>
|
||||
props.type === "small"
|
||||
? css`
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
|
||||
svg {
|
||||
/*stroke-width: 2;*/
|
||||
}
|
||||
`
|
||||
: css`
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
`}
|
||||
svg {
|
||||
/*stroke-width: 2;*/
|
||||
}
|
||||
`
|
||||
: css`
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
`}
|
||||
`;
|
||||
|
||||
const Rows = styled.div`
|
||||
gap: 8px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
> div {
|
||||
gap: 8px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
> div {
|
||||
gap: 8px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
`;
|
||||
|
||||
export default function ColourSwatches({ value, onChange }: Props) {
|
||||
const ref = useRef<HTMLInputElement>();
|
||||
const ref = useRef<HTMLInputElement>();
|
||||
|
||||
return (
|
||||
<SwatchesBase>
|
||||
<Swatch
|
||||
colour={value}
|
||||
type="large"
|
||||
onClick={() => ref.current.click()}
|
||||
>
|
||||
<Palette size={32} />
|
||||
</Swatch>
|
||||
<input
|
||||
type="color"
|
||||
value={value}
|
||||
ref={ref}
|
||||
onChange={(ev) => onChange(ev.currentTarget.value)}
|
||||
/>
|
||||
<Rows>
|
||||
{presets.map((row, i) => (
|
||||
<div key={i}>
|
||||
{row.map((swatch, i) => (
|
||||
<Swatch
|
||||
colour={swatch}
|
||||
type="small"
|
||||
key={i}
|
||||
onClick={() => onChange(swatch)}
|
||||
>
|
||||
{swatch === value && (
|
||||
<Check size={18} />
|
||||
)}
|
||||
</Swatch>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</Rows>
|
||||
</SwatchesBase>
|
||||
);
|
||||
return (
|
||||
<SwatchesBase>
|
||||
<Swatch
|
||||
colour={value}
|
||||
type="large"
|
||||
onClick={() => ref.current.click()}>
|
||||
<Palette size={32} />
|
||||
</Swatch>
|
||||
<input
|
||||
type="color"
|
||||
value={value}
|
||||
ref={ref}
|
||||
onChange={(ev) => onChange(ev.currentTarget.value)}
|
||||
/>
|
||||
<Rows>
|
||||
{presets.map((row, i) => (
|
||||
<div key={i}>
|
||||
{row.map((swatch, i) => (
|
||||
<Swatch
|
||||
colour={swatch}
|
||||
type="small"
|
||||
key={i}
|
||||
onClick={() => onChange(swatch)}>
|
||||
{swatch === value && <Check size={18} />}
|
||||
</Swatch>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</Rows>
|
||||
</SwatchesBase>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import styled from "styled-components";
|
||||
|
||||
export default styled.select`
|
||||
padding: 8px;
|
||||
border-radius: 6px;
|
||||
font-family: inherit;
|
||||
color: var(--secondary-foreground);
|
||||
background: var(--secondary-background);
|
||||
font-size: .875rem;
|
||||
border: none;
|
||||
outline: 2px solid transparent;
|
||||
transition: outline-color 0.2s ease-in-out;
|
||||
transition: box-shadow .3s;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
border-radius: 6px;
|
||||
font-family: inherit;
|
||||
color: var(--secondary-foreground);
|
||||
background: var(--secondary-background);
|
||||
font-size: 0.875rem;
|
||||
border: none;
|
||||
outline: 2px solid transparent;
|
||||
transition: outline-color 0.2s ease-in-out;
|
||||
transition: box-shadow 0.3s;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 2pt var(--accent);
|
||||
}
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 2pt var(--accent);
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -2,47 +2,47 @@ import dayjs from "dayjs";
|
||||
import styled, { css } from "styled-components";
|
||||
|
||||
const Base = styled.div<{ unread?: boolean }>`
|
||||
height: 0;
|
||||
display: flex;
|
||||
user-select: none;
|
||||
align-items: center;
|
||||
margin: 17px 12px 5px;
|
||||
border-top: thin solid var(--tertiary-foreground);
|
||||
height: 0;
|
||||
display: flex;
|
||||
user-select: none;
|
||||
align-items: center;
|
||||
margin: 17px 12px 5px;
|
||||
border-top: thin solid var(--tertiary-foreground);
|
||||
|
||||
time {
|
||||
margin-top: -2px;
|
||||
font-size: .6875rem;
|
||||
line-height: .6875rem;
|
||||
padding: 2px 5px 2px 0;
|
||||
color: var(--tertiary-foreground);
|
||||
background: var(--primary-background);
|
||||
}
|
||||
time {
|
||||
margin-top: -2px;
|
||||
font-size: 0.6875rem;
|
||||
line-height: 0.6875rem;
|
||||
padding: 2px 5px 2px 0;
|
||||
color: var(--tertiary-foreground);
|
||||
background: var(--primary-background);
|
||||
}
|
||||
|
||||
${ props => props.unread && css`
|
||||
border-top: thin solid var(--accent);
|
||||
` }
|
||||
${(props) =>
|
||||
props.unread &&
|
||||
css`
|
||||
border-top: thin solid var(--accent);
|
||||
`}
|
||||
`;
|
||||
|
||||
const Unread = styled.div`
|
||||
background: var(--accent);
|
||||
color: white;
|
||||
padding: 5px 8px;
|
||||
border-radius: 60px;
|
||||
font-weight: 600;
|
||||
background: var(--accent);
|
||||
color: white;
|
||||
padding: 5px 8px;
|
||||
border-radius: 60px;
|
||||
font-weight: 600;
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
date: Date;
|
||||
unread?: boolean;
|
||||
date: Date;
|
||||
unread?: boolean;
|
||||
}
|
||||
|
||||
export default function DateDivider(props: Props) {
|
||||
return (
|
||||
<Base unread={props.unread}>
|
||||
{ props.unread && <Unread>NEW</Unread> }
|
||||
<time>
|
||||
{ dayjs(props.date).format("LL") }
|
||||
</time>
|
||||
</Base>
|
||||
);
|
||||
return (
|
||||
<Base unread={props.unread}>
|
||||
{props.unread && <Unread>NEW</Unread>}
|
||||
<time>{dayjs(props.date).format("LL")}</time>
|
||||
</Base>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,68 +1,74 @@
|
||||
import styled, { css } from "styled-components";
|
||||
|
||||
export default styled.details<{ sticky?: boolean, large?: boolean }>`
|
||||
summary {
|
||||
${ props => props.sticky && css`
|
||||
top: -1px;
|
||||
z-index: 10;
|
||||
position: sticky;
|
||||
` }
|
||||
export default styled.details<{ sticky?: boolean; large?: boolean }>`
|
||||
summary {
|
||||
${(props) =>
|
||||
props.sticky &&
|
||||
css`
|
||||
top: -1px;
|
||||
z-index: 10;
|
||||
position: sticky;
|
||||
`}
|
||||
|
||||
${ props => props.large && css`
|
||||
/*padding: 5px 0;*/
|
||||
background: var(--primary-background);
|
||||
color: var(--secondary-foreground);
|
||||
${(props) =>
|
||||
props.large &&
|
||||
css`
|
||||
/*padding: 5px 0;*/
|
||||
background: var(--primary-background);
|
||||
color: var(--secondary-foreground);
|
||||
|
||||
.padding { /*TOFIX: make this applicable only for the friends list menu, DO NOT REMOVE.*/
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 5px 0;
|
||||
margin: 0.8em 0px 0.4em;
|
||||
cursor: pointer;
|
||||
}
|
||||
` }
|
||||
.padding {
|
||||
/*TOFIX: make this applicable only for the friends list menu, DO NOT REMOVE.*/
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 5px 0;
|
||||
margin: 0.8em 0px 0.4em;
|
||||
cursor: pointer;
|
||||
}
|
||||
`}
|
||||
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
list-style: none;
|
||||
align-items: center;
|
||||
transition: .2s opacity;
|
||||
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
cursor: pointer;
|
||||
list-style: none;
|
||||
align-items: center;
|
||||
transition: 0.2s opacity;
|
||||
|
||||
&::marker, &::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
|
||||
.title {
|
||||
flex-grow: 1;
|
||||
margin-top: 1px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
&::marker,
|
||||
&::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.padding {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.title {
|
||||
flex-grow: 1;
|
||||
margin-top: 1px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
> svg {
|
||||
flex-shrink: 0;
|
||||
margin-inline-end: 4px;
|
||||
transition: .2s ease transform;
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&:not([open]) {
|
||||
summary {
|
||||
opacity: .7;
|
||||
}
|
||||
|
||||
summary svg {
|
||||
transform: rotateZ(-90deg);
|
||||
}
|
||||
}
|
||||
> svg {
|
||||
flex-shrink: 0;
|
||||
margin-inline-end: 4px;
|
||||
transition: 0.2s ease transform;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:not([open]) {
|
||||
summary {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
summary svg {
|
||||
transform: rotateZ(-90deg);
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -1,51 +1,57 @@
|
||||
import styled, { css } from "styled-components";
|
||||
|
||||
interface Props {
|
||||
borders?: boolean;
|
||||
background?: boolean;
|
||||
placement: 'primary' | 'secondary'
|
||||
borders?: boolean;
|
||||
background?: boolean;
|
||||
placement: "primary" | "secondary";
|
||||
}
|
||||
|
||||
export default styled.div<Props>`
|
||||
gap: 6px;
|
||||
height: 48px;
|
||||
flex: 0 auto;
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
padding: 0 16px;
|
||||
font-weight: 600;
|
||||
user-select: none;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
height: 48px;
|
||||
flex: 0 auto;
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
padding: 0 16px;
|
||||
font-weight: 600;
|
||||
user-select: none;
|
||||
align-items: center;
|
||||
|
||||
background-size: cover !important;
|
||||
background-position: center !important;
|
||||
background-color: var(--primary-header);
|
||||
background-size: cover !important;
|
||||
background-position: center !important;
|
||||
background-color: var(--primary-header);
|
||||
|
||||
svg {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
svg {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/*@media only screen and (max-width: 768px) {
|
||||
/*@media only screen and (max-width: 768px) {
|
||||
padding: 0 12px;
|
||||
}*/
|
||||
|
||||
@media (pointer: coarse) {
|
||||
height: 56px;
|
||||
}
|
||||
|
||||
${ props => props.background && css`
|
||||
height: 120px !important;
|
||||
align-items: flex-end;
|
||||
@media (pointer: coarse) {
|
||||
height: 56px;
|
||||
}
|
||||
|
||||
text-shadow: 0px 0px 1px black;
|
||||
` }
|
||||
${(props) =>
|
||||
props.background &&
|
||||
css`
|
||||
height: 120px !important;
|
||||
align-items: flex-end;
|
||||
|
||||
${ props => props.placement === 'secondary' && css`
|
||||
background-color: var(--secondary-header);
|
||||
padding: 14px;
|
||||
` }
|
||||
text-shadow: 0px 0px 1px black;
|
||||
`}
|
||||
|
||||
${ props => props.borders && css`
|
||||
border-start-start-radius: 8px;
|
||||
` }
|
||||
${(props) =>
|
||||
props.placement === "secondary" &&
|
||||
css`
|
||||
background-color: var(--secondary-header);
|
||||
padding: 14px;
|
||||
`}
|
||||
|
||||
${(props) =>
|
||||
props.borders &&
|
||||
css`
|
||||
border-start-start-radius: 8px;
|
||||
`}
|
||||
`;
|
||||
|
||||
@@ -1,44 +1,46 @@
|
||||
import styled, { css } from "styled-components";
|
||||
|
||||
interface Props {
|
||||
type?: 'default' | 'circle'
|
||||
type?: "default" | "circle";
|
||||
}
|
||||
|
||||
const normal = `var(--secondary-foreground)`;
|
||||
const hover = `var(--foreground)`;
|
||||
|
||||
export default styled.div<Props>`
|
||||
z-index: 1;
|
||||
display: grid;
|
||||
cursor: pointer;
|
||||
place-items: center;
|
||||
transition: .1s ease background-color;
|
||||
z-index: 1;
|
||||
display: grid;
|
||||
cursor: pointer;
|
||||
place-items: center;
|
||||
transition: 0.1s ease background-color;
|
||||
|
||||
fill: ${normal};
|
||||
color: ${normal};
|
||||
/*stroke: ${normal};*/
|
||||
fill: ${normal};
|
||||
color: ${normal};
|
||||
/*stroke: ${normal};*/
|
||||
|
||||
a {
|
||||
color: ${normal};
|
||||
}
|
||||
a {
|
||||
color: ${normal};
|
||||
}
|
||||
|
||||
&:hover {
|
||||
fill: ${hover};
|
||||
color: ${hover};
|
||||
/*stroke: ${hover};*/
|
||||
&:hover {
|
||||
fill: ${hover};
|
||||
color: ${hover};
|
||||
/*stroke: ${hover};*/
|
||||
|
||||
a {
|
||||
color: ${hover};
|
||||
}
|
||||
}
|
||||
a {
|
||||
color: ${hover};
|
||||
}
|
||||
}
|
||||
|
||||
${ props => props.type === 'circle' && css`
|
||||
padding: 4px;
|
||||
border-radius: 50%;
|
||||
background-color: var(--secondary-header);
|
||||
${(props) =>
|
||||
props.type === "circle" &&
|
||||
css`
|
||||
padding: 4px;
|
||||
border-radius: 50%;
|
||||
background-color: var(--secondary-header);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--primary-header);
|
||||
}
|
||||
` }
|
||||
&:hover {
|
||||
background-color: var(--primary-header);
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
@@ -1,39 +1,39 @@
|
||||
import styled, { css } from "styled-components";
|
||||
|
||||
interface Props {
|
||||
readonly contrast?: boolean;
|
||||
readonly contrast?: boolean;
|
||||
}
|
||||
|
||||
export default styled.input<Props>`
|
||||
z-index: 1;
|
||||
padding: 8px 16px;
|
||||
border-radius: 6px;
|
||||
|
||||
font-family: inherit;
|
||||
color: var(--foreground);
|
||||
background: var(--primary-background);
|
||||
transition: 0.2s ease background-color;
|
||||
|
||||
border: none;
|
||||
outline: 2px solid transparent;
|
||||
transition: outline-color 0.2s ease-in-out;
|
||||
z-index: 1;
|
||||
padding: 8px 16px;
|
||||
border-radius: 6px;
|
||||
|
||||
&:hover {
|
||||
background: var(--secondary-background);
|
||||
}
|
||||
font-family: inherit;
|
||||
color: var(--foreground);
|
||||
background: var(--primary-background);
|
||||
transition: 0.2s ease background-color;
|
||||
|
||||
&:focus {
|
||||
outline: 2px solid var(--accent);
|
||||
}
|
||||
border: none;
|
||||
outline: 2px solid transparent;
|
||||
transition: outline-color 0.2s ease-in-out;
|
||||
|
||||
${(props) =>
|
||||
props.contrast &&
|
||||
css`
|
||||
color: var(--secondary-foreground);
|
||||
background: var(--secondary-background);
|
||||
&:hover {
|
||||
background: var(--secondary-background);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: var(--hover);
|
||||
}
|
||||
`}
|
||||
&:focus {
|
||||
outline: 2px solid var(--accent);
|
||||
}
|
||||
|
||||
${(props) =>
|
||||
props.contrast &&
|
||||
css`
|
||||
color: var(--secondary-foreground);
|
||||
background: var(--secondary-background);
|
||||
|
||||
&:hover {
|
||||
background: var(--hover);
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import styled from "styled-components";
|
||||
|
||||
export default styled.div`
|
||||
height: 0px;
|
||||
opacity: 0.6;
|
||||
flex-shrink: 0;
|
||||
margin: 8px 10px;
|
||||
border-top: 1px solid var(--tertiary-foreground);
|
||||
height: 0px;
|
||||
opacity: 0.6;
|
||||
flex-shrink: 0;
|
||||
margin: 8px 10px;
|
||||
border-top: 1px solid var(--tertiary-foreground);
|
||||
`;
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
// This file must be imported and used at least once for SVG masks.
|
||||
|
||||
export default function Masks() {
|
||||
return (
|
||||
<svg width={0} height={0} style={{ position: 'fixed' }}>
|
||||
<defs>
|
||||
<mask id="server">
|
||||
<rect x="0" y="0" width="32" height="32" fill="white" />
|
||||
<circle cx="27" cy="5" r="7" fill={"black"} />
|
||||
</mask>
|
||||
<mask id="user">
|
||||
<rect x="0" y="0" width="32" height="32" fill="white" />
|
||||
<circle cx="27" cy="27" r="7" fill={"black"} />
|
||||
</mask>
|
||||
<mask id="overlap">
|
||||
<rect x="0" y="0" width="32" height="32" fill="white" />
|
||||
<circle cx="32" cy="16" r="18" fill={"black"} />
|
||||
</mask>
|
||||
</defs>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<svg width={0} height={0} style={{ position: "fixed" }}>
|
||||
<defs>
|
||||
<mask id="server">
|
||||
<rect x="0" y="0" width="32" height="32" fill="white" />
|
||||
<circle cx="27" cy="5" r="7" fill={"black"} />
|
||||
</mask>
|
||||
<mask id="user">
|
||||
<rect x="0" y="0" width="32" height="32" fill="white" />
|
||||
<circle cx="27" cy="27" r="7" fill={"black"} />
|
||||
</mask>
|
||||
<mask id="overlap">
|
||||
<rect x="0" y="0" width="32" height="32" fill="white" />
|
||||
<circle cx="32" cy="16" r="18" fill={"black"} />
|
||||
</mask>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import Button from "./Button";
|
||||
import classNames from "classnames";
|
||||
import { Children } from "../../types/Preact";
|
||||
import { createPortal, useEffect } from "preact/compat";
|
||||
import styled, { css, keyframes } from "styled-components";
|
||||
|
||||
import classNames from "classnames";
|
||||
import { createPortal, useEffect } from "preact/compat";
|
||||
|
||||
import { Children } from "../../types/Preact";
|
||||
import Button from "./Button";
|
||||
|
||||
const open = keyframes`
|
||||
0% {opacity: 0;}
|
||||
70% {opacity: 0;}
|
||||
@@ -17,168 +19,181 @@ const zoomIn = keyframes`
|
||||
`;
|
||||
|
||||
const ModalBase = styled.div`
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 9999;
|
||||
position: fixed;
|
||||
max-height: 100%;
|
||||
user-select: none;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 9999;
|
||||
position: fixed;
|
||||
max-height: 100%;
|
||||
user-select: none;
|
||||
|
||||
animation-name: ${open};
|
||||
animation-duration: 0.2s;
|
||||
animation-name: ${open};
|
||||
animation-duration: 0.2s;
|
||||
|
||||
display: grid;
|
||||
overflow-y: auto;
|
||||
place-items: center;
|
||||
display: grid;
|
||||
overflow-y: auto;
|
||||
place-items: center;
|
||||
|
||||
color: var(--foreground);
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
color: var(--foreground);
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
`;
|
||||
|
||||
const ModalContainer = styled.div`
|
||||
overflow: hidden;
|
||||
border-radius: 8px;
|
||||
max-width: calc(100vw - 20px);
|
||||
overflow: hidden;
|
||||
border-radius: 8px;
|
||||
max-width: calc(100vw - 20px);
|
||||
|
||||
animation-name: ${zoomIn};
|
||||
animation-duration: 0.25s;
|
||||
animation-timing-function: cubic-bezier(.3,.3,.18,1.1);
|
||||
animation-name: ${zoomIn};
|
||||
animation-duration: 0.25s;
|
||||
animation-timing-function: cubic-bezier(0.3, 0.3, 0.18, 1.1);
|
||||
`;
|
||||
|
||||
const ModalContent = styled.div<{ [key in 'attachment' | 'noBackground' | 'border' | 'padding']?: boolean }>`
|
||||
border-radius: 8px;
|
||||
text-overflow: ellipsis;
|
||||
const ModalContent = styled.div<
|
||||
{ [key in "attachment" | "noBackground" | "border" | "padding"]?: boolean }
|
||||
>`
|
||||
border-radius: 8px;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
h3 {
|
||||
margin-top: 0;
|
||||
}
|
||||
h3 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
${ props => !props.noBackground && css`
|
||||
background: var(--secondary-header);
|
||||
` }
|
||||
${(props) =>
|
||||
!props.noBackground &&
|
||||
css`
|
||||
background: var(--secondary-header);
|
||||
`}
|
||||
|
||||
${ props => props.padding && css`
|
||||
padding: 1.5em;
|
||||
` }
|
||||
${(props) =>
|
||||
props.padding &&
|
||||
css`
|
||||
padding: 1.5em;
|
||||
`}
|
||||
|
||||
${ props => props.attachment && css`
|
||||
border-radius: 8px 8px 0 0;
|
||||
` }
|
||||
${(props) =>
|
||||
props.attachment &&
|
||||
css`
|
||||
border-radius: 8px 8px 0 0;
|
||||
`}
|
||||
|
||||
${ props => props.border && css`
|
||||
border-radius: 10px;
|
||||
border: 2px solid var(--secondary-background);
|
||||
` }
|
||||
${(props) =>
|
||||
props.border &&
|
||||
css`
|
||||
border-radius: 10px;
|
||||
border: 2px solid var(--secondary-background);
|
||||
`}
|
||||
`;
|
||||
|
||||
const ModalActions = styled.div`
|
||||
gap: 8px;
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
gap: 8px;
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
|
||||
padding: 1em 1.5em;
|
||||
border-radius: 0 0 8px 8px;
|
||||
background: var(--secondary-background);
|
||||
padding: 1em 1.5em;
|
||||
border-radius: 0 0 8px 8px;
|
||||
background: var(--secondary-background);
|
||||
`;
|
||||
|
||||
export interface Action {
|
||||
text: Children;
|
||||
onClick: () => void;
|
||||
confirmation?: boolean;
|
||||
contrast?: boolean;
|
||||
error?: boolean;
|
||||
text: Children;
|
||||
onClick: () => void;
|
||||
confirmation?: boolean;
|
||||
contrast?: boolean;
|
||||
error?: boolean;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
children?: Children;
|
||||
title?: Children;
|
||||
children?: Children;
|
||||
title?: Children;
|
||||
|
||||
disallowClosing?: boolean;
|
||||
noBackground?: boolean;
|
||||
dontModal?: boolean;
|
||||
padding?: boolean;
|
||||
disallowClosing?: boolean;
|
||||
noBackground?: boolean;
|
||||
dontModal?: boolean;
|
||||
padding?: boolean;
|
||||
|
||||
onClose: () => void;
|
||||
actions?: Action[];
|
||||
disabled?: boolean;
|
||||
border?: boolean;
|
||||
visible: boolean;
|
||||
onClose: () => void;
|
||||
actions?: Action[];
|
||||
disabled?: boolean;
|
||||
border?: boolean;
|
||||
visible: boolean;
|
||||
}
|
||||
|
||||
export default function Modal(props: Props) {
|
||||
if (!props.visible) return null;
|
||||
if (!props.visible) return null;
|
||||
|
||||
let content = (
|
||||
<ModalContent
|
||||
attachment={!!props.actions}
|
||||
noBackground={props.noBackground}
|
||||
border={props.border}
|
||||
padding={props.padding ?? !props.dontModal}>
|
||||
{props.title && <h3>{props.title}</h3>}
|
||||
{props.children}
|
||||
</ModalContent>
|
||||
);
|
||||
let content = (
|
||||
<ModalContent
|
||||
attachment={!!props.actions}
|
||||
noBackground={props.noBackground}
|
||||
border={props.border}
|
||||
padding={props.padding ?? !props.dontModal}>
|
||||
{props.title && <h3>{props.title}</h3>}
|
||||
{props.children}
|
||||
</ModalContent>
|
||||
);
|
||||
|
||||
if (props.dontModal) {
|
||||
return content;
|
||||
}
|
||||
if (props.dontModal) {
|
||||
return content;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (props.disallowClosing) return;
|
||||
useEffect(() => {
|
||||
if (props.disallowClosing) return;
|
||||
|
||||
function keyDown(e: KeyboardEvent) {
|
||||
if (e.key === "Escape") {
|
||||
props.onClose();
|
||||
}
|
||||
}
|
||||
function keyDown(e: KeyboardEvent) {
|
||||
if (e.key === "Escape") {
|
||||
props.onClose();
|
||||
}
|
||||
}
|
||||
|
||||
document.body.addEventListener("keydown", keyDown);
|
||||
return () => document.body.removeEventListener("keydown", keyDown);
|
||||
}, [ props.disallowClosing, props.onClose ]);
|
||||
document.body.addEventListener("keydown", keyDown);
|
||||
return () => document.body.removeEventListener("keydown", keyDown);
|
||||
}, [props.disallowClosing, props.onClose]);
|
||||
|
||||
let confirmationAction = props.actions?.find(action => action.confirmation);
|
||||
useEffect(() => {
|
||||
if (!confirmationAction) return;
|
||||
let confirmationAction = props.actions?.find(
|
||||
(action) => action.confirmation,
|
||||
);
|
||||
useEffect(() => {
|
||||
if (!confirmationAction) return;
|
||||
|
||||
// ! FIXME: this may be done better if we
|
||||
// ! can focus the button although that
|
||||
// ! doesn't seem to work...
|
||||
function keyDown(e: KeyboardEvent) {
|
||||
if (e.key === "Enter") {
|
||||
confirmationAction!.onClick();
|
||||
}
|
||||
}
|
||||
// ! FIXME: this may be done better if we
|
||||
// ! can focus the button although that
|
||||
// ! doesn't seem to work...
|
||||
function keyDown(e: KeyboardEvent) {
|
||||
if (e.key === "Enter") {
|
||||
confirmationAction!.onClick();
|
||||
}
|
||||
}
|
||||
|
||||
document.body.addEventListener("keydown", keyDown);
|
||||
return () => document.body.removeEventListener("keydown", keyDown);
|
||||
}, [ confirmationAction ]);
|
||||
document.body.addEventListener("keydown", keyDown);
|
||||
return () => document.body.removeEventListener("keydown", keyDown);
|
||||
}, [confirmationAction]);
|
||||
|
||||
return createPortal(
|
||||
<ModalBase onClick={(!props.disallowClosing && props.onClose) || undefined}>
|
||||
<ModalContainer onClick={e => (e.cancelBubble = true)}>
|
||||
{content}
|
||||
{props.actions && (
|
||||
<ModalActions>
|
||||
{props.actions.map(x => (
|
||||
<Button
|
||||
contrast={x.contrast ?? true}
|
||||
error={x.error ?? false}
|
||||
onClick={x.onClick}
|
||||
disabled={props.disabled}>
|
||||
{x.text}
|
||||
</Button>
|
||||
))}
|
||||
</ModalActions>
|
||||
)}
|
||||
</ModalContainer>
|
||||
</ModalBase>,
|
||||
document.body
|
||||
);
|
||||
return createPortal(
|
||||
<ModalBase
|
||||
onClick={(!props.disallowClosing && props.onClose) || undefined}>
|
||||
<ModalContainer onClick={(e) => (e.cancelBubble = true)}>
|
||||
{content}
|
||||
{props.actions && (
|
||||
<ModalActions>
|
||||
{props.actions.map((x) => (
|
||||
<Button
|
||||
contrast={x.contrast ?? true}
|
||||
error={x.error ?? false}
|
||||
onClick={x.onClick}
|
||||
disabled={props.disabled}>
|
||||
{x.text}
|
||||
</Button>
|
||||
))}
|
||||
</ModalActions>
|
||||
)}
|
||||
</ModalContainer>
|
||||
</ModalBase>,
|
||||
document.body,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,58 +1,64 @@
|
||||
import styled, { css } from "styled-components";
|
||||
import { Children } from "../../types/Preact";
|
||||
import { Text } from 'preact-i18n';
|
||||
|
||||
type Props = Omit<JSX.HTMLAttributes<HTMLDivElement>, 'children' | 'as'> & {
|
||||
error?: string;
|
||||
block?: boolean;
|
||||
spaced?: boolean;
|
||||
children?: Children;
|
||||
type?: "default" | "subtle" | "error";
|
||||
}
|
||||
import { Text } from "preact-i18n";
|
||||
|
||||
import { Children } from "../../types/Preact";
|
||||
|
||||
type Props = Omit<JSX.HTMLAttributes<HTMLDivElement>, "children" | "as"> & {
|
||||
error?: string;
|
||||
block?: boolean;
|
||||
spaced?: boolean;
|
||||
children?: Children;
|
||||
type?: "default" | "subtle" | "error";
|
||||
};
|
||||
|
||||
const OverlineBase = styled.div<Omit<Props, "children" | "error">>`
|
||||
display: inline;
|
||||
margin: 0.4em 0;
|
||||
display: inline;
|
||||
margin: 0.4em 0;
|
||||
|
||||
${ props => props.spaced && css`
|
||||
margin-top: 0.8em;
|
||||
` }
|
||||
${(props) =>
|
||||
props.spaced &&
|
||||
css`
|
||||
margin-top: 0.8em;
|
||||
`}
|
||||
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: var(--foreground);
|
||||
text-transform: uppercase;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: var(--foreground);
|
||||
text-transform: uppercase;
|
||||
|
||||
${(props) =>
|
||||
props.type === "subtle" &&
|
||||
css`
|
||||
font-size: 12px;
|
||||
color: var(--secondary-foreground);
|
||||
`}
|
||||
|
||||
${(props) =>
|
||||
props.type === "error" &&
|
||||
css`
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
color: var(--error);
|
||||
`}
|
||||
|
||||
${(props) =>
|
||||
props.type === "subtle" &&
|
||||
css`
|
||||
font-size: 12px;
|
||||
color: var(--secondary-foreground);
|
||||
`}
|
||||
|
||||
${(props) =>
|
||||
props.type === "error" &&
|
||||
css`
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
color: var(--error);
|
||||
`}
|
||||
|
||||
${(props) =>
|
||||
props.block &&
|
||||
css`
|
||||
display: block;
|
||||
`}
|
||||
props.block &&
|
||||
css`
|
||||
display: block;
|
||||
`}
|
||||
`;
|
||||
|
||||
export default function Overline(props: Props) {
|
||||
return (
|
||||
<OverlineBase {...props}>
|
||||
{props.children}
|
||||
{props.children && props.error && <> · </>}
|
||||
{props.error && <Overline type="error">
|
||||
<Text id={`error.${props.error}`}>{props.error}</Text>
|
||||
</Overline>}
|
||||
</OverlineBase>
|
||||
);
|
||||
return (
|
||||
<OverlineBase {...props}>
|
||||
{props.children}
|
||||
{props.children && props.error && <> · </>}
|
||||
{props.error && (
|
||||
<Overline type="error">
|
||||
<Text id={`error.${props.error}`}>{props.error}</Text>
|
||||
</Overline>
|
||||
)}
|
||||
</OverlineBase>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -21,83 +21,83 @@ const prRing = keyframes`
|
||||
`;
|
||||
|
||||
const PreloaderBase = styled.div`
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
display: grid;
|
||||
place-items: center;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
|
||||
.spinner {
|
||||
width: 58px;
|
||||
display: flex;
|
||||
text-align: center;
|
||||
margin: 100px auto 0;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.spinner {
|
||||
width: 58px;
|
||||
display: flex;
|
||||
text-align: center;
|
||||
margin: 100px auto 0;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.spinner > div {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
background-color: var(--tertiary-foreground);
|
||||
.spinner > div {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
background-color: var(--tertiary-foreground);
|
||||
|
||||
border-radius: 100%;
|
||||
display: inline-block;
|
||||
animation: ${skSpinner} 1.4s infinite ease-in-out both;
|
||||
}
|
||||
border-radius: 100%;
|
||||
display: inline-block;
|
||||
animation: ${skSpinner} 1.4s infinite ease-in-out both;
|
||||
}
|
||||
|
||||
.spinner div:nth-child(1) {
|
||||
animation-delay: -0.32s;
|
||||
}
|
||||
.spinner div:nth-child(1) {
|
||||
animation-delay: -0.32s;
|
||||
}
|
||||
|
||||
.spinner div:nth-child(2) {
|
||||
animation-delay: -0.16s;
|
||||
}
|
||||
.spinner div:nth-child(2) {
|
||||
animation-delay: -0.16s;
|
||||
}
|
||||
|
||||
.ring {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
width: 48px;
|
||||
height: 52px;
|
||||
}
|
||||
.ring {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
width: 48px;
|
||||
height: 52px;
|
||||
}
|
||||
|
||||
.ring div {
|
||||
width: 32px;
|
||||
margin: 8px;
|
||||
height: 32px;
|
||||
display: block;
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
box-sizing: border-box;
|
||||
border: 2px solid #fff;
|
||||
animation: ${prRing} 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
|
||||
border-color: #fff transparent transparent transparent;
|
||||
}
|
||||
.ring div {
|
||||
width: 32px;
|
||||
margin: 8px;
|
||||
height: 32px;
|
||||
display: block;
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
box-sizing: border-box;
|
||||
border: 2px solid #fff;
|
||||
animation: ${prRing} 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
|
||||
border-color: #fff transparent transparent transparent;
|
||||
}
|
||||
|
||||
.ring div:nth-child(1) {
|
||||
animation-delay: -0.45s;
|
||||
}
|
||||
.ring div:nth-child(1) {
|
||||
animation-delay: -0.45s;
|
||||
}
|
||||
|
||||
.ring div:nth-child(2) {
|
||||
animation-delay: -0.3s;
|
||||
}
|
||||
.ring div:nth-child(2) {
|
||||
animation-delay: -0.3s;
|
||||
}
|
||||
|
||||
.ring div:nth-child(3) {
|
||||
animation-delay: -0.15s;
|
||||
}
|
||||
.ring div:nth-child(3) {
|
||||
animation-delay: -0.15s;
|
||||
}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
type: 'spinner' | 'ring'
|
||||
type: "spinner" | "ring";
|
||||
}
|
||||
|
||||
export default function Preloader({ type }: Props) {
|
||||
return (
|
||||
<PreloaderBase>
|
||||
<div class={type}>
|
||||
<div />
|
||||
<div />
|
||||
<div />
|
||||
</div>
|
||||
</PreloaderBase>
|
||||
);
|
||||
return (
|
||||
<PreloaderBase>
|
||||
<div class={type}>
|
||||
<div />
|
||||
<div />
|
||||
<div />
|
||||
</div>
|
||||
</PreloaderBase>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,111 +1,111 @@
|
||||
import { Children } from "../../types/Preact";
|
||||
import styled, { css } from "styled-components";
|
||||
import { Circle } from "@styled-icons/boxicons-regular";
|
||||
import styled, { css } from "styled-components";
|
||||
|
||||
import { Children } from "../../types/Preact";
|
||||
|
||||
interface Props {
|
||||
children: Children;
|
||||
description?: Children;
|
||||
children: Children;
|
||||
description?: Children;
|
||||
|
||||
checked: boolean;
|
||||
disabled?: boolean;
|
||||
onSelect: () => void;
|
||||
checked: boolean;
|
||||
disabled?: boolean;
|
||||
onSelect: () => void;
|
||||
}
|
||||
|
||||
interface BaseProps {
|
||||
selected: boolean;
|
||||
selected: boolean;
|
||||
}
|
||||
|
||||
const RadioBase = styled.label<BaseProps>`
|
||||
gap: 4px;
|
||||
z-index: 1;
|
||||
padding: 4px;
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
z-index: 1;
|
||||
padding: 4px;
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
align-items: center;
|
||||
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
user-select: none;
|
||||
border-radius: 4px;
|
||||
transition: 0.2s ease all;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
user-select: none;
|
||||
border-radius: 4px;
|
||||
transition: 0.2s ease all;
|
||||
|
||||
&:hover {
|
||||
background: var(--hover);
|
||||
}
|
||||
&:hover {
|
||||
background: var(--hover);
|
||||
}
|
||||
|
||||
> input {
|
||||
display: none;
|
||||
}
|
||||
> input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
> div {
|
||||
margin: 4px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
display: grid;
|
||||
border-radius: 50%;
|
||||
place-items: center;
|
||||
background: var(--foreground);
|
||||
> div {
|
||||
margin: 4px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
display: grid;
|
||||
border-radius: 50%;
|
||||
place-items: center;
|
||||
background: var(--foreground);
|
||||
|
||||
svg {
|
||||
color: var(--foreground);
|
||||
/*stroke-width: 2;*/
|
||||
}
|
||||
}
|
||||
svg {
|
||||
color: var(--foreground);
|
||||
/*stroke-width: 2;*/
|
||||
}
|
||||
}
|
||||
|
||||
${(props) =>
|
||||
props.selected &&
|
||||
css`
|
||||
color: white;
|
||||
cursor: default;
|
||||
background: var(--accent);
|
||||
${(props) =>
|
||||
props.selected &&
|
||||
css`
|
||||
color: white;
|
||||
cursor: default;
|
||||
background: var(--accent);
|
||||
|
||||
> div {
|
||||
background: white;
|
||||
}
|
||||
> div {
|
||||
background: white;
|
||||
}
|
||||
|
||||
> div svg {
|
||||
color: var(--accent);
|
||||
}
|
||||
> div svg {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: var(--accent);
|
||||
}
|
||||
`}
|
||||
&:hover {
|
||||
background: var(--accent);
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
const RadioDescription = styled.span<BaseProps>`
|
||||
font-size: 0.8em;
|
||||
font-weight: 400;
|
||||
color: var(--secondary-foreground);
|
||||
font-size: 0.8em;
|
||||
font-weight: 400;
|
||||
color: var(--secondary-foreground);
|
||||
|
||||
${(props) =>
|
||||
props.selected &&
|
||||
css`
|
||||
color: white;
|
||||
`}
|
||||
${(props) =>
|
||||
props.selected &&
|
||||
css`
|
||||
color: white;
|
||||
`}
|
||||
`;
|
||||
|
||||
export default function Radio(props: Props) {
|
||||
return (
|
||||
<RadioBase
|
||||
selected={props.checked}
|
||||
disabled={props.disabled}
|
||||
onClick={() =>
|
||||
!props.disabled && props.onSelect && props.onSelect()
|
||||
}
|
||||
>
|
||||
<div>
|
||||
<Circle size={12} />
|
||||
</div>
|
||||
<input type="radio" checked={props.checked} />
|
||||
<span>
|
||||
<span>{props.children}</span>
|
||||
{props.description && (
|
||||
<RadioDescription selected={props.checked}>
|
||||
{props.description}
|
||||
</RadioDescription>
|
||||
)}
|
||||
</span>
|
||||
</RadioBase>
|
||||
);
|
||||
return (
|
||||
<RadioBase
|
||||
selected={props.checked}
|
||||
disabled={props.disabled}
|
||||
onClick={() =>
|
||||
!props.disabled && props.onSelect && props.onSelect()
|
||||
}>
|
||||
<div>
|
||||
<Circle size={12} />
|
||||
</div>
|
||||
<input type="radio" checked={props.checked} />
|
||||
<span>
|
||||
<span>{props.children}</span>
|
||||
{props.description && (
|
||||
<RadioDescription selected={props.checked}>
|
||||
{props.description}
|
||||
</RadioDescription>
|
||||
)}
|
||||
</span>
|
||||
</RadioBase>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import styled, { css } from "styled-components";
|
||||
|
||||
export interface TextAreaProps {
|
||||
code?: boolean;
|
||||
padding?: number;
|
||||
lineHeight?: number;
|
||||
hideBorder?: boolean;
|
||||
code?: boolean;
|
||||
padding?: number;
|
||||
lineHeight?: number;
|
||||
hideBorder?: boolean;
|
||||
}
|
||||
|
||||
export const TEXT_AREA_BORDER_WIDTH = 2;
|
||||
@@ -12,37 +12,46 @@ export const DEFAULT_TEXT_AREA_PADDING = 16;
|
||||
export const DEFAULT_LINE_HEIGHT = 20;
|
||||
|
||||
export default styled.textarea<TextAreaProps>`
|
||||
width: 100%;
|
||||
resize: none;
|
||||
display: block;
|
||||
color: var(--foreground);
|
||||
background: var(--secondary-background);
|
||||
padding: ${ props => props.padding ?? DEFAULT_TEXT_AREA_PADDING }px;
|
||||
line-height: ${ props => props.lineHeight ?? DEFAULT_LINE_HEIGHT }px;
|
||||
width: 100%;
|
||||
resize: none;
|
||||
display: block;
|
||||
color: var(--foreground);
|
||||
background: var(--secondary-background);
|
||||
padding: ${(props) => props.padding ?? DEFAULT_TEXT_AREA_PADDING}px;
|
||||
line-height: ${(props) => props.lineHeight ?? DEFAULT_LINE_HEIGHT}px;
|
||||
|
||||
${ props => props.hideBorder && css`
|
||||
border: none;
|
||||
` }
|
||||
${(props) =>
|
||||
props.hideBorder &&
|
||||
css`
|
||||
border: none;
|
||||
`}
|
||||
|
||||
${ props => !props.hideBorder && css`
|
||||
border-radius: 4px;
|
||||
transition: border-color .2s ease-in-out;
|
||||
border: ${TEXT_AREA_BORDER_WIDTH}px solid transparent;
|
||||
` }
|
||||
${(props) =>
|
||||
!props.hideBorder &&
|
||||
css`
|
||||
border-radius: 4px;
|
||||
transition: border-color 0.2s ease-in-out;
|
||||
border: ${TEXT_AREA_BORDER_WIDTH}px solid transparent;
|
||||
`}
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
outline: none;
|
||||
|
||||
${ props => !props.hideBorder && css`
|
||||
border: ${TEXT_AREA_BORDER_WIDTH}px solid var(--accent);
|
||||
` }
|
||||
}
|
||||
${(props) =>
|
||||
!props.hideBorder &&
|
||||
css`
|
||||
border: ${TEXT_AREA_BORDER_WIDTH}px solid var(--accent);
|
||||
`}
|
||||
}
|
||||
|
||||
${ props => props.code ? css`
|
||||
font-family: var(--monoscape-font-font), monospace;
|
||||
` : css`
|
||||
font-family: inherit;
|
||||
` }
|
||||
|
||||
font-variant-ligatures: var(--ligatures);
|
||||
${(props) =>
|
||||
props.code
|
||||
? css`
|
||||
font-family: var(--monoscape-font-font), monospace;
|
||||
`
|
||||
: css`
|
||||
font-family: inherit;
|
||||
`}
|
||||
|
||||
font-variant-ligatures: var(--ligatures);
|
||||
`;
|
||||
|
||||
@@ -1,65 +1,69 @@
|
||||
import { Children } from "../../types/Preact";
|
||||
import styled, { css } from "styled-components";
|
||||
import { InfoCircle } from "@styled-icons/boxicons-regular";
|
||||
import styled, { css } from "styled-components";
|
||||
|
||||
import { Children } from "../../types/Preact";
|
||||
|
||||
interface Props {
|
||||
warning?: boolean
|
||||
error?: boolean
|
||||
warning?: boolean;
|
||||
error?: boolean;
|
||||
}
|
||||
|
||||
export const Separator = styled.div<Props>`
|
||||
height: 1px;
|
||||
width: calc(100% - 10px);
|
||||
background: var(--secondary-header);
|
||||
margin: 18px auto;
|
||||
height: 1px;
|
||||
width: calc(100% - 10px);
|
||||
background: var(--secondary-header);
|
||||
margin: 18px auto;
|
||||
`;
|
||||
|
||||
export const TipBase = styled.div<Props>`
|
||||
display: flex;
|
||||
padding: 12px;
|
||||
overflow: hidden;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
padding: 12px;
|
||||
overflow: hidden;
|
||||
align-items: center;
|
||||
|
||||
font-size: 14px;
|
||||
border-radius: 7px;
|
||||
background: var(--primary-header);
|
||||
border: 2px solid var(--secondary-header);
|
||||
font-size: 14px;
|
||||
border-radius: 7px;
|
||||
background: var(--primary-header);
|
||||
border: 2px solid var(--secondary-header);
|
||||
|
||||
a {
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
a {
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
flex-shrink: 0;
|
||||
margin-inline-end: 10px;
|
||||
}
|
||||
svg {
|
||||
flex-shrink: 0;
|
||||
margin-inline-end: 10px;
|
||||
}
|
||||
|
||||
${ props => props.warning && css`
|
||||
color: var(--warning);
|
||||
border: 2px solid var(--warning);
|
||||
background: var(--secondary-header);
|
||||
` }
|
||||
${(props) =>
|
||||
props.warning &&
|
||||
css`
|
||||
color: var(--warning);
|
||||
border: 2px solid var(--warning);
|
||||
background: var(--secondary-header);
|
||||
`}
|
||||
|
||||
${ props => props.error && css`
|
||||
color: var(--error);
|
||||
border: 2px solid var(--error);
|
||||
background: var(--secondary-header);
|
||||
` }
|
||||
${(props) =>
|
||||
props.error &&
|
||||
css`
|
||||
color: var(--error);
|
||||
border: 2px solid var(--error);
|
||||
background: var(--secondary-header);
|
||||
`}
|
||||
`;
|
||||
|
||||
export default function Tip(props: Props & { children: Children }) {
|
||||
const { children, ...tipProps } = props;
|
||||
return (
|
||||
<>
|
||||
<Separator />
|
||||
<TipBase {...tipProps}>
|
||||
<InfoCircle size={20} />
|
||||
<span>{props.children}</span>
|
||||
</TipBase>
|
||||
</>
|
||||
|
||||
);
|
||||
const { children, ...tipProps } = props;
|
||||
return (
|
||||
<>
|
||||
<Separator />
|
||||
<TipBase {...tipProps}>
|
||||
<InfoCircle size={20} />
|
||||
<span>{props.children}</span>
|
||||
</TipBase>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user