mirror of
https://github.com/Blah-IM/Weblah.git
synced 2025-07-06 22:15:34 +00:00
refactor: migrate to svelte 5, vite 6 and bits-ui 1.
This commit is contained in:
parent
0bb201636a
commit
1e95dc0830
45 changed files with 1069 additions and 793 deletions
|
@ -2,10 +2,15 @@
|
|||
import { tw } from '$lib/tw';
|
||||
import { patterns, type PatternName } from './BgPattern';
|
||||
|
||||
export let pattern: PatternName = 'rain';
|
||||
|
||||
let className: string = '';
|
||||
export { className as class };
|
||||
interface Props {
|
||||
pattern?: PatternName;
|
||||
class?: string;
|
||||
children?: import('svelte').Snippet;
|
||||
}
|
||||
|
||||
let { pattern = 'rain', class: className = '', children }: Props = $props();
|
||||
|
||||
</script>
|
||||
|
||||
<div
|
||||
|
@ -15,5 +20,5 @@
|
|||
)}
|
||||
style:--pattern-image={`url("${patterns[pattern]}")`}
|
||||
>
|
||||
<slot />
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
|
|
@ -1,42 +1,44 @@
|
|||
<script lang="ts">
|
||||
import { tw } from '$lib/tw';
|
||||
import type { HTMLAnchorAttributes, HTMLButtonAttributes } from 'svelte/elements';
|
||||
|
||||
type HTMLButtonOrAnchorAttributes = Partial<HTMLAnchorAttributes> & Partial<HTMLButtonAttributes>;
|
||||
|
||||
interface $$Props extends HTMLButtonOrAnchorAttributes {
|
||||
interface Props {
|
||||
variant?: 'primary' | 'secondary';
|
||||
class?: string | null;
|
||||
href?: string | null;
|
||||
children?: import('svelte').Snippet;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export let variant: $$Props['variant'] = 'secondary';
|
||||
let className: string | null = '';
|
||||
export { className as class };
|
||||
|
||||
export let href: string | null = null;
|
||||
let {
|
||||
variant = 'secondary',
|
||||
class: className = '',
|
||||
href = null,
|
||||
children,
|
||||
...rest
|
||||
}: Props = $props();
|
||||
</script>
|
||||
|
||||
<svelte:element
|
||||
this={href ? 'a' : 'button'}
|
||||
{href}
|
||||
class={tw(
|
||||
'inline-flex cursor-default items-center justify-center rounded-md px-2 py-1 text-sf-secondary shadow-xs ring-1 ring-ss-secondary',
|
||||
'font-normal transition-shadow duration-200 hover:ring-ss-primary active:shadow-inner',
|
||||
'text-sf-secondary ring-ss-secondary inline-flex cursor-default items-center justify-center rounded-md px-2 py-1 shadow-xs ring-1',
|
||||
'hover:ring-ss-primary font-normal transition-shadow duration-200 active:shadow-inner',
|
||||
variant === 'primary' && [
|
||||
'relative text-slate-50 ring-0 duration-200',
|
||||
'before:absolute before:-inset-px before:rounded-[7px]',
|
||||
'before:bg-linear-to-b before:from-accent-400 before:from-40% before:to-accent-500 before:ring-1 before:ring-inset before:ring-black/10',
|
||||
'before:transition-shadow active:before:shadow-inner dark:before:from-accent-500 dark:before:to-accent-600'
|
||||
'before:from-accent-400 before:to-accent-500 before:bg-linear-to-b before:from-40% before:ring-1 before:ring-black/10 before:ring-inset',
|
||||
'dark:before:from-accent-500 dark:before:to-accent-600 before:transition-shadow active:before:shadow-inner'
|
||||
],
|
||||
className
|
||||
)}
|
||||
{...$$restProps}
|
||||
on:click
|
||||
role="button"
|
||||
tabindex="0"
|
||||
{...rest}
|
||||
>
|
||||
{#if variant === 'primary'}
|
||||
<div class="z-10 drop-shadow-[0_-1px_0_--theme(--color-black/0.2)]"><slot /></div>
|
||||
<div class="z-10 drop-shadow-[0_-1px_0_--theme(--color-black/0.2)]">{@render children?.()}</div>
|
||||
{:else}
|
||||
<slot />
|
||||
{@render children?.()}
|
||||
{/if}
|
||||
</svelte:element>
|
||||
|
|
|
@ -4,9 +4,15 @@
|
|||
import { expoOut } from 'svelte/easing';
|
||||
import { scale } from 'svelte/transition';
|
||||
|
||||
interface $$Props extends DropdownMenuContentProps {}
|
||||
let className: $$Props['class'] = '';
|
||||
export { className as class };
|
||||
|
||||
interface Props {
|
||||
class?: $$Props['class'];
|
||||
children?: import('svelte').Snippet;
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
let { class: className = '', children, ...rest }: Props = $props();
|
||||
|
||||
</script>
|
||||
|
||||
<DropdownMenu.Content
|
||||
|
@ -17,7 +23,7 @@
|
|||
sideOffset={4}
|
||||
transition={scale}
|
||||
transitionConfig={{ start: 0.96, duration: 300, easing: expoOut }}
|
||||
{...$$restProps}
|
||||
{...rest}
|
||||
>
|
||||
<slot />
|
||||
{@render children?.()}
|
||||
</DropdownMenu.Content>
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
<script lang="ts">
|
||||
import { DropdownMenu, type DropdownMenuItemProps } from 'bits-ui';
|
||||
interface Props extends DropdownMenuItemProps {
|
||||
children?: import('svelte').Snippet;
|
||||
}
|
||||
|
||||
type $$Props = DropdownMenuItemProps;
|
||||
let { children, ...rest }: Props = $props();
|
||||
</script>
|
||||
|
||||
<DropdownMenu.Item
|
||||
class="cursor-default rounded-sm px-1.5 py-0.5 text-sf-primary transition-colors duration-200 hover:bg-accent-50 group-has-data-melt-dropdown-menu-radio-group:ps-6 dark:hover:bg-white/5"
|
||||
on:click
|
||||
{...$$restProps}
|
||||
class="text-sf-primary hover:bg-accent-50 cursor-default rounded-sm px-1.5 py-0.5 transition-colors duration-200 group-has-data-melt-dropdown-menu-radio-group:ps-6 dark:hover:bg-white/5"
|
||||
{...rest}
|
||||
>
|
||||
<slot />
|
||||
{@render children?.()}
|
||||
</DropdownMenu.Item>
|
||||
|
|
|
@ -3,17 +3,21 @@
|
|||
import { DropdownMenu } from 'bits-ui';
|
||||
import { Check, Icon } from 'svelte-hero-icons';
|
||||
|
||||
type $$Props = DropdownMenuRadioItemProps;
|
||||
export let value: string;
|
||||
interface Props extends DropdownMenuRadioItemProps {
|
||||
children?: import('svelte').Snippet;
|
||||
}
|
||||
|
||||
let { children: componentChildren, ...props }: Props = $props();
|
||||
</script>
|
||||
|
||||
<DropdownMenu.RadioItem
|
||||
class="flex cursor-default items-center gap-1 rounded-sm px-1.5 py-0.5 text-sf-primary transition-colors duration-200 hover:bg-accent-50 dark:hover:bg-white/5"
|
||||
{value}
|
||||
{...$$props}
|
||||
class="text-sf-primary hover:bg-accent-50 flex cursor-default items-center gap-1 rounded-sm px-1.5 py-0.5 transition-colors duration-200 dark:hover:bg-white/5"
|
||||
{...props}
|
||||
>
|
||||
<DropdownMenu.RadioIndicator class="relative size-4">
|
||||
<Icon src={Check} class="size-full" micro />
|
||||
</DropdownMenu.RadioIndicator>
|
||||
<slot />
|
||||
{#snippet children(itemProps)}
|
||||
{#if itemProps.checked}
|
||||
<Icon src={Check} class="size-full" micro />
|
||||
{/if}
|
||||
{@render componentChildren?.()}
|
||||
{/snippet}
|
||||
</DropdownMenu.RadioItem>
|
||||
|
|
|
@ -1,8 +1,14 @@
|
|||
<script lang="ts">
|
||||
import { DropdownMenu, type DropdownMenuTriggerProps } from 'bits-ui';
|
||||
interface Props {
|
||||
children?: import('svelte').Snippet;
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
let { children, ...rest }: Props = $props();
|
||||
type $$Props = DropdownMenuTriggerProps;
|
||||
</script>
|
||||
|
||||
<DropdownMenu.Trigger class="cursor-default" {...$$restProps}>
|
||||
<slot />
|
||||
<DropdownMenu.Trigger class="cursor-default" {...rest}>
|
||||
{@render children?.()}
|
||||
</DropdownMenu.Trigger>
|
||||
|
|
|
@ -1 +1,9 @@
|
|||
<div class="mx-auto max-w-3xl"><slot /></div>
|
||||
<script lang="ts">
|
||||
interface Props {
|
||||
children?: import('svelte').Snippet;
|
||||
}
|
||||
|
||||
let { children }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div class="mx-auto max-w-3xl">{@render children?.()}</div>
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
<script lang="ts">
|
||||
interface Props {
|
||||
children?: import('svelte').Snippet;
|
||||
}
|
||||
|
||||
let { children }: Props = $props();
|
||||
</script>
|
||||
|
||||
<label
|
||||
class="flex gap-2 px-4 py-3 font-medium text-sf-primary [align-items:first_baseline] [&>input]:flex-1 [&>input]:bg-transparent [&>input]:text-end [&>input]:outline-hidden [&>input]:placeholder:opacity-50"
|
||||
>
|
||||
<slot />
|
||||
{@render children?.()}
|
||||
</label>
|
||||
|
|
|
@ -1,10 +1,23 @@
|
|||
<script lang="ts">
|
||||
import { createBubbler } from 'svelte/legacy';
|
||||
|
||||
const bubble = createBubbler();
|
||||
import { tw } from '$lib/tw';
|
||||
import { Icon, type IconSource } from 'svelte-hero-icons';
|
||||
|
||||
export let href: string | undefined = undefined;
|
||||
export let icon: IconSource | undefined = undefined;
|
||||
export let selected: boolean = false;
|
||||
interface Props {
|
||||
href?: string | undefined;
|
||||
icon?: IconSource | undefined;
|
||||
selected?: boolean;
|
||||
children?: import('svelte').Snippet;
|
||||
}
|
||||
|
||||
let {
|
||||
href = undefined,
|
||||
icon = undefined,
|
||||
selected = false,
|
||||
children
|
||||
}: Props = $props();
|
||||
</script>
|
||||
|
||||
<svelte:element
|
||||
|
@ -16,7 +29,7 @@
|
|||
)}
|
||||
tabindex="0"
|
||||
role="button"
|
||||
on:click
|
||||
onclick={bubble('click')}
|
||||
>
|
||||
{#if icon}
|
||||
<Icon
|
||||
|
@ -25,5 +38,5 @@
|
|||
mini
|
||||
/>
|
||||
{/if}
|
||||
<slot />
|
||||
{@render children?.()}
|
||||
</svelte:element>
|
||||
|
|
|
@ -1,17 +1,27 @@
|
|||
<script lang="ts">
|
||||
interface Props {
|
||||
header?: import('svelte').Snippet;
|
||||
children?: import('svelte').Snippet;
|
||||
footer?: import('svelte').Snippet;
|
||||
}
|
||||
|
||||
let { header, children, footer }: Props = $props();
|
||||
</script>
|
||||
|
||||
<section class="my-6 cursor-default px-4">
|
||||
{#if $$slots.header}
|
||||
{#if header}
|
||||
<h3 class="mb-1 truncate px-4 text-sm font-medium uppercase text-sf-tertiary">
|
||||
<slot name="header" />
|
||||
{@render header?.()}
|
||||
</h3>
|
||||
{/if}
|
||||
<div
|
||||
class="divide-y-[0.5px] divide-ss-secondary overflow-hidden rounded-lg border-[0.5px] border-ss-secondary bg-sb-primary shadow-xs"
|
||||
>
|
||||
<slot />
|
||||
{@render children?.()}
|
||||
</div>
|
||||
{#if $$slots.footer}
|
||||
{#if footer}
|
||||
<div class="mt-1 px-4 text-sm text-sf-tertiary">
|
||||
<slot name="footer" />
|
||||
{@render footer?.()}
|
||||
</div>
|
||||
{/if}
|
||||
</section>
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
<script lang="ts">
|
||||
import { tw } from '$lib/tw';
|
||||
|
||||
let className = '';
|
||||
export { className as class };
|
||||
interface Props {
|
||||
class?: string;
|
||||
children?: import('svelte').Snippet;
|
||||
}
|
||||
|
||||
let { class: className = '', children }: Props = $props();
|
||||
|
||||
</script>
|
||||
|
||||
<label
|
||||
|
@ -11,5 +16,5 @@
|
|||
className
|
||||
)}
|
||||
>
|
||||
<slot />
|
||||
{@render children?.()}
|
||||
</label>
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
<script lang="ts">
|
||||
import { tw } from '$lib/tw';
|
||||
|
||||
export let href: string;
|
||||
export let variant: 'primary' | 'secondary' = 'primary';
|
||||
interface Props {
|
||||
href: string;
|
||||
variant?: 'primary' | 'secondary';
|
||||
children?: import('svelte').Snippet;
|
||||
}
|
||||
|
||||
let { href, variant = 'primary', children }: Props = $props();
|
||||
</script>
|
||||
|
||||
<a
|
||||
|
@ -12,5 +17,5 @@
|
|||
variant === 'primary'
|
||||
? 'text-accent-600 dark:text-accent-500'
|
||||
: 'text-accent-400 dark:text-accent-500'
|
||||
)}><slot /></a
|
||||
)}>{@render children?.()}</a
|
||||
>
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
<script lang="ts">
|
||||
import { tw } from '$lib/tw';
|
||||
|
||||
let className = '';
|
||||
export { className as class };
|
||||
interface Props {
|
||||
class?: string;
|
||||
}
|
||||
|
||||
let { class: className = '' }: Props = $props();
|
||||
|
||||
</script>
|
||||
|
||||
<div class={tw('loading-indicator relative inline-block size-5 [&>div]:bg-sf-tertiary', className)}>
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
<script lang="ts">
|
||||
import { tw } from '$lib/tw';
|
||||
|
||||
let className = '';
|
||||
export { className as class };
|
||||
interface Props {
|
||||
class?: string;
|
||||
children?: import('svelte').Snippet;
|
||||
}
|
||||
|
||||
let { class: className = '', children }: Props = $props();
|
||||
|
||||
</script>
|
||||
|
||||
<header
|
||||
|
@ -11,5 +16,5 @@
|
|||
className
|
||||
)}
|
||||
>
|
||||
<slot />
|
||||
{@render children?.()}
|
||||
</header>
|
||||
|
|
|
@ -2,8 +2,12 @@
|
|||
import type { Account } from '$lib/accounts/accountStore';
|
||||
import { AvatarBeam } from 'svelte-boring-avatars';
|
||||
|
||||
export let account: Account | undefined;
|
||||
export let size: number = 32;
|
||||
interface Props {
|
||||
account: Account | undefined;
|
||||
size?: number;
|
||||
}
|
||||
|
||||
let { account, size = 32 }: Props = $props();
|
||||
</script>
|
||||
|
||||
{#if account}
|
||||
|
@ -13,9 +17,9 @@
|
|||
<span class="sr-only">{account.profile.signee.payload.name}</span>
|
||||
{:else}
|
||||
<div
|
||||
class="box-border size-(--weblah-profile-pic-size) rounded-full border-2 border-dashed border-ss-primary"
|
||||
class="border-ss-primary box-border size-(--weblah-profile-pic-size) rounded-full border-2 border-dashed"
|
||||
style:--weblah-profile-pic-size={`${size}px`}
|
||||
aria-hidden
|
||||
/>
|
||||
aria-hidden="true"
|
||||
></div>
|
||||
<span class="sr-only">Account Unavailable</span>
|
||||
{/if}
|
||||
|
|
|
@ -4,14 +4,27 @@
|
|||
import InputFrame from '$lib/components/InputFrame.svelte';
|
||||
import { tw } from '$lib/tw';
|
||||
|
||||
export let delta: Delta | null = null;
|
||||
export let plainText: string | undefined = undefined;
|
||||
export let keyboardSubmitMethod: 'enter' | 'shiftEnter' | undefined = undefined;
|
||||
export let placeholder: string = '';
|
||||
export let editor: Editor | undefined;
|
||||
|
||||
let className = '';
|
||||
export { className as class };
|
||||
interface Props {
|
||||
delta?: Delta | null;
|
||||
plainText?: string | undefined;
|
||||
keyboardSubmitMethod?: 'enter' | 'shiftEnter' | undefined;
|
||||
placeholder?: string;
|
||||
editor: Editor | undefined;
|
||||
class?: string;
|
||||
children?: import('svelte').Snippet;
|
||||
}
|
||||
|
||||
let {
|
||||
delta = $bindable(null),
|
||||
plainText = $bindable(undefined),
|
||||
keyboardSubmitMethod = undefined,
|
||||
placeholder = '',
|
||||
editor = $bindable(),
|
||||
class: className = '',
|
||||
children
|
||||
}: Props = $props();
|
||||
|
||||
|
||||
const loadClientComponent = async () => {
|
||||
if (!browser) return;
|
||||
|
@ -26,8 +39,7 @@
|
|||
<p>{placeholder}</p>
|
||||
</div>
|
||||
{:then Input}
|
||||
<svelte:component
|
||||
this={Input}
|
||||
<Input
|
||||
bind:delta
|
||||
bind:plainText
|
||||
{placeholder}
|
||||
|
@ -35,7 +47,7 @@
|
|||
{keyboardSubmitMethod}
|
||||
on:keyboardSubmit
|
||||
>
|
||||
<slot />
|
||||
</svelte:component>
|
||||
{@render children?.()}
|
||||
</Input>
|
||||
{/await}
|
||||
</InputFrame>
|
||||
|
|
|
@ -1,24 +1,37 @@
|
|||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { Delta, Editor, asRoot, h } from 'typewriter-editor';
|
||||
import { keyboardSubmit } from './keyboardSubmitModule';
|
||||
|
||||
export let delta: Delta = new Delta();
|
||||
export let plainText: string | undefined = undefined;
|
||||
export let placeholder: string = '';
|
||||
export let keyboardSubmitMethod: 'enter' | 'shiftEnter' | undefined = undefined;
|
||||
interface Props {
|
||||
delta?: Delta;
|
||||
plainText?: string | undefined;
|
||||
placeholder?: string;
|
||||
keyboardSubmitMethod?: 'enter' | 'shiftEnter' | undefined;
|
||||
onKeyboardSubmit?: () => void;
|
||||
children?: import('svelte').Snippet;
|
||||
}
|
||||
|
||||
const dispatch = createEventDispatcher<{
|
||||
keyboardSubmit: void;
|
||||
}>();
|
||||
let {
|
||||
delta = $bindable(new Delta()),
|
||||
plainText = $bindable(undefined),
|
||||
placeholder = '',
|
||||
keyboardSubmitMethod = undefined,
|
||||
onKeyboardSubmit,
|
||||
children
|
||||
}: Props = $props();
|
||||
|
||||
let editor: Editor;
|
||||
let editor: Editor = $state(initEditor());
|
||||
|
||||
function initEditor() {
|
||||
const modules = keyboardSubmitMethod
|
||||
? { keyboardSubmit: keyboardSubmit(() => dispatch('keyboardSubmit'), keyboardSubmitMethod) }
|
||||
? {
|
||||
keyboardSubmit: keyboardSubmit(
|
||||
() => onKeyboardSubmit && onKeyboardSubmit(),
|
||||
keyboardSubmitMethod
|
||||
)
|
||||
}
|
||||
: undefined;
|
||||
editor = new Editor({ modules });
|
||||
const editor = new Editor({ modules });
|
||||
editor.typeset.formats.add({
|
||||
name: 'underline',
|
||||
selector: 'span[data-weblah-brt=underline]',
|
||||
|
@ -41,12 +54,20 @@
|
|||
delta = editor.getDelta();
|
||||
if (typeof plainText === 'string') plainText = editor.getText();
|
||||
});
|
||||
|
||||
return editor;
|
||||
}
|
||||
|
||||
$: if (keyboardSubmitMethod || typeof keyboardSubmitMethod === 'undefined') initEditor();
|
||||
$effect.pre(() => {
|
||||
if (keyboardSubmitMethod || typeof keyboardSubmitMethod === 'undefined') editor = initEditor();
|
||||
});
|
||||
|
||||
$: editor.setDelta(delta ?? new Delta());
|
||||
$: if (typeof plainText === 'string' && plainText !== editor.getText()) editor.setText(plainText);
|
||||
$effect.pre(() => {
|
||||
editor.setDelta(delta ?? new Delta());
|
||||
});
|
||||
$effect.pre(() => {
|
||||
if (typeof plainText === 'string' && plainText !== editor.getText()) editor.setText(plainText);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div
|
||||
|
@ -59,5 +80,5 @@
|
|||
role="textbox"
|
||||
tabindex="0"
|
||||
>
|
||||
<slot />
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
|
|
@ -4,9 +4,13 @@
|
|||
import RichTextSpan from './RichTextRenderer/RichTextSpan.svelte';
|
||||
import PlainTextRenderer from './RichTextRenderer/PlainTextRenderer.svelte';
|
||||
|
||||
export let content: BlahRichText;
|
||||
let className = '';
|
||||
export { className as class };
|
||||
interface Props {
|
||||
content: BlahRichText;
|
||||
class?: string;
|
||||
}
|
||||
|
||||
let { content, class: className = '' }: Props = $props();
|
||||
|
||||
</script>
|
||||
|
||||
<div class={tw('rich-text', className)}>
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
<script lang="ts">
|
||||
export let text: string;
|
||||
interface Props {
|
||||
text: string;
|
||||
}
|
||||
|
||||
let { text }: Props = $props();
|
||||
</script>
|
||||
|
||||
{#each text.split('\n') as segment, idx}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
<!-- @migration-task Error while migrating Svelte code: $$props is used together with named props in a way that cannot be automatically migrated. -->
|
||||
<script lang="ts">
|
||||
import type { BlahRichTextSpanAttributes } from '$lib/richText';
|
||||
import PlainTextRenderer from './PlainTextRenderer.svelte';
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
<script lang="ts">
|
||||
import { tw } from '$lib/tw';
|
||||
|
||||
let className: string = '';
|
||||
export { className as class };
|
||||
interface Props {
|
||||
class?: string;
|
||||
children?: import('svelte').Snippet;
|
||||
}
|
||||
|
||||
let { class: className = '', children }: Props = $props();
|
||||
|
||||
</script>
|
||||
|
||||
<div
|
||||
|
@ -11,5 +16,5 @@
|
|||
className
|
||||
)}
|
||||
>
|
||||
<slot />
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue