@khanacademy/perseus 19.1.3 → 19.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -14,11 +14,11 @@ declare const InteractiveUtil: {
14
14
  /**
15
15
  * Turn a function or an array of functions into an array of functions
16
16
  */
17
- readonly arrayify: (funcOrArray: readonly unknown[] | ((...args: ReadonlyArray<unknown>) => unknown)) => ReadonlyArray<unknown>;
17
+ readonly arrayify: (funcOrArray: ((...args: ReadonlyArray<unknown>) => unknown) | readonly unknown[]) => ReadonlyArray<unknown>;
18
18
  /**
19
19
  * Convert all function-or-array arguments to arrays of functions
20
20
  */
21
- readonly normalizeOptions: (arrayOptionNames: ReadonlyArray<string>, options: Record<any, any>) => Record<any, any>;
21
+ readonly normalizeOptions: (options: Record<any, any>) => Record<any, any>;
22
22
  /**
23
23
  * Get the correct vendor-prefixed `transform`.
24
24
  */
@@ -1,4 +1,5 @@
1
- import type { Coord } from "./types";
1
+ import type { Constraint, Coord } from "./types";
2
+ import type { Interval } from "../util/interval";
2
3
  declare const _default: {
3
4
  add: {
4
5
  readonly constrain: () => void;
@@ -17,9 +18,9 @@ declare const _default: {
17
18
  standard: null;
18
19
  };
19
20
  constraints: {
20
- readonly fixed: () => () => boolean;
21
- readonly snap: (snap: any) => (arg1: Coord) => unknown;
22
- readonly bound: (range: any, snap: any, paddingPx: number) => (arg1: Coord, arg2: Coord, arg3: any) => Coord;
21
+ readonly fixed: () => Constraint;
22
+ readonly snap: (snap?: number | null | undefined) => Constraint;
23
+ readonly bound: (range?: [Interval, Interval] | undefined, snap?: number | Coord | undefined, optionalPaddingPx?: number | undefined) => Constraint;
23
24
  };
24
25
  onMove: {
25
26
  standard: null;
@@ -1,2 +1,98 @@
1
- declare const Movable: (graphie: any, options: any) => void;
2
- export default Movable;
1
+ import type { Constraint, ConstraintCallbacks, Coord } from "./types";
2
+ import type { Graphie } from "../util/graphie";
3
+ export interface State {
4
+ added?: boolean;
5
+ isHovering?: boolean;
6
+ isMouseOver?: boolean;
7
+ isDragging?: boolean;
8
+ mouseTarget?: {
9
+ toFront(): void;
10
+ toBack(): void;
11
+ remove(): void;
12
+ getMouseTarget(): Element;
13
+ } | null;
14
+ cursor?: null;
15
+ id: string;
16
+ add?: (() => void)[];
17
+ modify?: ((state: State, prevState?: State) => void)[];
18
+ draw?: ((state: State, prevState?: State) => void)[];
19
+ remove?: (() => void)[];
20
+ onMoveStart?: ((position: Coord, positionAgain: Coord) => void)[];
21
+ onMove?: ((end: Coord, start: Coord) => void)[];
22
+ onMoveEnd?: ((end: Coord, start: Coord) => void)[];
23
+ onClick?: ((position: Coord, start: Coord) => void)[];
24
+ constraints?: Constraint[];
25
+ }
26
+ export declare class Movable<Options extends Record<string, any>> {
27
+ graphie: Graphie;
28
+ state: State;
29
+ prevState: State | undefined;
30
+ _listenerMap: Record<string, number>;
31
+ constructor(graphie: Graphie, options: Options);
32
+ modify(options: Options): void;
33
+ _createDefaultState(): {
34
+ cursor: null;
35
+ id: string;
36
+ add: never[];
37
+ modify: never[];
38
+ draw: never[];
39
+ remove: never[];
40
+ onMoveStart: never[];
41
+ onMove: never[];
42
+ onMoveEnd: never[];
43
+ onClick: never[];
44
+ };
45
+ update(options: Options): void;
46
+ cloneState(): State;
47
+ /**
48
+ * Fire an onSomething type event to all functions in listeners
49
+ */
50
+ _fireEvent<F extends (...args: any[]) => any>(listeners: F[] | undefined, ...args: Parameters<F>): void;
51
+ /**
52
+ * Call all draw functions, and update our prevState for the next
53
+ * draw function
54
+ */
55
+ draw(): void;
56
+ /**
57
+ * Simulates a mouse grab event on the movable object.
58
+ */
59
+ grab(coord: Coord): void;
60
+ _applyConstraints(current: Coord, previous: Coord, extraOptions?: ConstraintCallbacks): any;
61
+ toBack(): void;
62
+ toFront(): void;
63
+ /**
64
+ * Add a listener to any event: startMove, constraints, onMove, onMoveEnd,
65
+ * etc. If a listener is already bound to the given eventName and id, then
66
+ * it is overwritten by func.
67
+ *
68
+ * eventName: the string name of the event to listen to. one of:
69
+ * "onMoveStart", "onMove", "onMoveEnd", "draw", "remove"
70
+ *
71
+ * id: a string id that can be used to remove this event at a later time
72
+ * note: adding multiple listeners with the same id is undefined behavior
73
+ *
74
+ * func: the function to call when the event happens, which is called
75
+ * with the event's standard parameters [usually (coord, prevCoord) or
76
+ * (state, prevState)]
77
+ */
78
+ listen(eventName: string, id: string, func: () => unknown): void;
79
+ /**
80
+ * Remove a previously added listener, by the id specified in the
81
+ * corresponding listen() call
82
+ *
83
+ * If the given id has not been registered already, this is a no-op
84
+ */
85
+ unlisten(eventName: string, id: string): void;
86
+ remove(): void;
87
+ cursor(): null | undefined;
88
+ added(): boolean | undefined;
89
+ isHovering(): boolean | undefined;
90
+ isMouseOver(): boolean | undefined;
91
+ isDragging(): boolean | undefined;
92
+ mouseTarget(): {
93
+ toFront(): void;
94
+ toBack(): void;
95
+ remove(): void;
96
+ getMouseTarget(): Element;
97
+ } | null | undefined;
98
+ }
@@ -1,2 +1,8 @@
1
+ import type { Movable } from "./movable";
1
2
  export type Coord = [number, number];
2
3
  export type Line = [Coord, Coord];
4
+ export type Constraint = (this: Movable<any>, current: Coord | boolean, previous: Coord, options: ConstraintCallbacks) => Coord | boolean | undefined;
5
+ export type ConstraintCallbacks = {
6
+ onSkipRemaining(): void;
7
+ onOutOfBounds?: () => void;
8
+ };
@@ -40,9 +40,10 @@ export declare class Graphie {
40
40
  dimensions?: Coord;
41
41
  xpixels?: number;
42
42
  ypixels?: number;
43
+ isDragging?: boolean;
43
44
  constructor(el: Element);
44
45
  init(options: {
45
- range?: [Interval, Interval];
46
+ range: [Interval, Interval];
46
47
  scale?: number | Coord;
47
48
  isMobile?: boolean;
48
49
  }): this;
@@ -102,6 +103,10 @@ export declare class Graphie {
102
103
  unscalePoint: (point: Coord) => any;
103
104
  unscaleVector: (point: Coord) => any;
104
105
  private processAttributes;
106
+ getMouseCoord(event: Readonly<{
107
+ pageX?: number;
108
+ pageY?: number;
109
+ }>): Coord;
105
110
  }
106
111
  type RangeSpecifier = [Interval, Interval] | Coord | number;
107
112
  export declare function normalizeRange(range: RangeSpecifier): [Interval, Interval];
@@ -212,10 +212,10 @@ declare const _default: Readonly<{
212
212
  onFocus: (blurPath: import("../types").FocusPath) => void;
213
213
  times: boolean;
214
214
  value: string;
215
- }, "trackInteraction" | "onChange" | "static" | "analytics" | "problemNum" | "questionCompleted" | "keypadElement" | "alignment" | "buttonsVisible" | "containerSizeClass" | "widgetId" | "findWidgets" | "keypadConfiguration" | "reviewModeRubric" | "isLastUsedWidget"> & {
216
- value?: string | undefined;
215
+ }, "onChange" | "trackInteraction" | "static" | "analytics" | "problemNum" | "questionCompleted" | "keypadElement" | "alignment" | "buttonsVisible" | "containerSizeClass" | "widgetId" | "findWidgets" | "keypadConfiguration" | "reviewModeRubric" | "isLastUsedWidget"> & {
217
216
  onFocus?: ((blurPath: import("../types").FocusPath) => void) | undefined;
218
217
  onBlur?: ((blurPath: import("../types").FocusPath) => void) | undefined;
218
+ value?: string | undefined;
219
219
  linterContext?: import("@khanacademy/perseus-linter").LinterContextProps | undefined;
220
220
  apiOptions?: Readonly<Readonly<{
221
221
  isArticle?: boolean | undefined;
@@ -401,10 +401,10 @@ declare const _default: Readonly<{
401
401
  onFocus: (blurPath: import("../types").FocusPath) => void;
402
402
  times: boolean;
403
403
  value: string;
404
- }, "trackInteraction" | "onChange" | "static" | "analytics" | "problemNum" | "questionCompleted" | "keypadElement" | "alignment" | "buttonsVisible" | "containerSizeClass" | "widgetId" | "findWidgets" | "keypadConfiguration" | "reviewModeRubric" | "isLastUsedWidget"> & {
405
- value?: string | undefined;
404
+ }, "onChange" | "trackInteraction" | "static" | "analytics" | "problemNum" | "questionCompleted" | "keypadElement" | "alignment" | "buttonsVisible" | "containerSizeClass" | "widgetId" | "findWidgets" | "keypadConfiguration" | "reviewModeRubric" | "isLastUsedWidget"> & {
406
405
  onFocus?: ((blurPath: import("../types").FocusPath) => void) | undefined;
407
406
  onBlur?: ((blurPath: import("../types").FocusPath) => void) | undefined;
407
+ value?: string | undefined;
408
408
  linterContext?: import("@khanacademy/perseus-linter").LinterContextProps | undefined;
409
409
  apiOptions?: Readonly<Readonly<{
410
410
  isArticle?: boolean | undefined;
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "Core Perseus API (includes renderers and widgets)",
4
4
  "author": "Khan Academy",
5
5
  "license": "MIT",
6
- "version": "19.1.3",
6
+ "version": "19.1.5",
7
7
  "publishConfig": {
8
8
  "access": "public"
9
9
  },
@@ -23,8 +23,8 @@
23
23
  },
24
24
  "dependencies": {
25
25
  "@khanacademy/kas": "^0.3.7",
26
- "@khanacademy/kmath": "^0.1.10",
27
- "@khanacademy/math-input": "^17.0.2",
26
+ "@khanacademy/kmath": "^0.1.11",
27
+ "@khanacademy/math-input": "^17.0.4",
28
28
  "@khanacademy/perseus-core": "1.4.1",
29
29
  "@khanacademy/perseus-linter": "^0.3.10",
30
30
  "@khanacademy/pure-markdown": "^0.2.13",
@@ -62,6 +62,9 @@ type DefaultProps = {
62
62
 
63
63
  interface Movable {
64
64
  remove(): void;
65
+ props: unknown;
66
+ modify(graphie: GraphieDrawingContext): "reordered" | undefined;
67
+ toFront(): void;
65
68
  }
66
69
 
67
70
  class Graphie extends React.Component<Props> {
@@ -231,7 +234,12 @@ class Graphie extends React.Component<Props> {
231
234
  // @ts-expect-error - TS2322 - Type '(children: readonly any[], options: any) => ReactElement<any, string | JSXElementConstructor<any>> | readonly ReactElement<any, string | JSXElementConstructor<any>>[]' is not assignable to type '(children: readonly any[], arg2: any) => ReactElement<any, string | JSXElementConstructor<any>>'.
232
235
  _renderMovables: (
233
236
  children: ReadonlyArray<any>,
234
- arg2: any,
237
+ arg2: {
238
+ nextKey: number;
239
+ graphie: GraphieDrawingContext;
240
+ oldMovables: Record<string, Movable>;
241
+ newMovables: Record<string, Movable>;
242
+ },
235
243
  ) => React.ReactElement = (children, options) => {
236
244
  // Each leaf of `children` is a movable descriptor created by a call to
237
245
  // some `GraphieMovable`, such as `MovablePoint`.
@@ -4,39 +4,52 @@
4
4
 
5
5
  import _ from "underscore";
6
6
 
7
- import Movable from "../movable";
7
+ import {Graphie} from "../../util/graphie";
8
+ import {Movable} from "../movable";
9
+
10
+ import type {Coord} from "@khanacademy/perseus";
8
11
 
9
12
  const createMock = function (): any {
10
- const movable = new Movable(null, {
11
- mouseTarget: null,
12
- });
13
- movable.modify = function (options: any) {
14
- Movable.prototype.modify.call(movable, _.omit(options, "mouseTarget"));
15
- };
16
- _.each(
17
- ["onMoveStart", "onMove", "onMoveEnd", "onClick"],
18
- function (eventName) {
19
- movable[eventName] = function (coord: any, prevCoord) {
20
- this._fireEvent(this.state[eventName], coord, prevCoord);
21
- };
22
- },
23
- );
24
- movable.move = function (...args) {
25
- const startPoint = _.first(args);
13
+ return new MovableMock(new Graphie(document.createElement("div")), {});
14
+ };
15
+
16
+ class MovableMock extends Movable<Record<string, never>> {
17
+ modify(options: any) {
18
+ // TODO(benchristel): what is mouseTarget? It seems to be unused.
19
+ super.modify(_.omit(options, "mouseTarget"));
20
+ }
21
+
22
+ move(...args) {
23
+ const [startPoint, ...rest] = args;
26
24
  // TODO(jack): Move these into onMoveStart, onMove, and onMoveEnd
27
- movable.state.isMouseOver = true;
28
- movable.state.isHovering = true;
29
- movable.onMoveStart(startPoint, startPoint);
30
- _.each(_.rest(args), function (point, i) {
31
- movable.state.isDragging = true;
32
- movable.onMove(point, args[i]);
25
+ this.state.isMouseOver = true;
26
+ this.state.isHovering = true;
27
+ this.onMoveStart(startPoint, startPoint);
28
+ rest.forEach((point, i) => {
29
+ this.state.isDragging = true;
30
+ this.onMove(point, args[i]);
33
31
  });
34
- movable.onMoveEnd(_.last(args), startPoint);
35
- movable.state.dragging = false;
36
- movable.state.isMouseOver = false;
37
- movable.state.isHovering = false;
38
- };
39
- return movable;
40
- };
32
+ this.onMoveEnd(_.last(args), startPoint);
33
+ this.state.isDragging = false;
34
+ this.state.isMouseOver = false;
35
+ this.state.isHovering = false;
36
+ }
37
+
38
+ onMoveStart(coord: Coord, prevCoord: Coord) {
39
+ this._fireEvent(this.state.onMoveStart, coord, prevCoord);
40
+ }
41
+
42
+ onMove(coord: Coord, prevCoord: Coord) {
43
+ this._fireEvent(this.state.onMove, coord, prevCoord);
44
+ }
45
+
46
+ onMoveEnd(coord: Coord, prevCoord: Coord) {
47
+ this._fireEvent(this.state.onMoveEnd, coord, prevCoord);
48
+ }
49
+
50
+ onClick(coord: Coord, prevCoord: Coord) {
51
+ this._fireEvent(this.state.onClick, coord, prevCoord);
52
+ }
53
+ }
41
54
 
42
55
  export default createMock;
@@ -0,0 +1,93 @@
1
+ import InteractiveUtil from "./interactive-util";
2
+
3
+ describe("normalizeOptions", () => {
4
+ it("wraps single functions in arrays", () => {
5
+ const options = {
6
+ add: () => {},
7
+ modify: () => {},
8
+ draw: () => {},
9
+ remove: () => {},
10
+ constraints: () => {},
11
+ onMoveStart: () => {},
12
+ onMove: () => {},
13
+ onMoveEnd: () => {},
14
+ onClick: () => {},
15
+ };
16
+ expect(InteractiveUtil.normalizeOptions(options)).toEqual({
17
+ add: [options.add],
18
+ modify: [options.modify],
19
+ draw: [options.draw],
20
+ remove: [options.remove],
21
+ constraints: [options.constraints],
22
+ onMoveStart: [options.onMoveStart],
23
+ onMove: [options.onMove],
24
+ onMoveEnd: [options.onMoveEnd],
25
+ onClick: [options.onClick],
26
+ });
27
+ });
28
+
29
+ it("tolerates missing properties", () => {
30
+ const options = {
31
+ add: () => {},
32
+ };
33
+ expect(InteractiveUtil.normalizeOptions(options)).toEqual({
34
+ add: [options.add],
35
+ });
36
+ });
37
+
38
+ it("ignores extra properties", () => {
39
+ const options = {
40
+ foo: () => {},
41
+ };
42
+ expect(InteractiveUtil.normalizeOptions(options)).toEqual({
43
+ foo: options.foo,
44
+ });
45
+ });
46
+
47
+ it("does not modify its argument", () => {
48
+ const options = {
49
+ add: () => {},
50
+ };
51
+ const optionsCopy = {...options};
52
+ InteractiveUtil.normalizeOptions(options);
53
+ expect(options).toEqual(optionsCopy);
54
+ });
55
+
56
+ it("leaves existing arrays of functions alone", () => {
57
+ const options = {
58
+ add: [() => {}],
59
+ modify: [() => {}],
60
+ draw: [() => {}],
61
+ remove: [() => {}],
62
+ onMoveStart: [() => {}],
63
+ onMove: [() => {}],
64
+ onMoveEnd: [() => {}],
65
+ onClick: [() => {}],
66
+ };
67
+ expect(InteractiveUtil.normalizeOptions(options)).toEqual(options);
68
+ });
69
+
70
+ it("flattens arrays", () => {
71
+ const dummyFunction = () => {};
72
+ const options = {
73
+ add: [[dummyFunction]],
74
+ modify: [[dummyFunction]],
75
+ draw: [[dummyFunction]],
76
+ remove: [[dummyFunction]],
77
+ onMoveStart: [[dummyFunction]],
78
+ onMove: [[dummyFunction]],
79
+ onMoveEnd: [[dummyFunction]],
80
+ onClick: [[dummyFunction]],
81
+ };
82
+ expect(InteractiveUtil.normalizeOptions(options)).toEqual({
83
+ add: [dummyFunction],
84
+ modify: [dummyFunction],
85
+ draw: [dummyFunction],
86
+ remove: [dummyFunction],
87
+ onMoveStart: [dummyFunction],
88
+ onMove: [dummyFunction],
89
+ onMoveEnd: [dummyFunction],
90
+ onClick: [dummyFunction],
91
+ });
92
+ });
93
+ });
@@ -52,6 +52,18 @@ function computeCanUse3dTransform(): boolean {
52
52
  return !!el.style[prefix];
53
53
  }
54
54
 
55
+ const FUNCTION_ARRAY_OPTIONS = [
56
+ "add",
57
+ "modify",
58
+ "draw",
59
+ "remove",
60
+ "constraints",
61
+ "onMoveStart",
62
+ "onMove",
63
+ "onMoveEnd",
64
+ "onClick",
65
+ ];
66
+
55
67
  const InteractiveUtil = {
56
68
  assert: function (isTrue: boolean, message?: string) {
57
69
  if (!isTrue) {
@@ -106,24 +118,16 @@ const InteractiveUtil = {
106
118
  /**
107
119
  * Convert all function-or-array arguments to arrays of functions
108
120
  */
109
- normalizeOptions: function (
110
- arrayOptionNames: ReadonlyArray<string>,
111
- options: Record<any, any>,
112
- ): Record<any, any> {
113
- // TODO(jack): Having to clone here is annoying; this
114
- // function should really just modify this.state in place
115
- // (and maybe be a function on MovableHelperMethods to get access
116
- // to this.state), which would also be nicer because we could
117
- // normalizeOptions once in this.modify
118
- const result = _.clone(options);
119
- _.each(arrayOptionNames, function (eventName) {
120
- const funcOrArray = options[eventName];
121
+ normalizeOptions: function (options: Record<any, any>): Record<any, any> {
122
+ const result = {...options};
123
+ FUNCTION_ARRAY_OPTIONS.forEach(function (eventName) {
121
124
  // Only propagate values which were set; not present values
122
125
  // shouldn't be added to options because we'd like them to
123
126
  // fall through to defaults
124
- if (funcOrArray !== undefined) {
125
- const funcArray = InteractiveUtil.arrayify(funcOrArray);
126
- result[eventName] = funcArray;
127
+ if (options[eventName] !== undefined) {
128
+ result[eventName] = InteractiveUtil.arrayify(
129
+ options[eventName],
130
+ );
127
131
  }
128
132
  });
129
133
  return result;
@@ -27,8 +27,13 @@ const MovableHelperMethods: any = {
27
27
  /**
28
28
  * Fire an onSomething type event to all functions in listeners
29
29
  */
30
- _fireEvent: function (listeners, currentValue, previousValue) {
31
- _.invoke(listeners, "call", this, currentValue, previousValue);
30
+ _fireEvent: function <F extends (...args: any[]) => any>(
31
+ listeners: F[],
32
+ ...args: Parameters<F>
33
+ ) {
34
+ for (const listener of listeners) {
35
+ listener.call(this, ...args);
36
+ }
32
37
  },
33
38
 
34
39
  /**
@@ -14,16 +14,6 @@ import WrappedLine from "./wrapped-line";
14
14
  const assert = InteractiveUtil.assert;
15
15
  const normalizeOptions = InteractiveUtil.normalizeOptions;
16
16
 
17
- const FUNCTION_ARRAY_OPTIONS = [
18
- "add",
19
- "draw",
20
- "remove",
21
- "onMoveStart",
22
- "constraints",
23
- "onMove",
24
- "onMoveEnd",
25
- ];
26
-
27
17
  // Default "props" and "state". Both are added to this.state and
28
18
  // receive magic getter methods (this.cursor() etc).
29
19
  // However, properties in DEFAULT_PROPS are updated on `modify()`,
@@ -87,7 +77,6 @@ _.extend(MovableLine.prototype, {
87
77
  id: this.state.id,
88
78
  },
89
79
  normalizeOptions(
90
- FUNCTION_ARRAY_OPTIONS,
91
80
  // Defaults are copied from MovableLineOptions.*.standard
92
81
  // These defaults are set here instead of DEFAULT_PROPS/STATE
93
82
  // because they:
@@ -121,7 +110,7 @@ _.extend(MovableLine.prototype, {
121
110
  const graphie = this.graphie;
122
111
  const state = (self.state = _.extend(
123
112
  self.state,
124
- normalizeOptions(FUNCTION_ARRAY_OPTIONS, options),
113
+ normalizeOptions(options),
125
114
  ));
126
115
 
127
116
  // Default things inside the state.normalStyle object, because
@@ -7,7 +7,8 @@ import _ from "underscore";
7
7
 
8
8
  import WrappedEllipse from "./wrapped-ellipse";
9
9
 
10
- import type {Coord} from "./types";
10
+ import type {Constraint, ConstraintCallbacks, Coord} from "./types";
11
+ import type {Interval} from "../util/interval";
11
12
 
12
13
  const add = {
13
14
  constrain: function () {
@@ -119,51 +120,62 @@ const remove = {
119
120
  remove.standard = remove.basic;
120
121
 
121
122
  const constraints = {
122
- fixed: function (): () => boolean {
123
+ fixed: function (): Constraint {
123
124
  return function () {
124
125
  return false;
125
126
  };
126
127
  },
127
128
 
128
- snap: function (snap: any): (arg1: Coord) => unknown {
129
- return function (coord: Coord): unknown {
129
+ snap: function (snap?: number | null): Constraint {
130
+ return function (coord: Coord) {
130
131
  if (snap === null) {
131
- // NOTE(kevinb): this should probably return the original point
132
+ // TODO(benchristel), NOTE(kevinb): this should probably return
133
+ // the original point
132
134
  return true;
133
135
  }
134
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
135
- snap = snap || this.graphie.snap;
136
- return kpoint.roundTo(coord, snap);
136
+ // @ts-expect-error - TS2339: Property 'snap' does not exist on type 'Graphie'.
137
+ return kpoint.roundTo(coord, snap || this.graphie.snap);
137
138
  };
138
139
  },
139
140
 
140
141
  bound: function (
141
- range: any,
142
- snap: any,
143
- paddingPx: number,
144
- ): (arg1: Coord, arg2: Coord, arg3: any) => Coord {
145
- if (paddingPx === undefined) {
146
- if (range === undefined) {
147
- paddingPx = 10;
142
+ range?: [Interval, Interval],
143
+ snap?: Coord | number,
144
+ optionalPaddingPx?: number,
145
+ ): Constraint {
146
+ const paddingPx = (() => {
147
+ if (optionalPaddingPx === undefined) {
148
+ if (range === undefined) {
149
+ return 10;
150
+ } else {
151
+ return 0;
152
+ }
148
153
  } else {
149
- paddingPx = 0;
154
+ return optionalPaddingPx;
150
155
  }
151
- }
152
- return function (coord: Coord, prev, options: any) {
153
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
156
+ })();
157
+
158
+ return function (
159
+ coord: Coord,
160
+ prev: Coord,
161
+ options: ConstraintCallbacks,
162
+ ) {
154
163
  const graphie = this.graphie;
155
164
  range = range || graphie.range;
156
165
 
157
166
  if (snap === undefined) {
167
+ // @ts-expect-error - Property 'snap' does not exist on type 'Graphie'.
158
168
  snap = graphie.snap;
159
169
  }
160
170
 
161
171
  let lower = graphie.unscalePoint([
162
172
  paddingPx,
173
+ // @ts-expect-error - 'graphie.ypixels' is possibly 'undefined'.
163
174
  graphie.ypixels - paddingPx,
164
175
  ]);
165
176
 
166
177
  let upper = graphie.unscalePoint([
178
+ // @ts-expect-error - 'graphie.ypixels' is possibly 'undefined'.
167
179
  graphie.xpixels - paddingPx,
168
180
  paddingPx,
169
181
  ]);
@@ -69,10 +69,6 @@ const normalizeOptions = InteractiveUtil.normalizeOptions;
69
69
 
70
70
  const {processMath} = Tex;
71
71
 
72
- // State parameters that should be converted into an array of
73
- // functions
74
- const FUNCTION_ARRAY_OPTIONS = _.keys(MovablePointOptions);
75
-
76
72
  // Default "props" and "state". Both are added to this.state and
77
73
  // receive magic getter methods (this.coord() etc).
78
74
  // However, properties in DEFAULT_PROPS are updated on `modify()`,
@@ -134,7 +130,6 @@ _.extend(MovablePoint.prototype, {
134
130
  id: this.state.id,
135
131
  },
136
132
  normalizeOptions(
137
- FUNCTION_ARRAY_OPTIONS,
138
133
  // Defaults are copied from MovablePointOptions.*.standard
139
134
  // These defaults are set here instead of DEFAULT_PROPS/STATE
140
135
  // because they:
@@ -216,10 +211,7 @@ _.extend(MovablePoint.prototype, {
216
211
  update: function (options) {
217
212
  const self = this;
218
213
  const graphie = self.graphie;
219
- const state = _.extend(
220
- self.state,
221
- normalizeOptions(FUNCTION_ARRAY_OPTIONS, options),
222
- );
214
+ const state = _.extend(self.state, normalizeOptions(options));
223
215
 
224
216
  assert(kpoint.is(state.coord));
225
217