Skip to content

Commit

Permalink
Add Svelte 5 runes support (#59)
Browse files Browse the repository at this point in the history
Update to Svelte 5
  • Loading branch information
rtrampox authored Jan 13, 2025
1 parent 527f88b commit 9de3bd8
Show file tree
Hide file tree
Showing 6 changed files with 570 additions and 223 deletions.
14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,24 +36,24 @@
},
"devDependencies": {
"@sveltejs/adapter-auto": "^3.0.0",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/package": "^2.2.4",
"@sveltejs/vite-plugin-svelte": "^3.0.0",
"@sveltejs/kit": "^2.15.0",
"@sveltejs/package": "^2.3.7",
"@sveltejs/vite-plugin-svelte": "^5.0.3",
"@typescript-eslint/eslint-plugin": "^6.15.0",
"@typescript-eslint/parser": "^6.15.0",
"cypress": "^12.4.1",
"eslint": "^8.28.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-cypress": "^2.12.1",
"eslint-plugin-svelte": "^2.35.1",
"prettier": "^2.8.0",
"prettier-plugin-svelte": "^2.10.1",
"prettier": "^3.4.2",
"prettier-plugin-svelte": "^3.3.2",
"query-string": "^8.1.0",
"start-server-and-test": "^1.15.3",
"svelte-check": "^3.4.3",
"tslib": "^2.4.1",
"typescript": "^5.0.0",
"vite": "^5.0.0",
"vite": "^6.0.5",
"vitest": "^1.0.0"
},
"peerDependencies": {
Expand All @@ -74,6 +74,6 @@
"!dist/**/*.spec.*"
],
"dependencies": {
"svelte": "^4.2.8"
"svelte": "^5.15.0"
}
}
105 changes: 61 additions & 44 deletions src/lib/Cropper.svelte
Original file line number Diff line number Diff line change
@@ -1,35 +1,36 @@
<script lang="ts">
import { createEventDispatcher, onDestroy, onMount } from 'svelte'
import type { HTMLImgAttributes } from 'svelte/elements'
import { onDestroy, onMount } from 'svelte'
import * as helpers from './helpers'
import type { CropShape, DispatchEvents, ImageSize, Point, Size } from './types'
export let image: string
export let crop: Point = { x: 0, y: 0 }
export let zoom = 1
export let aspect = 4 / 3
export let minZoom = 1
export let maxZoom = 3
export let cropSize: Size | null = null
export let cropShape: CropShape = 'rect'
export let showGrid = true
export let zoomSpeed = 1
export let crossOrigin: HTMLImgAttributes['crossorigin'] = null
export let restrictPosition = true
export let tabindex: number | undefined = undefined
let cropperSize: Size | null = null
let imageSize: ImageSize = { width: 0, height: 0, naturalWidth: 0, naturalHeight: 0 }
let containerEl: HTMLDivElement | null = null
let containerRect: DOMRect | null = null
let imgEl: HTMLImageElement | null = null
let dragStartPosition: Point = { x: 0, y: 0 }
let dragStartCrop: Point = { x: 0, y: 0 }
let lastPinchDistance = 0
let rafDragTimeout: number | null = null
let rafZoomTimeout: number | null = null
const dispatch = createEventDispatcher<DispatchEvents>()
import type { ImageSize, Point, CropperProps, Size } from './types'
import type { Action } from 'svelte/action'
let {
image,
crop = $bindable({ x: 0, y: 0 }),
zoom = $bindable(1),
minZoom = $bindable(1),
maxZoom = $bindable(3),
aspect = 4 / 3,
cropSize = null,
cropShape = 'rect',
showGrid = true,
zoomSpeed = 1,
crossOrigin = null,
restrictPosition = true,
tabindex = undefined,
oncropcomplete,
}: Partial<CropperProps> = $props()
let cropperSize = $state<Size | null>(null)
let imageSize = $state<ImageSize>({ width: 0, height: 0, naturalWidth: 0, naturalHeight: 0 })
let containerEl = $state<HTMLDivElement | null>(null)
let containerRect = $state<DOMRect | null>(null)
let imgEl = $state<HTMLImageElement | null>(null)
let dragStartPosition = $state<Point>({ x: 0, y: 0 })
let dragStartCrop = $state<Point>({ x: 0, y: 0 })
let lastPinchDistance = $state(0)
let rafDragTimeout = $state<number | null>(null)
let rafZoomTimeout = $state<number | null>(null)
onMount(() => {
// when rendered via SSR, the image can already be loaded and its onLoad callback will never be called
Expand Down Expand Up @@ -100,6 +101,7 @@
})
const onMouseDown = (e: MouseEvent) => {
e.preventDefault()
document.addEventListener('mousemove', onMouseMove)
document.addEventListener('mouseup', onDragStopped)
onDragStart(getMousePoint(e))
Expand All @@ -108,6 +110,7 @@
const onMouseMove = (e: MouseEvent) => onDrag(getMousePoint(e))
const onTouchStart = (e: TouchEvent) => {
e.preventDefault()
document.addEventListener('touchmove', onTouchMove, { passive: false }) // iOS 11 now defaults to passive: true
document.addEventListener('touchend', onDragStopped)
Expand Down Expand Up @@ -179,6 +182,7 @@
}
const onWheel = (e: WheelEvent) => {
e.preventDefault()
const point = getMousePoint(e)
const newZoom = zoom - (e.deltaY * zoomSpeed) / 200
setNewZoom(newZoom, point)
Expand Down Expand Up @@ -229,34 +233,47 @@
restrictPosition
)
dispatch('cropcomplete', {
oncropcomplete?.({
percent: croppedAreaPercentages,
pixels: croppedAreaPixels,
})
}
// ------ Reactive statement ------
//when aspect changes, we reset the cropperSize
$: if (imgEl) {
cropperSize = cropSize ? cropSize : helpers.getCropSize(imgEl.width, imgEl.height, aspect)
}
$effect(() => {
if (imgEl) {
cropperSize = cropSize ? cropSize : helpers.getCropSize(imgEl.width, imgEl.height, aspect)
}
})
// when zoom changes, we recompute the cropped area
$: zoom && emitCropData()
$effect(() => {
if (zoom) {
emitCropData()
}
})
$: if(aspect) {
computeSizes()
emitCropData()
}
const containerAction: Action<HTMLDivElement> = node => {
$effect(() => {
node.addEventListener('touchstart', onTouchStart)
node.addEventListener('mousedown', onMouseDown)
node.addEventListener('wheel', onWheel, { passive: false })
return () => {
node.removeEventListener('touchstart', onTouchStart)
node.removeEventListener('mousedown', onMouseDown)
node.removeEventListener('wheel', onWheel)
}
})
}
</script>

<svelte:window on:resize={computeSizes} />
<div
class="svelte-easy-crop-container"
bind:this={containerEl}
on:mousedown|preventDefault={onMouseDown}
on:touchstart|nonpassive|preventDefault={onTouchStart}
on:wheel|nonpassive|preventDefault={onWheel}
use:containerAction
{tabindex}
role="button"
data-testid="container"
Expand All @@ -265,7 +282,7 @@
bind:this={imgEl}
class="svelte-easy-crop-image"
src={image}
on:load={onImgLoad}
onload={onImgLoad}
alt=""
style="transform: translate({crop.x}px, {crop.y}px) scale({zoom});"
crossorigin={crossOrigin}
Expand All @@ -277,7 +294,7 @@
class:svelte-easy-crop-grid={showGrid}
style="width: {cropperSize.width}px; height: {cropperSize.height}px;"
data-testid="cropper"
/>
></div>
{/if}
</div>

Expand Down
1 change: 1 addition & 0 deletions src/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import Cropper from './Cropper.svelte'

export * from "./types"
export default Cropper
22 changes: 22 additions & 0 deletions src/lib/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,27 @@
import type { HTMLImgAttributes } from 'svelte/elements'

export type CropShape = 'rect' | 'round'

export type OnCropCompleteEvent = { percent: CropArea; pixels: CropArea }
export type OnCropComplete = (event: OnCropCompleteEvent) => void

export type CropperProps = {
image: string
crop: Point
zoom: number
aspect: number
minZoom: number
maxZoom: number
cropSize: Size | null
cropShape: CropShape
showGrid: boolean
zoomSpeed: number
crossOrigin: HTMLImgAttributes['crossorigin']
restrictPosition: boolean
tabindex: number | undefined
oncropcomplete: OnCropComplete
}

export interface Size {
width: number
height: number
Expand Down
10 changes: 5 additions & 5 deletions src/routes/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
import queryString from 'query-string'
import Cropper from '../lib'
let crop = { x: 0, y: 0 }
let zoom = 1
let minZoom = 0.5;
let maxZoom = 3;
let crop = $state({ x: 0, y: 0 })
let zoom = $state(1)
let minZoom = $state(0.5)
let maxZoom = $state(3)
const urlArgs = typeof window !== 'undefined' ? queryString.parse(window.location.search) : null
let image = typeof urlArgs?.img === 'string' ? urlArgs.img : '/images/dog.jpeg' // so we can change the image from our tests
Expand All @@ -17,5 +17,5 @@
bind:zoom
bind:minZoom
bind:maxZoom
on:cropcomplete={e => console.log(e.detail)}
oncropcomplete={(e) => console.log(e)}
/>
Loading

0 comments on commit 9de3bd8

Please sign in to comment.