fix: export TweenOptions, SpringOptions, SpringUpdateOptions and Updater from svelte/motion (#17967)

Exports `TweenedOptions`, `SpringOpts`, `SpringUpdateOpts`, and
`Updater` from `svelte/motion`.

These types are required for the public method signatures of `spring`
and `tweened` (e.g., as parameters for `.set()` and `.update()`). This
PR makes them accessible to TypeScript users, following the established
pattern in modules like `svelte/store` and `svelte/transition`.

Internal implementation details like `TickContext` remain private as
they do not appear in any public-facing signatures.

Fixes #16151

### Before submitting the PR, please make sure you do the following

- [x] It's really useful if your PR references an issue where it is
discussed ahead of time. In many cases, features are absent for a
reason. For large changes, please create an RFC:
https://github.com/sveltejs/rfcs
- [x] Prefix your PR title with `feat:`, `fix:`, `chore:`, or `docs:`.
- [x] This message body should clearly illustrate what problems it
solves.
- [x] Ideally, include a test that fails without this PR but passes with
it.
- [x] If this PR changes code within `packages/svelte/src`, add a
changeset (`npx changeset`).

### Tests and linting

- [x] Run the tests with `pnpm test` and lint the project with `pnpm
lint`

---------

Co-authored-by: Rich Harris <rich.harris@vercel.com>
Co-authored-by: Rich Harris <hello@rich-harris.dev>
pull/17985/head
moaaz 1 month ago committed by GitHub
parent 0c669d9bc9
commit a94924b677
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': minor
---
feat: export TweenOptions, SpringOptions, SpringUpdateOptions and Updater from svelte/motion

@ -8,37 +8,3 @@ export interface TickContext {
};
settled: boolean;
}
export interface SpringOpts {
stiffness?: number;
damping?: number;
precision?: number;
}
export interface SpringUpdateOpts {
/**
* @deprecated Only use this for the spring store; does nothing when set on the Spring class
*/
hard?: any;
/**
* @deprecated Only use this for the spring store; does nothing when set on the Spring class
*/
soft?: string | number | boolean;
/**
* Only use this for the Spring class; does nothing when set on the spring store
*/
instant?: boolean;
/**
* Only use this for the Spring class; does nothing when set on the spring store
*/
preserveMomentum?: number;
}
export type Updater<T> = (target_value: T, value: T) => T;
export interface TweenedOptions<T> {
delay?: number;
duration?: number | ((from: T, to: T) => number);
easing?: (t: number) => number;
interpolate?: (a: T, b: T) => (t: number) => T;
}

@ -1,16 +1,49 @@
import { Readable, type Unsubscriber } from '../store/public.js';
import { SpringUpdateOpts, TweenedOptions, Updater, SpringOpts } from './private.js';
export interface SpringOptions {
stiffness?: number;
damping?: number;
precision?: number;
}
export interface SpringUpdateOptions {
/**
* @deprecated Only use this for the spring store; does nothing when set on the Spring class
*/
hard?: any;
/**
* @deprecated Only use this for the spring store; does nothing when set on the Spring class
*/
soft?: string | number | boolean;
/**
* Only use this for the Spring class; does nothing when set on the spring store
*/
instant?: boolean;
/**
* Only use this for the Spring class; does nothing when set on the spring store
*/
preserveMomentum?: number;
}
export type Updater<T> = (target_value: T, value: T) => T;
export interface TweenOptions<T> {
delay?: number;
duration?: number | ((from: T, to: T) => number);
easing?: (t: number) => number;
interpolate?: (a: T, b: T) => (t: number) => T;
}
// TODO we do declaration merging here in order to not have a breaking change (renaming the Spring interface)
// this means both the Spring class and the Spring interface are merged into one with some things only
// existing on one side. In Svelte 6, remove the type definition and move the jsdoc onto the class in spring.js
export interface Spring<T> extends Readable<T> {
set(new_value: T, opts?: SpringUpdateOpts): Promise<void>;
set(new_value: T, opts?: SpringUpdateOptions): Promise<void>;
/**
* @deprecated Only exists on the legacy `spring` store, not the `Spring` class
*/
update: (fn: Updater<T>, opts?: SpringUpdateOpts) => Promise<void>;
update: (fn: Updater<T>, opts?: SpringUpdateOptions) => Promise<void>;
/**
* @deprecated Only exists on the legacy `spring` store, not the `Spring` class
*/
@ -37,7 +70,7 @@ export interface Spring<T> extends Readable<T> {
* @since 5.8.0
*/
export class Spring<T> {
constructor(value: T, options?: SpringOpts);
constructor(value: T, options?: SpringOptions);
/**
* Create a spring whose value is bound to the return value of `fn`. This must be called
@ -53,7 +86,7 @@ export class Spring<T> {
* </script>
* ```
*/
static of<U>(fn: () => U, options?: SpringOpts): Spring<U>;
static of<U>(fn: () => U, options?: SpringOptions): Spring<U>;
/**
* Sets `spring.target` to `value` and returns a `Promise` that resolves if and when `spring.current` catches up to it.
@ -63,7 +96,7 @@ export class Spring<T> {
* If `options.preserveMomentum` is provided, the spring will continue on its current trajectory for
* the specified number of milliseconds. This is useful for things like 'fling' gestures.
*/
set(value: T, options?: SpringUpdateOpts): Promise<void>;
set(value: T, options?: SpringUpdateOptions): Promise<void>;
damping: number;
precision: number;
@ -81,8 +114,8 @@ export class Spring<T> {
}
export interface Tweened<T> extends Readable<T> {
set(value: T, opts?: TweenedOptions<T>): Promise<void>;
update(updater: Updater<T>, opts?: TweenedOptions<T>): Promise<void>;
set(value: T, opts?: TweenOptions<T>): Promise<void>;
update(updater: Updater<T>, opts?: TweenOptions<T>): Promise<void>;
}
export { prefersReducedMotion, spring, tweened, Tween } from './index.js';

@ -1,6 +1,6 @@
/** @import { Task } from '#client' */
/** @import { SpringOpts, SpringUpdateOpts, TickContext } from './private.js' */
/** @import { Spring as SpringStore } from './public.js' */
/** @import { TickContext } from './private.js' */
/** @import { Spring as SpringStore, SpringOptions, SpringUpdateOptions } from './public.js' */
import { writable } from '../store/shared/index.js';
import { loop } from '../internal/client/loop.js';
import { raf } from '../internal/client/timing.js';
@ -62,7 +62,7 @@ function tick_spring(ctx, last_value, current_value, target_value) {
* @deprecated Use [`Spring`](https://svelte.dev/docs/svelte/svelte-motion#Spring) instead
* @template [T=any]
* @param {T} [value]
* @param {SpringOpts} [opts]
* @param {SpringOptions} [opts]
* @returns {SpringStore<T>}
*/
export function spring(value, opts = {}) {
@ -83,7 +83,7 @@ export function spring(value, opts = {}) {
let cancel_task = false;
/**
* @param {T} new_value
* @param {SpringUpdateOpts} opts
* @param {SpringUpdateOptions} opts
* @returns {Promise<void>}
*/
function set(new_value, opts = {}) {
@ -191,7 +191,7 @@ export class Spring {
/**
* @param {T} value
* @param {SpringOpts} [options]
* @param {SpringOptions} [options]
*/
constructor(value, options = {}) {
this.#current = DEV ? tag(state(value), 'Spring.current') : state(value);
@ -225,7 +225,7 @@ export class Spring {
* ```
* @template U
* @param {() => U} fn
* @param {SpringOpts} [options]
* @param {SpringOptions} [options]
*/
static of(fn, options) {
const spring = new Spring(fn(), options);
@ -293,7 +293,7 @@ export class Spring {
* the specified number of milliseconds. This is useful for things like 'fling' gestures.
*
* @param {T} value
* @param {SpringUpdateOpts} [options]
* @param {SpringUpdateOptions} [options]
*/
set(value, options) {
this.#deferred?.reject(new Error('Aborted'));

@ -1,6 +1,5 @@
/** @import { Task } from '../internal/client/types' */
/** @import { Tweened } from './public' */
/** @import { TweenedOptions } from './private' */
/** @import { Tweened, TweenOptions } from './public' */
import { writable } from '../store/shared/index.js';
import { raf } from '../internal/client/timing.js';
import { loop } from '../internal/client/loop.js';
@ -84,7 +83,7 @@ function get_interpolator(a, b) {
* @deprecated Use [`Tween`](https://svelte.dev/docs/svelte/svelte-motion#Tween) instead
* @template T
* @param {T} [value]
* @param {TweenedOptions<T>} [defaults]
* @param {TweenOptions<T>} [defaults]
* @returns {Tweened<T>}
*/
export function tweened(value, defaults = {}) {
@ -94,7 +93,7 @@ export function tweened(value, defaults = {}) {
let target_value = value;
/**
* @param {T} new_value
* @param {TweenedOptions<T>} [opts]
* @param {TweenOptions<T>} [opts]
*/
function set(new_value, opts) {
target_value = new_value;
@ -180,7 +179,7 @@ export class Tween {
#current;
#target;
/** @type {TweenedOptions<T>} */
/** @type {TweenOptions<T>} */
#defaults;
/** @type {import('../internal/client/types').Task | null} */
@ -188,7 +187,7 @@ export class Tween {
/**
* @param {T} value
* @param {TweenedOptions<T>} options
* @param {TweenOptions<T>} options
*/
constructor(value, options = {}) {
this.#current = state(value);
@ -216,7 +215,7 @@ export class Tween {
* ```
* @template U
* @param {() => U} fn
* @param {TweenedOptions<U>} [options]
* @param {TweenOptions<U>} [options]
*/
static of(fn, options) {
const tween = new Tween(fn(), options);
@ -233,7 +232,7 @@ export class Tween {
*
* If `options` are provided, they will override the tween's defaults.
* @param {T} value
* @param {TweenedOptions<T>} [options]
* @param {TweenOptions<T>} [options]
* @returns
*/
set(value, options) {

@ -0,0 +1,22 @@
import {
type TweenOptions,
type SpringOptions,
type SpringUpdateOptions,
type Updater
} from 'svelte/motion';
let tweenOptions: TweenOptions<number> = {
delay: 100,
duration: 400
};
let springOptions: SpringOptions = {
stiffness: 0.1,
damping: 0.5
};
let springUpdateOptions: SpringUpdateOptions = {
instant: true
};
let updater: Updater<number> = (target, value) => target + value;

@ -2005,16 +2005,50 @@ declare module 'svelte/legacy' {
declare module 'svelte/motion' {
import type { MediaQuery } from 'svelte/reactivity';
export interface SpringOptions {
stiffness?: number;
damping?: number;
precision?: number;
}
export interface SpringUpdateOptions {
/**
* @deprecated Only use this for the spring store; does nothing when set on the Spring class
*/
hard?: any;
/**
* @deprecated Only use this for the spring store; does nothing when set on the Spring class
*/
soft?: string | number | boolean;
/**
* Only use this for the Spring class; does nothing when set on the spring store
*/
instant?: boolean;
/**
* Only use this for the Spring class; does nothing when set on the spring store
*/
preserveMomentum?: number;
}
export type Updater<T> = (target_value: T, value: T) => T;
export interface TweenOptions<T> {
delay?: number;
duration?: number | ((from: T, to: T) => number);
easing?: (t: number) => number;
interpolate?: (a: T, b: T) => (t: number) => T;
}
// TODO we do declaration merging here in order to not have a breaking change (renaming the Spring interface)
// this means both the Spring class and the Spring interface are merged into one with some things only
// existing on one side. In Svelte 6, remove the type definition and move the jsdoc onto the class in spring.js
export interface Spring<T> extends Readable<T> {
set(new_value: T, opts?: SpringUpdateOpts): Promise<void>;
set(new_value: T, opts?: SpringUpdateOptions): Promise<void>;
/**
* @deprecated Only exists on the legacy `spring` store, not the `Spring` class
*/
update: (fn: Updater<T>, opts?: SpringUpdateOpts) => Promise<void>;
update: (fn: Updater<T>, opts?: SpringUpdateOptions) => Promise<void>;
/**
* @deprecated Only exists on the legacy `spring` store, not the `Spring` class
*/
@ -2041,7 +2075,7 @@ declare module 'svelte/motion' {
* @since 5.8.0
*/
export class Spring<T> {
constructor(value: T, options?: SpringOpts);
constructor(value: T, options?: SpringOptions);
/**
* Create a spring whose value is bound to the return value of `fn`. This must be called
@ -2057,7 +2091,7 @@ declare module 'svelte/motion' {
* </script>
* ```
*/
static of<U>(fn: () => U, options?: SpringOpts): Spring<U>;
static of<U>(fn: () => U, options?: SpringOptions): Spring<U>;
/**
* Sets `spring.target` to `value` and returns a `Promise` that resolves if and when `spring.current` catches up to it.
@ -2067,7 +2101,7 @@ declare module 'svelte/motion' {
* If `options.preserveMomentum` is provided, the spring will continue on its current trajectory for
* the specified number of milliseconds. This is useful for things like 'fling' gestures.
*/
set(value: T, options?: SpringUpdateOpts): Promise<void>;
set(value: T, options?: SpringUpdateOptions): Promise<void>;
damping: number;
precision: number;
@ -2085,8 +2119,8 @@ declare module 'svelte/motion' {
}
export interface Tweened<T> extends Readable<T> {
set(value: T, opts?: TweenedOptions<T>): Promise<void>;
update(updater: Updater<T>, opts?: TweenedOptions<T>): Promise<void>;
set(value: T, opts?: TweenOptions<T>): Promise<void>;
update(updater: Updater<T>, opts?: TweenOptions<T>): Promise<void>;
}
/** Callback to inform of a value updates. */
type Subscriber<T> = (value: T) => void;
@ -2103,39 +2137,6 @@ declare module 'svelte/motion' {
*/
subscribe(this: void, run: Subscriber<T>, invalidate?: () => void): Unsubscriber;
}
interface SpringOpts {
stiffness?: number;
damping?: number;
precision?: number;
}
interface SpringUpdateOpts {
/**
* @deprecated Only use this for the spring store; does nothing when set on the Spring class
*/
hard?: any;
/**
* @deprecated Only use this for the spring store; does nothing when set on the Spring class
*/
soft?: string | number | boolean;
/**
* Only use this for the Spring class; does nothing when set on the spring store
*/
instant?: boolean;
/**
* Only use this for the Spring class; does nothing when set on the spring store
*/
preserveMomentum?: number;
}
type Updater<T> = (target_value: T, value: T) => T;
interface TweenedOptions<T> {
delay?: number;
duration?: number | ((from: T, to: T) => number);
easing?: (t: number) => number;
interpolate?: (a: T, b: T) => (t: number) => T;
}
/**
* A [media query](https://svelte.dev/docs/svelte/svelte-reactivity#MediaQuery) that matches if the user [prefers reduced motion](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion).
*
@ -2165,13 +2166,13 @@ declare module 'svelte/motion' {
*
* @deprecated Use [`Spring`](https://svelte.dev/docs/svelte/svelte-motion#Spring) instead
* */
export function spring<T = any>(value?: T | undefined, opts?: SpringOpts | undefined): Spring<T>;
export function spring<T = any>(value?: T | undefined, opts?: SpringOptions | undefined): Spring<T>;
/**
* A tweened store in Svelte is a special type of store that provides smooth transitions between state values over time.
*
* @deprecated Use [`Tween`](https://svelte.dev/docs/svelte/svelte-motion#Tween) instead
* */
export function tweened<T>(value?: T | undefined, defaults?: TweenedOptions<T> | undefined): Tweened<T>;
export function tweened<T>(value?: T | undefined, defaults?: TweenOptions<T> | undefined): Tweened<T>;
/**
* A wrapper for a value that tweens smoothly to its target value. Changes to `tween.target` will cause `tween.current` to
* move towards it over time, taking account of the `delay`, `duration` and `easing` options.
@ -2204,15 +2205,15 @@ declare module 'svelte/motion' {
* ```
*
*/
static of<U>(fn: () => U, options?: TweenedOptions<U> | undefined): Tween<U>;
static of<U>(fn: () => U, options?: TweenOptions<U> | undefined): Tween<U>;
constructor(value: T, options?: TweenedOptions<T>);
constructor(value: T, options?: TweenOptions<T>);
/**
* Sets `tween.target` to `value` and returns a `Promise` that resolves if and when `tween.current` catches up to it.
*
* If `options` are provided, they will override the tween's defaults.
* */
set(value: T, options?: TweenedOptions<T> | undefined): Promise<void>;
set(value: T, options?: TweenOptions<T> | undefined): Promise<void>;
get current(): T;
set target(v: T);
get target(): T;

Loading…
Cancel
Save