Skip to content

Commit

Permalink
Add support for Svelte 5 ✨ (#331)
Browse files Browse the repository at this point in the history
  • Loading branch information
willnguyen1312 authored Oct 25, 2024
1 parent 5af0378 commit 2de0842
Show file tree
Hide file tree
Showing 19 changed files with 1,966 additions and 480 deletions.
1 change: 1 addition & 0 deletions .changeset/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"with-next",
"with-solid",
"with-svelte",
"with-svelte-5",
"with-preact",
"with-qwik",
"with-remix"
Expand Down
5 changes: 5 additions & 0 deletions .changeset/serious-steaks-sneeze.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@zoom-image/svelte": minor
---

Add support for Svelte 5 ✨
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ pnpm build

## Commands

Please change all versions from "latest" to "workspace\*" inside the example project you want to start, run pnpm install
before executing one of the commands below. I wanted to keep those packages for Stackblitz usage so I use "latest",
unfortunately pnpm doesn't understand it during local development 🙈
Please change all versions from "latest" to "workspace:\*" inside the example project you want to start, run pnpm
install before executing one of the commands below. I wanted to keep those packages for Stackblitz usage so I use
"latest", unfortunately pnpm doesn't understand it during local development 🙈

### With Docs

Expand Down
13 changes: 13 additions & 0 deletions examples/with-svelte-5/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Svelte + TS</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
23 changes: 23 additions & 0 deletions examples/with-svelte-5/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "with-svelte-5",
"private": true,
"type": "module",
"scripts": {
"dev": "vite --host"
},
"dependencies": {
"@zoom-image/core": "latest",
"@zoom-image/svelte": "latest"
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^4.0.0",
"@tsconfig/svelte": "^5.0.4",
"@unocss/reset": "^0.63.6",
"svelte": "^5.1.2",
"svelte-check": "^4.0.5",
"tslib": "^2.8.0",
"typescript": "^5.6.3",
"unocss": "^0.63.6",
"vite": "^5.4.10"
}
}
Binary file added examples/with-svelte-5/public/sample.avif
Binary file not shown.
186 changes: 186 additions & 0 deletions examples/with-svelte-5/src/App.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
<script lang="ts">
import { cropImage, type ZoomImageWheelState } from "@zoom-image/core"
import { useZoomImageClick, useZoomImageHover, useZoomImageMove, useZoomImageWheel } from "@zoom-image/svelte"
import { onMount, tick } from "svelte"
let tabs: {
name: string
href: string
current: boolean
value: "wheel" | "hover" | "move" | "click"
}[] = $state([
{ name: "Wheel", href: "#", current: true, value: "wheel" },
{ name: "Hover", href: "#", current: false, value: "hover" },
{ name: "Move", href: "#", current: false, value: "move" },
{ name: "Click", href: "#", current: false, value: "click" },
])
let zoomType = $derived(tabs.find((tab) => tab.current)!.value)
let croppedImage = $state("")
// svelte-ignore non_reactive_update
let imageWheelContainer: HTMLDivElement
// svelte-ignore non_reactive_update
let imageMoveContainer: HTMLDivElement
// svelte-ignore non_reactive_update
let imageHoverContainer: HTMLDivElement
// svelte-ignore non_reactive_update
let imageClickContainer: HTMLDivElement
// svelte-ignore non_reactive_update
let zoomTarget: HTMLDivElement
const {
createZoomImage: createZoomImageWheel,
zoomImageState: zoomImageWheelState,
setZoomImageState: setZoomImageWheelState,
} = useZoomImageWheel()
const { createZoomImage: createZoomImageHover } = useZoomImageHover()
const { createZoomImage: createZoomImageMove } = useZoomImageMove()
const { createZoomImage: createZoomImageClick } = useZoomImageClick()
let zoomImageWheelStateValue = $state<ZoomImageWheelState>() as ZoomImageWheelState
let croppedImageClasses = $derived(
zoomImageWheelStateValue.currentRotation % 180 === 90 ? "h-[200px] w-[300px]" : "h-[300px] w-[200px]",
)
zoomImageWheelState.subscribe((value) => {
zoomImageWheelStateValue = value
})
async function processZoom(zoomType: "wheel" | "hover" | "move" | "click") {
croppedImage = ""
await tick()
if (zoomType === "hover") {
createZoomImageHover(imageHoverContainer, {
zoomImageSource: "/sample.avif",
customZoom: { width: 300, height: 500 },
zoomTarget,
scale: 2,
})
}
if (zoomType === "wheel") {
createZoomImageWheel(imageWheelContainer)
}
if (zoomType === "move") {
createZoomImageMove(imageMoveContainer, {
zoomImageSource: "/sample.avif",
})
}
if (zoomType === "click") {
createZoomImageClick(imageClickContainer, {
zoomImageSource: "/sample.avif",
})
}
}
async function handleCropImage() {
croppedImage = await cropImage({
currentZoom: zoomImageWheelStateValue.currentZoom,
image: imageWheelContainer.querySelector("img") as HTMLImageElement,
positionX: zoomImageWheelStateValue.currentPositionX,
positionY: zoomImageWheelStateValue.currentPositionY,
rotation: zoomImageWheelStateValue.currentRotation,
})
}
function rotate() {
setZoomImageWheelState({
currentRotation: zoomImageWheelStateValue.currentRotation + 90,
})
if (croppedImage) {
handleCropImage()
}
}
onMount(() => {
processZoom(zoomType)
})
</script>

<div class="p-4 font-sans">
<nav class="flex space-x-4 pb-4" aria-label="Tabs">
{#each tabs as tab (tab.name)}
<a
onclick={() => {
tabs.forEach((tab) => {
tab.current = false
})
tab.current = true
processZoom(tab.value)
}}
aria-current={tab.current ? "page" : undefined}
href={tab.href}
class={`decoration-none rounded-md px-3 py-2 text-sm font-medium ${
tab.current ? "bg-gray-100 text-gray-700" : "text-gray-500 hover:text-gray-700"
}`}>{tab.name}</a
>
{/each}
</nav>

{#if zoomType === "wheel"}
<div class="space-y-4">
<p>Current zoom: {`${Math.round(zoomImageWheelStateValue.currentZoom * 100)}%`}</p>
<p>Scroll inside the image to see zoom in-out effect</p>
<div class="flex items-center gap-4">
<div class="mt-1 grid h-[300px] w-[300px] place-content-center bg-black">
<div bind:this={imageWheelContainer} class="h-[300px] w-[200px] cursor-crosshair duration-500">
<img class="h-full w-full" alt="Large Pic" src="/sample.avif" />
</div>
</div>

{#if croppedImage !== ""}
<img src={croppedImage} class={croppedImageClasses} alt="Cropped placeholder" />
{/if}
</div>
<div class="flex space-x-2">
<button
onclick={() => {
setZoomImageWheelState({
currentZoom: zoomImageWheelStateValue.currentZoom + 0.5,
})
}}
class="text-dark-500 rounded bg-gray-100 p-2 text-sm font-medium"
>
Zoom in
</button>
<button
onclick={() => {
setZoomImageWheelState({
currentZoom: zoomImageWheelStateValue.currentZoom - 0.5,
})
}}
class="text-dark-500 rounded bg-gray-100 p-2 text-sm font-medium"
>
Zoom out
</button>
<button class="text-dark-500 rounded bg-gray-100 p-2 text-sm font-medium" onclick={handleCropImage}>
Crop image
</button>
<button onclick={rotate} class="text-dark-500 rounded bg-gray-100 p-2 text-sm font-medium">Rotate</button>
</div>
</div>
{/if}

{#if zoomType === "hover"}
<div bind:this={imageHoverContainer} class="relative flex h-[300px] w-[200px] items-start">
<img class="h-full w-full" alt="Small Pic" src="/sample.avif" />
<div bind:this={zoomTarget} class="absolute left-[350px]"></div>
</div>
{/if}

{#if zoomType === "move"}
<div bind:this={imageMoveContainer} class="relative h-[300px] w-[200px] cursor-crosshair overflow-hidden">
<img class="h-full w-full" alt="Large Pic" src="/sample.avif" />
</div>
{/if}

{#if zoomType === "click"}
<div bind:this={imageClickContainer} class="relative h-[300px] w-[200px] cursor-crosshair overflow-hidden">
<img class="h-full w-full" alt="Large Pic" src="/sample.avif" />
</div>
{/if}
</div>
10 changes: 10 additions & 0 deletions examples/with-svelte-5/src/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import "virtual:uno.css"
import "@unocss/reset/tailwind.css"
import { mount } from "svelte"
import App from "./App.svelte"

const app = mount(App, {
target: document.getElementById("app")!,
})

export default app
2 changes: 2 additions & 0 deletions examples/with-svelte-5/src/vite-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/// <reference types="svelte" />
/// <reference types="vite/client" />
7 changes: 7 additions & 0 deletions examples/with-svelte-5/svelte.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { vitePreprocess } from "@sveltejs/vite-plugin-svelte"

export default {
// Consult https://svelte.dev/docs#compile-time-svelte-preprocess
// for more information about preprocessors
preprocess: vitePreprocess(),
}
14 changes: 14 additions & 0 deletions examples/with-svelte-5/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"extends": "@tsconfig/svelte/tsconfig.json",
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"module": "ESNext",
"resolveJsonModule": true,
"allowJs": true,
"checkJs": true,
"isolatedModules": true
},
"include": ["src/**/*.d.ts", "src/**/*.ts", "src/**/*.js", "src/**/*.svelte"],
"references": [{ "path": "./tsconfig.node.json" }]
}
9 changes: 9 additions & 0 deletions examples/with-svelte-5/tsconfig.node.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler"
},
"include": ["vite.config.ts"]
}
3 changes: 3 additions & 0 deletions examples/with-svelte-5/uno.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { defineConfig } from "unocss"

export default defineConfig({})
15 changes: 15 additions & 0 deletions examples/with-svelte-5/vite.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { defineConfig } from "vite"

import { svelte } from "@sveltejs/vite-plugin-svelte"

import UnoCSS from "unocss/vite"

// https://vitejs.dev/config/
export default defineConfig({
plugins: [svelte(), UnoCSS()],
server: {
port: 1312,
host: true,
open: true,
},
})
4 changes: 2 additions & 2 deletions examples/with-svelte/src/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@
async function handleCropImage() {
croppedImage = await cropImage({
currentZoom: zoomImageWheelState_value.currentZoom,
image: imageWheelContainer.querySelector("img"),
image: imageWheelContainer.querySelector("img") as HTMLImageElement,
positionX: zoomImageWheelState_value.currentPositionX,
positionY: zoomImageWheelState_value.currentPositionY,
rotation: zoomImageWheelState_value.currentRotation,
Expand Down Expand Up @@ -161,7 +161,7 @@
{#if zoomType === "hover"}
<div bind:this={imageHoverContainer} class="relative flex h-[300px] w-[200px] items-start">
<img class="h-full w-full" alt="Small Pic" src="/sample.avif" />
<div bind:this={zoomTarget} class="absolute left-[350px]" />
<div bind:this={zoomTarget} class="absolute left-[350px]"></div>
</div>
{/if}

Expand Down
4 changes: 4 additions & 0 deletions packages/docs/src/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ export default defineConfig({
text: "Svelte",
link: "/examples/svelte",
},
{
text: "Svelte 5",
link: "/examples/svelte-5",
},
{
text: "Solid",
link: "/examples/solid",
Expand Down
11 changes: 11 additions & 0 deletions packages/docs/src/examples/svelte-5.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
aside: false
---

# Svelte Example

<script setup>
import Demo from '../components/DemoComp.vue'
</script>

<Demo url="https://stackblitz.com/github/willnguyen1312/zoom-image/tree/main/examples/with-svelte-5?embed=1&theme=dark" />
2 changes: 1 addition & 1 deletion packages/svelte/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,6 @@
"@zoom-image/core": "workspace:*"
},
"peerDependencies": {
"svelte": "^3.0.0 || ^4.0.0"
"svelte": "^3.0.0 || ^4.0.0 || ^5.0.0"
}
}
Loading

0 comments on commit 2de0842

Please sign in to comment.