kaplay-plugin-ui 0.0.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Menglin "Mark" Xu <mark@remarkablemark.org>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,95 @@
1
+ # kaplay-plugin-ui
2
+
3
+ [![NPM](https://nodei.co/npm/kaplay-plugin-ui.svg)](https://www.npmjs.com/package/kaplay-plugin-ui)
4
+
5
+ [![NPM version](https://img.shields.io/npm/v/kaplay-plugin-ui.svg)](https://www.npmjs.com/package/kaplay-plugin-ui)
6
+ [![build](https://github.com/remarkablegames/kaplay-plugin-ui/actions/workflows/build.yml/badge.svg)](https://github.com/remarkablegames/kaplay-plugin-ui/actions/workflows/build.yml)
7
+ [![codecov](https://codecov.io/gh/remarkablegames/kaplay-plugin-ui/graph/badge.svg?token=54qf1pKjqI)](https://codecov.io/gh/remarkablegames/kaplay-plugin-ui)
8
+
9
+ 🦖 KAPLAY plugin UI:
10
+
11
+ - `addButton(text?, opt?)`: button game object
12
+ - `button(text?, opt?)`: button component (use in `add([button(...)])`)
13
+ - `setTheme(theme)`: update theme
14
+
15
+ ## Prerequisites
16
+
17
+ Install [kaplay](https://www.npmjs.com/package/kaplay):
18
+
19
+ ```sh
20
+ npm install kaplay
21
+ ```
22
+
23
+ ## Install
24
+
25
+ [NPM](https://www.npmjs.com/package/kaplay-plugin-ui):
26
+
27
+ ```sh
28
+ npm install kaplay-plugin-ui
29
+ ```
30
+
31
+ [CDN](https://unpkg.com/browse/kaplay-plugin-ui/):
32
+
33
+ ```html
34
+ <script src="https://unpkg.com/kaplay-plugin-ui@latest/dist/plugin.umd.js"></script>
35
+ ```
36
+
37
+ ## Usage
38
+
39
+ Import the plugin:
40
+
41
+ ```ts
42
+ import kaplay from 'kaplay';
43
+ import { uiPlugin } from 'kaplay-plugin-ui';
44
+
45
+ const k = kaplay({
46
+ plugins: [uiPlugin],
47
+ });
48
+ ```
49
+
50
+ Use the plugin:
51
+
52
+ ```ts
53
+ const button = k.addButton('Start', {
54
+ onClick: () => {
55
+ k.go('game');
56
+ },
57
+ });
58
+ ```
59
+
60
+ Customize the theme:
61
+
62
+ ```ts
63
+ k.setTheme({
64
+ palette: {
65
+ primary: k.rgb(80, 160, 255),
66
+ text: k.rgb(255, 255, 255),
67
+ },
68
+ shape: {
69
+ borderRadius: 8,
70
+ },
71
+ });
72
+ ```
73
+
74
+ To load the plugin using a script:
75
+
76
+ ```html
77
+ <script src="https://unpkg.com/kaplay@latest/dist/kaplay.js"></script>
78
+ <script src="https://unpkg.com/kaplay-plugin-ui@latest/dist/plugin.umd.js"></script>
79
+
80
+ <script>
81
+ const k = kaplay({
82
+ plugins: [KaplayPluginUI.uiPlugin],
83
+ });
84
+
85
+ const button = k.addButton('Start');
86
+ </script>
87
+ ```
88
+
89
+ ## Release
90
+
91
+ Release is automated with [Release Please](https://github.com/googleapis/release-please).
92
+
93
+ ## License
94
+
95
+ [MIT](https://github.com/remarkablegames/kaplay-plugin-ui/blob/master/LICENSE)
@@ -0,0 +1,243 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ // src/button.ts
3
+ function createButtonComponent(k, theme, text, opt) {
4
+ let currentText = text;
5
+ let currentOpt = { ...opt };
6
+ const readWidth = () => {
7
+ return currentOpt.width ?? theme.components.button.width;
8
+ };
9
+ const readHeight = () => {
10
+ return currentOpt.height ?? theme.components.button.height;
11
+ };
12
+ const comp = {
13
+ id: "button",
14
+ get text() {
15
+ return currentText;
16
+ },
17
+ set text(value) {
18
+ currentText = value;
19
+ },
20
+ get width() {
21
+ return readWidth();
22
+ },
23
+ set width(value) {
24
+ currentOpt.width = value;
25
+ },
26
+ get height() {
27
+ return readHeight();
28
+ },
29
+ set height(value) {
30
+ currentOpt.height = value;
31
+ },
32
+ renderArea() {
33
+ return new k.Rect(k.vec2(), comp.width, comp.height);
34
+ },
35
+ setStyle(next) {
36
+ currentOpt = {
37
+ ...currentOpt,
38
+ ...next
39
+ };
40
+ },
41
+ add() {},
42
+ draw() {
43
+ const width = comp.width;
44
+ const height = comp.height;
45
+ const radius = currentOpt.radius ?? theme.shape.borderRadius;
46
+ const fill = currentOpt.fill ?? theme.palette.primary;
47
+ const textColor = currentOpt.textColor ?? theme.palette.text;
48
+ const size = currentOpt.size ?? theme.typography.fontSize;
49
+ const font = currentOpt.font ?? theme.typography.fontFamily;
50
+ const drawRect = k.drawRect;
51
+ drawRect?.({
52
+ pos: k.vec2(),
53
+ width,
54
+ height,
55
+ color: fill,
56
+ radius
57
+ });
58
+ const drawText = k.drawText;
59
+ drawText?.({
60
+ text: currentText,
61
+ pos: k.vec2(width / 2, height / 2),
62
+ size,
63
+ font,
64
+ color: textColor,
65
+ anchor: "center"
66
+ });
67
+ },
68
+ destroy() {}
69
+ };
70
+ return comp;
71
+ }
72
+ function createButtonObject(k, theme, text, opt) {
73
+ const width = opt.width ?? theme.components.button.width;
74
+ const height = opt.height ?? theme.components.button.height;
75
+ const radius = opt.radius ?? theme.shape.borderRadius;
76
+ const baseColor = opt.color ?? opt.fill ?? theme.palette.primary;
77
+ const hoverColor = opt.hoverColor ?? theme.palette.hover;
78
+ const pressedColor = opt.pressedColor ?? theme.palette.pressed;
79
+ const textColor = opt.textColor ?? theme.palette.text;
80
+ const size = opt.size ?? theme.typography.fontSize;
81
+ const font = opt.font ?? theme.typography.fontFamily;
82
+ const pos = opt.pos ?? k.vec2();
83
+ const obj = k.add([
84
+ k.rect(width, height, radius > 0 ? { radius } : void 0),
85
+ k.pos(pos),
86
+ k.area(),
87
+ k.color(baseColor),
88
+ k.anchor("center")
89
+ ]);
90
+ const label = obj.add([
91
+ k.text(text, {
92
+ size,
93
+ font
94
+ }),
95
+ k.color(textColor),
96
+ k.anchor("center")
97
+ ]);
98
+ let currentText = text;
99
+ let currentColor = baseColor;
100
+ obj.onClick(() => {
101
+ obj.color = pressedColor;
102
+ opt.onClick?.();
103
+ });
104
+ obj.onHover(() => {
105
+ obj.color = hoverColor;
106
+ obj.scale = k.vec2(1.05, 1.05);
107
+ k.setCursor("pointer");
108
+ });
109
+ obj.onHoverEnd(() => {
110
+ obj.color = currentColor;
111
+ obj.scale = k.vec2(1, 1);
112
+ k.setCursor("default");
113
+ });
114
+ obj.setSize = (w, h) => {
115
+ obj.width = w;
116
+ obj.height = h;
117
+ };
118
+ obj.setButtonColor = (color) => {
119
+ currentColor = color;
120
+ obj.color = color;
121
+ };
122
+ obj.setButtonText = (value) => {
123
+ currentText = value;
124
+ label.text = value;
125
+ };
126
+ obj.setButtonTextSize = (value) => {
127
+ label.textSize = value;
128
+ };
129
+ obj.setPosition = (x, y) => {
130
+ obj.pos = k.vec2(x, y);
131
+ };
132
+ label.text = currentText;
133
+ return obj;
134
+ }
135
+ // src/theme.ts
136
+ const DEFAULT_PRIMARY = "#4f8cff";
137
+ const DEFAULT_TEXT = "#ffffff";
138
+ /** Default theme used when no theme is provided. */
139
+ const defaultTheme = {
140
+ palette: {
141
+ primary: DEFAULT_PRIMARY,
142
+ secondary: DEFAULT_PRIMARY,
143
+ background: "#111827",
144
+ surface: "#1f2937",
145
+ text: DEFAULT_TEXT,
146
+ textSecondary: DEFAULT_TEXT,
147
+ hover: DEFAULT_PRIMARY,
148
+ pressed: DEFAULT_PRIMARY,
149
+ disabled: "#6b7280",
150
+ error: "#ef4444"
151
+ },
152
+ typography: {
153
+ fontFamily: "sans-serif",
154
+ fontSize: 18,
155
+ fontWeight: 400,
156
+ lineHeight: 1.2
157
+ },
158
+ shape: { borderRadius: 0 },
159
+ spacing: 8,
160
+ components: {
161
+ button: {
162
+ width: 120,
163
+ height: 40,
164
+ minWidth: 120,
165
+ paddingX: 12,
166
+ paddingY: 8,
167
+ textTransform: "none"
168
+ },
169
+ modal: {
170
+ backdropOpacity: .5,
171
+ borderRadius: 8
172
+ },
173
+ tooltip: {
174
+ background: "#111827",
175
+ textColor: DEFAULT_TEXT,
176
+ delay: 300
177
+ }
178
+ }
179
+ };
180
+ /** Resolve a partial theme into a fully resolved theme. */
181
+ function resolveTheme(input) {
182
+ return {
183
+ palette: {
184
+ ...defaultTheme.palette,
185
+ ...input?.palette
186
+ },
187
+ typography: {
188
+ ...defaultTheme.typography,
189
+ ...input?.typography
190
+ },
191
+ shape: {
192
+ ...defaultTheme.shape,
193
+ ...input?.shape
194
+ },
195
+ spacing: input?.spacing ?? defaultTheme.spacing,
196
+ components: {
197
+ button: {
198
+ ...defaultTheme.components.button,
199
+ ...input?.components?.button,
200
+ width: input?.components?.button?.width ?? input?.components?.button?.minWidth ?? defaultTheme.components.button.width
201
+ },
202
+ modal: {
203
+ ...defaultTheme.components.modal,
204
+ ...input?.components?.modal
205
+ },
206
+ tooltip: {
207
+ ...defaultTheme.components.tooltip,
208
+ ...input?.components?.tooltip
209
+ }
210
+ }
211
+ };
212
+ }
213
+ // src/plugin.ts
214
+ /** KAPLAY UI plugin. */
215
+ function uiPlugin(k, theme = {}) {
216
+ let userTheme = theme;
217
+ let currentTheme = resolveTheme(userTheme);
218
+ const getTheme = () => currentTheme;
219
+ const setTheme = (next) => {
220
+ userTheme = {
221
+ ...userTheme,
222
+ ...next
223
+ };
224
+ currentTheme = resolveTheme(userTheme);
225
+ };
226
+ const button = (text = "", opt = {}) => {
227
+ return createButtonComponent(k, currentTheme, text, opt);
228
+ };
229
+ const addButton = (text = "", opt = {}) => {
230
+ return createButtonObject(k, currentTheme, text, opt);
231
+ };
232
+ return {
233
+ button,
234
+ addButton,
235
+ setTheme,
236
+ getTheme
237
+ };
238
+ }
239
+ exports.createButtonComponent = createButtonComponent;
240
+ exports.createButtonObject = createButtonObject;
241
+ exports.uiPlugin = uiPlugin;
242
+
243
+ //# sourceMappingURL=plugin.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.cjs","names":[],"sources":["../src/button.ts","../src/theme.ts","../src/plugin.ts"],"sourcesContent":["import type { Comp, GameObj, KAPLAYCtx, Vec2 } from 'kaplay';\n\nimport type { ButtonComp, ButtonCompOpt, Color, Theme } from './types';\n\nexport function createButtonComponent(\n k: KAPLAYCtx,\n theme: Theme,\n text: string,\n opt: ButtonCompOpt,\n): ButtonComp {\n let currentText = text;\n let currentOpt: ButtonCompOpt = { ...opt };\n\n const readWidth = (): number => {\n return currentOpt.width ?? theme.components.button.width;\n };\n\n const readHeight = (): number => {\n return currentOpt.height ?? theme.components.button.height;\n };\n\n const comp: ButtonComp = {\n id: 'button',\n get text() {\n return currentText;\n },\n set text(value: string) {\n currentText = value;\n },\n get width() {\n return readWidth();\n },\n set width(value: number) {\n currentOpt.width = value;\n },\n get height() {\n return readHeight();\n },\n set height(value: number) {\n currentOpt.height = value;\n },\n renderArea() {\n return new k.Rect(k.vec2(), comp.width, comp.height);\n },\n setStyle(next: Partial<ButtonCompOpt>): void {\n currentOpt = { ...currentOpt, ...next };\n },\n add(): void {\n // No-op: rendering is handled in draw().\n },\n draw(): void {\n const width = comp.width;\n const height = comp.height;\n const radius = currentOpt.radius ?? theme.shape.borderRadius;\n const fill = currentOpt.fill ?? theme.palette.primary;\n const textColor = currentOpt.textColor ?? theme.palette.text;\n const size = currentOpt.size ?? theme.typography.fontSize;\n const font = currentOpt.font ?? theme.typography.fontFamily;\n\n const drawRect = k.drawRect as unknown as\n ((opt: Record<string, unknown>) => void) | undefined;\n drawRect?.({\n pos: k.vec2(),\n width,\n height,\n color: fill,\n radius,\n });\n\n const drawText = k.drawText as unknown as\n ((opt: Record<string, unknown>) => void) | undefined;\n drawText?.({\n text: currentText,\n pos: k.vec2(width / 2, height / 2),\n size,\n font,\n color: textColor,\n anchor: 'center',\n });\n },\n destroy(): void {\n // No resources to release.\n },\n };\n\n return comp;\n}\n\nexport function createButtonObject(\n k: KAPLAYCtx,\n theme: Theme,\n text: string,\n opt: ButtonCompOpt & {\n pos?: Vec2;\n onClick?: () => void;\n color?: Color;\n hoverColor?: Color;\n pressedColor?: Color;\n textColor?: Color;\n },\n): GameObj {\n const width = opt.width ?? theme.components.button.width;\n const height = opt.height ?? theme.components.button.height;\n const radius = opt.radius ?? theme.shape.borderRadius;\n const baseColor = opt.color ?? opt.fill ?? theme.palette.primary;\n const hoverColor = opt.hoverColor ?? theme.palette.hover;\n const pressedColor = opt.pressedColor ?? theme.palette.pressed;\n const textColor = opt.textColor ?? theme.palette.text;\n const size = opt.size ?? theme.typography.fontSize;\n const font = opt.font ?? theme.typography.fontFamily;\n const pos = opt.pos ?? k.vec2();\n\n interface ButtonGameObj {\n add: (comps: Comp[]) => { text: string; textSize: number };\n onClick: (cb: () => void) => void;\n onHover: (cb: () => void) => void;\n onHoverEnd: (cb: () => void) => void;\n width: number;\n height: number;\n color: unknown;\n scale: { x: number; y: number };\n pos: { x: number; y: number };\n setSize: (w: number, h: number) => void;\n setButtonColor: (color: Color) => void;\n setButtonText: (value: string) => void;\n setButtonTextSize: (value: number) => void;\n setPosition: (x: number, y: number) => void;\n }\n\n const obj = k.add([\n k.rect(width, height, radius > 0 ? { radius } : undefined),\n k.pos(pos as never),\n k.area(),\n k.color(baseColor as never),\n k.anchor('center'),\n ]) as unknown as ButtonGameObj;\n\n const label = obj.add([\n k.text(text, { size, font }),\n k.color(textColor as never),\n k.anchor('center'),\n ]) as GameObj & { text: string; textSize: number };\n\n let currentText = text;\n let currentColor: Color = baseColor;\n\n obj.onClick(() => {\n obj.color = pressedColor;\n opt.onClick?.();\n });\n obj.onHover(() => {\n obj.color = hoverColor;\n obj.scale = k.vec2(1.05, 1.05);\n k.setCursor('pointer');\n });\n obj.onHoverEnd(() => {\n obj.color = currentColor;\n obj.scale = k.vec2(1, 1);\n k.setCursor('default');\n });\n\n obj.setSize = (w: number, h: number): void => {\n obj.width = w;\n obj.height = h;\n };\n\n obj.setButtonColor = (color: Color): void => {\n currentColor = color;\n obj.color = color;\n };\n\n obj.setButtonText = (value: string): void => {\n currentText = value;\n label.text = value;\n };\n\n obj.setButtonTextSize = (value: number): void => {\n label.textSize = value;\n };\n\n obj.setPosition = (x: number, y: number): void => {\n obj.pos = k.vec2(x, y);\n };\n\n label.text = currentText;\n\n return obj as unknown as GameObj;\n}\n","import type { DeepPartial, Theme } from './types';\n\nconst DEFAULT_PRIMARY = '#4f8cff';\nconst DEFAULT_TEXT = '#ffffff';\n\n/** Default theme used when no theme is provided. */\nexport const defaultTheme: Theme = {\n palette: {\n primary: DEFAULT_PRIMARY,\n secondary: DEFAULT_PRIMARY,\n background: '#111827',\n surface: '#1f2937',\n text: DEFAULT_TEXT,\n textSecondary: DEFAULT_TEXT,\n hover: DEFAULT_PRIMARY,\n pressed: DEFAULT_PRIMARY,\n disabled: '#6b7280',\n error: '#ef4444',\n },\n typography: {\n fontFamily: 'sans-serif',\n fontSize: 18,\n fontWeight: 400,\n lineHeight: 1.2,\n },\n shape: {\n borderRadius: 0,\n },\n spacing: 8,\n components: {\n button: {\n width: 120,\n height: 40,\n minWidth: 120,\n paddingX: 12,\n paddingY: 8,\n textTransform: 'none',\n },\n modal: {\n backdropOpacity: 0.5,\n borderRadius: 8,\n },\n tooltip: {\n background: '#111827',\n textColor: DEFAULT_TEXT,\n delay: 300,\n },\n },\n};\n\n/** Resolve a partial theme into a fully resolved theme. */\nexport function resolveTheme(input?: DeepPartial<Theme>): Theme {\n return {\n palette: { ...defaultTheme.palette, ...input?.palette },\n typography: { ...defaultTheme.typography, ...input?.typography },\n shape: { ...defaultTheme.shape, ...input?.shape },\n spacing: input?.spacing ?? defaultTheme.spacing,\n components: {\n button: {\n ...defaultTheme.components.button,\n ...input?.components?.button,\n width:\n input?.components?.button?.width ??\n input?.components?.button?.minWidth ??\n defaultTheme.components.button.width,\n },\n modal: {\n ...defaultTheme.components.modal,\n ...input?.components?.modal,\n },\n tooltip: {\n ...defaultTheme.components.tooltip,\n ...input?.components?.tooltip,\n },\n },\n };\n}\n","import type { GameObj, KAPLAYCtx, Vec2 } from 'kaplay';\n\nimport { createButtonComponent, createButtonObject } from './button';\nimport { resolveTheme } from './theme';\nimport type {\n ButtonComp,\n ButtonCompOpt,\n Color,\n DeepPartial,\n Theme,\n} from './types';\n\nexport { createButtonComponent, createButtonObject } from './button';\nexport type { ButtonComp, ButtonCompOpt, Color, Theme } from './types';\n\ndeclare module 'kaplay' {\n interface KAPLAYCtx {\n button: (text?: string, opt?: ButtonCompOpt) => ButtonComp;\n addButton: (\n text?: string,\n opt?: ButtonCompOpt & {\n pos?: Vec2;\n onClick?: () => void;\n color?: Color;\n hoverColor?: Color;\n pressedColor?: Color;\n textColor?: Color;\n },\n ) => GameObj;\n setTheme: (theme: DeepPartial<Theme>) => void;\n getTheme: () => Theme;\n }\n}\n\n/** KAPLAY UI plugin. */\nexport function uiPlugin(k: KAPLAYCtx, theme: DeepPartial<Theme> = {}) {\n let userTheme: DeepPartial<Theme> = theme;\n let currentTheme: Theme = resolveTheme(userTheme);\n\n const getTheme = (): Theme => currentTheme;\n\n const setTheme = (next: DeepPartial<Theme>): void => {\n userTheme = { ...userTheme, ...next };\n currentTheme = resolveTheme(userTheme);\n };\n\n const button = (text = '', opt: ButtonCompOpt = {}): ButtonComp => {\n return createButtonComponent(k, currentTheme, text, opt);\n };\n\n const addButton = (\n text = '',\n opt: ButtonCompOpt & {\n pos?: Vec2;\n onClick?: () => void;\n color?: Color;\n hoverColor?: Color;\n pressedColor?: Color;\n textColor?: Color;\n } = {},\n ): GameObj => {\n return createButtonObject(k, currentTheme, text, opt);\n };\n\n return {\n button,\n addButton,\n setTheme,\n getTheme,\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,109 @@
1
+ import { Color as Color$1, Comp, GameObj, KAPLAYCtx, Rect, Vec2 } from "kaplay";
2
+ /** Color value accepted by the UI theme and components. */
3
+ type Color = Color$1 | string;
4
+ /** Recursively partial version of a type. */
5
+ type DeepPartial<T> = { [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K]; };
6
+ interface Theme {
7
+ palette: {
8
+ primary: Color;
9
+ secondary: Color;
10
+ background: Color;
11
+ surface: Color;
12
+ text: Color;
13
+ textSecondary: Color;
14
+ hover: Color;
15
+ pressed: Color;
16
+ disabled: Color;
17
+ error: Color;
18
+ };
19
+ typography: {
20
+ fontFamily: string;
21
+ fontSize: number;
22
+ fontWeight: number | string;
23
+ lineHeight: number;
24
+ };
25
+ shape: {
26
+ borderRadius: number;
27
+ };
28
+ spacing: number;
29
+ components: {
30
+ button: {
31
+ width: number;
32
+ height: number;
33
+ minWidth: number;
34
+ paddingX: number;
35
+ paddingY: number;
36
+ textTransform: 'none' | 'uppercase' | 'lowercase' | 'capitalize';
37
+ };
38
+ modal: {
39
+ backdropOpacity: number;
40
+ borderRadius: number;
41
+ };
42
+ tooltip: {
43
+ background: Color;
44
+ textColor: Color;
45
+ delay: number;
46
+ };
47
+ };
48
+ }
49
+ /** Options for a single button. */
50
+ interface ButtonCompOpt {
51
+ width?: number;
52
+ height?: number;
53
+ size?: number;
54
+ font?: string;
55
+ fill?: Color;
56
+ textColor?: Color;
57
+ radius?: number;
58
+ hoverFill?: Color;
59
+ pressedFill?: Color;
60
+ }
61
+ /** The `button` component. */
62
+ interface ButtonComp extends Comp {
63
+ id: 'button';
64
+ text: string;
65
+ width: number;
66
+ height: number;
67
+ renderArea(): Rect;
68
+ setStyle(opt: Partial<ButtonCompOpt>): void;
69
+ }
70
+ export declare function createButtonComponent(k: KAPLAYCtx, theme: Theme, text: string, opt: ButtonCompOpt): ButtonComp;
71
+ export declare function createButtonObject(k: KAPLAYCtx, theme: Theme, text: string, opt: ButtonCompOpt & {
72
+ pos?: Vec2;
73
+ onClick?: () => void;
74
+ color?: Color;
75
+ hoverColor?: Color;
76
+ pressedColor?: Color;
77
+ textColor?: Color;
78
+ }): GameObj;
79
+ declare module 'kaplay' {
80
+ interface KAPLAYCtx {
81
+ button: (text?: string, opt?: ButtonCompOpt) => ButtonComp;
82
+ addButton: (text?: string, opt?: ButtonCompOpt & {
83
+ pos?: Vec2;
84
+ onClick?: () => void;
85
+ color?: Color;
86
+ hoverColor?: Color;
87
+ pressedColor?: Color;
88
+ textColor?: Color;
89
+ }) => GameObj;
90
+ setTheme: (theme: DeepPartial<Theme>) => void;
91
+ getTheme: () => Theme;
92
+ }
93
+ }
94
+ /** KAPLAY UI plugin. */
95
+ export declare function uiPlugin(k: KAPLAYCtx, theme?: DeepPartial<Theme>): {
96
+ button: (text?: string, opt?: ButtonCompOpt) => ButtonComp;
97
+ addButton: (text?: string, opt?: ButtonCompOpt & {
98
+ pos?: Vec2;
99
+ onClick?: () => void;
100
+ color?: Color;
101
+ hoverColor?: Color;
102
+ pressedColor?: Color;
103
+ textColor?: Color;
104
+ }) => GameObj;
105
+ setTheme: (next: DeepPartial<Theme>) => void;
106
+ getTheme: () => Theme;
107
+ };
108
+ export type { ButtonComp, ButtonCompOpt, Color, Theme };
109
+ //# sourceMappingURL=plugin.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.cts","names":["KaplayColor"],"sources":["../src/types.ts","../src/button.ts","../src/plugin.ts"],"mappings":";;KAGY,QAAQA;;KAGR,YAAY,QACrB,WAAW,KAAK,EAAE,oBAAoB,YAAY,EAAE,MAAM,EAAE;UAG9C;EACf;IACE,SAAS;IACT,WAAW;IACX,YAAY;IACZ,SAAS;IACT,MAAM;IACN,eAAe;IACf,OAAO;IACP,SAAS;IACT,UAAU;IACV,OAAO;;EAET;IACE;IACA;IACA;IACA;;EAEF;IACE;;EAEF;EACA;IACE;MACE;MACA;MACA;MACA;MACA;MACA;;IAEF;MACE;MACA;;IAEF;MACE,YAAY;MACZ,WAAW;MACX;;;;;UAMW;EACf;EACA;EACA;EACA;EACA,OAAO;EACP,YAAY;EACZ;EACA,YAAY;EACZ,cAAc;;;UAIC,mBAAmB;EAClC;EACA;EACA;EACA;EACA,cAAc;EACd,SAAS,KAAK,QAAQ;;wBCtER,sBACd,GAAG,WACH,OAAO,OACP,cACA,KAAK,gBACJ;wBA+Ea,mBACd,GAAG,WACH,OAAO,OACP,cACA,KAAK;EACH,MAAM;EACN;EACA,QAAQ;EACR,aAAa;EACb,eAAe;EACf,YAAY;IAEb;;YCpFS;IACR,SAAS,eAAe,MAAM,kBAAkB;IAChD,YACE,eACA,MAAM;MACJ,MAAM;MACN;MACA,QAAQ;MACR,aAAa;MACb,eAAe;MACf,YAAY;UAEX;IACL,WAAW,OAAO,YAAY;IAC9B,gBAAgB;;;;wBAKJ,SAAS,GAAG,WAAW,QAAO,YAAY;EAWxB,SAAA,eAAA,MAAA,kBAAqB;EAM9C,YAAA,eAAA,MAAA;IACH,MAAM;IACN;IACA,QAAQ;IACR,aAAa;IACb,eAAe;IACf,YAAY;QAEb;EAnBqB,WAAA,MAAA,YAAY;EAFf,gBAAA"}
@@ -0,0 +1,109 @@
1
+ import { Color as Color$1, Comp, GameObj, KAPLAYCtx, Rect, Vec2 } from "kaplay";
2
+ /** Color value accepted by the UI theme and components. */
3
+ type Color = Color$1 | string;
4
+ /** Recursively partial version of a type. */
5
+ type DeepPartial<T> = { [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K]; };
6
+ interface Theme {
7
+ palette: {
8
+ primary: Color;
9
+ secondary: Color;
10
+ background: Color;
11
+ surface: Color;
12
+ text: Color;
13
+ textSecondary: Color;
14
+ hover: Color;
15
+ pressed: Color;
16
+ disabled: Color;
17
+ error: Color;
18
+ };
19
+ typography: {
20
+ fontFamily: string;
21
+ fontSize: number;
22
+ fontWeight: number | string;
23
+ lineHeight: number;
24
+ };
25
+ shape: {
26
+ borderRadius: number;
27
+ };
28
+ spacing: number;
29
+ components: {
30
+ button: {
31
+ width: number;
32
+ height: number;
33
+ minWidth: number;
34
+ paddingX: number;
35
+ paddingY: number;
36
+ textTransform: 'none' | 'uppercase' | 'lowercase' | 'capitalize';
37
+ };
38
+ modal: {
39
+ backdropOpacity: number;
40
+ borderRadius: number;
41
+ };
42
+ tooltip: {
43
+ background: Color;
44
+ textColor: Color;
45
+ delay: number;
46
+ };
47
+ };
48
+ }
49
+ /** Options for a single button. */
50
+ interface ButtonCompOpt {
51
+ width?: number;
52
+ height?: number;
53
+ size?: number;
54
+ font?: string;
55
+ fill?: Color;
56
+ textColor?: Color;
57
+ radius?: number;
58
+ hoverFill?: Color;
59
+ pressedFill?: Color;
60
+ }
61
+ /** The `button` component. */
62
+ interface ButtonComp extends Comp {
63
+ id: 'button';
64
+ text: string;
65
+ width: number;
66
+ height: number;
67
+ renderArea(): Rect;
68
+ setStyle(opt: Partial<ButtonCompOpt>): void;
69
+ }
70
+ export declare function createButtonComponent(k: KAPLAYCtx, theme: Theme, text: string, opt: ButtonCompOpt): ButtonComp;
71
+ export declare function createButtonObject(k: KAPLAYCtx, theme: Theme, text: string, opt: ButtonCompOpt & {
72
+ pos?: Vec2;
73
+ onClick?: () => void;
74
+ color?: Color;
75
+ hoverColor?: Color;
76
+ pressedColor?: Color;
77
+ textColor?: Color;
78
+ }): GameObj;
79
+ declare module 'kaplay' {
80
+ interface KAPLAYCtx {
81
+ button: (text?: string, opt?: ButtonCompOpt) => ButtonComp;
82
+ addButton: (text?: string, opt?: ButtonCompOpt & {
83
+ pos?: Vec2;
84
+ onClick?: () => void;
85
+ color?: Color;
86
+ hoverColor?: Color;
87
+ pressedColor?: Color;
88
+ textColor?: Color;
89
+ }) => GameObj;
90
+ setTheme: (theme: DeepPartial<Theme>) => void;
91
+ getTheme: () => Theme;
92
+ }
93
+ }
94
+ /** KAPLAY UI plugin. */
95
+ export declare function uiPlugin(k: KAPLAYCtx, theme?: DeepPartial<Theme>): {
96
+ button: (text?: string, opt?: ButtonCompOpt) => ButtonComp;
97
+ addButton: (text?: string, opt?: ButtonCompOpt & {
98
+ pos?: Vec2;
99
+ onClick?: () => void;
100
+ color?: Color;
101
+ hoverColor?: Color;
102
+ pressedColor?: Color;
103
+ textColor?: Color;
104
+ }) => GameObj;
105
+ setTheme: (next: DeepPartial<Theme>) => void;
106
+ getTheme: () => Theme;
107
+ };
108
+ export type { ButtonComp, ButtonCompOpt, Color, Theme };
109
+ //# sourceMappingURL=plugin.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.mts","names":["KaplayColor"],"sources":["../src/types.ts","../src/button.ts","../src/plugin.ts"],"mappings":";;KAGY,QAAQA;;KAGR,YAAY,QACrB,WAAW,KAAK,EAAE,oBAAoB,YAAY,EAAE,MAAM,EAAE;UAG9C;EACf;IACE,SAAS;IACT,WAAW;IACX,YAAY;IACZ,SAAS;IACT,MAAM;IACN,eAAe;IACf,OAAO;IACP,SAAS;IACT,UAAU;IACV,OAAO;;EAET;IACE;IACA;IACA;IACA;;EAEF;IACE;;EAEF;EACA;IACE;MACE;MACA;MACA;MACA;MACA;MACA;;IAEF;MACE;MACA;;IAEF;MACE,YAAY;MACZ,WAAW;MACX;;;;;UAMW;EACf;EACA;EACA;EACA;EACA,OAAO;EACP,YAAY;EACZ;EACA,YAAY;EACZ,cAAc;;;UAIC,mBAAmB;EAClC;EACA;EACA;EACA;EACA,cAAc;EACd,SAAS,KAAK,QAAQ;;wBCtER,sBACd,GAAG,WACH,OAAO,OACP,cACA,KAAK,gBACJ;wBA+Ea,mBACd,GAAG,WACH,OAAO,OACP,cACA,KAAK;EACH,MAAM;EACN;EACA,QAAQ;EACR,aAAa;EACb,eAAe;EACf,YAAY;IAEb;;YCpFS;IACR,SAAS,eAAe,MAAM,kBAAkB;IAChD,YACE,eACA,MAAM;MACJ,MAAM;MACN;MACA,QAAQ;MACR,aAAa;MACb,eAAe;MACf,YAAY;UAEX;IACL,WAAW,OAAO,YAAY;IAC9B,gBAAgB;;;;wBAKJ,SAAS,GAAG,WAAW,QAAO,YAAY;EAWxB,SAAA,eAAA,MAAA,kBAAqB;EAM9C,YAAA,eAAA,MAAA;IACH,MAAM;IACN;IACA,QAAQ;IACR,aAAa;IACb,eAAe;IACf,YAAY;QAEb;EAnBqB,WAAA,MAAA,YAAY;EAFf,gBAAA"}
@@ -0,0 +1,240 @@
1
+ // src/button.ts
2
+ function createButtonComponent(k, theme, text, opt) {
3
+ let currentText = text;
4
+ let currentOpt = { ...opt };
5
+ const readWidth = () => {
6
+ return currentOpt.width ?? theme.components.button.width;
7
+ };
8
+ const readHeight = () => {
9
+ return currentOpt.height ?? theme.components.button.height;
10
+ };
11
+ const comp = {
12
+ id: "button",
13
+ get text() {
14
+ return currentText;
15
+ },
16
+ set text(value) {
17
+ currentText = value;
18
+ },
19
+ get width() {
20
+ return readWidth();
21
+ },
22
+ set width(value) {
23
+ currentOpt.width = value;
24
+ },
25
+ get height() {
26
+ return readHeight();
27
+ },
28
+ set height(value) {
29
+ currentOpt.height = value;
30
+ },
31
+ renderArea() {
32
+ return new k.Rect(k.vec2(), comp.width, comp.height);
33
+ },
34
+ setStyle(next) {
35
+ currentOpt = {
36
+ ...currentOpt,
37
+ ...next
38
+ };
39
+ },
40
+ add() {},
41
+ draw() {
42
+ const width = comp.width;
43
+ const height = comp.height;
44
+ const radius = currentOpt.radius ?? theme.shape.borderRadius;
45
+ const fill = currentOpt.fill ?? theme.palette.primary;
46
+ const textColor = currentOpt.textColor ?? theme.palette.text;
47
+ const size = currentOpt.size ?? theme.typography.fontSize;
48
+ const font = currentOpt.font ?? theme.typography.fontFamily;
49
+ const drawRect = k.drawRect;
50
+ drawRect?.({
51
+ pos: k.vec2(),
52
+ width,
53
+ height,
54
+ color: fill,
55
+ radius
56
+ });
57
+ const drawText = k.drawText;
58
+ drawText?.({
59
+ text: currentText,
60
+ pos: k.vec2(width / 2, height / 2),
61
+ size,
62
+ font,
63
+ color: textColor,
64
+ anchor: "center"
65
+ });
66
+ },
67
+ destroy() {}
68
+ };
69
+ return comp;
70
+ }
71
+ function createButtonObject(k, theme, text, opt) {
72
+ const width = opt.width ?? theme.components.button.width;
73
+ const height = opt.height ?? theme.components.button.height;
74
+ const radius = opt.radius ?? theme.shape.borderRadius;
75
+ const baseColor = opt.color ?? opt.fill ?? theme.palette.primary;
76
+ const hoverColor = opt.hoverColor ?? theme.palette.hover;
77
+ const pressedColor = opt.pressedColor ?? theme.palette.pressed;
78
+ const textColor = opt.textColor ?? theme.palette.text;
79
+ const size = opt.size ?? theme.typography.fontSize;
80
+ const font = opt.font ?? theme.typography.fontFamily;
81
+ const pos = opt.pos ?? k.vec2();
82
+ const obj = k.add([
83
+ k.rect(width, height, radius > 0 ? { radius } : void 0),
84
+ k.pos(pos),
85
+ k.area(),
86
+ k.color(baseColor),
87
+ k.anchor("center")
88
+ ]);
89
+ const label = obj.add([
90
+ k.text(text, {
91
+ size,
92
+ font
93
+ }),
94
+ k.color(textColor),
95
+ k.anchor("center")
96
+ ]);
97
+ let currentText = text;
98
+ let currentColor = baseColor;
99
+ obj.onClick(() => {
100
+ obj.color = pressedColor;
101
+ opt.onClick?.();
102
+ });
103
+ obj.onHover(() => {
104
+ obj.color = hoverColor;
105
+ obj.scale = k.vec2(1.05, 1.05);
106
+ k.setCursor("pointer");
107
+ });
108
+ obj.onHoverEnd(() => {
109
+ obj.color = currentColor;
110
+ obj.scale = k.vec2(1, 1);
111
+ k.setCursor("default");
112
+ });
113
+ obj.setSize = (w, h) => {
114
+ obj.width = w;
115
+ obj.height = h;
116
+ };
117
+ obj.setButtonColor = (color) => {
118
+ currentColor = color;
119
+ obj.color = color;
120
+ };
121
+ obj.setButtonText = (value) => {
122
+ currentText = value;
123
+ label.text = value;
124
+ };
125
+ obj.setButtonTextSize = (value) => {
126
+ label.textSize = value;
127
+ };
128
+ obj.setPosition = (x, y) => {
129
+ obj.pos = k.vec2(x, y);
130
+ };
131
+ label.text = currentText;
132
+ return obj;
133
+ }
134
+ // src/theme.ts
135
+ const DEFAULT_PRIMARY = "#4f8cff";
136
+ const DEFAULT_TEXT = "#ffffff";
137
+ /** Default theme used when no theme is provided. */
138
+ const defaultTheme = {
139
+ palette: {
140
+ primary: DEFAULT_PRIMARY,
141
+ secondary: DEFAULT_PRIMARY,
142
+ background: "#111827",
143
+ surface: "#1f2937",
144
+ text: DEFAULT_TEXT,
145
+ textSecondary: DEFAULT_TEXT,
146
+ hover: DEFAULT_PRIMARY,
147
+ pressed: DEFAULT_PRIMARY,
148
+ disabled: "#6b7280",
149
+ error: "#ef4444"
150
+ },
151
+ typography: {
152
+ fontFamily: "sans-serif",
153
+ fontSize: 18,
154
+ fontWeight: 400,
155
+ lineHeight: 1.2
156
+ },
157
+ shape: { borderRadius: 0 },
158
+ spacing: 8,
159
+ components: {
160
+ button: {
161
+ width: 120,
162
+ height: 40,
163
+ minWidth: 120,
164
+ paddingX: 12,
165
+ paddingY: 8,
166
+ textTransform: "none"
167
+ },
168
+ modal: {
169
+ backdropOpacity: .5,
170
+ borderRadius: 8
171
+ },
172
+ tooltip: {
173
+ background: "#111827",
174
+ textColor: DEFAULT_TEXT,
175
+ delay: 300
176
+ }
177
+ }
178
+ };
179
+ /** Resolve a partial theme into a fully resolved theme. */
180
+ function resolveTheme(input) {
181
+ return {
182
+ palette: {
183
+ ...defaultTheme.palette,
184
+ ...input?.palette
185
+ },
186
+ typography: {
187
+ ...defaultTheme.typography,
188
+ ...input?.typography
189
+ },
190
+ shape: {
191
+ ...defaultTheme.shape,
192
+ ...input?.shape
193
+ },
194
+ spacing: input?.spacing ?? defaultTheme.spacing,
195
+ components: {
196
+ button: {
197
+ ...defaultTheme.components.button,
198
+ ...input?.components?.button,
199
+ width: input?.components?.button?.width ?? input?.components?.button?.minWidth ?? defaultTheme.components.button.width
200
+ },
201
+ modal: {
202
+ ...defaultTheme.components.modal,
203
+ ...input?.components?.modal
204
+ },
205
+ tooltip: {
206
+ ...defaultTheme.components.tooltip,
207
+ ...input?.components?.tooltip
208
+ }
209
+ }
210
+ };
211
+ }
212
+ // src/plugin.ts
213
+ /** KAPLAY UI plugin. */
214
+ function uiPlugin(k, theme = {}) {
215
+ let userTheme = theme;
216
+ let currentTheme = resolveTheme(userTheme);
217
+ const getTheme = () => currentTheme;
218
+ const setTheme = (next) => {
219
+ userTheme = {
220
+ ...userTheme,
221
+ ...next
222
+ };
223
+ currentTheme = resolveTheme(userTheme);
224
+ };
225
+ const button = (text = "", opt = {}) => {
226
+ return createButtonComponent(k, currentTheme, text, opt);
227
+ };
228
+ const addButton = (text = "", opt = {}) => {
229
+ return createButtonObject(k, currentTheme, text, opt);
230
+ };
231
+ return {
232
+ button,
233
+ addButton,
234
+ setTheme,
235
+ getTheme
236
+ };
237
+ }
238
+ export { createButtonComponent, createButtonObject, uiPlugin };
239
+
240
+ //# sourceMappingURL=plugin.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.mjs","names":[],"sources":["../src/button.ts","../src/theme.ts","../src/plugin.ts"],"sourcesContent":["import type { Comp, GameObj, KAPLAYCtx, Vec2 } from 'kaplay';\n\nimport type { ButtonComp, ButtonCompOpt, Color, Theme } from './types';\n\nexport function createButtonComponent(\n k: KAPLAYCtx,\n theme: Theme,\n text: string,\n opt: ButtonCompOpt,\n): ButtonComp {\n let currentText = text;\n let currentOpt: ButtonCompOpt = { ...opt };\n\n const readWidth = (): number => {\n return currentOpt.width ?? theme.components.button.width;\n };\n\n const readHeight = (): number => {\n return currentOpt.height ?? theme.components.button.height;\n };\n\n const comp: ButtonComp = {\n id: 'button',\n get text() {\n return currentText;\n },\n set text(value: string) {\n currentText = value;\n },\n get width() {\n return readWidth();\n },\n set width(value: number) {\n currentOpt.width = value;\n },\n get height() {\n return readHeight();\n },\n set height(value: number) {\n currentOpt.height = value;\n },\n renderArea() {\n return new k.Rect(k.vec2(), comp.width, comp.height);\n },\n setStyle(next: Partial<ButtonCompOpt>): void {\n currentOpt = { ...currentOpt, ...next };\n },\n add(): void {\n // No-op: rendering is handled in draw().\n },\n draw(): void {\n const width = comp.width;\n const height = comp.height;\n const radius = currentOpt.radius ?? theme.shape.borderRadius;\n const fill = currentOpt.fill ?? theme.palette.primary;\n const textColor = currentOpt.textColor ?? theme.palette.text;\n const size = currentOpt.size ?? theme.typography.fontSize;\n const font = currentOpt.font ?? theme.typography.fontFamily;\n\n const drawRect = k.drawRect as unknown as\n ((opt: Record<string, unknown>) => void) | undefined;\n drawRect?.({\n pos: k.vec2(),\n width,\n height,\n color: fill,\n radius,\n });\n\n const drawText = k.drawText as unknown as\n ((opt: Record<string, unknown>) => void) | undefined;\n drawText?.({\n text: currentText,\n pos: k.vec2(width / 2, height / 2),\n size,\n font,\n color: textColor,\n anchor: 'center',\n });\n },\n destroy(): void {\n // No resources to release.\n },\n };\n\n return comp;\n}\n\nexport function createButtonObject(\n k: KAPLAYCtx,\n theme: Theme,\n text: string,\n opt: ButtonCompOpt & {\n pos?: Vec2;\n onClick?: () => void;\n color?: Color;\n hoverColor?: Color;\n pressedColor?: Color;\n textColor?: Color;\n },\n): GameObj {\n const width = opt.width ?? theme.components.button.width;\n const height = opt.height ?? theme.components.button.height;\n const radius = opt.radius ?? theme.shape.borderRadius;\n const baseColor = opt.color ?? opt.fill ?? theme.palette.primary;\n const hoverColor = opt.hoverColor ?? theme.palette.hover;\n const pressedColor = opt.pressedColor ?? theme.palette.pressed;\n const textColor = opt.textColor ?? theme.palette.text;\n const size = opt.size ?? theme.typography.fontSize;\n const font = opt.font ?? theme.typography.fontFamily;\n const pos = opt.pos ?? k.vec2();\n\n interface ButtonGameObj {\n add: (comps: Comp[]) => { text: string; textSize: number };\n onClick: (cb: () => void) => void;\n onHover: (cb: () => void) => void;\n onHoverEnd: (cb: () => void) => void;\n width: number;\n height: number;\n color: unknown;\n scale: { x: number; y: number };\n pos: { x: number; y: number };\n setSize: (w: number, h: number) => void;\n setButtonColor: (color: Color) => void;\n setButtonText: (value: string) => void;\n setButtonTextSize: (value: number) => void;\n setPosition: (x: number, y: number) => void;\n }\n\n const obj = k.add([\n k.rect(width, height, radius > 0 ? { radius } : undefined),\n k.pos(pos as never),\n k.area(),\n k.color(baseColor as never),\n k.anchor('center'),\n ]) as unknown as ButtonGameObj;\n\n const label = obj.add([\n k.text(text, { size, font }),\n k.color(textColor as never),\n k.anchor('center'),\n ]) as GameObj & { text: string; textSize: number };\n\n let currentText = text;\n let currentColor: Color = baseColor;\n\n obj.onClick(() => {\n obj.color = pressedColor;\n opt.onClick?.();\n });\n obj.onHover(() => {\n obj.color = hoverColor;\n obj.scale = k.vec2(1.05, 1.05);\n k.setCursor('pointer');\n });\n obj.onHoverEnd(() => {\n obj.color = currentColor;\n obj.scale = k.vec2(1, 1);\n k.setCursor('default');\n });\n\n obj.setSize = (w: number, h: number): void => {\n obj.width = w;\n obj.height = h;\n };\n\n obj.setButtonColor = (color: Color): void => {\n currentColor = color;\n obj.color = color;\n };\n\n obj.setButtonText = (value: string): void => {\n currentText = value;\n label.text = value;\n };\n\n obj.setButtonTextSize = (value: number): void => {\n label.textSize = value;\n };\n\n obj.setPosition = (x: number, y: number): void => {\n obj.pos = k.vec2(x, y);\n };\n\n label.text = currentText;\n\n return obj as unknown as GameObj;\n}\n","import type { DeepPartial, Theme } from './types';\n\nconst DEFAULT_PRIMARY = '#4f8cff';\nconst DEFAULT_TEXT = '#ffffff';\n\n/** Default theme used when no theme is provided. */\nexport const defaultTheme: Theme = {\n palette: {\n primary: DEFAULT_PRIMARY,\n secondary: DEFAULT_PRIMARY,\n background: '#111827',\n surface: '#1f2937',\n text: DEFAULT_TEXT,\n textSecondary: DEFAULT_TEXT,\n hover: DEFAULT_PRIMARY,\n pressed: DEFAULT_PRIMARY,\n disabled: '#6b7280',\n error: '#ef4444',\n },\n typography: {\n fontFamily: 'sans-serif',\n fontSize: 18,\n fontWeight: 400,\n lineHeight: 1.2,\n },\n shape: {\n borderRadius: 0,\n },\n spacing: 8,\n components: {\n button: {\n width: 120,\n height: 40,\n minWidth: 120,\n paddingX: 12,\n paddingY: 8,\n textTransform: 'none',\n },\n modal: {\n backdropOpacity: 0.5,\n borderRadius: 8,\n },\n tooltip: {\n background: '#111827',\n textColor: DEFAULT_TEXT,\n delay: 300,\n },\n },\n};\n\n/** Resolve a partial theme into a fully resolved theme. */\nexport function resolveTheme(input?: DeepPartial<Theme>): Theme {\n return {\n palette: { ...defaultTheme.palette, ...input?.palette },\n typography: { ...defaultTheme.typography, ...input?.typography },\n shape: { ...defaultTheme.shape, ...input?.shape },\n spacing: input?.spacing ?? defaultTheme.spacing,\n components: {\n button: {\n ...defaultTheme.components.button,\n ...input?.components?.button,\n width:\n input?.components?.button?.width ??\n input?.components?.button?.minWidth ??\n defaultTheme.components.button.width,\n },\n modal: {\n ...defaultTheme.components.modal,\n ...input?.components?.modal,\n },\n tooltip: {\n ...defaultTheme.components.tooltip,\n ...input?.components?.tooltip,\n },\n },\n };\n}\n","import type { GameObj, KAPLAYCtx, Vec2 } from 'kaplay';\n\nimport { createButtonComponent, createButtonObject } from './button';\nimport { resolveTheme } from './theme';\nimport type {\n ButtonComp,\n ButtonCompOpt,\n Color,\n DeepPartial,\n Theme,\n} from './types';\n\nexport { createButtonComponent, createButtonObject } from './button';\nexport type { ButtonComp, ButtonCompOpt, Color, Theme } from './types';\n\ndeclare module 'kaplay' {\n interface KAPLAYCtx {\n button: (text?: string, opt?: ButtonCompOpt) => ButtonComp;\n addButton: (\n text?: string,\n opt?: ButtonCompOpt & {\n pos?: Vec2;\n onClick?: () => void;\n color?: Color;\n hoverColor?: Color;\n pressedColor?: Color;\n textColor?: Color;\n },\n ) => GameObj;\n setTheme: (theme: DeepPartial<Theme>) => void;\n getTheme: () => Theme;\n }\n}\n\n/** KAPLAY UI plugin. */\nexport function uiPlugin(k: KAPLAYCtx, theme: DeepPartial<Theme> = {}) {\n let userTheme: DeepPartial<Theme> = theme;\n let currentTheme: Theme = resolveTheme(userTheme);\n\n const getTheme = (): Theme => currentTheme;\n\n const setTheme = (next: DeepPartial<Theme>): void => {\n userTheme = { ...userTheme, ...next };\n currentTheme = resolveTheme(userTheme);\n };\n\n const button = (text = '', opt: ButtonCompOpt = {}): ButtonComp => {\n return createButtonComponent(k, currentTheme, text, opt);\n };\n\n const addButton = (\n text = '',\n opt: ButtonCompOpt & {\n pos?: Vec2;\n onClick?: () => void;\n color?: Color;\n hoverColor?: Color;\n pressedColor?: Color;\n textColor?: Color;\n } = {},\n ): GameObj => {\n return createButtonObject(k, currentTheme, text, opt);\n };\n\n return {\n button,\n addButton,\n setTheme,\n getTheme,\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,247 @@
1
+ (function(global, factory) {
2
+ typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define(["exports"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.KaplayPluginUI = {}));
3
+ })(this, function(exports) {
4
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
5
+ // src/button.ts
6
+ function createButtonComponent(k, theme, text, opt) {
7
+ let currentText = text;
8
+ let currentOpt = { ...opt };
9
+ const readWidth = () => {
10
+ return currentOpt.width ?? theme.components.button.width;
11
+ };
12
+ const readHeight = () => {
13
+ return currentOpt.height ?? theme.components.button.height;
14
+ };
15
+ const comp = {
16
+ id: "button",
17
+ get text() {
18
+ return currentText;
19
+ },
20
+ set text(value) {
21
+ currentText = value;
22
+ },
23
+ get width() {
24
+ return readWidth();
25
+ },
26
+ set width(value) {
27
+ currentOpt.width = value;
28
+ },
29
+ get height() {
30
+ return readHeight();
31
+ },
32
+ set height(value) {
33
+ currentOpt.height = value;
34
+ },
35
+ renderArea() {
36
+ return new k.Rect(k.vec2(), comp.width, comp.height);
37
+ },
38
+ setStyle(next) {
39
+ currentOpt = {
40
+ ...currentOpt,
41
+ ...next
42
+ };
43
+ },
44
+ add() {},
45
+ draw() {
46
+ const width = comp.width;
47
+ const height = comp.height;
48
+ const radius = currentOpt.radius ?? theme.shape.borderRadius;
49
+ const fill = currentOpt.fill ?? theme.palette.primary;
50
+ const textColor = currentOpt.textColor ?? theme.palette.text;
51
+ const size = currentOpt.size ?? theme.typography.fontSize;
52
+ const font = currentOpt.font ?? theme.typography.fontFamily;
53
+ const drawRect = k.drawRect;
54
+ drawRect?.({
55
+ pos: k.vec2(),
56
+ width,
57
+ height,
58
+ color: fill,
59
+ radius
60
+ });
61
+ const drawText = k.drawText;
62
+ drawText?.({
63
+ text: currentText,
64
+ pos: k.vec2(width / 2, height / 2),
65
+ size,
66
+ font,
67
+ color: textColor,
68
+ anchor: "center"
69
+ });
70
+ },
71
+ destroy() {}
72
+ };
73
+ return comp;
74
+ }
75
+ function createButtonObject(k, theme, text, opt) {
76
+ const width = opt.width ?? theme.components.button.width;
77
+ const height = opt.height ?? theme.components.button.height;
78
+ const radius = opt.radius ?? theme.shape.borderRadius;
79
+ const baseColor = opt.color ?? opt.fill ?? theme.palette.primary;
80
+ const hoverColor = opt.hoverColor ?? theme.palette.hover;
81
+ const pressedColor = opt.pressedColor ?? theme.palette.pressed;
82
+ const textColor = opt.textColor ?? theme.palette.text;
83
+ const size = opt.size ?? theme.typography.fontSize;
84
+ const font = opt.font ?? theme.typography.fontFamily;
85
+ const pos = opt.pos ?? k.vec2();
86
+ const obj = k.add([
87
+ k.rect(width, height, radius > 0 ? { radius } : void 0),
88
+ k.pos(pos),
89
+ k.area(),
90
+ k.color(baseColor),
91
+ k.anchor("center")
92
+ ]);
93
+ const label = obj.add([
94
+ k.text(text, {
95
+ size,
96
+ font
97
+ }),
98
+ k.color(textColor),
99
+ k.anchor("center")
100
+ ]);
101
+ let currentText = text;
102
+ let currentColor = baseColor;
103
+ obj.onClick(() => {
104
+ obj.color = pressedColor;
105
+ opt.onClick?.();
106
+ });
107
+ obj.onHover(() => {
108
+ obj.color = hoverColor;
109
+ obj.scale = k.vec2(1.05, 1.05);
110
+ k.setCursor("pointer");
111
+ });
112
+ obj.onHoverEnd(() => {
113
+ obj.color = currentColor;
114
+ obj.scale = k.vec2(1, 1);
115
+ k.setCursor("default");
116
+ });
117
+ obj.setSize = (w, h) => {
118
+ obj.width = w;
119
+ obj.height = h;
120
+ };
121
+ obj.setButtonColor = (color) => {
122
+ currentColor = color;
123
+ obj.color = color;
124
+ };
125
+ obj.setButtonText = (value) => {
126
+ currentText = value;
127
+ label.text = value;
128
+ };
129
+ obj.setButtonTextSize = (value) => {
130
+ label.textSize = value;
131
+ };
132
+ obj.setPosition = (x, y) => {
133
+ obj.pos = k.vec2(x, y);
134
+ };
135
+ label.text = currentText;
136
+ return obj;
137
+ }
138
+ // src/theme.ts
139
+ const DEFAULT_PRIMARY = "#4f8cff";
140
+ const DEFAULT_TEXT = "#ffffff";
141
+ /** Default theme used when no theme is provided. */
142
+ const defaultTheme = {
143
+ palette: {
144
+ primary: DEFAULT_PRIMARY,
145
+ secondary: DEFAULT_PRIMARY,
146
+ background: "#111827",
147
+ surface: "#1f2937",
148
+ text: DEFAULT_TEXT,
149
+ textSecondary: DEFAULT_TEXT,
150
+ hover: DEFAULT_PRIMARY,
151
+ pressed: DEFAULT_PRIMARY,
152
+ disabled: "#6b7280",
153
+ error: "#ef4444"
154
+ },
155
+ typography: {
156
+ fontFamily: "sans-serif",
157
+ fontSize: 18,
158
+ fontWeight: 400,
159
+ lineHeight: 1.2
160
+ },
161
+ shape: { borderRadius: 0 },
162
+ spacing: 8,
163
+ components: {
164
+ button: {
165
+ width: 120,
166
+ height: 40,
167
+ minWidth: 120,
168
+ paddingX: 12,
169
+ paddingY: 8,
170
+ textTransform: "none"
171
+ },
172
+ modal: {
173
+ backdropOpacity: .5,
174
+ borderRadius: 8
175
+ },
176
+ tooltip: {
177
+ background: "#111827",
178
+ textColor: DEFAULT_TEXT,
179
+ delay: 300
180
+ }
181
+ }
182
+ };
183
+ /** Resolve a partial theme into a fully resolved theme. */
184
+ function resolveTheme(input) {
185
+ return {
186
+ palette: {
187
+ ...defaultTheme.palette,
188
+ ...input?.palette
189
+ },
190
+ typography: {
191
+ ...defaultTheme.typography,
192
+ ...input?.typography
193
+ },
194
+ shape: {
195
+ ...defaultTheme.shape,
196
+ ...input?.shape
197
+ },
198
+ spacing: input?.spacing ?? defaultTheme.spacing,
199
+ components: {
200
+ button: {
201
+ ...defaultTheme.components.button,
202
+ ...input?.components?.button,
203
+ width: input?.components?.button?.width ?? input?.components?.button?.minWidth ?? defaultTheme.components.button.width
204
+ },
205
+ modal: {
206
+ ...defaultTheme.components.modal,
207
+ ...input?.components?.modal
208
+ },
209
+ tooltip: {
210
+ ...defaultTheme.components.tooltip,
211
+ ...input?.components?.tooltip
212
+ }
213
+ }
214
+ };
215
+ }
216
+ // src/plugin.ts
217
+ /** KAPLAY UI plugin. */
218
+ function uiPlugin(k, theme = {}) {
219
+ let userTheme = theme;
220
+ let currentTheme = resolveTheme(userTheme);
221
+ const getTheme = () => currentTheme;
222
+ const setTheme = (next) => {
223
+ userTheme = {
224
+ ...userTheme,
225
+ ...next
226
+ };
227
+ currentTheme = resolveTheme(userTheme);
228
+ };
229
+ const button = (text = "", opt = {}) => {
230
+ return createButtonComponent(k, currentTheme, text, opt);
231
+ };
232
+ const addButton = (text = "", opt = {}) => {
233
+ return createButtonObject(k, currentTheme, text, opt);
234
+ };
235
+ return {
236
+ button,
237
+ addButton,
238
+ setTheme,
239
+ getTheme
240
+ };
241
+ }
242
+ exports.createButtonComponent = createButtonComponent;
243
+ exports.createButtonObject = createButtonObject;
244
+ exports.uiPlugin = uiPlugin;
245
+ });
246
+
247
+ //# sourceMappingURL=plugin.umd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.umd.js","names":[],"sources":["../src/button.ts","../src/theme.ts","../src/plugin.ts"],"sourcesContent":["import type { Comp, GameObj, KAPLAYCtx, Vec2 } from 'kaplay';\n\nimport type { ButtonComp, ButtonCompOpt, Color, Theme } from './types';\n\nexport function createButtonComponent(\n k: KAPLAYCtx,\n theme: Theme,\n text: string,\n opt: ButtonCompOpt,\n): ButtonComp {\n let currentText = text;\n let currentOpt: ButtonCompOpt = { ...opt };\n\n const readWidth = (): number => {\n return currentOpt.width ?? theme.components.button.width;\n };\n\n const readHeight = (): number => {\n return currentOpt.height ?? theme.components.button.height;\n };\n\n const comp: ButtonComp = {\n id: 'button',\n get text() {\n return currentText;\n },\n set text(value: string) {\n currentText = value;\n },\n get width() {\n return readWidth();\n },\n set width(value: number) {\n currentOpt.width = value;\n },\n get height() {\n return readHeight();\n },\n set height(value: number) {\n currentOpt.height = value;\n },\n renderArea() {\n return new k.Rect(k.vec2(), comp.width, comp.height);\n },\n setStyle(next: Partial<ButtonCompOpt>): void {\n currentOpt = { ...currentOpt, ...next };\n },\n add(): void {\n // No-op: rendering is handled in draw().\n },\n draw(): void {\n const width = comp.width;\n const height = comp.height;\n const radius = currentOpt.radius ?? theme.shape.borderRadius;\n const fill = currentOpt.fill ?? theme.palette.primary;\n const textColor = currentOpt.textColor ?? theme.palette.text;\n const size = currentOpt.size ?? theme.typography.fontSize;\n const font = currentOpt.font ?? theme.typography.fontFamily;\n\n const drawRect = k.drawRect as unknown as\n ((opt: Record<string, unknown>) => void) | undefined;\n drawRect?.({\n pos: k.vec2(),\n width,\n height,\n color: fill,\n radius,\n });\n\n const drawText = k.drawText as unknown as\n ((opt: Record<string, unknown>) => void) | undefined;\n drawText?.({\n text: currentText,\n pos: k.vec2(width / 2, height / 2),\n size,\n font,\n color: textColor,\n anchor: 'center',\n });\n },\n destroy(): void {\n // No resources to release.\n },\n };\n\n return comp;\n}\n\nexport function createButtonObject(\n k: KAPLAYCtx,\n theme: Theme,\n text: string,\n opt: ButtonCompOpt & {\n pos?: Vec2;\n onClick?: () => void;\n color?: Color;\n hoverColor?: Color;\n pressedColor?: Color;\n textColor?: Color;\n },\n): GameObj {\n const width = opt.width ?? theme.components.button.width;\n const height = opt.height ?? theme.components.button.height;\n const radius = opt.radius ?? theme.shape.borderRadius;\n const baseColor = opt.color ?? opt.fill ?? theme.palette.primary;\n const hoverColor = opt.hoverColor ?? theme.palette.hover;\n const pressedColor = opt.pressedColor ?? theme.palette.pressed;\n const textColor = opt.textColor ?? theme.palette.text;\n const size = opt.size ?? theme.typography.fontSize;\n const font = opt.font ?? theme.typography.fontFamily;\n const pos = opt.pos ?? k.vec2();\n\n interface ButtonGameObj {\n add: (comps: Comp[]) => { text: string; textSize: number };\n onClick: (cb: () => void) => void;\n onHover: (cb: () => void) => void;\n onHoverEnd: (cb: () => void) => void;\n width: number;\n height: number;\n color: unknown;\n scale: { x: number; y: number };\n pos: { x: number; y: number };\n setSize: (w: number, h: number) => void;\n setButtonColor: (color: Color) => void;\n setButtonText: (value: string) => void;\n setButtonTextSize: (value: number) => void;\n setPosition: (x: number, y: number) => void;\n }\n\n const obj = k.add([\n k.rect(width, height, radius > 0 ? { radius } : undefined),\n k.pos(pos as never),\n k.area(),\n k.color(baseColor as never),\n k.anchor('center'),\n ]) as unknown as ButtonGameObj;\n\n const label = obj.add([\n k.text(text, { size, font }),\n k.color(textColor as never),\n k.anchor('center'),\n ]) as GameObj & { text: string; textSize: number };\n\n let currentText = text;\n let currentColor: Color = baseColor;\n\n obj.onClick(() => {\n obj.color = pressedColor;\n opt.onClick?.();\n });\n obj.onHover(() => {\n obj.color = hoverColor;\n obj.scale = k.vec2(1.05, 1.05);\n k.setCursor('pointer');\n });\n obj.onHoverEnd(() => {\n obj.color = currentColor;\n obj.scale = k.vec2(1, 1);\n k.setCursor('default');\n });\n\n obj.setSize = (w: number, h: number): void => {\n obj.width = w;\n obj.height = h;\n };\n\n obj.setButtonColor = (color: Color): void => {\n currentColor = color;\n obj.color = color;\n };\n\n obj.setButtonText = (value: string): void => {\n currentText = value;\n label.text = value;\n };\n\n obj.setButtonTextSize = (value: number): void => {\n label.textSize = value;\n };\n\n obj.setPosition = (x: number, y: number): void => {\n obj.pos = k.vec2(x, y);\n };\n\n label.text = currentText;\n\n return obj as unknown as GameObj;\n}\n","import type { DeepPartial, Theme } from './types';\n\nconst DEFAULT_PRIMARY = '#4f8cff';\nconst DEFAULT_TEXT = '#ffffff';\n\n/** Default theme used when no theme is provided. */\nexport const defaultTheme: Theme = {\n palette: {\n primary: DEFAULT_PRIMARY,\n secondary: DEFAULT_PRIMARY,\n background: '#111827',\n surface: '#1f2937',\n text: DEFAULT_TEXT,\n textSecondary: DEFAULT_TEXT,\n hover: DEFAULT_PRIMARY,\n pressed: DEFAULT_PRIMARY,\n disabled: '#6b7280',\n error: '#ef4444',\n },\n typography: {\n fontFamily: 'sans-serif',\n fontSize: 18,\n fontWeight: 400,\n lineHeight: 1.2,\n },\n shape: {\n borderRadius: 0,\n },\n spacing: 8,\n components: {\n button: {\n width: 120,\n height: 40,\n minWidth: 120,\n paddingX: 12,\n paddingY: 8,\n textTransform: 'none',\n },\n modal: {\n backdropOpacity: 0.5,\n borderRadius: 8,\n },\n tooltip: {\n background: '#111827',\n textColor: DEFAULT_TEXT,\n delay: 300,\n },\n },\n};\n\n/** Resolve a partial theme into a fully resolved theme. */\nexport function resolveTheme(input?: DeepPartial<Theme>): Theme {\n return {\n palette: { ...defaultTheme.palette, ...input?.palette },\n typography: { ...defaultTheme.typography, ...input?.typography },\n shape: { ...defaultTheme.shape, ...input?.shape },\n spacing: input?.spacing ?? defaultTheme.spacing,\n components: {\n button: {\n ...defaultTheme.components.button,\n ...input?.components?.button,\n width:\n input?.components?.button?.width ??\n input?.components?.button?.minWidth ??\n defaultTheme.components.button.width,\n },\n modal: {\n ...defaultTheme.components.modal,\n ...input?.components?.modal,\n },\n tooltip: {\n ...defaultTheme.components.tooltip,\n ...input?.components?.tooltip,\n },\n },\n };\n}\n","import type { GameObj, KAPLAYCtx, Vec2 } from 'kaplay';\n\nimport { createButtonComponent, createButtonObject } from './button';\nimport { resolveTheme } from './theme';\nimport type {\n ButtonComp,\n ButtonCompOpt,\n Color,\n DeepPartial,\n Theme,\n} from './types';\n\nexport { createButtonComponent, createButtonObject } from './button';\nexport type { ButtonComp, ButtonCompOpt, Color, Theme } from './types';\n\ndeclare module 'kaplay' {\n interface KAPLAYCtx {\n button: (text?: string, opt?: ButtonCompOpt) => ButtonComp;\n addButton: (\n text?: string,\n opt?: ButtonCompOpt & {\n pos?: Vec2;\n onClick?: () => void;\n color?: Color;\n hoverColor?: Color;\n pressedColor?: Color;\n textColor?: Color;\n },\n ) => GameObj;\n setTheme: (theme: DeepPartial<Theme>) => void;\n getTheme: () => Theme;\n }\n}\n\n/** KAPLAY UI plugin. */\nexport function uiPlugin(k: KAPLAYCtx, theme: DeepPartial<Theme> = {}) {\n let userTheme: DeepPartial<Theme> = theme;\n let currentTheme: Theme = resolveTheme(userTheme);\n\n const getTheme = (): Theme => currentTheme;\n\n const setTheme = (next: DeepPartial<Theme>): void => {\n userTheme = { ...userTheme, ...next };\n currentTheme = resolveTheme(userTheme);\n };\n\n const button = (text = '', opt: ButtonCompOpt = {}): ButtonComp => {\n return createButtonComponent(k, currentTheme, text, opt);\n };\n\n const addButton = (\n text = '',\n opt: ButtonCompOpt & {\n pos?: Vec2;\n onClick?: () => void;\n color?: Color;\n hoverColor?: Color;\n pressedColor?: Color;\n textColor?: Color;\n } = {},\n ): GameObj => {\n return createButtonObject(k, currentTheme, text, opt);\n };\n\n return {\n button,\n addButton,\n setTheme,\n getTheme,\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ (function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports):typeof define==`function`&&define.amd?define([`exports`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.KaplayPluginUI={}))})(this,function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});function t(e,t,n,r){let i=n,a={...r},o=()=>a.width??t.components.button.width,s=()=>a.height??t.components.button.height,c={id:`button`,get text(){return i},set text(e){i=e},get width(){return o()},set width(e){a.width=e},get height(){return s()},set height(e){a.height=e},renderArea(){return new e.Rect(e.vec2(),c.width,c.height)},setStyle(e){a={...a,...e}},add(){},draw(){let n=c.width,r=c.height,o=a.radius??t.shape.borderRadius,s=a.fill??t.palette.primary,l=a.textColor??t.palette.text,u=a.size??t.typography.fontSize,d=a.font??t.typography.fontFamily,f=e.drawRect;f?.({pos:e.vec2(),width:n,height:r,color:s,radius:o});let p=e.drawText;p?.({text:i,pos:e.vec2(n/2,r/2),size:u,font:d,color:l,anchor:`center`})},destroy(){}};return c}function n(e,t,n,r){let i=r.width??t.components.button.width,a=r.height??t.components.button.height,o=r.radius??t.shape.borderRadius,s=r.color??r.fill??t.palette.primary,c=r.hoverColor??t.palette.hover,l=r.pressedColor??t.palette.pressed,u=r.textColor??t.palette.text,d=r.size??t.typography.fontSize,f=r.font??t.typography.fontFamily,p=r.pos??e.vec2(),m=e.add([e.rect(i,a,o>0?{radius:o}:void 0),e.pos(p),e.area(),e.color(s),e.anchor(`center`)]),h=m.add([e.text(n,{size:d,font:f}),e.color(u),e.anchor(`center`)]),g=n,_=s;return m.onClick(()=>{m.color=l,r.onClick?.()}),m.onHover(()=>{m.color=c,m.scale=e.vec2(1.05,1.05),e.setCursor(`pointer`)}),m.onHoverEnd(()=>{m.color=_,m.scale=e.vec2(1,1),e.setCursor(`default`)}),m.setSize=(e,t)=>{m.width=e,m.height=t},m.setButtonColor=e=>{_=e,m.color=e},m.setButtonText=e=>{g=e,h.text=e},m.setButtonTextSize=e=>{h.textSize=e},m.setPosition=(t,n)=>{m.pos=e.vec2(t,n)},h.text=g,m}let r=`#4f8cff`,i=`#ffffff`,a={palette:{primary:r,secondary:r,background:`#111827`,surface:`#1f2937`,text:i,textSecondary:i,hover:r,pressed:r,disabled:`#6b7280`,error:`#ef4444`},typography:{fontFamily:`sans-serif`,fontSize:18,fontWeight:400,lineHeight:1.2},shape:{borderRadius:0},spacing:8,components:{button:{width:120,height:40,minWidth:120,paddingX:12,paddingY:8,textTransform:`none`},modal:{backdropOpacity:.5,borderRadius:8},tooltip:{background:`#111827`,textColor:i,delay:300}}};function o(e){return{palette:{...a.palette,...e?.palette},typography:{...a.typography,...e?.typography},shape:{...a.shape,...e?.shape},spacing:e?.spacing??a.spacing,components:{button:{...a.components.button,...e?.components?.button,width:e?.components?.button?.width??e?.components?.button?.minWidth??a.components.button.width},modal:{...a.components.modal,...e?.components?.modal},tooltip:{...a.components.tooltip,...e?.components?.tooltip}}}}function s(e,r={}){let i=r,a=o(i);return{button:(n=``,r={})=>t(e,a,n,r),addButton:(t=``,r={})=>n(e,a,t,r),setTheme:e=>{i={...i,...e},a=o(i)},getTheme:()=>a}}e.createButtonComponent=t,e.createButtonObject=n,e.uiPlugin=s});
2
+ //# sourceMappingURL=plugin.umd.min.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.umd.min.js","names":["factory","global","exports","createButtonComponent","currentText","text","currentOpt","opt","readWidth","theme","readHeight","comp","value","k","next","width","height","radius","fill","textColor","size","font","drawRect","drawText","createButtonObject","baseColor","hoverColor","pressedColor","pos","obj","label","currentColor","w","h","color","x","y","DEFAULT_PRIMARY","DEFAULT_TEXT","defaultTheme","resolveTheme","input","uiPlugin","userTheme","currentTheme"],"sources":["../src/button.ts","../src/theme.ts","../src/plugin.ts"],"sourcesContent":["import type { Comp, GameObj, KAPLAYCtx, Vec2 } from 'kaplay';\n\nimport type { ButtonComp, ButtonCompOpt, Color, Theme } from './types';\n\nexport function createButtonComponent(\n k: KAPLAYCtx,\n theme: Theme,\n text: string,\n opt: ButtonCompOpt,\n): ButtonComp {\n let currentText = text;\n let currentOpt: ButtonCompOpt = { ...opt };\n\n const readWidth = (): number => {\n return currentOpt.width ?? theme.components.button.width;\n };\n\n const readHeight = (): number => {\n return currentOpt.height ?? theme.components.button.height;\n };\n\n const comp: ButtonComp = {\n id: 'button',\n get text() {\n return currentText;\n },\n set text(value: string) {\n currentText = value;\n },\n get width() {\n return readWidth();\n },\n set width(value: number) {\n currentOpt.width = value;\n },\n get height() {\n return readHeight();\n },\n set height(value: number) {\n currentOpt.height = value;\n },\n renderArea() {\n return new k.Rect(k.vec2(), comp.width, comp.height);\n },\n setStyle(next: Partial<ButtonCompOpt>): void {\n currentOpt = { ...currentOpt, ...next };\n },\n add(): void {\n // No-op: rendering is handled in draw().\n },\n draw(): void {\n const width = comp.width;\n const height = comp.height;\n const radius = currentOpt.radius ?? theme.shape.borderRadius;\n const fill = currentOpt.fill ?? theme.palette.primary;\n const textColor = currentOpt.textColor ?? theme.palette.text;\n const size = currentOpt.size ?? theme.typography.fontSize;\n const font = currentOpt.font ?? theme.typography.fontFamily;\n\n const drawRect = k.drawRect as unknown as\n ((opt: Record<string, unknown>) => void) | undefined;\n drawRect?.({\n pos: k.vec2(),\n width,\n height,\n color: fill,\n radius,\n });\n\n const drawText = k.drawText as unknown as\n ((opt: Record<string, unknown>) => void) | undefined;\n drawText?.({\n text: currentText,\n pos: k.vec2(width / 2, height / 2),\n size,\n font,\n color: textColor,\n anchor: 'center',\n });\n },\n destroy(): void {\n // No resources to release.\n },\n };\n\n return comp;\n}\n\nexport function createButtonObject(\n k: KAPLAYCtx,\n theme: Theme,\n text: string,\n opt: ButtonCompOpt & {\n pos?: Vec2;\n onClick?: () => void;\n color?: Color;\n hoverColor?: Color;\n pressedColor?: Color;\n textColor?: Color;\n },\n): GameObj {\n const width = opt.width ?? theme.components.button.width;\n const height = opt.height ?? theme.components.button.height;\n const radius = opt.radius ?? theme.shape.borderRadius;\n const baseColor = opt.color ?? opt.fill ?? theme.palette.primary;\n const hoverColor = opt.hoverColor ?? theme.palette.hover;\n const pressedColor = opt.pressedColor ?? theme.palette.pressed;\n const textColor = opt.textColor ?? theme.palette.text;\n const size = opt.size ?? theme.typography.fontSize;\n const font = opt.font ?? theme.typography.fontFamily;\n const pos = opt.pos ?? k.vec2();\n\n interface ButtonGameObj {\n add: (comps: Comp[]) => { text: string; textSize: number };\n onClick: (cb: () => void) => void;\n onHover: (cb: () => void) => void;\n onHoverEnd: (cb: () => void) => void;\n width: number;\n height: number;\n color: unknown;\n scale: { x: number; y: number };\n pos: { x: number; y: number };\n setSize: (w: number, h: number) => void;\n setButtonColor: (color: Color) => void;\n setButtonText: (value: string) => void;\n setButtonTextSize: (value: number) => void;\n setPosition: (x: number, y: number) => void;\n }\n\n const obj = k.add([\n k.rect(width, height, radius > 0 ? { radius } : undefined),\n k.pos(pos as never),\n k.area(),\n k.color(baseColor as never),\n k.anchor('center'),\n ]) as unknown as ButtonGameObj;\n\n const label = obj.add([\n k.text(text, { size, font }),\n k.color(textColor as never),\n k.anchor('center'),\n ]) as GameObj & { text: string; textSize: number };\n\n let currentText = text;\n let currentColor: Color = baseColor;\n\n obj.onClick(() => {\n obj.color = pressedColor;\n opt.onClick?.();\n });\n obj.onHover(() => {\n obj.color = hoverColor;\n obj.scale = k.vec2(1.05, 1.05);\n k.setCursor('pointer');\n });\n obj.onHoverEnd(() => {\n obj.color = currentColor;\n obj.scale = k.vec2(1, 1);\n k.setCursor('default');\n });\n\n obj.setSize = (w: number, h: number): void => {\n obj.width = w;\n obj.height = h;\n };\n\n obj.setButtonColor = (color: Color): void => {\n currentColor = color;\n obj.color = color;\n };\n\n obj.setButtonText = (value: string): void => {\n currentText = value;\n label.text = value;\n };\n\n obj.setButtonTextSize = (value: number): void => {\n label.textSize = value;\n };\n\n obj.setPosition = (x: number, y: number): void => {\n obj.pos = k.vec2(x, y);\n };\n\n label.text = currentText;\n\n return obj as unknown as GameObj;\n}\n","import type { DeepPartial, Theme } from './types';\n\nconst DEFAULT_PRIMARY = '#4f8cff';\nconst DEFAULT_TEXT = '#ffffff';\n\n/** Default theme used when no theme is provided. */\nexport const defaultTheme: Theme = {\n palette: {\n primary: DEFAULT_PRIMARY,\n secondary: DEFAULT_PRIMARY,\n background: '#111827',\n surface: '#1f2937',\n text: DEFAULT_TEXT,\n textSecondary: DEFAULT_TEXT,\n hover: DEFAULT_PRIMARY,\n pressed: DEFAULT_PRIMARY,\n disabled: '#6b7280',\n error: '#ef4444',\n },\n typography: {\n fontFamily: 'sans-serif',\n fontSize: 18,\n fontWeight: 400,\n lineHeight: 1.2,\n },\n shape: {\n borderRadius: 0,\n },\n spacing: 8,\n components: {\n button: {\n width: 120,\n height: 40,\n minWidth: 120,\n paddingX: 12,\n paddingY: 8,\n textTransform: 'none',\n },\n modal: {\n backdropOpacity: 0.5,\n borderRadius: 8,\n },\n tooltip: {\n background: '#111827',\n textColor: DEFAULT_TEXT,\n delay: 300,\n },\n },\n};\n\n/** Resolve a partial theme into a fully resolved theme. */\nexport function resolveTheme(input?: DeepPartial<Theme>): Theme {\n return {\n palette: { ...defaultTheme.palette, ...input?.palette },\n typography: { ...defaultTheme.typography, ...input?.typography },\n shape: { ...defaultTheme.shape, ...input?.shape },\n spacing: input?.spacing ?? defaultTheme.spacing,\n components: {\n button: {\n ...defaultTheme.components.button,\n ...input?.components?.button,\n width:\n input?.components?.button?.width ??\n input?.components?.button?.minWidth ??\n defaultTheme.components.button.width,\n },\n modal: {\n ...defaultTheme.components.modal,\n ...input?.components?.modal,\n },\n tooltip: {\n ...defaultTheme.components.tooltip,\n ...input?.components?.tooltip,\n },\n },\n };\n}\n","import type { GameObj, KAPLAYCtx, Vec2 } from 'kaplay';\n\nimport { createButtonComponent, createButtonObject } from './button';\nimport { resolveTheme } from './theme';\nimport type {\n ButtonComp,\n ButtonCompOpt,\n Color,\n DeepPartial,\n Theme,\n} from './types';\n\nexport { createButtonComponent, createButtonObject } from './button';\nexport type { ButtonComp, ButtonCompOpt, Color, Theme } from './types';\n\ndeclare module 'kaplay' {\n interface KAPLAYCtx {\n button: (text?: string, opt?: ButtonCompOpt) => ButtonComp;\n addButton: (\n text?: string,\n opt?: ButtonCompOpt & {\n pos?: Vec2;\n onClick?: () => void;\n color?: Color;\n hoverColor?: Color;\n pressedColor?: Color;\n textColor?: Color;\n },\n ) => GameObj;\n setTheme: (theme: DeepPartial<Theme>) => void;\n getTheme: () => Theme;\n }\n}\n\n/** KAPLAY UI plugin. */\nexport function uiPlugin(k: KAPLAYCtx, theme: DeepPartial<Theme> = {}) {\n let userTheme: DeepPartial<Theme> = theme;\n let currentTheme: Theme = resolveTheme(userTheme);\n\n const getTheme = (): Theme => currentTheme;\n\n const setTheme = (next: DeepPartial<Theme>): void => {\n userTheme = { ...userTheme, ...next };\n currentTheme = resolveTheme(userTheme);\n };\n\n const button = (text = '', opt: ButtonCompOpt = {}): ButtonComp => {\n return createButtonComponent(k, currentTheme, text, opt);\n };\n\n const addButton = (\n text = '',\n opt: ButtonCompOpt & {\n pos?: Vec2;\n onClick?: () => void;\n color?: Color;\n hoverColor?: Color;\n pressedColor?: Color;\n textColor?: Color;\n } = {},\n ): GameObj => {\n return createButtonObject(k, currentTheme, text, opt);\n };\n\n return {\n button,\n addButton,\n setTheme,\n getTheme,\n };\n}\n"],"mappings":"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"}
package/global.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ import type { uiPlugin } from './dist/plugin.cjs';
2
+
3
+ declare global {
4
+ var uiPlugin: uiPlugin;
5
+ }
6
+
7
+ export {};
package/global.js ADDED
File without changes
package/package.json CHANGED
@@ -1,4 +1,98 @@
1
1
  {
2
2
  "name": "kaplay-plugin-ui",
3
- "version": "0.0.0"
3
+ "version": "1.0.0",
4
+ "description": "Kaplay plugin UI",
5
+ "author": "Mark <mark@remarkablemark.org> (https://remarkablemark.org)",
6
+ "main": "./dist/plugin.cjs",
7
+ "module": "./dist/plugin.mjs",
8
+ "types": "./dist/plugin.d.mts",
9
+ "exports": {
10
+ ".": {
11
+ "import": {
12
+ "types": "./dist/plugin.d.mts",
13
+ "default": "./dist/plugin.mjs"
14
+ },
15
+ "require": {
16
+ "types": "./dist/plugin.d.cts",
17
+ "default": "./dist/plugin.cjs"
18
+ }
19
+ },
20
+ "./global": {
21
+ "import": {
22
+ "types": "./global.d.ts",
23
+ "default": "./global.js"
24
+ },
25
+ "require": {
26
+ "types": "./global.d.ts",
27
+ "default": "./global.js"
28
+ }
29
+ }
30
+ },
31
+ "scripts": {
32
+ "build": "tsdown",
33
+ "clean": "rm -rf coverage dist docs",
34
+ "docs": "typedoc",
35
+ "docs:watch": "npm run docs -- --watch",
36
+ "lint": "eslint .",
37
+ "lint:fix": "npm run lint -- --fix",
38
+ "lint:package": "publint",
39
+ "lint:tsc": "tsc",
40
+ "lint:types": "npx @arethetypeswrong/cli --pack",
41
+ "prepare": "husky",
42
+ "prepublishOnly": "npm run build && npm run lint && npm run lint:tsc && npm run test:ci",
43
+ "start": "vite --open",
44
+ "test": "vitest run",
45
+ "test:ci": "CI=true npm test -- --color --coverage",
46
+ "test:watch": "vitest --coverage.enabled=false"
47
+ },
48
+ "repository": {
49
+ "type": "git",
50
+ "url": "git+https://github.com/remarkablegames/kaplay-plugin-ui.git"
51
+ },
52
+ "bugs": {
53
+ "url": "https://github.com/remarkablegames/kaplay-plugin-ui/issues"
54
+ },
55
+ "keywords": [
56
+ "kaplay",
57
+ "plugin",
58
+ "ui",
59
+ "game"
60
+ ],
61
+ "peerDependencies": {
62
+ "kaplay": "*"
63
+ },
64
+ "devDependencies": {
65
+ "@commitlint/cli": "21.2.4",
66
+ "@commitlint/config-conventional": "21.2.4",
67
+ "@eslint/js": "10.0.1",
68
+ "@types/node": "26.6.6",
69
+ "@vitest/coverage-v8": "5.0.5",
70
+ "eslint": "10.13.0",
71
+ "eslint-plugin-prettier": "5.5.7",
72
+ "eslint-plugin-simple-import-sort": "14.0.0",
73
+ "eslint-plugin-tsdoc": "0.5.4",
74
+ "husky": "9.1.7",
75
+ "kaplay": "3001.0.19",
76
+ "lint-staged": "17.6.0",
77
+ "prettier": "3.9.10",
78
+ "publint": "0.3.25",
79
+ "tsdown": "0.23.4",
80
+ "typedoc": "0.28.20",
81
+ "typescript": "6.0.3",
82
+ "typescript-eslint": "8.72.1",
83
+ "vite": "8.3.5",
84
+ "vitest": "5.0.5"
85
+ },
86
+ "files": [
87
+ "dist/",
88
+ "global.d.ts",
89
+ "global.js"
90
+ ],
91
+ "funding": [
92
+ {
93
+ "type": "github",
94
+ "url": "https://github.com/sponsors/remarkablemark"
95
+ }
96
+ ],
97
+ "license": "MIT"
4
98
  }