@debuffjp/ui 0.0.0-stage → 0.3.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 +46 -2
- package/dist/draw-like.d.ts +30 -0
- package/dist/draw-like.d.ts.map +1 -0
- package/dist/draw-like.js +2 -0
- package/dist/draw-like.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/menu.d.ts +107 -0
- package/dist/menu.d.ts.map +1 -0
- package/dist/menu.js +182 -0
- package/dist/menu.js.map +1 -0
- package/dist/panel.d.ts +23 -0
- package/dist/panel.d.ts.map +1 -0
- package/dist/panel.js +30 -0
- package/dist/panel.js.map +1 -0
- package/dist/transition.d.ts +44 -0
- package/dist/transition.d.ts.map +1 -0
- package/dist/transition.js +76 -0
- package/dist/transition.js.map +1 -0
- package/package.json +44 -4
package/README.md
CHANGED
|
@@ -1,3 +1,47 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @debuffjp/ui
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The screens around your game: fades between areas, title and pause menus, framed panels. No dependencies, and each export can be used on its own. Everything runs on `update(dt)`, so replays and scenarios stay deterministic.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { Menu, Transition } from "@debuffjp/ui";
|
|
7
|
+
|
|
8
|
+
const fade = new Transition({ duration: 0.35, color: "#1a1020" });
|
|
9
|
+
let volume = 10;
|
|
10
|
+
const pause = new Menu(
|
|
11
|
+
[
|
|
12
|
+
{ label: "resume", onSelect: () => (paused = false) },
|
|
13
|
+
{ label: "volume", value: () => `${volume}`, change: (d) => (volume = Math.max(0, Math.min(10, volume + d))) },
|
|
14
|
+
{ label: "restart", onSelect: () => fade.run(reset) },
|
|
15
|
+
],
|
|
16
|
+
{ actions: { up: "up", down: "down", confirm: "jump", back: "pause", left: "left", right: "right" } },
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
// every step
|
|
20
|
+
fade.update(dt);
|
|
21
|
+
if (fade.blocking) return;
|
|
22
|
+
if (paused) pause.update(input);
|
|
23
|
+
|
|
24
|
+
// render, after the world
|
|
25
|
+
if (paused) pause.drawPanel(draw, game.width / 2, game.height / 2, { title: "paused" });
|
|
26
|
+
fade.draw(draw, 0, 0, game.width, game.height);
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Layout
|
|
30
|
+
|
|
31
|
+
Text is measured with `draw.measure`, so nothing is positioned by guesswork:
|
|
32
|
+
|
|
33
|
+
- The cursor has its own column. Labels never shift when focus moves.
|
|
34
|
+
- Values (`value: () => string`) are right-aligned in a column of their own.
|
|
35
|
+
- `drawPanel` centres the frame on a point, mirrors the cursor column so the labels and the title share a centre, and pads evenly on all sides.
|
|
36
|
+
- Every position snaps to whole pixels, so pixel fonts stay crisp.
|
|
37
|
+
|
|
38
|
+
## API
|
|
39
|
+
|
|
40
|
+
- `Transition`: `run(swap?)` → covers the screen, calls `swap` once fully covered, then uncovers. `update(dt)`, `draw(draw, x, y, w, h)`, `stop()`, `active`, `blocking`, `cover`, `inspect()`
|
|
41
|
+
- `Menu`: `new Menu(items, { actions, onBack, wrap })`, `update(input)` (with `input.pointer`, a tap selects the row under it; on a value row the left half lowers and the right half raises), `tap(x, y)`, `move(delta)`, `select()`, `measure(draw, style)`, `draw(draw, x, y, style)`, `drawPanel(draw, cx, cy, style)`, `focused`, `inspect()`
|
|
42
|
+
- items: `{ label, onSelect?, disabled?, value?, change? }`
|
|
43
|
+
- style: `font`, `color`, `focusColor`, `disabledColor`, `gap` (3), `align` (`left`/`center`), `cursor` (`>`), `cursorGap` (4), `valueGap` (12); panel adds `title`, `titleFont`, `titleColor`, `titleGap` (6), `borders`, `fill`, `padding` (6)
|
|
44
|
+
- `panel(draw, x, y, w, h, borders, fill?)`: 1px rings, outermost first, then the fill
|
|
45
|
+
- `panelAround(draw, cx, cy, w, h, { borders, fill, padding })`: a panel fitted around content, centred and pixel-snapped; returns the content's top-left
|
|
46
|
+
|
|
47
|
+
Colours and fonts are yours to choose; the defaults are plain greys.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** A CSS font string or a font object such as `@debuffjp/core`'s `BitmapFont`. */
|
|
2
|
+
export type FontLike = string | object;
|
|
3
|
+
export interface TextOptions {
|
|
4
|
+
font?: FontLike;
|
|
5
|
+
color?: string | number;
|
|
6
|
+
align?: "left" | "center" | "right";
|
|
7
|
+
baseline?: "top" | "middle" | "bottom";
|
|
8
|
+
}
|
|
9
|
+
/** The parts of `@debuffjp/core`'s `Draw` that ui uses. Structural, so ui has no dependencies. */
|
|
10
|
+
export interface DrawLike {
|
|
11
|
+
alpha: number;
|
|
12
|
+
rect(x: number, y: number, w: number, h: number, color: string | number): void;
|
|
13
|
+
text(text: string, x: number, y: number, options?: TextOptions): void;
|
|
14
|
+
/** The box `text()` fills, in logical pixels. */
|
|
15
|
+
measure(text: string, font?: FontLike): {
|
|
16
|
+
w: number;
|
|
17
|
+
h: number;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/** The part of `@debuffjp/input`'s `Input` that `Menu` reads. */
|
|
21
|
+
export interface InputLike<A extends string = string> {
|
|
22
|
+
pressed(action: A): boolean;
|
|
23
|
+
/** When present, tapping or clicking a row selects it. */
|
|
24
|
+
pointer?: {
|
|
25
|
+
x: number;
|
|
26
|
+
y: number;
|
|
27
|
+
pressed: boolean;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=draw-like.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"draw-like.d.ts","sourceRoot":"","sources":["../src/draw-like.ts"],"names":[],"mappings":"AAAA,kFAAkF;AAClF,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC;AAEvC,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAC;IACpC,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,QAAQ,CAAC;CACxC;AAED,kGAAkG;AAClG,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC/E,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IACtE,iDAAiD;IACjD,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,QAAQ,GAAG;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAClE;AAED,iEAAiE;AACjE,MAAM,WAAW,SAAS,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM;IAClD,OAAO,CAAC,MAAM,EAAE,CAAC,GAAG,OAAO,CAAC;IAC5B,0DAA0D;IAC1D,OAAO,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;CACtD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"draw-like.js","sourceRoot":"","sources":["../src/draw-like.ts"],"names":[],"mappings":""}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export type { DrawLike, FontLike, InputLike, TextOptions } from "./draw-like.js";
|
|
2
|
+
export { Menu, type MenuActions, type MenuItem, type MenuOptions, type MenuPanelStyle, type MenuStyle } from "./menu.js";
|
|
3
|
+
export { type FrameStyle, panel, panelAround } from "./panel.js";
|
|
4
|
+
export { Transition, type TransitionOptions } from "./transition.js";
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AACjF,OAAO,EAAE,IAAI,EAAE,KAAK,WAAW,EAAE,KAAK,QAAQ,EAAE,KAAK,WAAW,EAAE,KAAK,cAAc,EAAE,KAAK,SAAS,EAAE,MAAM,WAAW,CAAC;AACzH,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAA0F,MAAM,WAAW,CAAC;AACzH,OAAO,EAAmB,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACjE,OAAO,EAAE,UAAU,EAA0B,MAAM,iBAAiB,CAAC"}
|
package/dist/menu.d.ts
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import type { DrawLike, FontLike, InputLike } from "./draw-like.js";
|
|
2
|
+
import { type FrameStyle } from "./panel.js";
|
|
3
|
+
export interface MenuItem {
|
|
4
|
+
label: string;
|
|
5
|
+
onSelect?: () => void;
|
|
6
|
+
/** Skipped by navigation and drawn dimmed. */
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
/** Shown in a right-hand column, e.g. a volume or "on"/"off". Read every draw. */
|
|
9
|
+
value?: () => string;
|
|
10
|
+
/** Left/right on this row (with `left`/`right` actions) call this with -1 or 1. */
|
|
11
|
+
change?: (dir: -1 | 1) => void;
|
|
12
|
+
}
|
|
13
|
+
export interface MenuActions<A extends string = string> {
|
|
14
|
+
up: A;
|
|
15
|
+
down: A;
|
|
16
|
+
confirm: A;
|
|
17
|
+
/** Optional; calls `onBack`. */
|
|
18
|
+
back?: A;
|
|
19
|
+
/** Optional; adjust the focused row's value. */
|
|
20
|
+
left?: A;
|
|
21
|
+
right?: A;
|
|
22
|
+
}
|
|
23
|
+
export interface MenuOptions<A extends string = string> {
|
|
24
|
+
/** Input action names. Default `{ up: "up", down: "down", confirm: "confirm" }`. */
|
|
25
|
+
actions?: MenuActions<A>;
|
|
26
|
+
onBack?: () => void;
|
|
27
|
+
/** Wrap from the last item to the first. Default true. */
|
|
28
|
+
wrap?: boolean;
|
|
29
|
+
}
|
|
30
|
+
export interface MenuStyle {
|
|
31
|
+
/** CSS font or `BitmapFont`. Default "8px monospace". */
|
|
32
|
+
font?: FontLike;
|
|
33
|
+
color?: string | number;
|
|
34
|
+
focusColor?: string | number;
|
|
35
|
+
disabledColor?: string | number;
|
|
36
|
+
/** Extra pixels between rows. Default 3. */
|
|
37
|
+
gap?: number;
|
|
38
|
+
/** "left" lines labels up after the cursor; "center" centres each label. Default "left". */
|
|
39
|
+
align?: "left" | "center";
|
|
40
|
+
/** Drawn beside the focused label, in its own column so labels never shift. Default ">". */
|
|
41
|
+
cursor?: string;
|
|
42
|
+
/** Pixels between the cursor and its label. Default 4. */
|
|
43
|
+
cursorGap?: number;
|
|
44
|
+
/** Minimum pixels between the label column and the value column. Default 12. */
|
|
45
|
+
valueGap?: number;
|
|
46
|
+
}
|
|
47
|
+
export interface MenuPanelStyle extends MenuStyle, FrameStyle {
|
|
48
|
+
title?: string;
|
|
49
|
+
titleFont?: FontLike;
|
|
50
|
+
titleColor?: string | number;
|
|
51
|
+
/** Pixels between the title and the first row. Default 6. */
|
|
52
|
+
titleGap?: number;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* A vertical list of choices for title screens, pause menus and settings. Navigates with your
|
|
56
|
+
* existing input actions, so keyboard, gamepad and touch bindings all work. Layout is measured, so
|
|
57
|
+
* rows, cursor and frame line up to the pixel with any font.
|
|
58
|
+
*
|
|
59
|
+
* ```ts
|
|
60
|
+
* const pause = new Menu([
|
|
61
|
+
* { label: "resume", onSelect: () => (paused = false) },
|
|
62
|
+
* { label: "restart", onSelect: reset },
|
|
63
|
+
* ], { actions: { up: "up", down: "down", confirm: "jump" } });
|
|
64
|
+
* // every step while paused: pause.update(input)
|
|
65
|
+
* // render: pause.drawPanel(draw, game.width / 2, game.height / 2, { title: "paused" })
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
export declare class Menu<A extends string = string> {
|
|
69
|
+
items: MenuItem[];
|
|
70
|
+
focus: number;
|
|
71
|
+
wrap: boolean;
|
|
72
|
+
onBack: (() => void) | undefined;
|
|
73
|
+
private actions;
|
|
74
|
+
/** Row boxes from the last draw, for pointer hits. */
|
|
75
|
+
private rows;
|
|
76
|
+
constructor(items: MenuItem[], options?: MenuOptions<A>);
|
|
77
|
+
get focused(): MenuItem | undefined;
|
|
78
|
+
/** Move focus by `delta`, skipping disabled items. */
|
|
79
|
+
move(delta: number): void;
|
|
80
|
+
/** Activate the focused item. */
|
|
81
|
+
select(): void;
|
|
82
|
+
/** Read input for this step. Returns true if anything happened. */
|
|
83
|
+
update(input: InputLike<A>): boolean;
|
|
84
|
+
/** `mirror` reserves the cursor column on the right too, so the labels sit on the box's centre. */
|
|
85
|
+
/**
|
|
86
|
+
* Tap at logical (x, y), using the rows from the last draw. Selects the row; on a row with a value,
|
|
87
|
+
* the left half lowers it and the right half raises it.
|
|
88
|
+
*/
|
|
89
|
+
tap(x: number, y: number): boolean;
|
|
90
|
+
private layout;
|
|
91
|
+
/** The box `draw` fills with this style, in logical pixels. */
|
|
92
|
+
measure(draw: DrawLike, style?: MenuStyle): {
|
|
93
|
+
w: number;
|
|
94
|
+
h: number;
|
|
95
|
+
};
|
|
96
|
+
/** Draw the rows with their box's top-left at (x, y). Positions snap to whole pixels. */
|
|
97
|
+
draw(draw: DrawLike, x: number, y: number, style?: MenuStyle): void;
|
|
98
|
+
private drawLayout;
|
|
99
|
+
/** A framed menu with an optional title, centred on (cx, cy). */
|
|
100
|
+
drawPanel(draw: DrawLike, cx: number, cy: number, style?: MenuPanelStyle): void;
|
|
101
|
+
inspect(): {
|
|
102
|
+
focus: number;
|
|
103
|
+
focused: string | undefined;
|
|
104
|
+
items: string[];
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=menu.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"menu.d.ts","sourceRoot":"","sources":["../src/menu.ts"],"names":[],"mappings":"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"}
|
package/dist/menu.js
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { panelAround } from "./panel.js";
|
|
2
|
+
const FONT = "8px monospace";
|
|
3
|
+
/**
|
|
4
|
+
* A vertical list of choices for title screens, pause menus and settings. Navigates with your
|
|
5
|
+
* existing input actions, so keyboard, gamepad and touch bindings all work. Layout is measured, so
|
|
6
|
+
* rows, cursor and frame line up to the pixel with any font.
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* const pause = new Menu([
|
|
10
|
+
* { label: "resume", onSelect: () => (paused = false) },
|
|
11
|
+
* { label: "restart", onSelect: reset },
|
|
12
|
+
* ], { actions: { up: "up", down: "down", confirm: "jump" } });
|
|
13
|
+
* // every step while paused: pause.update(input)
|
|
14
|
+
* // render: pause.drawPanel(draw, game.width / 2, game.height / 2, { title: "paused" })
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export class Menu {
|
|
18
|
+
items;
|
|
19
|
+
focus = 0;
|
|
20
|
+
wrap;
|
|
21
|
+
onBack;
|
|
22
|
+
actions;
|
|
23
|
+
/** Row boxes from the last draw, for pointer hits. */
|
|
24
|
+
rows = [];
|
|
25
|
+
constructor(items, options = {}) {
|
|
26
|
+
this.items = items;
|
|
27
|
+
this.actions = options.actions ?? { up: "up", down: "down", confirm: "confirm" };
|
|
28
|
+
this.wrap = options.wrap ?? true;
|
|
29
|
+
this.onBack = options.onBack;
|
|
30
|
+
if (this.items[this.focus]?.disabled)
|
|
31
|
+
this.move(1);
|
|
32
|
+
}
|
|
33
|
+
get focused() {
|
|
34
|
+
return this.items[this.focus];
|
|
35
|
+
}
|
|
36
|
+
/** Move focus by `delta`, skipping disabled items. */
|
|
37
|
+
move(delta) {
|
|
38
|
+
const n = this.items.length;
|
|
39
|
+
if (n === 0)
|
|
40
|
+
return;
|
|
41
|
+
let i = this.focus;
|
|
42
|
+
for (let tries = 0; tries < n; tries++) {
|
|
43
|
+
const next = i + delta;
|
|
44
|
+
if (!this.wrap && (next < 0 || next >= n))
|
|
45
|
+
return;
|
|
46
|
+
i = (next + n) % n;
|
|
47
|
+
if (!this.items[i]?.disabled) {
|
|
48
|
+
this.focus = i;
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/** Activate the focused item. */
|
|
54
|
+
select() {
|
|
55
|
+
const item = this.focused;
|
|
56
|
+
if (item && !item.disabled)
|
|
57
|
+
item.onSelect?.();
|
|
58
|
+
}
|
|
59
|
+
/** Read input for this step. Returns true if anything happened. */
|
|
60
|
+
update(input) {
|
|
61
|
+
// A tap is handled by the rows alone, so a pointer bound to "confirm" doesn't also select the focused row.
|
|
62
|
+
if (input.pointer?.pressed)
|
|
63
|
+
return this.tap(input.pointer.x, input.pointer.y);
|
|
64
|
+
const a = this.actions;
|
|
65
|
+
if (input.pressed(a.up))
|
|
66
|
+
this.move(-1);
|
|
67
|
+
else if (input.pressed(a.down))
|
|
68
|
+
this.move(1);
|
|
69
|
+
else if (input.pressed(a.confirm))
|
|
70
|
+
this.select();
|
|
71
|
+
else if (a.back && input.pressed(a.back))
|
|
72
|
+
this.onBack?.();
|
|
73
|
+
else if (a.left && input.pressed(a.left))
|
|
74
|
+
this.focused?.change?.(-1);
|
|
75
|
+
else if (a.right && input.pressed(a.right))
|
|
76
|
+
this.focused?.change?.(1);
|
|
77
|
+
else
|
|
78
|
+
return false;
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
/** `mirror` reserves the cursor column on the right too, so the labels sit on the box's centre. */
|
|
82
|
+
/**
|
|
83
|
+
* Tap at logical (x, y), using the rows from the last draw. Selects the row; on a row with a value,
|
|
84
|
+
* the left half lowers it and the right half raises it.
|
|
85
|
+
*/
|
|
86
|
+
tap(x, y) {
|
|
87
|
+
const i = this.rows.findIndex((r) => x >= r.x && x < r.x + r.w && y >= r.y && y < r.y + r.h);
|
|
88
|
+
const item = this.items[i];
|
|
89
|
+
if (!item || item.disabled)
|
|
90
|
+
return false;
|
|
91
|
+
this.focus = i;
|
|
92
|
+
const row = this.rows[i];
|
|
93
|
+
if (item.change)
|
|
94
|
+
item.change(x < row.x + row.w / 2 ? -1 : 1);
|
|
95
|
+
else
|
|
96
|
+
this.select();
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
layout(draw, style, mirror = style.align === "center") {
|
|
100
|
+
const font = style.font ?? FONT;
|
|
101
|
+
const cursor = draw.measure(style.cursor ?? ">", font);
|
|
102
|
+
const gutter = cursor.w + (style.cursorGap ?? 4);
|
|
103
|
+
let rowH = cursor.h;
|
|
104
|
+
const widths = this.items.map((item) => {
|
|
105
|
+
const m = draw.measure(item.label, font);
|
|
106
|
+
rowH = Math.max(rowH, m.h);
|
|
107
|
+
return m.w;
|
|
108
|
+
});
|
|
109
|
+
const labelW = Math.max(0, ...widths);
|
|
110
|
+
const values = this.items.map((item) => item.value?.());
|
|
111
|
+
const valueWidths = values.map((v) => (v ? draw.measure(v, font).w : 0));
|
|
112
|
+
const valueW = Math.max(0, ...valueWidths);
|
|
113
|
+
const gap = style.gap ?? 3;
|
|
114
|
+
const n = this.items.length;
|
|
115
|
+
const w = labelW + gutter * (mirror ? 2 : 1) + (valueW ? (style.valueGap ?? 12) + valueW : 0);
|
|
116
|
+
return {
|
|
117
|
+
mirror,
|
|
118
|
+
font,
|
|
119
|
+
gutter,
|
|
120
|
+
cursorW: cursor.w,
|
|
121
|
+
rowH,
|
|
122
|
+
gap,
|
|
123
|
+
labelW,
|
|
124
|
+
widths,
|
|
125
|
+
values,
|
|
126
|
+
valueWidths,
|
|
127
|
+
w,
|
|
128
|
+
h: n ? n * rowH + (n - 1) * gap : 0,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
/** The box `draw` fills with this style, in logical pixels. */
|
|
132
|
+
measure(draw, style = {}) {
|
|
133
|
+
const { w, h } = this.layout(draw, style);
|
|
134
|
+
return { w, h };
|
|
135
|
+
}
|
|
136
|
+
/** Draw the rows with their box's top-left at (x, y). Positions snap to whole pixels. */
|
|
137
|
+
draw(draw, x, y, style = {}) {
|
|
138
|
+
this.drawLayout(draw, x, y, style, this.layout(draw, style));
|
|
139
|
+
}
|
|
140
|
+
drawLayout(draw, x, y, style, l) {
|
|
141
|
+
const mirror = l.mirror;
|
|
142
|
+
this.rows = [];
|
|
143
|
+
const x0 = Math.round(x);
|
|
144
|
+
const y0 = Math.round(y);
|
|
145
|
+
for (let i = 0; i < this.items.length; i++) {
|
|
146
|
+
const item = this.items[i];
|
|
147
|
+
const focused = i === this.focus;
|
|
148
|
+
const color = item.disabled ? (style.disabledColor ?? "#666") : focused ? (style.focusColor ?? "#fff") : (style.color ?? "#999");
|
|
149
|
+
const offset = style.align === "center" ? Math.floor((l.labelW - (l.widths[i] ?? 0)) / 2) : 0;
|
|
150
|
+
const lx = x0 + l.gutter + offset;
|
|
151
|
+
const ry = y0 + i * (l.rowH + l.gap);
|
|
152
|
+
// Hit box: the full row width, extended into half the gap above and below so taps between rows land.
|
|
153
|
+
this.rows.push({ x: x0, y: ry - Math.floor(l.gap / 2), w: l.w, h: l.rowH + l.gap });
|
|
154
|
+
draw.text(item.label, lx, ry, { font: l.font, color });
|
|
155
|
+
if (focused)
|
|
156
|
+
draw.text(style.cursor ?? ">", lx - l.gutter, ry, { font: l.font, color });
|
|
157
|
+
const value = l.values[i];
|
|
158
|
+
// Values are right-aligned to the box edge, so a column of numbers lines up.
|
|
159
|
+
if (value)
|
|
160
|
+
draw.text(value, x0 + l.w - (mirror ? l.gutter : 0) - (l.valueWidths[i] ?? 0), ry, { font: l.font, color });
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/** A framed menu with an optional title, centred on (cx, cy). */
|
|
164
|
+
drawPanel(draw, cx, cy, style = {}) {
|
|
165
|
+
const menu = this.layout(draw, style, true);
|
|
166
|
+
const titleFont = style.titleFont ?? menu.font;
|
|
167
|
+
const title = style.title ? draw.measure(style.title, titleFont) : { w: 0, h: 0 };
|
|
168
|
+
const titleH = style.title ? title.h + (style.titleGap ?? 6) : 0;
|
|
169
|
+
const w = Math.max(menu.w, title.w);
|
|
170
|
+
const at = panelAround(draw, cx, cy, w, titleH + menu.h, style);
|
|
171
|
+
if (style.title)
|
|
172
|
+
draw.text(style.title, at.x + Math.floor((w - title.w) / 2), at.y, {
|
|
173
|
+
font: titleFont,
|
|
174
|
+
color: style.titleColor ?? style.focusColor ?? "#fff",
|
|
175
|
+
});
|
|
176
|
+
this.drawLayout(draw, at.x + Math.floor((w - menu.w) / 2), at.y + titleH, style, menu);
|
|
177
|
+
}
|
|
178
|
+
inspect() {
|
|
179
|
+
return { focus: this.focus, focused: this.focused?.label, items: this.items.map((i) => i.label) };
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=menu.js.map
|
package/dist/menu.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"menu.js","sourceRoot":"","sources":["../src/menu.ts"],"names":[],"mappings":"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"}
|
package/dist/panel.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { DrawLike } from "./draw-like.js";
|
|
2
|
+
/**
|
|
3
|
+
* A framed box: each colour in `borders` is a 1px ring, outermost first, then `fill` inside.
|
|
4
|
+
* `panel(draw, 48, 40, 160, 64, ["#c3a6d8", "#1a1020", "#5b3a6e"], "#1a1020")`
|
|
5
|
+
*/
|
|
6
|
+
export declare function panel(draw: DrawLike, x: number, y: number, w: number, h: number, borders: readonly (string | number)[], fill?: string | number): void;
|
|
7
|
+
export interface FrameStyle {
|
|
8
|
+
/** 1px rings, outermost first. Default one light ring. */
|
|
9
|
+
borders?: readonly (string | number)[];
|
|
10
|
+
/** Default a dark grey. */
|
|
11
|
+
fill?: string | number;
|
|
12
|
+
/** Space between the innermost ring and the content. Default 6. */
|
|
13
|
+
padding?: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Draw a panel that fits content of `w`×`h` centred on (cx, cy), snapped to whole pixels.
|
|
17
|
+
* Returns the content's top-left, where you draw what goes inside.
|
|
18
|
+
*/
|
|
19
|
+
export declare function panelAround(draw: DrawLike, cx: number, cy: number, w: number, h: number, style?: FrameStyle): {
|
|
20
|
+
x: number;
|
|
21
|
+
y: number;
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=panel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"panel.d.ts","sourceRoot":"","sources":["../src/panel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C;;;GAGG;AACH,wBAAgB,KAAK,CACnB,IAAI,EAAE,QAAQ,EACd,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,OAAO,EAAE,SAAS,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,EACrC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,GACrB,IAAI,CAQN;AAED,MAAM,WAAW,UAAU;IACzB,0DAA0D;IAC1D,OAAO,CAAC,EAAE,SAAS,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;IACvC,2BAA2B;IAC3B,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,mEAAmE;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,QAAQ,EACd,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,EACV,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,KAAK,GAAE,UAAe,GACrB;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAS1B"}
|
package/dist/panel.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A framed box: each colour in `borders` is a 1px ring, outermost first, then `fill` inside.
|
|
3
|
+
* `panel(draw, 48, 40, 160, 64, ["#c3a6d8", "#1a1020", "#5b3a6e"], "#1a1020")`
|
|
4
|
+
*/
|
|
5
|
+
export function panel(draw, x, y, w, h, borders, fill) {
|
|
6
|
+
let i = 0;
|
|
7
|
+
for (const c of borders) {
|
|
8
|
+
if (w - i * 2 <= 0 || h - i * 2 <= 0)
|
|
9
|
+
return;
|
|
10
|
+
draw.rect(x + i, y + i, w - i * 2, h - i * 2, c);
|
|
11
|
+
i++;
|
|
12
|
+
}
|
|
13
|
+
if (fill !== undefined && w - i * 2 > 0 && h - i * 2 > 0)
|
|
14
|
+
draw.rect(x + i, y + i, w - i * 2, h - i * 2, fill);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Draw a panel that fits content of `w`×`h` centred on (cx, cy), snapped to whole pixels.
|
|
18
|
+
* Returns the content's top-left, where you draw what goes inside.
|
|
19
|
+
*/
|
|
20
|
+
export function panelAround(draw, cx, cy, w, h, style = {}) {
|
|
21
|
+
const borders = style.borders ?? ["#ddd"];
|
|
22
|
+
const inset = borders.length + (style.padding ?? 6);
|
|
23
|
+
const outerW = Math.ceil(w) + inset * 2;
|
|
24
|
+
const outerH = Math.ceil(h) + inset * 2;
|
|
25
|
+
const x = Math.round(cx - outerW / 2);
|
|
26
|
+
const y = Math.round(cy - outerH / 2);
|
|
27
|
+
panel(draw, x, y, outerW, outerH, borders, style.fill ?? "#222");
|
|
28
|
+
return { x: x + inset, y: y + inset };
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=panel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"panel.js","sourceRoot":"","sources":["../src/panel.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,UAAU,KAAK,CACnB,IAAc,EACd,CAAS,EACT,CAAS,EACT,CAAS,EACT,CAAS,EACT,OAAqC,EACrC,IAAsB;IAEtB,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,OAAO;QAC7C,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QACjD,CAAC,EAAE,CAAC;IACN,CAAC;IACD,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC;AAChH,CAAC;AAWD;;;GAGG;AACH,MAAM,UAAU,WAAW,CACzB,IAAc,EACd,EAAU,EACV,EAAU,EACV,CAAS,EACT,CAAS,EACT,KAAK,GAAe,EAAE;IAEtB,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC;IAC1C,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC;IACpD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC;IACxC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC;IACxC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,MAAM,GAAG,CAAC,CAAC,CAAC;IACtC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,MAAM,GAAG,CAAC,CAAC,CAAC;IACtC,KAAK,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,IAAI,IAAI,MAAM,CAAC,CAAC;IACjE,OAAO,EAAE,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC;AACxC,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { DrawLike } from "./draw-like.js";
|
|
2
|
+
export interface TransitionOptions {
|
|
3
|
+
/** Seconds to fade out, and again to fade back in. Default 0.35. */
|
|
4
|
+
duration?: number;
|
|
5
|
+
/** Cover colour. Default black. */
|
|
6
|
+
color?: string | number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* A fade-out → swap → fade-in screen transition. `run(swap)` covers the screen, calls `swap` once
|
|
10
|
+
* fully covered (move the player, load the level, change scene), then uncovers it. Driven by
|
|
11
|
+
* `update(dt)`, so it stays deterministic under replays and scenarios.
|
|
12
|
+
*
|
|
13
|
+
* ```ts
|
|
14
|
+
* const fade = new Transition({ color: "#1a1020" });
|
|
15
|
+
* fade.run(() => placePlayer(stairs));
|
|
16
|
+
* // every step: fade.update(dt); if (fade.blocking) return;
|
|
17
|
+
* // after drawing the world: fade.draw(draw, 0, 0, game.width, game.height);
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export declare class Transition {
|
|
21
|
+
duration: number;
|
|
22
|
+
color: string | number;
|
|
23
|
+
private t;
|
|
24
|
+
private swap;
|
|
25
|
+
constructor(options?: TransitionOptions);
|
|
26
|
+
/** Start a transition. Ignored while one is already running, so a held button can't stack them. */
|
|
27
|
+
run(swap?: () => void): boolean;
|
|
28
|
+
/** Cancel without calling the pending swap, e.g. on a full reset. */
|
|
29
|
+
stop(): void;
|
|
30
|
+
/** A transition is running. */
|
|
31
|
+
get active(): boolean;
|
|
32
|
+
/** Still covering or swapping: gameplay usually pauses until the fade-in starts. */
|
|
33
|
+
get blocking(): boolean;
|
|
34
|
+
/** How covered the screen is, 0 to 1. */
|
|
35
|
+
get cover(): number;
|
|
36
|
+
update(dt: number): void;
|
|
37
|
+
/** Draw the cover over a region (usually the whole screen, or the play area below a HUD). */
|
|
38
|
+
draw(draw: DrawLike, x: number, y: number, w: number, h: number): void;
|
|
39
|
+
inspect(): {
|
|
40
|
+
active: boolean;
|
|
41
|
+
cover: number;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=transition.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transition.d.ts","sourceRoot":"","sources":["../src/transition.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,MAAM,WAAW,iBAAiB;IAChC,oEAAoE;IACpE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mCAAmC;IACnC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACzB;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,UAAU;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,OAAO,CAAC,CAAC,CAAM;IACf,OAAO,CAAC,IAAI,CAA2B;IAEvC,YAAY,OAAO,GAAE,iBAAsB,EAG1C;IAED,mGAAmG;IACnG,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAK9B;IAED,qEAAqE;IACrE,IAAI,IAAI,IAAI,CAGX;IAED,+BAA+B;IAC/B,IAAI,MAAM,IAAI,OAAO,CAEpB;IAED,oFAAoF;IACpF,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,yCAAyC;IACzC,IAAI,KAAK,IAAI,MAAM,CAGlB;IAED,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAUvB;IAED,6FAA6F;IAC7F,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAOrE;IAED,OAAO,IAAI;QAAE,MAAM,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAE5C;CACF"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A fade-out → swap → fade-in screen transition. `run(swap)` covers the screen, calls `swap` once
|
|
3
|
+
* fully covered (move the player, load the level, change scene), then uncovers it. Driven by
|
|
4
|
+
* `update(dt)`, so it stays deterministic under replays and scenarios.
|
|
5
|
+
*
|
|
6
|
+
* ```ts
|
|
7
|
+
* const fade = new Transition({ color: "#1a1020" });
|
|
8
|
+
* fade.run(() => placePlayer(stairs));
|
|
9
|
+
* // every step: fade.update(dt); if (fade.blocking) return;
|
|
10
|
+
* // after drawing the world: fade.draw(draw, 0, 0, game.width, game.height);
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
export class Transition {
|
|
14
|
+
duration;
|
|
15
|
+
color;
|
|
16
|
+
t = -1;
|
|
17
|
+
swap;
|
|
18
|
+
constructor(options = {}) {
|
|
19
|
+
this.duration = options.duration ?? 0.35;
|
|
20
|
+
this.color = options.color ?? "#000";
|
|
21
|
+
}
|
|
22
|
+
/** Start a transition. Ignored while one is already running, so a held button can't stack them. */
|
|
23
|
+
run(swap) {
|
|
24
|
+
if (this.active)
|
|
25
|
+
return false;
|
|
26
|
+
this.t = 0;
|
|
27
|
+
this.swap = swap;
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
/** Cancel without calling the pending swap, e.g. on a full reset. */
|
|
31
|
+
stop() {
|
|
32
|
+
this.t = -1;
|
|
33
|
+
this.swap = undefined;
|
|
34
|
+
}
|
|
35
|
+
/** A transition is running. */
|
|
36
|
+
get active() {
|
|
37
|
+
return this.t >= 0;
|
|
38
|
+
}
|
|
39
|
+
/** Still covering or swapping: gameplay usually pauses until the fade-in starts. */
|
|
40
|
+
get blocking() {
|
|
41
|
+
return this.t >= 0 && this.t < 1;
|
|
42
|
+
}
|
|
43
|
+
/** How covered the screen is, 0 to 1. */
|
|
44
|
+
get cover() {
|
|
45
|
+
if (this.t < 0)
|
|
46
|
+
return 0;
|
|
47
|
+
return this.t < 1 ? this.t : Math.max(0, 2 - this.t);
|
|
48
|
+
}
|
|
49
|
+
update(dt) {
|
|
50
|
+
if (this.t < 0)
|
|
51
|
+
return;
|
|
52
|
+
const before = this.t;
|
|
53
|
+
this.t += dt / Math.max(this.duration, 1e-6);
|
|
54
|
+
if (before < 1 && this.t >= 1) {
|
|
55
|
+
const swap = this.swap;
|
|
56
|
+
this.swap = undefined;
|
|
57
|
+
swap?.();
|
|
58
|
+
}
|
|
59
|
+
if (this.t >= 2)
|
|
60
|
+
this.t = -1;
|
|
61
|
+
}
|
|
62
|
+
/** Draw the cover over a region (usually the whole screen, or the play area below a HUD). */
|
|
63
|
+
draw(draw, x, y, w, h) {
|
|
64
|
+
const a = this.cover;
|
|
65
|
+
if (a <= 0)
|
|
66
|
+
return;
|
|
67
|
+
const prev = draw.alpha;
|
|
68
|
+
draw.alpha = prev * a;
|
|
69
|
+
draw.rect(x, y, w, h, this.color);
|
|
70
|
+
draw.alpha = prev;
|
|
71
|
+
}
|
|
72
|
+
inspect() {
|
|
73
|
+
return { active: this.active, cover: this.cover };
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=transition.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transition.js","sourceRoot":"","sources":["../src/transition.ts"],"names":[],"mappings":"AASA;;;;;;;;;;;GAWG;AACH,MAAM,OAAO,UAAU;IACrB,QAAQ,CAAS;IACjB,KAAK,CAAkB;IACf,CAAC,GAAG,CAAC,CAAC,CAAC;IACP,IAAI,CAA2B;IAEvC,YAAY,OAAO,GAAsB,EAAE;QACzC,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,IAAI,CAAC;QACzC,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,MAAM,CAAC;IACvC,CAAC;IAED,mGAAmG;IACnG,GAAG,CAAC,IAAiB;QACnB,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QAC9B,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QACX,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,qEAAqE;IACrE,IAAI;QACF,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACZ,IAAI,CAAC,IAAI,GAAG,SAAS,CAAC;IACxB,CAAC;IAED,+BAA+B;IAC/B,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;IACrB,CAAC;IAED,oFAAoF;IACpF,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;IACnC,CAAC;IAED,yCAAyC;IACzC,IAAI,KAAK;QACP,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC;YAAE,OAAO,CAAC,CAAC;QACzB,OAAO,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACvD,CAAC;IAED,MAAM,CAAC,EAAU;QACf,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC;YAAE,OAAO;QACvB,MAAM,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC;QACtB,IAAI,CAAC,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAC7C,IAAI,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;YACvB,IAAI,CAAC,IAAI,GAAG,SAAS,CAAC;YACtB,IAAI,EAAE,EAAE,CAAC;QACX,CAAC;QACD,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC;YAAE,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/B,CAAC;IAED,6FAA6F;IAC7F,IAAI,CAAC,IAAc,EAAE,CAAS,EAAE,CAAS,EAAE,CAAS,EAAE,CAAS;QAC7D,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC;QACrB,IAAI,CAAC,IAAI,CAAC;YAAE,OAAO;QACnB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC;QACxB,IAAI,CAAC,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC;QACtB,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QAClC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;IACpB,CAAC;IAED,OAAO;QACL,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC;IACpD,CAAC;CACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,46 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@debuffjp/ui",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Screen transitions, menus and panels for debuff",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"game",
|
|
7
|
+
"gamedev",
|
|
8
|
+
"game-engine",
|
|
9
|
+
"2d",
|
|
10
|
+
"html5-game",
|
|
11
|
+
"browser-game",
|
|
12
|
+
"debuff",
|
|
13
|
+
"ui",
|
|
14
|
+
"menu",
|
|
15
|
+
"hud",
|
|
16
|
+
"transition"
|
|
17
|
+
],
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"homepage": "https://debuff.jp",
|
|
20
|
+
"bugs": "https://github.com/debuffjp/debuff/issues",
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/debuffjp/debuff.git",
|
|
24
|
+
"directory": "packages/ui"
|
|
25
|
+
},
|
|
26
|
+
"type": "module",
|
|
27
|
+
"sideEffects": false,
|
|
28
|
+
"main": "./dist/index.js",
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"exports": {
|
|
31
|
+
".": {
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"default": "./dist/index.js"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"dist",
|
|
38
|
+
"README.md"
|
|
39
|
+
],
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "tsc -b"
|
|
42
|
+
},
|
|
43
|
+
"publishConfig": {
|
|
44
|
+
"access": "public"
|
|
45
|
+
}
|
|
46
|
+
}
|