kaplay-ui 1.0.0-alpha.13 → 1.0.0-alpha.14

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 CHANGED
@@ -17,6 +17,19 @@ For now it helps you build Game Objects like text buttons and labels — without
17
17
 
18
18
  ---
19
19
 
20
+ ## ✨ _What this plugin currently provides_
21
+
22
+ When installed, this plugin extends the KAPLAY context (`k`) with:
23
+
24
+ - `k.addTextButton()` – interactive buttons with centered text
25
+ - `k.addLabel()` – lightweight, non-interactive text labels
26
+
27
+ Each helper returns a regular KAPLAY game object enhanced with
28
+ **convenience methods** for changing size, position, colors, text, and more
29
+ after creation.
30
+
31
+ ---
32
+
20
33
  ## 📦 Installation
21
34
 
22
35
  ### Prerelease (new v1 work)
@@ -29,11 +42,9 @@ This gives you the latest `1.0.0‑alpha.*` builds.
29
42
 
30
43
  ---
31
44
 
32
- ## 🚀 Usage (_1.0.0-alpha.\*_)
45
+ ## 🚀 Quick start (_1.0.0-alpha.\*_)
33
46
 
34
- **Kaplay UI** exports a plugin for adding UI Game Objects.
35
-
36
- The `kaplayUI` plugin is exported from the package root:
47
+ The **Kaplay-UI** plugin is exported as `kaplayUI` from the package root:
37
48
 
38
49
  ```ts
39
50
  import kaplay from "kaplay";
@@ -47,130 +58,144 @@ const k = kaplay({
47
58
  You now have access to the UI helpers via your `k` instance:
48
59
 
49
60
  ```ts
50
- const btn = k.addTextButton("Play");
51
- const label = k.addLabel("Score: 0");
61
+ const btn = k.addTextButton("Start");
62
+ btn.onClick(() => {
63
+ k.go("game");
64
+ });
65
+
66
+ const lbl = k.addLabel("Score: 0");
67
+ lbl.setLabelText("Score: 1");
52
68
  ```
53
69
 
54
70
  ---
55
71
 
56
72
  ## 🧩 Game Objects (_**1.0.0‑alpha.\***_)
57
73
 
58
- ### 🔤 **Text Button** (`addTextButton()`)
59
-
60
- Creates a button-like GameObj with centered text and some convenient defaults.
74
+ ### 🔤 Text Button `addTextButton()`
61
75
 
62
- #### _**Signature**_
76
+ Creates a clickable button with centered text and default visuals.
63
77
 
64
78
  ```ts
65
- addTextButton(
66
- txt: string,
67
- opts?: object
68
- ): GameObj
79
+ const btn = k.addTextButton("Start", {
80
+ width: 200,
81
+ height: 80,
82
+ });
69
83
  ```
70
84
 
71
- #### _**Default `opts` parameter values**_
85
+ Buttons support runtime updates such as:
72
86
 
73
- | Parameter | Default |
74
- | --------- | :------ |
75
- | `width` | `200` |
76
- | `height` | `100` |
77
- | `radius` | `10` |
78
- | `posX` | `0` |
79
- | `posY` | `0` |
80
- | `outline` | `3` |
81
- | `txtSize` | `22` |
87
+ ```ts
88
+ btn.setSize(300, 100);
89
+ btn.setButtonColor([255, 120, 120]);
90
+ btn.setButtonText("Go!");
91
+ btn.setButtonTextSize(24);
92
+ ```
82
93
 
83
- #### _**Default styling**_
94
+ Perfect for menus, dialogs, and in‑game actions.
95
+
96
+ ---
84
97
 
85
- | Comps | Values |
86
- | ---------------------- | :-------------- |
87
- | `button anchor` | `"topleft"` |
88
- | `button color` | `200, 200, 200` |
89
- | `button outline color` | `92, 91, 91` |
90
- | `text anchor` | `"center"` |
91
- | `text color` | `0, 0, 0` |
98
+ ### 🏷️ Label — `addLabel()`
92
99
 
93
- #### _**Examples**_
100
+ Creates a lightweight background + text container for HUD elements or titles.
94
101
 
95
102
  ```ts
96
- // Basic button
97
- const btn1 = k.addTextButton("Play!");
103
+ const label = k.addLabel("Score: 0");
104
+ ```
98
105
 
99
- // Custom button
100
- const btn2 = k.addTextButton("Play!", { posX: 300, posY: 200 });
106
+ Update it dynamically:
101
107
 
102
- // Add interactivity
103
- btn2.onClick(() => {
104
- console.log("Button clicked!");
105
- });
108
+ ```ts
109
+ label.setLabelText("Score: 10");
110
+ label.setLabelTextColor([255, 255, 0]);
111
+ label.setOpacity(0.9);
106
112
  ```
107
113
 
114
+ Ideal for HUDs, counters, status text, and overlays.
115
+
108
116
  ---
109
117
 
110
- ### 🏷️ **Label** (`addLabel()`)
118
+ ## 🎨 Customization
111
119
 
112
- A lightweight text-based UI element ideal for HUD counters, tooltips, status text, or titles.
120
+ kaplay-ui is designed to be **customized imperatively**, without hiding
121
+ or abstracting away KAPLAY’s core APIs.
113
122
 
114
- #### _**Signature**_
123
+ ### ✅ Option-based customization (at creation)
124
+
125
+ Both helpers accept an optional `opts` object for common configuration:
115
126
 
116
127
  ```ts
117
- addLabel(
118
- txt: string,
119
- opts?: object
120
- )
128
+ const btn = k.addTextButton("Options", {
129
+ width: 250,
130
+ height: 70,
131
+ posX: 100,
132
+ posY: 200,
133
+ radius: 8,
134
+ });
121
135
  ```
122
136
 
123
- #### _**Default `opts` values**_
137
+ ---
124
138
 
125
- | Parameter | Default |
126
- | --------- | ------- |
127
- | `width` | `160` |
128
- | `height` | `96` |
129
- | `txtSize` | `22` |
139
+ ### Runtime customization (after creation)
130
140
 
131
- #### _**Default styling**_
141
+ Each UI element exposes helper methods for updating its appearance and layout:
132
142
 
133
- | Comps | Values |
134
- | --------------- | :-------------- |
135
- | `label pos` | `0, 0` |
136
- | `label color` | `0, 0,0 ` |
137
- | `label opacity` | `0.7` |
138
- | `label anchor` | `"topleft"` |
139
- | `text color` | `255, 255, 255` |
140
- | `text anchor` | `"center"` |
143
+ ```ts
144
+ btn.setPosition(150, 220);
145
+ btn.setButtonColor([80, 160, 255]);
146
+ btn.setButtonTextColor([255, 255, 255]);
147
+ ```
141
148
 
142
- #### _**Examples**_
149
+ This makes it easy to animate, react to state changes, or reuse UI elements.
143
150
 
144
- ```ts
145
- // Basic label
146
- const lbl2 = k.addLabel("Score: 0");
151
+ ---
147
152
 
148
- // Custom label
149
- const lbl3 = k.addLabel("Start", { width: 100, height: 50 });
153
+ ### Full KAPLAY access
150
154
 
151
- // Update label text example
152
- let score = 0;
153
- const scoreLabel = k.addLabel(`Score: ${score}`);
155
+ UI elements are **regular KAPLAY game objects**, so you can still use all
156
+ standard KAPLAY features:
154
157
 
155
- k.wait(2, () => {
156
- score++;
157
- scoreLabel.children[0].text = `Score: ${score}`;
158
- });
158
+ ```ts
159
+ btn.use(k.opacity(0.8));
160
+ btn.onHover(() => btn.setScale(1.1));
161
+ label.use(k.rotate(5));
159
162
  ```
160
163
 
161
- #### _**Common use cases**_
164
+ kaplay-ui never locks you into a restricted UI model.
165
+
166
+ ---
167
+
168
+ ## 🧠 Design philosophy
169
+
170
+ kaplay-ui is intentionally simple:
171
+
172
+ - ✅ No retained UI trees
173
+ - ✅ No layout engine
174
+ - ✅ No reactive framework
175
+ - ✅ Just KAPLAY game objects + helpers
176
+
177
+ You stay in control of how and when UI updates happen.
162
178
 
163
- - HUD overlays
164
- - Score counters
165
- - Time and health displays
166
- - UI section headings
167
- - Tooltips and indicators
179
+ ---
180
+
181
+ ## 📚 Documentation
182
+
183
+ - Full API documentation is available via **JSDocs**
184
+ - All helpers are strongly typed and editor-friendly
185
+ - Hover over methods in your editor to explore options
168
186
 
169
187
  ---
170
188
 
171
189
  ## 🛣️ Roadmap
172
190
 
173
- _See evolving roadmap at: https://github.com/jbakchr/kaplay-ui/blob/v1/ROADMAP.md_
191
+ Future components may include:
192
+
193
+ - Toggles
194
+ - Sliders
195
+ - Panels / containers
196
+
197
+ See the roadmap here:
198
+ 👉 <https://github.com/jbakchr/kaplay-ui/blob/v1/ROADMAP.md>
174
199
 
175
200
  ---
176
201
 
package/dist/uiplugin.js CHANGED
@@ -1,62 +1,48 @@
1
- //#region src/components/button/index.ts
1
+ //#region src/helpers/color-utils.ts
2
2
  function e(e, t, n) {
3
- let { width: r, height: i, radius: a, posX: o, posY: s, outline: c } = {
4
- width: 150,
5
- height: 60,
6
- radius: 10,
7
- posX: 0,
8
- posY: 0,
9
- outline: 3,
10
- ...t
11
- };
12
- return e.make([
13
- e.rect(r, i, { radius: a }),
14
- e.pos(o, s),
15
- e.color(200, 200, 200),
16
- e.outline(c, e.rgb(92, 91, 91)),
17
- e.anchor(n),
18
- e.area()
19
- ]);
3
+ if (Array.isArray(n)) {
4
+ t.use(e.color([
5
+ n[0],
6
+ n[1],
7
+ n[2]
8
+ ]));
9
+ return;
10
+ }
11
+ if (typeof n == "string") {
12
+ t.use(e.color(n));
13
+ return;
14
+ }
15
+ if (typeof n == "object" && "r" in n) {
16
+ t.use(e.color(e.rgb(n.r, n.g, n.b)));
17
+ return;
18
+ }
19
+ t.use(e.color(n));
20
20
  }
21
- //#endregion
22
- //#region src/components/label/index.ts
23
- function t(e, t) {
24
- let { width: n, height: r } = {
25
- width: 160,
26
- height: 96,
27
- ...t
28
- };
29
- return e.make([
30
- e.rect(n, r),
31
- e.pos(0, 0),
32
- e.color(0, 0, 0),
33
- e.opacity(.7),
34
- e.anchor("topleft")
35
- ]);
21
+ function t(e, t, n, r) {
22
+ if (typeof r == "string") {
23
+ t.use(e.outline(n, e.rgb(r)));
24
+ return;
25
+ }
26
+ if (Array.isArray(r)) {
27
+ t.use(e.outline(n, e.rgb(r[0], r[1], r[2])));
28
+ return;
29
+ }
36
30
  }
37
31
  //#endregion
38
- //#region src/components/text/index.ts
39
- function n(e, t, n, r, i, a) {
40
- let { txtSize: o } = {
41
- txtSize: 22,
42
- ...n
43
- };
44
- return e.make([
45
- e.text(t, { size: o }),
46
- e.color(0, 0, 0),
47
- e.pos(r, i),
48
- e.anchor(a)
49
- ]);
32
+ //#region src/helpers/layout-utils.ts
33
+ var n = new Set(["anchor", "rect"]);
34
+ function r(e) {
35
+ return n.has(e);
50
36
  }
51
37
  //#endregion
52
38
  //#region src/helpers/pos-utils.ts
53
- function r(e) {
39
+ function i(e) {
54
40
  return {
55
41
  cX: e.width / 2,
56
42
  cY: e.height / 2
57
43
  };
58
44
  }
59
- function i(e, t, n, r, i) {
45
+ function a(e, t, n, r, i) {
60
46
  switch (t.anchor) {
61
47
  case "bot":
62
48
  n.use(e.pos(0 * r, -1 * i));
@@ -89,43 +75,189 @@ function i(e, t, n, r, i) {
89
75
  }
90
76
  }
91
77
  //#endregion
92
- //#region src/helpers/layout-utils.ts
93
- var a = new Set([
94
- "anchor",
95
- "pos",
96
- "area"
97
- ]);
98
- function o(e) {
99
- return a.has(e);
78
+ //#region src/components/label.ts
79
+ function o(t, n) {
80
+ let { width: r, height: o, radius: s, posX: c, posY: l, opacity: u, lblColor: d } = {
81
+ width: 160,
82
+ height: 96,
83
+ radius: 0,
84
+ posX: 0,
85
+ posY: 0,
86
+ opacity: .7,
87
+ lblColor: [
88
+ 0,
89
+ 0,
90
+ 0
91
+ ],
92
+ ...n
93
+ }, f = t.make([
94
+ t.rect(r, o, { radius: s }),
95
+ t.pos(c, l),
96
+ t.opacity(u),
97
+ t.anchor("topleft"),
98
+ {
99
+ setSize(e, n) {
100
+ f.width = e, f.height = n;
101
+ let { cX: r, cY: o } = i(f);
102
+ a(t, f, f.children[0], r, o);
103
+ },
104
+ setRadius(e) {
105
+ f.radius = e;
106
+ },
107
+ setPosition(e, t) {
108
+ f.pos.x = e, f.pos.y = t;
109
+ },
110
+ setOpacity(e) {
111
+ f.opacity = e;
112
+ },
113
+ setLabelColor(n) {
114
+ e(t, f, n);
115
+ },
116
+ setLabelText(e) {
117
+ let t = f.children[0];
118
+ t.text = e;
119
+ },
120
+ setLabelTextColor(n) {
121
+ let r = f.children[0];
122
+ e(t, r, n);
123
+ },
124
+ setLabelTextSize(e) {
125
+ let t = f.children[0];
126
+ t.textSize = e;
127
+ },
128
+ setLabelAnchor(e) {
129
+ f.anchor = e;
130
+ let { cX: n, cY: r } = i(f);
131
+ a(t, f, f.children[0], n, r);
132
+ }
133
+ }
134
+ ]);
135
+ return e(t, f, d), f;
100
136
  }
101
137
  //#endregion
102
- //#region src/elements/text-button/index.ts
103
- function s(t, a, s) {
104
- let c = e(t, s, "topleft"), { cX: l, cY: u } = r(c), d = n(t, a, s, l, u, "center");
105
- return c.add(d), t.add(c), c.onUse((e) => {
106
- if (o(e)) {
107
- let { cX: e, cY: n } = r(c);
108
- i(t, c, d, e, n);
138
+ //#region src/components/button.ts
139
+ function s(n, r, o) {
140
+ let { width: s, height: c, posX: l, posY: u, radius: d, outline: f, btnColor: p, outlineColor: m } = {
141
+ width: 150,
142
+ height: 60,
143
+ posX: 0,
144
+ posY: 0,
145
+ radius: 10,
146
+ outline: 3,
147
+ btnColor: [
148
+ 200,
149
+ 200,
150
+ 200
151
+ ],
152
+ outlineColor: "#5c5b5b",
153
+ ...r
154
+ }, h = n.make([
155
+ n.rect(s, c, { radius: d }),
156
+ n.pos(l, u),
157
+ n.anchor(o),
158
+ n.area(),
159
+ {
160
+ setSize(e, t) {
161
+ h.width = e, h.height = t;
162
+ let { cX: r, cY: o } = i(h);
163
+ a(n, h, h.children[0], r, o);
164
+ },
165
+ setPosition(e, t) {
166
+ h.pos.x = e, h.pos.y = t;
167
+ },
168
+ setRadius(e) {
169
+ h.radius = e;
170
+ },
171
+ setOutline(e) {
172
+ t(n, h, e, m);
173
+ },
174
+ setButtonText(e) {
175
+ let t = h.children[0];
176
+ t.text = e;
177
+ },
178
+ setButtonTextSize(e) {
179
+ let t = h.children[0];
180
+ t.textSize = e;
181
+ },
182
+ setButtonColor(t) {
183
+ e(n, h, t);
184
+ },
185
+ setButtonOutlineColor(e) {
186
+ t(n, h, f, e);
187
+ },
188
+ setAnchor(e) {
189
+ h.anchor = e;
190
+ let { cX: t, cY: r } = i(h);
191
+ a(n, h, h.children[0], t, r);
192
+ },
193
+ setButtonTextColor(t) {
194
+ let r = h.children[0];
195
+ e(n, r, t);
196
+ }
197
+ }
198
+ ]);
199
+ return e(n, h, p), t(n, h, f, m), h;
200
+ }
201
+ //#endregion
202
+ //#region src/components/text.ts
203
+ function c(t, n, r, i, a, o) {
204
+ let { txtSize: s, txtColor: c } = {
205
+ txtSize: 22,
206
+ ...r
207
+ }, l = t.make([
208
+ t.text(n, { size: s }),
209
+ t.pos(i, a),
210
+ t.anchor(o)
211
+ ]);
212
+ return e(t, l, c), l;
213
+ }
214
+ //#endregion
215
+ //#region src/elements/label.ts
216
+ function l(e, t, n) {
217
+ let s = {
218
+ txtColor: [
219
+ 255,
220
+ 255,
221
+ 255
222
+ ],
223
+ ...n
224
+ }, l = o(e, s), { cX: u, cY: d } = i(l), f = c(e, t, s, u, d, "center");
225
+ return l.add(f), e.add(l), l.onUse((t) => {
226
+ if (r(t)) {
227
+ let { cX: t, cY: n } = i(l);
228
+ a(e, l, f, t, n);
109
229
  }
110
- }), c.onHover(() => {
111
- t.setCursor("pointer");
112
- }), c.onHoverEnd(() => {
113
- t.setCursor("default");
114
- }), c;
230
+ }), l;
115
231
  }
116
232
  //#endregion
117
- //#region src/elements/label/index.ts
118
- function c(e, i, a) {
119
- let o = t(e, a), { cX: s, cY: c } = r(o), l = n(e, i, a, s, c, "center");
120
- return l.use(e.color(255, 255, 255)), o.add(l), e.add(o), o;
233
+ //#region src/elements/text-button.ts
234
+ function u(e, t, n) {
235
+ let o = {
236
+ txtColor: [
237
+ 0,
238
+ 0,
239
+ 0
240
+ ],
241
+ ...n
242
+ }, l = s(e, o, "topleft"), { cX: u, cY: d } = i(l), f = c(e, t, o, u, d, "center");
243
+ return l.add(f), e.add(l), l.onUse((t) => {
244
+ if (r(t)) {
245
+ let { cX: t, cY: n } = i(l);
246
+ a(e, l, f, t, n);
247
+ }
248
+ }), l.onHover(() => {
249
+ e.setCursor("pointer");
250
+ }), l.onHoverEnd(() => {
251
+ e.setCursor("default");
252
+ }), l;
121
253
  }
122
254
  //#endregion
123
255
  //#region src/index.ts
124
- function l(e) {
256
+ function d(e) {
125
257
  return {
126
- addTextButton: (t, n = {}) => s(e, t, n),
127
- addLabel: (t, n = {}) => c(e, t, n)
258
+ addTextButton: (t, n = {}) => u(e, t, n),
259
+ addLabel: (t, n = {}) => l(e, t, n)
128
260
  };
129
261
  }
130
262
  //#endregion
131
- export { l as default };
263
+ export { d as default };
@@ -1 +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){let{width:r,height:i,radius:a,posX:o,posY:s,outline:c}={width:150,height:60,radius:10,posX:0,posY:0,outline:3,...t};return e.make([e.rect(r,i,{radius:a}),e.pos(o,s),e.color(200,200,200),e.outline(c,e.rgb(92,91,91)),e.anchor(n),e.area()])}function t(e,t){let{width:n,height:r}={width:160,height:96,...t};return e.make([e.rect(n,r),e.pos(0,0),e.color(0,0,0),e.opacity(.7),e.anchor(`topleft`)])}function n(e,t,n,r,i,a){let{txtSize:o}={txtSize:22,...n};return e.make([e.text(t,{size:o}),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){let c=e(t,s,`topleft`),{cX:l,cY:u}=r(c),d=n(t,a,s,l,u,`center`);return c.add(d),t.add(c),c.onUse(e=>{if(o(e)){let{cX:e,cY:n}=r(c);i(t,c,d,e,n)}}),c.onHover(()=>{t.setCursor(`pointer`)}),c.onHoverEnd(()=>{t.setCursor(`default`)}),c}function c(e,i,a){let o=t(e,a),{cX:s,cY:c}=r(o),l=n(e,i,a,s,c,`center`);return l.use(e.color(255,255,255)),o.add(l),e.add(o),o}function l(e){return{addTextButton:(t,n={})=>s(e,t,n),addLabel:(t,n={})=>c(e,t,n)}}return l});
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){if(Array.isArray(n)){t.use(e.color([n[0],n[1],n[2]]));return}if(typeof n==`string`){t.use(e.color(n));return}if(typeof n==`object`&&`r`in n){t.use(e.color(e.rgb(n.r,n.g,n.b)));return}t.use(e.color(n))}function t(e,t,n,r){if(typeof r==`string`){t.use(e.outline(n,e.rgb(r)));return}if(Array.isArray(r)){t.use(e.outline(n,e.rgb(r[0],r[1],r[2])));return}}var n=new Set([`anchor`,`rect`]);function r(e){return n.has(e)}function i(e){return{cX:e.width/2,cY:e.height/2}}function a(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}}function o(t,n){let{width:r,height:o,radius:s,posX:c,posY:l,opacity:u,lblColor:d}={width:160,height:96,radius:0,posX:0,posY:0,opacity:.7,lblColor:[0,0,0],...n},f=t.make([t.rect(r,o,{radius:s}),t.pos(c,l),t.opacity(u),t.anchor(`topleft`),{setSize(e,n){f.width=e,f.height=n;let{cX:r,cY:o}=i(f);a(t,f,f.children[0],r,o)},setRadius(e){f.radius=e},setPosition(e,t){f.pos.x=e,f.pos.y=t},setOpacity(e){f.opacity=e},setLabelColor(n){e(t,f,n)},setLabelText(e){let t=f.children[0];t.text=e},setLabelTextColor(n){let r=f.children[0];e(t,r,n)},setLabelTextSize(e){let t=f.children[0];t.textSize=e},setLabelAnchor(e){f.anchor=e;let{cX:n,cY:r}=i(f);a(t,f,f.children[0],n,r)}}]);return e(t,f,d),f}function s(n,r,o){let{width:s,height:c,posX:l,posY:u,radius:d,outline:f,btnColor:p,outlineColor:m}={width:150,height:60,posX:0,posY:0,radius:10,outline:3,btnColor:[200,200,200],outlineColor:`#5c5b5b`,...r},h=n.make([n.rect(s,c,{radius:d}),n.pos(l,u),n.anchor(o),n.area(),{setSize(e,t){h.width=e,h.height=t;let{cX:r,cY:o}=i(h);a(n,h,h.children[0],r,o)},setPosition(e,t){h.pos.x=e,h.pos.y=t},setRadius(e){h.radius=e},setOutline(e){t(n,h,e,m)},setButtonText(e){let t=h.children[0];t.text=e},setButtonTextSize(e){let t=h.children[0];t.textSize=e},setButtonColor(t){e(n,h,t)},setButtonOutlineColor(e){t(n,h,f,e)},setAnchor(e){h.anchor=e;let{cX:t,cY:r}=i(h);a(n,h,h.children[0],t,r)},setButtonTextColor(t){let r=h.children[0];e(n,r,t)}}]);return e(n,h,p),t(n,h,f,m),h}function c(t,n,r,i,a,o){let{txtSize:s,txtColor:c}={txtSize:22,...r},l=t.make([t.text(n,{size:s}),t.pos(i,a),t.anchor(o)]);return e(t,l,c),l}function l(e,t,n){let s={txtColor:[255,255,255],...n},l=o(e,s),{cX:u,cY:d}=i(l),f=c(e,t,s,u,d,`center`);return l.add(f),e.add(l),l.onUse(t=>{if(r(t)){let{cX:t,cY:n}=i(l);a(e,l,f,t,n)}}),l}function u(e,t,n){let o={txtColor:[0,0,0],...n},l=s(e,o,`topleft`),{cX:u,cY:d}=i(l),f=c(e,t,o,u,d,`center`);return l.add(f),e.add(l),l.onUse(t=>{if(r(t)){let{cX:t,cY:n}=i(l);a(e,l,f,t,n)}}),l.onHover(()=>{e.setCursor(`pointer`)}),l.onHoverEnd(()=>{e.setCursor(`default`)}),l}function d(e){return{addTextButton:(t,n={})=>u(e,t,n),addLabel:(t,n={})=>l(e,t,n)}}return d});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kaplay-ui",
3
- "version": "1.0.0-alpha.13",
3
+ "version": "1.0.0-alpha.14",
4
4
  "description": "UI components for KAPLAY",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
package/src/index.ts CHANGED
@@ -2,9 +2,9 @@ import type { KAPLAYCtx } from "kaplay";
2
2
 
3
3
  // Types
4
4
  import {
5
+ ButtonComponent,
5
6
  LabelComponent,
6
7
  LabelOptions,
7
- TextButtonElement,
8
8
  TextButtonOptions,
9
9
  } from "./types";
10
10
 
@@ -12,31 +12,73 @@ import {
12
12
  import { createLabel, createTextButton } from "./elements";
13
13
 
14
14
  /**
15
- * # KAPLAY UI Plugin
15
+ * # KAPLAY-UI Plugin
16
16
  *
17
- * A small UI helper plugin that adds convenience functions for creating
18
- * common UI components such as buttons and labels within a KAPLAY game.
17
+ * A lightweight UI helper plugin that extends the KAPLAY context (`k`)
18
+ * with convenience methods for creating common, reusable UI components
19
+ * such as text buttons and labels.
19
20
  *
20
- * ## Available UI Creation Helpers
21
+ * This plugin focuses on *simple, code-driven UI composition* by providing
22
+ * sensible defaults while still allowing full runtime control over layout,
23
+ * styling, and text.
21
24
  *
22
- * @returns {object} An object exposing UI creation helpers on `k`:
23
- * - `addTextButton(txt, opts?) → TextButtonElement`
24
- * - `addLabel(txt, opts?) → LabelComponent`
25
+ * ---
25
26
  *
26
- * ## Examples
27
+ * ## What this plugin does
28
+ * When included via `kaplay({ plugins: [...] })`, this plugin augments
29
+ * the existing KAPLAY context with the following helper methods:
30
+ *
31
+ * - `k.addTextButton(txt, opts?) → ButtonComponent`
32
+ * Creates an interactive button with centered text and exposes methods
33
+ * for updating size, position, colors, radius, outline, anchor, and text
34
+ * properties at runtime.
35
+ *
36
+ * - `k.addLabel(txt, opts?) → LabelComponent`
37
+ * Creates a non-interactive text label with a background surface and
38
+ * exposes methods for dynamically updating layout, appearance, and text.
39
+ *
40
+ * These helpers eliminate the need to manually wire up common UI primitives
41
+ * while still returning fully controllable game objects.
42
+ *
43
+ * ---
44
+ *
45
+ * ## Design philosophy
46
+ * - No layout system or retained UI tree
47
+ * - No reactivity framework
48
+ * - No hidden state
49
+ *
50
+ * UI elements are regular KAPLAY game objects with small, focused helper
51
+ * methods attached for convenience.
52
+ *
53
+ * ---
54
+ *
55
+ * ## Usage
27
56
  *
28
57
  * @example
29
58
  * import kaplay from "kaplay";
30
- * import kaplayUI from "kaplay-ui"
59
+ * import kaplayUI from "kaplay-ui";
31
60
  *
32
61
  * const k = kaplay({
33
- * plugins: [kaplayUI]
34
- * })
62
+ * plugins: [kaplayUI],
63
+ * });
35
64
  *
36
- * const button = k.addTextButton("Start");
37
- * const label = k.addLabel("Hello!");
65
+ * const btn = k.addTextButton("Start");
66
+ * btn.onClick(() => console.log("Game starting…"));
67
+ *
68
+ * const label = k.addLabel("Score: 0");
69
+ * label.setPosition(20, 20);
70
+ *
71
+ * ---
72
+ *
73
+ * @returns {{
74
+ * addTextButton: Function,
75
+ * addLabel: Function
76
+ * }}
77
+ * Kaplay merges the returned object into the `k` context, making the
78
+ * UI helpers available as `k.addTextButton()` and `k.addLabel()`.
79
+ *
80
+ * The plugin itself does not create or return UI elements directly.
38
81
  */
39
-
40
82
  export default function kaplayUI(k: KAPLAYCtx): {
41
83
  /**
42
84
  * # Text Button
@@ -45,93 +87,217 @@ export default function kaplayUI(k: KAPLAYCtx): {
45
87
  * This helper provides sensible defaults for size, layout, and styling<br>
46
88
  * so you can quickly add text-based buttons to your KAPLAY game or UI.
47
89
  *
90
+ * ---
91
+ *
48
92
  * ## Parameters
49
93
  *
50
- * ### Required Paramters
94
+ * ### Required Parameters
51
95
  * - `txt` {string}
96
+ * Initial text label displayed at the center of the button.
52
97
  *
53
- * ### Default Parameter Values for 'opts'
98
+ * ### Default Parameter Values for `opts`
54
99
  * - `opts.width`: `150`
55
100
  * - `opts.height`: `60`
56
- * - `opts.radius`: `10`
57
101
  * - `opts.posX`: `0`
58
102
  * - `opts.posY`: `0`
103
+ * - `opts.radius`: `10`
59
104
  * - `opts.outline`: `3`
105
+ * - `opts.btnColor`: `[200, 200, 200]`
106
+ * - `opts.outlineColor`: `"#5c5b5b"`
107
+ * - `opts.txtSize`: `20`
108
+ * - `opts.txtColor`: `[0, 0, 0]`
109
+ * ---
60
110
  *
61
111
  * ## Default Styling
62
112
  * The button object is created with:
63
113
  * - `k.anchor("topleft")`
64
- * - `k.color(200, 200, 200)`
65
- * - `k.outline(opts.outline, k.rgb(92, 91, 91))`
66
114
  * - `k.area()`
67
115
  *
68
- * @param {string} [txt]
116
+ * The button text is created with:
117
+ * - `k.anchor("center")`
118
+ *
119
+ * ---
120
+ *
121
+ * ## Button Instance Methods
122
+ * The returned `ButtonComponent` exposes the following mutator methods,
123
+ * allowing the button to be updated after creation:
124
+ *
125
+ * ### Layout & Geometry
126
+ * - `setSize(w: number, h: number): void`
127
+ * Update the button width and height.
128
+ *
129
+ * - `setPosition(x: number, y: number): void`
130
+ * Move the button to a new position.
131
+ *
132
+ * - `setAnchor(a: Anchor): void`
133
+ * Change the anchor used for positioning.
134
+ *
135
+ * - `setRadius(r: number): void`
136
+ * Update the corner radius.
137
+ *
138
+ * - `setOutline(t: number): void`
139
+ * Set the outline thickness.
140
+ *
141
+ * ### Text
142
+ * - `setButtonText(txt: string): void`
143
+ * Replace the button label.
144
+ *
145
+ * - `setButtonTextSize(size: number): void`
146
+ * Change the font size of the button text.
147
+ *
148
+ * - `setButtonTextColor(color: KaplayColor): void`
149
+ * Update the text color.
150
+ *
151
+ * ### Colors
152
+ * - `setButtonColor(color: KaplayColor): void`
153
+ * Change the button fill color.
154
+ *
155
+ * - `setButtonOutlineColor(color: KaplayRGB): void`
156
+ * Change the outline stroke color.
157
+ *
158
+ * ---
159
+ *
160
+ * @param {string} txt
69
161
  * Required text label to be displayed at the center of the button.
70
162
  *
71
163
  * @param {TextButtonOptions} [opts={}]
72
164
  * Optional configuration object used to customize the button.
73
165
  *
74
- * @returns {TextButtonElement}
75
- * A KAPLAY Game Object representing the button with centered text.
166
+ * @returns {ButtonComponent}
167
+ * A clickable button instance with runtime mutation helpers attached.
168
+ *
169
+ * ---
76
170
  *
77
171
  * @example
78
- * // Basic usage (uses default width/height)
172
+ * // Basic usage
79
173
  * const playBtn = addTextButton("Play");
80
174
  * playBtn.onClick(() => console.log("Play clicked!"));
81
175
  *
82
176
  * @example
83
- * // Custom sizing
84
- * const bigBtn = addTextButton("Start", { width: 300, height: 120 });
177
+ * // Runtime mutation
178
+ * const btn = addTextButton("Start");
179
+ * btn.setSize(300, 120);
180
+ * btn.setButtonColor([255, 100, 100]);
181
+ * btn.setButtonText("Go!");
85
182
  *
86
183
  * @example
87
- * // Override only one dimension
88
- * const tallBtn = addTextButton("Options", { radius: 2 });
184
+ * // Visual-only updates
185
+ * const optionsBtn = addTextButton("Options", { radius: 2 });
186
+ * optionsBtn.setButtonOutlineColor([0, 0, 0]);
187
+ * optionsBtn.setButtonTextSize(24);
89
188
  */
90
- addTextButton(txt: string, opts?: TextButtonOptions): TextButtonElement;
189
+ addTextButton(txt: string, opts?: TextButtonOptions): ButtonComponent;
190
+
91
191
  /**
92
192
  * # Label
93
- *
94
193
  * Creates a simple text container with a background and layout box.
95
194
  *
96
195
  * Labels are lightweight UI elements used to display non-interactive text
97
196
  * such as titles, descriptions, or dynamic info (scores, status, etc.).
98
197
  *
198
+ * ---
199
+ *
99
200
  * ## Parameters
100
201
  *
101
- * ### Required Paramters
202
+ * ### Required Parameters
102
203
  * - `txt` {string}
204
+ * Initial label text content.
103
205
  *
104
- * ### Default Parameter Values for 'opts'
206
+ * ### Default Parameter Values for `opts`
105
207
  * - `opts.width`: `160`
106
208
  * - `opts.height`: `96`
209
+ * - `opts.posX`: `0`
210
+ * - `opts.posY`: `0`
211
+ * - `opts.opacity`: `0.7`
107
212
  * - `opts.txtSize`: `22`
108
213
  *
109
- * ## Default Styling
214
+ * ---
110
215
  *
111
- * A labels base (its 'background' surface) includes:
112
- * - `k.pos(0, 0)`
216
+ * ## Default Styling
217
+ * The label background ("base") includes:
113
218
  * - `k.color(0, 0, 0)`
114
- * - `k.opacity(0.7)`
115
219
  * - `k.anchor("topleft")`
116
220
  *
117
- * A label centered text includes:
118
- * - `k.anchor("center"))
119
- * - `k.color(255, 255, 255))
221
+ * The centered label text includes:
222
+ * - `k.anchor("center")`
223
+ * - `k.color(255, 255, 255)`
224
+ *
225
+ * ---
226
+ *
227
+ * ## Label Instance Methods
228
+ * The returned `LabelComponent` exposes helper methods that allow
229
+ * the label to be updated dynamically at runtime.
230
+ *
231
+ * ### Layout & Geometry
232
+ * - `setSize(w: number, h: number): void`
233
+ * Update the label width and height.
234
+ *
235
+ * - `setPosition(x: number, y: number): void`
236
+ * Move the label to a new position.
237
+ *
238
+ * - `setRadius(r: number): void`
239
+ * Update the background corner radius.
240
+ *
241
+ * - `setLabelAnchor(anchor: Anchor): void`
242
+ * Change the anchor used for positioning the label.
243
+ *
244
+ * ### Appearance
245
+ * - `setOpacity(o: number): void`
246
+ * Set label background opacity (0–1).
247
+ *
248
+ * - `setLabelColor(c: KaplayColor): void`
249
+ * Change the background color of the label.
250
+ *
251
+ * ### Text
252
+ * - `setLabelText(txt: string): void`
253
+ * Replace the label text.
254
+ *
255
+ * - `setLabelTextSize(size: number): void`
256
+ * Change the font size of the label text.
257
+ *
258
+ * - `setLabelTextColor(c: KaplayColor): void`
259
+ * Update the label text color.
260
+ *
261
+ * ---
262
+ *
263
+ * @param {string} txt
264
+ * Required text content for the label.
120
265
  *
121
266
  * @param {LabelOptions} [opts={}]
122
- * Optional configuration object used to customize the button.
267
+ * Optional configuration object used to customize the label.
123
268
  *
124
- * @returns {LabelElement}
125
- * A KAPLAY game object representing a text label.
269
+ * @returns {LabelComponent}
270
+ * A non-interactive label element with runtime mutation helpers attached.
271
+ *
272
+ * ---
273
+ *
274
+ * @example
275
+ * // Basic usage
276
+ * const label = addLabel("Score: 0");
277
+ * label.setPosition(20, 20);
126
278
  *
127
279
  * @example
128
- * let score = 0
280
+ * // Dynamic text updates (e.g. score counter)
281
+ * let score = 0;
129
282
  * const scoreLabel = addLabel(`Score: ${score}`);
130
283
  *
131
284
  * k.wait(2, () => {
132
285
  * score++;
133
- * scoreLabel.children[0].text = `Score: ${score}`;
134
- * })
286
+ * scoreLabel.setLabelText(`Score: ${score}`);
287
+ * });
288
+ *
289
+ * @example
290
+ * // Runtime appearance updates
291
+ * const title = addLabel("Game Over", { txtSize: 36 });
292
+ * title.setLabelColor([40, 40, 40]);
293
+ * title.setLabelTextColor([255, 80, 80]);
294
+ * title.setOpacity(0.9);
295
+ *
296
+ * @example
297
+ * // Layout adjustment after creation
298
+ * const hudLabel = addLabel("Paused");
299
+ * hudLabel.setSize(200, 60);
300
+ * hudLabel.setLabelAnchor("center");
135
301
  */
136
302
  addLabel(txt: string, opts?: LabelOptions): LabelComponent;
137
303
  } {
@@ -139,8 +305,8 @@ export default function kaplayUI(k: KAPLAYCtx): {
139
305
  addTextButton: (
140
306
  txt: string,
141
307
  opts: TextButtonOptions = {},
142
- ): TextButtonElement => createTextButton(k, txt, opts),
143
- addLabel: (txt, opts: LabelOptions = {}): LabelComponent =>
308
+ ): ButtonComponent => createTextButton(k, txt, opts),
309
+ addLabel: (txt: string, opts: LabelOptions = {}): LabelComponent =>
144
310
  createLabel(k, txt, opts),
145
311
  };
146
312
  }