Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions frontend/viewer/src/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

@utility animate-shimmer {
animation: shimmer 2s linear infinite;
background-image: linear-gradient(90deg, hsl(var(--muted-foreground)/0.2) 20%, hsl(var(--foreground)/0.3) 50%, hsl(var(--muted-foreground)/0.2) 65%);
background-image: linear-gradient(90deg, oklch(from var(--muted-foreground) l c h / 0.2) 20%, oklch(from var(--foreground) l c h / 0.3) 50%, oklch(from var(--muted-foreground) l c h / 0.2) 65%);
background-size: 200% 100%;
background-repeat: repeat;
}
Expand All @@ -35,7 +35,9 @@
@layer base {
/* shadcn generated styles */
* {
@apply border-border;
@apply border-border outline-ring/50;
/* EDIT: Fixes dark mode gets "outline-style: none" which results in white (browser bug?) */
@apply focus-visible:outline-solid;
}
.app {
@apply bg-background text-foreground;
Expand Down
4 changes: 2 additions & 2 deletions frontend/viewer/src/home/AppBar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
linear-gradient is just a way to use a solid color and work around: "Only the last background can include a background color."
See: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_backgrounds_and_borders/Using_multiple_backgrounds
*/
linear-gradient(hsl(var(--primary) / 0.4)),
hsl(var(--background));
linear-gradient(oklch(from var(--primary) l c h / 0.4)),
var(--background);
}
</style>
2 changes: 1 addition & 1 deletion frontend/viewer/src/home/HomeView.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@
<Button href="/swagger" variant="ghost" size="icon" icon="i-mdi-api" target="_blank" title="Swagger"/>
</DevContent>
<LocalizationPicker/>
<ThemePicker buttonProps={{variant: 'outline'}}/>
<ThemePicker />
<ResponsiveMenu.Root>
<ResponsiveMenu.Trigger/>
<ResponsiveMenu.Content>
Expand Down
2 changes: 1 addition & 1 deletion frontend/viewer/src/home/ProjectListItem.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

{#if skeleton || !project}
<ListItem {...rest}
class="animate-pulse dark:bg-muted/50 bg-muted/80 hover:bg-muted/30 hover:dark:bg-muted dark:text-neutral-50/50 cursor-default text-neutral-500">
class="animate-pulse hover:bg-muted/30 hover:dark:bg-muted dark:text-neutral-50/50 cursor-default text-neutral-500">
<div class="h-4 dark:bg-neutral-50/50 bg-neutral-500 rounded-full w-32"></div>
<div class="h-3 mt-3 dark:bg-neutral-50/50 bg-neutral-500 rounded-full w-20"></div>
</ListItem>
Expand Down
7 changes: 5 additions & 2 deletions frontend/viewer/src/lib/components/ListItem.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,11 @@
data-skeleton={skeleton || undefined}
class={cn(
'w-full max-w-full px-4 py-3 flex text-left overflow-hidden items-center gap-4',
'dark:bg-muted/50 bg-muted/80 hover:bg-primary/15 hover:dark:bg-primary/15 aria-selected:ring-2 ring-primary ring-offset-background rounded',
'shadow hover:shadow-lg hover:z-10',
'bg-muted rounded outline-none shadow-sm hover:shadow-md hover:z-10',
'focus-visible:ring-[3px] focus-visible:ring-ring/50',
'border-l-5 border-l-transparent aria-selected:border-l-primary',
Comment thread
myieye marked this conversation as resolved.
'hover:bg-primary/15 aria-selected:bg-primary/15',
'dark:hover:bg-primary/25 aria-selected:dark:bg-primary/25',
'disabled:pointer-events-none disabled:contrast-[0.8]',
loading && 'animate-pulse',
skeleton && 'cursor-default hover:bg-transparent pointer-events-none shadow-none',
Expand Down
22 changes: 14 additions & 8 deletions frontend/viewer/src/lib/components/ThemePicker.svelte
Original file line number Diff line number Diff line change
@@ -1,32 +1,38 @@
<script module lang="ts">
import {msg} from 'svelte-i18n-lingui';

export const themes = [msg`green`, msg`blue`, msg`rose`, msg`orange`, msg`violet`, msg`stone`];
</script>

<script lang="ts">
import {Button, type ButtonProps, XButton} from '$lib/components/ui/button';
import {Icon} from '$lib/components/ui/icon';
import {Label} from '$lib/components/ui/label/index.js';
import * as Popover from '$lib/components/ui/popover';
import {cn} from '$lib/utils';
import {mode, resetMode, setMode, setTheme, theme, userPrefersMode} from 'mode-watcher';
import {t, msg} from 'svelte-i18n-lingui';
import {mergeProps} from 'bits-ui';
import {t} from 'svelte-i18n-lingui';

const themes = [msg`green`, msg`blue`, msg`rose`, msg`orange`, msg`violet`, msg`stone`];
let {
buttonProps = {},
variant,
}: {
buttonProps?: Partial<ButtonProps>;
variant?: ButtonProps['variant'] & ('default' | 'ghost');
} = $props();

const triggerClass = $derived(variant === 'ghost' ? 'text-primary' : undefined);
</script>

<Popover.Root>
<Popover.Trigger>
{#snippet child({props})}
<Button variant="ghost" size="icon" {...mergeProps(props, buttonProps)}>
<Button {variant} size="icon" {...props}>
<Icon
icon="i-mdi-white-balance-sunny"
class="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0 text-primary"
class="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0 {triggerClass}"
/>
<Icon
icon="i-mdi-weather-night"
class="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100 text-primary"
class="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100 {triggerClass}"
/>
<span class="sr-only">{$t`Choose theme`}</span>
</Button>
Expand Down
5 changes: 3 additions & 2 deletions frontend/viewer/src/lib/components/ui/button/button.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@
import Anchor from '../anchor/anchor.svelte';

export const buttonVariants = tv({
base: "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:ring-[3px] disabled:pointer-events-none disabled:[&:not(.loading)]:opacity-50 aria-disabled:pointer-events-none aria-disabled:[&:not(.loading)]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
base: "focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:ring-[3px] disabled:pointer-events-none disabled:[&:not(.loading)]:opacity-50 aria-disabled:pointer-events-none aria-disabled:[&:not(.loading)]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
variants: {
variant: {
default: 'bg-primary text-primary-foreground hover:bg-primary/90 shadow-xs',
destructive:
'bg-destructive hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60 text-white shadow-xs',
/* all border classes have been moved here, because they were/are the only variant that actually uses them */
outline:
'bg-background hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 border shadow-xs',
'bg-background hover:bg-accent hover:text-accent-foreground dark:bg-input/30 focus-visible:border-ring border border-input aria-invalid:border-destructive dark:hover:bg-input/50 shadow-xs',
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80 shadow-xs',
ghost: 'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50',
link: 'text-primary underline-offset-4 hover:underline',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,9 @@

const stringPlaceholder = $derived(typeof placeholder === 'string' ? placeholder : undefined);
const snippetPlaceholder = $derived(typeof placeholder === 'function' ? placeholder : undefined);
const focusRingClass =
'has-[.real-input:focus-visible]:ring-ring has-[.real-input:focus-visible]:outline-none has-[.real-input:focus-visible]:ring-2 has-[.real-input:focus-visible]:ring-offset-2';
</script>

<InputShell bind:ref {focusRingClass} class={cn('gap-0', className)} {...restProps}>
<InputShell bind:ref class={cn('gap-0', className)} {...restProps}>
{@render before?.()}
<div class="grow flex relative overflow-hidden items-center h-full">
<Input
Expand Down
11 changes: 2 additions & 9 deletions frontend/viewer/src/lib/components/ui/input/input-shell.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,14 @@
import type {HTMLAttributes} from 'svelte/elements';
import {inputVariants} from './input.svelte';

interface Props extends WithElementRef<HTMLAttributes<HTMLDivElement>> {
focusRingClass?: string;
}

const anyChildHasFocusRing = 'ring-ring outline-none ring-offset-2 has-focus-visible:ring-2';

let {
ref = $bindable(null),
class: className,
focusRingClass = anyChildHasFocusRing,
children,
...restProps
}: Props = $props();
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>

<div bind:this={ref} class={cn(inputVariants({variant: 'shell'}), focusRingClass, className)} {...restProps}>
<div bind:this={ref} class={cn(inputVariants({variant: 'shell'}), className)} {...restProps}>
{@render children?.()}
</div>
12 changes: 8 additions & 4 deletions frontend/viewer/src/lib/components/ui/input/input.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,14 @@
'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]',
'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',
),
ghost: 'outline-none min-w-0 bg-transparent',
// shell variant has a dedicated component
shell:
'border-input bg-background ring-offset-background placeholder:text-muted-foreground flex h-10 w-full rounded-md border text-base has-disabled:cursor-not-allowed has-disabled:opacity-50 md:text-sm flex gap-2 items-center justify-between',
ghost: 'outline-none min-w-0 bg-transparent selection:bg-primary selection:text-primary-foreground',
// shell variant has a dedicated component. Users should apply the class "real-input" to a wrapped <input> to enable focus styles on the shell.
shell: cn(
'border-input bg-background selection:bg-primary dark:bg-input/30 selection:text-primary-foreground ring-offset-background placeholder:text-muted-foreground flex h-10 w-full min-w-0 rounded-md border text-base shadow-xs transition-[color,box-shadow] outline-none has-disabled:cursor-not-allowed has-disabled:opacity-50 md:text-sm',
'flex gap-2 items-center justify-between',
'has-[.real-input:focus-visible]:border-ring has-[.real-input:focus-visible]:ring-ring/50 has-[.real-input:focus-visible]:ring-[3px]',
'has-[.real-input:aria-invalid]:ring-destructive/20 dark:has-[.real-input:aria-invalid]:ring-destructive/40 has-[.real-input:aria-invalid]:border-destructive',
),
},
visibleFocus: {
off: '',
Expand Down
2 changes: 1 addition & 1 deletion frontend/viewer/src/project/ProjectSidebar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
<ProjectDropdown
onSelect={handleProjectSelect}
/>
<ThemePicker />
<ThemePicker variant="ghost" />
</div>
<div class="mx-auto">
{@render primaryAction.snippet?.(sidebar?.isOpen())}
Expand Down
2 changes: 1 addition & 1 deletion frontend/viewer/src/project/browse/EntryView.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@
{@render preview(entry)}
</div>
{/if}
<div class="max-md:p-2 md:px-2">
<div class="max-md:p-2 md:pb-2 md:px-2">
<EntryEditor bind:ref={editorRef} {entry} readonly={readonly || !features.write || deleted} {...entryPersistence.entryEditorProps} />
</div>
</ScrollArea>
Expand Down
5 changes: 3 additions & 2 deletions frontend/viewer/src/project/browse/sort/SortMenu.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,13 @@

<script lang="ts">
import {t} from 'svelte-i18n-lingui';
import { badgeVariants } from '$lib/components/ui/badge';
import {badgeVariants} from '$lib/components/ui/badge';
import * as ResponsiveMenu from '$lib/components/responsive-menu';
import {cn} from '$lib/utils';
import {watch, type Getter} from 'runed';
import {Icon} from '$lib/components/ui/icon';
import {sortOptions, type SortConfig} from './options';
import {buttonVariants} from '$lib/components/ui/button';

type Props = {
value?: SortConfig;
Expand All @@ -47,7 +48,7 @@
</script>

<ResponsiveMenu.Root>
<ResponsiveMenu.Trigger class={badgeVariants({ variant: 'secondary' })}>
<ResponsiveMenu.Trigger class={cn(buttonVariants({variant: 'secondary', size: 'xs'}), badgeVariants({ variant: 'secondary' }), 'border-none h-7')}>
{#snippet child({props})}
<button {...props}>
<Icon icon={sortIcons[sortField]?.[direction] ?? 'i-mdi-arrow-down'} class="size-4 mr-1" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,13 +226,13 @@ function calcWritingSystemColors(writingSystems: IWritingSystems): WritingSystem
}

const vernacularColors = [
'text-emerald-400 dark:text-emerald-300',
'text-fuchsia-600 dark:text-fuchsia-300',
'text-lime-600 dark:text-lime-200',
'text-emerald-700 dark:text-emerald-300',
'text-fuchsia-700 dark:text-fuchsia-300',
'text-amber-700 dark:text-amber-300',
] as const;

const analysisColors = [
'text-blue-500 dark:text-blue-300',
'text-yellow-500 dark:text-yellow-200',
'text-rose-500 dark:text-rose-400',
'text-blue-700 dark:text-blue-300',
'text-rose-700 dark:text-rose-400',
'text-lime-700 dark:text-lime-300',
] as const;
91 changes: 82 additions & 9 deletions frontend/viewer/src/project/demo/demo-entry-data.ts

Large diffs are not rendered by default.

80 changes: 80 additions & 0 deletions frontend/viewer/src/stories/editor/fields/ws-colors.stories.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<script module lang="ts">
import {defineMeta} from '@storybook/addon-svelte-csf';
import {onMount} from 'svelte';

const {Story} = defineMeta({});

</script>

<script lang="ts">
import {useWritingSystemService, type WritingSystemService} from '$project/data';
import {allWsEntry, writingSystems} from '$project/demo/demo-entry-data';
import DictionaryEntry from '$lib/components/dictionary/DictionaryEntry.svelte';

let wsService = $state<WritingSystemService>();
onMount(() => {
wsService = useWritingSystemService();
});
</script>

<Story name="All writing system colors">
{#snippet template()}
<div class="flex flex-col gap-8 p-4">
Comment thread
myieye marked this conversation as resolved.
<h2 class="text-lg font-bold">Dictionary Entry — Light & Dark</h2>
<div class="grid grid-cols-2 gap-4">
<div class="light rounded border p-4" data-theme="blue">
<h3 class="font-semibold mb-2">Light</h3>
<DictionaryEntry entry={allWsEntry} />
</div>
<div class="dark rounded border p-4 bg-background text-foreground" data-theme="blue">
<h3 class="font-semibold mb-2">Dark</h3>
<DictionaryEntry entry={allWsEntry} />
</div>
</div>

<hr class="border-t" />

<h2 class="text-lg font-bold">Color Swatches — Light & Dark</h2>
<div class="grid grid-cols-2 gap-4">
<div class="light rounded border p-4" data-theme="blue">
<h3 class="font-semibold mb-2">Light</h3>
<div class="flex flex-col gap-2">
<h4 class="text-sm font-medium">Vernacular</h4>
{#each writingSystems.vernacular as ws (ws.wsId)}
{@const color = wsService?.wsColor(ws.wsId, 'vernacular')}
<span class={color}>
{color}
</span>
{/each}
<h4 class="text-sm font-medium mt-2">Analysis</h4>
{#each writingSystems.analysis as ws (ws.wsId)}
{@const color = wsService?.wsColor(ws.wsId, 'analysis')}
<span class={color}>
{color}
</span>
{/each}
</div>
</div>
<div class="dark rounded border p-4 bg-background text-foreground" data-theme="blue">
<h3 class="font-semibold mb-2">Dark</h3>
<div class="flex flex-col gap-2">
<h4 class="text-sm font-medium">Vernacular</h4>
{#each writingSystems.vernacular as ws (ws.wsId)}
{@const color = wsService?.wsColor(ws.wsId, 'vernacular')}
<span class={color}>
{color}
</span>
{/each}
<h4 class="text-sm font-medium mt-2">Analysis</h4>
{#each writingSystems.analysis as ws (ws.wsId)}
{@const color = wsService?.wsColor(ws.wsId, 'analysis')}
<span class={color}>
{color}
</span>
{/each}
</div>
</div>
</div>
</div>
{/snippet}
</Story>
Loading
Loading