@khanacademy/perseus 81.0.0 → 81.0.2

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.
@@ -1,4 +1,4 @@
1
- import type image from "../../widgets/image/image.class";
1
+ import type image from "../../widgets/image/image";
2
2
  import type React from "react";
3
3
  /**
4
4
  * JSON describing an image widget. Intended for consumption by AI tools.
@@ -1,3 +1,67 @@
1
- import * as React from "react";
2
- import type { ImageWidgetProps } from "./image.class";
3
- export declare const ImageComponent: (props: ImageWidgetProps) => React.JSX.Element | null;
1
+ import React from "react";
2
+ import type { Widget } from "../../types";
3
+ import type { PerseusImageWidgetOptions } from "@khanacademy/perseus-core";
4
+ type WidgetHandle = Pick<Widget, "getPromptJSON">;
5
+ declare const _default: {
6
+ name: string;
7
+ displayName: string;
8
+ widget: React.ForwardRefExoticComponent<PerseusImageWidgetOptions & {
9
+ trackInteraction: (extraData?: Empty | undefined) => void;
10
+ widgetId: string;
11
+ widgetIndex: number;
12
+ alignment: string | null | undefined;
13
+ static: boolean | null | undefined;
14
+ graded?: boolean | null;
15
+ problemNum: number | null | undefined;
16
+ apiOptions: Readonly<Readonly<{
17
+ isArticle?: boolean;
18
+ onFocusChange?: (newFocusPath: import("../..").FocusPath, oldFocusPath: import("../..").FocusPath, keypadHeight?: number, focusedElement?: HTMLElement) => unknown;
19
+ showAlignmentOptions?: boolean;
20
+ readOnly?: boolean;
21
+ editingDisabled?: boolean;
22
+ answerableCallback?: (arg1: boolean) => unknown;
23
+ getAnotherHint?: () => unknown;
24
+ interactionCallback?: (widgetData: {
25
+ [widgetId: string]: any;
26
+ }) => void;
27
+ imagePlaceholder?: React.ReactNode;
28
+ widgetPlaceholder?: React.ReactNode;
29
+ baseElements?: {
30
+ Link: React.ComponentType<any>;
31
+ };
32
+ trackInteraction?: (args: {
33
+ type: string;
34
+ id: string;
35
+ correct?: boolean;
36
+ } & Partial<import("../../types").TrackingGradedGroupExtraArguments> & Partial<{
37
+ visible: number;
38
+ }>) => void;
39
+ customKeypad?: boolean;
40
+ isMobile?: boolean;
41
+ isMobileApp?: boolean;
42
+ hintProgressColor?: string;
43
+ flags?: Record<"perseus-renderer-upgrade" | "desmos-calculator" | "perseus-enable-point-label-field", boolean>;
44
+ }> & {
45
+ baseElements: NonNullable<import("../..").APIOptions["baseElements"]>;
46
+ isArticle: NonNullable<import("../..").APIOptions["isArticle"]>;
47
+ isMobile: NonNullable<import("../..").APIOptions["isMobile"]>;
48
+ isMobileApp: NonNullable<import("../..").APIOptions["isMobileApp"]>;
49
+ editingDisabled: NonNullable<import("../..").APIOptions["editingDisabled"]>;
50
+ onFocusChange: NonNullable<import("../..").APIOptions["onFocusChange"]>;
51
+ readOnly: NonNullable<import("../..").APIOptions["readOnly"]>;
52
+ showAlignmentOptions: NonNullable<import("../..").APIOptions["showAlignmentOptions"]>;
53
+ }>;
54
+ keypadElement?: any;
55
+ onFocus: (blurPath: import("../..").FocusPath) => void;
56
+ onBlur: (blurPath: import("../..").FocusPath) => void;
57
+ findWidgets: import("../../types").FindWidgetsFunction;
58
+ reviewMode: boolean;
59
+ showSolutions?: import("@khanacademy/perseus-core").ShowSolutions;
60
+ handleUserInput: (newUserInput: Empty, cb?: () => void, silent?: boolean) => void;
61
+ userInput: Empty;
62
+ linterContext: import("@khanacademy/perseus-linter").LinterContextProps;
63
+ containerSizeClass: import("../../util/sizing-utils").SizeClass;
64
+ } & React.RefAttributes<WidgetHandle>>;
65
+ isLintable: true;
66
+ };
67
+ export default _default;
@@ -1 +1 @@
1
- export { default } from "./image.class";
1
+ export { default } from "./image";
@@ -0,0 +1,16 @@
1
+ import * as React from "react";
2
+ /**
3
+ * DOM node of the HTML overlay above the graph's SVG. Movable points portal an
4
+ * HTML hitbox `<div>` (`touch-action: none`) into it as their drag target.
5
+ *
6
+ * Why HTML, not an SVG hitbox: Safari ignores `touch-action` on SVG, so under
7
+ * the container's `touch-action: pan-y` a vertical point-drag scrolls the page.
8
+ * `touch-action` on real HTML is honored everywhere, so no scroll and no JS
9
+ * fallback. The layer is `pointer-events: none` (empty area falls through to
10
+ * scroll / add-point); each hitbox opts back in. Null until the layer mounts.
11
+ *
12
+ * UPSTREAM (Mafs): this technique + relaxing `.MafsView { touch-action: none }`
13
+ * in `mafs/core.css` is the fix to contribute to Mafs' `MovablePoint`. Perseus
14
+ * forked these components, so keep them in sync if that PR lands.
15
+ */
16
+ export declare const HitboxLayerContext: React.Context<HTMLElement | null>;
@@ -0,0 +1,53 @@
1
+ import * as React from "react";
2
+ import type { vec } from "mafs";
3
+ /**
4
+ * Touch/click target size for small handles (movable points, arrowheads, the
5
+ * asymptote pill). Preserved from the legacy interactive graph (48×48px) and a
6
+ * comfortable target per WCAG 2.5.8.
7
+ */
8
+ export declare const HANDLE_HITBOX_SIZE_PX = 48;
9
+ /**
10
+ * Drag hitbox for an interactive-graph element, rendered as a real HTML element
11
+ * in the graph's overlay layer rather than as SVG. Safari doesn't reliably
12
+ * honor `touch-action` on SVG, so under the container's `touch-action: pan-y`
13
+ * an SVG hitbox lets a drag scroll the page; a real HTML element's
14
+ * `touch-action: none` is honored everywhere. See {@link HitboxLayerContext}.
15
+ *
16
+ * All coordinates are **graph-space**; this hook converts them to the overlay's
17
+ * pixel space with the same `pointToPixel` transform movable points use, so
18
+ * hitboxes line up exactly with the SVG shapes they cover.
19
+ */
20
+ type HitboxShape = {
21
+ kind: "box";
22
+ center: vec.Vector2;
23
+ sizePx: number;
24
+ } | {
25
+ kind: "line";
26
+ start: vec.Vector2;
27
+ end: vec.Vector2;
28
+ thicknessPx: number;
29
+ } | {
30
+ kind: "ellipse";
31
+ center: vec.Vector2;
32
+ radius: vec.Vector2;
33
+ } | {
34
+ kind: "polygon";
35
+ vertices: ReadonlyArray<vec.Vector2>;
36
+ };
37
+ type HitboxParams = {
38
+ shape: HitboxShape;
39
+ /** Assigned to the hitbox element; use as the `useDraggable` gestureTarget. */
40
+ hitboxRef: React.RefObject<HTMLDivElement>;
41
+ /**
42
+ * Stacking within the overlay. Point/vertex handles ("handle") sit above
43
+ * shape bodies ("body") so a vertex on a segment/polygon stays grabbable.
44
+ */
45
+ layer?: "handle" | "body";
46
+ cursor?: string;
47
+ dragging?: boolean;
48
+ onClick?: () => void;
49
+ onHoverChange?: (hovered: boolean) => void;
50
+ testId?: string;
51
+ };
52
+ export declare function useHitbox(params: HitboxParams): React.ReactNode;
53
+ export {};
@@ -6,6 +6,7 @@ type Props = {
6
6
  angle: number;
7
7
  dragging: boolean;
8
8
  focused: boolean;
9
+ hovered?: boolean;
9
10
  showFocusRing: boolean;
10
11
  cursor?: CSSCursor | undefined;
11
12
  onClick?: () => unknown;
@@ -5,6 +5,7 @@ type Props = {
5
5
  point: vec.Vector2;
6
6
  dragging: boolean;
7
7
  focused: boolean;
8
+ hovered?: boolean;
8
9
  showFocusRing: boolean;
9
10
  cursor?: CSSCursor | undefined;
10
11
  onClick?: () => unknown;
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": "81.0.0",
6
+ "version": "81.0.2",
7
7
  "publishConfig": {
8
8
  "access": "public"
9
9
  },
@@ -43,39 +43,39 @@
43
43
  "tiny-invariant": "1.3.1",
44
44
  "uuid": "^10.0.0",
45
45
  "@khanacademy/kas": "2.2.3",
46
- "@khanacademy/keypad-context": "3.2.74",
47
- "@khanacademy/kmath": "2.4.33",
48
- "@khanacademy/math-input": "26.6.2",
49
- "@khanacademy/perseus-core": "32.0.0",
50
- "@khanacademy/perseus-linter": "5.1.14",
51
- "@khanacademy/perseus-score": "8.12.6",
46
+ "@khanacademy/keypad-context": "3.2.75",
47
+ "@khanacademy/kmath": "2.4.34",
48
+ "@khanacademy/math-input": "26.6.4",
49
+ "@khanacademy/perseus-core": "32.0.1",
50
+ "@khanacademy/perseus-linter": "5.1.15",
51
+ "@khanacademy/perseus-score": "8.12.7",
52
52
  "@khanacademy/perseus-utils": "2.1.5",
53
- "@khanacademy/pure-markdown": "2.3.0",
54
- "@khanacademy/simple-markdown": "2.2.3"
53
+ "@khanacademy/pure-markdown": "2.3.1",
54
+ "@khanacademy/simple-markdown": "3.0.0"
55
55
  },
56
56
  "devDependencies": {
57
57
  "@khanacademy/wonder-blocks-announcer": "1.1.1",
58
- "@khanacademy/wonder-blocks-banner": "5.1.9",
59
- "@khanacademy/wonder-blocks-button": "11.7.5",
60
- "@khanacademy/wonder-blocks-clickable": "8.2.7",
58
+ "@khanacademy/wonder-blocks-banner": "5.1.10",
59
+ "@khanacademy/wonder-blocks-button": "11.7.6",
60
+ "@khanacademy/wonder-blocks-clickable": "8.2.8",
61
61
  "@khanacademy/wonder-blocks-core": "12.4.4",
62
62
  "@khanacademy/wonder-blocks-data": "15.0.2",
63
- "@khanacademy/wonder-blocks-dropdown": "10.11.6",
64
- "@khanacademy/wonder-blocks-form": "7.6.8",
65
- "@khanacademy/wonder-blocks-icon": "5.3.21",
66
- "@khanacademy/wonder-blocks-icon-button": "11.4.3",
67
- "@khanacademy/wonder-blocks-labeled-field": "4.1.7",
68
- "@khanacademy/wonder-blocks-layout": "3.1.58",
69
- "@khanacademy/wonder-blocks-link": "10.3.7",
70
- "@khanacademy/wonder-blocks-modal": "8.7.9",
71
- "@khanacademy/wonder-blocks-pill": "3.1.71",
72
- "@khanacademy/wonder-blocks-popover": "6.3.9",
73
- "@khanacademy/wonder-blocks-progress-spinner": "3.1.58",
74
- "@khanacademy/wonder-blocks-switch": "3.4.7",
63
+ "@khanacademy/wonder-blocks-dropdown": "10.11.7",
64
+ "@khanacademy/wonder-blocks-form": "7.6.9",
65
+ "@khanacademy/wonder-blocks-icon": "5.3.22",
66
+ "@khanacademy/wonder-blocks-icon-button": "11.4.4",
67
+ "@khanacademy/wonder-blocks-labeled-field": "4.1.8",
68
+ "@khanacademy/wonder-blocks-layout": "3.1.59",
69
+ "@khanacademy/wonder-blocks-link": "10.3.8",
70
+ "@khanacademy/wonder-blocks-modal": "8.7.10",
71
+ "@khanacademy/wonder-blocks-pill": "3.1.72",
72
+ "@khanacademy/wonder-blocks-popover": "6.3.11",
73
+ "@khanacademy/wonder-blocks-progress-spinner": "3.1.59",
74
+ "@khanacademy/wonder-blocks-switch": "3.4.8",
75
75
  "@khanacademy/wonder-blocks-timing": "7.1.0",
76
- "@khanacademy/wonder-blocks-tokens": "17.1.0",
77
- "@khanacademy/wonder-blocks-tooltip": "4.1.84",
78
- "@khanacademy/wonder-blocks-typography": "5.0.1",
76
+ "@khanacademy/wonder-blocks-tokens": "17.2.0",
77
+ "@khanacademy/wonder-blocks-tooltip": "4.2.0",
78
+ "@khanacademy/wonder-blocks-typography": "5.0.2",
79
79
  "@khanacademy/wonder-stuff-core": "3.0.0",
80
80
  "@phosphor-icons/core": "2.0.2",
81
81
  "@popperjs/core": "2.10.2",
@@ -92,27 +92,27 @@
92
92
  },
93
93
  "peerDependencies": {
94
94
  "@khanacademy/wonder-blocks-announcer": "^1.1.1",
95
- "@khanacademy/wonder-blocks-banner": "^5.1.9",
96
- "@khanacademy/wonder-blocks-button": "^11.7.5",
97
- "@khanacademy/wonder-blocks-clickable": "^8.2.7",
95
+ "@khanacademy/wonder-blocks-banner": "^5.1.10",
96
+ "@khanacademy/wonder-blocks-button": "^11.7.6",
97
+ "@khanacademy/wonder-blocks-clickable": "^8.2.8",
98
98
  "@khanacademy/wonder-blocks-core": "^12.4.4",
99
99
  "@khanacademy/wonder-blocks-data": "^15.0.2",
100
- "@khanacademy/wonder-blocks-dropdown": "^10.11.6",
101
- "@khanacademy/wonder-blocks-form": "^7.6.8",
102
- "@khanacademy/wonder-blocks-icon": "^5.3.21",
103
- "@khanacademy/wonder-blocks-icon-button": "^11.4.3",
104
- "@khanacademy/wonder-blocks-labeled-field": "^4.1.7",
105
- "@khanacademy/wonder-blocks-layout": "^3.1.58",
106
- "@khanacademy/wonder-blocks-link": "^10.3.7",
107
- "@khanacademy/wonder-blocks-modal": "^8.7.9",
108
- "@khanacademy/wonder-blocks-pill": "^3.1.71",
109
- "@khanacademy/wonder-blocks-popover": "^6.3.9",
110
- "@khanacademy/wonder-blocks-progress-spinner": "^3.1.58",
111
- "@khanacademy/wonder-blocks-switch": "^3.4.7",
100
+ "@khanacademy/wonder-blocks-dropdown": "^10.11.7",
101
+ "@khanacademy/wonder-blocks-form": "^7.6.9",
102
+ "@khanacademy/wonder-blocks-icon": "^5.3.22",
103
+ "@khanacademy/wonder-blocks-icon-button": "^11.4.4",
104
+ "@khanacademy/wonder-blocks-labeled-field": "^4.1.8",
105
+ "@khanacademy/wonder-blocks-layout": "^3.1.59",
106
+ "@khanacademy/wonder-blocks-link": "^10.3.8",
107
+ "@khanacademy/wonder-blocks-modal": "^8.7.10",
108
+ "@khanacademy/wonder-blocks-pill": "^3.1.72",
109
+ "@khanacademy/wonder-blocks-popover": "^6.3.11",
110
+ "@khanacademy/wonder-blocks-progress-spinner": "^3.1.59",
111
+ "@khanacademy/wonder-blocks-switch": "^3.4.8",
112
112
  "@khanacademy/wonder-blocks-timing": "^7.1.0",
113
- "@khanacademy/wonder-blocks-tokens": "^17.1.0",
114
- "@khanacademy/wonder-blocks-tooltip": "^4.1.84",
115
- "@khanacademy/wonder-blocks-typography": "^5.0.1",
113
+ "@khanacademy/wonder-blocks-tokens": "^17.2.0",
114
+ "@khanacademy/wonder-blocks-tooltip": "^4.2.0",
115
+ "@khanacademy/wonder-blocks-typography": "^5.0.2",
116
116
  "@khanacademy/wonder-stuff-core": "^3.0.0",
117
117
  "@phosphor-icons/core": "^2.0.2",
118
118
  "@popperjs/core": "^2.10.2",
@@ -127,7 +127,7 @@
127
127
  },
128
128
  "keywords": [],
129
129
  "khan": {
130
- "catalogHash": "1680f43b40dfea22"
130
+ "catalogHash": "ad217163f04bf0b2"
131
131
  },
132
132
  "scripts": {}
133
133
  }
@@ -1,20 +0,0 @@
1
- import * as React from "react";
2
- import { PerseusI18nContext } from "../../components/i18n-context";
3
- import type { WidgetProps, Widget } from "../../types";
4
- import type { ImagePromptJSON } from "../../widget-ai-utils/image/image-ai-utils";
5
- import type { PerseusImageWidgetOptions } from "@khanacademy/perseus-core";
6
- export type ImageWidgetProps = WidgetProps<PerseusImageWidgetOptions>;
7
- declare class ImageWidget extends React.Component<ImageWidgetProps> implements Widget {
8
- static contextType: React.Context<import("../../components/i18n-context").I18nContextType>;
9
- context: React.ContextType<typeof PerseusI18nContext>;
10
- isWidget: true;
11
- getPromptJSON(): ImagePromptJSON;
12
- render(): React.ReactNode;
13
- }
14
- declare const _default: {
15
- name: string;
16
- displayName: string;
17
- widget: typeof ImageWidget;
18
- isLintable: true;
19
- };
20
- export default _default;