@ntmats/hc-ui 0.2.0 → 0.4.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/README.md +57 -3
- package/dist/index.d.ts +1518 -6
- package/dist/index.js +3178 -39
- package/dist/styles.css +2357 -2
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import { type ButtonHTMLAttributes, type HTMLAttributes, type InputHTMLAttributes, type ReactNode } from 'react';
|
|
2
|
-
export type SurfaceTone = 'page' | 'main' | 'content' | 'elevated' | 'overlay';
|
|
1
|
+
import { type ButtonHTMLAttributes, type Dispatch, type HTMLAttributes, type InputHTMLAttributes, type KeyboardEvent, type MouseEvent, type ReactElement, type ReactNode, type Ref, type SetStateAction } from 'react';
|
|
2
|
+
export type SurfaceTone = 'page' | 'main' | 'content' | 'elevated' | 'overlay' | 'card';
|
|
3
3
|
export interface SurfaceProps extends HTMLAttributes<HTMLDivElement> {
|
|
4
4
|
tone?: SurfaceTone;
|
|
5
|
+
/** Corner radius of the rounded tones (`content`, `elevated`, `overlay`, `card`): 16, 20 or 24px.
|
|
6
|
+
* Defaults to `md` (16px), and to `lg` (20px) for `card`, as HCUI's `surface` mixin. */
|
|
7
|
+
radius?: 'md' | 'lg' | 'xl';
|
|
5
8
|
}
|
|
6
9
|
/** Semantic color role only; placement, elevation and overlay behavior stay local. */
|
|
7
|
-
export declare function Surface({ tone, className, ...props }: SurfaceProps): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export declare function Surface({ tone, radius, className, ...props }: SurfaceProps): import("react/jsx-runtime").JSX.Element;
|
|
8
11
|
export interface SegmentedOption<T extends string> {
|
|
9
12
|
value: T;
|
|
10
13
|
label: string;
|
|
@@ -21,9 +24,16 @@ export interface SegmentedControlProps<T extends string> extends Omit<HTMLAttrib
|
|
|
21
24
|
orientation?: 'horizontal' | 'vertical';
|
|
22
25
|
/** Icon-only options: labels stay as accessible names and tooltips. */
|
|
23
26
|
hideLabels?: boolean;
|
|
27
|
+
/** `static` (default): the chosen option is raised in place. `slide`: one knob slides
|
|
28
|
+
* under the options, as HCUI's point cost toggle; options are laid out at equal width. */
|
|
29
|
+
indicator?: 'static' | 'slide';
|
|
30
|
+
/** Every option as wide as the widest one (or as `optionWidth`). */
|
|
31
|
+
equalWidth?: boolean;
|
|
32
|
+
/** Fixed width of each option in px; implies `equalWidth`. */
|
|
33
|
+
optionWidth?: number;
|
|
24
34
|
}
|
|
25
35
|
/** Controlled, single-choice control with radio semantics and roving focus. */
|
|
26
|
-
export declare function SegmentedControl<T extends string>({ label, value, options, onChange, density, orientation, hideLabels, className, ...props }: SegmentedControlProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
36
|
+
export declare function SegmentedControl<T extends string>({ label, value, options, onChange, density, orientation, hideLabels, indicator, equalWidth, optionWidth, className, style, ...props }: SegmentedControlProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
27
37
|
export type ButtonVariant = 'secondary' | 'primary' | 'outline' | 'danger';
|
|
28
38
|
export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
29
39
|
variant?: ButtonVariant;
|
|
@@ -46,18 +56,99 @@ export interface IconButtonProps extends Omit<ButtonHTMLAttributes<HTMLButtonEle
|
|
|
46
56
|
/** 36, 40 or 44px. The 36px size keeps a 44px touch target. */
|
|
47
57
|
size?: 'sm' | 'md' | 'lg';
|
|
48
58
|
tone?: 'primary' | 'secondary';
|
|
59
|
+
/** Fill: `muted` (default, HCUI `icon-button`), `surface` (surface with the card ring
|
|
60
|
+
* and shadow, HCUI's mobile theme button), `outline` (surface with a 1px border ring,
|
|
61
|
+
* HCUI's mobile sort button) or `ghost` (no fill until hover). */
|
|
62
|
+
variant?: 'muted' | 'surface' | 'outline' | 'ghost';
|
|
63
|
+
/** `circle` (default) or `rounded` (a 12px-radius square, HCUI's desktop utilities). */
|
|
64
|
+
shape?: 'circle' | 'rounded';
|
|
49
65
|
/** Toggle state (`aria-pressed`); omit for a plain action. */
|
|
50
66
|
selected?: boolean;
|
|
67
|
+
/** When set, the icon is keyed by it and enters with a short turn and fade each time it
|
|
68
|
+
* changes (and on first render), as HCUI's theme button swaps sun and moon. */
|
|
69
|
+
iconKey?: string | number;
|
|
51
70
|
}
|
|
52
71
|
/** Round icon-only button (back, close, options). */
|
|
53
72
|
export declare const IconButton: import("react").ForwardRefExoticComponent<IconButtonProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
54
|
-
|
|
73
|
+
interface ChipBaseProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
55
74
|
/** Toggle state for filters; omit for a plain action chip. */
|
|
56
75
|
selected?: boolean;
|
|
57
76
|
icon?: ReactNode;
|
|
77
|
+
/**
|
|
78
|
+
* `md` (default): 36px, 13px text (HCUI `chip`). `sm`: 36px, 12px text and 12px padding (HCUI's
|
|
79
|
+
* desktop filter pills and sort trigger). `xs`: 26px, 11px text, muted fill without ring (HCUI's
|
|
80
|
+
* desktop quick filters and chosen-value tags); selected turns to the contrast fill.
|
|
81
|
+
*/
|
|
82
|
+
size?: 'md' | 'sm' | 'xs';
|
|
83
|
+
/** `outline` (default), or `quiet`: lighter ring and secondary text, bolder once applied (HCUI's rule filter pills). */
|
|
84
|
+
variant?: 'outline' | 'quiet';
|
|
85
|
+
/** A filter with values applied: a 1.5px ring in the primary text color (and primary, bolder text when `quiet`). */
|
|
86
|
+
applied?: boolean;
|
|
87
|
+
/**
|
|
88
|
+
* Muted number after the label, such as how many results a quick filter keeps. A number
|
|
89
|
+
* shows as given, 0 included (HCUI's quick filters show "0"); hidden when `undefined`, `null`, `false` or `''`.
|
|
90
|
+
*/
|
|
91
|
+
count?: ReactNode;
|
|
92
|
+
/**
|
|
93
|
+
* Filled badge after the label: how many values a filter has applied. Shows 0 too; pass
|
|
94
|
+
* `undefined` (e.g. `badge={n || undefined}`) to hide it when nothing is applied.
|
|
95
|
+
*/
|
|
96
|
+
badge?: ReactNode;
|
|
97
|
+
/**
|
|
98
|
+
* Down chevron at the end. On by default when the chip opens a popup (`aria-haspopup`,
|
|
99
|
+
* which `Menu` and `Popover` give their trigger); `aria-expanded="true"` draws the ring.
|
|
100
|
+
*/
|
|
101
|
+
chevron?: boolean;
|
|
102
|
+
/**
|
|
103
|
+
* Adds a separate remove button (×) after the text. The chip then renders as a `span`
|
|
104
|
+
* holding the main button (ref, `onClick`, `aria-pressed`; a plain `span` when there is
|
|
105
|
+
* no `onClick`, as a chosen-value tag) and the remove button. `className` and `style` go on the `span`.
|
|
106
|
+
* Requires `clearLabel`.
|
|
107
|
+
*/
|
|
108
|
+
onClear?: (event: MouseEvent<HTMLButtonElement>) => void;
|
|
109
|
+
/** Accessible name and tooltip of the remove button (e.g. "Remover Batman"); required with `onClear`. */
|
|
110
|
+
clearLabel?: string;
|
|
58
111
|
}
|
|
59
|
-
/**
|
|
112
|
+
/** `Chip` props: with `onClear`, `clearLabel` is required, so the remove button always has a name. */
|
|
113
|
+
export type ChipProps = ChipBaseProps & ({
|
|
114
|
+
onClear?: undefined;
|
|
115
|
+
clearLabel?: string;
|
|
116
|
+
} | {
|
|
117
|
+
onClear?: (event: MouseEvent<HTMLButtonElement>) => void;
|
|
118
|
+
clearLabel: string;
|
|
119
|
+
});
|
|
120
|
+
/**
|
|
121
|
+
* Filter or option chip. Selected chips are filled with the accent color. As a filter
|
|
122
|
+
* trigger it takes a `badge` with the number of values applied, `applied`, and the
|
|
123
|
+
* chevron and ring of an open popup; `onClear` adds a remove button.
|
|
124
|
+
*/
|
|
60
125
|
export declare const Chip: import("react").ForwardRefExoticComponent<ChipProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
126
|
+
export interface FilterBarProps extends HTMLAttributes<HTMLDivElement> {
|
|
127
|
+
/** Accessible name of the group (e.g. "Filtros"). */
|
|
128
|
+
label: string;
|
|
129
|
+
/** Before the filters, on the same line (a search field, "Filtros avançados"). */
|
|
130
|
+
start?: ReactNode;
|
|
131
|
+
/** After the filters, at the end of the line (a sort menu, a view toggle). */
|
|
132
|
+
end?: ReactNode;
|
|
133
|
+
/** Under the line: chosen-value tags, "Limpar tudo". */
|
|
134
|
+
footer?: ReactNode;
|
|
135
|
+
/**
|
|
136
|
+
* `wrap`: the filters wrap onto more lines (desktop). `scroll`: one line that scrolls
|
|
137
|
+
* sideways with a hidden scrollbar (phones). `auto` (default): `scroll` below 960px and
|
|
138
|
+
* `wrap` from 960px, by a CSS media query (HCUI's cut).
|
|
139
|
+
*/
|
|
140
|
+
layout?: 'wrap' | 'scroll' | 'auto';
|
|
141
|
+
/** Space between filters: 8px (`comfortable`, default) or 4px (`compact`, HCUI's desktop quick filters). */
|
|
142
|
+
density?: 'comfortable' | 'compact';
|
|
143
|
+
/** A band that sticks to the top of the scrolling panel, with the surface fill and 8px above and below (HCUI's desktop filter bar). The offset is `--hc-filter-bar-top`. */
|
|
144
|
+
sticky?: boolean;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Layout of a row of filters (`Chip`s) with optional `start`, `end` and `footer` slots.
|
|
148
|
+
* Presentation only: which filters exist, their values, the search rules and persistence
|
|
149
|
+
* are the caller's. A scrolling row bleeds into the page gutter by `--hc-filter-bar-bleed`.
|
|
150
|
+
*/
|
|
151
|
+
export declare const FilterBar: import("react").ForwardRefExoticComponent<FilterBarProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
61
152
|
export interface FieldProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size'> {
|
|
62
153
|
/** Accessible name. Visible above the field unless `hideLabel` is set. */
|
|
63
154
|
label: string;
|
|
@@ -84,3 +175,1424 @@ export interface SkeletonProps extends HTMLAttributes<HTMLSpanElement> {
|
|
|
84
175
|
* enclosing region announces loading once (role="status" + label).
|
|
85
176
|
*/
|
|
86
177
|
export declare function Skeleton({ width, height, circle, className, style, ...props }: SkeletonProps): import("react/jsx-runtime").JSX.Element;
|
|
178
|
+
interface MeterOwnProps extends Omit<HTMLAttributes<HTMLSpanElement>, 'children' | 'role'> {
|
|
179
|
+
/** Current amount, such as a team's points. May exceed `max`. */
|
|
180
|
+
value: number;
|
|
181
|
+
/** The limit the value is measured against; the minimum is 0. */
|
|
182
|
+
max: number;
|
|
183
|
+
/** Spoken value (`aria-valuetext`); defaults to `"<value> / <max>"`. */
|
|
184
|
+
valueText?: string;
|
|
185
|
+
/** Bar height in px: 3 (HCUI's mobile team bar), 4 (desktop sidebar draft) or 5 (team rules bar). */
|
|
186
|
+
thickness?: 3 | 4 | 5;
|
|
187
|
+
/** Track behind the fill: `muted` (bg-muted), `border` (color-border) or `none` (fill only). */
|
|
188
|
+
track?: 'muted' | 'border' | 'none';
|
|
189
|
+
/** Leading end of the fill: rounded, or cut square against the track. */
|
|
190
|
+
cap?: 'round' | 'square';
|
|
191
|
+
/**
|
|
192
|
+
* Above the limit: `clamp` fills the whole bar; `mark` scales the bar to the value
|
|
193
|
+
* and draws a 2px line where the limit is, in the text color so it shows over the danger fill.
|
|
194
|
+
*/
|
|
195
|
+
overflow?: 'clamp' | 'mark';
|
|
196
|
+
/** `auto` turns `over` (danger) when value > max; `default` and `over` force a tone. */
|
|
197
|
+
tone?: 'auto' | 'default' | 'over';
|
|
198
|
+
/** Transition the fill's width (none under reduced motion). */
|
|
199
|
+
animated?: boolean;
|
|
200
|
+
}
|
|
201
|
+
export type MeterProps = MeterOwnProps & (
|
|
202
|
+
/** `label` is the accessible name; the visible text ("375 / 300 pts") stays with the caller. */
|
|
203
|
+
{
|
|
204
|
+
label: string;
|
|
205
|
+
decorative?: false;
|
|
206
|
+
}
|
|
207
|
+
/** Hidden from assistive technology, for when the numbers are already next to it. */
|
|
208
|
+
| {
|
|
209
|
+
decorative: true;
|
|
210
|
+
label?: string;
|
|
211
|
+
});
|
|
212
|
+
/**
|
|
213
|
+
* Bar of a value against a limit (`role="meter"`), with an over-the-limit tone. It
|
|
214
|
+
* renders only spans, so it fits inside a button. Placement (absolute, grid) is the caller's.
|
|
215
|
+
*/
|
|
216
|
+
export declare const Meter: import("react").ForwardRefExoticComponent<MeterProps & import("react").RefAttributes<HTMLSpanElement>>;
|
|
217
|
+
export interface CheckboxProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size' | 'type'> {
|
|
218
|
+
/** 20px (touch lists, sheets) or 16px (dense desktop lists). */
|
|
219
|
+
size?: 'sm' | 'md';
|
|
220
|
+
/** Mixed state. Visual only: the value still comes from `checked`. */
|
|
221
|
+
indeterminate?: boolean;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Native checkbox laid transparently over a drawn box, so keyboard, focus, form
|
|
225
|
+
* and `<label>` behave as usual. Wrap it in a `<label>` (the whole row becomes the
|
|
226
|
+
* target) or pass `aria-label`; the caller supplies the text.
|
|
227
|
+
*/
|
|
228
|
+
export declare const Checkbox: import("react").ForwardRefExoticComponent<CheckboxProps & import("react").RefAttributes<HTMLInputElement>>;
|
|
229
|
+
export interface DialogProps extends Omit<HTMLAttributes<HTMLDialogElement>, 'title' | 'open' | 'onClose' | 'onCancel'> {
|
|
230
|
+
/** Controlled: the dialog shows while `open` is true and plays its exit before unmounting. */
|
|
231
|
+
open: boolean;
|
|
232
|
+
/**
|
|
233
|
+
* Asked to close: Esc and a backdrop click (when `dismissible`) and the close
|
|
234
|
+
* button. The dialog stays open until the caller sets `open` to false.
|
|
235
|
+
*/
|
|
236
|
+
onClose: () => void;
|
|
237
|
+
/** Visible heading; it names the dialog (`aria-labelledby`). */
|
|
238
|
+
title: ReactNode;
|
|
239
|
+
/** Short text under the title; it describes the dialog (`aria-describedby`). */
|
|
240
|
+
description?: ReactNode;
|
|
241
|
+
children?: ReactNode;
|
|
242
|
+
/** Slot for the buttons (Cancel | Confirm). */
|
|
243
|
+
actions?: ReactNode;
|
|
244
|
+
/** Shows a close button with this accessible name. No close button by default. */
|
|
245
|
+
closeLabel?: string;
|
|
246
|
+
/** Icon for the close button; an inline X by default. */
|
|
247
|
+
closeIcon?: ReactNode;
|
|
248
|
+
/** `sm`: 400px confirmation (HCUI ConfirmDialog). `md`: 440px panel with header, scrolling body and footer (HCUI Sheet on desktop). */
|
|
249
|
+
size?: 'sm' | 'md';
|
|
250
|
+
/** Esc and a backdrop click ask to close. Default true. */
|
|
251
|
+
dismissible?: boolean;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Centered modal dialog on the native `<dialog>` and `showModal()`: the browser
|
|
255
|
+
* keeps it in the top layer, makes the rest of the page inert and traps Tab.
|
|
256
|
+
* The caller owns `open` and the texts; the dialog only asks to close.
|
|
257
|
+
*/
|
|
258
|
+
export declare const Dialog: import("react").ForwardRefExoticComponent<DialogProps & import("react").RefAttributes<HTMLDialogElement>>;
|
|
259
|
+
export type SheetPresentation = 'bottom' | 'center' | 'auto';
|
|
260
|
+
export interface SheetProps extends Omit<DialogProps, 'size' | 'actions' | 'description' | 'closeLabel'> {
|
|
261
|
+
/** Accessible name of the close button, which every sheet shows. */
|
|
262
|
+
closeLabel: string;
|
|
263
|
+
/** Slot under the scrolling body, for the buttons (Clear | Done). */
|
|
264
|
+
footer?: ReactNode;
|
|
265
|
+
/**
|
|
266
|
+
* `bottom`: rises from the bottom edge, full width, with a grabber and drag to close.
|
|
267
|
+
* `center`: the centered panel (`Dialog size="md"`). `auto` (default): `bottom` below
|
|
268
|
+
* 960px and `center` from 960px, by a CSS media query (HCUI's cut).
|
|
269
|
+
*/
|
|
270
|
+
presentation?: SheetPresentation;
|
|
271
|
+
/** Maximum width of the centered panel, in px. Default 440. */
|
|
272
|
+
width?: number;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Modal sheet on the `Dialog` base (native `<dialog>`, `showModal()`): the same focus,
|
|
276
|
+
* Tab trap, Esc, focus return, scroll lock, stacking and controlled `open`/`onClose`.
|
|
277
|
+
* At the bottom it has a grabber, drag to close (past 20% of its height or a quick
|
|
278
|
+
* flick) and drag up from the grabber or header to full height; content that has
|
|
279
|
+
* scrolled down scrolls back before the sheet moves.
|
|
280
|
+
*/
|
|
281
|
+
export declare const Sheet: import("react").ForwardRefExoticComponent<SheetProps & import("react").RefAttributes<HTMLDialogElement>>;
|
|
282
|
+
export interface SheetOption<T extends string> {
|
|
283
|
+
value: T;
|
|
284
|
+
label: ReactNode;
|
|
285
|
+
/** Second line, in a muted color. */
|
|
286
|
+
hint?: ReactNode;
|
|
287
|
+
/** Decorative icon before the text; pass it already sized (18px in HCUI). */
|
|
288
|
+
icon?: ReactNode;
|
|
289
|
+
disabled?: boolean;
|
|
290
|
+
}
|
|
291
|
+
interface SheetChoiceProps<T extends string> extends Omit<HTMLAttributes<HTMLDivElement>, 'onChange' | 'role'> {
|
|
292
|
+
/** Accessible name of the group. */
|
|
293
|
+
label: string;
|
|
294
|
+
/** The chosen value; one that matches no option leaves the group with none chosen. */
|
|
295
|
+
value: T;
|
|
296
|
+
/** NoInfer: `T` comes from `value` and `options`, so a state setter cannot widen it to string. */
|
|
297
|
+
onChange: (value: NoInfer<T>) => void;
|
|
298
|
+
}
|
|
299
|
+
export interface SheetOptionsProps<T extends string> extends SheetChoiceProps<T> {
|
|
300
|
+
options: readonly SheetOption<T>[];
|
|
301
|
+
/**
|
|
302
|
+
* `card` (default): rows on a `bg-muted-2` card, as HCUI's SheetOptions. `list`: plain
|
|
303
|
+
* 48px rows divided by a line, the chosen one bolder with a 20px check in the text color,
|
|
304
|
+
* as the list of HCUI's mobile sort sheet (`SortButton`).
|
|
305
|
+
*/
|
|
306
|
+
variant?: 'card' | 'list';
|
|
307
|
+
}
|
|
308
|
+
/** Single-choice list of rows with a check (HCUI's SheetOptions), for inside a sheet. */
|
|
309
|
+
export declare function SheetOptions<T extends string>({ label, options, value, onChange, variant, className, ...props }: SheetOptionsProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
310
|
+
export interface SheetChipsProps<T extends string> extends SheetChoiceProps<T> {
|
|
311
|
+
options: readonly {
|
|
312
|
+
value: T;
|
|
313
|
+
label: ReactNode;
|
|
314
|
+
disabled?: boolean;
|
|
315
|
+
}[];
|
|
316
|
+
}
|
|
317
|
+
/** Single-choice group of `Chip`s that wrap (HCUI's SheetChips), for inside a sheet. */
|
|
318
|
+
export declare function SheetChips<T extends string>({ label, options, value, onChange, className, ...props }: SheetChipsProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
319
|
+
export type PopupSide = 'bottom' | 'top';
|
|
320
|
+
export type PopupAlign = 'start' | 'end';
|
|
321
|
+
/** Props a trigger receives: spread them on the element that opens the popup. */
|
|
322
|
+
export interface PopupTriggerProps {
|
|
323
|
+
ref: (node: HTMLElement | null) => void;
|
|
324
|
+
'aria-haspopup': 'menu' | 'dialog';
|
|
325
|
+
'aria-expanded': boolean;
|
|
326
|
+
'aria-controls': string | undefined;
|
|
327
|
+
onClick: (event: MouseEvent<HTMLElement>) => void;
|
|
328
|
+
onKeyDown: (event: KeyboardEvent<HTMLElement>) => void;
|
|
329
|
+
}
|
|
330
|
+
/** An element (it gets the props merged in) or a render function that spreads them. */
|
|
331
|
+
export type PopupTrigger = ReactElement | ((props: PopupTriggerProps, state: {
|
|
332
|
+
open: boolean;
|
|
333
|
+
}) => ReactNode);
|
|
334
|
+
interface PopupBaseProps {
|
|
335
|
+
/** Controlled open state; omit it (and use `defaultOpen`) to let the popup own it. */
|
|
336
|
+
open?: boolean;
|
|
337
|
+
defaultOpen?: boolean;
|
|
338
|
+
onOpenChange?: (open: boolean) => void;
|
|
339
|
+
/** Preferred side; it flips when there is no room. Default `bottom`. */
|
|
340
|
+
side?: PopupSide;
|
|
341
|
+
/** Edge shared with the trigger; it flips when there is no room. Default `end`. */
|
|
342
|
+
align?: PopupAlign;
|
|
343
|
+
/**
|
|
344
|
+
* Render in a portal: `true` uses the trigger's open `<dialog>` (so it stays in the
|
|
345
|
+
* top layer) or `document.body`; an element is used as is. The portal keeps
|
|
346
|
+
* `data-hc-ds` and the trigger's `data-theme`. Default false: the popup is placed
|
|
347
|
+
* right after the trigger and inherits everything.
|
|
348
|
+
*/
|
|
349
|
+
portal?: boolean | HTMLElement;
|
|
350
|
+
/** Theme for the portal; read from the trigger's scope by default. */
|
|
351
|
+
theme?: 'light' | 'dark';
|
|
352
|
+
/** Class of the wrapper around the trigger (the popup's positioning box). */
|
|
353
|
+
anchorClassName?: string;
|
|
354
|
+
}
|
|
355
|
+
type Rect = {
|
|
356
|
+
top: number;
|
|
357
|
+
right: number;
|
|
358
|
+
bottom: number;
|
|
359
|
+
left: number;
|
|
360
|
+
};
|
|
361
|
+
/** Keeps the preferred side and edge when the popup fits; otherwise flips to the roomier one. */
|
|
362
|
+
export declare function placePopup(trigger: Rect, size: {
|
|
363
|
+
width: number;
|
|
364
|
+
height: number;
|
|
365
|
+
}, boundary: Rect, side: PopupSide, align: PopupAlign): {
|
|
366
|
+
side: PopupSide;
|
|
367
|
+
align: PopupAlign;
|
|
368
|
+
};
|
|
369
|
+
export interface MenuProps extends PopupBaseProps, Omit<HTMLAttributes<HTMLDivElement>, 'role' | 'children'> {
|
|
370
|
+
/** Accessible name of the menu (`aria-label`). */
|
|
371
|
+
label: string;
|
|
372
|
+
/** The button that opens it. It gets `aria-haspopup="menu"`, `aria-expanded`, `aria-controls` and the handlers. */
|
|
373
|
+
trigger: PopupTrigger;
|
|
374
|
+
children: ReactNode;
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Menu button: actions (`MenuItem`), one choice (`MenuRadioGroup` + `MenuItemRadio`),
|
|
378
|
+
* toggles (`MenuItemCheckbox`), `MenuSeparator` and `MenuLabel`. Arrows, Home/End and
|
|
379
|
+
* typing move between items; Enter/Space choose; Esc closes and returns focus to the
|
|
380
|
+
* trigger; Tab closes and moves on; a press outside closes.
|
|
381
|
+
*/
|
|
382
|
+
export declare const Menu: import("react").ForwardRefExoticComponent<MenuProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
383
|
+
interface MenuItemBaseProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'role' | 'onSelect'> {
|
|
384
|
+
/** Decorative icon at the end of the row; pass it already sized (15px in HCUI). */
|
|
385
|
+
icon?: ReactNode;
|
|
386
|
+
/** Second line in muted, smaller text. */
|
|
387
|
+
hint?: ReactNode;
|
|
388
|
+
/** Destructive action: drawn in the danger color. */
|
|
389
|
+
danger?: boolean;
|
|
390
|
+
}
|
|
391
|
+
export interface MenuItemProps extends MenuItemBaseProps {
|
|
392
|
+
/** Runs the action. Call `event.preventDefault()` to keep the menu open. */
|
|
393
|
+
onSelect?: (event: MouseEvent<HTMLButtonElement>) => void;
|
|
394
|
+
}
|
|
395
|
+
/** An action (`menuitem`). It closes the menu; focus returns to the trigger unless the action moved it. */
|
|
396
|
+
export declare const MenuItem: import("react").ForwardRefExoticComponent<MenuItemProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
397
|
+
export interface MenuRadioGroupProps<T extends string> extends Omit<HTMLAttributes<HTMLDivElement>, 'role' | 'onChange'> {
|
|
398
|
+
value: T;
|
|
399
|
+
onValueChange: (value: NoInfer<T>) => void;
|
|
400
|
+
/** Accessible name of the group, when the menu holds more than one. */
|
|
401
|
+
label?: string;
|
|
402
|
+
}
|
|
403
|
+
/** One choice among `MenuItemRadio`s, such as a sort order. */
|
|
404
|
+
export declare function MenuRadioGroup<T extends string>({ value, onValueChange, label, className, children, ...props }: MenuRadioGroupProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
405
|
+
export interface MenuItemRadioProps extends MenuItemBaseProps {
|
|
406
|
+
value: string;
|
|
407
|
+
}
|
|
408
|
+
/** A `menuitemradio`: the chosen one is bold with a check. Choosing closes the menu and returns focus to the trigger. */
|
|
409
|
+
export declare const MenuItemRadio: import("react").ForwardRefExoticComponent<MenuItemRadioProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
410
|
+
export interface MenuItemCheckboxProps extends MenuItemBaseProps {
|
|
411
|
+
checked: boolean;
|
|
412
|
+
onCheckedChange: (checked: boolean) => void;
|
|
413
|
+
/** Close after toggling. Default false, so several toggles take one opening. */
|
|
414
|
+
closeOnSelect?: boolean;
|
|
415
|
+
}
|
|
416
|
+
/** A `menuitemcheckbox`: toggles in place, with a check while on. */
|
|
417
|
+
export declare const MenuItemCheckbox: import("react").ForwardRefExoticComponent<MenuItemCheckboxProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
418
|
+
/** A line between groups of items. */
|
|
419
|
+
export declare function MenuSeparator({ className, ...props }: Omit<HTMLAttributes<HTMLDivElement>, 'role'>): import("react/jsx-runtime").JSX.Element;
|
|
420
|
+
/** Plain text inside the menu, such as a question before a destructive choice. Not focusable. */
|
|
421
|
+
export declare function MenuLabel({ className, ...props }: HTMLAttributes<HTMLDivElement>): import("react/jsx-runtime").JSX.Element;
|
|
422
|
+
export interface SortOption<T extends string> {
|
|
423
|
+
value: T;
|
|
424
|
+
label: string;
|
|
425
|
+
/** Second line in the menu. */
|
|
426
|
+
hint?: ReactNode;
|
|
427
|
+
disabled?: boolean;
|
|
428
|
+
}
|
|
429
|
+
export interface SortMenuProps<T extends string> extends Omit<MenuProps, 'label' | 'trigger' | 'children' | 'onChange'> {
|
|
430
|
+
/** Name of the menu, and of the trigger with the current choice ("Ordenar: A–Z"). */
|
|
431
|
+
label: string;
|
|
432
|
+
value: T;
|
|
433
|
+
options: readonly SortOption<T>[];
|
|
434
|
+
onChange: (value: NoInfer<T>) => void;
|
|
435
|
+
/** Marks the choice as a filter in effect (the ring of an applied filter). */
|
|
436
|
+
applied?: boolean;
|
|
437
|
+
/** Trigger size, as `Chip`. Default `sm` (HCUI's desktop sort trigger). */
|
|
438
|
+
size?: ChipProps['size'];
|
|
439
|
+
/** Class of the trigger `Chip`. */
|
|
440
|
+
triggerClassName?: string;
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Sort control: a `Chip` showing the current choice that opens a `Menu` with a
|
|
444
|
+
* `MenuRadioGroup` (HCUI's `DesktopSortMenu`). Options and value come from the caller.
|
|
445
|
+
* On phones, compose `Sheet` with `SheetOptions` behind an `IconButton` instead.
|
|
446
|
+
*/
|
|
447
|
+
export declare function SortMenu<T extends string>({ label, value, options, onChange, applied, size, triggerClassName, ...props }: SortMenuProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
448
|
+
export interface PopoverProps extends PopupBaseProps, Omit<HTMLAttributes<HTMLDivElement>, 'role' | 'children'> {
|
|
449
|
+
/** Accessible name of the dialog (`aria-label`). */
|
|
450
|
+
label: string;
|
|
451
|
+
/** The button that opens it. It gets `aria-haspopup="dialog"`, `aria-expanded`, `aria-controls` and the click handler. */
|
|
452
|
+
trigger: PopupTrigger;
|
|
453
|
+
/** Free content; a function receives `close` (focus goes back to the trigger). */
|
|
454
|
+
children: ReactNode | ((api: {
|
|
455
|
+
close: () => void;
|
|
456
|
+
}) => ReactNode);
|
|
457
|
+
/** Move focus to the first focusable element inside when it opens. Default false: focus stays on the trigger. */
|
|
458
|
+
autoFocus?: boolean;
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Non-modal dialog anchored to its trigger, with the same placement and dismissal as
|
|
462
|
+
* `Menu`: Esc closes and returns focus to the trigger, a press outside closes. Tab is
|
|
463
|
+
* not trapped. The caller supplies the content and its texts. Esc is taken before it
|
|
464
|
+
* reaches the content (so an enclosing Dialog stays open): a field inside cannot use it.
|
|
465
|
+
*/
|
|
466
|
+
export declare const Popover: import("react").ForwardRefExoticComponent<PopoverProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
467
|
+
export type TableSortDirection = 'ascending' | 'descending';
|
|
468
|
+
export interface TableColumn<Row> {
|
|
469
|
+
/** Stable id of the column; it is what `sort.key` and `onSort` name. */
|
|
470
|
+
key: string;
|
|
471
|
+
/** Header text (or node). For a sortable column it is the name of the sort button. */
|
|
472
|
+
header: ReactNode;
|
|
473
|
+
/** What the column shows for a row: free content (an avatar and a name, a formatted number). */
|
|
474
|
+
cell: (row: Row, index: number) => ReactNode;
|
|
475
|
+
/** `start` (default), `center` or `end`. Numeric columns default to `end`. */
|
|
476
|
+
align?: 'start' | 'center' | 'end';
|
|
477
|
+
/** Figures line up: tabular numbers, aligned to the end unless `align` says otherwise. */
|
|
478
|
+
numeric?: boolean;
|
|
479
|
+
/** Column width (a number is px), set on the header cell. Cells are border-box. */
|
|
480
|
+
width?: number | string;
|
|
481
|
+
/** The cell names its row: rendered as `<th scope="row">`. With `onRowClick`, its content goes in the row button. */
|
|
482
|
+
rowHeader?: boolean;
|
|
483
|
+
/** The header becomes a button that calls `onSort(key)`, and the cell gets `aria-sort`. */
|
|
484
|
+
sortable?: boolean;
|
|
485
|
+
/** Text color of the header and the cells: `muted` (a rank) or `secondary` (a category). */
|
|
486
|
+
tone?: 'muted' | 'secondary';
|
|
487
|
+
/** Class for the header and every cell of the column (e.g. to hide it in a narrow container). */
|
|
488
|
+
className?: string;
|
|
489
|
+
}
|
|
490
|
+
export interface TableProps<Row> extends Omit<HTMLAttributes<HTMLTableElement>, 'children' | 'onClick'> {
|
|
491
|
+
columns: readonly TableColumn<Row>[];
|
|
492
|
+
rows: readonly Row[];
|
|
493
|
+
/** Stable key of a row. */
|
|
494
|
+
rowKey: (row: Row, index: number) => string;
|
|
495
|
+
/** The column the rows are sorted by, and in which direction. The order of `rows` is the caller's. */
|
|
496
|
+
sort?: {
|
|
497
|
+
key: string;
|
|
498
|
+
direction: TableSortDirection;
|
|
499
|
+
} | null;
|
|
500
|
+
/** A sortable header was pressed. The caller decides the new order (toggle, default direction). */
|
|
501
|
+
onSort?: (key: string) => void;
|
|
502
|
+
/** The header row sticks to the top of the scrolling ancestor, at `--hc-table-sticky-top` (0 by default). */
|
|
503
|
+
stickyHeader?: boolean;
|
|
504
|
+
/** Rows react to a press, and the `rowHeader` cell becomes a button for the keyboard. */
|
|
505
|
+
onRowClick?: (row: Row, index: number) => void;
|
|
506
|
+
/** Key of the selected row (the one open in a side panel): filled and marked `aria-current` on its row button. */
|
|
507
|
+
selectedKey?: string | null;
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* Dense data table: a semantic `<table>` whose columns come from props. Presentation only:
|
|
511
|
+
* the rows, their order and what a press does are the caller's. Sortable headers are buttons
|
|
512
|
+
* and carry `aria-sort`; `onSort` reports the press.
|
|
513
|
+
*/
|
|
514
|
+
export declare const Table: <Row>(props: TableProps<Row> & {
|
|
515
|
+
ref?: Ref<HTMLTableElement>;
|
|
516
|
+
}) => ReactElement;
|
|
517
|
+
type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
|
|
518
|
+
export interface PageHeaderProps extends Omit<HTMLAttributes<HTMLElement>, 'title'> {
|
|
519
|
+
/** Page title. */
|
|
520
|
+
title: ReactNode;
|
|
521
|
+
/** Heading level of the title: 1 (default), 2 or 3. The look does not change. */
|
|
522
|
+
level?: HeadingLevel;
|
|
523
|
+
/** Muted number after the title, inside the heading (e.g. how many teams). Hidden when 0, null or false. */
|
|
524
|
+
count?: ReactNode;
|
|
525
|
+
/** At the end of the line: buttons, a menu. */
|
|
526
|
+
actions?: ReactNode;
|
|
527
|
+
/** Keeps the heading for assistive technology only, where the page shows its own title. */
|
|
528
|
+
visuallyHidden?: boolean;
|
|
529
|
+
}
|
|
530
|
+
/** Desktop page header: one line with the title (22px, semibold) and actions at the end. */
|
|
531
|
+
export declare const PageHeader: import("react").ForwardRefExoticComponent<PageHeaderProps & import("react").RefAttributes<HTMLElement>>;
|
|
532
|
+
export interface PanelHeaderStep {
|
|
533
|
+
label: string;
|
|
534
|
+
/** Goes back to this step. The last step is the current one and is never a button. */
|
|
535
|
+
onSelect?: () => void;
|
|
536
|
+
}
|
|
537
|
+
export interface PanelHeaderProps extends Omit<HTMLAttributes<HTMLElement>, 'title'> {
|
|
538
|
+
/** Panel or section title. With `trail`, the last step is the title instead. */
|
|
539
|
+
title?: ReactNode;
|
|
540
|
+
/** Heading level of the title: 2 by default. The look does not change. */
|
|
541
|
+
level?: HeadingLevel;
|
|
542
|
+
/**
|
|
543
|
+
* `bar` (default): the header bar of a side panel (52px, a line under it, 13px text).
|
|
544
|
+
* `section`: a section title (17px, HCUI's `section-title`), with no bar.
|
|
545
|
+
*/
|
|
546
|
+
variant?: 'bar' | 'section';
|
|
547
|
+
/** Small uppercase line above the title (HCUI's `eyebrow`). */
|
|
548
|
+
eyebrow?: ReactNode;
|
|
549
|
+
/** Muted line under the title. */
|
|
550
|
+
subtitle?: ReactNode;
|
|
551
|
+
/** The subtitle follows the title on the same line (baseline), cut with an ellipsis, instead of under it. */
|
|
552
|
+
subtitleInline?: boolean;
|
|
553
|
+
/** Decorative icon before the title, in a 36px muted circle (`section`); pass it sized (18px in HCUI). */
|
|
554
|
+
icon?: ReactNode;
|
|
555
|
+
/** Steps of a trail (breadcrumb): earlier steps go back with `onSelect`; the last is the title (`aria-current="location"`). */
|
|
556
|
+
trail?: readonly PanelHeaderStep[];
|
|
557
|
+
/** Accessible name of the trail's `nav` (e.g. "Trilha do painel"). */
|
|
558
|
+
trailLabel?: string;
|
|
559
|
+
/** At the end of the line, before the close button. */
|
|
560
|
+
actions?: ReactNode;
|
|
561
|
+
/** Shows a close button (an inline X, or `closeIcon`) that calls it. */
|
|
562
|
+
onClose?: () => void;
|
|
563
|
+
/** Accessible name of the close button; give it with `onClose` (e.g. "Fechar painel"). */
|
|
564
|
+
closeLabel?: string;
|
|
565
|
+
/** Tooltip of the close button, when it says more than the name (e.g. "Fechar painel (Esc)"). */
|
|
566
|
+
closeTitle?: string;
|
|
567
|
+
closeIcon?: ReactNode;
|
|
568
|
+
}
|
|
569
|
+
/** Header of a panel or section: title (or a trail), optional eyebrow, subtitle, icon, actions and close button. */
|
|
570
|
+
export declare const PanelHeader: import("react").ForwardRefExoticComponent<PanelHeaderProps & import("react").RefAttributes<HTMLElement>>;
|
|
571
|
+
export type ListRowProps = ButtonHTMLAttributes<HTMLButtonElement>;
|
|
572
|
+
/**
|
|
573
|
+
* A tappable list row (HCUI's `list-row` mixin): full width, at least 56px, 8px 16px of padding,
|
|
574
|
+
* a 12px gap between its children, a translucent veil on hover and press and the focus ring inside.
|
|
575
|
+
* The content and its semantics (`aria-pressed`, `role="option"`) are the caller's.
|
|
576
|
+
*/
|
|
577
|
+
export declare const ListRow: import("react").ForwardRefExoticComponent<ListRowProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
578
|
+
export interface CommandPaletteItem {
|
|
579
|
+
/** Unique among all items. */
|
|
580
|
+
key: string;
|
|
581
|
+
label: ReactNode;
|
|
582
|
+
/** Second, muted line (code, points, a description), cut with an ellipsis. */
|
|
583
|
+
detail?: ReactNode;
|
|
584
|
+
/** Slot in a 32px box before the text: an avatar, an icon, a swatch. */
|
|
585
|
+
icon?: ReactNode;
|
|
586
|
+
/** Enter or a click. The palette does not close by itself: close it here when it should. */
|
|
587
|
+
onSelect: () => void;
|
|
588
|
+
/** ⌘Enter / Ctrl+Enter (HCUI: add the unit to the team). Without it, ⌘Enter selects. */
|
|
589
|
+
onAltSelect?: () => void;
|
|
590
|
+
/** Replaces the palette's `altHint` for this item. */
|
|
591
|
+
altHint?: ReactNode;
|
|
592
|
+
}
|
|
593
|
+
export interface CommandPaletteGroup {
|
|
594
|
+
key: string;
|
|
595
|
+
/** Small uppercase title above the group; it names the group. Without it the items follow the previous group. */
|
|
596
|
+
heading?: ReactNode;
|
|
597
|
+
items: readonly CommandPaletteItem[];
|
|
598
|
+
}
|
|
599
|
+
export interface CommandPaletteHint {
|
|
600
|
+
/** Each key is drawn in its own `<kbd>`. */
|
|
601
|
+
keys: readonly ReactNode[];
|
|
602
|
+
label: ReactNode;
|
|
603
|
+
}
|
|
604
|
+
export interface CommandPaletteProps extends Omit<HTMLAttributes<HTMLDialogElement>, 'open' | 'onClose' | 'onCancel' | 'children' | 'onChange'> {
|
|
605
|
+
/** Controlled: shown while true. Esc and a backdrop click call `onClose`. */
|
|
606
|
+
open: boolean;
|
|
607
|
+
onClose: () => void;
|
|
608
|
+
/** Accessible name of the dialog and of the list. */
|
|
609
|
+
label: string;
|
|
610
|
+
/** Accessible name of the search field; `label` by default. */
|
|
611
|
+
inputLabel?: string;
|
|
612
|
+
placeholder?: string;
|
|
613
|
+
/** The search text (controlled). A new query makes the first item active. */
|
|
614
|
+
query: string;
|
|
615
|
+
onQueryChange: (query: string) => void;
|
|
616
|
+
/** Results already searched, filtered and capped by the caller, in order. */
|
|
617
|
+
groups: readonly CommandPaletteGroup[];
|
|
618
|
+
/** Icon at the start of the field; Iconoir's Search (20px) by default. */
|
|
619
|
+
searchIcon?: ReactNode;
|
|
620
|
+
/** While searching: this text shows at the end of the field and the list is `aria-busy`. */
|
|
621
|
+
loading?: ReactNode;
|
|
622
|
+
/** Message at the top of the list (nothing found); the caller decides when. */
|
|
623
|
+
empty?: ReactNode;
|
|
624
|
+
/** Key shown on the active item (HCUI: ↵). */
|
|
625
|
+
selectHint?: ReactNode;
|
|
626
|
+
/** Shown on the active item when it has `onAltSelect` (HCUI: ⌘↵ add). */
|
|
627
|
+
altHint?: ReactNode;
|
|
628
|
+
/** Keyboard hints in the footer; no footer without them. */
|
|
629
|
+
hints?: readonly CommandPaletteHint[];
|
|
630
|
+
/** Class for the scrolling list (HCUI reserves its scrollbar gutter there). */
|
|
631
|
+
listClassName?: string;
|
|
632
|
+
}
|
|
633
|
+
/**
|
|
634
|
+
* HCUI's desktop ⌘K palette: a modal `<dialog>` (`showModal()`, focus in the field,
|
|
635
|
+
* Esc and the backdrop ask to close, focus returns, page scroll locked) with a search
|
|
636
|
+
* field that is a combobox over a listbox of groups. Focus stays in the field; arrows
|
|
637
|
+
* (wrapping), Home and End move the active item (`aria-activedescendant`), which scrolls
|
|
638
|
+
* into view; Enter selects, ⌘/Ctrl+Enter takes the alternative; the pointer activates the
|
|
639
|
+
* item under it. Texts, results and actions come from the caller.
|
|
640
|
+
*/
|
|
641
|
+
export declare const CommandPalette: import("react").ForwardRefExoticComponent<CommandPaletteProps & import("react").RefAttributes<HTMLDialogElement>>;
|
|
642
|
+
/**
|
|
643
|
+
* Walks a list of image addresses: starts at the first and moves to the next on
|
|
644
|
+
* each load error. Starts over when the list changes. `src` is `undefined` once
|
|
645
|
+
* every address failed.
|
|
646
|
+
*/
|
|
647
|
+
export declare function useImageChain(sources: readonly string[]): {
|
|
648
|
+
src: string | undefined;
|
|
649
|
+
index: number;
|
|
650
|
+
/** Call from `onError`: moves to the next address. */
|
|
651
|
+
next: () => void;
|
|
652
|
+
};
|
|
653
|
+
/** Rows of the mini dial, top to bottom. */
|
|
654
|
+
export declare const MINI_DIAL_STATS: readonly ["speed", "attack", "defense", "damage"];
|
|
655
|
+
export type MiniDialStat = (typeof MINI_DIAL_STATS)[number];
|
|
656
|
+
/** One resolved cell: the power color (or none), whether it is round and whether it is a special power. */
|
|
657
|
+
export interface MiniDialCell {
|
|
658
|
+
/** Standard power color. Without it the cell is the dial paper (no power). */
|
|
659
|
+
color?: string;
|
|
660
|
+
/** Round power (e.g. WWE). */
|
|
661
|
+
round?: boolean;
|
|
662
|
+
/** Special power: paper with a dark outline. */
|
|
663
|
+
special?: boolean;
|
|
664
|
+
}
|
|
665
|
+
/** One click of the dial, with its 4 cells. */
|
|
666
|
+
export interface MiniDialColumn {
|
|
667
|
+
/** Click number; also decides what has passed relative to `currentClick`. */
|
|
668
|
+
click: number;
|
|
669
|
+
cells: Record<MiniDialStat, MiniDialCell>;
|
|
670
|
+
}
|
|
671
|
+
export interface MiniDialViewProps {
|
|
672
|
+
columns: readonly MiniDialColumn[];
|
|
673
|
+
/** In play: the current click gets an outline and earlier ones fade. */
|
|
674
|
+
currentClick?: number;
|
|
675
|
+
/** Accessible label, already localized (e.g. "Dial: 6 clicks"). */
|
|
676
|
+
label: string;
|
|
677
|
+
className?: string;
|
|
678
|
+
}
|
|
679
|
+
/**
|
|
680
|
+
* The dial summarized in small squares: one column per click, one row per value
|
|
681
|
+
* (speed, attack, defense, damage). The cell size is `--hc-mini-dial-cell` (7px).
|
|
682
|
+
*/
|
|
683
|
+
export declare const MiniDialView: import("react").ForwardRefExoticComponent<MiniDialViewProps & import("react").RefAttributes<HTMLSpanElement>>;
|
|
684
|
+
export interface PowerIconViewProps {
|
|
685
|
+
/** Power color (the circle's fill). */
|
|
686
|
+
color: string;
|
|
687
|
+
/** Address of the value symbol (boot, fist, shield, starburst), already built. */
|
|
688
|
+
glyphSrc: string;
|
|
689
|
+
/** The symbol is black; `true` turns it white, for contrast on dark colors. */
|
|
690
|
+
invertGlyph?: boolean;
|
|
691
|
+
size?: number;
|
|
692
|
+
}
|
|
693
|
+
/** Power icon: a circle in the power color with the value symbol on top. */
|
|
694
|
+
export declare const PowerIconView: import("react").ForwardRefExoticComponent<PowerIconViewProps & import("react").RefAttributes<HTMLSpanElement>>;
|
|
695
|
+
export interface StatGlyphViewProps {
|
|
696
|
+
/** Address of the symbol, already built. */
|
|
697
|
+
src: string;
|
|
698
|
+
/** Alternative text, as the caller wants it read. */
|
|
699
|
+
alt: string;
|
|
700
|
+
size?: number;
|
|
701
|
+
}
|
|
702
|
+
/** The value symbol alone (no circle), in neutral gray. Used in the dial legend. */
|
|
703
|
+
export declare const StatGlyphView: import("react").ForwardRefExoticComponent<StatGlyphViewProps & import("react").RefAttributes<HTMLImageElement>>;
|
|
704
|
+
export interface SpecialIconViewProps {
|
|
705
|
+
/** Round white background (the image server's `sp/white.svg`), already built. */
|
|
706
|
+
backgroundSrc: string;
|
|
707
|
+
/** Symbol over the background (the trait star or a value symbol), already built. */
|
|
708
|
+
glyphSrc: string;
|
|
709
|
+
size?: number;
|
|
710
|
+
}
|
|
711
|
+
/** Special power and trait icon: a white circle with a symbol in the middle. */
|
|
712
|
+
export declare const SpecialIconView: import("react").ForwardRefExoticComponent<SpecialIconViewProps & import("react").RefAttributes<HTMLSpanElement>>;
|
|
713
|
+
export interface EquipmentIconProps {
|
|
714
|
+
size?: number;
|
|
715
|
+
}
|
|
716
|
+
/** Equipment icon: a white diamond on a purple circle. Decorative. */
|
|
717
|
+
export declare function EquipmentIcon({ size }: EquipmentIconProps): import("react/jsx-runtime").JSX.Element;
|
|
718
|
+
export type StatIconType = 'speed' | 'attack' | 'defense' | 'damage';
|
|
719
|
+
export interface StatIconProps {
|
|
720
|
+
type: StatIconType;
|
|
721
|
+
size?: number;
|
|
722
|
+
className?: string;
|
|
723
|
+
}
|
|
724
|
+
/** Arrow, shield or diamond drawn in SVG in the text color (`currentColor`); not the official symbols. */
|
|
725
|
+
export declare function StatIcon({ type, size, className }: StatIconProps): import("react/jsx-runtime").JSX.Element;
|
|
726
|
+
/**
|
|
727
|
+
* Avatar shape:
|
|
728
|
+
* - `round`: the default (the caller's class crops it);
|
|
729
|
+
* - `card`: one-shot, a landscape card (width ÷ 1.42, proportional corners);
|
|
730
|
+
* - `square`: maps, a square with slightly rounded corners;
|
|
731
|
+
* - `square-fit`: terrain marker, a square with the whole image fitted in.
|
|
732
|
+
*/
|
|
733
|
+
export type AvatarShape = 'round' | 'card' | 'square' | 'square-fit';
|
|
734
|
+
export interface AvatarViewProps {
|
|
735
|
+
/** Image addresses, in the order to try (e.g. a thumbnail, then the original). */
|
|
736
|
+
sources: readonly string[];
|
|
737
|
+
/** Image drawn when there is no address or all of them failed. */
|
|
738
|
+
fallbackSrc: string;
|
|
739
|
+
/** Alternative text, in the app's language. */
|
|
740
|
+
alt: string;
|
|
741
|
+
shape?: AvatarShape;
|
|
742
|
+
className?: string;
|
|
743
|
+
/** Reports that every attempt failed and the fallback is showing. */
|
|
744
|
+
onFallback?: () => void;
|
|
745
|
+
/** Width and height in px (the height follows the shape for `card`). Without it the caller's class sizes it. */
|
|
746
|
+
size?: number;
|
|
747
|
+
}
|
|
748
|
+
/**
|
|
749
|
+
* The piece's portrait. Tries each address in order and, when none loads, shows
|
|
750
|
+
* `fallbackSrc` (with `data-fallback` and the muted fill). The image cannot be
|
|
751
|
+
* dragged, selected or opened in a context menu.
|
|
752
|
+
*/
|
|
753
|
+
export declare const AvatarView: import("react").ForwardRefExoticComponent<AvatarViewProps & import("react").RefAttributes<HTMLImageElement>>;
|
|
754
|
+
export interface SetIconViewProps {
|
|
755
|
+
/** Addresses of the set icon, in the order to try. Empty: renders nothing. */
|
|
756
|
+
sources: readonly string[];
|
|
757
|
+
className?: string;
|
|
758
|
+
/** Tooltip (e.g. the set name), in the app's language. */
|
|
759
|
+
title?: string;
|
|
760
|
+
}
|
|
761
|
+
/** Set icon. Tries each address in order and disappears when none loads. Decorative: the set name is next to it. */
|
|
762
|
+
export declare const SetIconView: import("react").ForwardRefExoticComponent<SetIconViewProps & import("react").RefAttributes<HTMLImageElement>>;
|
|
763
|
+
/** Value rows of the dial, top to bottom (the top row is the click number). */
|
|
764
|
+
export declare const COMBAT_DIAL_STATS: readonly ["speed", "attack", "defense", "damage"];
|
|
765
|
+
export type CombatDialStat = (typeof COMBAT_DIAL_STATS)[number];
|
|
766
|
+
/** Content of a power's tip, already in the app's language. */
|
|
767
|
+
export interface CombatDialCellTip {
|
|
768
|
+
title: string;
|
|
769
|
+
description: string;
|
|
770
|
+
/** Standard power color; none for a special power. */
|
|
771
|
+
accent?: string;
|
|
772
|
+
/** The color mark is a circle (round power, e.g. WWE). */
|
|
773
|
+
accentRound?: boolean;
|
|
774
|
+
}
|
|
775
|
+
/** One resolved value cell. */
|
|
776
|
+
export interface CombatDialCell {
|
|
777
|
+
value: number;
|
|
778
|
+
/** Accessible label, already localized (e.g. "Click 1: Speed 8 — Charge"). */
|
|
779
|
+
label: string;
|
|
780
|
+
/** Cell fill: the power color, or the dial paper (`var(--hc-color-dial-paper)`) for no power. */
|
|
781
|
+
background: string;
|
|
782
|
+
/** Number color, chosen for contrast with the fill. */
|
|
783
|
+
color: string;
|
|
784
|
+
/** Round power (e.g. WWE). */
|
|
785
|
+
round?: boolean;
|
|
786
|
+
/** Special power: dark outline. */
|
|
787
|
+
outlined?: boolean;
|
|
788
|
+
/** Without a tip the cell opens nothing; on a static dial it is not even a button. */
|
|
789
|
+
tip?: CombatDialCellTip;
|
|
790
|
+
}
|
|
791
|
+
/** One click of the dial. */
|
|
792
|
+
export interface CombatDialColumn {
|
|
793
|
+
click: number;
|
|
794
|
+
/** Group label (e.g. "Click 3"). */
|
|
795
|
+
label: string;
|
|
796
|
+
/** Label of the number button when the dial changes the click (e.g. "Select click 3"). */
|
|
797
|
+
selectLabel: string;
|
|
798
|
+
/** Outside the selected point value: faded. */
|
|
799
|
+
dimmed?: boolean;
|
|
800
|
+
/** Starting line of a point value: color of the bar left of the column. */
|
|
801
|
+
startColor?: string;
|
|
802
|
+
cells: Record<CombatDialStat, CombatDialCell>;
|
|
803
|
+
}
|
|
804
|
+
/** One legend symbol, with its address already built. */
|
|
805
|
+
export interface CombatDialLegendIcon {
|
|
806
|
+
src: string;
|
|
807
|
+
alt: string;
|
|
808
|
+
title?: string;
|
|
809
|
+
/** `true`: the neutral value symbol (`StatGlyphView`); otherwise the piece's combat symbol. */
|
|
810
|
+
glyph?: boolean;
|
|
811
|
+
}
|
|
812
|
+
export interface CombatDialLegend {
|
|
813
|
+
range: number;
|
|
814
|
+
targets: number;
|
|
815
|
+
/** Address of the target bolt. */
|
|
816
|
+
boltSrc: string;
|
|
817
|
+
icons: Record<CombatDialStat, CombatDialLegendIcon>;
|
|
818
|
+
}
|
|
819
|
+
/** One point value, with the chip texts ready. */
|
|
820
|
+
export interface CombatDialPoint {
|
|
821
|
+
index: number;
|
|
822
|
+
startClick: number;
|
|
823
|
+
color: string;
|
|
824
|
+
/** E.g. "100 pts". Without it the chip shows only the clicks; with none at all the row is hidden. */
|
|
825
|
+
valueLabel?: ReactNode;
|
|
826
|
+
/** E.g. "6 clicks". */
|
|
827
|
+
clicksLabel: ReactNode;
|
|
828
|
+
/** Native tooltip of the chip, shown only when there is more than one point value. */
|
|
829
|
+
title?: string;
|
|
830
|
+
}
|
|
831
|
+
export interface CombatDialLabels {
|
|
832
|
+
/** Header title (e.g. "Dial"). */
|
|
833
|
+
heading: string;
|
|
834
|
+
/** KO mark in the header (e.g. "K.O"). */
|
|
835
|
+
koMark: string;
|
|
836
|
+
/** What follows the KO mark (e.g. "at 7"), already localized. A node, so the number can stay in its own text node. */
|
|
837
|
+
koAt: ReactNode;
|
|
838
|
+
/** Text of the KO column cells (e.g. "KO"). */
|
|
839
|
+
koCell: string;
|
|
840
|
+
}
|
|
841
|
+
/** The open tip: which cell, how (passive hover or the touch dialog), where and its content. */
|
|
842
|
+
export interface CombatDialTipState {
|
|
843
|
+
key: string;
|
|
844
|
+
click: number;
|
|
845
|
+
stat: CombatDialStat;
|
|
846
|
+
content: CombatDialCellTip;
|
|
847
|
+
mode: 'hover' | 'dialog';
|
|
848
|
+
anchor: {
|
|
849
|
+
x: number;
|
|
850
|
+
y: number;
|
|
851
|
+
preferAbove?: boolean;
|
|
852
|
+
};
|
|
853
|
+
trigger: HTMLElement;
|
|
854
|
+
}
|
|
855
|
+
export interface CombatDialViewProps {
|
|
856
|
+
columns: readonly CombatDialColumn[];
|
|
857
|
+
legend: CombatDialLegend;
|
|
858
|
+
points: readonly CombatDialPoint[];
|
|
859
|
+
/** `index` of the selected point value; -1 for none. */
|
|
860
|
+
selectedPointIndex: number;
|
|
861
|
+
/** Click centered in the track when the point value changes (its starting line). */
|
|
862
|
+
focusClick?: number;
|
|
863
|
+
currentClick?: number;
|
|
864
|
+
labels: CombatDialLabels;
|
|
865
|
+
/** `printed`: the physical card's dial, without the header, in rows of 12 clicks. */
|
|
866
|
+
presentation?: 'default' | 'printed';
|
|
867
|
+
/** Without it the dial is static: it neither marks nor changes the click; only cells with a tip react. */
|
|
868
|
+
onClickChange?: (click: number) => void;
|
|
869
|
+
onSelectPoint: (point: CombatDialPoint) => void;
|
|
870
|
+
/** The open tip, controlled by the caller (which closes it when the piece changes). */
|
|
871
|
+
tip: CombatDialTipState | null;
|
|
872
|
+
onTipChange: Dispatch<SetStateAction<CombatDialTipState | null>>;
|
|
873
|
+
/** `id` of the tip element, for the hover's `aria-describedby`. */
|
|
874
|
+
tipId: string;
|
|
875
|
+
/** The tip drawn by the caller (callout, dialog), shown while `tip` is set. */
|
|
876
|
+
tipSlot?: ReactNode;
|
|
877
|
+
}
|
|
878
|
+
/**
|
|
879
|
+
* Combat dial: the legend, one column per click, the KO column and, outside the
|
|
880
|
+
* printed card, the header with the point values. Hover, touch and keyboard on
|
|
881
|
+
* the cells open the tip through `onTipChange`; the caller draws the tip.
|
|
882
|
+
*/
|
|
883
|
+
export declare const CombatDialView: import("react").ForwardRefExoticComponent<CombatDialViewProps & import("react").RefAttributes<HTMLElement>>;
|
|
884
|
+
export declare const CARD_WIDTH = 375;
|
|
885
|
+
export declare const CARD_HEIGHT = 525;
|
|
886
|
+
export declare const BACK_TABLE_HEIGHT = 440;
|
|
887
|
+
/** Position of each value of the bystander's dial: every row goes 24px down and 7px right. */
|
|
888
|
+
export declare const BYSTANDER_DIAL_STEP: {
|
|
889
|
+
x: number;
|
|
890
|
+
y: number;
|
|
891
|
+
};
|
|
892
|
+
/**
|
|
893
|
+
* Splits the text blocks between the front (table 0) and the backs (1, 2, …), as
|
|
894
|
+
* HCUnits: a block that does not fit moves to the next column. The front may end
|
|
895
|
+
* up empty (the portrait and keywords already take room); a back column always
|
|
896
|
+
* keeps at least one block, even if it overflows.
|
|
897
|
+
*
|
|
898
|
+
* `overflowing(table)` tells whether that column overflows with the current split.
|
|
899
|
+
* Returns the next split, or `null` once it is stable.
|
|
900
|
+
*/
|
|
901
|
+
export declare function nextDistribution(pages: number[], overflowing: (table: number) => boolean): number[] | null;
|
|
902
|
+
/** How many backs the split needs (each back has two columns). There is always one. */
|
|
903
|
+
export declare function backCount(pages: number[]): number;
|
|
904
|
+
/** Cursor position on the card (0 to 1) → tilt, parallax (-1 to 1) and shine (%). */
|
|
905
|
+
export declare function tiltAt(x: number, y: number, damping?: number): {
|
|
906
|
+
rotateX: number;
|
|
907
|
+
rotateY: number;
|
|
908
|
+
parallaxX: number;
|
|
909
|
+
parallaxY: number;
|
|
910
|
+
shineX: number;
|
|
911
|
+
shineY: number;
|
|
912
|
+
};
|
|
913
|
+
type CardTilt = ReturnType<typeof tiltAt>;
|
|
914
|
+
/** The finished values each moving part of the card takes, ready for `style`. */
|
|
915
|
+
export declare function tiltStyles(tilt: CardTilt): {
|
|
916
|
+
card: string;
|
|
917
|
+
art: string;
|
|
918
|
+
header: string;
|
|
919
|
+
shine: string;
|
|
920
|
+
};
|
|
921
|
+
/** Sensor reading relative to the reference pose → position on the card (0 to 1). */
|
|
922
|
+
export declare function devicePosition(beta: number, gamma: number, baseline: {
|
|
923
|
+
beta: number;
|
|
924
|
+
gamma: number;
|
|
925
|
+
}, screenAngle?: number): {
|
|
926
|
+
x: number;
|
|
927
|
+
y: number;
|
|
928
|
+
};
|
|
929
|
+
/**
|
|
930
|
+
* The iPhone only releases the motion sensor after a touch. Called at the end of
|
|
931
|
+
* the touches while the focus view is open, starting with the finger that opened
|
|
932
|
+
* it; outside iOS it does nothing. A touch the browser refuses leaves the request
|
|
933
|
+
* for the next one.
|
|
934
|
+
*/
|
|
935
|
+
export declare function requestMotionAccess(): void;
|
|
936
|
+
/** Value rows of the dial, top to bottom. */
|
|
937
|
+
export declare const PHYSICAL_CARD_STATS: readonly ["speed", "attack", "defense", "damage"];
|
|
938
|
+
export type PhysicalCardStat = (typeof PHYSICAL_CARD_STATS)[number];
|
|
939
|
+
/** A tip's content (team ability, improved ability, bystander power), in the app's language. */
|
|
940
|
+
export interface PhysicalCardTip {
|
|
941
|
+
title: string;
|
|
942
|
+
/** Original description; the app may translate it before drawing the tip. */
|
|
943
|
+
description: string;
|
|
944
|
+
iconSrc?: string;
|
|
945
|
+
/** Standard power color; without one it is a special power. */
|
|
946
|
+
accent?: string;
|
|
947
|
+
badgeType?: PhysicalCardStat;
|
|
948
|
+
}
|
|
949
|
+
/** An open tip: which control, how (passive hover or the touch dialog), where, and the content. */
|
|
950
|
+
export interface PhysicalCardTipState extends PhysicalCardTip {
|
|
951
|
+
/** The control's key (e.g. `ta:<id>`, `imp:<type>:<id>`, `dial:<value>`). */
|
|
952
|
+
key: string;
|
|
953
|
+
mode: 'hover' | 'dialog';
|
|
954
|
+
anchor: {
|
|
955
|
+
x: number;
|
|
956
|
+
y: number;
|
|
957
|
+
preferAbove?: boolean;
|
|
958
|
+
};
|
|
959
|
+
trigger: HTMLElement;
|
|
960
|
+
}
|
|
961
|
+
/** A control that opens a tip: its key and content. */
|
|
962
|
+
export interface PhysicalCardTipTarget {
|
|
963
|
+
key: string;
|
|
964
|
+
tip: PhysicalCardTip;
|
|
965
|
+
}
|
|
966
|
+
/** Team ability button in the title (or on the bystander token's stripe). */
|
|
967
|
+
export interface PhysicalCardTeamAbility extends PhysicalCardTipTarget {
|
|
968
|
+
/** Accessible label (e.g. "Team ability: Avengers"). */
|
|
969
|
+
label: string;
|
|
970
|
+
iconSrc: string;
|
|
971
|
+
}
|
|
972
|
+
/** A group of improved abilities (movement or targeting) on the strip above the dial. */
|
|
973
|
+
export interface PhysicalCardImprovedGroup {
|
|
974
|
+
kind: 'movement' | 'targeting';
|
|
975
|
+
kindIconSrc: string;
|
|
976
|
+
kindLabel: string;
|
|
977
|
+
abilities: (PhysicalCardTipTarget & {
|
|
978
|
+
label: string;
|
|
979
|
+
iconSrc: string;
|
|
980
|
+
})[];
|
|
981
|
+
}
|
|
982
|
+
/** A text block (special power, trait, effect), translated and assembled. */
|
|
983
|
+
export interface PhysicalCardRule {
|
|
984
|
+
name?: string;
|
|
985
|
+
/** Drawn text (the game symbols already swapped in by the app). */
|
|
986
|
+
text: ReactNode;
|
|
987
|
+
/** Text copied when the paragraph is tapped. */
|
|
988
|
+
copyText: string;
|
|
989
|
+
icon?: ReactNode;
|
|
990
|
+
}
|
|
991
|
+
/** The front's portrait: the character's art, the terrain's art or an object token. */
|
|
992
|
+
export type PhysicalCardToken =
|
|
993
|
+
/** The character's portrait, drawn by the app with the class the card asks for. */
|
|
994
|
+
{
|
|
995
|
+
kind: 'portrait';
|
|
996
|
+
render: (className: string) => ReactNode;
|
|
997
|
+
} | {
|
|
998
|
+
kind: 'terrain';
|
|
999
|
+
src: string;
|
|
1000
|
+
alt: string;
|
|
1001
|
+
} | {
|
|
1002
|
+
kind: 'object';
|
|
1003
|
+
src?: string;
|
|
1004
|
+
alt: string;
|
|
1005
|
+
color?: string | null;
|
|
1006
|
+
};
|
|
1007
|
+
/** A cell of the bystander's dial (a single click). */
|
|
1008
|
+
export interface PhysicalCardBystanderCell {
|
|
1009
|
+
stat: PhysicalCardStat;
|
|
1010
|
+
value: number;
|
|
1011
|
+
label: string;
|
|
1012
|
+
background?: string;
|
|
1013
|
+
color?: string;
|
|
1014
|
+
special: boolean;
|
|
1015
|
+
/** Without a tip the cell is not a button. */
|
|
1016
|
+
tip?: PhysicalCardTipTarget;
|
|
1017
|
+
}
|
|
1018
|
+
/** Replica of the bystander's physical token (HCUnits' `BystanderView`). */
|
|
1019
|
+
export interface PhysicalCardBystander {
|
|
1020
|
+
art?: {
|
|
1021
|
+
src: string;
|
|
1022
|
+
alt: string;
|
|
1023
|
+
};
|
|
1024
|
+
range?: number | null;
|
|
1025
|
+
rangeLabel: string;
|
|
1026
|
+
targets: number;
|
|
1027
|
+
boltSrc: string;
|
|
1028
|
+
symbols: Partial<Record<PhysicalCardStat, string>>;
|
|
1029
|
+
/** In an arc on the left: the kind's icon (without `ability`) and each ability. */
|
|
1030
|
+
improved: {
|
|
1031
|
+
kind: 'movement' | 'targeting';
|
|
1032
|
+
iconSrc: string;
|
|
1033
|
+
label: string;
|
|
1034
|
+
ability?: PhysicalCardTipTarget & {
|
|
1035
|
+
label: string;
|
|
1036
|
+
iconSrc: string;
|
|
1037
|
+
};
|
|
1038
|
+
}[];
|
|
1039
|
+
trait?: {
|
|
1040
|
+
src: string;
|
|
1041
|
+
alt: string;
|
|
1042
|
+
};
|
|
1043
|
+
extra?: {
|
|
1044
|
+
kind: 'construct' | 'stack' | 'passengers';
|
|
1045
|
+
src: string;
|
|
1046
|
+
alt: string;
|
|
1047
|
+
text?: ReactNode;
|
|
1048
|
+
};
|
|
1049
|
+
name: string;
|
|
1050
|
+
dial?: {
|
|
1051
|
+
label: string;
|
|
1052
|
+
cells: PhysicalCardBystanderCell[];
|
|
1053
|
+
};
|
|
1054
|
+
}
|
|
1055
|
+
export interface PhysicalCardPoint {
|
|
1056
|
+
index: number;
|
|
1057
|
+
value: number;
|
|
1058
|
+
color: string;
|
|
1059
|
+
/** Accessible label (e.g. "100 pts, starts at click 1"). */
|
|
1060
|
+
label: string;
|
|
1061
|
+
}
|
|
1062
|
+
/** Measures, backgrounds and seals of the front (the app's layout rules, already applied). */
|
|
1063
|
+
export interface PhysicalCardFrontLayout {
|
|
1064
|
+
frontTable: 'normal' | 'long' | 'short' | 'none';
|
|
1065
|
+
frontTableHeight: number;
|
|
1066
|
+
rarityImage?: string | null;
|
|
1067
|
+
dialImage?: string | null;
|
|
1068
|
+
teamUp: boolean;
|
|
1069
|
+
/** Smaller portrait when the dial takes two rows. */
|
|
1070
|
+
smallToken: boolean;
|
|
1071
|
+
improvedTop: number;
|
|
1072
|
+
}
|
|
1073
|
+
export interface PhysicalCardViewProps {
|
|
1074
|
+
/** The card's backgrounds (front, back and the legacy overlay, when there is one), as addresses the app serves. */
|
|
1075
|
+
images: {
|
|
1076
|
+
front: string;
|
|
1077
|
+
back: string;
|
|
1078
|
+
legacy?: string | null;
|
|
1079
|
+
};
|
|
1080
|
+
layout: PhysicalCardFrontLayout;
|
|
1081
|
+
/** Name in capitals; the type shrinks until it fits the title line. */
|
|
1082
|
+
title: string;
|
|
1083
|
+
gearSrc?: string;
|
|
1084
|
+
/** Lines under the title, in order (object or bystander type, keywords). */
|
|
1085
|
+
headerLines: {
|
|
1086
|
+
kind: 'type' | 'keywords';
|
|
1087
|
+
text: string;
|
|
1088
|
+
}[];
|
|
1089
|
+
teamAbilities: PhysicalCardTeamAbility[];
|
|
1090
|
+
/** Shield drawn in every team ability button. */
|
|
1091
|
+
teamAbilityIcon?: ReactNode;
|
|
1092
|
+
collection: {
|
|
1093
|
+
number: ReactNode;
|
|
1094
|
+
setSources: readonly string[];
|
|
1095
|
+
setTitle?: string;
|
|
1096
|
+
};
|
|
1097
|
+
dimensions?: {
|
|
1098
|
+
text: string;
|
|
1099
|
+
oval: boolean;
|
|
1100
|
+
};
|
|
1101
|
+
token?: PhysicalCardToken;
|
|
1102
|
+
bystander?: PhysicalCardBystander;
|
|
1103
|
+
/** Keyphrase lines of the object, in order, before the effects. */
|
|
1104
|
+
keyphrases?: {
|
|
1105
|
+
key: string;
|
|
1106
|
+
content: ReactNode;
|
|
1107
|
+
}[];
|
|
1108
|
+
relic?: {
|
|
1109
|
+
label: string;
|
|
1110
|
+
value: string;
|
|
1111
|
+
};
|
|
1112
|
+
rules: PhysicalCardRule[];
|
|
1113
|
+
/**
|
|
1114
|
+
* Restarts the split of the text between front and backs when it changes
|
|
1115
|
+
* (piece, language and translated texts).
|
|
1116
|
+
*/
|
|
1117
|
+
contentKey: string;
|
|
1118
|
+
improved: PhysicalCardImprovedGroup[];
|
|
1119
|
+
/** The printed dial (a `CombatDialView` with `presentation="printed"`, built by the app) and how many rows it takes. */
|
|
1120
|
+
dial?: {
|
|
1121
|
+
tables: 1 | 2;
|
|
1122
|
+
node: ReactNode;
|
|
1123
|
+
};
|
|
1124
|
+
terrainDial?: {
|
|
1125
|
+
icons: {
|
|
1126
|
+
range: {
|
|
1127
|
+
src: string;
|
|
1128
|
+
alt: string;
|
|
1129
|
+
};
|
|
1130
|
+
giantReach: {
|
|
1131
|
+
src: string;
|
|
1132
|
+
alt: string;
|
|
1133
|
+
};
|
|
1134
|
+
damage: {
|
|
1135
|
+
src: string;
|
|
1136
|
+
alt: string;
|
|
1137
|
+
};
|
|
1138
|
+
destroy: {
|
|
1139
|
+
src: string;
|
|
1140
|
+
alt: string;
|
|
1141
|
+
};
|
|
1142
|
+
};
|
|
1143
|
+
values: [ReactNode, ReactNode, ReactNode];
|
|
1144
|
+
};
|
|
1145
|
+
/** Points on the front's bar: the point value buttons, an object's line, or nothing. */
|
|
1146
|
+
points: {
|
|
1147
|
+
kind: 'none';
|
|
1148
|
+
} | {
|
|
1149
|
+
kind: 'object';
|
|
1150
|
+
values: string;
|
|
1151
|
+
suffix: string;
|
|
1152
|
+
} | {
|
|
1153
|
+
kind: 'normal' | 'long' | 'short';
|
|
1154
|
+
label: string;
|
|
1155
|
+
items: PhysicalCardPoint[];
|
|
1156
|
+
selectedIndex: number;
|
|
1157
|
+
};
|
|
1158
|
+
onSelectPoint: (point: PhysicalCardPoint) => void;
|
|
1159
|
+
/** The last back's black bar: real name and source, already in capitals. */
|
|
1160
|
+
backBar: {
|
|
1161
|
+
realName?: {
|
|
1162
|
+
label: string;
|
|
1163
|
+
value: string;
|
|
1164
|
+
};
|
|
1165
|
+
source?: {
|
|
1166
|
+
label: string;
|
|
1167
|
+
value: string;
|
|
1168
|
+
};
|
|
1169
|
+
};
|
|
1170
|
+
labels: {
|
|
1171
|
+
/** The front's label (e.g. "Batman · card front"). */
|
|
1172
|
+
front: string;
|
|
1173
|
+
/** Each back's label (`index` from 0). */
|
|
1174
|
+
back: (index: number, count: number) => string;
|
|
1175
|
+
};
|
|
1176
|
+
onCopy: (text: string) => void;
|
|
1177
|
+
/** Desktop: the front fits whole in the visible height of the scrolling area (`fitHeightWithin`). */
|
|
1178
|
+
fitHeight?: boolean;
|
|
1179
|
+
/** Selector of the scrolling area, looked up from the card, for `fitHeight`. */
|
|
1180
|
+
fitHeightWithin?: string;
|
|
1181
|
+
/** Focus view: one side at a time, with the `focusActions` buttons below. */
|
|
1182
|
+
focus?: boolean;
|
|
1183
|
+
/** Holding the card with a finger (outside the focus view) asks for the focus view. */
|
|
1184
|
+
onRequestFocus?: () => void;
|
|
1185
|
+
/** The focus view's buttons; `flip` turns to the next side (`nextSide`: 0 is the front). */
|
|
1186
|
+
focusActions?: (state: {
|
|
1187
|
+
nextSide: number;
|
|
1188
|
+
backs: number;
|
|
1189
|
+
flip: () => void;
|
|
1190
|
+
}) => ReactNode;
|
|
1191
|
+
/** The open tip, controlled by the caller. */
|
|
1192
|
+
tip: PhysicalCardTipState | null;
|
|
1193
|
+
onTipChange: Dispatch<SetStateAction<PhysicalCardTipState | null>>;
|
|
1194
|
+
/** `id` of the tip element, for the hover's `aria-describedby`. */
|
|
1195
|
+
tipId: string;
|
|
1196
|
+
/** The tip drawn by the app, while `tip` is set. */
|
|
1197
|
+
tipSlot?: ReactNode;
|
|
1198
|
+
}
|
|
1199
|
+
export interface PhysicalCardFocusViewProps {
|
|
1200
|
+
/** The dialog's accessible name (the piece's name). */
|
|
1201
|
+
label: string;
|
|
1202
|
+
onClose: () => void;
|
|
1203
|
+
/** A second card with `focus`, usually the app's adapter again. */
|
|
1204
|
+
children: ReactNode;
|
|
1205
|
+
}
|
|
1206
|
+
/**
|
|
1207
|
+
* The card's focus view: a full-screen modal dialog over a dimmed, blurred
|
|
1208
|
+
* background. Inside goes a second card with `focus`; tapping outside it (after
|
|
1209
|
+
* the finger that opened it is lifted) or Esc calls `onClose`.
|
|
1210
|
+
*/
|
|
1211
|
+
export declare function PhysicalCardFocusView({ label, onClose, children }: PhysicalCardFocusViewProps): import("react/jsx-runtime").JSX.Element;
|
|
1212
|
+
/**
|
|
1213
|
+
* The physical card (front and backs in HCUnits' format), presentation only. It
|
|
1214
|
+
* gets everything resolved: backgrounds, seals, texts already translated and
|
|
1215
|
+
* assembled, symbol addresses, the printed dial ready and the points. It handles
|
|
1216
|
+
* the scale, the split of the text between front and backs, the tilt and shine,
|
|
1217
|
+
* holding to open the focus view and flipping in it, and opens the tips through
|
|
1218
|
+
* `onTipChange`; drawing the tip, translating and copying are the caller's.
|
|
1219
|
+
*/
|
|
1220
|
+
export declare const PhysicalCardView: import("react").ForwardRefExoticComponent<PhysicalCardViewProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
1221
|
+
/**
|
|
1222
|
+
* A faithful port of hcmaps.net's map drawing engine (map.js / tile.js / edge.js /
|
|
1223
|
+
* wall.js), the same engine HCUnits uses to draw the map's canvas. It reads the
|
|
1224
|
+
* same JSON (the piece's map) and draws it 1:1 locally, with no runtime
|
|
1225
|
+
* dependency on their scripts. Credit: hcmaps.net (original engine in JS).
|
|
1226
|
+
*/
|
|
1227
|
+
export declare const TILE_SIZE = 64;
|
|
1228
|
+
export declare const ELEVATION_TILE_COLORS: string[];
|
|
1229
|
+
export declare const TERRAIN_TILE_COLORS: Record<string, string>;
|
|
1230
|
+
export declare const TERRAIN_EDGE_COLORS: Record<string, string>;
|
|
1231
|
+
export declare const ELEVATION_EDGE_COLOR = "#FF0000";
|
|
1232
|
+
export declare const INDOOR_EDGE_COLOR = "#FFFF00";
|
|
1233
|
+
export declare const WALL_COLORS: Record<string, string>;
|
|
1234
|
+
interface JsonLabel {
|
|
1235
|
+
text: string;
|
|
1236
|
+
color?: string;
|
|
1237
|
+
rotate?: number;
|
|
1238
|
+
}
|
|
1239
|
+
interface JsonTile {
|
|
1240
|
+
terrain?: string;
|
|
1241
|
+
type?: string;
|
|
1242
|
+
elevation?: number | string;
|
|
1243
|
+
isStartingZone?: boolean;
|
|
1244
|
+
isStartingZone4p?: boolean;
|
|
1245
|
+
group?: number;
|
|
1246
|
+
label?: JsonLabel;
|
|
1247
|
+
}
|
|
1248
|
+
interface JsonWall {
|
|
1249
|
+
x0: number;
|
|
1250
|
+
y0: number;
|
|
1251
|
+
x1: number;
|
|
1252
|
+
y1: number;
|
|
1253
|
+
type?: string;
|
|
1254
|
+
}
|
|
1255
|
+
interface JsonRamp {
|
|
1256
|
+
x0: number;
|
|
1257
|
+
y0: number;
|
|
1258
|
+
x1: number;
|
|
1259
|
+
y1: number;
|
|
1260
|
+
}
|
|
1261
|
+
export interface HCMapJson {
|
|
1262
|
+
name?: string;
|
|
1263
|
+
width: number;
|
|
1264
|
+
height: number;
|
|
1265
|
+
type?: string;
|
|
1266
|
+
defaultTerrain?: string;
|
|
1267
|
+
defaultElevation?: number;
|
|
1268
|
+
defaultType?: string;
|
|
1269
|
+
defaultWallType?: string;
|
|
1270
|
+
hideLabels?: boolean;
|
|
1271
|
+
rows: {
|
|
1272
|
+
tiles: JsonTile[];
|
|
1273
|
+
}[];
|
|
1274
|
+
walls?: JsonWall[];
|
|
1275
|
+
ramps?: JsonRamp[];
|
|
1276
|
+
ziplines?: JsonWall[];
|
|
1277
|
+
}
|
|
1278
|
+
export declare class HCMap {
|
|
1279
|
+
width: number;
|
|
1280
|
+
height: number;
|
|
1281
|
+
type: string;
|
|
1282
|
+
private tiles;
|
|
1283
|
+
private walls;
|
|
1284
|
+
private ziplines;
|
|
1285
|
+
constructor(json: HCMapJson);
|
|
1286
|
+
private getTile;
|
|
1287
|
+
private tryAddEdge;
|
|
1288
|
+
draw(ctx: CanvasRenderingContext2D): void;
|
|
1289
|
+
private drawWall;
|
|
1290
|
+
}
|
|
1291
|
+
export declare function mapSquare(map: HCMapJson, x: number, y: number): {
|
|
1292
|
+
x: number;
|
|
1293
|
+
y: number;
|
|
1294
|
+
terrain: string;
|
|
1295
|
+
elevation: string | number;
|
|
1296
|
+
environment: string;
|
|
1297
|
+
starting: boolean;
|
|
1298
|
+
label: string | undefined;
|
|
1299
|
+
};
|
|
1300
|
+
export type MapSquare = ReturnType<typeof mapSquare>;
|
|
1301
|
+
/** A text elevation exists in the hcmaps format; the 2D drawing treats it as an elevated level. */
|
|
1302
|
+
export declare function elevationLevel(elevation: number | string): number;
|
|
1303
|
+
export declare function mapMaxLevel(map: HCMapJson): number;
|
|
1304
|
+
export interface MapInspection {
|
|
1305
|
+
square: MapSquare;
|
|
1306
|
+
adjacent?: MapSquare;
|
|
1307
|
+
line?: {
|
|
1308
|
+
x0: number;
|
|
1309
|
+
y0: number;
|
|
1310
|
+
x1: number;
|
|
1311
|
+
y1: number;
|
|
1312
|
+
};
|
|
1313
|
+
wall?: string;
|
|
1314
|
+
}
|
|
1315
|
+
/** Coordinates are in squares; wall tolerance is based on screen pixels by the caller. */
|
|
1316
|
+
export declare function inspectMap(map: HCMapJson, x: number, y: number, tolerance?: number): MapInspection | null;
|
|
1317
|
+
export declare function squareCoordinate(square: Pick<MapSquare, 'x' | 'y'>): string;
|
|
1318
|
+
/**
|
|
1319
|
+
* The 3D model's scene, with no drawing. It uses only what the map JSON has:
|
|
1320
|
+
* terrain, elevation, environment, walls, ramps and ziplines. Blocking follows
|
|
1321
|
+
* the game's rule: indoor goes up to the map's highest elevation, outdoor takes
|
|
1322
|
+
* one elevation. Walls, water and obscuring have no height in the data: those
|
|
1323
|
+
* are conventions.
|
|
1324
|
+
*/
|
|
1325
|
+
export declare const LEVEL_HEIGHT = 0.62;
|
|
1326
|
+
export declare const BASE_Z = -0.34;
|
|
1327
|
+
export interface SceneCell extends MapSquare {
|
|
1328
|
+
level: number;
|
|
1329
|
+
/** The square's floor, without the terrain's volume. */
|
|
1330
|
+
floor: number;
|
|
1331
|
+
top: number;
|
|
1332
|
+
}
|
|
1333
|
+
export interface SceneWall {
|
|
1334
|
+
x0: number;
|
|
1335
|
+
y0: number;
|
|
1336
|
+
x1: number;
|
|
1337
|
+
y1: number;
|
|
1338
|
+
type: string;
|
|
1339
|
+
z0: number;
|
|
1340
|
+
z1: number;
|
|
1341
|
+
}
|
|
1342
|
+
export interface SceneRamp {
|
|
1343
|
+
x: number;
|
|
1344
|
+
y: number;
|
|
1345
|
+
dx: number;
|
|
1346
|
+
dy: number;
|
|
1347
|
+
from: number;
|
|
1348
|
+
to: number;
|
|
1349
|
+
}
|
|
1350
|
+
export interface SceneLine {
|
|
1351
|
+
x0: number;
|
|
1352
|
+
y0: number;
|
|
1353
|
+
x1: number;
|
|
1354
|
+
y1: number;
|
|
1355
|
+
z: number;
|
|
1356
|
+
}
|
|
1357
|
+
export interface MapScene {
|
|
1358
|
+
width: number;
|
|
1359
|
+
height: number;
|
|
1360
|
+
cells: SceneCell[];
|
|
1361
|
+
walls: SceneWall[];
|
|
1362
|
+
ramps: SceneRamp[];
|
|
1363
|
+
ziplines: SceneLine[];
|
|
1364
|
+
maxTop: number;
|
|
1365
|
+
}
|
|
1366
|
+
export declare function sceneCell(scene: Pick<MapScene, 'width' | 'height' | 'cells'>, x: number, y: number): SceneCell | null;
|
|
1367
|
+
export declare function buildMapScene(map: HCMapJson): MapScene;
|
|
1368
|
+
export interface MapCamera {
|
|
1369
|
+
azimuth: number;
|
|
1370
|
+
tilt: number;
|
|
1371
|
+
zoom: number;
|
|
1372
|
+
panX: number;
|
|
1373
|
+
panY: number;
|
|
1374
|
+
}
|
|
1375
|
+
export interface Viewport {
|
|
1376
|
+
width: number;
|
|
1377
|
+
height: number;
|
|
1378
|
+
}
|
|
1379
|
+
export declare const DEFAULT_CAMERA: MapCamera;
|
|
1380
|
+
/** 90 is the flat view from above. Below the minimum the board becomes a line. */
|
|
1381
|
+
export declare const TILT_RANGE: readonly [6, 90];
|
|
1382
|
+
export declare const ZOOM_RANGE: readonly [1, 6];
|
|
1383
|
+
export declare const clamp: (value: number, min: number, max: number) => number;
|
|
1384
|
+
export interface Projection {
|
|
1385
|
+
scale: number;
|
|
1386
|
+
sin: number;
|
|
1387
|
+
cos: number;
|
|
1388
|
+
point(x: number, y: number, z: number): [number, number];
|
|
1389
|
+
/** Larger = closer to the viewer. */
|
|
1390
|
+
depth(x: number, y: number): number;
|
|
1391
|
+
/** From the screen to the map, on a plane at height `z`. */
|
|
1392
|
+
ground(px: number, py: number, z: number): [number, number];
|
|
1393
|
+
}
|
|
1394
|
+
export declare function makeProjection(scene: MapScene, camera: MapCamera, viewport: Viewport): Projection;
|
|
1395
|
+
/** The map's center may go as far as any of its corners reaches the middle of the screen. */
|
|
1396
|
+
export declare function clampPan(scene: MapScene, camera: MapCamera, viewport: Viewport): MapCamera;
|
|
1397
|
+
/** Zooms keeping the point at `anchor` (measured from the screen's center) still. */
|
|
1398
|
+
export declare function zoomCamera(scene: MapScene, camera: MapCamera, viewport: Viewport, factor: number, anchor?: {
|
|
1399
|
+
x: number;
|
|
1400
|
+
y: number;
|
|
1401
|
+
}): MapCamera;
|
|
1402
|
+
/** A square's four sides: the edge's points, the neighbor and the normal. */
|
|
1403
|
+
export declare const CELL_SIDES: readonly [{
|
|
1404
|
+
readonly ax: 0;
|
|
1405
|
+
readonly ay: 1;
|
|
1406
|
+
readonly bx: 0;
|
|
1407
|
+
readonly by: 0;
|
|
1408
|
+
readonly dx: -1;
|
|
1409
|
+
readonly dy: 0;
|
|
1410
|
+
}, {
|
|
1411
|
+
readonly ax: 1;
|
|
1412
|
+
readonly ay: 0;
|
|
1413
|
+
readonly bx: 1;
|
|
1414
|
+
readonly by: 1;
|
|
1415
|
+
readonly dx: 1;
|
|
1416
|
+
readonly dy: 0;
|
|
1417
|
+
}, {
|
|
1418
|
+
readonly ax: 0;
|
|
1419
|
+
readonly ay: 0;
|
|
1420
|
+
readonly bx: 1;
|
|
1421
|
+
readonly by: 0;
|
|
1422
|
+
readonly dx: 0;
|
|
1423
|
+
readonly dy: -1;
|
|
1424
|
+
}, {
|
|
1425
|
+
readonly ax: 1;
|
|
1426
|
+
readonly ay: 1;
|
|
1427
|
+
readonly bx: 0;
|
|
1428
|
+
readonly by: 1;
|
|
1429
|
+
readonly dx: 0;
|
|
1430
|
+
readonly dy: 1;
|
|
1431
|
+
}];
|
|
1432
|
+
/**
|
|
1433
|
+
* From the screen to map coordinates, testing from front to back. Returns the
|
|
1434
|
+
* point in `inspectMap`'s terms, so the tooltip and selection match the 2D board.
|
|
1435
|
+
*/
|
|
1436
|
+
export declare function pickScene(scene: MapScene, view: Projection, px: number, py: number): {
|
|
1437
|
+
x: number;
|
|
1438
|
+
y: number;
|
|
1439
|
+
} | null;
|
|
1440
|
+
type Point = readonly [number, number];
|
|
1441
|
+
/**
|
|
1442
|
+
* The target's squares inside the board and their outer outline, in map coordinates.
|
|
1443
|
+
* Corners are where the outline turns or ends: the volume's edges rise there.
|
|
1444
|
+
*/
|
|
1445
|
+
export declare function footprintOutline({ x, y, width, height, cells }: {
|
|
1446
|
+
x: number;
|
|
1447
|
+
y: number;
|
|
1448
|
+
width: number;
|
|
1449
|
+
height: number;
|
|
1450
|
+
cells?: readonly (readonly boolean[])[] | null;
|
|
1451
|
+
}, boardWidth: number, boardHeight: number): {
|
|
1452
|
+
squares: Point[];
|
|
1453
|
+
edges: [Point, Point][];
|
|
1454
|
+
corners: Point[];
|
|
1455
|
+
};
|
|
1456
|
+
export interface SceneFootprint {
|
|
1457
|
+
x: number;
|
|
1458
|
+
y: number;
|
|
1459
|
+
width: number;
|
|
1460
|
+
height: number;
|
|
1461
|
+
top: number;
|
|
1462
|
+
valid: boolean;
|
|
1463
|
+
/** Occupied squares (`cells[y][x]`, from the corner); without it, the whole rectangle. */
|
|
1464
|
+
cells?: readonly (readonly boolean[])[] | null;
|
|
1465
|
+
}
|
|
1466
|
+
export interface SceneHighlight {
|
|
1467
|
+
square?: {
|
|
1468
|
+
x: number;
|
|
1469
|
+
y: number;
|
|
1470
|
+
};
|
|
1471
|
+
line?: {
|
|
1472
|
+
x0: number;
|
|
1473
|
+
y0: number;
|
|
1474
|
+
x1: number;
|
|
1475
|
+
y1: number;
|
|
1476
|
+
};
|
|
1477
|
+
/** The target of a piece being moved: floor and volume, cut to the board. */
|
|
1478
|
+
footprint?: SceneFootprint;
|
|
1479
|
+
}
|
|
1480
|
+
export declare function drawMapScene(ctx: CanvasRenderingContext2D, scene: MapScene, view: Projection, texture: HTMLCanvasElement, highlight?: SceneHighlight | null, { tufts }?: {
|
|
1481
|
+
tufts?: boolean;
|
|
1482
|
+
}): void;
|
|
1483
|
+
/** The thumbnail's default height, in pixels. */
|
|
1484
|
+
export declare const MAP_THUMBNAIL_HEIGHT = 192;
|
|
1485
|
+
/**
|
|
1486
|
+
* The map's thumbnail as a PNG data URL: draws with the engine at full resolution
|
|
1487
|
+
* and scales down to `height` pixels. Gets the JSON already fetched; `null` when it
|
|
1488
|
+
* has no dimensions or the browser gives no 2D context.
|
|
1489
|
+
*/
|
|
1490
|
+
export declare function renderMapThumbnail(json: HCMapJson, height?: number): string | null;
|
|
1491
|
+
export interface MapBoardViewProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'onPointerDown' | 'onPointerMove' | 'onPointerLeave'> {
|
|
1492
|
+
/** The hcmaps JSON, already fetched and checked. */
|
|
1493
|
+
map: HCMapJson;
|
|
1494
|
+
/** The drawing's accessible name, in the app's language (the map's name or a generic one). */
|
|
1495
|
+
canvasLabel: string;
|
|
1496
|
+
/** What is inspected: the square is shaded; an edge becomes a line. */
|
|
1497
|
+
selection?: Pick<MapInspection, 'square' | 'line'> | null;
|
|
1498
|
+
/** Pointer over the map, in squares (0..width, 0..height), and the screen point. Touch counts only on press; mouse and pen also while passing over. */
|
|
1499
|
+
onInspect?(x: number, y: number, clientX: number, clientY: number): void;
|
|
1500
|
+
/** The mouse or pen left the map. */
|
|
1501
|
+
onLeave?(): void;
|
|
1502
|
+
/** Drawing failed (a JSON outside the expected format). */
|
|
1503
|
+
onDrawError?(): void;
|
|
1504
|
+
}
|
|
1505
|
+
/**
|
|
1506
|
+
* The map's 2D board: the hcmaps drawing in a canvas and, over it, the inspection's
|
|
1507
|
+
* highlight. Gets the map already fetched; fetching, translating and deciding what
|
|
1508
|
+
* the tooltip shows are the caller's. Other attributes (role, focus, keyboard,
|
|
1509
|
+
* `aria-*`, `data-*`) go to the container.
|
|
1510
|
+
*/
|
|
1511
|
+
export declare const MapBoardView: import("react").ForwardRefExoticComponent<MapBoardViewProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
1512
|
+
export interface MapBoardPlaceholderProps {
|
|
1513
|
+
/** Read by screen readers while the map loads (e.g. "Loading map"). */
|
|
1514
|
+
label: string;
|
|
1515
|
+
}
|
|
1516
|
+
/** The map's place while it loads: the empty grid in a map's proportion. The label is visually hidden. */
|
|
1517
|
+
export declare function MapBoardPlaceholder({ label }: MapBoardPlaceholderProps): import("react/jsx-runtime").JSX.Element;
|
|
1518
|
+
export interface MapSwatchProps {
|
|
1519
|
+
/** The square's fill, as on the board. */
|
|
1520
|
+
fill: string;
|
|
1521
|
+
/** The square's border (the terrain's or the elevation's color). */
|
|
1522
|
+
edge: string;
|
|
1523
|
+
/** Number inside the square (the elevation's level). */
|
|
1524
|
+
children?: number;
|
|
1525
|
+
/** White text, for dark fills. */
|
|
1526
|
+
lightText?: boolean;
|
|
1527
|
+
}
|
|
1528
|
+
/** A legend square with a map square's fill and border. Decorative. */
|
|
1529
|
+
export declare function MapSwatch({ fill, edge, children, lightText }: MapSwatchProps): import("react/jsx-runtime").JSX.Element;
|
|
1530
|
+
/** One inspected square, resolved: texts in the app's language and the board's colors. */
|
|
1531
|
+
export interface MapTooltipSquare {
|
|
1532
|
+
/** Coordinate (e.g. "H8"); shown only when the title is not the coordinate itself. */
|
|
1533
|
+
coordinate?: string;
|
|
1534
|
+
terrain: {
|
|
1535
|
+
label: string;
|
|
1536
|
+
fill: string;
|
|
1537
|
+
edge: string;
|
|
1538
|
+
};
|
|
1539
|
+
elevation: {
|
|
1540
|
+
level: number;
|
|
1541
|
+
label: string;
|
|
1542
|
+
fill: string;
|
|
1543
|
+
edge: string;
|
|
1544
|
+
lightText?: boolean;
|
|
1545
|
+
};
|
|
1546
|
+
/** The square's environment, e.g. a `MapEnvironmentView`. */
|
|
1547
|
+
environment?: ReactNode;
|
|
1548
|
+
/** Text in place of the environment, when the map does not say (e.g. "Environment unspecified"). */
|
|
1549
|
+
environmentNote?: string;
|
|
1550
|
+
/** Notes, in order (what fills a blocking square, starting area, the square's label). */
|
|
1551
|
+
notes?: readonly string[];
|
|
1552
|
+
}
|
|
1553
|
+
export interface MapTooltipViewProps {
|
|
1554
|
+
id?: string;
|
|
1555
|
+
/** Screen point (viewport coordinates) the tooltip opens next to. A new object moves it. */
|
|
1556
|
+
anchor: {
|
|
1557
|
+
x: number;
|
|
1558
|
+
y: number;
|
|
1559
|
+
};
|
|
1560
|
+
title: string;
|
|
1561
|
+
/** The wall's color, when the inspected point is a wall. */
|
|
1562
|
+
wallColor?: string;
|
|
1563
|
+
/** One square, or two on an edge between squares. */
|
|
1564
|
+
squares: readonly MapTooltipSquare[];
|
|
1565
|
+
}
|
|
1566
|
+
/**
|
|
1567
|
+
* The map inspection's tooltip: the title (wall, edge or coordinate) and what is in
|
|
1568
|
+
* each square. It is `position: fixed`, next to `anchor`, without leaving the
|
|
1569
|
+
* screen; to take it out of a container with overflow, the caller portals it.
|
|
1570
|
+
*/
|
|
1571
|
+
export declare function MapTooltipView({ id, anchor, title, wallColor, squares }: MapTooltipViewProps): import("react/jsx-runtime").JSX.Element;
|
|
1572
|
+
export interface MapLegendItem {
|
|
1573
|
+
fill: string;
|
|
1574
|
+
edge: string;
|
|
1575
|
+
/** Visible name, in the app's language; also the item's key. */
|
|
1576
|
+
label: string;
|
|
1577
|
+
}
|
|
1578
|
+
export interface MapLegendViewProps {
|
|
1579
|
+
items: readonly MapLegendItem[];
|
|
1580
|
+
/** After the items, e.g. the map's environments (`MapEnvironmentView`). */
|
|
1581
|
+
children?: ReactNode;
|
|
1582
|
+
}
|
|
1583
|
+
/** The 3D model's legend: a small square and the name of each terrain on the map. */
|
|
1584
|
+
export declare function MapLegendView({ items, children }: MapLegendViewProps): import("react/jsx-runtime").JSX.Element;
|
|
1585
|
+
export type MapEnvironmentKind = 'indoor' | 'outdoor';
|
|
1586
|
+
/** The map's types (`indoor`, `outdoor`, `indoorOutdoor`, in any spelling) as environments, without repeats. */
|
|
1587
|
+
export declare function mapEnvironments(types: readonly string[]): MapEnvironmentKind[];
|
|
1588
|
+
export interface MapEnvironmentViewProps {
|
|
1589
|
+
types: readonly string[];
|
|
1590
|
+
/** Each environment's icon address (black line icons; the dark theme turns them light). */
|
|
1591
|
+
icons: Record<MapEnvironmentKind, string>;
|
|
1592
|
+
/** Each environment's visible name. A game term: HCUI shows it in English in all three languages. */
|
|
1593
|
+
labels: Record<MapEnvironmentKind, string>;
|
|
1594
|
+
className?: string;
|
|
1595
|
+
}
|
|
1596
|
+
/** The map's environments (indoor / outdoor), each with its icon and name. With no known environment it draws nothing. */
|
|
1597
|
+
export declare function MapEnvironmentView({ types, icons, labels, className }: MapEnvironmentViewProps): import("react/jsx-runtime").JSX.Element | null;
|
|
1598
|
+
export {};
|