Skip to content

Commit 06078af

Browse files
committed
Merge branch 'main' into pr/541
2 parents 02dd194 + 90a6553 commit 06078af

440 files changed

Lines changed: 22514 additions & 1516 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.changeset/config.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"@embedpdf/plugin-document-manager",
1616
"@embedpdf/plugin-export",
1717
"@embedpdf/plugin-fullscreen",
18+
"@embedpdf/plugin-form",
1819
"@embedpdf/plugin-history",
1920
"@embedpdf/plugin-i18n",
2021
"@embedpdf/plugin-interaction-manager",

.changeset/fair-cups-smile.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@embedpdf/snippet': patch
3+
---
4+
5+
Add form plugin support to the snippet API and expand the snippet documentation with standalone vanilla HTML examples.
6+
7+
This includes exporting the form plugin types from `@embedpdf/snippet`, updating form-related command/category behavior, and adding vanilla Tailwind examples plus new plugin docs for forms.

examples/svelte-tailwind/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
"@embedpdf/plugin-commands": "workspace:*",
4242
"@embedpdf/plugin-document-manager": "workspace:*",
4343
"@embedpdf/plugin-export": "workspace:*",
44+
"@embedpdf/plugin-form": "workspace:*",
4445
"@embedpdf/plugin-fullscreen": "workspace:*",
4546
"@embedpdf/plugin-history": "workspace:*",
4647
"@embedpdf/plugin-i18n": "workspace:*",
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
<script lang="ts">
2+
import { Viewport } from '@embedpdf/plugin-viewport/svelte';
3+
import { Scroller, type RenderPageProps } from '@embedpdf/plugin-scroll/svelte';
4+
import { RenderLayer } from '@embedpdf/plugin-render/svelte';
5+
import { AnnotationLayer, LockModeType, useAnnotation } from '@embedpdf/plugin-annotation/svelte';
6+
import { PagePointerProvider } from '@embedpdf/plugin-interaction-manager/svelte';
7+
import { SelectionLayer } from '@embedpdf/plugin-selection/svelte';
8+
import { useZoom } from '@embedpdf/plugin-zoom/svelte';
9+
import { Loader2, Lock, LockOpen, ZoomIn, ZoomOut } from 'lucide-svelte';
10+
11+
interface Props {
12+
documentId: string;
13+
}
14+
15+
const fillModeLock = {
16+
type: LockModeType.Include,
17+
categories: ['form'],
18+
};
19+
20+
let { documentId }: Props = $props();
21+
22+
const annotation = useAnnotation(() => documentId);
23+
const zoom = useZoom(() => documentId);
24+
25+
const fillMode = $derived(
26+
annotation.state.locked.type === LockModeType.Include &&
27+
annotation.state.locked.categories?.includes('form'),
28+
);
29+
30+
function toggleMode() {
31+
annotation.provides?.setLocked(fillMode ? { type: LockModeType.None } : fillModeLock);
32+
}
33+
</script>
34+
35+
<div
36+
class="overflow-hidden rounded-lg border border-gray-300 bg-white shadow-sm dark:border-gray-700 dark:bg-gray-900"
37+
>
38+
<div
39+
class="flex flex-wrap items-center justify-between gap-3 border-b border-gray-300 bg-gray-100 px-3 py-2 dark:border-gray-700 dark:bg-gray-800"
40+
>
41+
<div class="flex items-center gap-2">
42+
<span
43+
class={[
44+
'inline-flex items-center gap-1.5 rounded-full px-2.5 py-1 text-xs font-medium',
45+
fillMode
46+
? 'bg-emerald-100 text-emerald-700 dark:bg-emerald-500/15 dark:text-emerald-300'
47+
: 'bg-amber-100 text-amber-700 dark:bg-amber-500/15 dark:text-amber-300',
48+
].join(' ')}
49+
>
50+
{#if fillMode}
51+
<Lock size={14} />
52+
Fill Mode
53+
{:else}
54+
<LockOpen size={14} />
55+
Design Mode
56+
{/if}
57+
</span>
58+
</div>
59+
60+
<div class="flex flex-wrap items-center gap-2">
61+
<div class="flex items-center gap-1.5">
62+
<button
63+
type="button"
64+
onclick={() => zoom.provides?.zoomOut()}
65+
disabled={!zoom.provides}
66+
class="inline-flex h-8 w-8 items-center justify-center rounded-md bg-white text-gray-600 shadow-sm ring-1 ring-gray-300 transition-all hover:bg-gray-50 hover:text-gray-900 disabled:cursor-not-allowed disabled:opacity-50 dark:bg-gray-700 dark:text-gray-300 dark:ring-gray-600 dark:hover:bg-gray-600 dark:hover:text-gray-100"
67+
title="Zoom Out"
68+
>
69+
<ZoomOut size={16} />
70+
</button>
71+
72+
<button
73+
type="button"
74+
onclick={() => zoom.provides?.zoomIn()}
75+
disabled={!zoom.provides}
76+
class="inline-flex h-8 w-8 items-center justify-center rounded-md bg-white text-gray-600 shadow-sm ring-1 ring-gray-300 transition-all hover:bg-gray-50 hover:text-gray-900 disabled:cursor-not-allowed disabled:opacity-50 dark:bg-gray-700 dark:text-gray-300 dark:ring-gray-600 dark:hover:bg-gray-600 dark:hover:text-gray-100"
77+
title="Zoom In"
78+
>
79+
<ZoomIn size={16} />
80+
</button>
81+
</div>
82+
83+
<button
84+
type="button"
85+
onclick={toggleMode}
86+
disabled={!annotation.provides}
87+
class="inline-flex items-center justify-center gap-1.5 rounded-md bg-blue-600 px-3 py-1.5 text-sm font-medium text-white transition-colors hover:bg-blue-700 disabled:cursor-not-allowed disabled:opacity-50"
88+
>
89+
{#if fillMode}
90+
<LockOpen size={16} />
91+
Switch to Design Mode
92+
{:else}
93+
<Lock size={16} />
94+
Switch to Fill Mode
95+
{/if}
96+
</button>
97+
</div>
98+
</div>
99+
100+
<div class="relative h-[420px] sm:h-[550px]" style="user-select: none">
101+
{#snippet renderPage(page: RenderPageProps)}
102+
<PagePointerProvider {documentId} pageIndex={page.pageIndex}>
103+
<RenderLayer {documentId} pageIndex={page.pageIndex} style="pointer-events: none;" />
104+
<SelectionLayer {documentId} pageIndex={page.pageIndex} />
105+
<AnnotationLayer {documentId} pageIndex={page.pageIndex} />
106+
</PagePointerProvider>
107+
{/snippet}
108+
109+
<Viewport {documentId} class="absolute inset-0 bg-gray-200 dark:bg-gray-800">
110+
<Scroller {documentId} {renderPage} />
111+
</Viewport>
112+
</div>
113+
</div>
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<script lang="ts">
2+
import { usePdfiumEngine } from '@embedpdf/engines/svelte';
3+
import { EmbedPDF } from '@embedpdf/core/svelte';
4+
import { createPluginRegistration } from '@embedpdf/core';
5+
import {
6+
DocumentManagerPluginPackage,
7+
DocumentContent,
8+
} from '@embedpdf/plugin-document-manager/svelte';
9+
import { ViewportPluginPackage } from '@embedpdf/plugin-viewport/svelte';
10+
import { ScrollPluginPackage } from '@embedpdf/plugin-scroll/svelte';
11+
import { RenderPluginPackage } from '@embedpdf/plugin-render/svelte';
12+
import { HistoryPluginPackage } from '@embedpdf/plugin-history/svelte';
13+
import { AnnotationPluginPackage, LockModeType } from '@embedpdf/plugin-annotation/svelte';
14+
import { InteractionManagerPluginPackage } from '@embedpdf/plugin-interaction-manager/svelte';
15+
import { SelectionPluginPackage } from '@embedpdf/plugin-selection/svelte';
16+
import { ZoomPluginPackage, ZoomMode } from '@embedpdf/plugin-zoom/svelte';
17+
import { FormPluginPackage } from '@embedpdf/plugin-form/svelte';
18+
import FormEditModeExampleContent from './form-edit-mode-example-content.svelte';
19+
20+
const pdfEngine = usePdfiumEngine();
21+
22+
const plugins = [
23+
createPluginRegistration(DocumentManagerPluginPackage, {
24+
initialDocuments: [{ url: '/form.pdf' }],
25+
}),
26+
createPluginRegistration(ViewportPluginPackage),
27+
createPluginRegistration(ScrollPluginPackage),
28+
createPluginRegistration(RenderPluginPackage),
29+
createPluginRegistration(ZoomPluginPackage, {
30+
defaultZoomLevel: ZoomMode.FitPage,
31+
}),
32+
createPluginRegistration(InteractionManagerPluginPackage),
33+
createPluginRegistration(SelectionPluginPackage),
34+
createPluginRegistration(HistoryPluginPackage),
35+
createPluginRegistration(AnnotationPluginPackage, {
36+
locked: { type: LockModeType.Include, categories: ['form'] },
37+
}),
38+
createPluginRegistration(FormPluginPackage),
39+
];
40+
</script>
41+
42+
{#if pdfEngine.isLoading || !pdfEngine.engine}
43+
<div>Loading PDF Engine...</div>
44+
{:else}
45+
<EmbedPDF engine={pdfEngine.engine} {plugins}>
46+
{#snippet children({ activeDocumentId })}
47+
{#if activeDocumentId}
48+
<DocumentContent documentId={activeDocumentId}>
49+
{#snippet children(documentContent)}
50+
{#if documentContent.isLoaded}
51+
<FormEditModeExampleContent documentId={activeDocumentId} />
52+
{/if}
53+
{/snippet}
54+
</DocumentContent>
55+
{/if}
56+
{/snippet}
57+
</EmbedPDF>
58+
{/if}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from './form-edit-mode-example.svelte';
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
<script lang="ts">
2+
import { Viewport } from '@embedpdf/plugin-viewport/svelte';
3+
import { Scroller, type RenderPageProps } from '@embedpdf/plugin-scroll/svelte';
4+
import { RenderLayer } from '@embedpdf/plugin-render/svelte';
5+
import { useExport } from '@embedpdf/plugin-export/svelte';
6+
import { SelectionLayer } from '@embedpdf/plugin-selection/svelte';
7+
import { AnnotationLayer } from '@embedpdf/plugin-annotation/svelte';
8+
import { PagePointerProvider } from '@embedpdf/plugin-interaction-manager/svelte';
9+
import { useZoom } from '@embedpdf/plugin-zoom/svelte';
10+
import { useFormCapability } from '@embedpdf/plugin-form/svelte';
11+
import { Download, ZoomIn, ZoomOut } from 'lucide-svelte';
12+
13+
interface Props {
14+
documentId: string;
15+
}
16+
17+
let { documentId }: Props = $props();
18+
19+
const zoom = useZoom(() => documentId);
20+
const exportApi = useExport(() => documentId);
21+
const formCapability = useFormCapability();
22+
let formValues = $state<Record<string, string>>({});
23+
24+
$effect(() => {
25+
if (!formCapability.provides || !documentId) return;
26+
27+
const scope = formCapability.provides.forDocument(documentId);
28+
formValues = scope.getFormValues();
29+
30+
const unsubReady = scope.onFormReady(() => {
31+
formValues = scope.getFormValues();
32+
});
33+
const unsubChange = scope.onFieldValueChange(() => {
34+
formValues = scope.getFormValues();
35+
});
36+
37+
return () => {
38+
unsubReady();
39+
unsubChange();
40+
};
41+
});
42+
</script>
43+
44+
<div
45+
class="overflow-hidden rounded-lg border border-gray-300 bg-white shadow-sm dark:border-gray-700 dark:bg-gray-900"
46+
style="user-select: none"
47+
>
48+
<div
49+
class="flex flex-wrap items-center justify-between gap-3 border-b border-gray-300 bg-gray-100 px-3 py-2 dark:border-gray-700 dark:bg-gray-800"
50+
>
51+
<div class="flex items-center gap-1.5">
52+
<button
53+
type="button"
54+
onclick={() => zoom.provides?.zoomOut()}
55+
class="inline-flex h-8 w-8 items-center justify-center rounded-md bg-white text-gray-600 shadow-sm ring-1 ring-gray-300 transition-all hover:bg-gray-50 hover:text-gray-900 dark:bg-gray-700 dark:text-gray-300 dark:ring-gray-600 dark:hover:bg-gray-600 dark:hover:text-gray-100"
56+
title="Zoom Out"
57+
>
58+
<ZoomOut size={16} />
59+
</button>
60+
61+
<button
62+
type="button"
63+
onclick={() => zoom.provides?.zoomIn()}
64+
class="inline-flex h-8 w-8 items-center justify-center rounded-md bg-white text-gray-600 shadow-sm ring-1 ring-gray-300 transition-all hover:bg-gray-50 hover:text-gray-900 dark:bg-gray-700 dark:text-gray-300 dark:ring-gray-600 dark:hover:bg-gray-600 dark:hover:text-gray-100"
65+
title="Zoom In"
66+
>
67+
<ZoomIn size={16} />
68+
</button>
69+
</div>
70+
71+
<button
72+
type="button"
73+
onclick={() => exportApi.provides?.download()}
74+
disabled={!exportApi.provides}
75+
class="inline-flex items-center justify-center gap-1.5 rounded-md bg-blue-600 px-3 py-1.5 text-sm font-medium text-white transition-colors hover:bg-blue-700 disabled:cursor-not-allowed disabled:opacity-50"
76+
>
77+
<Download size={16} />
78+
Download PDF
79+
</button>
80+
</div>
81+
82+
<div class="flex flex-col lg:h-[550px] lg:flex-row">
83+
<div class="relative h-[420px] sm:h-[550px] lg:h-auto lg:flex-1">
84+
{#snippet renderPage(page: RenderPageProps)}
85+
<PagePointerProvider {documentId} pageIndex={page.pageIndex}>
86+
<RenderLayer {documentId} pageIndex={page.pageIndex} style="pointer-events: none;" />
87+
<SelectionLayer {documentId} pageIndex={page.pageIndex} />
88+
<AnnotationLayer {documentId} pageIndex={page.pageIndex} />
89+
</PagePointerProvider>
90+
{/snippet}
91+
92+
<Viewport {documentId} class="absolute inset-0 bg-gray-200 dark:bg-gray-800">
93+
<Scroller {documentId} {renderPage} />
94+
</Viewport>
95+
</div>
96+
97+
<div
98+
class="flex h-full min-h-[240px] flex-col overflow-hidden border-t border-gray-200 bg-white dark:border-gray-800 dark:bg-gray-900 lg:w-1/3 lg:min-w-[250px] lg:max-w-[400px] lg:border-l lg:border-t-0"
99+
>
100+
<div
101+
class="border-b border-gray-200 bg-gray-50 px-4 py-3 dark:border-gray-800 dark:bg-gray-800"
102+
>
103+
<h3 class="text-sm font-semibold text-gray-700 dark:text-gray-200">Form State (JSON)</h3>
104+
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
105+
Fill the form on the left to see the state update here.
106+
</p>
107+
</div>
108+
109+
<div class="flex-1 overflow-auto p-4">
110+
{#if Object.keys(formValues).length > 0}
111+
<pre class="text-xs text-gray-800 dark:text-gray-300">{JSON.stringify(
112+
formValues,
113+
null,
114+
2,
115+
)}</pre>
116+
{:else}
117+
<div class="flex h-full items-center justify-center text-sm text-gray-400">
118+
Loading form data...
119+
</div>
120+
{/if}
121+
</div>
122+
</div>
123+
</div>
124+
</div>
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<script lang="ts">
2+
import { usePdfiumEngine } from '@embedpdf/engines/svelte';
3+
import { EmbedPDF } from '@embedpdf/core/svelte';
4+
import { createPluginRegistration } from '@embedpdf/core';
5+
import {
6+
DocumentManagerPluginPackage,
7+
DocumentContent,
8+
} from '@embedpdf/plugin-document-manager/svelte';
9+
import { ViewportPluginPackage } from '@embedpdf/plugin-viewport/svelte';
10+
import { ScrollPluginPackage } from '@embedpdf/plugin-scroll/svelte';
11+
import { RenderPluginPackage } from '@embedpdf/plugin-render/svelte';
12+
import { ExportPluginPackage } from '@embedpdf/plugin-export/svelte';
13+
import { HistoryPluginPackage } from '@embedpdf/plugin-history/svelte';
14+
import { AnnotationPluginPackage, LockModeType } from '@embedpdf/plugin-annotation/svelte';
15+
import { InteractionManagerPluginPackage } from '@embedpdf/plugin-interaction-manager/svelte';
16+
import { SelectionPluginPackage } from '@embedpdf/plugin-selection/svelte';
17+
import { ZoomPluginPackage, ZoomMode } from '@embedpdf/plugin-zoom/svelte';
18+
import { FormPluginPackage } from '@embedpdf/plugin-form/svelte';
19+
import FormExampleContent from './form-example-content.svelte';
20+
21+
const pdfEngine = usePdfiumEngine();
22+
23+
const plugins = [
24+
createPluginRegistration(DocumentManagerPluginPackage, {
25+
initialDocuments: [{ url: '/form.pdf' }],
26+
}),
27+
createPluginRegistration(ViewportPluginPackage),
28+
createPluginRegistration(ScrollPluginPackage),
29+
createPluginRegistration(RenderPluginPackage),
30+
createPluginRegistration(ExportPluginPackage, {
31+
defaultFileName: 'form.pdf',
32+
}),
33+
createPluginRegistration(ZoomPluginPackage, {
34+
defaultZoomLevel: ZoomMode.FitPage,
35+
}),
36+
createPluginRegistration(InteractionManagerPluginPackage),
37+
createPluginRegistration(SelectionPluginPackage),
38+
createPluginRegistration(HistoryPluginPackage),
39+
createPluginRegistration(AnnotationPluginPackage, {
40+
locked: { type: LockModeType.Include, categories: ['form'] },
41+
}),
42+
createPluginRegistration(FormPluginPackage),
43+
];
44+
</script>
45+
46+
{#if pdfEngine.isLoading || !pdfEngine.engine}
47+
<div>Loading PDF Engine...</div>
48+
{:else}
49+
<EmbedPDF engine={pdfEngine.engine} {plugins}>
50+
{#snippet children({ activeDocumentId })}
51+
{#if activeDocumentId}
52+
<DocumentContent documentId={activeDocumentId}>
53+
{#snippet children(documentContent)}
54+
{#if documentContent.isLoaded}
55+
<FormExampleContent documentId={activeDocumentId} />
56+
{/if}
57+
{/snippet}
58+
</DocumentContent>
59+
{/if}
60+
{/snippet}
61+
</EmbedPDF>
62+
{/if}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from './form-example.svelte';

0 commit comments

Comments
 (0)