fix: update components to use Svelte 5 snippets and fix editor content loading

- Convert Button component to use snippets instead of slots
- Update BaseDropdown and StatusDropdown to use new Button snippet syntax
- Add effect to watch for data changes in ComposerCore and update editor content
- Fix SVG component usage in Album component for Svelte 5 compatibility

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Justin Edmund 2025-06-26 09:22:19 -04:00
parent e64788962e
commit 639a4a2429
5 changed files with 68 additions and 39 deletions

View file

@ -165,9 +165,9 @@
class:playing={isPlaying} class:playing={isPlaying}
> >
{#if isPlaying} {#if isPlaying}
<PauseIcon /> <svelte:component this={PauseIcon} />
{:else} {:else}
<PlayIcon /> <svelte:component this={PlayIcon} />
{/if} {/if}
</button> </button>
{/if} {/if}

View file

@ -1,4 +1,5 @@
<script lang="ts"> <script lang="ts">
import type { Snippet } from 'svelte'
import Button from './Button.svelte' import Button from './Button.svelte'
import DropdownMenuContainer from './DropdownMenuContainer.svelte' import DropdownMenuContainer from './DropdownMenuContainer.svelte'
@ -9,6 +10,8 @@
dropdownTriggerSize?: 'small' | 'medium' | 'large' dropdownTriggerSize?: 'small' | 'medium' | 'large'
class?: string class?: string
onToggle?: (isOpen: boolean) => void onToggle?: (isOpen: boolean) => void
trigger: Snippet
dropdown?: Snippet
} }
let { let {
@ -17,7 +20,9 @@
isLoading = false, isLoading = false,
dropdownTriggerSize = 'large', dropdownTriggerSize = 'large',
class: className = '', class: className = '',
onToggle onToggle,
trigger,
dropdown
}: Props = $props() }: Props = $props()
function handleDropdownToggle(e: MouseEvent) { function handleDropdownToggle(e: MouseEvent) {
@ -47,9 +52,9 @@
<div class="dropdown-container {className}"> <div class="dropdown-container {className}">
<div class="dropdown-trigger"> <div class="dropdown-trigger">
<slot name="trigger" /> {@render trigger()}
{#if $$slots.dropdown} {#if dropdown}
<Button <Button
variant="ghost" variant="ghost"
iconOnly iconOnly
@ -59,22 +64,24 @@
{isLoading} {isLoading}
class="dropdown-toggle" class="dropdown-toggle"
> >
<svg slot="icon" width="12" height="12" viewBox="0 0 12 12" fill="none"> {#snippet icon()}
<path <svg width="12" height="12" viewBox="0 0 12 12" fill="none">
d="M3 4.5L6 7.5L9 4.5" <path
stroke="currentColor" d="M3 4.5L6 7.5L9 4.5"
stroke-width="1.5" stroke="currentColor"
stroke-linecap="round" stroke-width="1.5"
stroke-linejoin="round" stroke-linecap="round"
/> stroke-linejoin="round"
</svg> />
</svg>
{/snippet}
</Button> </Button>
{/if} {/if}
</div> </div>
{#if isOpen && $$slots.dropdown} {#if isOpen && dropdown}
<DropdownMenuContainer> <DropdownMenuContainer>
<slot name="dropdown" /> {@render dropdown()}
</DropdownMenuContainer> </DropdownMenuContainer>
{/if} {/if}
</div> </div>

View file

@ -1,5 +1,6 @@
<script lang="ts"> <script lang="ts">
import type { HTMLButtonAttributes } from 'svelte/elements' import type { HTMLButtonAttributes } from 'svelte/elements'
import type { Snippet } from 'svelte'
interface Props extends HTMLButtonAttributes { interface Props extends HTMLButtonAttributes {
variant?: 'primary' | 'secondary' | 'danger' | 'ghost' | 'text' | 'overlay' | 'danger-text' variant?: 'primary' | 'secondary' | 'danger' | 'ghost' | 'text' | 'overlay' | 'danger-text'
@ -12,6 +13,8 @@
active?: boolean active?: boolean
href?: string href?: string
class?: string class?: string
icon?: Snippet
children?: Snippet
} }
let { let {
@ -27,6 +30,7 @@
type = 'button', type = 'button',
href, href,
class: className = '', class: className = '',
icon,
children, children,
onclick, onclick,
...restProps ...restProps
@ -60,8 +64,8 @@
}) })
// Handle icon slot positioning // Handle icon slot positioning
const hasIcon = $derived(!!$$slots.icon) const hasIcon = $derived(!!icon)
const hasDefaultSlot = $derived(!!$$slots.default) const hasDefaultSlot = $derived(!!children)
const showSpinner = $derived(loading && !iconOnly) const showSpinner = $derived(loading && !iconOnly)
</script> </script>
@ -94,21 +98,21 @@
{#if hasIcon && iconPosition === 'left' && !iconOnly} {#if hasIcon && iconPosition === 'left' && !iconOnly}
<span class="btn-icon-wrapper"> <span class="btn-icon-wrapper">
<slot name="icon" /> {@render icon()}
</span> </span>
{/if} {/if}
{#if hasDefaultSlot && !iconOnly} {#if hasDefaultSlot && !iconOnly}
<span class="btn-label"> <span class="btn-label">
<slot /> {@render children()}
</span> </span>
{:else if iconOnly && hasIcon} {:else if iconOnly && hasIcon}
<slot name="icon" /> {@render icon()}
{/if} {/if}
{#if hasIcon && iconPosition === 'right' && !iconOnly} {#if hasIcon && iconPosition === 'right' && !iconOnly}
<span class="btn-icon-wrapper"> <span class="btn-icon-wrapper">
<slot name="icon" /> {@render icon()}
</span> </span>
{/if} {/if}
</a> </a>
@ -141,21 +145,21 @@
{#if hasIcon && iconPosition === 'left' && !iconOnly} {#if hasIcon && iconPosition === 'left' && !iconOnly}
<span class="btn-icon-wrapper"> <span class="btn-icon-wrapper">
<slot name="icon" /> {@render icon()}
</span> </span>
{/if} {/if}
{#if hasDefaultSlot && !iconOnly} {#if hasDefaultSlot && !iconOnly}
<span class="btn-label"> <span class="btn-label">
<slot /> {@render children()}
</span> </span>
{:else if iconOnly && hasIcon} {:else if iconOnly && hasIcon}
<slot name="icon" /> {@render icon()}
{/if} {/if}
{#if hasIcon && iconPosition === 'right' && !iconOnly} {#if hasIcon && iconPosition === 'right' && !iconOnly}
<span class="btn-icon-wrapper"> <span class="btn-icon-wrapper">
<slot name="icon" /> {@render icon()}
</span> </span>
{/if} {/if}
</button> </button>

View file

@ -55,18 +55,21 @@
{isLoading} {isLoading}
class="status-dropdown" class="status-dropdown"
> >
<Button {#snippet trigger()}
slot="trigger" <Button
variant="primary" variant="primary"
buttonSize="large" buttonSize="large"
onclick={handlePrimaryAction} onclick={handlePrimaryAction}
disabled={disabled || isLoading} disabled={disabled || isLoading}
> >
{primaryAction.label} {#snippet children()}
</Button> {primaryAction.label}
{/snippet}
</Button>
{/snippet}
{#if hasDropdownContent} {#snippet dropdown()}
<div slot="dropdown"> {#if hasDropdownContent}
{#each availableActions as action} {#each availableActions as action}
<DropdownItem onclick={() => handleDropdownAction(action.status)}> <DropdownItem onclick={() => handleDropdownAction(action.status)}>
{action.label} {action.label}
@ -85,8 +88,8 @@
View on site View on site
</a> </a>
{/if} {/if}
</div> {/if}
{/if} {/snippet}
</BaseDropdown> </BaseDropdown>
<style lang="scss"> <style lang="scss">

View file

@ -71,6 +71,7 @@
let element = $state<HTMLElement>() let element = $state<HTMLElement>()
let isLoading = $state(true) let isLoading = $state(true)
let initialized = false let initialized = false
let contentLoaded = false
const mediaSelectionState = $derived($mediaSelectionStore) const mediaSelectionState = $derived($mediaSelectionStore)
// Toolbar component ref // Toolbar component ref
@ -161,6 +162,20 @@
} }
} }
// Watch for external data changes and update editor
$effect(() => {
if (editor && data && !contentLoaded) {
// Only update if the data has actual content (not just empty doc)
const hasContent = data.content && data.content.length > 0 &&
!(data.content.length === 1 && data.content[0].type === 'paragraph' && !data.content[0].content);
if (hasContent) {
editor.commands.setContent(data);
contentLoaded = true;
}
}
});
onMount(() => { onMount(() => {
// Get extensions with custom options // Get extensions with custom options
const extensions = getEditorExtensions({ const extensions = getEditorExtensions({