some restructuring and added a color-scheme-picker
This commit is contained in:
parent
c02fb2d924
commit
fa9264326b
11 changed files with 137 additions and 103 deletions
|
@ -23,6 +23,11 @@ export default defineConfig({
|
||||||
navigateFallback: 'index.html',
|
navigateFallback: 'index.html',
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
|
solid: {
|
||||||
|
babel: {
|
||||||
|
compact: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
77
src/app.css
77
src/app.css
|
@ -1,22 +1,22 @@
|
||||||
@import url("https://fonts.googleapis.com/css2?family=Roboto+Flex:opsz,wght,GRAD@8..144,400,45;8..144,400,50;8..144,1000,0&family=Roboto+Serif:opsz,GRAD@8..144,71&display=swap");
|
@import url("https://fonts.googleapis.com/css2?family=Roboto+Flex:opsz,wght,GRAD@8..144,400,45;8..144,400,50;8..144,1000,0&family=Roboto+Serif:opsz,GRAD@8..144,71&display=swap");
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--surface-1: #ddd;
|
--surface-1: light-dark(#ddd, #222);
|
||||||
--surface-2: #e8e8e8;
|
--surface-2: light-dark(#e8e8e8, #282828);
|
||||||
--surface-3: #eee;
|
--surface-3: light-dark(#eee, #333);
|
||||||
--surface-4: #f8f8f8;
|
--surface-4: light-dark(#f8f8f8, #383838);
|
||||||
--surface-5: #fff;
|
--surface-5: light-dark(#fff, #444);
|
||||||
--text-1: #222;
|
--text-1: light-dark(#222, #eee);
|
||||||
--text-2: #282828;
|
--text-2: light-dark(#282828, #d8d8d8);
|
||||||
--primary: #41c6b3;
|
--primary: light-dark(#41c6b3, #6be8d6);
|
||||||
|
|
||||||
color: var(--text-1);
|
color: var(--text-1);
|
||||||
accent-color: var(--primary);
|
accent-color: var(--primary);
|
||||||
|
|
||||||
--info: oklch(.71 .17 249);
|
--info: light-dark(oklch(.71 .17 249), oklch(.71 .17 249));
|
||||||
--fail: oklch(.64 .21 25.3);
|
--fail: light-dark(oklch(.64 .21 25.3), oklch(.64 .21 25.3));
|
||||||
--warn: oklch(.82 .18 78.9);
|
--warn: light-dark(oklch(.82 .18 78.9), oklch(.82 .18 78.9));
|
||||||
--succ: oklch(.86 .28 150);
|
--succ: light-dark(oklch(.86 .28 150), oklch(.86 .28 150));
|
||||||
|
|
||||||
--radii-s: .125em;
|
--radii-s: .125em;
|
||||||
--radii-m: .25em;
|
--radii-m: .25em;
|
||||||
|
@ -33,25 +33,6 @@
|
||||||
--padding-l: 1em;
|
--padding-l: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
:root {
|
|
||||||
--surface-1: #222;
|
|
||||||
--surface-2: #282828;
|
|
||||||
--surface-3: #333;
|
|
||||||
--surface-4: #383838;
|
|
||||||
--surface-5: #444;
|
|
||||||
--text-1: #eee;
|
|
||||||
--text-2: #d8d8d8;
|
|
||||||
|
|
||||||
--primary: #6be8d6;
|
|
||||||
|
|
||||||
--info: oklch(.71 .17 249);
|
|
||||||
--fail: oklch(.64 .21 25.3);
|
|
||||||
--warn: oklch(.82 .18 78.9);
|
|
||||||
--succ: oklch(.86 .28 150);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
html {
|
html {
|
||||||
inline-size: 100%;
|
inline-size: 100%;
|
||||||
block-size: 100%;
|
block-size: 100%;
|
||||||
|
@ -80,40 +61,6 @@ body {
|
||||||
outline: 1px solid var(--info);
|
outline: 1px solid var(--info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
& .menu-root {
|
|
||||||
display: grid;
|
|
||||||
grid-auto-flow: column;
|
|
||||||
justify-content: start;
|
|
||||||
position: relative;
|
|
||||||
z-index: 10;
|
|
||||||
|
|
||||||
gap: .5em;
|
|
||||||
padding-inline-start: 1em;
|
|
||||||
block-size: 2em;
|
|
||||||
|
|
||||||
background-color: var(--surface-3);
|
|
||||||
color: var(--text-1);
|
|
||||||
|
|
||||||
& > .logo {
|
|
||||||
inline-size: 3em;
|
|
||||||
block-size: 3em;
|
|
||||||
padding: .75em;
|
|
||||||
margin-block-end: -1em;
|
|
||||||
background-color: inherit;
|
|
||||||
color: inherit;
|
|
||||||
border-radius: .25em;
|
|
||||||
|
|
||||||
& > svg {
|
|
||||||
inline-size: 100%;
|
|
||||||
block-size: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
& > div {
|
|
||||||
display: contents;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
|
|
4
src/components/colorschemepicker.module.css
Normal file
4
src/components/colorschemepicker.module.css
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
.picker {
|
||||||
|
border: none;
|
||||||
|
background-color: var(--surface-3);
|
||||||
|
}
|
43
src/components/colorschemepicker.tsx
Normal file
43
src/components/colorschemepicker.tsx
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
import { Accessor, Component, createEffect, createSignal, For, Setter } from "solid-js";
|
||||||
|
import css from './colorschemepicker.module.css';
|
||||||
|
|
||||||
|
export enum ColorScheme {
|
||||||
|
Auto = 'light dark',
|
||||||
|
Light = 'light',
|
||||||
|
Dark = 'dark',
|
||||||
|
}
|
||||||
|
|
||||||
|
const colorSchemeEntries = [
|
||||||
|
[ColorScheme.Auto, 'Auto'],
|
||||||
|
[ColorScheme.Light, 'Light'],
|
||||||
|
[ColorScheme.Dark, 'Dark'],
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
interface ColorSchemePickerProps {
|
||||||
|
value?: Setter<ColorScheme> | [Accessor<ColorScheme>, Setter<ColorScheme>];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ColorSchemePicker: Component<ColorSchemePickerProps> = (props) => {
|
||||||
|
const [value, setValue] = createSignal<ColorScheme>(ColorScheme.Auto);
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
const currentValue = value();
|
||||||
|
const setter = props.value instanceof Array ? props.value[1] : props.value;
|
||||||
|
|
||||||
|
if (!setter) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setter(currentValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
return <select class={css.picker} name="color-scheme-picker" value={value()} onInput={(e) => {
|
||||||
|
if (e.target.value !== value()) {
|
||||||
|
setValue(e.target.value as any);
|
||||||
|
}
|
||||||
|
}}>
|
||||||
|
<For each={colorSchemeEntries}>{
|
||||||
|
([value, label]) => <option value={value}>{label}</option>
|
||||||
|
}</For>
|
||||||
|
</select>;
|
||||||
|
};
|
|
@ -19,6 +19,7 @@
|
||||||
color: var(--text-2);
|
color: var(--text-2);
|
||||||
padding: var(--padding-m) var(--padding-l);
|
padding: var(--padding-m) var(--padding-l);
|
||||||
border: none;
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
background-color: var(--surface-3);
|
background-color: var(--surface-3);
|
||||||
|
|
|
@ -24,6 +24,15 @@
|
||||||
color: var(--text-1);
|
color: var(--text-1);
|
||||||
border-color: var(--text-2);
|
border-color: var(--text-2);
|
||||||
border-radius: var(--radii-s);
|
border-radius: var(--radii-s);
|
||||||
|
|
||||||
|
&:has(::spelling-error, ::grammar-error) {
|
||||||
|
border-color: var(--fail);
|
||||||
|
}
|
||||||
|
|
||||||
|
& ::spelling-error {
|
||||||
|
outline: 1px solid var(--fail);
|
||||||
|
text-decoration: yellow underline;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
& .cell {
|
& .cell {
|
||||||
|
|
|
@ -218,26 +218,13 @@ const TextArea: Component<{ key: string, value: string, lang: string, oninput?:
|
||||||
mutate();
|
mutate();
|
||||||
};
|
};
|
||||||
|
|
||||||
createEffect(() => {
|
|
||||||
props.value;
|
|
||||||
|
|
||||||
resize();
|
|
||||||
});
|
|
||||||
|
|
||||||
const observer = new MutationObserver((e) => {
|
|
||||||
if (element()?.isConnected) {
|
|
||||||
resize();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
observer.observe(document.body, { childList: true, subtree: true });
|
|
||||||
|
|
||||||
return <textarea
|
return <textarea
|
||||||
ref={setElement}
|
ref={setElement}
|
||||||
value={props.value}
|
value={props.value}
|
||||||
lang={props.lang}
|
lang={props.lang}
|
||||||
placeholder={props.lang}
|
placeholder={`${props.key} in ${props.lang}`}
|
||||||
name={`${props.key}:${props.lang}`}
|
name={`${props.key}:${props.lang}`}
|
||||||
spellcheck
|
spellcheck={true}
|
||||||
wrap="soft"
|
wrap="soft"
|
||||||
onkeyup={onKeyUp}
|
onkeyup={onKeyUp}
|
||||||
on:keydown={e => e.stopPropagation()}
|
on:keydown={e => e.stopPropagation()}
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
.root {
|
||||||
|
& > div {
|
||||||
|
display: contents;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.item {
|
.item {
|
||||||
padding: var(--padding-m) var(--padding-l);
|
padding: var(--padding-m) var(--padding-l);
|
||||||
|
|
||||||
|
|
|
@ -189,10 +189,9 @@ declare module "solid-js" {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const asMenuRoot = (element: Element) => {
|
const Mount: Component = (props) => {
|
||||||
const menu = useMenu();
|
const menu = useMenu();
|
||||||
|
|
||||||
const c = 'menu-root';
|
|
||||||
const listener = (e: KeyboardEvent) => {
|
const listener = (e: KeyboardEvent) => {
|
||||||
const key = e.key.toLowerCase();
|
const key = e.key.toLowerCase();
|
||||||
const modifiers =
|
const modifiers =
|
||||||
|
@ -214,20 +213,10 @@ export const asMenuRoot = (element: Element) => {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
onMount(() => {
|
return <div class={css.root} ref={menu.setRef} onKeyDown={listener}></div>;
|
||||||
element.classList.add(c);
|
|
||||||
document.addEventListener('keydown', listener);
|
|
||||||
});
|
|
||||||
|
|
||||||
onCleanup(() => {
|
|
||||||
element.classList.remove(c);
|
|
||||||
document.removeEventListener('keydown', listener);
|
|
||||||
});
|
|
||||||
|
|
||||||
menu.setRef(element);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Menu = { Root, Item, Separator } as const;
|
export const Menu = { Mount, Root, Item, Separator } as const;
|
||||||
|
|
||||||
export interface CommandPaletteApi {
|
export interface CommandPaletteApi {
|
||||||
readonly open: Accessor<boolean>;
|
readonly open: Accessor<boolean>;
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
import { Title } from "@solidjs/meta";
|
import { Meta, Title } from "@solidjs/meta";
|
||||||
import { createSignal, ParentProps, Show } from "solid-js";
|
import { createSignal, For, ParentProps, Show } from "solid-js";
|
||||||
import { BsTranslate } from "solid-icons/bs";
|
import { BsTranslate } from "solid-icons/bs";
|
||||||
import { FilesProvider } from "~/features/file";
|
import { FilesProvider } from "~/features/file";
|
||||||
import { CommandPalette, CommandPaletteApi, MenuProvider, asMenuRoot } from "~/features/menu";
|
import { CommandPalette, CommandPaletteApi, Menu, MenuProvider } from "~/features/menu";
|
||||||
import { isServer } from "solid-js/web";
|
import { isServer } from "solid-js/web";
|
||||||
import { A } from "@solidjs/router";
|
import { A } from "@solidjs/router";
|
||||||
import { createCommand, Modifier } from "~/features/command";
|
import { createCommand, Modifier } from "~/features/command";
|
||||||
|
import { ColorScheme, ColorSchemePicker } from "~/components/colorschemepicker";
|
||||||
import css from "./editor.module.css";
|
import css from "./editor.module.css";
|
||||||
|
|
||||||
asMenuRoot // prevents removal of import
|
|
||||||
|
|
||||||
export default function Editor(props: ParentProps) {
|
export default function Editor(props: ParentProps) {
|
||||||
const [commandPalette, setCommandPalette] = createSignal<CommandPaletteApi>();
|
const [commandPalette, setCommandPalette] = createSignal<CommandPaletteApi>();
|
||||||
|
const [colorScheme, setColorScheme] = createSignal<ColorScheme>(ColorScheme.Auto);
|
||||||
|
|
||||||
const supported = isServer || typeof window.showDirectoryPicker === 'function';
|
const supported = isServer || typeof window.showDirectoryPicker === 'function';
|
||||||
const commands = [
|
const commands = [
|
||||||
|
@ -22,10 +22,17 @@ export default function Editor(props: ParentProps) {
|
||||||
|
|
||||||
return <MenuProvider commands={commands}>
|
return <MenuProvider commands={commands}>
|
||||||
<Title>Translation-Tool</Title>
|
<Title>Translation-Tool</Title>
|
||||||
|
<Meta name="color-scheme" content={colorScheme()} />
|
||||||
|
|
||||||
<main class={css.layout} inert={commandPalette()?.open()}>
|
<main class={css.layout} inert={commandPalette()?.open()}>
|
||||||
<nav use:asMenuRoot>
|
<nav class={css.menu}>
|
||||||
<A class="logo" href="/"><BsTranslate /></A>
|
<A class={css.logo} href="/"><BsTranslate /></A>
|
||||||
|
|
||||||
|
<Menu.Mount />
|
||||||
|
|
||||||
|
<section class={css.right}>
|
||||||
|
<ColorSchemePicker value={[colorScheme, setColorScheme]} />
|
||||||
|
</section>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<Show when={supported} fallback={<span>too bad, so sad. Your browser does not support the File Access API</span>}>
|
<Show when={supported} fallback={<span>too bad, so sad. Your browser does not support the File Access API</span>}>
|
||||||
|
|
|
@ -5,4 +5,40 @@
|
||||||
block-size: 100%;
|
block-size: 100%;
|
||||||
overflow: clip;
|
overflow: clip;
|
||||||
background-color: var(--surface-1);
|
background-color: var(--surface-1);
|
||||||
|
|
||||||
|
.menu {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto minmax(0, 1fr) auto;
|
||||||
|
grid-auto-flow: column;
|
||||||
|
justify-content: start;
|
||||||
|
position: relative;
|
||||||
|
z-index: 10;
|
||||||
|
|
||||||
|
gap: .5em;
|
||||||
|
padding-inline: 1em;
|
||||||
|
block-size: 2em;
|
||||||
|
|
||||||
|
background-color: var(--surface-3);
|
||||||
|
color: var(--text-1);
|
||||||
|
|
||||||
|
& > .logo {
|
||||||
|
inline-size: 3em;
|
||||||
|
block-size: 3em;
|
||||||
|
padding: .75em;
|
||||||
|
margin-block-end: -1em;
|
||||||
|
background-color: inherit;
|
||||||
|
color: inherit;
|
||||||
|
border-radius: .25em;
|
||||||
|
|
||||||
|
& > svg {
|
||||||
|
inline-size: 100%;
|
||||||
|
block-size: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& .right {
|
||||||
|
display: grid;
|
||||||
|
align-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Add table
Add a link
Reference in a new issue