@khanacademy/perseus 87.0.0 → 87.1.0
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.
- package/dist/components/drag-and-drop/answer-tile/answer-tile.d.ts +88 -0
- package/dist/components/drag-and-drop/answer-tile/index.d.ts +2 -0
- package/dist/components/drag-and-drop/blank/blank-component.d.ts +41 -0
- package/dist/components/drag-and-drop/blank/index.d.ts +2 -0
- package/dist/components/drag-and-drop/choice-bank.d.ts +10 -3
- package/dist/components/drag-and-drop/dnd-action-menu/dnd-action-menu.d.ts +39 -0
- package/dist/components/drag-and-drop/dnd-action-menu/index.d.ts +2 -0
- package/dist/components/drag-and-drop/dnd-action-menu/merged-ref-opener.d.ts +21 -0
- package/dist/components/drag-and-drop/drag-ids.d.ts +23 -0
- package/dist/components/drag-and-drop/perseus-dnd-provider.d.ts +34 -0
- package/dist/components/drag-and-drop/temp-strings.d.ts +36 -0
- package/dist/components/drag-and-drop/tile-placements.d.ts +33 -0
- package/dist/components/drag-and-drop/use-tile-move-actions.d.ts +28 -0
- package/dist/es/index.css +1 -1
- package/dist/es/index.css.map +1 -1
- package/dist/es/index.js +16 -11
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +1 -1
- package/dist/index.css.map +1 -1
- package/dist/index.js +18 -13
- package/dist/index.js.map +1 -1
- package/dist/widgets/categorizer/categorizer.d.ts +3 -5
- package/dist/widgets/dropdown/dropdown.d.ts +3 -4
- package/dist/widgets/graded-group-set/graded-group-set.d.ts +2 -3
- package/dist/widgets/matcher/matcher.d.ts +3 -4
- package/dist/widgets/number-line/number-line.d.ts +6 -4
- package/dist/widgets/python-program/python-program.d.ts +3 -4
- package/package.json +31 -30
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { MoveTarget } from "../dnd-action-menu";
|
|
3
|
+
/** What a scored tile shows. Each value names a CSS class in the module. */
|
|
4
|
+
export type TileScoring = "correct" | "incorrect" | "unused";
|
|
5
|
+
export interface AnswerTileProps {
|
|
6
|
+
/** The tile that moves, named as the widget's placements name it. */
|
|
7
|
+
tileId: string;
|
|
8
|
+
/**
|
|
9
|
+
* The blank this tile sits in. Omit for a tile in the choice bank.
|
|
10
|
+
* The tile reports both ids to dnd-kit, so a drop knows what moved
|
|
11
|
+
* and where it came from.
|
|
12
|
+
*/
|
|
13
|
+
fromBlankId?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Perseus markdown for the tile face: text, TeX, or an image.
|
|
16
|
+
* Use "" for an empty tile.
|
|
17
|
+
*/
|
|
18
|
+
content: string;
|
|
19
|
+
/**
|
|
20
|
+
* Labels the tile's menu button for screen readers, as translated
|
|
21
|
+
* plain text.
|
|
22
|
+
*/
|
|
23
|
+
label: string;
|
|
24
|
+
/**
|
|
25
|
+
* The result the tile shows once the question is scored. A placed tile
|
|
26
|
+
* is "correct" or "incorrect"; a tile the learner left in the choice
|
|
27
|
+
* bank is "unused", which dims it. Omit it before scoring.
|
|
28
|
+
*
|
|
29
|
+
* A scored tile has no menu and cannot be dragged, whichever result
|
|
30
|
+
* it shows.
|
|
31
|
+
*/
|
|
32
|
+
scoring?: TileScoring;
|
|
33
|
+
/**
|
|
34
|
+
* Blanks the tile can move to, which populate the menu.
|
|
35
|
+
* An empty array is valid: a placed tile in a one-blank exercise
|
|
36
|
+
* can only be cleared, due to no other legitimate moveTargets.
|
|
37
|
+
*/
|
|
38
|
+
moveTargets: ReadonlyArray<MoveTarget>;
|
|
39
|
+
/** Called with the target blank's id when a move action is selected. */
|
|
40
|
+
onMove: (targetId: string) => void;
|
|
41
|
+
/**
|
|
42
|
+
* Label of the blank/column the tile currently sits in, which is used
|
|
43
|
+
* as part of the aria-label for the "Clear" button in the Action Menu.
|
|
44
|
+
*/
|
|
45
|
+
clearFromLabel?: string;
|
|
46
|
+
/** Callback for removing the tile from its blank. */
|
|
47
|
+
onClear?: () => void;
|
|
48
|
+
/** Remaining uses for a multi-use tile, spoken by the menu opener. */
|
|
49
|
+
remainingUses?: number;
|
|
50
|
+
/**
|
|
51
|
+
* This ref points to the menu's opener button. The parent uses it to
|
|
52
|
+
* move focus after a tile moves.
|
|
53
|
+
*/
|
|
54
|
+
menuRef?: React.Ref<HTMLButtonElement>;
|
|
55
|
+
/**
|
|
56
|
+
* Set true for a tile whose menu stays hidden until hover or focus.
|
|
57
|
+
* Fill in the Blank uses this for a tile in an inline blank, to keep
|
|
58
|
+
* the line of text short at rest.
|
|
59
|
+
*/
|
|
60
|
+
hidesMenuAtRest?: boolean;
|
|
61
|
+
/**
|
|
62
|
+
* Set true for a placed tile that stretches to fill its blank, with
|
|
63
|
+
* the content at the start. The menu stays visible. The Sorter uses
|
|
64
|
+
* this for placed tiles.
|
|
65
|
+
*/
|
|
66
|
+
fillsBlank?: boolean;
|
|
67
|
+
/** Called when the tile's content renders (TeX ready, image update). */
|
|
68
|
+
onContentRender?: () => void;
|
|
69
|
+
/**
|
|
70
|
+
* Set true for a tile placed in a sub/superscript blank. The tile
|
|
71
|
+
* rests as a small value chip until hover or focus.
|
|
72
|
+
*/
|
|
73
|
+
compact?: boolean;
|
|
74
|
+
/**
|
|
75
|
+
* Display height in pixels for an image tile's image. The schema
|
|
76
|
+
* types this as a plain number; the editor offers 24-96 in steps
|
|
77
|
+
* of 12.
|
|
78
|
+
*/
|
|
79
|
+
imageHeight?: number;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* AnswerTile is the card that a learner moves into a blank. It is part of
|
|
83
|
+
* the Drag-and-Drop widget family. The tile shows authored markdown
|
|
84
|
+
* content: text, TeX, or an image. It puts the DndActionMenu at its
|
|
85
|
+
* leading edge. The parent widget sets `scoring` once the question is
|
|
86
|
+
* scored.
|
|
87
|
+
*/
|
|
88
|
+
export declare function AnswerTile(props: AnswerTileProps): React.ReactElement;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
/** How a blank renders: full size, or smaller for exponents/indices. */
|
|
3
|
+
export type BlankDisplayType = "normal" | "superscript" | "subscript";
|
|
4
|
+
export interface BlankComponentProps {
|
|
5
|
+
/**
|
|
6
|
+
* Unique identifier of this blank. It doubles as the dnd-kit droppable
|
|
7
|
+
* id and as the target id the widget receives when a tile moves here,
|
|
8
|
+
* so it must be unique within the surrounding PerseusDndProvider.
|
|
9
|
+
* The blank widget passes its renderer-assigned `widgetId`; widgets
|
|
10
|
+
* that use this component directly supply their own ids.
|
|
11
|
+
*/
|
|
12
|
+
blankId: string;
|
|
13
|
+
displayType: BlankDisplayType;
|
|
14
|
+
/** The placed answer tile, when one sits in this blank. */
|
|
15
|
+
children?: React.ReactNode;
|
|
16
|
+
/**
|
|
17
|
+
* Minimum width of the empty slot. The FITB spec sizes an empty blank
|
|
18
|
+
* to the widest answer tile so the slot's size does not reveal the
|
|
19
|
+
* answer; the widget computes that width and passes it here. Defaults
|
|
20
|
+
* to the design's 65px minimum. A placed tile still grows the blank
|
|
21
|
+
* beyond this.
|
|
22
|
+
*/
|
|
23
|
+
minWidth?: number;
|
|
24
|
+
/**
|
|
25
|
+
* Keeps the empty-slot width when a tile is placed, so the line
|
|
26
|
+
* does not reflow. Without it a filled blank hugs its tile.
|
|
27
|
+
*/
|
|
28
|
+
keepsWidthWhenFilled?: boolean;
|
|
29
|
+
/** Extra class for the slot element, for widget-level layout. */
|
|
30
|
+
className?: string;
|
|
31
|
+
testId?: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Blank is the drop slot an answer tile can be placed into. It is part of
|
|
35
|
+
* the Drag-and-Drop widget family. It renders the slot's chrome (dashed
|
|
36
|
+
* border while empty) and registers itself as a dnd-kit drop target.
|
|
37
|
+
*
|
|
38
|
+
* Fill in the Blank reaches it through the `blank` widget, which owns the
|
|
39
|
+
* Perseus widget plumbing; other widgets can render it directly.
|
|
40
|
+
*/
|
|
41
|
+
export declare function BlankComponent(props: BlankComponentProps): React.ReactElement;
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* The bank's droppable id: a tile dropped here returns to the bank.
|
|
4
|
+
* Every widget mounts its own PerseusDndProvider, which scopes drag ids,
|
|
5
|
+
* so one shared value stays unique for two widgets on one page.
|
|
6
|
+
*/
|
|
7
|
+
export declare const CHOICE_BANK_DROP_ID = "choice-bank";
|
|
2
8
|
interface ChoiceBankProps {
|
|
3
9
|
/**
|
|
4
|
-
* The answer tiles to lay out. Each renders as its own `<li
|
|
5
|
-
*
|
|
10
|
+
* The answer tiles to lay out. Each child renders as its own `<li>`,
|
|
11
|
+
* so pass the tiles as siblings and not inside a fragment. Pass
|
|
12
|
+
* nothing for an empty bank (e.g. once every tile has been placed).
|
|
6
13
|
*/
|
|
7
|
-
children
|
|
14
|
+
children?: React.ReactNode;
|
|
8
15
|
/** Visible label; also names the tile list for assistive tech. */
|
|
9
16
|
label: string;
|
|
10
17
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
export type MoveTarget = {
|
|
3
|
+
/** id of the intended blank target */
|
|
4
|
+
id: string;
|
|
5
|
+
/** The blank/column's label, e.g. "Blank 1" or an authored column name. */
|
|
6
|
+
label: string;
|
|
7
|
+
};
|
|
8
|
+
export interface DndActionMenuProps {
|
|
9
|
+
/** The tile's value — labels the button via aria-label. */
|
|
10
|
+
tileLabel: string;
|
|
11
|
+
/**
|
|
12
|
+
* Remaining uses for a multi-use tile. When provided, it is spoken as
|
|
13
|
+
* part of the opener's description ("5 remaining. Actions menu").
|
|
14
|
+
*/
|
|
15
|
+
remainingUses?: number;
|
|
16
|
+
/**
|
|
17
|
+
* Available Blanks/columns this tile can move to.
|
|
18
|
+
*/
|
|
19
|
+
moveTargets: ReadonlyArray<MoveTarget>;
|
|
20
|
+
/** Called with the target blank's id when a move action is selected. */
|
|
21
|
+
onMove: (targetId: string) => void;
|
|
22
|
+
/**
|
|
23
|
+
* The blank's visible label, e.g. "Blank 1", spoken as "Clear from
|
|
24
|
+
* Blank 1". Pass it with onClear for a tile that sits in a blank;
|
|
25
|
+
* omit both for a tile in the choice bank.
|
|
26
|
+
*/
|
|
27
|
+
clearFromLabel?: string;
|
|
28
|
+
/** Called when the Clear action is selected. */
|
|
29
|
+
onClear?: () => void;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* DndActionMenu is the menu that appears on each Answer Tile in our upcoming
|
|
33
|
+
* Drag-and-Drop widget family. It lists every blank the tile can move to and,
|
|
34
|
+
* when the tile is placed in a blank, a Clear action.
|
|
35
|
+
*
|
|
36
|
+
* This is currently purely presentational, and does not include the dnd-kit wiring
|
|
37
|
+
* or move announcements, which will be tackled in future tickets.
|
|
38
|
+
*/
|
|
39
|
+
export declare const DndActionMenu: React.ForwardRefExoticComponent<DndActionMenuProps & React.RefAttributes<HTMLButtonElement>>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Props for the custom Action Menu opener
|
|
4
|
+
*/
|
|
5
|
+
type MergedRefOpenerProps = {
|
|
6
|
+
/** The parent-facing ref, forwarded by DndActionMenu for focus return. */
|
|
7
|
+
openerRef: React.ForwardedRef<HTMLButtonElement>;
|
|
8
|
+
disabled: boolean;
|
|
9
|
+
"aria-label": string;
|
|
10
|
+
"aria-describedby": string;
|
|
11
|
+
className: string;
|
|
12
|
+
children: React.ReactNode;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Two things need a ref to the opener button: ActionMenu (to restore focus
|
|
16
|
+
* when the menu closes) and our parent (to move focus after a tile moves).
|
|
17
|
+
* ActionMenu overwrites any ref we set directly, so this wrapper catches
|
|
18
|
+
* ActionMenu's ref and writes the button into both.
|
|
19
|
+
*/
|
|
20
|
+
export declare const MergedRefOpener: React.ForwardRefExoticComponent<MergedRefOpenerProps & React.RefAttributes<HTMLButtonElement>>;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The payload a dragged tile carries. dnd-kit returns it at drag end as
|
|
3
|
+
* `operation.source.data`, so widgets read the move directly instead of
|
|
4
|
+
* decoding the drag id.
|
|
5
|
+
*/
|
|
6
|
+
export type TileDragData = {
|
|
7
|
+
/** The tile that moves. */
|
|
8
|
+
tileId: string;
|
|
9
|
+
/** The blank the tile starts in. Absent for a tile in the bank. */
|
|
10
|
+
fromBlankId?: string;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Builds the drag id for a tile. dnd-kit needs one unique id per
|
|
14
|
+
* draggable, and one tile can show in the bank and in a blank at the
|
|
15
|
+
* same time, so the id includes the location.
|
|
16
|
+
*/
|
|
17
|
+
export declare function tileDragId(data: TileDragData): string;
|
|
18
|
+
/**
|
|
19
|
+
* Reads the payload from a dnd-kit drag. dnd-kit types its data field
|
|
20
|
+
* as an open record, so this is where the shape is checked. Returns
|
|
21
|
+
* null for a drag that another component started.
|
|
22
|
+
*/
|
|
23
|
+
export declare function readTileDragData(data: Record<string, any> | undefined): TileDragData | null;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { DragEndEvent } from "@dnd-kit/react";
|
|
3
|
+
type Props = {
|
|
4
|
+
children: React.ReactNode;
|
|
5
|
+
/**
|
|
6
|
+
* Called when a drag gesture finishes. Read the dropped tile and its
|
|
7
|
+
* target from `event.operation.source` / `event.operation.target`.
|
|
8
|
+
* Temporary surface: the provider will grow an `onMove`/`onClear`
|
|
9
|
+
* translation layer so widgets never touch raw dnd-kit events.
|
|
10
|
+
*/
|
|
11
|
+
onDragEnd?: (event: DragEndEvent) => void;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* PerseusDndProvider is the drag-and-drop context that the Drag-and-Drop
|
|
15
|
+
* widget family renders around its draggables and droppables. Each widget
|
|
16
|
+
* instance mounts its own provider, so drag state never leaks between two
|
|
17
|
+
* widgets on one page.
|
|
18
|
+
*
|
|
19
|
+
* Dragging is a pointer-device interaction (mouse and touch): keyboard and
|
|
20
|
+
* screen-reader users move tiles through each tile's DndActionMenu, which
|
|
21
|
+
* is the single-pointer, non-dragging alternative WCAG 2.5.7 requires.
|
|
22
|
+
* Two dnd-kit defaults are therefore overridden:
|
|
23
|
+
*
|
|
24
|
+
* - Sensors: only the PointerSensor. dnd-kit's KeyboardSensor would add a
|
|
25
|
+
* second, competing keyboard path.
|
|
26
|
+
* - Plugins: dnd-kit's Accessibility plugin is removed. It would make
|
|
27
|
+
* every tile a focusable `role="button"` (an invalid nested control
|
|
28
|
+
* around the menu's real button) and speak keyboard-drag instructions
|
|
29
|
+
* for a mode that is off. Move announcements are instead the widget's
|
|
30
|
+
* job, through the Wonder Blocks Announcer, for the drag path and the
|
|
31
|
+
* menu path alike — one owner per event.
|
|
32
|
+
*/
|
|
33
|
+
export declare const PerseusDndProvider: (props: Props) => React.JSX.Element;
|
|
34
|
+
export {};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Temporary, untranslated strings for the Drag-and-Drop components.
|
|
3
|
+
*
|
|
4
|
+
* The copy is still in flux during development, so these live here
|
|
5
|
+
* instead of PerseusStrings to avoid churning the frontend's translation
|
|
6
|
+
* surface with every tweak. Each entry mirrors the shape it will have in
|
|
7
|
+
* PerseusStrings.
|
|
8
|
+
*
|
|
9
|
+
* TODO(LEMS-4368): once the copy is finalized, move these entries into
|
|
10
|
+
* PerseusStrings (strings.ts) and swap consumers to usePerseusI18n.
|
|
11
|
+
*/
|
|
12
|
+
export declare const tempDndStrings: {
|
|
13
|
+
moveTo: string;
|
|
14
|
+
clear: string;
|
|
15
|
+
moveToTarget: ({ target }: {
|
|
16
|
+
target: string;
|
|
17
|
+
}) => string;
|
|
18
|
+
clearTarget: ({ target }: {
|
|
19
|
+
target: string;
|
|
20
|
+
}) => string;
|
|
21
|
+
actionsMenu: string;
|
|
22
|
+
menuRemaining: ({ num }: {
|
|
23
|
+
num: number;
|
|
24
|
+
}) => string;
|
|
25
|
+
choices: string;
|
|
26
|
+
blankLabel: ({ num }: {
|
|
27
|
+
num: number;
|
|
28
|
+
}) => string;
|
|
29
|
+
movedToTarget: ({ tile, target }: {
|
|
30
|
+
tile: string;
|
|
31
|
+
target: string;
|
|
32
|
+
}) => string;
|
|
33
|
+
returnedToChoices: ({ tile }: {
|
|
34
|
+
tile: string;
|
|
35
|
+
}) => string;
|
|
36
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure placement transitions for the Drag-and-Drop widget family.
|
|
3
|
+
* The parent widget owns the state. The drag path and the menu path
|
|
4
|
+
* call the same functions.
|
|
5
|
+
*
|
|
6
|
+
* These functions apply the FITB/Sorter model: one tile per blank,
|
|
7
|
+
* swap-to-bank displacement. Stacking models (Categorizer) are out of
|
|
8
|
+
* scope.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Maps blankId to the tileId of the placed tile. A missing key means
|
|
12
|
+
* the blank is empty. With multi-use tiles, one tileId can appear
|
|
13
|
+
* under more than one blank.
|
|
14
|
+
*/
|
|
15
|
+
export type TilePlacements = Readonly<Record<string, string>>;
|
|
16
|
+
export type TileUsage = "single" | "multi";
|
|
17
|
+
/**
|
|
18
|
+
* Places a tile into a blank. The occupant of the target blank returns
|
|
19
|
+
* to the bank. Set `fromBlankId` when the tile moves out of a blank.
|
|
20
|
+
*/
|
|
21
|
+
export declare function placeTile(placements: TilePlacements, move: {
|
|
22
|
+
tileId: string;
|
|
23
|
+
fromBlankId?: string;
|
|
24
|
+
}, targetBlankId: string, tileUsage: TileUsage): TilePlacements;
|
|
25
|
+
/** Empties a blank. Its tile returns to the bank. */
|
|
26
|
+
export declare function clearBlank(placements: TilePlacements, blankId: string): TilePlacements;
|
|
27
|
+
/**
|
|
28
|
+
* Returns how many more times the tile can be placed. Returns null for
|
|
29
|
+
* a multi-use tile with no cap.
|
|
30
|
+
*/
|
|
31
|
+
export declare function remainingUses(placements: TilePlacements, tileId: string, tileUsage: TileUsage, maxUsesPerTile?: number): number | null;
|
|
32
|
+
/** Returns true when the choice bank shows this tile. */
|
|
33
|
+
export declare function isTileInBank(placements: TilePlacements, tileId: string, tileUsage: TileUsage, maxUsesPerTile?: number): boolean;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { TileDragData } from "./drag-ids";
|
|
2
|
+
import type { TilePlacements, TileUsage } from "./tile-placements";
|
|
3
|
+
import type { DragEndEvent } from "@dnd-kit/react";
|
|
4
|
+
/**
|
|
5
|
+
* Move, clear, and drag-end handling shared by the widgets that place
|
|
6
|
+
* tiles into blanks. Each action updates the controlled placements and
|
|
7
|
+
* announces the result.
|
|
8
|
+
*
|
|
9
|
+
* handleMove and handleClear are for the tiles' menus, and also move
|
|
10
|
+
* focus back to a menu afterwards: pass `firstBankMenuRef` to the
|
|
11
|
+
* bank's first menu button and `placedMenuRef(blankId)` to each placed
|
|
12
|
+
* tile's menu button. handleDragEnd is for the PerseusDndProvider, and
|
|
13
|
+
* leaves focus alone: a drag is a pointer interaction.
|
|
14
|
+
*/
|
|
15
|
+
export declare function useTileMoveActions(options: {
|
|
16
|
+
placements: TilePlacements;
|
|
17
|
+
onPlacementsChange: (next: TilePlacements) => void;
|
|
18
|
+
tileUsage: TileUsage;
|
|
19
|
+
getTileLabel: (tileId: string) => string;
|
|
20
|
+
getBlankLabel: (blankId: string) => string;
|
|
21
|
+
blankIds: ReadonlyArray<string>;
|
|
22
|
+
}): {
|
|
23
|
+
handleMove: (move: TileDragData, targetBlankId: string) => void;
|
|
24
|
+
handleClear: (blankId: string) => void;
|
|
25
|
+
handleDragEnd: (event: DragEndEvent) => void;
|
|
26
|
+
firstBankMenuRef: (button: HTMLButtonElement | null) => void;
|
|
27
|
+
placedMenuRef: (blankId: string) => (button: HTMLButtonElement | null) => void;
|
|
28
|
+
};
|
package/dist/es/index.css
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
--wb-font-heading-size-small
|
|
17
17
|
);--perseus-multiple-choice-indicator-size:3.2rem;--perseus-multiple-choice-indicator-correct-size:calc(var(--perseus-multiple-choice-indicator-font-size)*1.5 + var(--perseus-multiple-choice-indicator-size));--perseus-multiple-choice-content-margin:calc(var(--perseus-multiple-choice-spacing) + var(--perseus-multiple-choice-indicator-size));--perseus-multiple-choice-fade-direction:90deg;--perseus-multiple-choice-fade-color:var(
|
|
18
18
|
--perseus-widget-background-color,var(--wb-semanticColor-core-background-base-default)
|
|
19
|
-
);box-sizing:border-box;min-width:auto;padding-inline-end:var(--perseus-multiple-choice-spacing);position:relative;transform:translateZ(0)}.perseus_ejJezW-c{list-style:none;margin:0;min-width:fit-content;padding:0;scrollbar-width:thin;width:100%}.perseus_ejJezW-c:dir(rtl){--perseus-multiple-choice-fade-direction:270deg}.perseus_ejJezW-c:before{background:linear-gradient(var(--perseus-multiple-choice-fade-direction),var(--perseus-multiple-choice-fade-color) calc(var(--mobile-font-scale, 1)*(var(--perseus-multiple-choice-content-margin) + (var(--perseus-multiple-choice-spacing)/2))),transparent calc(var(--mobile-font-scale, 1)*(var(--perseus-multiple-choice-content-margin) + var(--perseus-multiple-choice-spacing))) calc(100% - var(--mobile-font-scale, 1)*var(--perseus-multiple-choice-spacing)*1.5),var(--perseus-multiple-choice-fade-color) calc(100% - var(--mobile-font-scale, 1)*var(--perseus-multiple-choice-spacing)));content:"";display:block;height:100%;position:fixed;width:100%}.perseus_zAWs4E3G{--perseus-content-line-height:var(--wb-font-heading-lineHeight-medium);display:flex;flex-direction:column;gap:var(--wb-sizing-size_120);margin-inline-start:var(--perseus-multiple-choice-content-margin);position:relative;z-index:-1}@supports (margin-block-start:1lh){.perseus_zAWs4E3G{--perseus-content-line-height:1lh}}.perseus_zAWs4E3G:not(:has(.paragraph:first-child>.paragraph :is(img,mjx-mfrac,mjx-msqrt,mjx-mroot,mjx-munder,mjx-mover,mjx-munderover))){margin-block-start:calc((var(--perseus-multiple-choice-indicator-size) - var(--perseus-content-line-height))*.4)}@media (prefers-reduced-motion:no-preference){.perseus_zAWs4E3G{transition:all .125s}}.perseus_WyGpYl3q{color:var(--wb-semanticColor-core-foreground-neutral-strong);display:block;font-family:inherit;font-style:normal;font-weight:var(--wb-font-weight-bold);margin-block-end:var(--wb-sizing-size_160)}.perseus_6vMrWVz0,.perseus_WyGpYl3q{font-size:var(--wb-font-body-size-medium);line-height:var(--wb-font-body-lineHeight-medium)}.perseus_6vMrWVz0{font-weight:var(--wb-font-weight-medium)}.perseus_6vMrWVz0.perseus_Zv66BVdg{color:var(--wb-semanticColor-core-foreground-neutral-default)}.perseus_6vMrWVz0 .perseus-renderer{display:flex;flex-direction:column;gap:var(--wb-sizing-size_120)}.perseus_6vMrWVz0 .perseus-renderer .paragraph{margin:0}.perseus_6vMrWVz0 .perseus-renderer>.paragraph:not(:has(*)){display:none}.perseus_hJzSXKwq{--perseus-multiple-choice-content-margin:calc(var(--perseus-multiple-choice-spacing) + var(--perseus-multiple-choice-indicator-correct-size))}.
|
|
19
|
+
);box-sizing:border-box;min-width:auto;padding-inline-end:var(--perseus-multiple-choice-spacing);position:relative;transform:translateZ(0)}.perseus_ejJezW-c{list-style:none;margin:0;min-width:fit-content;padding:0;scrollbar-width:thin;width:100%}.perseus_ejJezW-c:dir(rtl){--perseus-multiple-choice-fade-direction:270deg}.perseus_ejJezW-c:before{background:linear-gradient(var(--perseus-multiple-choice-fade-direction),var(--perseus-multiple-choice-fade-color) calc(var(--mobile-font-scale, 1)*(var(--perseus-multiple-choice-content-margin) + (var(--perseus-multiple-choice-spacing)/2))),transparent calc(var(--mobile-font-scale, 1)*(var(--perseus-multiple-choice-content-margin) + var(--perseus-multiple-choice-spacing))) calc(100% - var(--mobile-font-scale, 1)*var(--perseus-multiple-choice-spacing)*1.5),var(--perseus-multiple-choice-fade-color) calc(100% - var(--mobile-font-scale, 1)*var(--perseus-multiple-choice-spacing)));content:"";display:block;height:100%;position:fixed;width:100%}.perseus_zAWs4E3G{--perseus-content-line-height:var(--wb-font-heading-lineHeight-medium);display:flex;flex-direction:column;gap:var(--wb-sizing-size_120);margin-inline-start:var(--perseus-multiple-choice-content-margin);position:relative;z-index:-1}@supports (margin-block-start:1lh){.perseus_zAWs4E3G{--perseus-content-line-height:1lh}}.perseus_zAWs4E3G:not(:has(.paragraph:first-child>.paragraph :is(img,mjx-mfrac,mjx-msqrt,mjx-mroot,mjx-munder,mjx-mover,mjx-munderover))){margin-block-start:calc((var(--perseus-multiple-choice-indicator-size) - var(--perseus-content-line-height))*.4)}@media (prefers-reduced-motion:no-preference){.perseus_zAWs4E3G{transition:all .125s}}.perseus_WyGpYl3q{color:var(--wb-semanticColor-core-foreground-neutral-strong);display:block;font-family:inherit;font-style:normal;font-weight:var(--wb-font-weight-bold);margin-block-end:var(--wb-sizing-size_160)}.perseus_6vMrWVz0,.perseus_WyGpYl3q{font-size:var(--wb-font-body-size-medium);line-height:var(--wb-font-body-lineHeight-medium)}.perseus_6vMrWVz0{font-weight:var(--wb-font-weight-medium)}.perseus_6vMrWVz0.perseus_Zv66BVdg{color:var(--wb-semanticColor-core-foreground-neutral-default)}.perseus_6vMrWVz0 .perseus-renderer{display:flex;flex-direction:column;gap:var(--wb-sizing-size_120)}.perseus_6vMrWVz0 .perseus-renderer .paragraph{margin:0}.perseus_6vMrWVz0 .perseus-renderer>.paragraph:not(:has(*)){display:none}.perseus_hJzSXKwq{--perseus-multiple-choice-content-margin:calc(var(--perseus-multiple-choice-spacing) + var(--perseus-multiple-choice-indicator-correct-size))}.perseus_w5JEPtr3{align-items:center;background:var(--wb-semanticColor-core-background-base-subtle);border:var(--wb-border-width-thin) dashed var(--wb-semanticColor-core-border-neutral-default);border-radius:var(--wb-border-radius-radius_040);box-sizing:border-box;display:inline-flex;justify-content:center;margin-inline:var(--wb-sizing-size_040);min-height:48px;min-width:var(--blank-min-inline-size,65px);padding:var(--wb-sizing-size_0);transition:min-width .15s ease-out;vertical-align:middle}@media (prefers-reduced-motion:reduce){.perseus_w5JEPtr3{transition:none}}.perseus_w5JEPtr3.perseus_YYUlZ0FC{margin-inline:0;min-height:28px;min-width:26px}.perseus_w5JEPtr3.perseus_zuqfZw15{vertical-align:.5em}.perseus_w5JEPtr3.perseus_WBbw9tQT{vertical-align:-.7em}.perseus_w5JEPtr3.perseus_YYUlZ0FC.perseus_1Uom-GrS:focus-within,.perseus_w5JEPtr3.perseus_YYUlZ0FC.perseus_1Uom-GrS:hover{vertical-align:middle}.perseus_w5JEPtr3.perseus_1Uom-GrS{background:transparent;border:none;min-height:0;min-width:0}.perseus_w5JEPtr3.perseus_1Uom-GrS.perseus_djiO5HzC{min-width:var(--blank-min-inline-size,65px)}.perseus_w5JEPtr3.perseus_ZiDXp0R-{background:var(--wb-semanticColor-core-background-instructive-subtle);border-color:transparent;border-radius:var(--wb-border-radius-radius_080);outline:var(--wb-border-width-medium) dashed var(--wb-semanticColor-core-border-instructive-default);outline-offset:calc(var(--wb-border-width-medium)*-1)}@container fitb-answer-zone (width < 504px){.perseus_w5JEPtr3:not(.perseus_YYUlZ0FC){display:flex;inline-size:100cqi;justify-content:flex-start;margin-inline:0}}.perseus_5CDFHduT{border-bottom:none;color:var(--wb-semanticColor-core-foreground-instructive-default)}:active>.perseus_5CDFHduT,:focus>.perseus_5CDFHduT,:hover>.perseus_5CDFHduT{border-bottom:var(--wb-border-width-medium) solid var(--wb-semanticColor-core-border-instructive-default)}.perseus_ydfsz2Xj{color:var(--wb-semanticColor-core-foreground-neutral-strong);font-size:var(--wb-font-body-size-medium);font-weight:var(--wb-font-weight-medium);line-height:var(--wb-font-body-lineHeight-medium)}.perseus_3AGWQQ8Z{height:auto;line-height:inherit;margin-left:-.2rem;margin-right:.2rem;padding-left:.2rem}.perseus_HDIlPzEz{border-left:var(--wb-border-width-none) solid var(--wb-semanticColor-core-border-neutral-subtle);display:inline-grid;position:relative}.perseus_NkFQFgcZ{grid-template-columns:0fr;grid-template-rows:0fr;margin-bottom:0;margin-top:0;min-width:0;padding-bottom:0;visibility:hidden}.perseus_XhV-9haB{border-left-width:5px;grid-template-columns:1fr;grid-template-rows:1fr;margin-bottom:1.4rem;margin-left:-2.3rem;margin-top:1.4rem;min-width:100%;padding-bottom:1rem;padding-left:2.3rem;visibility:visible}.perseus_WEJESS6h{overflow:hidden}.perseus_SX0g-1Bs{font-size:var(--wb-font-heading-size-medium);line-height:inherit;margin-right:-.6rem;text-align:left;white-space:normal}@media (prefers-reduced-motion:no-preference){.perseus_sbBrAODD{transition:all .25s step-end,grid-template-rows .25s,margin-top .25s,margin-bottom .25s,padding-bottom .25s}.perseus_Wp72j6qk{transition:grid-template-rows .5s,margin-top .5s,margin-bottom .5s,padding-bottom .5s}}.MafsView .protractor-rotation-handle{cursor:grab;pointer-events:all;transition:transform .15s ease-out}.MafsView .protractor-rotation-handle:hover{transform:scale(1.2)}.MafsView .protractor-rotation-handle-arrow-arc{fill:none;stroke:var(--mafs-blue);stroke-width:8;stroke-linecap:square}.MafsView .protractor-rotation-handle-arrowhead{fill:var(--mafs-blue);stroke:none}.MafsView{--mafs-bg:#000;--mafs-fg:#fff;--mafs-origin-color:var(--mafs-fg);--mafs-line-color:#555;--mafs-line-stroke-dash-style:4,3;--mafs-axis-stroke-width:1px;--grid-line-subdivision-color:#222;--mafs-red:#f11d0e;--mafs-orange:#f14e0e;--mafs-yellow:#ffe44a;--mafs-green:#15e272;--mafs-blue:#58a6ff;--mafs-indigo:#7c58ff;--mafs-violet:#ae58ff;--mafs-pink:#ee00ab;background:var(--mafs-bg);display:block;font-family:inherit;font-variant-numeric:tabular-nums;outline:0;overflow:hidden;touch-action:none;-webkit-user-select:none;user-select:none}.MafsView text{fill:var(--mafs-fg);cursor:default}.MafsView path{stroke:var(--mafs-fg)}.MafsView:focus-visible{border-radius:5px;outline:3px solid #58a6ff}@supports not selector(:focus-visible){.MafsView:focus{border-radius:5px;outline:3px solid #58a6ff}}.mafs-shadow{stroke-width:3px;stroke:var(--mafs-bg);stroke-opacity:.75;stroke-linejoin:round;paint-order:stroke}.mafs-movable-point{cursor:grab;touch-action:none}.mafs-movable-point-dragging{cursor:grabbing}.mafs-movable-point:focus{outline:0}.mafs-movable-point *{fill:none;stroke:none}.mafs-movable-point-hitbox{fill:transparent}.mafs-movable-point-focus{stroke:var(--movable-point-color);stroke-width:2;stroke-opacity:0;fill:none;transition:stroke-opacity .2s ease}.mafs-movable-point-ring{fill:var(--movable-point-color);fill-opacity:.25;stroke:none;transition:r .2s ease}.mafs-movable-point-point{fill:var(--movable-point-color);transition:r .2s ease}.mafs-movable-point:focus-visible .mafs-movable-point-point,.mafs-movable-point:hover .mafs-movable-point-point{r:calc(var(--movable-point-ring-size) - 2px)}.mafs-movable-point:focus-visible .mafs-movable-point-ring,.mafs-movable-point:hover .mafs-movable-point-ring{r:calc(var(--movable-point-ring-size) + 3px)}.mafs-movable-point.mafs-movable-point-dragging .mafs-movable-point-ring{r:var(--movable-point-ring-size)}.mafs-movable-point:focus-visible .mafs-movable-point-focus{stroke-opacity:1}.MafsView{--mafs-bg:transparent;--mafs-fg:var(--wb-semanticColor-core-foreground-neutral-strong);--mafs-line-color:var(--wb-semanticColor-core-border-neutral-subtle);--mafs-axis-stroke-width:2px;--mafs-blue:var(--wb-semanticColor-core-foreground-instructive-default);--mafs-red:var(--wb-semanticColor-core-foreground-critical-default);--mafs-green:var(--wb-semanticColor-core-foreground-success-default);--mafs-violet:var(--wb-semanticColor-mastery-primary);--mafs-yellow:var(--wb-semanticColor-core-foreground-warning-default);--static-gray:var(--wb-semanticColor-core-foreground-disabled-strong);--movable-point-color:var(--mafs-blue);--movable-point-center-radius:6px;--movable-point-ring-radius:calc(2px + var(--movable-point-center-radius));--movable-point-halo-radius:calc(3px + var(--movable-point-ring-radius));--movable-point-hover-expansion:2px;--movable-point-focus-ring-offset:2px;--movable-line-stroke-weight:2px;--movable-line-stroke-weight-active:4px;--movable-asymptote-stroke-weight:2px;--movable-asymptote-stroke-weight-active:4px;--movable-asymptote-dash-length:6;--movable-asymptote-dash-gap:8;--movable-asymptote-dash-length-active:8;--movable-asymptote-dash-gap-active:12;overflow:visible!important;overscroll-behavior-y:contain;touch-action:pan-y}.MafsView .movable-circle,.MafsView .movable-line,.MafsView .movable-point__focusable-handle,.MafsView .movable-polygon{touch-action:none}.MafsView>svg{display:block;overflow:visible}.MafsView .movable-line--hover,.MafsView .movable-line:focus-visible,.MafsView .movable-line:hover,.movable-dragging{--movable-line-stroke-weight:var(--movable-line-stroke-weight-active);--movable-asymptote-stroke-weight:var(
|
|
20
20
|
--movable-asymptote-stroke-weight-active
|
|
21
21
|
);--movable-asymptote-dash-length:var(
|
|
22
22
|
--movable-asymptote-dash-length-active
|