Document utilities and remove unused code
This commit is contained in:
parent
dd585b0790
commit
464ce24923
18 changed files with 170 additions and 124 deletions
|
@ -1,12 +1,23 @@
|
||||||
import type {
|
|
||||||
$IntentionalAny,
|
|
||||||
SerializableMap,
|
|
||||||
SerializableValue,
|
|
||||||
} from '@theatre/shared/utils/types'
|
|
||||||
import type {ObjectAddressKey, ProjectId, SheetId, SheetInstanceId} from './ids'
|
import type {ObjectAddressKey, ProjectId, SheetId, SheetInstanceId} from './ids'
|
||||||
import memoizeFn from './memoizeFn'
|
import memoizeFn from './memoizeFn'
|
||||||
import type {Nominal} from './Nominal'
|
import type {Nominal} from './Nominal'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Addresses are used to identify projects, sheets, objects, and other things.
|
||||||
|
*
|
||||||
|
* For example, a project's address looks like `{projectId: 'my-project'}`, and a sheet's
|
||||||
|
* address looks like `{projectId: 'my-project', sheetId: 'my-sheet'}`.
|
||||||
|
*
|
||||||
|
* As you see, a Sheet's address is a superset of a Project's address. This is so that we can
|
||||||
|
* use the same address type for both. All addresses follow the same rule. An object's address
|
||||||
|
* extends its sheet's address, which extends its project's address.
|
||||||
|
*
|
||||||
|
* For example, generating an object's address from a sheet's address is as simple as `{...sheetAddress, objectId: 'my-object'}`.
|
||||||
|
*
|
||||||
|
* Also, if you need the projectAddress of an object, you can just re-use the object's address:
|
||||||
|
* `aFunctionThatRequiresProjectAddress(objectAddress)`.
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the address to a project
|
* Represents the address to a project
|
||||||
*/
|
*/
|
||||||
|
@ -23,6 +34,8 @@ export interface ProjectAddress {
|
||||||
* sheet.address.sheetId === 'a sheet'
|
* sheet.address.sheetId === 'a sheet'
|
||||||
* sheet.address.sheetInstanceId === 'sheetInstanceId'
|
* sheet.address.sheetInstanceId === 'sheetInstanceId'
|
||||||
* ```
|
* ```
|
||||||
|
*
|
||||||
|
* See {@link WithoutSheetInstance} for a type that doesn't include the sheet instance id.
|
||||||
*/
|
*/
|
||||||
export interface SheetAddress extends ProjectAddress {
|
export interface SheetAddress extends ProjectAddress {
|
||||||
sheetId: SheetId
|
sheetId: SheetId
|
||||||
|
@ -31,7 +44,9 @@ export interface SheetAddress extends ProjectAddress {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes `sheetInstanceId` from an address, making it refer to
|
* Removes `sheetInstanceId` from an address, making it refer to
|
||||||
* all instances of a certain `sheetId`
|
* all instances of a certain `sheetId`.
|
||||||
|
*
|
||||||
|
* See {@link SheetAddress} for a type that includes the sheet instance id.
|
||||||
*/
|
*/
|
||||||
export type WithoutSheetInstance<T extends SheetAddress> = Omit<
|
export type WithoutSheetInstance<T extends SheetAddress> = Omit<
|
||||||
T,
|
T,
|
||||||
|
@ -42,7 +57,10 @@ export type SheetInstanceOptional<T extends SheetAddress> =
|
||||||
WithoutSheetInstance<T> & {sheetInstanceId?: SheetInstanceId | undefined}
|
WithoutSheetInstance<T> & {sheetInstanceId?: SheetInstanceId | undefined}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the address to a Sheet's Object
|
* Represents the address to a Sheet's Object.
|
||||||
|
*
|
||||||
|
* It includes the sheetInstance, so it's specific to a single instance of a sheet. If you
|
||||||
|
* would like an address that doesn't include the sheetInstance, use `WithoutSheetInstance<SheetObjectAddress>`.
|
||||||
*/
|
*/
|
||||||
export interface SheetObjectAddress extends SheetAddress {
|
export interface SheetObjectAddress extends SheetAddress {
|
||||||
/**
|
/**
|
||||||
|
@ -57,15 +75,32 @@ export interface SheetObjectAddress extends SheetAddress {
|
||||||
objectKey: ObjectAddressKey
|
objectKey: ObjectAddressKey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a simple array representing the path to a prop, without specifying the object.
|
||||||
|
*/
|
||||||
export type PathToProp = Array<string | number>
|
export type PathToProp = Array<string | number>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Just like {@link PathToProp}, but encoded as a string. Since this type is nominal,
|
||||||
|
* it can only be generated using {@link encodePathToProp}.
|
||||||
|
*/
|
||||||
export type PathToProp_Encoded = Nominal<'PathToProp_Encoded'>
|
export type PathToProp_Encoded = Nominal<'PathToProp_Encoded'>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes a {@link PathToProp} as a string, and caches the result, so as long
|
||||||
|
* as the input is the same, the output won't have to be re-generated.
|
||||||
|
*/
|
||||||
export const encodePathToProp = memoizeFn(
|
export const encodePathToProp = memoizeFn(
|
||||||
(p: PathToProp): PathToProp_Encoded =>
|
(p: PathToProp): PathToProp_Encoded =>
|
||||||
|
// we're using JSON.stringify here, but we could use a faster alternative.
|
||||||
|
// If you happen to do that, first make sure no `PathToProp_Encoded` is ever
|
||||||
|
// used in the store, otherwise you'll have to write a migration.
|
||||||
JSON.stringify(p) as PathToProp_Encoded,
|
JSON.stringify(p) as PathToProp_Encoded,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The decoder of {@link encodePathToProp}.
|
||||||
|
*/
|
||||||
export const decodePathToProp = (s: PathToProp_Encoded): PathToProp =>
|
export const decodePathToProp = (s: PathToProp_Encoded): PathToProp =>
|
||||||
JSON.parse(s)
|
JSON.parse(s)
|
||||||
|
|
||||||
|
@ -76,52 +111,35 @@ export interface PropAddress extends SheetObjectAddress {
|
||||||
pathToProp: PathToProp
|
pathToProp: PathToProp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the address of a certain sequence of a sheet.
|
||||||
|
*
|
||||||
|
* Since currently sheets are single-sequence only, `sequenceName` is always `'default'` for now.
|
||||||
|
*/
|
||||||
export interface SequenceAddress extends SheetAddress {
|
export interface SequenceAddress extends SheetAddress {
|
||||||
sequenceName: string
|
sequenceName: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getValueByPropPath = (
|
/**
|
||||||
pathToProp: PathToProp,
|
* Returns true if `path` starts with `pathPrefix`.
|
||||||
rootVal: SerializableMap,
|
*
|
||||||
): undefined | SerializableValue => {
|
* Example:
|
||||||
const p = [...pathToProp]
|
* ```ts
|
||||||
let cur: $IntentionalAny = rootVal
|
* const prefix: PathToProp = ['a', 'b']
|
||||||
|
* console.log(doesPathStartWith(['a', 'b', 'c'], prefix)) // true
|
||||||
while (p.length !== 0) {
|
* console.log(doesPathStartWith(['x', 'b', 'c'], prefix)) // false
|
||||||
const key = p.shift()!
|
* ```
|
||||||
|
*/
|
||||||
if (cur !== null && typeof cur === 'object') {
|
export function doesPathStartWith(path: PathToProp, pathPrefix: PathToProp) {
|
||||||
if (Array.isArray(cur)) {
|
|
||||||
if (typeof key === 'number') {
|
|
||||||
cur = cur[key]
|
|
||||||
} else {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (typeof key === 'string') {
|
|
||||||
cur = cur[key]
|
|
||||||
} else {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return cur
|
|
||||||
}
|
|
||||||
|
|
||||||
export function doesPathStartWith(
|
|
||||||
path: (string | number)[],
|
|
||||||
pathPrefix: (string | number)[],
|
|
||||||
) {
|
|
||||||
return pathPrefix.every((pathPart, i) => pathPart === path[i])
|
return pathPrefix.every((pathPart, i) => pathPart === path[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if pathToPropA and pathToPropB are equal.
|
||||||
|
*/
|
||||||
export function arePathsEqual(
|
export function arePathsEqual(
|
||||||
pathToPropA: (string | number)[],
|
pathToPropA: PathToProp,
|
||||||
pathToPropB: (string | number)[],
|
pathToPropB: PathToProp,
|
||||||
) {
|
) {
|
||||||
if (pathToPropA.length !== pathToPropB.length) return false
|
if (pathToPropA.length !== pathToPropB.length) return false
|
||||||
for (let i = 0; i < pathToPropA.length; i++) {
|
for (let i = 0; i < pathToPropA.length; i++) {
|
||||||
|
@ -131,7 +149,9 @@ export function arePathsEqual(
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* e.g.
|
* Given an array of `PathToProp`s, returns the longest common prefix.
|
||||||
|
*
|
||||||
|
* Example
|
||||||
* ```
|
* ```
|
||||||
* commonRootOfPathsToProps([
|
* commonRootOfPathsToProps([
|
||||||
* ['a','b','c','d','e'],
|
* ['a','b','c','d','e'],
|
||||||
|
@ -140,8 +160,8 @@ export function arePathsEqual(
|
||||||
* ]) // = ['a','b']
|
* ]) // = ['a','b']
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
export function commonRootOfPathsToProps(pathsToProps: (string | number)[][]) {
|
export function commonRootOfPathsToProps(pathsToProps: PathToProp[]) {
|
||||||
const commonPathToProp: (string | number)[] = []
|
const commonPathToProp: PathToProp = []
|
||||||
while (true) {
|
while (true) {
|
||||||
const i = commonPathToProp.length
|
const i = commonPathToProp.length
|
||||||
let candidatePathPart = pathsToProps[0]?.[i]
|
let candidatePathPart = pathsToProps[0]?.[i]
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
export default function createWeakCache(): WeakCache {
|
|
||||||
const cache = new Map()
|
|
||||||
const cleanup = new FinalizationRegistry((key) => {
|
|
||||||
const ref = cache.get(key)
|
|
||||||
if (ref && !ref.deref()) cache.delete(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
return function getOrSet<T extends {}>(key: string, producer: () => T): T {
|
|
||||||
const ref = cache.get(key)
|
|
||||||
if (ref) {
|
|
||||||
const cached = ref.deref()
|
|
||||||
if (cached !== undefined) return cached
|
|
||||||
}
|
|
||||||
|
|
||||||
const fresh = producer()
|
|
||||||
cache.set(key, new WeakRef(fresh))
|
|
||||||
cleanup.register(fresh, key)
|
|
||||||
return fresh
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface WeakCache {
|
|
||||||
<T extends {}>(key: string, producer: () => T): T
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
import type {DeepPartialOfSerializableValue, SerializableMap} from './types'
|
|
||||||
|
|
||||||
export default function deepMerge<T extends SerializableMap>(
|
|
||||||
base: T,
|
|
||||||
override: DeepPartialOfSerializableValue<T>,
|
|
||||||
): T {
|
|
||||||
const merged = {...base}
|
|
||||||
for (const key of Object.keys(override)) {
|
|
||||||
const valueInOverride = override[key]
|
|
||||||
const valueInBase = base[key]
|
|
||||||
|
|
||||||
// @ts-ignore @todo
|
|
||||||
merged[key] =
|
|
||||||
typeof valueInOverride === 'object' && typeof valueInBase === 'object'
|
|
||||||
? deepMerge(valueInBase, valueInOverride)
|
|
||||||
: valueInOverride
|
|
||||||
}
|
|
||||||
|
|
||||||
return merged
|
|
||||||
}
|
|
|
@ -39,7 +39,7 @@ import type {DeepPartialOfSerializableValue, SerializableMap} from './types'
|
||||||
*
|
*
|
||||||
* 4. Both `base` and `override` must be plain JSON values and *NO* arrays, so: `boolean, string, number, undefined, {}`
|
* 4. Both `base` and `override` must be plain JSON values and *NO* arrays, so: `boolean, string, number, undefined, {}`
|
||||||
*
|
*
|
||||||
* Rationale: This is used in {@link SheetObject.getValues()} to deep-merge static and sequenced
|
* Rationale: This is used in {@link SheetObject.getValues} to deep-merge static and sequenced
|
||||||
* and other types of overrides. If we were to do a deep-merge without a cache, we'd be creating and discarding
|
* and other types of overrides. If we were to do a deep-merge without a cache, we'd be creating and discarding
|
||||||
* several JS objects on each frame for every Theatre object, and that would pressure the GC.
|
* several JS objects on each frame for every Theatre object, and that would pressure the GC.
|
||||||
* Plus, keeping the values referentially stable helps lib authors optimize how they patch these values
|
* Plus, keeping the values referentially stable helps lib authors optimize how they patch these values
|
||||||
|
|
|
@ -5,6 +5,28 @@ export interface Deferred<PromiseType> {
|
||||||
status: 'pending' | 'resolved' | 'rejected'
|
status: 'pending' | 'resolved' | 'rejected'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple imperative API for resolving/rejecting a promise.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* ```ts
|
||||||
|
* function doSomethingAsync() {
|
||||||
|
* const deferred = defer()
|
||||||
|
*
|
||||||
|
* setTimeout(() => {
|
||||||
|
* if (Math.random() > 0.5) {
|
||||||
|
* deferred.resolve('success')
|
||||||
|
* } else {
|
||||||
|
* deferred.reject('Something went wrong')
|
||||||
|
* }
|
||||||
|
* }, 1000)
|
||||||
|
*
|
||||||
|
* // we're just returning the promise, so that the caller cannot resolve/reject it
|
||||||
|
* return deferred.promise
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
export function defer<PromiseType>(): Deferred<PromiseType> {
|
export function defer<PromiseType>(): Deferred<PromiseType> {
|
||||||
let resolve: (d: PromiseType) => void
|
let resolve: (d: PromiseType) => void
|
||||||
let reject: (d: unknown) => void
|
let reject: (d: unknown) => void
|
||||||
|
|
|
@ -1,5 +1,13 @@
|
||||||
import propose from 'propose'
|
import propose from 'propose'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proposes a suggestion to fix a typo in `str`, using the options provided in `dictionary`.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* ```ts
|
||||||
|
* didYouMean('helo', ['hello', 'world']) // 'Did you mean "hello"?'
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
export default function didYouMean(
|
export default function didYouMean(
|
||||||
str: string,
|
str: string,
|
||||||
dictionary: string[],
|
dictionary: string[],
|
||||||
|
|
|
@ -1,3 +1,11 @@
|
||||||
|
/**
|
||||||
|
* Truncates a string to a given length, adding an ellipsis if it was truncated.
|
||||||
|
* Example:
|
||||||
|
* ```ts
|
||||||
|
* ellipsify('hello world', 5) // 'hello...'
|
||||||
|
* ellipsify('hello world', 100) // 'hello world'
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
export default function ellipsify(str: string, maxLength: number) {
|
export default function ellipsify(str: string, maxLength: number) {
|
||||||
if (str.length <= maxLength) return str
|
if (str.length <= maxLength) return str
|
||||||
return str.substr(0, maxLength - 3) + '...'
|
return str.substr(0, maxLength - 3) + '...'
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
|
/**
|
||||||
|
* All errors thrown to end-users should be an instance of this class.
|
||||||
|
*/
|
||||||
export class TheatreError extends Error {}
|
export class TheatreError extends Error {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If an end-user provided an invalid argument to a public API, the error thrown
|
||||||
|
* should be an instance of this class.
|
||||||
|
*/
|
||||||
export class InvalidArgumentError extends TheatreError {}
|
export class InvalidArgumentError extends TheatreError {}
|
||||||
|
|
|
@ -1,6 +1,29 @@
|
||||||
import type {PathToProp} from './addresses'
|
import type {PathToProp} from './addresses'
|
||||||
import type {$IntentionalAny, SerializableMap} from './types'
|
import type {$IntentionalAny, SerializableMap} from './types'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterates recursively over all props of an object (which should be a {@link SerializableMap}) and runs `fn`
|
||||||
|
* on each prop that has a primitive value (string/number/boolean) and is _NOT_ null/undefined.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* ```ts
|
||||||
|
* forEachDeep(
|
||||||
|
* // The object to iterate over. The `fn` is going to be called on `b` and `c`.
|
||||||
|
* {a: {b: 1, c: 2, d: null, e: undefined}},
|
||||||
|
* // the function to run on each prop
|
||||||
|
* (value, pathToValue) => {
|
||||||
|
* console.log(value, pathToValue)
|
||||||
|
* },
|
||||||
|
* // We can optionally pass a path prefix to prepend to the path of each prop
|
||||||
|
* ['foo', 'bar'])
|
||||||
|
*
|
||||||
|
* // The above will log:
|
||||||
|
* // 1 ['foo', 'bar', 'a', 'b']
|
||||||
|
* // 2 ['foo', 'bar', 'a', 'c']
|
||||||
|
* // Note that null and undefined values are skipped.
|
||||||
|
* // Also note that `a` is also skippped, because it's not a primitive value.
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
export default function forEachDeep<
|
export default function forEachDeep<
|
||||||
Primitive extends string | number | boolean,
|
Primitive extends string | number | boolean,
|
||||||
>(
|
>(
|
||||||
|
|
|
@ -2,6 +2,18 @@ import lodashGet from 'lodash-es/get'
|
||||||
import type {PathToProp} from './addresses'
|
import type {PathToProp} from './addresses'
|
||||||
import type {SerializableValue} from './types'
|
import type {SerializableValue} from './types'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value at `path` of `v`.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* ```ts
|
||||||
|
* getDeep({a: {b: 1}}, ['a', 'b']) // 1
|
||||||
|
* getDeep({a: {b: 1}}, ['a', 'c']) // undefined
|
||||||
|
* getDeep({a: {b: 1}}, []) // {a: {b: 1}}
|
||||||
|
* getDeep('hello', []) // 'hello''
|
||||||
|
* getDeep('hello', ['a']) // undefined
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
export default function getDeep(
|
export default function getDeep(
|
||||||
v: SerializableValue,
|
v: SerializableValue,
|
||||||
path: PathToProp,
|
path: PathToProp,
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
import type {$FixMe} from './types'
|
|
||||||
|
|
||||||
const getPropsInCommon = (a: $FixMe, b: $FixMe): (string | number)[] => {
|
|
||||||
const keysInA = Object.keys(a)
|
|
||||||
|
|
||||||
const inCommon: (string | number)[] = []
|
|
||||||
for (const key of keysInA) {
|
|
||||||
if (b.hasOwnProperty(key)) {
|
|
||||||
inCommon.push(key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return inCommon
|
|
||||||
}
|
|
||||||
|
|
||||||
export default getPropsInCommon
|
|
|
@ -1,3 +0,0 @@
|
||||||
export default function identity<T>(a: T) {
|
|
||||||
return a
|
|
||||||
}
|
|
|
@ -1,6 +1,11 @@
|
||||||
import type {VoidFn} from './types'
|
/**
|
||||||
|
* This is just an empty object used in place of `{}` when you want to:
|
||||||
export const voidFn: VoidFn = () => {}
|
* 1. Not create many new objects (less GC pressure)
|
||||||
|
* 2. Have the empty object be a singleton (so that `===` works), so it can be fed to memoized functions.
|
||||||
|
*/
|
||||||
export const emptyObject = {}
|
export const emptyObject = {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The array equivalent of {@link emptyObject}.
|
||||||
|
*/
|
||||||
export const emptyArray: ReadonlyArray<unknown> = []
|
export const emptyArray: ReadonlyArray<unknown> = []
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
/**
|
/**
|
||||||
* Memoizes a unary function using a simple weakmap.
|
* Memoizes a unary function using a simple weakmap. The argument to the unary
|
||||||
|
* function must be WeakCache-able, which means it must be an object and not a plain
|
||||||
|
* number/string/boolean/etc.
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* ```ts
|
* ```ts
|
||||||
|
|
|
@ -21,10 +21,6 @@ function typeOfValue(v: unknown): ValueType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @remarks
|
|
||||||
* TODO explain what this does.
|
|
||||||
*/
|
|
||||||
export default function minimalOverride<T>(base: T, override: T): T {
|
export default function minimalOverride<T>(base: T, override: T): T {
|
||||||
const typeofOverride = typeOfValue(override)
|
const typeofOverride = typeOfValue(override)
|
||||||
if (typeofOverride === ValueType.Opaque) {
|
if (typeofOverride === ValueType.Opaque) {
|
||||||
|
|
|
@ -7,6 +7,9 @@ export type ReduxReducer<State extends {}> = (
|
||||||
|
|
||||||
export type VoidFn = () => void
|
export type VoidFn = () => void
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A `SerializableMap` is a plain JS object that can be safely serialized to JSON.
|
||||||
|
*/
|
||||||
export type SerializableMap<
|
export type SerializableMap<
|
||||||
Primitives extends SerializablePrimitive = SerializablePrimitive,
|
Primitives extends SerializablePrimitive = SerializablePrimitive,
|
||||||
> = {[Key in string]?: SerializableValue<Primitives>}
|
> = {[Key in string]?: SerializableValue<Primitives>}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import {voidFn} from '@theatre/shared/utils'
|
import noop from '@theatre/shared/utils/noop'
|
||||||
import React, {createContext, useCallback, useContext, useRef} from 'react'
|
import React, {createContext, useCallback, useContext, useRef} from 'react'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
import {zIndexes} from './SequenceEditorPanel'
|
import {zIndexes} from './SequenceEditorPanel'
|
||||||
|
@ -22,7 +22,7 @@ const Container = styled.div`
|
||||||
|
|
||||||
type ReceiveVerticalWheelEventFn = (ev: Pick<WheelEvent, 'deltaY'>) => void
|
type ReceiveVerticalWheelEventFn = (ev: Pick<WheelEvent, 'deltaY'>) => void
|
||||||
|
|
||||||
const ctx = createContext<ReceiveVerticalWheelEventFn>(voidFn)
|
const ctx = createContext<ReceiveVerticalWheelEventFn>(noop)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See {@link VerticalScrollContainer} and references for how to use this.
|
* See {@link VerticalScrollContainer} and references for how to use this.
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
import identity from '@theatre/shared/utils/identity'
|
|
||||||
import type {$IntentionalAny} from '@theatre/shared/utils/types'
|
import type {$IntentionalAny} from '@theatre/shared/utils/types'
|
||||||
|
|
||||||
|
function identity<T>(a: T) {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
interface Transformer<
|
interface Transformer<
|
||||||
Input extends $IntentionalAny,
|
Input extends $IntentionalAny,
|
||||||
Output extends $IntentionalAny,
|
Output extends $IntentionalAny,
|
||||||
|
|
Loading…
Reference in a new issue