clio-design-system 0.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/LICENSE +21 -0
- package/README.md +12 -0
- package/dist/index.cjs +2162 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +731 -0
- package/dist/index.d.ts +731 -0
- package/dist/index.js +2096 -0
- package/dist/index.js.map +1 -0
- package/package.json +80 -0
- package/styles/styles.css +19 -0
- package/styles/tokens/brand-theme.css +12 -0
- package/styles/tokens/colors.css +87 -0
- package/styles/tokens/dark-mode.css +191 -0
- package/styles/tokens/fonts.css +6 -0
- package/styles/tokens/radius.css +15 -0
- package/styles/tokens/semantic.css +75 -0
- package/styles/tokens/shadows.css +14 -0
- package/styles/tokens/spacing.css +20 -0
- package/styles/tokens/topic-accent.css +28 -0
- package/styles/tokens/typography.css +40 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,731 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ButtonHTMLAttributes, ReactNode, MouseEvent, InputHTMLAttributes, ChangeEventHandler, CSSProperties, TextareaHTMLAttributes, HTMLAttributes, DragEventHandler, ElementType, MouseEventHandler } from 'react';
|
|
3
|
+
|
|
4
|
+
interface ButtonProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'onClick' | 'style' | 'title' | 'type'> {
|
|
5
|
+
/** Visual treatment. 'nav' is the segmented top-bar tab style (Learn/Practice/Review). */
|
|
6
|
+
variant?: 'primary' | 'dark' | 'secondary' | 'ghost' | 'nav';
|
|
7
|
+
size?: 'sm' | 'md';
|
|
8
|
+
/** For variant="nav" — whether this tab is the active one. */
|
|
9
|
+
active?: boolean;
|
|
10
|
+
disabled?: boolean;
|
|
11
|
+
/** Optional leading glyph, e.g. an ↥ import icon. Rendered aria-hidden — pass meaningful text via children, not icon. */
|
|
12
|
+
icon?: ReactNode;
|
|
13
|
+
children?: ReactNode;
|
|
14
|
+
onClick?: (event: MouseEvent<HTMLButtonElement>) => void;
|
|
15
|
+
title?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Button — Clio's pill-shaped mono-label action button.
|
|
19
|
+
* Variants: primary (filled accent), dark (filled ink-900, e.g. DOWNLOAD),
|
|
20
|
+
* secondary (outlined), ghost (text-only), nav (segmented top-nav tab).
|
|
21
|
+
*/
|
|
22
|
+
declare function Button({ variant, size, active, disabled, icon, children, onClick, title, ...rest }: ButtonProps): react.JSX.Element;
|
|
23
|
+
|
|
24
|
+
interface IconButtonProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'onClick' | 'style' | 'type' | 'title'> {
|
|
25
|
+
size?: number;
|
|
26
|
+
children?: ReactNode;
|
|
27
|
+
onClick?: (event: MouseEvent<HTMLButtonElement>) => void;
|
|
28
|
+
/** Also used as the accessible name (via aria-label) — required so glyph-only buttons are never announced by their raw unicode content. */
|
|
29
|
+
title: string;
|
|
30
|
+
/** Adds a hairline border + surface fill (used for ⚙ manage-tags, ⇥ import/export). */
|
|
31
|
+
bordered?: boolean;
|
|
32
|
+
hoverColor?: string;
|
|
33
|
+
disabled?: boolean;
|
|
34
|
+
/** Corner radius token. Defaults to the standard control radius; row-level menu triggers use the smaller 'var(--radius-sm)'. */
|
|
35
|
+
radius?: string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* IconButton — circular glyph-only button (⋯ menu trigger, ⚙ manage tags,
|
|
39
|
+
* chevron collapse toggle, back arrow).
|
|
40
|
+
*/
|
|
41
|
+
declare function IconButton({ size, children, onClick, title, bordered, hoverColor, disabled, radius, onMouseEnter, onMouseLeave, onFocus, onBlur, ...rest }: IconButtonProps): react.JSX.Element;
|
|
42
|
+
|
|
43
|
+
interface TagChipProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'onClick' | 'style' | 'color' | 'type'> {
|
|
44
|
+
label: string;
|
|
45
|
+
/** Tag or topic color — tints when inactive, fills solid when active. */
|
|
46
|
+
color?: string;
|
|
47
|
+
active?: boolean;
|
|
48
|
+
onClick?: (event: MouseEvent<HTMLButtonElement>) => void;
|
|
49
|
+
removable?: boolean;
|
|
50
|
+
onRemove?: () => void;
|
|
51
|
+
size?: 'sm' | 'md';
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* TagChip — mono capsule tag (#exam-prep). Two states: inactive (tinted)
|
|
55
|
+
* and active/selected (solid fill, white text) used in tag filter bars.
|
|
56
|
+
*/
|
|
57
|
+
declare function TagChip({ label, color, active, onClick, removable, onRemove, size, ...rest }: TagChipProps): react.JSX.Element;
|
|
58
|
+
|
|
59
|
+
interface BadgeProps {
|
|
60
|
+
variant?: 'topic' | 'mono' | 'eyebrow' | 'count';
|
|
61
|
+
color?: string;
|
|
62
|
+
tint?: string;
|
|
63
|
+
children?: ReactNode;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Badge — small labeled pill/rect used across the product:
|
|
67
|
+
* - 'topic' : rounded-rect topic-color label on note headers (e.g. MACHINE LEARNING)
|
|
68
|
+
* - 'mono' : neutral rounded-rect mono badge (e.g. MULTIPLE CHOICE problem type)
|
|
69
|
+
* - 'eyebrow' : borderless mono accent label above a content card (e.g. INTERACTIVE, CODE)
|
|
70
|
+
* - 'count' : small circular counter (review due-count)
|
|
71
|
+
*/
|
|
72
|
+
declare function Badge({ variant, children, color, tint }: BadgeProps): react.JSX.Element;
|
|
73
|
+
|
|
74
|
+
interface SolvedStatusButtonProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'onClick' | 'style' | 'type'> {
|
|
75
|
+
solved?: boolean;
|
|
76
|
+
onClick?: (event: MouseEvent<HTMLButtonElement>) => void;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* SolvedStatusButton — Practice problem's status toggle. Two states only:
|
|
80
|
+
* unsolved (neutral outline, "MARK SOLVED") and solved (green tint, "✓ SOLVED").
|
|
81
|
+
* FLAGGED IN README: exact green was inferred from the app's shared success
|
|
82
|
+
* color (#059669) — confirm this is intentional vs. a distinct status hue.
|
|
83
|
+
*/
|
|
84
|
+
declare function SolvedStatusButton({ solved, onClick, ...rest }: SolvedStatusButtonProps): react.JSX.Element;
|
|
85
|
+
|
|
86
|
+
interface TextInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange' | 'style' | 'aria-label' | 'type'> {
|
|
87
|
+
value: string;
|
|
88
|
+
onChange: ChangeEventHandler<HTMLInputElement>;
|
|
89
|
+
placeholder?: string;
|
|
90
|
+
mono?: boolean;
|
|
91
|
+
autoFocus?: boolean;
|
|
92
|
+
style?: CSSProperties;
|
|
93
|
+
/** Accessible name. The source design has no visible label for this field — pass this when the placeholder alone isn't a sufficient accessible name. */
|
|
94
|
+
'aria-label'?: string;
|
|
95
|
+
/** Restricted to text-like input types — this component's value/onChange contract assumes a string, which checkbox/radio/file don't provide. */
|
|
96
|
+
type?: 'text' | 'search' | 'email' | 'password' | 'tel' | 'url';
|
|
97
|
+
}
|
|
98
|
+
/** TextInput — single-line text field (rename inputs, tag search). */
|
|
99
|
+
declare function TextInput({ value, onChange, placeholder, mono, autoFocus, style, 'aria-label': ariaLabel, type, ...rest }: TextInputProps): react.JSX.Element;
|
|
100
|
+
|
|
101
|
+
interface TextAreaProps extends Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, 'value' | 'onChange' | 'style' | 'aria-label'> {
|
|
102
|
+
value: string;
|
|
103
|
+
onChange: ChangeEventHandler<HTMLTextAreaElement>;
|
|
104
|
+
placeholder?: string;
|
|
105
|
+
rows?: number;
|
|
106
|
+
mono?: boolean;
|
|
107
|
+
/** Accessible name. The source design has no visible label for this field — pass this when the placeholder alone isn't a sufficient accessible name. */
|
|
108
|
+
'aria-label'?: string;
|
|
109
|
+
}
|
|
110
|
+
/** TextArea — multi-line field (import/export paste box). */
|
|
111
|
+
declare function TextArea({ value, onChange, placeholder, rows, mono, 'aria-label': ariaLabel, ...rest }: TextAreaProps): react.JSX.Element;
|
|
112
|
+
|
|
113
|
+
interface AvatarProps extends Omit<HTMLAttributes<HTMLDivElement>, 'style'> {
|
|
114
|
+
initial?: string;
|
|
115
|
+
size?: number;
|
|
116
|
+
dark?: boolean;
|
|
117
|
+
style?: CSSProperties;
|
|
118
|
+
}
|
|
119
|
+
/** Avatar — user initial circle in the topbar (dark mono initial on tint). */
|
|
120
|
+
declare function Avatar({ initial, size, dark, style, ...rest }: AvatarProps): react.JSX.Element;
|
|
121
|
+
|
|
122
|
+
interface ColorSwatchProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'onClick' | 'style' | 'color' | 'type' | 'title'> {
|
|
123
|
+
color: string;
|
|
124
|
+
active?: boolean;
|
|
125
|
+
onClick?: (event: MouseEvent<HTMLButtonElement>) => void;
|
|
126
|
+
size?: number;
|
|
127
|
+
title?: string;
|
|
128
|
+
}
|
|
129
|
+
/** ColorSwatch — small circular color-picker swatch used in recolor rows. */
|
|
130
|
+
declare const ColorSwatch: react.ForwardRefExoticComponent<ColorSwatchProps & react.RefAttributes<HTMLButtonElement>>;
|
|
131
|
+
|
|
132
|
+
interface DragHandleProps extends Omit<HTMLAttributes<HTMLSpanElement>, 'onDragStart' | 'title' | 'style'> {
|
|
133
|
+
onDragStart?: DragEventHandler<HTMLSpanElement>;
|
|
134
|
+
title?: string;
|
|
135
|
+
style?: CSSProperties;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* DragHandle — the ⋮⋮ grip used to reorder topic/note rows.
|
|
139
|
+
*
|
|
140
|
+
* KNOWN GAP: this uses HTML5 native drag-and-drop (the `draggable` attribute),
|
|
141
|
+
* which has no keyboard equivalent — keyboard-only and screen-reader users
|
|
142
|
+
* cannot trigger it. It's marked aria-hidden so it isn't announced as a
|
|
143
|
+
* false, non-operable affordance in the meantime. Real keyboard reordering
|
|
144
|
+
* (e.g. Space to lift, arrow keys to move, a live region announcing the new
|
|
145
|
+
* position) needs actual reorder state to wire into, which belongs in the
|
|
146
|
+
* list that owns it (Sidebar/TopicRow/NoteRow) — revisit this when those
|
|
147
|
+
* are built, not in this leaf component alone.
|
|
148
|
+
*/
|
|
149
|
+
declare function DragHandle({ onDragStart, title, style, ...rest }: DragHandleProps): react.JSX.Element;
|
|
150
|
+
|
|
151
|
+
type NavTabId = 'notes' | 'practice' | 'review';
|
|
152
|
+
interface NavTabsProps extends Omit<HTMLAttributes<HTMLDivElement>, 'role' | 'onChange'> {
|
|
153
|
+
active?: NavTabId;
|
|
154
|
+
onChange?: (id: NavTabId) => void;
|
|
155
|
+
reviewCount?: number;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* NavTabs — top-bar Learn / Practice / Review segmented control, with an
|
|
159
|
+
* optional due-count badge on the Review tab.
|
|
160
|
+
*
|
|
161
|
+
* Implements the ARIA tabs pattern (role="tablist"/"tab", aria-selected,
|
|
162
|
+
* roving tabindex + arrow-key navigation) rather than aria-current, since
|
|
163
|
+
* these buttons swap the active view in place — aria-current is intended for
|
|
164
|
+
* navigation links to different pages/locations, not view-switching controls.
|
|
165
|
+
*/
|
|
166
|
+
declare function NavTabs({ active, onChange, reviewCount, ...rest }: NavTabsProps): react.JSX.Element;
|
|
167
|
+
|
|
168
|
+
interface SidebarProps extends Omit<HTMLAttributes<HTMLDivElement>, 'style'> {
|
|
169
|
+
collapsed?: boolean;
|
|
170
|
+
onToggle?: () => void;
|
|
171
|
+
/** Eyebrow section label, e.g. "TOPICS" or "CONCEPTS". */
|
|
172
|
+
label?: string;
|
|
173
|
+
width?: number;
|
|
174
|
+
children?: ReactNode;
|
|
175
|
+
borderSide?: 'right' | 'none';
|
|
176
|
+
style?: CSSProperties;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Sidebar — collapsible panel shell used for both the topics column and the
|
|
180
|
+
* notes column on desktop. Collapsed state shrinks to a 26px chevron rail;
|
|
181
|
+
* expanded shows a scrollable content area. Pass a section label + children.
|
|
182
|
+
*/
|
|
183
|
+
declare function Sidebar({ collapsed, onToggle, label, width, children, borderSide, style, ...rest }: SidebarProps): react.JSX.Element;
|
|
184
|
+
|
|
185
|
+
interface TopicRowProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onClick' | 'style' | 'color'> {
|
|
186
|
+
name: string;
|
|
187
|
+
color: string;
|
|
188
|
+
notesLabel: string;
|
|
189
|
+
selected?: boolean;
|
|
190
|
+
onClick?: () => void;
|
|
191
|
+
onMenuClick?: (event: MouseEvent<HTMLButtonElement>) => void;
|
|
192
|
+
draggable?: boolean;
|
|
193
|
+
onDragStart?: DragEventHandler<HTMLSpanElement>;
|
|
194
|
+
/** Mobile topics-screen layout omits the drag handle + indent. */
|
|
195
|
+
dense?: boolean;
|
|
196
|
+
style?: CSSProperties;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* TopicRow — a single row in the topics sidebar (desktop) / topics screen
|
|
200
|
+
* (mobile). Shows color dot, name, note count, drag handle, and a hover-
|
|
201
|
+
* revealed ⋯ menu. `selected` drives the sunken background + bold text.
|
|
202
|
+
*
|
|
203
|
+
* The selectable area (dot + name + note count) is a real <button>, not a
|
|
204
|
+
* click handler on the outer row: the row also hosts the drag handle and the
|
|
205
|
+
* ⋯ menu button, and an ARIA `role="button"` on the outer element would
|
|
206
|
+
* improperly nest interactive descendants inside another interactive
|
|
207
|
+
* element. A native button sidesteps that entirely.
|
|
208
|
+
*/
|
|
209
|
+
declare function TopicRow({ name, color, notesLabel, selected, onClick, onMenuClick, draggable, onDragStart, dense, style, ...rest }: TopicRowProps): react.JSX.Element;
|
|
210
|
+
|
|
211
|
+
interface NoteRowTag {
|
|
212
|
+
label: string;
|
|
213
|
+
color: string;
|
|
214
|
+
}
|
|
215
|
+
interface NoteRowProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onClick' | 'style'> {
|
|
216
|
+
title: string;
|
|
217
|
+
meta: string;
|
|
218
|
+
selected?: boolean;
|
|
219
|
+
showTopic?: boolean;
|
|
220
|
+
topicColor?: string;
|
|
221
|
+
topicName?: string;
|
|
222
|
+
tags?: NoteRowTag[];
|
|
223
|
+
onClick?: () => void;
|
|
224
|
+
onMenuClick?: (event: MouseEvent<HTMLButtonElement>) => void;
|
|
225
|
+
draggable?: boolean;
|
|
226
|
+
onDragStart?: DragEventHandler<HTMLSpanElement>;
|
|
227
|
+
style?: CSSProperties;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* NoteRow — a single concept/note row in the notes list (desktop sidebar
|
|
231
|
+
* column 2) / concepts screen (mobile). Optional topic dot+label (shown
|
|
232
|
+
* when listing across topics, e.g. tag-filtered results) and tag chips.
|
|
233
|
+
*
|
|
234
|
+
* Like TopicRow, the selectable area is a real <button> rather than a click
|
|
235
|
+
* handler on the outer row, since the row also hosts the drag handle and the
|
|
236
|
+
* ⋯ menu button as separate interactive controls.
|
|
237
|
+
*/
|
|
238
|
+
declare function NoteRow({ title, meta, selected, showTopic, topicColor, topicName, tags, onClick, onMenuClick, draggable, onDragStart, style, ...rest }: NoteRowProps): react.JSX.Element;
|
|
239
|
+
|
|
240
|
+
interface ContextMenuItem {
|
|
241
|
+
label?: string;
|
|
242
|
+
onClick?: () => void;
|
|
243
|
+
danger?: boolean;
|
|
244
|
+
icon?: ReactNode;
|
|
245
|
+
divider?: boolean;
|
|
246
|
+
}
|
|
247
|
+
interface ContextMenuSwatch {
|
|
248
|
+
color: string;
|
|
249
|
+
active?: boolean;
|
|
250
|
+
pick: () => void;
|
|
251
|
+
}
|
|
252
|
+
interface ContextMenuProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onClick' | 'style' | 'role' | 'tabIndex'> {
|
|
253
|
+
items: ContextMenuItem[];
|
|
254
|
+
/** Recolor swatch row, e.g. topic/tag color picker. */
|
|
255
|
+
swatches?: ContextMenuSwatch[];
|
|
256
|
+
width?: number;
|
|
257
|
+
style?: CSSProperties;
|
|
258
|
+
/** Called on Escape — the menu itself doesn't track open/closed state; the parent (typically toggled by a row's ⋯ button) owns that. */
|
|
259
|
+
onClose?: () => void;
|
|
260
|
+
/** Accessible label for the menu, since its contents vary by context (e.g. "Topic options"). */
|
|
261
|
+
'aria-label'?: string;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* ContextMenu — desktop floating dropdown (topic ⋯, note ⋯, manage tags).
|
|
265
|
+
* Pass `items`: array of { label, onClick, danger? } or { divider: true },
|
|
266
|
+
* plus optional `swatches` (color-recolor row) and `header`.
|
|
267
|
+
*
|
|
268
|
+
* Implements the ARIA menu pattern: role="menu"/"menuitem", roving tabindex,
|
|
269
|
+
* Arrow/Home/End navigation, and Escape-to-close — none of which the source
|
|
270
|
+
* design has, since it only renders the dropdown's visual chrome.
|
|
271
|
+
*/
|
|
272
|
+
declare function ContextMenu({ items, swatches, width, style, onClose, 'aria-label': ariaLabel, ...rest }: ContextMenuProps): react.JSX.Element;
|
|
273
|
+
|
|
274
|
+
interface BottomSheetProps extends Omit<HTMLAttributes<HTMLDivElement>, 'style' | 'onClick' | 'role' | 'aria-modal' | 'tabIndex'> {
|
|
275
|
+
open: boolean;
|
|
276
|
+
onClose?: () => void;
|
|
277
|
+
children?: ReactNode;
|
|
278
|
+
maxHeight?: string | number;
|
|
279
|
+
style?: CSSProperties;
|
|
280
|
+
/** Accessible name for the sheet, since its content varies by context (e.g. "Topic actions"). Prefer aria-labelledby if the sheet's content has its own visible heading. */
|
|
281
|
+
'aria-label'?: string;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* BottomSheet — mobile modal pattern: dims the screen, slides a rounded-top
|
|
285
|
+
* panel up from the bottom with a drag-handle bar. Used for topic/note
|
|
286
|
+
* action sheets, manage-tags, add-tag, and the TOC outline.
|
|
287
|
+
*
|
|
288
|
+
* A true modal dialog: role="dialog" + aria-modal, focus moves into the
|
|
289
|
+
* sheet on open and is trapped there, Escape closes it, and focus returns to
|
|
290
|
+
* whatever triggered it on close (see internal/useDialog). The source design
|
|
291
|
+
* has none of this — it's presentational chrome only.
|
|
292
|
+
*/
|
|
293
|
+
declare function BottomSheet({ open, onClose, children, maxHeight, style, 'aria-label': ariaLabel, ...rest }: BottomSheetProps): react.JSX.Element | null;
|
|
294
|
+
|
|
295
|
+
type ImportExportTab = 'import' | 'export';
|
|
296
|
+
interface ImportExportModalProps extends Omit<HTMLAttributes<HTMLDivElement>, 'style' | 'onClick' | 'role' | 'aria-modal' | 'tabIndex'> {
|
|
297
|
+
open: boolean;
|
|
298
|
+
tab?: ImportExportTab;
|
|
299
|
+
onTabChange?: (tab: ImportExportTab) => void;
|
|
300
|
+
onClose?: () => void;
|
|
301
|
+
children?: ReactNode;
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* ImportExportModal — desktop centered modal (mobile uses a full-screen
|
|
305
|
+
* variant, same content structure with a header row instead of an X close).
|
|
306
|
+
*
|
|
307
|
+
* A true modal dialog: role="dialog" + aria-modal, focus trapped inside and
|
|
308
|
+
* moved in on open / restored on close (internal/useDialog), Escape closes,
|
|
309
|
+
* and page scroll is locked while open (unlike BottomSheet, this overlay is
|
|
310
|
+
* `position: fixed; inset: 0` over the whole viewport, so background scroll
|
|
311
|
+
* genuinely needs to be suppressed here). The Import/Export toggle uses the
|
|
312
|
+
* ARIA tabs pattern (role="tablist"/"tab", aria-selected, roving tabindex +
|
|
313
|
+
* arrow-key navigation) since it swaps the modal's content in place, same
|
|
314
|
+
* reasoning as NavTabs. None of this exists in the source design.
|
|
315
|
+
*/
|
|
316
|
+
declare function ImportExportModal({ open, tab, onTabChange, onClose, children, ...rest }: ImportExportModalProps): react.JSX.Element | null;
|
|
317
|
+
interface ExportRowProps {
|
|
318
|
+
title: string;
|
|
319
|
+
sub: string;
|
|
320
|
+
onCopy?: () => void;
|
|
321
|
+
copyLabel?: string;
|
|
322
|
+
onDownload?: () => void;
|
|
323
|
+
}
|
|
324
|
+
/** ExportRow — one exportable-item row inside the EXPORT tab (This note, Topic, Full backup). */
|
|
325
|
+
declare function ExportRow({ title, sub, onCopy, copyLabel, onDownload }: ExportRowProps): react.JSX.Element;
|
|
326
|
+
|
|
327
|
+
interface TocItem {
|
|
328
|
+
label: string;
|
|
329
|
+
active?: boolean;
|
|
330
|
+
go?: () => void;
|
|
331
|
+
}
|
|
332
|
+
interface TocRailProps extends Omit<HTMLAttributes<HTMLElement>, 'onMouseEnter' | 'onMouseLeave' | 'onFocus' | 'onBlur' | 'aria-label' | 'style'> {
|
|
333
|
+
items: TocItem[];
|
|
334
|
+
/** Hovering (or focusing, via keyboard) the rail expands dots into labels. */
|
|
335
|
+
hovered?: boolean;
|
|
336
|
+
onHoverIn?: () => void;
|
|
337
|
+
onHoverOut?: () => void;
|
|
338
|
+
'aria-label'?: string;
|
|
339
|
+
style?: CSSProperties;
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* TocRail — desktop floating-dot outline rail (scroll-spy), hover to reveal
|
|
343
|
+
* labels.
|
|
344
|
+
*
|
|
345
|
+
* The source design only expands labels on mouse hover (onMouseEnter/
|
|
346
|
+
* onMouseLeave on the wrapping div) — a keyboard user tabbing through the
|
|
347
|
+
* rail's buttons never sees the labels, only bare dots. Also triggers the
|
|
348
|
+
* same expansion on focus-within (with a relatedTarget check so tabbing
|
|
349
|
+
* between the rail's own buttons doesn't flicker collapsed/expanded), and
|
|
350
|
+
* wraps the items in nav > ol so screen readers announce it as a real list
|
|
351
|
+
* inside a labeled navigation landmark, with aria-current marking the
|
|
352
|
+
* active section (this is exactly what aria-current is for — the current
|
|
353
|
+
* location within a browsing context — unlike a view-switching control).
|
|
354
|
+
* role="list" on the <ol> works around a long-standing Safari/VoiceOver
|
|
355
|
+
* quirk where `list-style: none` drops the implicit list semantics.
|
|
356
|
+
*/
|
|
357
|
+
declare function TocRail({ items, hovered, onHoverIn, onHoverOut, 'aria-label': ariaLabel, style, ...rest }: TocRailProps): react.JSX.Element;
|
|
358
|
+
|
|
359
|
+
interface TocFabProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'onClick' | 'style' | 'type'> {
|
|
360
|
+
onClick?: () => void;
|
|
361
|
+
/** Accessible name — the glyph alone ("☰") has no meaning to a screen reader. */
|
|
362
|
+
'aria-label'?: string;
|
|
363
|
+
style?: CSSProperties;
|
|
364
|
+
}
|
|
365
|
+
/** TocFab — mobile floating action button that opens the TOC bottom sheet. */
|
|
366
|
+
declare function TocFab({ onClick, 'aria-label': ariaLabel, style, ...rest }: TocFabProps): react.JSX.Element;
|
|
367
|
+
|
|
368
|
+
interface TocSheetListProps {
|
|
369
|
+
items: TocItem[];
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* TocSheetList — the outline list rendered inside a BottomSheet on mobile.
|
|
373
|
+
*
|
|
374
|
+
* Wrapped in nav > ol (screen readers announce it as a real list inside a
|
|
375
|
+
* navigation landmark) with the visible "OUTLINE" eyebrow as the landmark's
|
|
376
|
+
* accessible name via aria-labelledby, rather than a redundant separate
|
|
377
|
+
* aria-label repeating the same text. aria-current marks the active section
|
|
378
|
+
* — this is exactly what aria-current is for (current location within a
|
|
379
|
+
* browsing context), matching TocRail. role="list" on the <ol> works around
|
|
380
|
+
* a long-standing Safari/VoiceOver quirk where `list-style: none` drops the
|
|
381
|
+
* implicit list semantics.
|
|
382
|
+
*/
|
|
383
|
+
declare function TocSheetList({ items }: TocSheetListProps): react.JSX.Element;
|
|
384
|
+
|
|
385
|
+
interface CardShellProps extends Omit<HTMLAttributes<HTMLDivElement>, 'style'> {
|
|
386
|
+
children?: ReactNode;
|
|
387
|
+
dark?: boolean;
|
|
388
|
+
tint?: boolean;
|
|
389
|
+
style?: CSSProperties;
|
|
390
|
+
}
|
|
391
|
+
/** CardShell — shared white-surface wrapper most content-card types use. */
|
|
392
|
+
declare function CardShell({ children, dark, tint, style, ...rest }: CardShellProps): react.JSX.Element;
|
|
393
|
+
|
|
394
|
+
interface CardHeaderProps extends Omit<HTMLAttributes<HTMLDivElement>, 'style'> {
|
|
395
|
+
/** e.g. "INTERACTIVE", "WALKTHROUGH", "CODE", "COMPARE", "CHECKPOINT" */
|
|
396
|
+
eyebrow: string;
|
|
397
|
+
eyebrowColor?: string;
|
|
398
|
+
title?: string;
|
|
399
|
+
/**
|
|
400
|
+
* Element the title renders as. Defaults to a plain `span` (matches the
|
|
401
|
+
* source design exactly). Content cards sit at an arbitrary point in a
|
|
402
|
+
* note's document outline that only the consuming app knows — this
|
|
403
|
+
* library can't safely guess a heading level (h2 vs h3 vs h4) without
|
|
404
|
+
* risking an inconsistent or skipped-level heading hierarchy. Pass e.g.
|
|
405
|
+
* "h3" here when the consumer knows the right level and wants the title
|
|
406
|
+
* reachable via screen-reader heading navigation.
|
|
407
|
+
*/
|
|
408
|
+
as?: ElementType;
|
|
409
|
+
subtitle?: string;
|
|
410
|
+
right?: ReactNode;
|
|
411
|
+
dark?: boolean;
|
|
412
|
+
style?: CSSProperties;
|
|
413
|
+
}
|
|
414
|
+
/** CardHeader — eyebrow + title row shared by interactive/walkthrough/code/table/quiz/image/related/references cards. */
|
|
415
|
+
declare function CardHeader({ eyebrow, eyebrowColor, title, as: TitleTag, subtitle, right, dark, style, ...rest }: CardHeaderProps): react.JSX.Element;
|
|
416
|
+
|
|
417
|
+
interface TextBlockProps extends Omit<HTMLAttributes<HTMLParagraphElement>, 'style' | 'children'> {
|
|
418
|
+
text: string;
|
|
419
|
+
style?: CSSProperties;
|
|
420
|
+
}
|
|
421
|
+
/** TextBlock — plain paragraph content block (no card shell, no eyebrow). */
|
|
422
|
+
declare function TextBlock({ text, style, ...rest }: TextBlockProps): react.JSX.Element;
|
|
423
|
+
|
|
424
|
+
interface EquationBlockProps extends Omit<HTMLAttributes<HTMLDivElement>, 'style'> {
|
|
425
|
+
/** Raw TeX/LaTeX source — real product renders via KaTeX (`<ka-tex>`). */
|
|
426
|
+
tex: string;
|
|
427
|
+
style?: CSSProperties;
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* EquationBlock — display-math content block. Renders bare (no card shell).
|
|
431
|
+
* The real product uses a `<ka-tex>` custom element backed by KaTeX; this
|
|
432
|
+
* design-system stand-in renders the raw TeX in an italic serif style so
|
|
433
|
+
* the layout/spacing is representative without a KaTeX runtime dependency.
|
|
434
|
+
* Consumers should mount the actual `ka-tex` element (see readme.md).
|
|
435
|
+
*/
|
|
436
|
+
declare function EquationBlock({ tex, style, ...rest }: EquationBlockProps): react.JSX.Element;
|
|
437
|
+
|
|
438
|
+
interface IntuitionCalloutProps extends Omit<HTMLAttributes<HTMLDivElement>, 'style'> {
|
|
439
|
+
text: string;
|
|
440
|
+
/** e.g. "INTUITION" or "THIRD LAW" for a named-principle variant. */
|
|
441
|
+
label?: string;
|
|
442
|
+
style?: CSSProperties;
|
|
443
|
+
}
|
|
444
|
+
/** IntuitionCallout — tinted amber-ochre inline callout box (plain-language intuition, no card shell/border-radius-2xl but its own padded box). */
|
|
445
|
+
declare function IntuitionCallout({ text, label, style, ...rest }: IntuitionCalloutProps): react.JSX.Element;
|
|
446
|
+
|
|
447
|
+
interface WalkthroughStep {
|
|
448
|
+
tex: string;
|
|
449
|
+
note: string;
|
|
450
|
+
}
|
|
451
|
+
interface WalkthroughCardProps extends Omit<HTMLAttributes<HTMLDivElement>, 'style'> {
|
|
452
|
+
title: string;
|
|
453
|
+
steps: WalkthroughStep[];
|
|
454
|
+
currentStep: number;
|
|
455
|
+
onPrev?: () => void;
|
|
456
|
+
onNext?: () => void;
|
|
457
|
+
style?: CSSProperties;
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* WalkthroughCard — numbered step-through derivation. Steps reveal one at
|
|
461
|
+
* a time via Back/Next (earlier steps stay visible, dimmed).
|
|
462
|
+
*
|
|
463
|
+
* Two fixes over the source: the Next button now gets a real `disabled`
|
|
464
|
+
* attribute at the last step (it previously only dimmed via opacity while
|
|
465
|
+
* remaining fully clickable — inconsistent with Back, which already
|
|
466
|
+
* disables for real at the first step, and would let Next keep firing past
|
|
467
|
+
* the end). The revealed-steps list is wrapped in an aria-live region so
|
|
468
|
+
* screen reader users are told a new step appeared after Next/Back, rather
|
|
469
|
+
* than needing to rediscover it themselves.
|
|
470
|
+
*/
|
|
471
|
+
declare function WalkthroughCard({ title, steps, currentStep, onPrev, onNext, style, ...rest }: WalkthroughCardProps): react.JSX.Element;
|
|
472
|
+
|
|
473
|
+
interface CodeCardProps extends Omit<HTMLAttributes<HTMLDivElement>, 'style'> {
|
|
474
|
+
title: string;
|
|
475
|
+
lang: string;
|
|
476
|
+
code: string;
|
|
477
|
+
copyLabel?: string;
|
|
478
|
+
onCopy?: () => void;
|
|
479
|
+
style?: CSSProperties;
|
|
480
|
+
}
|
|
481
|
+
/** CodeCard — dark code block with copy button + optional My-note annotation trigger. */
|
|
482
|
+
declare function CodeCard({ title, lang, code, copyLabel, onCopy, style, ...rest }: CodeCardProps): react.JSX.Element;
|
|
483
|
+
|
|
484
|
+
interface TableCardProps extends Omit<HTMLAttributes<HTMLDivElement>, 'style'> {
|
|
485
|
+
title: string;
|
|
486
|
+
header: string[];
|
|
487
|
+
rows: string[][];
|
|
488
|
+
style?: CSSProperties;
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
491
|
+
* TableCard — "COMPARE" content block; a labeled comparison table.
|
|
492
|
+
*
|
|
493
|
+
* The source markup rendered every cell — header row included — as `<td>`
|
|
494
|
+
* inside a single flat `<tr>` list, with no `<thead>`/`<tbody>` split: a
|
|
495
|
+
* screen reader navigating the table got no column-header announcements at
|
|
496
|
+
* all. This shape is actually a two-way table (header[0] is blank because
|
|
497
|
+
* it's the corner above the row-label column — rows[i][0] labels that row,
|
|
498
|
+
* e.g. "Memory", the same way header[1..] label columns, e.g. "SGD"). Fixed
|
|
499
|
+
* to `<thead><tr><th scope="col">` for the header row and `<th scope="row">`
|
|
500
|
+
* for each row's first cell — the standard markup for a table with both row
|
|
501
|
+
* and column headers. The empty corner header cell gets a visually-hidden
|
|
502
|
+
* label (real content tables always pass a non-empty `header[0]` in
|
|
503
|
+
* practice, but an empty `<th>` fails an accessibility check regardless of
|
|
504
|
+
* why it's empty, so this covers that case defensively too).
|
|
505
|
+
*/
|
|
506
|
+
declare function TableCard({ title, header, rows, style, ...rest }: TableCardProps): react.JSX.Element;
|
|
507
|
+
|
|
508
|
+
interface QuizCardProps extends Omit<HTMLAttributes<HTMLDivElement>, 'style'> {
|
|
509
|
+
question: string;
|
|
510
|
+
answerOpen: boolean;
|
|
511
|
+
answer: string;
|
|
512
|
+
quizLabel: string;
|
|
513
|
+
onToggleQuiz?: () => void;
|
|
514
|
+
addFlashLabel: string;
|
|
515
|
+
onAddFlash?: () => void;
|
|
516
|
+
style?: CSSProperties;
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* QuizCard — "CHECKPOINT" self-test block: question, reveal-answer toggle,
|
|
520
|
+
* add-to-flashcards. Uses the accent-tinted background (mixes with the
|
|
521
|
+
* active topic color).
|
|
522
|
+
*
|
|
523
|
+
* The reveal-answer button is a disclosure widget (it shows/hides the
|
|
524
|
+
* answer panel below), so it now carries aria-expanded and aria-controls —
|
|
525
|
+
* the source only changed the button's visible text (quizLabel), giving
|
|
526
|
+
* assistive tech no programmatic signal of the panel's state or its
|
|
527
|
+
* relationship to the toggle, same gap fixed on Sidebar's chevron earlier.
|
|
528
|
+
*/
|
|
529
|
+
declare function QuizCard({ question, answerOpen, answer, quizLabel, onToggleQuiz, addFlashLabel, onAddFlash, style, ...rest }: QuizCardProps): react.JSX.Element;
|
|
530
|
+
|
|
531
|
+
interface ImageCardProps extends Omit<HTMLAttributes<HTMLDivElement>, 'style'> {
|
|
532
|
+
/** "UPLOAD" (user's own work) or "FIGURE" (author-provided diagram). */
|
|
533
|
+
label?: 'UPLOAD' | 'FIGURE';
|
|
534
|
+
title: string;
|
|
535
|
+
/** Unique id — required for the drop to persist (see image-slot.js). */
|
|
536
|
+
slotId: string;
|
|
537
|
+
placeholder?: string;
|
|
538
|
+
height?: number;
|
|
539
|
+
caption?: string;
|
|
540
|
+
style?: CSSProperties;
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* ImageCard — "UPLOAD"/"FIGURE" content block wrapping the product's
|
|
544
|
+
* `<image-slot>` web component (drag-and-drop image placeholder).
|
|
545
|
+
*
|
|
546
|
+
* `<image-slot>` is an opaque vendored utility, not a component this library
|
|
547
|
+
* owns — its internal accessibility (accessible name, keyboard operability
|
|
548
|
+
* of the drop zone) lives in that script, out of this port's scope. See
|
|
549
|
+
* readme.md for the empty/filled state spec, flagged there as ambiguous.
|
|
550
|
+
*/
|
|
551
|
+
declare function ImageCard({ label, title, slotId, placeholder, height, caption, style, ...rest }: ImageCardProps): react.JSX.Element;
|
|
552
|
+
|
|
553
|
+
interface RelatedCardItem {
|
|
554
|
+
label: string;
|
|
555
|
+
color: string;
|
|
556
|
+
onClick?: (event: MouseEvent) => void;
|
|
557
|
+
/** Renders as a real link (enabling native middle-click/Cmd-click "open in new tab") instead of a button. */
|
|
558
|
+
href?: string;
|
|
559
|
+
/** Render as a custom element/component — e.g. a router's Link — instead of the default <a>/<button>. Receives href and onClick. */
|
|
560
|
+
as?: ElementType;
|
|
561
|
+
}
|
|
562
|
+
interface RelatedCardProps extends Omit<HTMLAttributes<HTMLDivElement>, 'style'> {
|
|
563
|
+
items: RelatedCardItem[];
|
|
564
|
+
style?: CSSProperties;
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* RelatedCard — "RELATED" content block linking to other concepts.
|
|
568
|
+
*
|
|
569
|
+
* Items are wrapped in a real <ul role="list">/<li> instead of bare
|
|
570
|
+
* <button>s, so screen readers announce "list of N items" and let a user
|
|
571
|
+
* navigate it as a list; the trailing "›" glyph is aria-hidden since it's
|
|
572
|
+
* purely decorative next to each item's own visible label.
|
|
573
|
+
*
|
|
574
|
+
* Each item renders as a real <a href> when `href` is given (so
|
|
575
|
+
* middle-click/Cmd-click "open in new tab" works natively, which a <button>
|
|
576
|
+
* can never support), or as a fully custom element via `as` (e.g. a router's
|
|
577
|
+
* Link component) — falling back to a plain <button> only when neither is
|
|
578
|
+
* given.
|
|
579
|
+
*/
|
|
580
|
+
declare function RelatedCard({ items, style, ...rest }: RelatedCardProps): react.JSX.Element;
|
|
581
|
+
|
|
582
|
+
interface ReferencesCardProps extends Omit<HTMLAttributes<HTMLDivElement>, 'style'> {
|
|
583
|
+
items: string[];
|
|
584
|
+
style?: CSSProperties;
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* ReferencesCard — "REFERENCES" content block: a simple citation list.
|
|
588
|
+
*
|
|
589
|
+
* Items are wrapped in a real <ul role="list">/<li> instead of bare <div>s,
|
|
590
|
+
* so screen readers announce "list of N items"; the leading "›" glyph is
|
|
591
|
+
* aria-hidden since it's a purely decorative bullet marker.
|
|
592
|
+
*/
|
|
593
|
+
declare function ReferencesCard({ items, style, ...rest }: ReferencesCardProps): react.JSX.Element;
|
|
594
|
+
|
|
595
|
+
interface AnnotationPanelProps extends Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, 'value' | 'onChange' | 'placeholder' | 'style' | 'aria-labelledby' | 'rows'> {
|
|
596
|
+
open: boolean;
|
|
597
|
+
value: string;
|
|
598
|
+
onChange: ChangeEventHandler<HTMLTextAreaElement>;
|
|
599
|
+
placeholder?: string;
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* AnnotationPanel — the "MY NOTE" panel appended to every content-card and
|
|
603
|
+
* practice-problem type. Always amber, regardless of the active topic
|
|
604
|
+
* accent. Font is the hand-script Kalam — the ONLY place it's used.
|
|
605
|
+
*
|
|
606
|
+
* The visible "MY NOTE" label had no programmatic association with the
|
|
607
|
+
* textarea below it (just adjacent markup) — linked via aria-labelledby so
|
|
608
|
+
* the textarea has a real accessible name instead of none. Native textarea
|
|
609
|
+
* attributes (disabled, maxLength, onKeyDown, onBlur, etc.) spread onto the
|
|
610
|
+
* textarea itself, since that's the one interactive element here — the
|
|
611
|
+
* outer wrapper div has no independent need for them.
|
|
612
|
+
*/
|
|
613
|
+
declare function AnnotationPanel({ open, value, onChange, placeholder, ...rest }: AnnotationPanelProps): react.JSX.Element | null;
|
|
614
|
+
|
|
615
|
+
interface AnnotationToggleProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'onClick' | 'style' | 'type'> {
|
|
616
|
+
hasText?: boolean;
|
|
617
|
+
open?: boolean;
|
|
618
|
+
onClick?: MouseEventHandler<HTMLButtonElement>;
|
|
619
|
+
/** Row is hovered — reveals the otherwise-invisible toggle. */
|
|
620
|
+
hot?: boolean;
|
|
621
|
+
}
|
|
622
|
+
/**
|
|
623
|
+
* AnnotationToggle — the small "+ My note" trigger button shown in a card's
|
|
624
|
+
* header row.
|
|
625
|
+
*
|
|
626
|
+
* The source only reveals this button when the parent row reports `hot`
|
|
627
|
+
* (mouse hover). A keyboard user tabbing to the button directly (without
|
|
628
|
+
* the row ever being "hot") would land on an invisible-but-focusable
|
|
629
|
+
* control with no visual indication it exists until they arrive at it.
|
|
630
|
+
* Reusing the shared useHover hook makes the button also reveal itself on
|
|
631
|
+
* its own hover/focus, so tabbing to it makes it visible the same way
|
|
632
|
+
* hovering the row does.
|
|
633
|
+
*/
|
|
634
|
+
declare function AnnotationToggle({ hasText, open, onClick, hot, onMouseEnter, onMouseLeave, onFocus, onBlur, ...rest }: AnnotationToggleProps): react.JSX.Element;
|
|
635
|
+
|
|
636
|
+
interface McqOptionProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'onClick' | 'style' | 'type' | 'disabled'> {
|
|
637
|
+
label: string;
|
|
638
|
+
/** 'default' before pick; 'correct'/'wrong' after pick (shown on both the chosen option and the true correct one). */
|
|
639
|
+
state?: 'default' | 'correct' | 'wrong';
|
|
640
|
+
onClick?: () => void;
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* McqOption — a single multiple-choice option row. States: default, hover,
|
|
644
|
+
* picked-correct, picked-wrong.
|
|
645
|
+
*
|
|
646
|
+
* Two fixes over the source: the ✓/✕ mark was the ONLY signal of an
|
|
647
|
+
* option's correctness, conveyed purely as a raw glyph character with no
|
|
648
|
+
* semantic backing — some screen readers won't announce it meaningfully at
|
|
649
|
+
* all, and unlike a decorative icon next to a text label, hiding it outright
|
|
650
|
+
* would remove the only indicator entirely. Kept the mark visible but added
|
|
651
|
+
* adjacent visually-hidden text ("Correct"/"Incorrect") so the state reaches
|
|
652
|
+
* assistive tech in words, not just a symbol. Also: once a state is set
|
|
653
|
+
* (the question has been answered), the option is no longer meant to be
|
|
654
|
+
* re-clickable, but the source left every option fully interactive
|
|
655
|
+
* regardless of state — added a real `disabled` once state !== 'default'.
|
|
656
|
+
*/
|
|
657
|
+
declare function McqOption({ label, state, onClick, ...rest }: McqOptionProps): react.JSX.Element;
|
|
658
|
+
|
|
659
|
+
interface FillBlankInputProps extends Omit<HTMLAttributes<HTMLDivElement>, 'style'> {
|
|
660
|
+
value: string;
|
|
661
|
+
onChange: ChangeEventHandler<HTMLInputElement>;
|
|
662
|
+
checked?: boolean;
|
|
663
|
+
correct?: boolean;
|
|
664
|
+
onCheck?: () => void;
|
|
665
|
+
resultLabel?: string;
|
|
666
|
+
style?: CSSProperties;
|
|
667
|
+
}
|
|
668
|
+
/**
|
|
669
|
+
* FillBlankInput — fill-in-the-blank answer row with Check button + result
|
|
670
|
+
* state.
|
|
671
|
+
*
|
|
672
|
+
* Reuses the TextInput component (with a style override for the
|
|
673
|
+
* correct/incorrect border color) instead of a bare hand-rolled <input>. The
|
|
674
|
+
* input had no accessible name at all beyond its placeholder (not reliable
|
|
675
|
+
* across browsers/screen readers) — added one, plus aria-invalid reflecting
|
|
676
|
+
* the check result programmatically, not just via border color. The result
|
|
677
|
+
* panel is wrapped in an aria-live region so screen reader users hear the
|
|
678
|
+
* outcome as soon as Check is pressed, rather than needing to find it.
|
|
679
|
+
*/
|
|
680
|
+
declare function FillBlankInput({ value, onChange, checked, correct, onCheck, resultLabel, style, ...rest }: FillBlankInputProps): react.JSX.Element;
|
|
681
|
+
|
|
682
|
+
interface ScenarioBoxProps extends Omit<HTMLAttributes<HTMLDivElement>, 'style'> {
|
|
683
|
+
text: string;
|
|
684
|
+
style?: CSSProperties;
|
|
685
|
+
}
|
|
686
|
+
/** ScenarioBox — amber-tinted scenario framing shown above a practice prompt. */
|
|
687
|
+
declare function ScenarioBox({ text, style, ...rest }: ScenarioBoxProps): react.JSX.Element;
|
|
688
|
+
|
|
689
|
+
interface FlashcardProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'onClick' | 'style' | 'type' | 'aria-pressed'> {
|
|
690
|
+
flipped: boolean;
|
|
691
|
+
front: string;
|
|
692
|
+
back: string;
|
|
693
|
+
/** e.g. "BOX 3" or "NEW" */
|
|
694
|
+
boxLabel: string;
|
|
695
|
+
onFlip?: () => void;
|
|
696
|
+
}
|
|
697
|
+
/**
|
|
698
|
+
* Flashcard — flippable review card (front prompt / back answer) + box
|
|
699
|
+
* label.
|
|
700
|
+
*
|
|
701
|
+
* Added aria-pressed (the flip button is a real toggle — flipped/unflipped
|
|
702
|
+
* — so this is the correct ARIA state for it) and wrapped the revealed
|
|
703
|
+
* content in an aria-live region so screen reader users hear the answer as
|
|
704
|
+
* soon as the card flips, rather than needing to re-navigate into the
|
|
705
|
+
* button to discover it changed.
|
|
706
|
+
*/
|
|
707
|
+
declare function Flashcard({ flipped, front, back, boxLabel, onFlip, ...rest }: FlashcardProps): react.JSX.Element;
|
|
708
|
+
|
|
709
|
+
interface RateButtonsProps extends Omit<HTMLAttributes<HTMLDivElement>, 'style' | 'role' | 'aria-label'> {
|
|
710
|
+
/** Only enabled once the card is flipped. */
|
|
711
|
+
enabled?: boolean;
|
|
712
|
+
onAgain?: () => void;
|
|
713
|
+
onGood?: () => void;
|
|
714
|
+
onEasy?: () => void;
|
|
715
|
+
style?: CSSProperties;
|
|
716
|
+
}
|
|
717
|
+
/**
|
|
718
|
+
* RateButtons — Again / Good / Easy spaced-repetition rating row, revealed
|
|
719
|
+
* once flipped.
|
|
720
|
+
*
|
|
721
|
+
* The source "disabled" the row purely via `opacity` + `pointer-events:
|
|
722
|
+
* none` on the wrapping div, with no `disabled` attribute on the buttons
|
|
723
|
+
* themselves. `pointer-events: none` blocks mouse clicks but NOT keyboard
|
|
724
|
+
* activation — a keyboard user could Tab to these buttons and press
|
|
725
|
+
* Enter/Space to rate a card before flipping it, bypassing the entire
|
|
726
|
+
* intended flow. Added a real `disabled` to each button so it's actually
|
|
727
|
+
* inert, not just unclickable by mouse.
|
|
728
|
+
*/
|
|
729
|
+
declare function RateButtons({ enabled, onAgain, onGood, onEasy, style, ...rest }: RateButtonsProps): react.JSX.Element;
|
|
730
|
+
|
|
731
|
+
export { AnnotationPanel, type AnnotationPanelProps, AnnotationToggle, type AnnotationToggleProps, Avatar, type AvatarProps, Badge, type BadgeProps, BottomSheet, type BottomSheetProps, Button, type ButtonProps, CardHeader, type CardHeaderProps, CardShell, type CardShellProps, CodeCard, type CodeCardProps, ColorSwatch, type ColorSwatchProps, ContextMenu, type ContextMenuItem, type ContextMenuProps, type ContextMenuSwatch, DragHandle, type DragHandleProps, EquationBlock, type EquationBlockProps, ExportRow, type ExportRowProps, FillBlankInput, type FillBlankInputProps, Flashcard, type FlashcardProps, IconButton, type IconButtonProps, ImageCard, type ImageCardProps, ImportExportModal, type ImportExportModalProps, type ImportExportTab, IntuitionCallout, type IntuitionCalloutProps, McqOption, type McqOptionProps, type NavTabId, NavTabs, type NavTabsProps, NoteRow, type NoteRowProps, type NoteRowTag, QuizCard, type QuizCardProps, RateButtons, type RateButtonsProps, ReferencesCard, type ReferencesCardProps, RelatedCard, type RelatedCardItem, type RelatedCardProps, ScenarioBox, type ScenarioBoxProps, Sidebar, type SidebarProps, SolvedStatusButton, type SolvedStatusButtonProps, TableCard, type TableCardProps, TagChip, type TagChipProps, TextArea, type TextAreaProps, TextBlock, type TextBlockProps, TextInput, type TextInputProps, TocFab, type TocFabProps, type TocItem, TocRail, type TocRailProps, TocSheetList, type TocSheetListProps, TopicRow, type TopicRowProps, WalkthroughCard, type WalkthroughCardProps, type WalkthroughStep };
|