Skip to content
Open
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
47 changes: 47 additions & 0 deletions docs/src/lib/components/EditWithButton.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<script lang="ts">
import { Button, Toggle, ButtonGroup, Icon, MenuItem, Menu } from 'svelte-ux';
import { openInSvelteREPL } from '$lib/utils/svelte-repl';
import { openInStackBlitz } from '$lib/utils/stackblitz.svelte';

import ChevronDownIcon from '~icons/lucide/chevron-down';
import LucideFilePen from '~icons/lucide/file-pen';
import StackBlitzIcon from '~icons/simple-icons/stackblitz';
import SvelteIcon from '~icons/simple-icons/svelte';

let { component, source, name } = $props();

let isOpen = $state(false);
</script>

<ButtonGroup class="text-surface-content/70 py-1">
<Toggle bind:on={isOpen} let:on={open} let:toggle let:toggleOff>
<Button icon={LucideFilePen} on:click={toggle} class="py-1">
Edit in
<span style="transition: transform 300ms ease; transform: rotate({open ? -180 : 0}deg);">
<ChevronDownIcon />
</span>
<Menu {open} on:close={toggleOff} placement="bottom-start" class="z-25">
<MenuItem
class="text-surface-content/70 hover:text-surface-content"
onclick={() => {
toggleOff();
openInSvelteREPL(source);
}}
>
<Icon data={SvelteIcon} />
Svelte REPL (beta)
</MenuItem>
<MenuItem
class="text-surface-content/70 hover:text-surface-content"
onclick={() => {
toggleOff();
openInStackBlitz(component, name);
}}
>
<Icon data={StackBlitzIcon} />
StackBlitz
</MenuItem>
</Menu>
</Button>
</Toggle>
</ButtonGroup>
104 changes: 4 additions & 100 deletions docs/src/lib/components/Example.svelte
Original file line number Diff line number Diff line change
@@ -1,16 +1,8 @@
<script lang="ts">
import type { SvelteComponent } from 'svelte';
import { slide } from 'svelte/transition';
import {
Button,
CopyButton,
Dialog,
Menu,
MenuItem,
Notification,
Toggle,
Tooltip
} from 'svelte-ux';
import { Button, CopyButton, Dialog, Toggle, Tooltip } from 'svelte-ux';
import EditWithButton from './EditWithButton.svelte';
import { cls } from '@layerstack/tailwind';

import { examples } from '$lib/context';
Expand All @@ -20,15 +12,9 @@
import LucideCode from '~icons/lucide/code';
import LucideFullscreen from '~icons/lucide/fullscreen';
import LucideTable from '~icons/lucide/table';
import LucideFilePen from '~icons/lucide/file-pen';
import LucideGripVertical from '~icons/lucide/grip-vertical';
import LucideDownload from '~icons/lucide/download';

import { page } from '$app/state';
import { openInStackBlitz } from '$lib/utils/stackblitz.svelte';
import { downloadImage, downloadSvg, getSettings } from 'layerchart';

const settings = getSettings();
import { movable } from '$lib/actions/movable';

let {
Expand Down Expand Up @@ -148,48 +134,6 @@

return true;
});

let sentinelEl = $state<HTMLElement | null>(null);
let intersected = $state(false);
const lazy = $derived(!isDetailPage);
let isVisible = $derived(!lazy || intersected);

$effect(() => {
if (!lazy || !sentinelEl) return;

// Synchronously check if already in/near viewport to avoid flash for first examples
const rect = sentinelEl.getBoundingClientRect();
if (rect.top < window.innerHeight + 200 && rect.bottom > -200) {
intersected = true;
return;
}

const observer = new IntersectionObserver(
(entries) => {
if (entries[0].isIntersecting) {
intersected = true;
observer.disconnect();
}
},
{ rootMargin: '200px' }
);
observer.observe(sentinelEl);
return () => observer.disconnect();
});

// $inspect({ component, name, isVisible, intersected, lazy, example });

let svgUnavailable = $state(false);
let svgUnavailableTimer: ReturnType<typeof setTimeout>;

function handleSvgDownload() {
const downloaded = downloadSvg(containerEl!, { filename: name ?? component });
if (!downloaded) {
clearTimeout(svgUnavailableTimer);
svgUnavailable = true;
svgUnavailableTimer = setTimeout(() => (svgUnavailable = false), 3000);
}
}
</script>

<div class={cls('example relative', clip && 'overflow-clip', className)}>
Expand All @@ -209,11 +153,7 @@
style:width={containerWidth ? `${containerWidth}px` : undefined}
style:view-transition-name={viewTransitionName}
>
{#if isVisible}
<example.component bind:this={ref} />
{:else}
<div bind:this={sentinelEl} class="min-h-25"></div>
{/if}
<example.component bind:this={ref} />

{#if canResize}
<div
Expand Down Expand Up @@ -299,33 +239,8 @@
{/if}

{#if component && name}
<Button
icon={LucideFilePen}
class="text-surface-content/70 py-1"
on:click={() => openInStackBlitz(component, name)}
>
Edit
</Button>
<EditWithButton {component} source={example.source} {name} />
{/if}

<Toggle let:on={open} let:toggle let:toggleOff>
<Button icon={LucideDownload} class="text-surface-content/70 py-1" on:click={toggle}>
Download
<Menu {open} on:close={toggleOff} placement="bottom-start" classes={{ menu: 'p-1' }}>
<MenuItem
icon={LucideDownload}
on:click={() => downloadImage(containerEl!, { filename: name ?? component })}
>
Download as PNG
</MenuItem>
{#if settings.layer !== 'canvas'}
<MenuItem icon={LucideDownload} on:click={handleSvgDownload}>
Download as SVG
</MenuItem>
{/if}
</Menu>
</Button>
</Toggle>
</div>
{/if}
{:else}
Expand All @@ -338,14 +253,3 @@
</div>
{/if}
</div>

{#if svgUnavailable}
<div class="fixed bottom-4 right-4 z-50">
<Notification
description="SVG download is not available for Canvas-only charts"
color="warning"
closeIcon
on:close={() => (svgUnavailable = false)}
/>
</div>
{/if}
Loading
Loading