kaplay-ui 0.20.8 → 1.0.0-alpha.10
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 +140 -45
- package/dist/uiplugin.js +113 -0
- package/dist/uiplugin.umd.cjs +1 -0
- package/docs/_config.yml +4 -0
- package/docs/_layouts/default.html +23 -0
- package/docs/design-decisions/_layout/default.html +23 -0
- package/docs/design-decisions/design-decisions.md +6 -0
- package/docs/design-decisions/label/label-defaults.md +219 -0
- package/docs/faq.md +72 -0
- package/docs/index.md +126 -0
- package/package.json +13 -4
- package/src/index.ts +133 -0
- package/assets/cb-icon.png +0 -0
- package/assets/kaplay-ui-logo.png +0 -0
- package/inputs/index.js +0 -226
- /package/{index.js → docs/design-decisions/text-button/text-button-defaults.md} +0 -0
package/README.md
CHANGED
|
@@ -2,38 +2,49 @@
|
|
|
2
2
|
<img src="assets/kaplay-ui-logo.png" alt="kaplay-ui logo" width="400" >
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
|
-
# KAPLAY UI
|
|
5
|
+
# KAPLAY UI Plugin
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
A lightweight UI helper plugin for the [KAPLAY](https://kaplayjs.com/) game library, providing ready‑to‑use UI Game Objects such as text‑based buttons **and labels**.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
This plugin simplifies the creation of UI Game Object elements using familiar KAPLAY primitives, offering sensible defaults while remaining flexible and composable.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## ✨ Features
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
- 🎛️ **Text Button** — easily create a button with centered text
|
|
16
|
+
- 🏷️ **Label** — a lightweight text-based UI element for HUDs, counters, tooltips, and more
|
|
17
|
+
- 🧩 Designed to feel like native KAPLAY components
|
|
18
|
+
- 🎨 Comes with built‑in sizing, positioning, and outline defaults
|
|
19
|
+
- 🔧 Fully customizable through the normal KAPLAY component system
|
|
17
20
|
|
|
18
21
|
---
|
|
19
22
|
|
|
20
23
|
## 📦 Installation
|
|
21
24
|
|
|
22
|
-
|
|
25
|
+
Install the plugin using your preferred package manager:
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
npm install kaplay-ui
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
or:
|
|
23
32
|
|
|
24
|
-
```
|
|
25
|
-
|
|
33
|
+
```sh
|
|
34
|
+
pnpm add kaplay-ui
|
|
26
35
|
```
|
|
27
36
|
|
|
28
|
-
|
|
37
|
+
or:
|
|
38
|
+
|
|
39
|
+
```sh
|
|
40
|
+
yarn add kaplay-ui
|
|
41
|
+
```
|
|
29
42
|
|
|
30
43
|
---
|
|
31
44
|
|
|
32
45
|
## 🚀 Usage
|
|
33
46
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
The `kaplayUI` plugin is exported from the package root:
|
|
47
|
+
Import and register the plugin when initializing your KAPLAY game:
|
|
37
48
|
|
|
38
49
|
```ts
|
|
39
50
|
import kaplay from "kaplay";
|
|
@@ -46,20 +57,18 @@ const k = kaplay({
|
|
|
46
57
|
|
|
47
58
|
You now have access to the UI helpers via your `k` instance:
|
|
48
59
|
|
|
49
|
-
```
|
|
60
|
+
```js
|
|
50
61
|
const btn = k.addTextButton("Play", 200, 100);
|
|
51
62
|
const label = k.addLabel("Score: 0");
|
|
52
63
|
```
|
|
53
64
|
|
|
54
65
|
---
|
|
55
66
|
|
|
56
|
-
##
|
|
57
|
-
|
|
58
|
-
### 🔤 **Text Button** (`addTextButton()`)
|
|
67
|
+
## 🔘 `addTextButton()`
|
|
59
68
|
|
|
60
69
|
Creates a button-like GameObj with centered text and some convenient defaults.
|
|
61
70
|
|
|
62
|
-
|
|
71
|
+
### **Signature**
|
|
63
72
|
|
|
64
73
|
```ts
|
|
65
74
|
addTextButton(
|
|
@@ -69,7 +78,7 @@ addTextButton(
|
|
|
69
78
|
): GameObj
|
|
70
79
|
```
|
|
71
80
|
|
|
72
|
-
|
|
81
|
+
### **Default values**
|
|
73
82
|
|
|
74
83
|
| Parameter | Default |
|
|
75
84
|
| --------- | ---------- |
|
|
@@ -77,18 +86,18 @@ addTextButton(
|
|
|
77
86
|
| `width` | `200` |
|
|
78
87
|
| `height` | `100` |
|
|
79
88
|
|
|
80
|
-
|
|
89
|
+
### **Default styling**
|
|
81
90
|
|
|
82
91
|
When created, the button includes:
|
|
83
92
|
|
|
84
|
-
- k.outline(3)
|
|
85
|
-
- k.pos(0, 0)
|
|
86
|
-
- k.anchor("topleft")
|
|
87
|
-
- k.area() — for click/hover detection
|
|
93
|
+
- `k.outline(3)`
|
|
94
|
+
- `k.pos(0, 0)`
|
|
95
|
+
- `k.anchor("topleft")`
|
|
96
|
+
- `k.area()` — for click/hover detection
|
|
88
97
|
|
|
89
|
-
|
|
98
|
+
### **Example**
|
|
90
99
|
|
|
91
|
-
```
|
|
100
|
+
```js
|
|
92
101
|
// Default button
|
|
93
102
|
const btn1 = k.addTextButton();
|
|
94
103
|
|
|
@@ -106,11 +115,11 @@ btn2.onClick(() => {
|
|
|
106
115
|
|
|
107
116
|
---
|
|
108
117
|
|
|
109
|
-
|
|
118
|
+
## 🏷️ `addLabel()`
|
|
110
119
|
|
|
111
120
|
A lightweight text-based UI element — ideal for HUD counters, tooltips, status text, or titles.
|
|
112
121
|
|
|
113
|
-
|
|
122
|
+
### **Signature**
|
|
114
123
|
|
|
115
124
|
```ts
|
|
116
125
|
addLabel(
|
|
@@ -120,7 +129,7 @@ addLabel(
|
|
|
120
129
|
)
|
|
121
130
|
```
|
|
122
131
|
|
|
123
|
-
|
|
132
|
+
### **Default values**
|
|
124
133
|
|
|
125
134
|
| Parameter | Default |
|
|
126
135
|
| --------- | ------- |
|
|
@@ -128,7 +137,7 @@ addLabel(
|
|
|
128
137
|
| `width` | `160` |
|
|
129
138
|
| `height` | `96` |
|
|
130
139
|
|
|
131
|
-
|
|
140
|
+
### **Example**
|
|
132
141
|
|
|
133
142
|
```js
|
|
134
143
|
// Default button
|
|
@@ -146,11 +155,11 @@ const scoreLabel = k.addLabel(`Score: ${score}`);
|
|
|
146
155
|
|
|
147
156
|
k.wait(2, () => {
|
|
148
157
|
score++;
|
|
149
|
-
scoreLabel.children[0].text = `Score: ${score}
|
|
150
|
-
})
|
|
158
|
+
scoreLabel.children[0].text = `Score: ${score}`
|
|
159
|
+
})
|
|
151
160
|
```
|
|
152
161
|
|
|
153
|
-
|
|
162
|
+
### Common use cases
|
|
154
163
|
|
|
155
164
|
- HUD overlays
|
|
156
165
|
- Score counters
|
|
@@ -160,22 +169,108 @@ k.wait(2, () => {
|
|
|
160
169
|
|
|
161
170
|
---
|
|
162
171
|
|
|
163
|
-
##
|
|
172
|
+
## 📱 Mobile‑Friendly UI Demo
|
|
164
173
|
|
|
165
|
-
|
|
174
|
+
This example shows how to build a touch‑friendly UI using `addTextButton()` on mobile devices. It uses responsive scaling and KAPLAY’s `stretch` and `letterbox` features to adapt to different screen sizes.
|
|
166
175
|
|
|
167
|
-
|
|
176
|
+
### **Example**
|
|
168
177
|
|
|
169
|
-
|
|
178
|
+
```js
|
|
179
|
+
import kaplay from "kaplay";
|
|
180
|
+
import kaplayUI from "kaplay-ui";
|
|
170
181
|
|
|
171
|
-
|
|
172
|
-
|
|
182
|
+
const k = kaplay({
|
|
183
|
+
width: 480, // required when using letterbox/stretch
|
|
184
|
+
height: 270,
|
|
185
|
+
background: [25, 25, 25],
|
|
186
|
+
plugins: [kaplayUI],
|
|
187
|
+
|
|
188
|
+
stretch: true, // resize canvas on mobile
|
|
189
|
+
letterbox: true, // preserve aspect ratio
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
function uiScale() {
|
|
193
|
+
return Math.min(k.width() / 400, 1.4);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function centerX(width: number) {
|
|
197
|
+
return k.center().x - width / 2;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
k.scene("mobile-menu", () => {
|
|
201
|
+
const scale = uiScale();
|
|
202
|
+
|
|
203
|
+
k.add([
|
|
204
|
+
k.text("Mobile Menu", { size: 36 * scale }),
|
|
205
|
+
k.anchor("center"),
|
|
206
|
+
k.pos(k.center().x, 70 * scale),
|
|
207
|
+
]);
|
|
208
|
+
|
|
209
|
+
const btnWidth = 260 * scale;
|
|
210
|
+
const btnHeight = 90 * scale;
|
|
211
|
+
|
|
212
|
+
const startBtn = k.addTextButton("Start", btnWidth, btnHeight);
|
|
213
|
+
startBtn.pos = k.vec2(centerX(btnWidth), 150 * scale);
|
|
214
|
+
startBtn.onClick(() => k.go("mobile-game"));
|
|
215
|
+
startBtn.onHover(() => (startBtn.color = k.rgb(140, 220, 140)));
|
|
216
|
+
startBtn.onHoverEnd(() => (startBtn.color = k.WHITE));
|
|
217
|
+
|
|
218
|
+
const settingsBtn = k.addTextButton("Settings", btnWidth, btnHeight);
|
|
219
|
+
settingsBtn.pos = k.vec2(centerX(btnWidth), 280 * scale);
|
|
220
|
+
settingsBtn.onClick(() => k.go("settings"));
|
|
221
|
+
settingsBtn.onHover(() => (settingsBtn.color = k.rgb(140, 160, 240)));
|
|
222
|
+
settingsBtn.onHoverEnd(() => (settingsBtn.color = k.WHITE));
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
k.scene("mobile-game", () => {
|
|
226
|
+
const scale = uiScale();
|
|
227
|
+
|
|
228
|
+
k.add([
|
|
229
|
+
k.text("Game Scene", { size: 28 * scale }),
|
|
230
|
+
k.anchor("center"),
|
|
231
|
+
k.pos(k.center().x, 90 * scale),
|
|
232
|
+
]);
|
|
233
|
+
|
|
234
|
+
const backBtn = k.addTextButton("Back", 200 * scale, 80 * scale);
|
|
235
|
+
backBtn.pos = k.vec2(centerX(200 * scale), k.height() - 120 * scale);
|
|
236
|
+
backBtn.onClick(() => k.go("mobile-menu"));
|
|
237
|
+
backBtn.onHover(() => (backBtn.scale = 1.1 * scale));
|
|
238
|
+
backBtn.onHoverEnd(() => (backBtn.scale = scale));
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
k.scene("settings", () => {
|
|
242
|
+
const scale = uiScale();
|
|
243
|
+
|
|
244
|
+
k.add([
|
|
245
|
+
k.text("Settings", { size: 32 * scale }),
|
|
246
|
+
k.anchor("center"),
|
|
247
|
+
k.pos(k.center().x, 70 * scale),
|
|
248
|
+
]);
|
|
249
|
+
|
|
250
|
+
const backBtn = k.addTextButton("Back", 200 * scale, 80 * scale);
|
|
251
|
+
backBtn.pos = k.vec2(centerX(200 * scale), k.height() - 140 * scale);
|
|
252
|
+
backBtn.onClick(() => k.go("mobile-menu"));
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
k.go("mobile-menu");
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### ✔ Features demonstrated
|
|
259
|
+
|
|
260
|
+
- Responsive UI scaling
|
|
261
|
+
- Touch‑friendly hit areas
|
|
262
|
+
- Hover animations for mobile
|
|
263
|
+
- Auto‑centered vertical layout
|
|
264
|
+
- Scene navigation
|
|
173
265
|
|
|
174
266
|
---
|
|
175
267
|
|
|
176
|
-
##
|
|
268
|
+
## 📚 Documentation
|
|
269
|
+
|
|
270
|
+
_**Evolving**_ documentation is available in the [docs](https://github.com/jbakchr/kaplay-ui/blob/v1/docs/index.md) folder.
|
|
271
|
+
|
|
272
|
+
---
|
|
177
273
|
|
|
178
|
-
|
|
179
|
-
Open an issue on GitHub:
|
|
274
|
+
## 📄 License
|
|
180
275
|
|
|
181
|
-
|
|
276
|
+
MIT — free for personal and commercial use.
|
package/dist/uiplugin.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
//#region src/components/button/index.ts
|
|
2
|
+
function e(e, t, n, r) {
|
|
3
|
+
return e.make([
|
|
4
|
+
e.rect(t, n, { radius: 15 }),
|
|
5
|
+
e.color(200, 200, 200),
|
|
6
|
+
e.outline(3, e.rgb(92, 91, 91)),
|
|
7
|
+
e.pos(0, 0),
|
|
8
|
+
e.anchor(r),
|
|
9
|
+
e.area()
|
|
10
|
+
]);
|
|
11
|
+
}
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region src/components/label/index.ts
|
|
14
|
+
function t(e, t, n) {
|
|
15
|
+
return e.make([
|
|
16
|
+
e.rect(t, n),
|
|
17
|
+
e.pos(0, 0),
|
|
18
|
+
e.color(0, 0, 0),
|
|
19
|
+
e.opacity(.7),
|
|
20
|
+
e.anchor("topleft")
|
|
21
|
+
]);
|
|
22
|
+
}
|
|
23
|
+
//#endregion
|
|
24
|
+
//#region src/components/text/index.ts
|
|
25
|
+
function n(e, t, n, r, i, a) {
|
|
26
|
+
return e.make([
|
|
27
|
+
e.text(t, { size: n }),
|
|
28
|
+
e.color(0, 0, 0),
|
|
29
|
+
e.pos(r, i),
|
|
30
|
+
e.anchor(a)
|
|
31
|
+
]);
|
|
32
|
+
}
|
|
33
|
+
//#endregion
|
|
34
|
+
//#region src/helpers/pos-utils.ts
|
|
35
|
+
function r(e) {
|
|
36
|
+
return {
|
|
37
|
+
cX: e.width / 2,
|
|
38
|
+
cY: e.height / 2
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function i(e, t, n, r, i) {
|
|
42
|
+
switch (t.anchor) {
|
|
43
|
+
case "bot":
|
|
44
|
+
n.use(e.pos(0 * r, -1 * i));
|
|
45
|
+
break;
|
|
46
|
+
case "botleft":
|
|
47
|
+
n.use(e.pos(1 * r, -1 * i));
|
|
48
|
+
break;
|
|
49
|
+
case "botright":
|
|
50
|
+
n.use(e.pos(-1 * r, -1 * i));
|
|
51
|
+
break;
|
|
52
|
+
case "center":
|
|
53
|
+
n.use(e.pos(0 * r, 0 * i));
|
|
54
|
+
break;
|
|
55
|
+
case "left":
|
|
56
|
+
n.use(e.pos(1 * r, 0 * i));
|
|
57
|
+
break;
|
|
58
|
+
case "right":
|
|
59
|
+
n.use(e.pos(-1 * r, 0 * i));
|
|
60
|
+
break;
|
|
61
|
+
case "top":
|
|
62
|
+
n.use(e.pos(0 * r, 1 * i));
|
|
63
|
+
break;
|
|
64
|
+
case "topleft":
|
|
65
|
+
n.use(e.pos(1 * r, 1 * i));
|
|
66
|
+
break;
|
|
67
|
+
case "topright":
|
|
68
|
+
n.use(e.pos(-1 * r, 1 * i));
|
|
69
|
+
break;
|
|
70
|
+
default: break;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
//#endregion
|
|
74
|
+
//#region src/helpers/layout-utils.ts
|
|
75
|
+
var a = new Set([
|
|
76
|
+
"anchor",
|
|
77
|
+
"pos",
|
|
78
|
+
"area"
|
|
79
|
+
]);
|
|
80
|
+
function o(e) {
|
|
81
|
+
return a.has(e);
|
|
82
|
+
}
|
|
83
|
+
//#endregion
|
|
84
|
+
//#region src/elements/text-button/index.ts
|
|
85
|
+
function s(t, a, s, c) {
|
|
86
|
+
let l = e(t, s, c, "topleft"), { cX: u, cY: d } = r(l), f = n(t, a, 22, u, d, "center");
|
|
87
|
+
return l.add(f), t.add(l), l.onUse((e) => {
|
|
88
|
+
if (o(e)) {
|
|
89
|
+
let { cX: e, cY: n } = r(l);
|
|
90
|
+
i(t, l, f, e, n);
|
|
91
|
+
}
|
|
92
|
+
}), l.onHover(() => {
|
|
93
|
+
t.setCursor("pointer");
|
|
94
|
+
}), l.onHoverEnd(() => {
|
|
95
|
+
t.setCursor("default");
|
|
96
|
+
}), l;
|
|
97
|
+
}
|
|
98
|
+
//#endregion
|
|
99
|
+
//#region src/elements/label/index.ts
|
|
100
|
+
function c(e, i, a, o) {
|
|
101
|
+
let s = t(e, a, o), { cX: c, cY: l } = r(s), u = n(e, i, 20, c, l, "center");
|
|
102
|
+
return u.use(e.color(255, 255, 255)), s.add(u), e.add(s), s;
|
|
103
|
+
}
|
|
104
|
+
//#endregion
|
|
105
|
+
//#region src/index.ts
|
|
106
|
+
function l(e) {
|
|
107
|
+
return {
|
|
108
|
+
addTextButton: (t = "Settings", n = 150, r = 60) => s(e, t, n, r),
|
|
109
|
+
addLabel: (t = "", n = 160, r = 96) => c(e, t, n, r)
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
//#endregion
|
|
113
|
+
export { l as default };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(function(e,t){typeof exports==`object`&&typeof module<`u`?module.exports=t():typeof define==`function`&&define.amd?define([],t):(e=typeof globalThis<`u`?globalThis:e||self,e.uiPlugin=t())})(this,function(){function e(e,t,n,r){return e.make([e.rect(t,n,{radius:15}),e.color(200,200,200),e.outline(3,e.rgb(92,91,91)),e.pos(0,0),e.anchor(r),e.area()])}function t(e,t,n){return e.make([e.rect(t,n),e.pos(0,0),e.color(0,0,0),e.opacity(.7),e.anchor(`topleft`)])}function n(e,t,n,r,i,a){return e.make([e.text(t,{size:n}),e.color(0,0,0),e.pos(r,i),e.anchor(a)])}function r(e){return{cX:e.width/2,cY:e.height/2}}function i(e,t,n,r,i){switch(t.anchor){case`bot`:n.use(e.pos(0*r,-1*i));break;case`botleft`:n.use(e.pos(1*r,-1*i));break;case`botright`:n.use(e.pos(-1*r,-1*i));break;case`center`:n.use(e.pos(0*r,0*i));break;case`left`:n.use(e.pos(1*r,0*i));break;case`right`:n.use(e.pos(-1*r,0*i));break;case`top`:n.use(e.pos(0*r,1*i));break;case`topleft`:n.use(e.pos(1*r,1*i));break;case`topright`:n.use(e.pos(-1*r,1*i));break;default:break}}var a=new Set([`anchor`,`pos`,`area`]);function o(e){return a.has(e)}function s(t,a,s,c){let l=e(t,s,c,`topleft`),{cX:u,cY:d}=r(l),f=n(t,a,22,u,d,`center`);return l.add(f),t.add(l),l.onUse(e=>{if(o(e)){let{cX:e,cY:n}=r(l);i(t,l,f,e,n)}}),l.onHover(()=>{t.setCursor(`pointer`)}),l.onHoverEnd(()=>{t.setCursor(`default`)}),l}function c(e,i,a,o){let s=t(e,a,o),{cX:c,cY:l}=r(s),u=n(e,i,20,c,l,`center`);return u.use(e.color(255,255,255)),s.add(u),e.add(s),s}function l(e){return{addTextButton:(t=`Settings`,n=150,r=60)=>s(e,t,n,r),addLabel:(t=``,n=160,r=96)=>c(e,t,n,r)}}return l});
|
package/docs/_config.yml
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
|
|
8
|
+
<title>{{ page.title }} - {{ site.title }}</title>
|
|
9
|
+
|
|
10
|
+
<!-- Load custom stylesheet -->
|
|
11
|
+
<link rel="stylesheet" href="{{ '/assets/style.css' | relative_url }}" />
|
|
12
|
+
</head>
|
|
13
|
+
|
|
14
|
+
<body>
|
|
15
|
+
<!-- KAPLAY-style header -->
|
|
16
|
+
<header class="kaplay-header">
|
|
17
|
+
<img src="{{ '/assets/logo.png' | relative_url }}" class="kaplay-logo" />
|
|
18
|
+
</header>
|
|
19
|
+
|
|
20
|
+
<!-- Main content -->
|
|
21
|
+
<main class="page-content">{{ content }}</main>
|
|
22
|
+
</body>
|
|
23
|
+
</html>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
|
|
8
|
+
<title>{{ page.title }} - {{ site.title }}</title>
|
|
9
|
+
|
|
10
|
+
<!-- Load custom stylesheet -->
|
|
11
|
+
<link rel="stylesheet" href="{{ '/assets/style.css' | relative_url }}" />
|
|
12
|
+
</head>
|
|
13
|
+
|
|
14
|
+
<body>
|
|
15
|
+
<!-- KAPLAY-style header -->
|
|
16
|
+
<header class="kaplay-header">
|
|
17
|
+
<img src="{{ '/assets/logo.png' | relative_url }}" class="kaplay-logo" />
|
|
18
|
+
</header>
|
|
19
|
+
|
|
20
|
+
<!-- Main content -->
|
|
21
|
+
<main class="page-content">{{ content }}</main>
|
|
22
|
+
</body>
|
|
23
|
+
</html>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# Design Decisions
|
|
2
|
+
|
|
3
|
+
This md and the subfolders below it documents to design decision defaults of:
|
|
4
|
+
|
|
5
|
+
- [Label](https://github.com/jbakchr/kaplay-ui/blob/v1/docs/design-decisions/label/label-defaults.md)
|
|
6
|
+
- [Text Button](https://github.com/jbakchr/kaplay-ui/blob/v1/docs/design-decisions/text-button/text-button-defaults.md)
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# Label Design Decisions
|
|
2
|
+
|
|
3
|
+
This document provides why the current defaults for the `addLabel()` Game Objects are what they are for:
|
|
4
|
+
|
|
5
|
+
1. [Background color](#-most-common-background-colors-for-labels-in-games)
|
|
6
|
+
2. [Width and Height](#-the-most-common-label-sizes-in-games)
|
|
7
|
+
|
|
8
|
+
## 🎨 Most Common Background Colors for Labels in Games
|
|
9
|
+
|
|
10
|
+
### **1. Dark, Semi-Transparent Background (Most Common Overall)**
|
|
11
|
+
|
|
12
|
+
Examples:
|
|
13
|
+
|
|
14
|
+
- `rgba(0, 0, 0, 0.5)`
|
|
15
|
+
- `rgba(0, 0, 0, 0.7)`
|
|
16
|
+
|
|
17
|
+
Why it's common:
|
|
18
|
+
|
|
19
|
+
- Works on almost every scene background (bright, dark, colorful).
|
|
20
|
+
- Makes white or light text extremely readable.
|
|
21
|
+
- Gives a “HUD” feel found in many genres.
|
|
22
|
+
|
|
23
|
+
Where it's used:
|
|
24
|
+
|
|
25
|
+
- Health bars
|
|
26
|
+
- Tooltips
|
|
27
|
+
- HUD counters
|
|
28
|
+
- Inventory labels
|
|
29
|
+
|
|
30
|
+
This is the single most widely used choice in modern 2D games.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
### **2. Solid Black or Near‑Black Background**
|
|
35
|
+
|
|
36
|
+
Examples:
|
|
37
|
+
|
|
38
|
+
- `rgb(0, 0, 0)`
|
|
39
|
+
- `rgb(20, 20, 20)`
|
|
40
|
+
|
|
41
|
+
Why:
|
|
42
|
+
|
|
43
|
+
- Maximum contrast
|
|
44
|
+
- Minimalistic
|
|
45
|
+
- Fits sci-fi, retro, and clean UI styles
|
|
46
|
+
|
|
47
|
+
Often used for:
|
|
48
|
+
|
|
49
|
+
- Menu labels
|
|
50
|
+
- Title bars
|
|
51
|
+
- Status text
|
|
52
|
+
|
|
53
|
+
Good when opacity effects aren’t needed.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
### **3. Light Background With Dark Text**
|
|
58
|
+
|
|
59
|
+
Examples:
|
|
60
|
+
|
|
61
|
+
- `rgb(255, 255, 255)` with black text
|
|
62
|
+
- `rgb(240, 240, 240)` for softer look
|
|
63
|
+
|
|
64
|
+
Why:
|
|
65
|
+
|
|
66
|
+
- Works well for “window”-style UI
|
|
67
|
+
- Mimics desktop/UI patterns
|
|
68
|
+
- Very readable for high-contrast HUDs
|
|
69
|
+
|
|
70
|
+
Used in:
|
|
71
|
+
|
|
72
|
+
- Casual games
|
|
73
|
+
- Puzzle games
|
|
74
|
+
- Mobile UX-inspired UI
|
|
75
|
+
|
|
76
|
+
This is less common than dark backgrounds but still widely used.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
### **4. Colored Background Matching the Game’s Theme**
|
|
81
|
+
|
|
82
|
+
Examples:
|
|
83
|
+
|
|
84
|
+
- Blue, red, or green surfaces
|
|
85
|
+
- Faded color fills like `rgba(50, 100, 200, 0.8)`
|
|
86
|
+
|
|
87
|
+
Why:
|
|
88
|
+
|
|
89
|
+
- Looks more “stylized” or thematic
|
|
90
|
+
- Helps differentiate label purposes (e.g., warning = red)
|
|
91
|
+
- Used in highly themed menus and fantasy games
|
|
92
|
+
|
|
93
|
+
Common in:
|
|
94
|
+
|
|
95
|
+
- JRPG-style menus
|
|
96
|
+
- Crafting systems
|
|
97
|
+
- Quest logs
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
### **5. No Background (Text Only)**
|
|
102
|
+
|
|
103
|
+
While not literally a “background color,” this is a very common choice:
|
|
104
|
+
|
|
105
|
+
- Pure text HUD
|
|
106
|
+
- Floating damage numbers
|
|
107
|
+
- Dialogue text overlays
|
|
108
|
+
|
|
109
|
+
Games often use this when:
|
|
110
|
+
|
|
111
|
+
- Text doesn’t compete with backgrounds
|
|
112
|
+
- The look should be minimal
|
|
113
|
+
|
|
114
|
+
You can easily support this in `addLabel()` by letting `background: null`.
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## ⭐ If you want the safest, most universal choice…
|
|
119
|
+
|
|
120
|
+
#### → **Use a dark semi-transparent background with light text.**
|
|
121
|
+
|
|
122
|
+
Something like:
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
background: rgba(0, 0, 0, 0.5),
|
|
126
|
+
color: rgb(255, 255, 255)
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
This works cleanly on:
|
|
130
|
+
|
|
131
|
+
- Dark levels
|
|
132
|
+
- Bright levels
|
|
133
|
+
- High-motion action scenes
|
|
134
|
+
- Stylized or pixel art environments
|
|
135
|
+
|
|
136
|
+
And it's easy for players to read instantly.
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## 🎮 The Most Common Label Sizes in Games
|
|
141
|
+
|
|
142
|
+
Even though no source gives explicit “standard label dimensions,” we _can_ derive the common ranges from:
|
|
143
|
+
|
|
144
|
+
- Typical **pixel-art game resolutions** such as **320×180**, **640×360**, **1280×720** (all frequently used) [\[screwloose....github.io\]](https://screwloose-games.github.io/documentation/content/guides/art/pixel_art/pixel_art_guide.html)
|
|
145
|
+
- Common **sprite and tile sizes** (16×16, 32×32, 64×64) which UI assets usually align to [\[aipixelart.net\]](https://aipixelart.net/node/267)
|
|
146
|
+
- Observed dimensions in **pixel-art UI packs** on itch.io (buttons, panels, labels etc. typically in the 32–128px height range) [\[itch.io\]](https://itch.io/game-assets/tag-gui/tag-pixel-art)
|
|
147
|
+
|
|
148
|
+
From these, we can determine practical label sizes used in real games.
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
### 📐 **Typical Label Widths**
|
|
153
|
+
|
|
154
|
+
#### **Small labels (HUD counters, score, ammo):**
|
|
155
|
+
|
|
156
|
+
- **96px – 160px** wide
|
|
157
|
+
- Fits single short text like “SCORE: 0”, “HP”, “XP”, “TIME”
|
|
158
|
+
|
|
159
|
+
#### **Medium labels (section titles, longer stat text):**
|
|
160
|
+
|
|
161
|
+
- **160px – 256px** wide
|
|
162
|
+
- Fits text strings like “OBJECTIVE COMPLETE”, “CURRENT QUEST”
|
|
163
|
+
|
|
164
|
+
#### **Large labels (UI titles, menu headings):**
|
|
165
|
+
|
|
166
|
+
- **256px – 384px** wide
|
|
167
|
+
- Used for things like “INVENTORY”, “SETTINGS”, “PAUSED”
|
|
168
|
+
|
|
169
|
+
These sizes map cleanly into 16px/32px multiples recommended for pixel-art alignment.
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
### 📏 **Typical Label Heights**
|
|
174
|
+
|
|
175
|
+
Based on common UI asset proportions:
|
|
176
|
+
|
|
177
|
+
- **Text-only HUD label:**
|
|
178
|
+
**24–32px** tall (for 8–16px font sizes)
|
|
179
|
+
|
|
180
|
+
- **Label with padding + background panel:**
|
|
181
|
+
**32–48px** tall is most standard
|
|
182
|
+
|
|
183
|
+
- **Chunkier RPG or fantasy labels:**
|
|
184
|
+
**48–64px** tall in many itch.io GUI packs [\[itch.io\]](https://itch.io/game-assets/tag-gui/tag-pixel-art)
|
|
185
|
+
|
|
186
|
+
These heights align with the commonly used sprite grids of **16×16**, **32×32**, and **64×64** pixels from pixel-art standards. [\[aipixelart.net\]](https://aipixelart.net/node/267)
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
### 🧩 **Putting It All Together (Practical Defaults)**
|
|
191
|
+
|
|
192
|
+
For a game using a common base pixel-art resolution like **640×360** (widely recommended), typical label sizes look like this: [\[screwloose....github.io\]](https://screwloose-games.github.io/documentation/content/guides/art/pixel_art/pixel_art_guide.html)
|
|
193
|
+
|
|
194
|
+
| Label Type | Width | Height |
|
|
195
|
+
| ------------------- | ------------- | ----------- |
|
|
196
|
+
| Small HUD label | **120–160px** | **24–32px** |
|
|
197
|
+
| Medium UI label | **160–256px** | **32–48px** |
|
|
198
|
+
| Large heading label | **256–384px** | **48–64px** |
|
|
199
|
+
|
|
200
|
+
These fit cleanly into standard pixel-art grids and look correct across the common 320×180, 640×360, and 1280×720 style pixel resolutions.
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
### 🎯 **Recommended Defaults for kaplay-ui**
|
|
205
|
+
|
|
206
|
+
If you want to provide sensible defaults for your Label component:
|
|
207
|
+
|
|
208
|
+
- **Width:** Automatically sized based on text
|
|
209
|
+
- **Height:**
|
|
210
|
+
- **32px** for simple HUD labels
|
|
211
|
+
- **48px** for background panels
|
|
212
|
+
|
|
213
|
+
This keeps things aligned with the pixel-art norms and avoids awkward scaling.
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
_**See next decisions about `Text Button` here:**_
|
|
218
|
+
|
|
219
|
+
- [Text Button](https://github.com/jbakchr/kaplay-ui/blob/v1/docs/design-decisions/text-button/text-button-defaults.md)
|
package/docs/faq.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# ❓ KAPLAY UI — FAQ
|
|
2
|
+
|
|
3
|
+
_Frequently asked questions about the KAPLAY UI plugin._
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 📚 Table of Contents
|
|
8
|
+
|
|
9
|
+
- [General](#-general)
|
|
10
|
+
- [Installation](#-installation)
|
|
11
|
+
- [Usage](#-usage)
|
|
12
|
+
- [Licensing](#-licensing)
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 🧠 General
|
|
17
|
+
|
|
18
|
+
**Q:** _**What is this plugin for?**_
|
|
19
|
+
|
|
20
|
+
**A:** KAPLAY UI provides lightweight, composable UI helpers — such as text‑based buttons and labels — built using familiar **KAPLAY primitives**.
|
|
21
|
+
|
|
22
|
+
<div class="block">
|
|
23
|
+
It reduces boilerplate, keeps UI consistent, and helps you build menus, HUDs, overlays, and interactive UI faster.
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 📦 Installation
|
|
29
|
+
|
|
30
|
+
### **Q:** _**How do I install KAPLAY UI?**_
|
|
31
|
+
|
|
32
|
+
**A:** Install from your project root using your preferred package manager:
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
npm install kaplay-ui@next
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 🚀 Usage
|
|
41
|
+
|
|
42
|
+
### **Q:** _**How do I use the `kaplay-ui` plugin?**_
|
|
43
|
+
|
|
44
|
+
**A:** Import and enable the plugin when creating your KAPLAY instance:
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import kaplay from "kaplay";
|
|
48
|
+
import kaplayUI from "kaplay-ui";
|
|
49
|
+
|
|
50
|
+
const k = kaplay({
|
|
51
|
+
plugins: [kaplayUI],
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Now you can create UI components such as:
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
k.addTextButton("Play");
|
|
59
|
+
k.addLabel("Score: 0");
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## 📄 Licensing
|
|
65
|
+
|
|
66
|
+
### **Q:** _**How may I use `kaplay-ui` ?**_
|
|
67
|
+
|
|
68
|
+
The **MIT License** allows full personal and commercial use with virtually no restrictions.
|
|
69
|
+
|
|
70
|
+
<div class="block">
|
|
71
|
+
🔓 **In short:** You can use, modify, distribute, and even sell projects using KAPLAY UI.
|
|
72
|
+
</div>
|
package/docs/index.md
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# 🦖 KAPLAY-UI 🧩
|
|
2
|
+
|
|
3
|
+
_A lightweight and flexible **UI plugin for KAPLAY**, built to feel like a natural extension of the engine._
|
|
4
|
+
|
|
5
|
+
Create UI elements quickly using familiar KAPLAY primitives — buttons, labels, HUD elements, menus, and more.
|
|
6
|
+
|
|
7
|
+
<div class="block">
|
|
8
|
+
|
|
9
|
+
✨ KAPLAY UI helps you build game-ready user interfaces with almost no setup.
|
|
10
|
+
|
|
11
|
+
Perfect for menus, HUDs, mobile UI, prototypes, and small-to-medium games.
|
|
12
|
+
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 🚀 Why KAPLAY UI?
|
|
18
|
+
|
|
19
|
+
- 🧩 Works exactly like native KAPLAY
|
|
20
|
+
- 🎛️ Easy, declarative API
|
|
21
|
+
- ✨ Sensible styling defaults
|
|
22
|
+
- 🎨 Fully customizable
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 📦 Installation _(v1.0.0-alpha\*)_
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
npm install kaplay-ui@next
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 🧰 Getting Started
|
|
35
|
+
|
|
36
|
+
Add the plugin when creating your KAPLAY instance:
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
import kaplay from "kaplay";
|
|
40
|
+
import kaplayUI from "kaplay-ui";
|
|
41
|
+
|
|
42
|
+
const k = kaplay({
|
|
43
|
+
plugins: [kaplayUI],
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Now create a UI element:
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
const tb = k.addTextButton("Play");
|
|
51
|
+
const sl = k.addLabel("Score: 0");
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
_**That’s it** — you’re ready to build UI!_
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## 🧱 Core Components
|
|
59
|
+
|
|
60
|
+
### 🔤 Text Button
|
|
61
|
+
|
|
62
|
+
Create an interactive button with centered text and built‑in outline & area detection.
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
const tb = k.addTextButton("Play");
|
|
66
|
+
|
|
67
|
+
tb.onClick(() => {
|
|
68
|
+
console.log("Let's play!");
|
|
69
|
+
});
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### 🏷️ Label
|
|
73
|
+
|
|
74
|
+
A small, flexible text element — perfect for HUDs.
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
const lbl = k.addLabel("Score: 0");
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
_More components are being added soon as the plugin grows._ 🎉
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## 📚 Documentation
|
|
85
|
+
|
|
86
|
+
_Soon_ .. you can find more details here:
|
|
87
|
+
|
|
88
|
+
- 📄 **faq.md** — _Questions and answers_
|
|
89
|
+
- 🧠 **design-decisions** — _The Whys?_
|
|
90
|
+
- 🔘 **API Pages** — _The What & How_
|
|
91
|
+
- 🧪 **Examples** — _Learn by doing_
|
|
92
|
+
- 🪧 **Demos** — _See it in action_
|
|
93
|
+
|
|
94
|
+
## 🗺️ Roadmap (WIP)
|
|
95
|
+
|
|
96
|
+
- **⏳ More component types**
|
|
97
|
+
_(sliders, toggles, panels)_
|
|
98
|
+
|
|
99
|
+
- **🎨 Theme presets**
|
|
100
|
+
_(built-in light/dark UI packs)_
|
|
101
|
+
|
|
102
|
+
- **🎛️ Complex layout helpers**
|
|
103
|
+
_(stacks, grids)_
|
|
104
|
+
|
|
105
|
+
- **📘 Full documentation site**
|
|
106
|
+
_(getting started, faq and .. more)_
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## 🤝 Contributing
|
|
111
|
+
|
|
112
|
+
Contributions and feature ideas are warmly welcome!
|
|
113
|
+
|
|
114
|
+
Feel free to submit issues or PRs on GitHub — or share ideas in the discussions tab.
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## 📜 License
|
|
119
|
+
|
|
120
|
+
MIT © 2026 [Jonas Bak Phillipson](https://github.com/jbakchr)
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
<div class="block" style="text-align:center;font-weight:bold;">
|
|
125
|
+
<p>Made with 💚 + 🦖 by:<p>
|
|
126
|
+
<p style="font-style:italic;"><span style="color:#ffde86;">The</span> <span style="color:#f2ae99;">KAPLAY </span><span style="color:#CF9FFF;">Community</span></p>
|
package/package.json
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kaplay-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0-alpha.10",
|
|
4
4
|
"description": "UI components for KAPLAY",
|
|
5
|
-
"
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"dev": "vite dev",
|
|
9
|
+
"build": "vite build"
|
|
10
|
+
},
|
|
6
11
|
"repository": {
|
|
7
12
|
"type": "git",
|
|
8
13
|
"url": "git+https://github.com/jbakchr/kaplay-ui.git"
|
|
@@ -19,7 +24,11 @@
|
|
|
19
24
|
},
|
|
20
25
|
"homepage": "https://github.com/jbakchr/kaplay-ui#readme",
|
|
21
26
|
"dependencies": {
|
|
22
|
-
"kaplay": "^3001.0.
|
|
27
|
+
"kaplay": "^3001.0.19"
|
|
23
28
|
},
|
|
24
|
-
"
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/node": "^25.5.0",
|
|
31
|
+
"vite": "^8.0.2",
|
|
32
|
+
"vite-plugin-dts": "^4.5.4"
|
|
33
|
+
}
|
|
25
34
|
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import type { KAPLAYCtx } from "kaplay";
|
|
2
|
+
|
|
3
|
+
// Types
|
|
4
|
+
import { LabelComponent, TextButtonElement } from "./types";
|
|
5
|
+
|
|
6
|
+
// Text Button
|
|
7
|
+
import { createLabel, createTextButton } from "./elements";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* # KAPLAY UI Plugin
|
|
11
|
+
*
|
|
12
|
+
* A small UI helper plugin that adds convenience functions for creating
|
|
13
|
+
* common UI components such as buttons and labels within a KAPLAY game.
|
|
14
|
+
*
|
|
15
|
+
* The returned object extends your KAPLAY context with UI creation helpers.
|
|
16
|
+
*
|
|
17
|
+
* ## Available Helpers
|
|
18
|
+
* - `addTextButton(txt?, width?, height?)`
|
|
19
|
+
* - `addLabel(txt?, width?, height?)`
|
|
20
|
+
*
|
|
21
|
+
* @returns {{
|
|
22
|
+
* addTextButton: (txt?: string, width?: number, height?: number) => TextButtonElement,
|
|
23
|
+
* addLabel: (txt?: string, width?: number, height?: number) => LabelElement
|
|
24
|
+
* }}
|
|
25
|
+
* An object exposing helper functions for creating UI elements.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* import kaplay from "kaplay";
|
|
29
|
+
* import kaplayUI from "kaplay-ui"
|
|
30
|
+
*
|
|
31
|
+
* const k = kaplay({
|
|
32
|
+
* plugins: [kaplayUI]
|
|
33
|
+
* })
|
|
34
|
+
*
|
|
35
|
+
* const label = k.addLabel("Hello!");
|
|
36
|
+
* const button = k.addTextButton("Start");
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
export default function kaplayUI(k: KAPLAYCtx): {
|
|
40
|
+
addTextButton: (
|
|
41
|
+
txt?: string,
|
|
42
|
+
width?: number,
|
|
43
|
+
height?: number,
|
|
44
|
+
) => TextButtonElement;
|
|
45
|
+
addLabel: (txt?: string, width?: number, height?: number) => LabelComponent;
|
|
46
|
+
} {
|
|
47
|
+
return {
|
|
48
|
+
/**
|
|
49
|
+
* # Text button
|
|
50
|
+
* Creates a clickable game object containing a button with centered text.
|
|
51
|
+
*
|
|
52
|
+
* This helper provides sensible defaults for size, layout, and styling so you can
|
|
53
|
+
* quickly add UI buttons to your KAPLAY game.
|
|
54
|
+
*
|
|
55
|
+
* ## Default Parameter Values
|
|
56
|
+
* - `txt`: `"Button"`
|
|
57
|
+
* - `width`: `200`
|
|
58
|
+
* - `height`: `100`
|
|
59
|
+
*
|
|
60
|
+
* ## Default Styling
|
|
61
|
+
* The button object is created with:
|
|
62
|
+
* - `k.outline(3)`
|
|
63
|
+
* - `k.pos(0, 0)`
|
|
64
|
+
* - `k.anchor("topleft")`
|
|
65
|
+
* - `k.area()`
|
|
66
|
+
*
|
|
67
|
+
* @param {string} [txt="Button"]
|
|
68
|
+
* The text label displayed at the center of the button.
|
|
69
|
+
*
|
|
70
|
+
* @param {number} [width=200]
|
|
71
|
+
* Width of the button in pixels.
|
|
72
|
+
*
|
|
73
|
+
* @param {number} [height=100]
|
|
74
|
+
* Height of the button in pixels.
|
|
75
|
+
*
|
|
76
|
+
* @returns {TextButton}
|
|
77
|
+
* A KAPLAY Game Object representing the button with centered text.
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* const playBtn = addTextButton("Play");
|
|
81
|
+
* playBtn.onClick(() => console.log("Play clicked!"));
|
|
82
|
+
*/
|
|
83
|
+
addTextButton: (
|
|
84
|
+
txt: string = "Settings",
|
|
85
|
+
width: number = 150,
|
|
86
|
+
height: number = 60,
|
|
87
|
+
): TextButtonElement => createTextButton(k, txt, width, height),
|
|
88
|
+
/**
|
|
89
|
+
* # Label
|
|
90
|
+
* Creates a simple text container with a background and layout box.
|
|
91
|
+
*
|
|
92
|
+
* Labels are lightweight UI elements used to display non-interactive text
|
|
93
|
+
* such as titles, descriptions, or dynamic info (scores, status, etc.).
|
|
94
|
+
*
|
|
95
|
+
* ## Default Parameter Values
|
|
96
|
+
* - `txt`: `""`
|
|
97
|
+
* - `width`: `160`
|
|
98
|
+
* - `height`: `96`
|
|
99
|
+
*
|
|
100
|
+
* ## Default Styling
|
|
101
|
+
* A label includes:
|
|
102
|
+
* - `k.color(0, 0, 0)`
|
|
103
|
+
* - `k.opacity(0.7)`
|
|
104
|
+
* - `k.anchor("topleft")`
|
|
105
|
+
*
|
|
106
|
+
* @param {string} [txt=""]
|
|
107
|
+
* The text content shown inside the label.
|
|
108
|
+
*
|
|
109
|
+
* @param {number} [width=160]
|
|
110
|
+
* Width of the label container in pixels.
|
|
111
|
+
*
|
|
112
|
+
* @param {number} [height=96]
|
|
113
|
+
* Height of the label container in pixels.
|
|
114
|
+
*
|
|
115
|
+
* @returns {LabelElement}
|
|
116
|
+
* A KAPLAY game object representing a text label.
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* let score = 0
|
|
120
|
+
* const scoreLabel = addLabel(`Score: ${score}`);
|
|
121
|
+
*
|
|
122
|
+
* k.wait(2, () => {
|
|
123
|
+
* score++:
|
|
124
|
+
* scoreLabel.children[0].text = `Score: ${score}`
|
|
125
|
+
* })
|
|
126
|
+
*/
|
|
127
|
+
addLabel: (
|
|
128
|
+
txt: string = "",
|
|
129
|
+
width: number = 160,
|
|
130
|
+
height: number = 96,
|
|
131
|
+
): LabelComponent => createLabel(k, txt, width, height),
|
|
132
|
+
};
|
|
133
|
+
}
|
package/assets/cb-icon.png
DELETED
|
Binary file
|
|
Binary file
|
package/inputs/index.js
DELETED
|
@@ -1,226 +0,0 @@
|
|
|
1
|
-
import "kaplay/global";
|
|
2
|
-
|
|
3
|
-
import cbIcon from "../assets/cb-icon.png";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Makes button with centered text
|
|
7
|
-
* @param {string} txt - Button text to display
|
|
8
|
-
* @param {number} x - The x postion to set (default is 0).
|
|
9
|
-
* @param {number} y - The x postion to set (default is 0)
|
|
10
|
-
* @param {number} width - Width of button (default is 100)
|
|
11
|
-
* @param {number} height - Height of button (default is 50)
|
|
12
|
-
* @param {number} btnRadius - Border radius of button (default is 8)
|
|
13
|
-
* @param {number} btnOutline - Button outline (default is 1)
|
|
14
|
-
* @param {number} txtSize - Text size of button (default is 15)
|
|
15
|
-
* @returns {GameObj}
|
|
16
|
-
*/
|
|
17
|
-
export const makeTextButton = (
|
|
18
|
-
txt,
|
|
19
|
-
x = 0,
|
|
20
|
-
y = 0,
|
|
21
|
-
width = 100,
|
|
22
|
-
height = 50,
|
|
23
|
-
btnRadius = 8,
|
|
24
|
-
btnOutline = 1,
|
|
25
|
-
txtSize = 15
|
|
26
|
-
) => {
|
|
27
|
-
// Make button
|
|
28
|
-
const btn = make([
|
|
29
|
-
rect(width, height, { radius: btnRadius }),
|
|
30
|
-
pos(x, y),
|
|
31
|
-
area(),
|
|
32
|
-
scale(1),
|
|
33
|
-
outline(btnOutline),
|
|
34
|
-
color(255, 255, 255),
|
|
35
|
-
]);
|
|
36
|
-
|
|
37
|
-
// Make button text
|
|
38
|
-
const btnText = make([
|
|
39
|
-
text(txt, { size: txtSize }),
|
|
40
|
-
pos(width / 2, height / 2),
|
|
41
|
-
anchor("center"),
|
|
42
|
-
color(0, 0, 0),
|
|
43
|
-
]);
|
|
44
|
-
|
|
45
|
-
// Add text to button
|
|
46
|
-
btn.add(btnText);
|
|
47
|
-
|
|
48
|
-
return btn;
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Makes toggle
|
|
53
|
-
* @param {number} x - The x postion to set (default is 0)
|
|
54
|
-
* @param {number} y - The x postion to set (default is 0)
|
|
55
|
-
* @param {number} width - Toggle width (default is 50)
|
|
56
|
-
* @param {number} height - Toggle height (default is 25)
|
|
57
|
-
* @returns {GameObj}
|
|
58
|
-
*/
|
|
59
|
-
export const makeToggle = (x = 0, y = 0, width = 50, height = 25) => {
|
|
60
|
-
// Toggle base
|
|
61
|
-
const toggle = make([
|
|
62
|
-
rect(width, height, { radius: height / 2 }),
|
|
63
|
-
pos(x, y),
|
|
64
|
-
color(169, 169, 169),
|
|
65
|
-
area(),
|
|
66
|
-
{
|
|
67
|
-
toggled: false,
|
|
68
|
-
},
|
|
69
|
-
]);
|
|
70
|
-
|
|
71
|
-
// Toggle button
|
|
72
|
-
const toggleBtn = make([
|
|
73
|
-
circle(height * 0.4),
|
|
74
|
-
color(WHITE),
|
|
75
|
-
pos(height / 2, height / 2),
|
|
76
|
-
]);
|
|
77
|
-
|
|
78
|
-
// Add button to toggleBase
|
|
79
|
-
toggle.add(toggleBtn);
|
|
80
|
-
|
|
81
|
-
toggle.onClick(() => {
|
|
82
|
-
if (toggle.toggled) {
|
|
83
|
-
toggle.use(color(169, 169, 169));
|
|
84
|
-
toggleBtn.use(pos(height / 2, height / 2));
|
|
85
|
-
toggle.toggled = false;
|
|
86
|
-
} else {
|
|
87
|
-
toggle.use(color(148, 188, 236));
|
|
88
|
-
toggleBtn.use(pos(width - height / 2, height / 2));
|
|
89
|
-
toggle.toggled = true;
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
return toggle;
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Makes checkbox
|
|
98
|
-
* @param {number} x - The x postion to set (default is 0)
|
|
99
|
-
* @param {number} y - The x postion to set (default is 0)
|
|
100
|
-
* @param {number} width - Checkbox width (default is 25)
|
|
101
|
-
* @param {number} height - Checkbox height (default is 25)
|
|
102
|
-
* @returns {GameObj}
|
|
103
|
-
*/
|
|
104
|
-
export const makeCheckbox = (x = 0, y = 0, width = 25, height = 25) => {
|
|
105
|
-
loadSprite("cb", cbIcon);
|
|
106
|
-
|
|
107
|
-
const checkbox = make([
|
|
108
|
-
rect(width, height, { radius: 4 }),
|
|
109
|
-
pos(x, y),
|
|
110
|
-
color(255, 255, 255),
|
|
111
|
-
outline(1),
|
|
112
|
-
area(),
|
|
113
|
-
{
|
|
114
|
-
checked: false,
|
|
115
|
-
},
|
|
116
|
-
]);
|
|
117
|
-
|
|
118
|
-
const checkedIcon = make([sprite("cb", { width, height }), area()]);
|
|
119
|
-
|
|
120
|
-
checkbox.onClick(() => {
|
|
121
|
-
if (checkbox.checked) {
|
|
122
|
-
checkbox.use(color(255, 255, 255));
|
|
123
|
-
checkbox.remove(checkedIcon);
|
|
124
|
-
checkbox.checked = false;
|
|
125
|
-
} else {
|
|
126
|
-
checkbox.use(color(148, 188, 236));
|
|
127
|
-
checkbox.add(checkedIcon);
|
|
128
|
-
checkbox.checked = true;
|
|
129
|
-
}
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
return checkbox;
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Makes switch
|
|
137
|
-
* @param {number} x - Switch x postion (default is 0)
|
|
138
|
-
* @param {number} y - Switch y postion (default is 0)
|
|
139
|
-
* @param {number} width - Switch width (default is 50)
|
|
140
|
-
* @param {number} height - Switch height (default is 25)
|
|
141
|
-
* @returns {GameObj}
|
|
142
|
-
*/
|
|
143
|
-
export const makeSwitch = (x = 0, y = 0, width = 50, height = 25) => {
|
|
144
|
-
// Make switch base
|
|
145
|
-
const switchBase = make([
|
|
146
|
-
rect(width, height),
|
|
147
|
-
pos(x, y),
|
|
148
|
-
opacity(0),
|
|
149
|
-
area(),
|
|
150
|
-
{
|
|
151
|
-
switched: false,
|
|
152
|
-
},
|
|
153
|
-
]);
|
|
154
|
-
|
|
155
|
-
// Make switch line
|
|
156
|
-
const switchLine = make([
|
|
157
|
-
rect(width, height / 2, { radius: height / 4 }),
|
|
158
|
-
color(169, 169, 169),
|
|
159
|
-
pos(0, height / 2 - height / 4),
|
|
160
|
-
area(),
|
|
161
|
-
]);
|
|
162
|
-
|
|
163
|
-
// Make switch circle
|
|
164
|
-
const switchCircle = make([circle(height / 2), pos(height / 2, height / 2)]);
|
|
165
|
-
|
|
166
|
-
// On click
|
|
167
|
-
switchBase.onClick(() => {
|
|
168
|
-
if (switchBase.switched) {
|
|
169
|
-
switchLine.use(color(169, 169, 169));
|
|
170
|
-
switchCircle.use(color(255, 255, 255));
|
|
171
|
-
switchCircle.use(pos(height / 2, height / 2));
|
|
172
|
-
switchBase.switched = false;
|
|
173
|
-
} else {
|
|
174
|
-
switchLine.use(color(148, 188, 236));
|
|
175
|
-
switchCircle.use(color(24, 118, 210));
|
|
176
|
-
switchCircle.use(pos(width - switchCircle.radius, height / 2));
|
|
177
|
-
switchBase.switched = true;
|
|
178
|
-
}
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
// Add line and circle
|
|
182
|
-
switchBase.add(switchLine);
|
|
183
|
-
switchBase.add(switchCircle);
|
|
184
|
-
|
|
185
|
-
return switchBase;
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Makes text input
|
|
190
|
-
* @param {number} x - Text input x postion (default is 0)
|
|
191
|
-
* @param {number} y - Text input y postion (default is 0)
|
|
192
|
-
* @param {number} width - Text input width (default is 400)
|
|
193
|
-
* @param {number} txtSize - Text input text size (default is 15)
|
|
194
|
-
* @param {number} pad - Text input padding (default is 10)
|
|
195
|
-
* @param {boolean} hasFocus - Text input focus (default is true)
|
|
196
|
-
* @returns {GameObj}
|
|
197
|
-
*/
|
|
198
|
-
export const makeTextInput = (
|
|
199
|
-
x = 0,
|
|
200
|
-
y = 0,
|
|
201
|
-
width = 400,
|
|
202
|
-
txtSize = 15,
|
|
203
|
-
pad = 10,
|
|
204
|
-
hasFocus = true
|
|
205
|
-
) => {
|
|
206
|
-
// Text input container
|
|
207
|
-
const inputBase = make([
|
|
208
|
-
rect(width, txtSize + pad * 2),
|
|
209
|
-
pos(x, y),
|
|
210
|
-
area(),
|
|
211
|
-
outline(1),
|
|
212
|
-
]);
|
|
213
|
-
|
|
214
|
-
// Text input
|
|
215
|
-
const input = make([
|
|
216
|
-
text("", { width: width - pad * 2, size: txtSize }),
|
|
217
|
-
textInput(hasFocus),
|
|
218
|
-
color(BLACK),
|
|
219
|
-
pos(pad, pad),
|
|
220
|
-
area(),
|
|
221
|
-
]);
|
|
222
|
-
|
|
223
|
-
inputBase.add(input);
|
|
224
|
-
|
|
225
|
-
return inputBase;
|
|
226
|
-
};
|
|
File without changes
|