diff --git a/src/lib/index.ts b/src/lib/index.ts index e90afb2..63d93e2 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -50,3 +50,4 @@ export { getNavigationManager, type NavigationItemOpts } from './navigation.svelte'; +export { createLazyComponent, type LazyComponentProps } from './lazy.svelte'; diff --git a/src/lib/lazy.svelte.ts b/src/lib/lazy.svelte.ts new file mode 100644 index 0000000..4324f39 --- /dev/null +++ b/src/lib/lazy.svelte.ts @@ -0,0 +1,43 @@ +import type { Component, ComponentProps } from 'svelte'; + +/** + * Create a lazy-loaded Svelte component. + * Example: + * const LazyComponent = createLazyComponent(() => import('./MyComponent.svelte')); + * @param importFn - Function that returns a promise resolving to the component module. + * @returns An object containing the lazy-loaded component, loading state, error state, and a load function. + */ +export function createLazyComponent(importFn: () => Promise<{ default: T }>) { + let component = $state(null); + let loading = $state(true); + let error = $state(null); + + const load = async () => { + try { + const module = await importFn(); + component = module.default; + loading = false; + } catch (e) { + error = e as Error; + loading = false; + } + }; + + return { + get component() { + return component; + }, + get loading() { + return loading; + }, + get error() { + return error; + }, + load + }; +} + +/** + * Props for a lazy-loaded Svelte component. + */ +export type LazyComponentProps = ComponentProps; diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 1169d9f..0b361ad 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -9,13 +9,11 @@ import FramelessButton from '$lib/FramelessButton.svelte'; import InputGroup from '$lib/InputGroup.svelte'; import Link from '$lib/Link.svelte'; - import PhoneInput from '$lib/PhoneInput.svelte'; import PinInput from '$lib/PinInput.svelte'; import RadioGroup from '$lib/RadioGroup.svelte'; import StyledRawInput from '$lib/StyledRawInput.svelte'; import TextInput from '$lib/TextInput.svelte'; import TimeInput, { formatTime } from '$lib/TimeInput.svelte'; - import TimezoneInput from '$lib/TimezoneInput.svelte'; import ToggleGroup from '$lib/ToggleGroup.svelte'; import ToggleSelect from '$lib/ToggleSelect.svelte'; import { Toolbar } from '$lib/Toolbar'; @@ -29,9 +27,21 @@ TextStrikethrough, TextUnderline } from 'phosphor-svelte'; - import { ErrorMessage, type ComboboxOption, type Option } from '$lib'; + import { createLazyComponent, type ComboboxOption, type Option } from '$lib'; import Tabs from '$lib/Tabs.svelte'; import { Time } from '@internationalized/date'; + import { onMount, type Component } from 'svelte'; + import ErrorBox from '$lib/ErrorBox.svelte'; + + // Lazy-load heavy components + let PhoneInput = createLazyComponent(() => import('$lib/PhoneInput.svelte')); + let TimezoneInput = createLazyComponent(() => import('$lib/TimezoneInput.svelte')); + + // Load heavy components on mount + onMount(() => { + PhoneInput.load(); + TimezoneInput.load(); + }); const comboboxOptions = [ { value: 'option1', label: 'Option 1' }, @@ -223,7 +233,13 @@

Phone Input

- + {#if PhoneInput.loading} +
Loading...
+ {:else if PhoneInput.error} +
Failed to load component
+ {:else} + + {/if}
@@ -282,7 +298,13 @@

TimezoneInput

- + {#if TimezoneInput.loading} +
Loading...
+ {:else if TimezoneInput.error} +
Failed to load component
+ {:else} + + {/if}