kaplay-plugin-ui 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/plugin.cjs +34 -6
- package/dist/plugin.cjs.map +1 -1
- package/dist/plugin.d.cts +4 -0
- package/dist/plugin.d.cts.map +1 -1
- package/dist/plugin.d.mts +4 -0
- package/dist/plugin.d.mts.map +1 -1
- package/dist/plugin.mjs +34 -6
- package/dist/plugin.mjs.map +1 -1
- package/dist/plugin.umd.js +34 -6
- package/dist/plugin.umd.js.map +1 -1
- package/dist/plugin.umd.min.js +3 -1
- package/dist/plugin.umd.min.js.map +1 -1
- package/package.json +1 -1
package/dist/plugin.cjs
CHANGED
|
@@ -148,12 +148,13 @@ function createModalManager(k) {
|
|
|
148
148
|
function pauseSceneTree(gameObj) {
|
|
149
149
|
if (isModal(gameObj) || isInsideModal(gameObj)) return;
|
|
150
150
|
pausedScene ??= /* @__PURE__ */ new Map();
|
|
151
|
-
// v8 ignore
|
|
151
|
+
// v8 ignore if
|
|
152
152
|
if (!pausedScene.has(gameObj)) pausedScene.set(gameObj, gameObj.paused);
|
|
153
153
|
setPaused(gameObj);
|
|
154
154
|
gameObj.children.forEach(pauseSceneTree);
|
|
155
155
|
}
|
|
156
156
|
function openModal(modal) {
|
|
157
|
+
k.setCursor("default");
|
|
157
158
|
if (modals.length > 0) setPaused(getLast(modals), true, true);
|
|
158
159
|
modals.push(modal);
|
|
159
160
|
if (modals.length === 1) {
|
|
@@ -175,7 +176,7 @@ function createModalManager(k) {
|
|
|
175
176
|
if (!modals.length) {
|
|
176
177
|
cancelPauseOnAdd?.();
|
|
177
178
|
cancelPauseOnAdd = void 0;
|
|
178
|
-
// v8 ignore
|
|
179
|
+
// v8 ignore if
|
|
179
180
|
if (pausedScene) {
|
|
180
181
|
for (const [gameObj, paused] of pausedScene) if (gameObj.exists()) setPaused(gameObj, paused);
|
|
181
182
|
pausedScene = void 0;
|
|
@@ -250,6 +251,7 @@ function createButton(k, theme, text, opt) {
|
|
|
250
251
|
const radius = opt.radius ?? theme.shape.borderRadius;
|
|
251
252
|
const { minWidth, paddingX, paddingY } = theme.components.button;
|
|
252
253
|
const baseColor = toColor(k, opt.color ?? opt.fill ?? theme.palette.primary);
|
|
254
|
+
let disabled = opt.disabled ?? false;
|
|
253
255
|
const hoverColor = toColor(k, opt.hoverColor ?? theme.palette.hover);
|
|
254
256
|
const pressedColor = toColor(k, opt.pressedColor ?? theme.palette.pressed);
|
|
255
257
|
const textColor = toColor(k, opt.textColor ?? theme.palette.text);
|
|
@@ -267,7 +269,7 @@ function createButton(k, theme, text, opt) {
|
|
|
267
269
|
k.pos(pos),
|
|
268
270
|
k.area(),
|
|
269
271
|
k.scale(),
|
|
270
|
-
k.color(baseColor),
|
|
272
|
+
k.color(disabled ? toColor(k, theme.palette.disabled) : baseColor),
|
|
271
273
|
k.anchor("center")
|
|
272
274
|
];
|
|
273
275
|
const button = opt.parent ? opt.parent.add(components) : k.add(components);
|
|
@@ -332,6 +334,7 @@ function createButton(k, theme, text, opt) {
|
|
|
332
334
|
layout();
|
|
333
335
|
if (opt.width === void 0 || opt.height === void 0 || opt.icon !== void 0 || opt.background !== void 0) button.onUpdate(layout);
|
|
334
336
|
button.onClick(() => {
|
|
337
|
+
if (disabled) return;
|
|
335
338
|
k.wait(0, () => {
|
|
336
339
|
button.color = toColor(k, pressedColor);
|
|
337
340
|
if (icon && opt.pressedIcon) {
|
|
@@ -343,12 +346,18 @@ function createButton(k, theme, text, opt) {
|
|
|
343
346
|
background.sprite = opt.pressedBackground;
|
|
344
347
|
}
|
|
345
348
|
opt.onClick?.();
|
|
349
|
+
// v8 ignore if
|
|
350
|
+
if (disabled) k.setCursor("not-allowed");
|
|
351
|
+
else if (!button.isHovering()) k.setCursor("default");
|
|
346
352
|
});
|
|
347
353
|
});
|
|
348
354
|
button.onHover(() => {
|
|
355
|
+
if (disabled) {
|
|
356
|
+
k.setCursor("not-allowed");
|
|
357
|
+
return;
|
|
358
|
+
} else k.setCursor("pointer");
|
|
349
359
|
button.color = toColor(k, hoverColor);
|
|
350
360
|
button.scale = k.vec2(1.05, 1.05);
|
|
351
|
-
k.setCursor("pointer");
|
|
352
361
|
if (icon && opt.hoverIcon) {
|
|
353
362
|
iconState = "hover";
|
|
354
363
|
icon.sprite = opt.hoverIcon;
|
|
@@ -359,9 +368,10 @@ function createButton(k, theme, text, opt) {
|
|
|
359
368
|
}
|
|
360
369
|
});
|
|
361
370
|
button.onHoverEnd(() => {
|
|
371
|
+
k.setCursor("default");
|
|
372
|
+
if (disabled) return;
|
|
362
373
|
button.color = baseColor;
|
|
363
374
|
button.scale = k.vec2(1, 1);
|
|
364
|
-
k.setCursor("default");
|
|
365
375
|
if (icon && opt.icon) {
|
|
366
376
|
iconState = "base";
|
|
367
377
|
icon.sprite = opt.icon;
|
|
@@ -372,11 +382,29 @@ function createButton(k, theme, text, opt) {
|
|
|
372
382
|
}
|
|
373
383
|
if (icon || background) layout();
|
|
374
384
|
});
|
|
375
|
-
|
|
385
|
+
button.onDestroy(() => {
|
|
386
|
+
if (button.isHovering()) k.setCursor("default");
|
|
387
|
+
});
|
|
388
|
+
function applyDisabledState() {
|
|
389
|
+
button.color = disabled ? toColor(k, theme.palette.disabled) : baseColor;
|
|
390
|
+
}
|
|
391
|
+
applyDisabledState();
|
|
392
|
+
const assignedButton = Object.assign(button, {
|
|
376
393
|
background,
|
|
377
394
|
icon,
|
|
378
395
|
label
|
|
379
396
|
});
|
|
397
|
+
return Object.defineProperty(assignedButton, "disabled", {
|
|
398
|
+
get() {
|
|
399
|
+
return disabled;
|
|
400
|
+
},
|
|
401
|
+
set(value) {
|
|
402
|
+
disabled = value;
|
|
403
|
+
applyDisabledState();
|
|
404
|
+
},
|
|
405
|
+
enumerable: true,
|
|
406
|
+
configurable: true
|
|
407
|
+
});
|
|
380
408
|
}
|
|
381
409
|
// src/theme.ts
|
|
382
410
|
const PRIMARY = "#4f8cff";
|
package/dist/plugin.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.cjs","names":[],"sources":["../src/modal.ts","../src/utils.ts","../src/button.ts","../src/theme.ts","../src/plugin.ts"],"sourcesContent":["import type { GameObj, KAPLAYCtx } from 'kaplay';\n\nimport { createButton } from './button';\nimport type { ModalOpt, Theme } from './types';\nimport { getLast, isModal, setPaused, toColor, uniqueTag } from './utils';\n\n/** Tag applied to modal game objects. */\nexport const MODAL_TAG = uniqueTag('modal');\n\ntype SizedGameObj = GameObj & {\n height: number;\n width: number;\n};\n\nexport interface ModalHooks {\n isTopmost: (modal: GameObj) => boolean;\n onClose: (modal: GameObj) => void;\n onOpen: (modal: GameObj) => void;\n}\n\nfunction isSized(obj: GameObj): obj is SizedGameObj {\n const sized = obj as Partial<SizedGameObj>;\n return typeof sized.width === 'number' && typeof sized.height === 'number';\n}\n\nexport function createModal(\n k: KAPLAYCtx,\n theme: Theme,\n opt: ModalOpt,\n hooks: ModalHooks,\n) {\n const {\n backdropOpacity,\n borderRadius,\n gap,\n maxWidth,\n minWidth,\n paddingX,\n paddingY,\n } = theme.components.modal;\n const fontSize = theme.typography.fontSize;\n const titleSize = Math.round(fontSize * 1.25);\n const font = theme.typography.fontFamily;\n const dismissible = opt.dismissible ?? true;\n\n const modal = k.add([\n k.pos(k.center()),\n k.fixed(),\n k.z(opt.z ?? 1000),\n MODAL_TAG,\n ]);\n\n modal.onDestroy(() => {\n hooks.onClose(modal);\n });\n\n hooks.onOpen(modal);\n\n const backdrop = modal.add([\n k.rect(k.width(), k.height()),\n k.pos(),\n k.color(0, 0, 0),\n k.opacity(backdropOpacity),\n k.anchor('center'),\n k.area(),\n ]);\n\n const content = modal.add([\n k.rect(0, 0, borderRadius > 0 ? { radius: borderRadius } : undefined),\n k.pos(),\n k.color(toColor(k, theme.palette.surface)),\n k.anchor('center'),\n k.area(),\n ]);\n\n const actions = content.add([k.pos()]);\n\n for (const action of opt.actions ?? []) {\n createButton(k, theme, action.text, {\n parent: actions,\n onClick: () => {\n if (action.close !== false) {\n modal.destroy();\n }\n\n action.onClick?.();\n },\n });\n }\n\n const titleMeasure = opt.title\n ? k.text(opt.title, { size: titleSize, font })\n : undefined;\n const bodyMeasure = opt.text\n ? k.text(opt.text, { size: fontSize, font })\n : undefined;\n const textWrapWidth = Math.max(0, (opt.width ?? maxWidth) - paddingX * 2);\n\n const title = opt.title\n ? content.add([\n k.text(opt.title, {\n size: titleSize,\n font,\n width: textWrapWidth > 0 ? textWrapWidth : undefined,\n align: 'center',\n }),\n k.pos(),\n k.color(toColor(k, theme.palette.text)),\n k.anchor('center'),\n ])\n : undefined;\n\n const body = opt.text\n ? content.add([\n k.text(opt.text, {\n size: fontSize,\n font,\n width: textWrapWidth > 0 ? textWrapWidth : undefined,\n align: 'center',\n }),\n k.pos(),\n k.color(toColor(k, theme.palette.textSecondary)),\n k.anchor('center'),\n ])\n : undefined;\n\n /**\n * Update modal layout:\n *\n * - fits the backdrop to the screen,\n * - sizes the content to its title, text, and actions,\n * - repositions the title, text, and actions.\n */\n function layout() {\n const screenWidth = k.width();\n const screenHeight = k.height();\n\n if (backdrop.width !== screenWidth) {\n backdrop.width = screenWidth;\n }\n\n if (backdrop.height !== screenHeight) {\n backdrop.height = screenHeight;\n }\n\n modal.pos = k.center();\n\n const buttons = actions.children.filter(isSized);\n const actionsWidth =\n buttons.reduce((width, button) => width + button.width, 0) +\n gap * Math.max(0, buttons.length - 1);\n const actionsHeight = Math.max(\n 0,\n ...buttons.map((button) => button.height),\n );\n\n const textContentWidth = Math.max(\n titleMeasure?.width ?? 0,\n bodyMeasure?.width ?? 0,\n );\n const nextWidth =\n opt.width ??\n Math.max(\n Math.min(textContentWidth, maxWidth - paddingX * 2) + paddingX * 2,\n actionsWidth + paddingX * 2,\n minWidth,\n );\n\n const titleHeight = title?.height ?? 0;\n const bodyHeight = body?.height ?? 0;\n const hasTitle = title !== undefined;\n const hasBody = body !== undefined;\n const hasActions = buttons.length > 0;\n const blockCount = Number(hasTitle) + Number(hasBody) + Number(hasActions);\n const contentHeight =\n titleHeight +\n bodyHeight +\n actionsHeight +\n gap * Math.max(0, blockCount - 1);\n const nextHeight = opt.height ?? contentHeight + paddingY * 2;\n\n if (content.width !== nextWidth) {\n content.width = nextWidth;\n }\n\n if (content.height !== nextHeight) {\n content.height = nextHeight;\n }\n\n let y = -nextHeight / 2 + (nextHeight - contentHeight) / 2;\n\n if (title) {\n title.pos = k.vec2(0, y + title.height / 2);\n y += title.height + gap;\n }\n\n if (body) {\n body.pos = k.vec2(0, y + body.height / 2);\n y += body.height + gap;\n }\n\n if (hasActions) {\n let x = -actionsWidth / 2;\n\n for (const button of buttons) {\n button.pos = k.vec2(x + button.width / 2, y + actionsHeight / 2);\n x += button.width + gap;\n }\n }\n }\n\n layout();\n modal.onUpdate(layout);\n\n if (dismissible) {\n // prevent click-through so modal isn't dismissed right after creation\n k.wait(0, () => {\n backdrop.onClick(() => {\n if (!hooks.isTopmost(modal)) {\n return;\n }\n\n if (content.hasPoint(k.mousePos())) {\n return;\n }\n\n modal.destroy();\n });\n });\n\n modal.onKeyPress('escape', () => {\n if (hooks.isTopmost(modal)) {\n modal.destroy();\n }\n });\n }\n\n return Object.assign(modal, {\n actions,\n content,\n });\n}\n\nexport interface ModalManager {\n openModal: ModalHooks['onOpen'];\n closeModal: ModalHooks['onClose'];\n isTopmost: ModalHooks['isTopmost'];\n isInsideModal: (gameObj: GameObj) => boolean;\n}\n\nexport function createModalManager(k: KAPLAYCtx): ModalManager {\n const modals: GameObj[] = [];\n let pausedScene: Map<GameObj, boolean> | undefined;\n let cancelPauseOnAdd: (() => void) | undefined;\n\n const isInsideModal = (gameObj: GameObj) =>\n modals.some((modal) => modal === gameObj || modal.isAncestorOf(gameObj));\n\n function pauseSceneTree(gameObj: GameObj) {\n if (isModal(gameObj) || isInsideModal(gameObj)) {\n return;\n }\n\n pausedScene ??= new Map();\n\n // v8 ignore next\n if (!pausedScene.has(gameObj)) {\n pausedScene.set(gameObj, gameObj.paused);\n }\n\n setPaused(gameObj);\n gameObj.children.forEach(pauseSceneTree);\n }\n\n function openModal(modal: GameObj) {\n if (modals.length > 0) {\n setPaused(getLast(modals), true, true);\n }\n\n modals.push(modal);\n\n if (modals.length === 1) {\n pausedScene = new Map();\n k.getTreeRoot().children.forEach(pauseSceneTree);\n\n const controller = k.onAdd((gameObj) => {\n if (!isModal(gameObj) && !isInsideModal(gameObj)) {\n pauseSceneTree(gameObj);\n }\n });\n\n cancelPauseOnAdd = () => {\n controller.cancel();\n };\n }\n }\n\n function closeModal(modal: GameObj) {\n const index = modals.indexOf(modal);\n\n if (index === -1) {\n return;\n }\n\n const wasTopmost = index === modals.length - 1;\n\n modals.splice(index, 1);\n\n if (!modals.length) {\n cancelPauseOnAdd?.();\n cancelPauseOnAdd = undefined;\n\n // v8 ignore next\n if (pausedScene) {\n for (const [gameObj, paused] of pausedScene) {\n if (gameObj.exists()) {\n setPaused(gameObj, paused);\n }\n }\n\n pausedScene = undefined;\n }\n } else if (wasTopmost) {\n setPaused(getLast(modals), false, true);\n }\n }\n\n const isTopmost = (modal: GameObj) => getLast(modals) === modal;\n\n return { openModal, closeModal, isTopmost, isInsideModal };\n}\n","import type { Color, GameObj, KAPLAYCtx } from 'kaplay';\n\nimport { MODAL_TAG } from './modal';\n\n/**\n * Get the last item in an array.\n *\n * @param items - The items.\n * @returns The last item.\n */\nexport function getLast<T>(items: T[]): T {\n return items[items.length - 1];\n}\n\n/**\n * Check if the game object is a modal.\n *\n * @param gameObj - The game object.\n * @returns Whether game object is a modal.\n */\nexport function isModal(gameObj: GameObj): boolean {\n return gameObj.is(MODAL_TAG);\n}\n\n/**\n * Pause or unpause game object(s).\n *\n * @param gameObj - The root game object.\n * @param isPaused - Whether it should be paused.\n * @param includeTree - Whether to pause all game objects in tree.\n */\nexport function setPaused(\n gameObj: GameObj,\n isPaused = true,\n includeTree = false,\n): void {\n gameObj.paused = isPaused;\n\n if (includeTree) {\n gameObj.children.forEach((childGameObj) => {\n setPaused(childGameObj, isPaused, includeTree);\n });\n }\n}\n\n/**\n * Convert a hex string or color to color.\n *\n * @param k - The Kaplay context.\n * @param color - The hex string or color.\n * @returns The color.\n */\nexport function toColor(k: KAPLAYCtx, color: string | Color): Color {\n return typeof color === 'string' ? k.rgb(color) : color;\n}\n\n/**\n * Generate a unique tag.\n *\n * @param id - The identifier.\n * @returns The tag.\n */\nexport function uniqueTag(id: string): string {\n return [\n 'kaplay-plugin-ui',\n id,\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n crypto?.randomUUID?.(),\n ].join(':');\n}\n","import type { Color, KAPLAYCtx, Vec2 } from 'kaplay';\n\nimport type { ButtonOpt, Theme } from './types';\nimport { toColor } from './utils';\n\nexport function createButton(\n k: KAPLAYCtx,\n theme: Theme,\n text: string,\n opt: ButtonOpt & {\n pos?: Vec2;\n onClick?: () => void;\n color?: string | Color;\n hoverColor?: string | Color;\n pressedColor?: string | Color;\n textColor?: string | Color;\n },\n) {\n const radius = opt.radius ?? theme.shape.borderRadius;\n const { minWidth, paddingX, paddingY } = theme.components.button;\n const baseColor = toColor(k, opt.color ?? opt.fill ?? theme.palette.primary);\n const hoverColor = toColor(k, opt.hoverColor ?? theme.palette.hover);\n const pressedColor = toColor(k, opt.pressedColor ?? theme.palette.pressed);\n const textColor = toColor(k, 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 const textComp = k.text(text, { size, font });\n const isIconOnly = opt.icon !== undefined && !text.length;\n const hasBackground = opt.background !== undefined;\n const showFill = !hasBackground && !isIconOnly;\n const rectOpt = showFill\n ? radius > 0\n ? { radius }\n : undefined\n : { fill: false };\n\n const components = [\n k.rect(0, 0, rectOpt),\n k.pos(pos),\n k.area(),\n k.scale(),\n k.color(baseColor),\n k.anchor('center'),\n ];\n const button = opt.parent ? opt.parent.add(components) : k.add(components);\n\n const background = opt.background\n ? button.add([k.sprite(opt.background), k.pos(), k.anchor('center')])\n : undefined;\n\n const icon = opt.icon\n ? button.add([k.sprite(opt.icon), k.pos(), k.anchor('center')])\n : undefined;\n\n const label = button.add([\n textComp,\n k.color(textColor as never),\n k.pos(),\n k.anchor('center'),\n ]);\n\n let iconWidth = 0;\n let iconHeight = 0;\n let iconState: 'base' | 'hover' | 'pressed' = 'base';\n let backgroundWidth = 0;\n let backgroundHeight = 0;\n let backgroundMeasured = false;\n let backgroundState: 'base' | 'hover' | 'pressed' = 'base';\n\n /**\n * Update button layout:\n *\n * - caches the base icon/background dimensions,\n * - calculates content width/height,\n * - updates `button.width` / `button.height`,\n * - stretches the background and repositions the icon and label.\n */\n function layout() {\n if (icon && iconState === 'base') {\n iconWidth = icon.width;\n iconHeight = icon.height;\n }\n\n if (\n background &&\n backgroundState === 'base' &&\n !backgroundMeasured &&\n (background.width > 0 || background.height > 0)\n ) {\n backgroundWidth = background.width;\n backgroundHeight = background.height;\n backgroundMeasured = true;\n }\n\n const gap = icon && iconWidth > 0 && label.width > 0 ? theme.spacing : 0;\n const contentWidth = iconWidth + gap + label.width;\n const contentHeight = Math.max(iconHeight, label.height);\n const contentBoxWidth = contentWidth + paddingX * 2;\n const contentBoxHeight = contentHeight + paddingY * 2;\n\n const nextWidth =\n opt.width ??\n (background\n ? Math.max(contentBoxWidth, backgroundWidth)\n : Math.max(contentBoxWidth, minWidth));\n const nextHeight =\n opt.height ??\n (background\n ? Math.max(contentBoxHeight, backgroundHeight)\n : contentBoxHeight);\n\n if (button.width !== nextWidth) {\n button.width = nextWidth;\n }\n\n if (button.height !== nextHeight) {\n button.height = nextHeight;\n }\n\n if (background && backgroundMeasured) {\n background.width = nextWidth;\n background.height = nextHeight;\n background.pos = k.vec2();\n }\n\n if (icon) {\n icon.pos = k.vec2(-contentWidth / 2 + iconWidth / 2, 0);\n }\n\n label.pos = k.vec2(\n -contentWidth / 2 + iconWidth + gap + label.width / 2,\n 0,\n );\n }\n\n layout();\n\n if (\n opt.width === undefined ||\n opt.height === undefined ||\n opt.icon !== undefined ||\n opt.background !== undefined\n ) {\n button.onUpdate(layout);\n }\n\n button.onClick(() => {\n // prevent stray click from click-through\n k.wait(0, () => {\n button.color = toColor(k, pressedColor);\n\n if (icon && opt.pressedIcon) {\n iconState = 'pressed';\n icon.sprite = opt.pressedIcon as string;\n }\n\n if (background && opt.pressedBackground) {\n backgroundState = 'pressed';\n background.sprite = opt.pressedBackground as string;\n }\n\n opt.onClick?.();\n });\n });\n\n button.onHover(() => {\n button.color = toColor(k, hoverColor);\n button.scale = k.vec2(1.05, 1.05);\n k.setCursor('pointer');\n\n if (icon && opt.hoverIcon) {\n iconState = 'hover';\n icon.sprite = opt.hoverIcon as string;\n }\n\n if (background && opt.hoverBackground) {\n backgroundState = 'hover';\n background.sprite = opt.hoverBackground as string;\n }\n });\n\n button.onHoverEnd(() => {\n button.color = baseColor;\n button.scale = k.vec2(1, 1);\n k.setCursor('default');\n\n if (icon && opt.icon) {\n iconState = 'base';\n icon.sprite = opt.icon as string;\n }\n\n if (background && opt.background) {\n backgroundState = 'base';\n background.sprite = opt.background as string;\n }\n\n if (icon || background) {\n layout();\n }\n });\n\n return Object.assign(button, {\n background,\n icon,\n label,\n });\n}\n","import type { DeepPartial, Theme } from './types';\n\nconst PRIMARY = '#4f8cff';\nconst TEXT = '#ffffff';\n\n/** Default theme. */\nconst theme: Theme = {\n palette: {\n primary: PRIMARY,\n secondary: PRIMARY,\n background: '#111827',\n surface: '#1f2937',\n text: TEXT,\n textSecondary: TEXT,\n hover: PRIMARY,\n pressed: 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 minWidth: 120,\n paddingX: 12,\n paddingY: 8,\n textTransform: 'none',\n },\n modal: {\n backdropOpacity: 0.5,\n borderRadius: 8,\n gap: 16,\n maxWidth: 480,\n minWidth: 280,\n paddingX: 24,\n paddingY: 20,\n },\n tooltip: {\n background: '#111827',\n textColor: 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: { ...theme.palette, ...input?.palette },\n typography: { ...theme.typography, ...input?.typography },\n shape: { ...theme.shape, ...input?.shape },\n spacing: input?.spacing ?? theme.spacing,\n components: {\n button: {\n ...theme.components.button,\n ...input?.components?.button,\n },\n modal: {\n ...theme.components.modal,\n ...input?.components?.modal,\n },\n tooltip: {\n ...theme.components.tooltip,\n ...input?.components?.tooltip,\n },\n },\n };\n}\n","import type { KAPLAYCtx } from 'kaplay';\n\nimport { createButton } from './button';\nimport { createModal, createModalManager } from './modal';\nimport { resolveTheme } from './theme';\nimport type {\n AddButton,\n AddModal,\n DeepPartial,\n GetTheme,\n SetTheme,\n Theme,\n} from './types';\n\n/**\n * Kaplay UI plugin.\n *\n * @param k - The Kaplay context.\n * @param theme - The theme config.\n * @returns The plugin methods.\n */\nexport function uiPlugin(k: KAPLAYCtx, theme: DeepPartial<Theme> = {}) {\n let userTheme: DeepPartial<Theme> = theme;\n let currentTheme: Theme = resolveTheme(userTheme);\n\n const getTheme: GetTheme = () => currentTheme;\n\n const setTheme: SetTheme = (next) => {\n userTheme = { ...userTheme, ...next };\n currentTheme = resolveTheme(userTheme);\n };\n\n const addButton: AddButton = (text = '', opt = {}) => {\n return createButton(k, currentTheme, text, opt);\n };\n\n const modalManager = createModalManager(k);\n\n const addModal: AddModal = (opt = {}) => {\n return createModal(k, currentTheme, opt, {\n isTopmost: modalManager.isTopmost,\n onClose: modalManager.closeModal,\n onOpen: modalManager.openModal,\n });\n };\n\n return {\n addButton,\n addModal,\n setTheme,\n getTheme,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"plugin.cjs","names":[],"sources":["../src/modal.ts","../src/utils.ts","../src/button.ts","../src/theme.ts","../src/plugin.ts"],"sourcesContent":["import type { GameObj, KAPLAYCtx } from 'kaplay';\n\nimport { createButton } from './button';\nimport type { ModalOpt, Theme } from './types';\nimport { getLast, isModal, setPaused, toColor, uniqueTag } from './utils';\n\n/** Tag applied to modal game objects. */\nexport const MODAL_TAG = uniqueTag('modal');\n\ntype SizedGameObj = GameObj & {\n height: number;\n width: number;\n};\n\nexport interface ModalHooks {\n isTopmost: (modal: GameObj) => boolean;\n onClose: (modal: GameObj) => void;\n onOpen: (modal: GameObj) => void;\n}\n\nfunction isSized(obj: GameObj): obj is SizedGameObj {\n const sized = obj as Partial<SizedGameObj>;\n return typeof sized.width === 'number' && typeof sized.height === 'number';\n}\n\nexport function createModal(\n k: KAPLAYCtx,\n theme: Theme,\n opt: ModalOpt,\n hooks: ModalHooks,\n) {\n const {\n backdropOpacity,\n borderRadius,\n gap,\n maxWidth,\n minWidth,\n paddingX,\n paddingY,\n } = theme.components.modal;\n const fontSize = theme.typography.fontSize;\n const titleSize = Math.round(fontSize * 1.25);\n const font = theme.typography.fontFamily;\n const dismissible = opt.dismissible ?? true;\n\n const modal = k.add([\n k.pos(k.center()),\n k.fixed(),\n k.z(opt.z ?? 1000),\n MODAL_TAG,\n ]);\n\n modal.onDestroy(() => {\n hooks.onClose(modal);\n });\n\n hooks.onOpen(modal);\n\n const backdrop = modal.add([\n k.rect(k.width(), k.height()),\n k.pos(),\n k.color(0, 0, 0),\n k.opacity(backdropOpacity),\n k.anchor('center'),\n k.area(),\n ]);\n\n const content = modal.add([\n k.rect(0, 0, borderRadius > 0 ? { radius: borderRadius } : undefined),\n k.pos(),\n k.color(toColor(k, theme.palette.surface)),\n k.anchor('center'),\n k.area(),\n ]);\n\n const actions = content.add([k.pos()]);\n\n for (const action of opt.actions ?? []) {\n createButton(k, theme, action.text, {\n parent: actions,\n onClick: () => {\n if (action.close !== false) {\n modal.destroy();\n }\n\n action.onClick?.();\n },\n });\n }\n\n const titleMeasure = opt.title\n ? k.text(opt.title, { size: titleSize, font })\n : undefined;\n const bodyMeasure = opt.text\n ? k.text(opt.text, { size: fontSize, font })\n : undefined;\n const textWrapWidth = Math.max(0, (opt.width ?? maxWidth) - paddingX * 2);\n\n const title = opt.title\n ? content.add([\n k.text(opt.title, {\n size: titleSize,\n font,\n width: textWrapWidth > 0 ? textWrapWidth : undefined,\n align: 'center',\n }),\n k.pos(),\n k.color(toColor(k, theme.palette.text)),\n k.anchor('center'),\n ])\n : undefined;\n\n const body = opt.text\n ? content.add([\n k.text(opt.text, {\n size: fontSize,\n font,\n width: textWrapWidth > 0 ? textWrapWidth : undefined,\n align: 'center',\n }),\n k.pos(),\n k.color(toColor(k, theme.palette.textSecondary)),\n k.anchor('center'),\n ])\n : undefined;\n\n /**\n * Update modal layout:\n *\n * - fits the backdrop to the screen,\n * - sizes the content to its title, text, and actions,\n * - repositions the title, text, and actions.\n */\n function layout() {\n const screenWidth = k.width();\n const screenHeight = k.height();\n\n if (backdrop.width !== screenWidth) {\n backdrop.width = screenWidth;\n }\n\n if (backdrop.height !== screenHeight) {\n backdrop.height = screenHeight;\n }\n\n modal.pos = k.center();\n\n const buttons = actions.children.filter(isSized);\n const actionsWidth =\n buttons.reduce((width, button) => width + button.width, 0) +\n gap * Math.max(0, buttons.length - 1);\n const actionsHeight = Math.max(\n 0,\n ...buttons.map((button) => button.height),\n );\n\n const textContentWidth = Math.max(\n titleMeasure?.width ?? 0,\n bodyMeasure?.width ?? 0,\n );\n const nextWidth =\n opt.width ??\n Math.max(\n Math.min(textContentWidth, maxWidth - paddingX * 2) + paddingX * 2,\n actionsWidth + paddingX * 2,\n minWidth,\n );\n\n const titleHeight = title?.height ?? 0;\n const bodyHeight = body?.height ?? 0;\n const hasTitle = title !== undefined;\n const hasBody = body !== undefined;\n const hasActions = buttons.length > 0;\n const blockCount = Number(hasTitle) + Number(hasBody) + Number(hasActions);\n const contentHeight =\n titleHeight +\n bodyHeight +\n actionsHeight +\n gap * Math.max(0, blockCount - 1);\n const nextHeight = opt.height ?? contentHeight + paddingY * 2;\n\n if (content.width !== nextWidth) {\n content.width = nextWidth;\n }\n\n if (content.height !== nextHeight) {\n content.height = nextHeight;\n }\n\n let y = -nextHeight / 2 + (nextHeight - contentHeight) / 2;\n\n if (title) {\n title.pos = k.vec2(0, y + title.height / 2);\n y += title.height + gap;\n }\n\n if (body) {\n body.pos = k.vec2(0, y + body.height / 2);\n y += body.height + gap;\n }\n\n if (hasActions) {\n let x = -actionsWidth / 2;\n\n for (const button of buttons) {\n button.pos = k.vec2(x + button.width / 2, y + actionsHeight / 2);\n x += button.width + gap;\n }\n }\n }\n\n layout();\n modal.onUpdate(layout);\n\n if (dismissible) {\n // prevent click-through so modal isn't dismissed right after creation\n k.wait(0, () => {\n backdrop.onClick(() => {\n if (!hooks.isTopmost(modal)) {\n return;\n }\n\n if (content.hasPoint(k.mousePos())) {\n return;\n }\n\n modal.destroy();\n });\n });\n\n modal.onKeyPress('escape', () => {\n if (hooks.isTopmost(modal)) {\n modal.destroy();\n }\n });\n }\n\n return Object.assign(modal, {\n actions,\n content,\n });\n}\n\nexport interface ModalManager {\n openModal: ModalHooks['onOpen'];\n closeModal: ModalHooks['onClose'];\n isTopmost: ModalHooks['isTopmost'];\n isInsideModal: (gameObj: GameObj) => boolean;\n}\n\nexport function createModalManager(k: KAPLAYCtx): ModalManager {\n const modals: GameObj[] = [];\n let pausedScene: Map<GameObj, boolean> | undefined;\n let cancelPauseOnAdd: (() => void) | undefined;\n\n const isInsideModal = (gameObj: GameObj) =>\n modals.some((modal) => modal === gameObj || modal.isAncestorOf(gameObj));\n\n function pauseSceneTree(gameObj: GameObj) {\n if (isModal(gameObj) || isInsideModal(gameObj)) {\n return;\n }\n\n pausedScene ??= new Map();\n\n // v8 ignore if\n if (!pausedScene.has(gameObj)) {\n pausedScene.set(gameObj, gameObj.paused);\n }\n\n setPaused(gameObj);\n gameObj.children.forEach(pauseSceneTree);\n }\n\n function openModal(modal: GameObj) {\n k.setCursor('default');\n\n if (modals.length > 0) {\n setPaused(getLast(modals), true, true);\n }\n\n modals.push(modal);\n\n if (modals.length === 1) {\n pausedScene = new Map();\n k.getTreeRoot().children.forEach(pauseSceneTree);\n\n const controller = k.onAdd((gameObj) => {\n if (!isModal(gameObj) && !isInsideModal(gameObj)) {\n pauseSceneTree(gameObj);\n }\n });\n\n cancelPauseOnAdd = () => {\n controller.cancel();\n };\n }\n }\n\n function closeModal(modal: GameObj) {\n const index = modals.indexOf(modal);\n\n if (index === -1) {\n return;\n }\n\n const wasTopmost = index === modals.length - 1;\n\n modals.splice(index, 1);\n\n if (!modals.length) {\n cancelPauseOnAdd?.();\n cancelPauseOnAdd = undefined;\n\n // v8 ignore if\n if (pausedScene) {\n for (const [gameObj, paused] of pausedScene) {\n if (gameObj.exists()) {\n setPaused(gameObj, paused);\n }\n }\n\n pausedScene = undefined;\n }\n } else if (wasTopmost) {\n setPaused(getLast(modals), false, true);\n }\n }\n\n const isTopmost = (modal: GameObj) => getLast(modals) === modal;\n\n return { openModal, closeModal, isTopmost, isInsideModal };\n}\n","import type { Color, GameObj, KAPLAYCtx } from 'kaplay';\n\nimport { MODAL_TAG } from './modal';\n\n/**\n * Get the last item in an array.\n *\n * @param items - The items.\n * @returns The last item.\n */\nexport function getLast<T>(items: T[]): T {\n return items[items.length - 1];\n}\n\n/**\n * Check if the game object is a modal.\n *\n * @param gameObj - The game object.\n * @returns Whether game object is a modal.\n */\nexport function isModal(gameObj: GameObj): boolean {\n return gameObj.is(MODAL_TAG);\n}\n\n/**\n * Pause or unpause game object(s).\n *\n * @param gameObj - The root game object.\n * @param isPaused - Whether it should be paused.\n * @param includeTree - Whether to pause all game objects in tree.\n */\nexport function setPaused(\n gameObj: GameObj,\n isPaused = true,\n includeTree = false,\n): void {\n gameObj.paused = isPaused;\n\n if (includeTree) {\n gameObj.children.forEach((childGameObj) => {\n setPaused(childGameObj, isPaused, includeTree);\n });\n }\n}\n\n/**\n * Convert a hex string or color to color.\n *\n * @param k - The Kaplay context.\n * @param color - The hex string or color.\n * @returns The color.\n */\nexport function toColor(k: KAPLAYCtx, color: string | Color): Color {\n return typeof color === 'string' ? k.rgb(color) : color;\n}\n\n/**\n * Generate a unique tag.\n *\n * @param id - The identifier.\n * @returns The tag.\n */\nexport function uniqueTag(id: string): string {\n return [\n 'kaplay-plugin-ui',\n id,\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n crypto?.randomUUID?.(),\n ].join(':');\n}\n","import type { Color, KAPLAYCtx, Vec2 } from 'kaplay';\n\nimport type { ButtonOpt, Theme } from './types';\nimport { toColor } from './utils';\n\nexport function createButton(\n k: KAPLAYCtx,\n theme: Theme,\n text: string,\n opt: ButtonOpt & {\n pos?: Vec2;\n onClick?: () => void;\n color?: string | Color;\n hoverColor?: string | Color;\n pressedColor?: string | Color;\n textColor?: string | Color;\n },\n) {\n const radius = opt.radius ?? theme.shape.borderRadius;\n const { minWidth, paddingX, paddingY } = theme.components.button;\n const baseColor = toColor(k, opt.color ?? opt.fill ?? theme.palette.primary);\n let disabled = opt.disabled ?? false;\n const hoverColor = toColor(k, opt.hoverColor ?? theme.palette.hover);\n const pressedColor = toColor(k, opt.pressedColor ?? theme.palette.pressed);\n const textColor = toColor(k, 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 const textComp = k.text(text, { size, font });\n const isIconOnly = opt.icon !== undefined && !text.length;\n const hasBackground = opt.background !== undefined;\n const showFill = !hasBackground && !isIconOnly;\n const rectOpt = showFill\n ? radius > 0\n ? { radius }\n : undefined\n : { fill: false };\n\n const components = [\n k.rect(0, 0, rectOpt),\n k.pos(pos),\n k.area(),\n k.scale(),\n k.color(disabled ? toColor(k, theme.palette.disabled) : baseColor),\n k.anchor('center'),\n ];\n const button = opt.parent ? opt.parent.add(components) : k.add(components);\n\n const background = opt.background\n ? button.add([k.sprite(opt.background), k.pos(), k.anchor('center')])\n : undefined;\n\n const icon = opt.icon\n ? button.add([k.sprite(opt.icon), k.pos(), k.anchor('center')])\n : undefined;\n\n const label = button.add([\n textComp,\n k.color(textColor as never),\n k.pos(),\n k.anchor('center'),\n ]);\n\n let iconWidth = 0;\n let iconHeight = 0;\n let iconState: 'base' | 'hover' | 'pressed' = 'base';\n let backgroundWidth = 0;\n let backgroundHeight = 0;\n let backgroundMeasured = false;\n let backgroundState: 'base' | 'hover' | 'pressed' = 'base';\n\n /**\n * Update button layout:\n *\n * - caches the base icon/background dimensions,\n * - calculates content width/height,\n * - updates `button.width` / `button.height`,\n * - stretches the background and repositions the icon and label.\n */\n function layout() {\n if (icon && iconState === 'base') {\n iconWidth = icon.width;\n iconHeight = icon.height;\n }\n\n if (\n background &&\n backgroundState === 'base' &&\n !backgroundMeasured &&\n (background.width > 0 || background.height > 0)\n ) {\n backgroundWidth = background.width;\n backgroundHeight = background.height;\n backgroundMeasured = true;\n }\n\n const gap = icon && iconWidth > 0 && label.width > 0 ? theme.spacing : 0;\n const contentWidth = iconWidth + gap + label.width;\n const contentHeight = Math.max(iconHeight, label.height);\n const contentBoxWidth = contentWidth + paddingX * 2;\n const contentBoxHeight = contentHeight + paddingY * 2;\n\n const nextWidth =\n opt.width ??\n (background\n ? Math.max(contentBoxWidth, backgroundWidth)\n : Math.max(contentBoxWidth, minWidth));\n const nextHeight =\n opt.height ??\n (background\n ? Math.max(contentBoxHeight, backgroundHeight)\n : contentBoxHeight);\n\n if (button.width !== nextWidth) {\n button.width = nextWidth;\n }\n\n if (button.height !== nextHeight) {\n button.height = nextHeight;\n }\n\n if (background && backgroundMeasured) {\n background.width = nextWidth;\n background.height = nextHeight;\n background.pos = k.vec2();\n }\n\n if (icon) {\n icon.pos = k.vec2(-contentWidth / 2 + iconWidth / 2, 0);\n }\n\n label.pos = k.vec2(\n -contentWidth / 2 + iconWidth + gap + label.width / 2,\n 0,\n );\n }\n\n layout();\n\n if (\n opt.width === undefined ||\n opt.height === undefined ||\n opt.icon !== undefined ||\n opt.background !== undefined\n ) {\n button.onUpdate(layout);\n }\n\n button.onClick(() => {\n if (disabled) {\n return;\n }\n\n // prevent click-through\n k.wait(0, () => {\n button.color = toColor(k, pressedColor);\n\n if (icon && opt.pressedIcon) {\n iconState = 'pressed';\n icon.sprite = opt.pressedIcon as string;\n }\n\n if (background && opt.pressedBackground) {\n backgroundState = 'pressed';\n background.sprite = opt.pressedBackground as string;\n }\n\n opt.onClick?.();\n\n // v8 ignore if\n if (disabled) {\n k.setCursor('not-allowed');\n } else if (!button.isHovering()) {\n k.setCursor('default');\n }\n });\n });\n\n button.onHover(() => {\n if (disabled) {\n k.setCursor('not-allowed');\n return;\n } else {\n k.setCursor('pointer');\n }\n\n button.color = toColor(k, hoverColor);\n button.scale = k.vec2(1.05, 1.05);\n\n if (icon && opt.hoverIcon) {\n iconState = 'hover';\n icon.sprite = opt.hoverIcon as string;\n }\n\n if (background && opt.hoverBackground) {\n backgroundState = 'hover';\n background.sprite = opt.hoverBackground as string;\n }\n });\n\n button.onHoverEnd(() => {\n k.setCursor('default');\n\n if (disabled) {\n return;\n }\n\n button.color = baseColor;\n button.scale = k.vec2(1, 1);\n\n if (icon && opt.icon) {\n iconState = 'base';\n icon.sprite = opt.icon as string;\n }\n\n if (background && opt.background) {\n backgroundState = 'base';\n background.sprite = opt.background as string;\n }\n\n if (icon || background) {\n layout();\n }\n });\n\n button.onDestroy(() => {\n if (button.isHovering()) {\n k.setCursor('default');\n }\n });\n\n function applyDisabledState() {\n button.color = disabled ? toColor(k, theme.palette.disabled) : baseColor;\n }\n\n applyDisabledState();\n\n const assignedButton = Object.assign(button, {\n background,\n icon,\n label,\n });\n\n const definedButton = Object.defineProperty(assignedButton, 'disabled', {\n get() {\n return disabled;\n },\n set(value: boolean) {\n disabled = value;\n applyDisabledState();\n },\n enumerable: true,\n configurable: true,\n }) as typeof assignedButton & { disabled: boolean };\n\n return definedButton;\n}\n","import type { DeepPartial, Theme } from './types';\n\nconst PRIMARY = '#4f8cff';\nconst TEXT = '#ffffff';\n\n/** Default theme. */\nconst theme: Theme = {\n palette: {\n primary: PRIMARY,\n secondary: PRIMARY,\n background: '#111827',\n surface: '#1f2937',\n text: TEXT,\n textSecondary: TEXT,\n hover: PRIMARY,\n pressed: 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 minWidth: 120,\n paddingX: 12,\n paddingY: 8,\n textTransform: 'none',\n },\n modal: {\n backdropOpacity: 0.5,\n borderRadius: 8,\n gap: 16,\n maxWidth: 480,\n minWidth: 280,\n paddingX: 24,\n paddingY: 20,\n },\n tooltip: {\n background: '#111827',\n textColor: 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: { ...theme.palette, ...input?.palette },\n typography: { ...theme.typography, ...input?.typography },\n shape: { ...theme.shape, ...input?.shape },\n spacing: input?.spacing ?? theme.spacing,\n components: {\n button: {\n ...theme.components.button,\n ...input?.components?.button,\n },\n modal: {\n ...theme.components.modal,\n ...input?.components?.modal,\n },\n tooltip: {\n ...theme.components.tooltip,\n ...input?.components?.tooltip,\n },\n },\n };\n}\n","import type { KAPLAYCtx } from 'kaplay';\n\nimport { createButton } from './button';\nimport { createModal, createModalManager } from './modal';\nimport { resolveTheme } from './theme';\nimport type {\n AddButton,\n AddModal,\n DeepPartial,\n GetTheme,\n SetTheme,\n Theme,\n} from './types';\n\n/**\n * Kaplay UI plugin.\n *\n * @param k - The Kaplay context.\n * @param theme - The theme config.\n * @returns The plugin methods.\n */\nexport function uiPlugin(k: KAPLAYCtx, theme: DeepPartial<Theme> = {}) {\n let userTheme: DeepPartial<Theme> = theme;\n let currentTheme: Theme = resolveTheme(userTheme);\n\n const getTheme: GetTheme = () => currentTheme;\n\n const setTheme: SetTheme = (next) => {\n userTheme = { ...userTheme, ...next };\n currentTheme = resolveTheme(userTheme);\n };\n\n const addButton: AddButton = (text = '', opt = {}) => {\n return createButton(k, currentTheme, text, opt);\n };\n\n const modalManager = createModalManager(k);\n\n const addModal: AddModal = (opt = {}) => {\n return createModal(k, currentTheme, opt, {\n isTopmost: modalManager.isTopmost,\n onClose: modalManager.closeModal,\n onOpen: modalManager.openModal,\n });\n };\n\n return {\n addButton,\n addModal,\n setTheme,\n getTheme,\n };\n}\n"],"mappings":"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"}
|
package/dist/plugin.d.cts
CHANGED
|
@@ -10,6 +10,8 @@ declare function createButton(k: KAPLAYCtx, theme: Theme, text: string, opt: But
|
|
|
10
10
|
background: import("kaplay").GameObj<import("kaplay").PosComp | import("kaplay").AnchorComp | import("kaplay").SpriteComp> | undefined;
|
|
11
11
|
icon: import("kaplay").GameObj<import("kaplay").PosComp | import("kaplay").AnchorComp | import("kaplay").SpriteComp> | undefined;
|
|
12
12
|
label: import("kaplay").GameObj<import("kaplay").PosComp | import("kaplay").ColorComp | import("kaplay").AnchorComp | import("kaplay").TextComp>;
|
|
13
|
+
} & {
|
|
14
|
+
disabled: boolean;
|
|
13
15
|
};
|
|
14
16
|
interface ModalHooks {
|
|
15
17
|
isTopmost: (modal: GameObj) => boolean;
|
|
@@ -134,6 +136,8 @@ interface ButtonOpt {
|
|
|
134
136
|
hoverBackground?: SpriteSource;
|
|
135
137
|
/** Sprite drawn behind the label when pressed. */
|
|
136
138
|
pressedBackground?: SpriteSource;
|
|
139
|
+
/** Disable the button. Greys it out and ignores click/hover. */
|
|
140
|
+
disabled?: boolean;
|
|
137
141
|
}
|
|
138
142
|
/**
|
|
139
143
|
* Kaplay UI plugin.
|
package/dist/plugin.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.cts","names":[],"sources":["../src/button.ts","../src/modal.ts","../src/types.ts","../src/plugin.ts"],"mappings":";iBAKgB,aACd,GAAG,WACH,OAAO,OACP,cACA,KAAK;EACH,MAAM;EACN;EACA,iBAAiB;EACjB,sBAAsB;EACtB,wBAAwB;EACxB,qBAAqB;qBACtB,8BAAA,4BAAA,2BAAA,4BAAA,6BAAA,8BAAA,4BAAA;;;;;
|
|
1
|
+
{"version":3,"file":"plugin.d.cts","names":[],"sources":["../src/button.ts","../src/modal.ts","../src/types.ts","../src/plugin.ts"],"mappings":";iBAKgB,aACd,GAAG,WACH,OAAO,OACP,cACA,KAAK;EACH,MAAM;EACN;EACA,iBAAiB;EACjB,sBAAsB;EACtB,wBAAwB;EACxB,qBAAqB;qBACtB,8BAAA,4BAAA,2BAAA,4BAAA,6BAAA,8BAAA,4BAAA;;;;;EA8OyC;;UChP3B;EACf,YAAY,OAAO;EACnB,UAAU,OAAO;EACjB,SAAS,OAAO;;iBAQF,YACd,GAAG,WACH,OAAO,OACP,KAAK,UACL,OAAO,8BAAU,8BAAA,4BAAA,2BAAA,6BAAA;;;;KCxBd,wBAAwB,aAAa,MAAM;;YAGpC;IACR,WAAW;IACX,UAAU;IACV,UAAU;IACV,UAAU;;;KAIF,aACV,eACA,MAAM;EACJ,MAAM;EACN;EACA,iBAAiB;EACjB,sBAAsB;EACtB,wBAAwB;EACxB,qBAAqB;MAEpB,kBAAkB;KAEX,YAAY,MAAM,aAAa,kBAAkB;KAEjD,iBAAiB;KAEjB,YAAY,OAAO,YAAY;;KAG/B,YAAY,QACrB,WAAW,KAAK,EAAE,oBAAoB,YAAY,EAAE,MAAM,EAAE;;UAI9C;EACf;IACE,kBAAkB;IAClB,oBAAoB;IACpB,qBAAqB;IACrB,kBAAkB;IAClB,eAAe;IACf,wBAAwB;IACxB,gBAAgB;IAChB,kBAAkB;IAClB,mBAAmB;IACnB,gBAAgB;;EAElB;IACE;IACA;IACA;IACA;;EAEF;IACE;;EAEF;EACA;IACE;MACE;MACA;MACA;MACA;;IAEF;MACE;MACA;MACA;MACA;MACA;MACA;MACA;;IAEF;MACE,qBAAqB;MACrB,oBAAoB;MACpB;;;;;UAMW;EACf;EACA;EACA;EACA;EACA,UAAU;;EAEV;;EAEA;;;UAIe;EACf;;EAEA;;EAEA;;;UAIe;EACf;EACA;;EAEA,SAAS;EACT;EACA;EACA,gBAAgB;EAChB,qBAAqB;EACrB;EACA,qBAAqB;EACrB,uBAAuB;;EAEvB,OAAO;;EAEP,YAAY;;EAEZ,cAAc;;EAEd,aAAa;;EAEb,kBAAkB;;EAElB,oBAAoB;;EAEpB;;;;;;;;;wBClHc,SAAS,GAAG,WAAW,QAAO,YAAY"}
|
package/dist/plugin.d.mts
CHANGED
|
@@ -10,6 +10,8 @@ declare function createButton(k: KAPLAYCtx, theme: Theme, text: string, opt: But
|
|
|
10
10
|
background: import("kaplay").GameObj<import("kaplay").PosComp | import("kaplay").AnchorComp | import("kaplay").SpriteComp> | undefined;
|
|
11
11
|
icon: import("kaplay").GameObj<import("kaplay").PosComp | import("kaplay").AnchorComp | import("kaplay").SpriteComp> | undefined;
|
|
12
12
|
label: import("kaplay").GameObj<import("kaplay").PosComp | import("kaplay").ColorComp | import("kaplay").AnchorComp | import("kaplay").TextComp>;
|
|
13
|
+
} & {
|
|
14
|
+
disabled: boolean;
|
|
13
15
|
};
|
|
14
16
|
interface ModalHooks {
|
|
15
17
|
isTopmost: (modal: GameObj) => boolean;
|
|
@@ -134,6 +136,8 @@ interface ButtonOpt {
|
|
|
134
136
|
hoverBackground?: SpriteSource;
|
|
135
137
|
/** Sprite drawn behind the label when pressed. */
|
|
136
138
|
pressedBackground?: SpriteSource;
|
|
139
|
+
/** Disable the button. Greys it out and ignores click/hover. */
|
|
140
|
+
disabled?: boolean;
|
|
137
141
|
}
|
|
138
142
|
/**
|
|
139
143
|
* Kaplay UI plugin.
|
package/dist/plugin.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.mts","names":[],"sources":["../src/button.ts","../src/modal.ts","../src/types.ts","../src/plugin.ts"],"mappings":";iBAKgB,aACd,GAAG,WACH,OAAO,OACP,cACA,KAAK;EACH,MAAM;EACN;EACA,iBAAiB;EACjB,sBAAsB;EACtB,wBAAwB;EACxB,qBAAqB;qBACtB,8BAAA,4BAAA,2BAAA,4BAAA,6BAAA,8BAAA,4BAAA;;;;;
|
|
1
|
+
{"version":3,"file":"plugin.d.mts","names":[],"sources":["../src/button.ts","../src/modal.ts","../src/types.ts","../src/plugin.ts"],"mappings":";iBAKgB,aACd,GAAG,WACH,OAAO,OACP,cACA,KAAK;EACH,MAAM;EACN;EACA,iBAAiB;EACjB,sBAAsB;EACtB,wBAAwB;EACxB,qBAAqB;qBACtB,8BAAA,4BAAA,2BAAA,4BAAA,6BAAA,8BAAA,4BAAA;;;;;EA8OyC;;UChP3B;EACf,YAAY,OAAO;EACnB,UAAU,OAAO;EACjB,SAAS,OAAO;;iBAQF,YACd,GAAG,WACH,OAAO,OACP,KAAK,UACL,OAAO,8BAAU,8BAAA,4BAAA,2BAAA,6BAAA;;;;KCxBd,wBAAwB,aAAa,MAAM;;YAGpC;IACR,WAAW;IACX,UAAU;IACV,UAAU;IACV,UAAU;;;KAIF,aACV,eACA,MAAM;EACJ,MAAM;EACN;EACA,iBAAiB;EACjB,sBAAsB;EACtB,wBAAwB;EACxB,qBAAqB;MAEpB,kBAAkB;KAEX,YAAY,MAAM,aAAa,kBAAkB;KAEjD,iBAAiB;KAEjB,YAAY,OAAO,YAAY;;KAG/B,YAAY,QACrB,WAAW,KAAK,EAAE,oBAAoB,YAAY,EAAE,MAAM,EAAE;;UAI9C;EACf;IACE,kBAAkB;IAClB,oBAAoB;IACpB,qBAAqB;IACrB,kBAAkB;IAClB,eAAe;IACf,wBAAwB;IACxB,gBAAgB;IAChB,kBAAkB;IAClB,mBAAmB;IACnB,gBAAgB;;EAElB;IACE;IACA;IACA;IACA;;EAEF;IACE;;EAEF;EACA;IACE;MACE;MACA;MACA;MACA;;IAEF;MACE;MACA;MACA;MACA;MACA;MACA;MACA;;IAEF;MACE,qBAAqB;MACrB,oBAAoB;MACpB;;;;;UAMW;EACf;EACA;EACA;EACA;EACA,UAAU;;EAEV;;EAEA;;;UAIe;EACf;;EAEA;;EAEA;;;UAIe;EACf;EACA;;EAEA,SAAS;EACT;EACA;EACA,gBAAgB;EAChB,qBAAqB;EACrB;EACA,qBAAqB;EACrB,uBAAuB;;EAEvB,OAAO;;EAEP,YAAY;;EAEZ,cAAc;;EAEd,aAAa;;EAEb,kBAAkB;;EAElB,oBAAoB;;EAEpB;;;;;;;;;wBClHc,SAAS,GAAG,WAAW,QAAO,YAAY"}
|
package/dist/plugin.mjs
CHANGED
|
@@ -147,12 +147,13 @@ function createModalManager(k) {
|
|
|
147
147
|
function pauseSceneTree(gameObj) {
|
|
148
148
|
if (isModal(gameObj) || isInsideModal(gameObj)) return;
|
|
149
149
|
pausedScene ??= /* @__PURE__ */ new Map();
|
|
150
|
-
// v8 ignore
|
|
150
|
+
// v8 ignore if
|
|
151
151
|
if (!pausedScene.has(gameObj)) pausedScene.set(gameObj, gameObj.paused);
|
|
152
152
|
setPaused(gameObj);
|
|
153
153
|
gameObj.children.forEach(pauseSceneTree);
|
|
154
154
|
}
|
|
155
155
|
function openModal(modal) {
|
|
156
|
+
k.setCursor("default");
|
|
156
157
|
if (modals.length > 0) setPaused(getLast(modals), true, true);
|
|
157
158
|
modals.push(modal);
|
|
158
159
|
if (modals.length === 1) {
|
|
@@ -174,7 +175,7 @@ function createModalManager(k) {
|
|
|
174
175
|
if (!modals.length) {
|
|
175
176
|
cancelPauseOnAdd?.();
|
|
176
177
|
cancelPauseOnAdd = void 0;
|
|
177
|
-
// v8 ignore
|
|
178
|
+
// v8 ignore if
|
|
178
179
|
if (pausedScene) {
|
|
179
180
|
for (const [gameObj, paused] of pausedScene) if (gameObj.exists()) setPaused(gameObj, paused);
|
|
180
181
|
pausedScene = void 0;
|
|
@@ -249,6 +250,7 @@ function createButton(k, theme, text, opt) {
|
|
|
249
250
|
const radius = opt.radius ?? theme.shape.borderRadius;
|
|
250
251
|
const { minWidth, paddingX, paddingY } = theme.components.button;
|
|
251
252
|
const baseColor = toColor(k, opt.color ?? opt.fill ?? theme.palette.primary);
|
|
253
|
+
let disabled = opt.disabled ?? false;
|
|
252
254
|
const hoverColor = toColor(k, opt.hoverColor ?? theme.palette.hover);
|
|
253
255
|
const pressedColor = toColor(k, opt.pressedColor ?? theme.palette.pressed);
|
|
254
256
|
const textColor = toColor(k, opt.textColor ?? theme.palette.text);
|
|
@@ -266,7 +268,7 @@ function createButton(k, theme, text, opt) {
|
|
|
266
268
|
k.pos(pos),
|
|
267
269
|
k.area(),
|
|
268
270
|
k.scale(),
|
|
269
|
-
k.color(baseColor),
|
|
271
|
+
k.color(disabled ? toColor(k, theme.palette.disabled) : baseColor),
|
|
270
272
|
k.anchor("center")
|
|
271
273
|
];
|
|
272
274
|
const button = opt.parent ? opt.parent.add(components) : k.add(components);
|
|
@@ -331,6 +333,7 @@ function createButton(k, theme, text, opt) {
|
|
|
331
333
|
layout();
|
|
332
334
|
if (opt.width === void 0 || opt.height === void 0 || opt.icon !== void 0 || opt.background !== void 0) button.onUpdate(layout);
|
|
333
335
|
button.onClick(() => {
|
|
336
|
+
if (disabled) return;
|
|
334
337
|
k.wait(0, () => {
|
|
335
338
|
button.color = toColor(k, pressedColor);
|
|
336
339
|
if (icon && opt.pressedIcon) {
|
|
@@ -342,12 +345,18 @@ function createButton(k, theme, text, opt) {
|
|
|
342
345
|
background.sprite = opt.pressedBackground;
|
|
343
346
|
}
|
|
344
347
|
opt.onClick?.();
|
|
348
|
+
// v8 ignore if
|
|
349
|
+
if (disabled) k.setCursor("not-allowed");
|
|
350
|
+
else if (!button.isHovering()) k.setCursor("default");
|
|
345
351
|
});
|
|
346
352
|
});
|
|
347
353
|
button.onHover(() => {
|
|
354
|
+
if (disabled) {
|
|
355
|
+
k.setCursor("not-allowed");
|
|
356
|
+
return;
|
|
357
|
+
} else k.setCursor("pointer");
|
|
348
358
|
button.color = toColor(k, hoverColor);
|
|
349
359
|
button.scale = k.vec2(1.05, 1.05);
|
|
350
|
-
k.setCursor("pointer");
|
|
351
360
|
if (icon && opt.hoverIcon) {
|
|
352
361
|
iconState = "hover";
|
|
353
362
|
icon.sprite = opt.hoverIcon;
|
|
@@ -358,9 +367,10 @@ function createButton(k, theme, text, opt) {
|
|
|
358
367
|
}
|
|
359
368
|
});
|
|
360
369
|
button.onHoverEnd(() => {
|
|
370
|
+
k.setCursor("default");
|
|
371
|
+
if (disabled) return;
|
|
361
372
|
button.color = baseColor;
|
|
362
373
|
button.scale = k.vec2(1, 1);
|
|
363
|
-
k.setCursor("default");
|
|
364
374
|
if (icon && opt.icon) {
|
|
365
375
|
iconState = "base";
|
|
366
376
|
icon.sprite = opt.icon;
|
|
@@ -371,11 +381,29 @@ function createButton(k, theme, text, opt) {
|
|
|
371
381
|
}
|
|
372
382
|
if (icon || background) layout();
|
|
373
383
|
});
|
|
374
|
-
|
|
384
|
+
button.onDestroy(() => {
|
|
385
|
+
if (button.isHovering()) k.setCursor("default");
|
|
386
|
+
});
|
|
387
|
+
function applyDisabledState() {
|
|
388
|
+
button.color = disabled ? toColor(k, theme.palette.disabled) : baseColor;
|
|
389
|
+
}
|
|
390
|
+
applyDisabledState();
|
|
391
|
+
const assignedButton = Object.assign(button, {
|
|
375
392
|
background,
|
|
376
393
|
icon,
|
|
377
394
|
label
|
|
378
395
|
});
|
|
396
|
+
return Object.defineProperty(assignedButton, "disabled", {
|
|
397
|
+
get() {
|
|
398
|
+
return disabled;
|
|
399
|
+
},
|
|
400
|
+
set(value) {
|
|
401
|
+
disabled = value;
|
|
402
|
+
applyDisabledState();
|
|
403
|
+
},
|
|
404
|
+
enumerable: true,
|
|
405
|
+
configurable: true
|
|
406
|
+
});
|
|
379
407
|
}
|
|
380
408
|
// src/theme.ts
|
|
381
409
|
const PRIMARY = "#4f8cff";
|
package/dist/plugin.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.mjs","names":[],"sources":["../src/modal.ts","../src/utils.ts","../src/button.ts","../src/theme.ts","../src/plugin.ts"],"sourcesContent":["import type { GameObj, KAPLAYCtx } from 'kaplay';\n\nimport { createButton } from './button';\nimport type { ModalOpt, Theme } from './types';\nimport { getLast, isModal, setPaused, toColor, uniqueTag } from './utils';\n\n/** Tag applied to modal game objects. */\nexport const MODAL_TAG = uniqueTag('modal');\n\ntype SizedGameObj = GameObj & {\n height: number;\n width: number;\n};\n\nexport interface ModalHooks {\n isTopmost: (modal: GameObj) => boolean;\n onClose: (modal: GameObj) => void;\n onOpen: (modal: GameObj) => void;\n}\n\nfunction isSized(obj: GameObj): obj is SizedGameObj {\n const sized = obj as Partial<SizedGameObj>;\n return typeof sized.width === 'number' && typeof sized.height === 'number';\n}\n\nexport function createModal(\n k: KAPLAYCtx,\n theme: Theme,\n opt: ModalOpt,\n hooks: ModalHooks,\n) {\n const {\n backdropOpacity,\n borderRadius,\n gap,\n maxWidth,\n minWidth,\n paddingX,\n paddingY,\n } = theme.components.modal;\n const fontSize = theme.typography.fontSize;\n const titleSize = Math.round(fontSize * 1.25);\n const font = theme.typography.fontFamily;\n const dismissible = opt.dismissible ?? true;\n\n const modal = k.add([\n k.pos(k.center()),\n k.fixed(),\n k.z(opt.z ?? 1000),\n MODAL_TAG,\n ]);\n\n modal.onDestroy(() => {\n hooks.onClose(modal);\n });\n\n hooks.onOpen(modal);\n\n const backdrop = modal.add([\n k.rect(k.width(), k.height()),\n k.pos(),\n k.color(0, 0, 0),\n k.opacity(backdropOpacity),\n k.anchor('center'),\n k.area(),\n ]);\n\n const content = modal.add([\n k.rect(0, 0, borderRadius > 0 ? { radius: borderRadius } : undefined),\n k.pos(),\n k.color(toColor(k, theme.palette.surface)),\n k.anchor('center'),\n k.area(),\n ]);\n\n const actions = content.add([k.pos()]);\n\n for (const action of opt.actions ?? []) {\n createButton(k, theme, action.text, {\n parent: actions,\n onClick: () => {\n if (action.close !== false) {\n modal.destroy();\n }\n\n action.onClick?.();\n },\n });\n }\n\n const titleMeasure = opt.title\n ? k.text(opt.title, { size: titleSize, font })\n : undefined;\n const bodyMeasure = opt.text\n ? k.text(opt.text, { size: fontSize, font })\n : undefined;\n const textWrapWidth = Math.max(0, (opt.width ?? maxWidth) - paddingX * 2);\n\n const title = opt.title\n ? content.add([\n k.text(opt.title, {\n size: titleSize,\n font,\n width: textWrapWidth > 0 ? textWrapWidth : undefined,\n align: 'center',\n }),\n k.pos(),\n k.color(toColor(k, theme.palette.text)),\n k.anchor('center'),\n ])\n : undefined;\n\n const body = opt.text\n ? content.add([\n k.text(opt.text, {\n size: fontSize,\n font,\n width: textWrapWidth > 0 ? textWrapWidth : undefined,\n align: 'center',\n }),\n k.pos(),\n k.color(toColor(k, theme.palette.textSecondary)),\n k.anchor('center'),\n ])\n : undefined;\n\n /**\n * Update modal layout:\n *\n * - fits the backdrop to the screen,\n * - sizes the content to its title, text, and actions,\n * - repositions the title, text, and actions.\n */\n function layout() {\n const screenWidth = k.width();\n const screenHeight = k.height();\n\n if (backdrop.width !== screenWidth) {\n backdrop.width = screenWidth;\n }\n\n if (backdrop.height !== screenHeight) {\n backdrop.height = screenHeight;\n }\n\n modal.pos = k.center();\n\n const buttons = actions.children.filter(isSized);\n const actionsWidth =\n buttons.reduce((width, button) => width + button.width, 0) +\n gap * Math.max(0, buttons.length - 1);\n const actionsHeight = Math.max(\n 0,\n ...buttons.map((button) => button.height),\n );\n\n const textContentWidth = Math.max(\n titleMeasure?.width ?? 0,\n bodyMeasure?.width ?? 0,\n );\n const nextWidth =\n opt.width ??\n Math.max(\n Math.min(textContentWidth, maxWidth - paddingX * 2) + paddingX * 2,\n actionsWidth + paddingX * 2,\n minWidth,\n );\n\n const titleHeight = title?.height ?? 0;\n const bodyHeight = body?.height ?? 0;\n const hasTitle = title !== undefined;\n const hasBody = body !== undefined;\n const hasActions = buttons.length > 0;\n const blockCount = Number(hasTitle) + Number(hasBody) + Number(hasActions);\n const contentHeight =\n titleHeight +\n bodyHeight +\n actionsHeight +\n gap * Math.max(0, blockCount - 1);\n const nextHeight = opt.height ?? contentHeight + paddingY * 2;\n\n if (content.width !== nextWidth) {\n content.width = nextWidth;\n }\n\n if (content.height !== nextHeight) {\n content.height = nextHeight;\n }\n\n let y = -nextHeight / 2 + (nextHeight - contentHeight) / 2;\n\n if (title) {\n title.pos = k.vec2(0, y + title.height / 2);\n y += title.height + gap;\n }\n\n if (body) {\n body.pos = k.vec2(0, y + body.height / 2);\n y += body.height + gap;\n }\n\n if (hasActions) {\n let x = -actionsWidth / 2;\n\n for (const button of buttons) {\n button.pos = k.vec2(x + button.width / 2, y + actionsHeight / 2);\n x += button.width + gap;\n }\n }\n }\n\n layout();\n modal.onUpdate(layout);\n\n if (dismissible) {\n // prevent click-through so modal isn't dismissed right after creation\n k.wait(0, () => {\n backdrop.onClick(() => {\n if (!hooks.isTopmost(modal)) {\n return;\n }\n\n if (content.hasPoint(k.mousePos())) {\n return;\n }\n\n modal.destroy();\n });\n });\n\n modal.onKeyPress('escape', () => {\n if (hooks.isTopmost(modal)) {\n modal.destroy();\n }\n });\n }\n\n return Object.assign(modal, {\n actions,\n content,\n });\n}\n\nexport interface ModalManager {\n openModal: ModalHooks['onOpen'];\n closeModal: ModalHooks['onClose'];\n isTopmost: ModalHooks['isTopmost'];\n isInsideModal: (gameObj: GameObj) => boolean;\n}\n\nexport function createModalManager(k: KAPLAYCtx): ModalManager {\n const modals: GameObj[] = [];\n let pausedScene: Map<GameObj, boolean> | undefined;\n let cancelPauseOnAdd: (() => void) | undefined;\n\n const isInsideModal = (gameObj: GameObj) =>\n modals.some((modal) => modal === gameObj || modal.isAncestorOf(gameObj));\n\n function pauseSceneTree(gameObj: GameObj) {\n if (isModal(gameObj) || isInsideModal(gameObj)) {\n return;\n }\n\n pausedScene ??= new Map();\n\n // v8 ignore next\n if (!pausedScene.has(gameObj)) {\n pausedScene.set(gameObj, gameObj.paused);\n }\n\n setPaused(gameObj);\n gameObj.children.forEach(pauseSceneTree);\n }\n\n function openModal(modal: GameObj) {\n if (modals.length > 0) {\n setPaused(getLast(modals), true, true);\n }\n\n modals.push(modal);\n\n if (modals.length === 1) {\n pausedScene = new Map();\n k.getTreeRoot().children.forEach(pauseSceneTree);\n\n const controller = k.onAdd((gameObj) => {\n if (!isModal(gameObj) && !isInsideModal(gameObj)) {\n pauseSceneTree(gameObj);\n }\n });\n\n cancelPauseOnAdd = () => {\n controller.cancel();\n };\n }\n }\n\n function closeModal(modal: GameObj) {\n const index = modals.indexOf(modal);\n\n if (index === -1) {\n return;\n }\n\n const wasTopmost = index === modals.length - 1;\n\n modals.splice(index, 1);\n\n if (!modals.length) {\n cancelPauseOnAdd?.();\n cancelPauseOnAdd = undefined;\n\n // v8 ignore next\n if (pausedScene) {\n for (const [gameObj, paused] of pausedScene) {\n if (gameObj.exists()) {\n setPaused(gameObj, paused);\n }\n }\n\n pausedScene = undefined;\n }\n } else if (wasTopmost) {\n setPaused(getLast(modals), false, true);\n }\n }\n\n const isTopmost = (modal: GameObj) => getLast(modals) === modal;\n\n return { openModal, closeModal, isTopmost, isInsideModal };\n}\n","import type { Color, GameObj, KAPLAYCtx } from 'kaplay';\n\nimport { MODAL_TAG } from './modal';\n\n/**\n * Get the last item in an array.\n *\n * @param items - The items.\n * @returns The last item.\n */\nexport function getLast<T>(items: T[]): T {\n return items[items.length - 1];\n}\n\n/**\n * Check if the game object is a modal.\n *\n * @param gameObj - The game object.\n * @returns Whether game object is a modal.\n */\nexport function isModal(gameObj: GameObj): boolean {\n return gameObj.is(MODAL_TAG);\n}\n\n/**\n * Pause or unpause game object(s).\n *\n * @param gameObj - The root game object.\n * @param isPaused - Whether it should be paused.\n * @param includeTree - Whether to pause all game objects in tree.\n */\nexport function setPaused(\n gameObj: GameObj,\n isPaused = true,\n includeTree = false,\n): void {\n gameObj.paused = isPaused;\n\n if (includeTree) {\n gameObj.children.forEach((childGameObj) => {\n setPaused(childGameObj, isPaused, includeTree);\n });\n }\n}\n\n/**\n * Convert a hex string or color to color.\n *\n * @param k - The Kaplay context.\n * @param color - The hex string or color.\n * @returns The color.\n */\nexport function toColor(k: KAPLAYCtx, color: string | Color): Color {\n return typeof color === 'string' ? k.rgb(color) : color;\n}\n\n/**\n * Generate a unique tag.\n *\n * @param id - The identifier.\n * @returns The tag.\n */\nexport function uniqueTag(id: string): string {\n return [\n 'kaplay-plugin-ui',\n id,\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n crypto?.randomUUID?.(),\n ].join(':');\n}\n","import type { Color, KAPLAYCtx, Vec2 } from 'kaplay';\n\nimport type { ButtonOpt, Theme } from './types';\nimport { toColor } from './utils';\n\nexport function createButton(\n k: KAPLAYCtx,\n theme: Theme,\n text: string,\n opt: ButtonOpt & {\n pos?: Vec2;\n onClick?: () => void;\n color?: string | Color;\n hoverColor?: string | Color;\n pressedColor?: string | Color;\n textColor?: string | Color;\n },\n) {\n const radius = opt.radius ?? theme.shape.borderRadius;\n const { minWidth, paddingX, paddingY } = theme.components.button;\n const baseColor = toColor(k, opt.color ?? opt.fill ?? theme.palette.primary);\n const hoverColor = toColor(k, opt.hoverColor ?? theme.palette.hover);\n const pressedColor = toColor(k, opt.pressedColor ?? theme.palette.pressed);\n const textColor = toColor(k, 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 const textComp = k.text(text, { size, font });\n const isIconOnly = opt.icon !== undefined && !text.length;\n const hasBackground = opt.background !== undefined;\n const showFill = !hasBackground && !isIconOnly;\n const rectOpt = showFill\n ? radius > 0\n ? { radius }\n : undefined\n : { fill: false };\n\n const components = [\n k.rect(0, 0, rectOpt),\n k.pos(pos),\n k.area(),\n k.scale(),\n k.color(baseColor),\n k.anchor('center'),\n ];\n const button = opt.parent ? opt.parent.add(components) : k.add(components);\n\n const background = opt.background\n ? button.add([k.sprite(opt.background), k.pos(), k.anchor('center')])\n : undefined;\n\n const icon = opt.icon\n ? button.add([k.sprite(opt.icon), k.pos(), k.anchor('center')])\n : undefined;\n\n const label = button.add([\n textComp,\n k.color(textColor as never),\n k.pos(),\n k.anchor('center'),\n ]);\n\n let iconWidth = 0;\n let iconHeight = 0;\n let iconState: 'base' | 'hover' | 'pressed' = 'base';\n let backgroundWidth = 0;\n let backgroundHeight = 0;\n let backgroundMeasured = false;\n let backgroundState: 'base' | 'hover' | 'pressed' = 'base';\n\n /**\n * Update button layout:\n *\n * - caches the base icon/background dimensions,\n * - calculates content width/height,\n * - updates `button.width` / `button.height`,\n * - stretches the background and repositions the icon and label.\n */\n function layout() {\n if (icon && iconState === 'base') {\n iconWidth = icon.width;\n iconHeight = icon.height;\n }\n\n if (\n background &&\n backgroundState === 'base' &&\n !backgroundMeasured &&\n (background.width > 0 || background.height > 0)\n ) {\n backgroundWidth = background.width;\n backgroundHeight = background.height;\n backgroundMeasured = true;\n }\n\n const gap = icon && iconWidth > 0 && label.width > 0 ? theme.spacing : 0;\n const contentWidth = iconWidth + gap + label.width;\n const contentHeight = Math.max(iconHeight, label.height);\n const contentBoxWidth = contentWidth + paddingX * 2;\n const contentBoxHeight = contentHeight + paddingY * 2;\n\n const nextWidth =\n opt.width ??\n (background\n ? Math.max(contentBoxWidth, backgroundWidth)\n : Math.max(contentBoxWidth, minWidth));\n const nextHeight =\n opt.height ??\n (background\n ? Math.max(contentBoxHeight, backgroundHeight)\n : contentBoxHeight);\n\n if (button.width !== nextWidth) {\n button.width = nextWidth;\n }\n\n if (button.height !== nextHeight) {\n button.height = nextHeight;\n }\n\n if (background && backgroundMeasured) {\n background.width = nextWidth;\n background.height = nextHeight;\n background.pos = k.vec2();\n }\n\n if (icon) {\n icon.pos = k.vec2(-contentWidth / 2 + iconWidth / 2, 0);\n }\n\n label.pos = k.vec2(\n -contentWidth / 2 + iconWidth + gap + label.width / 2,\n 0,\n );\n }\n\n layout();\n\n if (\n opt.width === undefined ||\n opt.height === undefined ||\n opt.icon !== undefined ||\n opt.background !== undefined\n ) {\n button.onUpdate(layout);\n }\n\n button.onClick(() => {\n // prevent stray click from click-through\n k.wait(0, () => {\n button.color = toColor(k, pressedColor);\n\n if (icon && opt.pressedIcon) {\n iconState = 'pressed';\n icon.sprite = opt.pressedIcon as string;\n }\n\n if (background && opt.pressedBackground) {\n backgroundState = 'pressed';\n background.sprite = opt.pressedBackground as string;\n }\n\n opt.onClick?.();\n });\n });\n\n button.onHover(() => {\n button.color = toColor(k, hoverColor);\n button.scale = k.vec2(1.05, 1.05);\n k.setCursor('pointer');\n\n if (icon && opt.hoverIcon) {\n iconState = 'hover';\n icon.sprite = opt.hoverIcon as string;\n }\n\n if (background && opt.hoverBackground) {\n backgroundState = 'hover';\n background.sprite = opt.hoverBackground as string;\n }\n });\n\n button.onHoverEnd(() => {\n button.color = baseColor;\n button.scale = k.vec2(1, 1);\n k.setCursor('default');\n\n if (icon && opt.icon) {\n iconState = 'base';\n icon.sprite = opt.icon as string;\n }\n\n if (background && opt.background) {\n backgroundState = 'base';\n background.sprite = opt.background as string;\n }\n\n if (icon || background) {\n layout();\n }\n });\n\n return Object.assign(button, {\n background,\n icon,\n label,\n });\n}\n","import type { DeepPartial, Theme } from './types';\n\nconst PRIMARY = '#4f8cff';\nconst TEXT = '#ffffff';\n\n/** Default theme. */\nconst theme: Theme = {\n palette: {\n primary: PRIMARY,\n secondary: PRIMARY,\n background: '#111827',\n surface: '#1f2937',\n text: TEXT,\n textSecondary: TEXT,\n hover: PRIMARY,\n pressed: 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 minWidth: 120,\n paddingX: 12,\n paddingY: 8,\n textTransform: 'none',\n },\n modal: {\n backdropOpacity: 0.5,\n borderRadius: 8,\n gap: 16,\n maxWidth: 480,\n minWidth: 280,\n paddingX: 24,\n paddingY: 20,\n },\n tooltip: {\n background: '#111827',\n textColor: 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: { ...theme.palette, ...input?.palette },\n typography: { ...theme.typography, ...input?.typography },\n shape: { ...theme.shape, ...input?.shape },\n spacing: input?.spacing ?? theme.spacing,\n components: {\n button: {\n ...theme.components.button,\n ...input?.components?.button,\n },\n modal: {\n ...theme.components.modal,\n ...input?.components?.modal,\n },\n tooltip: {\n ...theme.components.tooltip,\n ...input?.components?.tooltip,\n },\n },\n };\n}\n","import type { KAPLAYCtx } from 'kaplay';\n\nimport { createButton } from './button';\nimport { createModal, createModalManager } from './modal';\nimport { resolveTheme } from './theme';\nimport type {\n AddButton,\n AddModal,\n DeepPartial,\n GetTheme,\n SetTheme,\n Theme,\n} from './types';\n\n/**\n * Kaplay UI plugin.\n *\n * @param k - The Kaplay context.\n * @param theme - The theme config.\n * @returns The plugin methods.\n */\nexport function uiPlugin(k: KAPLAYCtx, theme: DeepPartial<Theme> = {}) {\n let userTheme: DeepPartial<Theme> = theme;\n let currentTheme: Theme = resolveTheme(userTheme);\n\n const getTheme: GetTheme = () => currentTheme;\n\n const setTheme: SetTheme = (next) => {\n userTheme = { ...userTheme, ...next };\n currentTheme = resolveTheme(userTheme);\n };\n\n const addButton: AddButton = (text = '', opt = {}) => {\n return createButton(k, currentTheme, text, opt);\n };\n\n const modalManager = createModalManager(k);\n\n const addModal: AddModal = (opt = {}) => {\n return createModal(k, currentTheme, opt, {\n isTopmost: modalManager.isTopmost,\n onClose: modalManager.closeModal,\n onOpen: modalManager.openModal,\n });\n };\n\n return {\n addButton,\n addModal,\n setTheme,\n getTheme,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"plugin.mjs","names":[],"sources":["../src/modal.ts","../src/utils.ts","../src/button.ts","../src/theme.ts","../src/plugin.ts"],"sourcesContent":["import type { GameObj, KAPLAYCtx } from 'kaplay';\n\nimport { createButton } from './button';\nimport type { ModalOpt, Theme } from './types';\nimport { getLast, isModal, setPaused, toColor, uniqueTag } from './utils';\n\n/** Tag applied to modal game objects. */\nexport const MODAL_TAG = uniqueTag('modal');\n\ntype SizedGameObj = GameObj & {\n height: number;\n width: number;\n};\n\nexport interface ModalHooks {\n isTopmost: (modal: GameObj) => boolean;\n onClose: (modal: GameObj) => void;\n onOpen: (modal: GameObj) => void;\n}\n\nfunction isSized(obj: GameObj): obj is SizedGameObj {\n const sized = obj as Partial<SizedGameObj>;\n return typeof sized.width === 'number' && typeof sized.height === 'number';\n}\n\nexport function createModal(\n k: KAPLAYCtx,\n theme: Theme,\n opt: ModalOpt,\n hooks: ModalHooks,\n) {\n const {\n backdropOpacity,\n borderRadius,\n gap,\n maxWidth,\n minWidth,\n paddingX,\n paddingY,\n } = theme.components.modal;\n const fontSize = theme.typography.fontSize;\n const titleSize = Math.round(fontSize * 1.25);\n const font = theme.typography.fontFamily;\n const dismissible = opt.dismissible ?? true;\n\n const modal = k.add([\n k.pos(k.center()),\n k.fixed(),\n k.z(opt.z ?? 1000),\n MODAL_TAG,\n ]);\n\n modal.onDestroy(() => {\n hooks.onClose(modal);\n });\n\n hooks.onOpen(modal);\n\n const backdrop = modal.add([\n k.rect(k.width(), k.height()),\n k.pos(),\n k.color(0, 0, 0),\n k.opacity(backdropOpacity),\n k.anchor('center'),\n k.area(),\n ]);\n\n const content = modal.add([\n k.rect(0, 0, borderRadius > 0 ? { radius: borderRadius } : undefined),\n k.pos(),\n k.color(toColor(k, theme.palette.surface)),\n k.anchor('center'),\n k.area(),\n ]);\n\n const actions = content.add([k.pos()]);\n\n for (const action of opt.actions ?? []) {\n createButton(k, theme, action.text, {\n parent: actions,\n onClick: () => {\n if (action.close !== false) {\n modal.destroy();\n }\n\n action.onClick?.();\n },\n });\n }\n\n const titleMeasure = opt.title\n ? k.text(opt.title, { size: titleSize, font })\n : undefined;\n const bodyMeasure = opt.text\n ? k.text(opt.text, { size: fontSize, font })\n : undefined;\n const textWrapWidth = Math.max(0, (opt.width ?? maxWidth) - paddingX * 2);\n\n const title = opt.title\n ? content.add([\n k.text(opt.title, {\n size: titleSize,\n font,\n width: textWrapWidth > 0 ? textWrapWidth : undefined,\n align: 'center',\n }),\n k.pos(),\n k.color(toColor(k, theme.palette.text)),\n k.anchor('center'),\n ])\n : undefined;\n\n const body = opt.text\n ? content.add([\n k.text(opt.text, {\n size: fontSize,\n font,\n width: textWrapWidth > 0 ? textWrapWidth : undefined,\n align: 'center',\n }),\n k.pos(),\n k.color(toColor(k, theme.palette.textSecondary)),\n k.anchor('center'),\n ])\n : undefined;\n\n /**\n * Update modal layout:\n *\n * - fits the backdrop to the screen,\n * - sizes the content to its title, text, and actions,\n * - repositions the title, text, and actions.\n */\n function layout() {\n const screenWidth = k.width();\n const screenHeight = k.height();\n\n if (backdrop.width !== screenWidth) {\n backdrop.width = screenWidth;\n }\n\n if (backdrop.height !== screenHeight) {\n backdrop.height = screenHeight;\n }\n\n modal.pos = k.center();\n\n const buttons = actions.children.filter(isSized);\n const actionsWidth =\n buttons.reduce((width, button) => width + button.width, 0) +\n gap * Math.max(0, buttons.length - 1);\n const actionsHeight = Math.max(\n 0,\n ...buttons.map((button) => button.height),\n );\n\n const textContentWidth = Math.max(\n titleMeasure?.width ?? 0,\n bodyMeasure?.width ?? 0,\n );\n const nextWidth =\n opt.width ??\n Math.max(\n Math.min(textContentWidth, maxWidth - paddingX * 2) + paddingX * 2,\n actionsWidth + paddingX * 2,\n minWidth,\n );\n\n const titleHeight = title?.height ?? 0;\n const bodyHeight = body?.height ?? 0;\n const hasTitle = title !== undefined;\n const hasBody = body !== undefined;\n const hasActions = buttons.length > 0;\n const blockCount = Number(hasTitle) + Number(hasBody) + Number(hasActions);\n const contentHeight =\n titleHeight +\n bodyHeight +\n actionsHeight +\n gap * Math.max(0, blockCount - 1);\n const nextHeight = opt.height ?? contentHeight + paddingY * 2;\n\n if (content.width !== nextWidth) {\n content.width = nextWidth;\n }\n\n if (content.height !== nextHeight) {\n content.height = nextHeight;\n }\n\n let y = -nextHeight / 2 + (nextHeight - contentHeight) / 2;\n\n if (title) {\n title.pos = k.vec2(0, y + title.height / 2);\n y += title.height + gap;\n }\n\n if (body) {\n body.pos = k.vec2(0, y + body.height / 2);\n y += body.height + gap;\n }\n\n if (hasActions) {\n let x = -actionsWidth / 2;\n\n for (const button of buttons) {\n button.pos = k.vec2(x + button.width / 2, y + actionsHeight / 2);\n x += button.width + gap;\n }\n }\n }\n\n layout();\n modal.onUpdate(layout);\n\n if (dismissible) {\n // prevent click-through so modal isn't dismissed right after creation\n k.wait(0, () => {\n backdrop.onClick(() => {\n if (!hooks.isTopmost(modal)) {\n return;\n }\n\n if (content.hasPoint(k.mousePos())) {\n return;\n }\n\n modal.destroy();\n });\n });\n\n modal.onKeyPress('escape', () => {\n if (hooks.isTopmost(modal)) {\n modal.destroy();\n }\n });\n }\n\n return Object.assign(modal, {\n actions,\n content,\n });\n}\n\nexport interface ModalManager {\n openModal: ModalHooks['onOpen'];\n closeModal: ModalHooks['onClose'];\n isTopmost: ModalHooks['isTopmost'];\n isInsideModal: (gameObj: GameObj) => boolean;\n}\n\nexport function createModalManager(k: KAPLAYCtx): ModalManager {\n const modals: GameObj[] = [];\n let pausedScene: Map<GameObj, boolean> | undefined;\n let cancelPauseOnAdd: (() => void) | undefined;\n\n const isInsideModal = (gameObj: GameObj) =>\n modals.some((modal) => modal === gameObj || modal.isAncestorOf(gameObj));\n\n function pauseSceneTree(gameObj: GameObj) {\n if (isModal(gameObj) || isInsideModal(gameObj)) {\n return;\n }\n\n pausedScene ??= new Map();\n\n // v8 ignore if\n if (!pausedScene.has(gameObj)) {\n pausedScene.set(gameObj, gameObj.paused);\n }\n\n setPaused(gameObj);\n gameObj.children.forEach(pauseSceneTree);\n }\n\n function openModal(modal: GameObj) {\n k.setCursor('default');\n\n if (modals.length > 0) {\n setPaused(getLast(modals), true, true);\n }\n\n modals.push(modal);\n\n if (modals.length === 1) {\n pausedScene = new Map();\n k.getTreeRoot().children.forEach(pauseSceneTree);\n\n const controller = k.onAdd((gameObj) => {\n if (!isModal(gameObj) && !isInsideModal(gameObj)) {\n pauseSceneTree(gameObj);\n }\n });\n\n cancelPauseOnAdd = () => {\n controller.cancel();\n };\n }\n }\n\n function closeModal(modal: GameObj) {\n const index = modals.indexOf(modal);\n\n if (index === -1) {\n return;\n }\n\n const wasTopmost = index === modals.length - 1;\n\n modals.splice(index, 1);\n\n if (!modals.length) {\n cancelPauseOnAdd?.();\n cancelPauseOnAdd = undefined;\n\n // v8 ignore if\n if (pausedScene) {\n for (const [gameObj, paused] of pausedScene) {\n if (gameObj.exists()) {\n setPaused(gameObj, paused);\n }\n }\n\n pausedScene = undefined;\n }\n } else if (wasTopmost) {\n setPaused(getLast(modals), false, true);\n }\n }\n\n const isTopmost = (modal: GameObj) => getLast(modals) === modal;\n\n return { openModal, closeModal, isTopmost, isInsideModal };\n}\n","import type { Color, GameObj, KAPLAYCtx } from 'kaplay';\n\nimport { MODAL_TAG } from './modal';\n\n/**\n * Get the last item in an array.\n *\n * @param items - The items.\n * @returns The last item.\n */\nexport function getLast<T>(items: T[]): T {\n return items[items.length - 1];\n}\n\n/**\n * Check if the game object is a modal.\n *\n * @param gameObj - The game object.\n * @returns Whether game object is a modal.\n */\nexport function isModal(gameObj: GameObj): boolean {\n return gameObj.is(MODAL_TAG);\n}\n\n/**\n * Pause or unpause game object(s).\n *\n * @param gameObj - The root game object.\n * @param isPaused - Whether it should be paused.\n * @param includeTree - Whether to pause all game objects in tree.\n */\nexport function setPaused(\n gameObj: GameObj,\n isPaused = true,\n includeTree = false,\n): void {\n gameObj.paused = isPaused;\n\n if (includeTree) {\n gameObj.children.forEach((childGameObj) => {\n setPaused(childGameObj, isPaused, includeTree);\n });\n }\n}\n\n/**\n * Convert a hex string or color to color.\n *\n * @param k - The Kaplay context.\n * @param color - The hex string or color.\n * @returns The color.\n */\nexport function toColor(k: KAPLAYCtx, color: string | Color): Color {\n return typeof color === 'string' ? k.rgb(color) : color;\n}\n\n/**\n * Generate a unique tag.\n *\n * @param id - The identifier.\n * @returns The tag.\n */\nexport function uniqueTag(id: string): string {\n return [\n 'kaplay-plugin-ui',\n id,\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n crypto?.randomUUID?.(),\n ].join(':');\n}\n","import type { Color, KAPLAYCtx, Vec2 } from 'kaplay';\n\nimport type { ButtonOpt, Theme } from './types';\nimport { toColor } from './utils';\n\nexport function createButton(\n k: KAPLAYCtx,\n theme: Theme,\n text: string,\n opt: ButtonOpt & {\n pos?: Vec2;\n onClick?: () => void;\n color?: string | Color;\n hoverColor?: string | Color;\n pressedColor?: string | Color;\n textColor?: string | Color;\n },\n) {\n const radius = opt.radius ?? theme.shape.borderRadius;\n const { minWidth, paddingX, paddingY } = theme.components.button;\n const baseColor = toColor(k, opt.color ?? opt.fill ?? theme.palette.primary);\n let disabled = opt.disabled ?? false;\n const hoverColor = toColor(k, opt.hoverColor ?? theme.palette.hover);\n const pressedColor = toColor(k, opt.pressedColor ?? theme.palette.pressed);\n const textColor = toColor(k, 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 const textComp = k.text(text, { size, font });\n const isIconOnly = opt.icon !== undefined && !text.length;\n const hasBackground = opt.background !== undefined;\n const showFill = !hasBackground && !isIconOnly;\n const rectOpt = showFill\n ? radius > 0\n ? { radius }\n : undefined\n : { fill: false };\n\n const components = [\n k.rect(0, 0, rectOpt),\n k.pos(pos),\n k.area(),\n k.scale(),\n k.color(disabled ? toColor(k, theme.palette.disabled) : baseColor),\n k.anchor('center'),\n ];\n const button = opt.parent ? opt.parent.add(components) : k.add(components);\n\n const background = opt.background\n ? button.add([k.sprite(opt.background), k.pos(), k.anchor('center')])\n : undefined;\n\n const icon = opt.icon\n ? button.add([k.sprite(opt.icon), k.pos(), k.anchor('center')])\n : undefined;\n\n const label = button.add([\n textComp,\n k.color(textColor as never),\n k.pos(),\n k.anchor('center'),\n ]);\n\n let iconWidth = 0;\n let iconHeight = 0;\n let iconState: 'base' | 'hover' | 'pressed' = 'base';\n let backgroundWidth = 0;\n let backgroundHeight = 0;\n let backgroundMeasured = false;\n let backgroundState: 'base' | 'hover' | 'pressed' = 'base';\n\n /**\n * Update button layout:\n *\n * - caches the base icon/background dimensions,\n * - calculates content width/height,\n * - updates `button.width` / `button.height`,\n * - stretches the background and repositions the icon and label.\n */\n function layout() {\n if (icon && iconState === 'base') {\n iconWidth = icon.width;\n iconHeight = icon.height;\n }\n\n if (\n background &&\n backgroundState === 'base' &&\n !backgroundMeasured &&\n (background.width > 0 || background.height > 0)\n ) {\n backgroundWidth = background.width;\n backgroundHeight = background.height;\n backgroundMeasured = true;\n }\n\n const gap = icon && iconWidth > 0 && label.width > 0 ? theme.spacing : 0;\n const contentWidth = iconWidth + gap + label.width;\n const contentHeight = Math.max(iconHeight, label.height);\n const contentBoxWidth = contentWidth + paddingX * 2;\n const contentBoxHeight = contentHeight + paddingY * 2;\n\n const nextWidth =\n opt.width ??\n (background\n ? Math.max(contentBoxWidth, backgroundWidth)\n : Math.max(contentBoxWidth, minWidth));\n const nextHeight =\n opt.height ??\n (background\n ? Math.max(contentBoxHeight, backgroundHeight)\n : contentBoxHeight);\n\n if (button.width !== nextWidth) {\n button.width = nextWidth;\n }\n\n if (button.height !== nextHeight) {\n button.height = nextHeight;\n }\n\n if (background && backgroundMeasured) {\n background.width = nextWidth;\n background.height = nextHeight;\n background.pos = k.vec2();\n }\n\n if (icon) {\n icon.pos = k.vec2(-contentWidth / 2 + iconWidth / 2, 0);\n }\n\n label.pos = k.vec2(\n -contentWidth / 2 + iconWidth + gap + label.width / 2,\n 0,\n );\n }\n\n layout();\n\n if (\n opt.width === undefined ||\n opt.height === undefined ||\n opt.icon !== undefined ||\n opt.background !== undefined\n ) {\n button.onUpdate(layout);\n }\n\n button.onClick(() => {\n if (disabled) {\n return;\n }\n\n // prevent click-through\n k.wait(0, () => {\n button.color = toColor(k, pressedColor);\n\n if (icon && opt.pressedIcon) {\n iconState = 'pressed';\n icon.sprite = opt.pressedIcon as string;\n }\n\n if (background && opt.pressedBackground) {\n backgroundState = 'pressed';\n background.sprite = opt.pressedBackground as string;\n }\n\n opt.onClick?.();\n\n // v8 ignore if\n if (disabled) {\n k.setCursor('not-allowed');\n } else if (!button.isHovering()) {\n k.setCursor('default');\n }\n });\n });\n\n button.onHover(() => {\n if (disabled) {\n k.setCursor('not-allowed');\n return;\n } else {\n k.setCursor('pointer');\n }\n\n button.color = toColor(k, hoverColor);\n button.scale = k.vec2(1.05, 1.05);\n\n if (icon && opt.hoverIcon) {\n iconState = 'hover';\n icon.sprite = opt.hoverIcon as string;\n }\n\n if (background && opt.hoverBackground) {\n backgroundState = 'hover';\n background.sprite = opt.hoverBackground as string;\n }\n });\n\n button.onHoverEnd(() => {\n k.setCursor('default');\n\n if (disabled) {\n return;\n }\n\n button.color = baseColor;\n button.scale = k.vec2(1, 1);\n\n if (icon && opt.icon) {\n iconState = 'base';\n icon.sprite = opt.icon as string;\n }\n\n if (background && opt.background) {\n backgroundState = 'base';\n background.sprite = opt.background as string;\n }\n\n if (icon || background) {\n layout();\n }\n });\n\n button.onDestroy(() => {\n if (button.isHovering()) {\n k.setCursor('default');\n }\n });\n\n function applyDisabledState() {\n button.color = disabled ? toColor(k, theme.palette.disabled) : baseColor;\n }\n\n applyDisabledState();\n\n const assignedButton = Object.assign(button, {\n background,\n icon,\n label,\n });\n\n const definedButton = Object.defineProperty(assignedButton, 'disabled', {\n get() {\n return disabled;\n },\n set(value: boolean) {\n disabled = value;\n applyDisabledState();\n },\n enumerable: true,\n configurable: true,\n }) as typeof assignedButton & { disabled: boolean };\n\n return definedButton;\n}\n","import type { DeepPartial, Theme } from './types';\n\nconst PRIMARY = '#4f8cff';\nconst TEXT = '#ffffff';\n\n/** Default theme. */\nconst theme: Theme = {\n palette: {\n primary: PRIMARY,\n secondary: PRIMARY,\n background: '#111827',\n surface: '#1f2937',\n text: TEXT,\n textSecondary: TEXT,\n hover: PRIMARY,\n pressed: 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 minWidth: 120,\n paddingX: 12,\n paddingY: 8,\n textTransform: 'none',\n },\n modal: {\n backdropOpacity: 0.5,\n borderRadius: 8,\n gap: 16,\n maxWidth: 480,\n minWidth: 280,\n paddingX: 24,\n paddingY: 20,\n },\n tooltip: {\n background: '#111827',\n textColor: 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: { ...theme.palette, ...input?.palette },\n typography: { ...theme.typography, ...input?.typography },\n shape: { ...theme.shape, ...input?.shape },\n spacing: input?.spacing ?? theme.spacing,\n components: {\n button: {\n ...theme.components.button,\n ...input?.components?.button,\n },\n modal: {\n ...theme.components.modal,\n ...input?.components?.modal,\n },\n tooltip: {\n ...theme.components.tooltip,\n ...input?.components?.tooltip,\n },\n },\n };\n}\n","import type { KAPLAYCtx } from 'kaplay';\n\nimport { createButton } from './button';\nimport { createModal, createModalManager } from './modal';\nimport { resolveTheme } from './theme';\nimport type {\n AddButton,\n AddModal,\n DeepPartial,\n GetTheme,\n SetTheme,\n Theme,\n} from './types';\n\n/**\n * Kaplay UI plugin.\n *\n * @param k - The Kaplay context.\n * @param theme - The theme config.\n * @returns The plugin methods.\n */\nexport function uiPlugin(k: KAPLAYCtx, theme: DeepPartial<Theme> = {}) {\n let userTheme: DeepPartial<Theme> = theme;\n let currentTheme: Theme = resolveTheme(userTheme);\n\n const getTheme: GetTheme = () => currentTheme;\n\n const setTheme: SetTheme = (next) => {\n userTheme = { ...userTheme, ...next };\n currentTheme = resolveTheme(userTheme);\n };\n\n const addButton: AddButton = (text = '', opt = {}) => {\n return createButton(k, currentTheme, text, opt);\n };\n\n const modalManager = createModalManager(k);\n\n const addModal: AddModal = (opt = {}) => {\n return createModal(k, currentTheme, opt, {\n isTopmost: modalManager.isTopmost,\n onClose: modalManager.closeModal,\n onOpen: modalManager.openModal,\n });\n };\n\n return {\n addButton,\n addModal,\n setTheme,\n getTheme,\n };\n}\n"],"mappings":"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"}
|
package/dist/plugin.umd.js
CHANGED
|
@@ -151,12 +151,13 @@
|
|
|
151
151
|
function pauseSceneTree(gameObj) {
|
|
152
152
|
if (isModal(gameObj) || isInsideModal(gameObj)) return;
|
|
153
153
|
pausedScene ??= /* @__PURE__ */ new Map();
|
|
154
|
-
// v8 ignore
|
|
154
|
+
// v8 ignore if
|
|
155
155
|
if (!pausedScene.has(gameObj)) pausedScene.set(gameObj, gameObj.paused);
|
|
156
156
|
setPaused(gameObj);
|
|
157
157
|
gameObj.children.forEach(pauseSceneTree);
|
|
158
158
|
}
|
|
159
159
|
function openModal(modal) {
|
|
160
|
+
k.setCursor("default");
|
|
160
161
|
if (modals.length > 0) setPaused(getLast(modals), true, true);
|
|
161
162
|
modals.push(modal);
|
|
162
163
|
if (modals.length === 1) {
|
|
@@ -178,7 +179,7 @@
|
|
|
178
179
|
if (!modals.length) {
|
|
179
180
|
cancelPauseOnAdd?.();
|
|
180
181
|
cancelPauseOnAdd = void 0;
|
|
181
|
-
// v8 ignore
|
|
182
|
+
// v8 ignore if
|
|
182
183
|
if (pausedScene) {
|
|
183
184
|
for (const [gameObj, paused] of pausedScene) if (gameObj.exists()) setPaused(gameObj, paused);
|
|
184
185
|
pausedScene = void 0;
|
|
@@ -253,6 +254,7 @@
|
|
|
253
254
|
const radius = opt.radius ?? theme.shape.borderRadius;
|
|
254
255
|
const { minWidth, paddingX, paddingY } = theme.components.button;
|
|
255
256
|
const baseColor = toColor(k, opt.color ?? opt.fill ?? theme.palette.primary);
|
|
257
|
+
let disabled = opt.disabled ?? false;
|
|
256
258
|
const hoverColor = toColor(k, opt.hoverColor ?? theme.palette.hover);
|
|
257
259
|
const pressedColor = toColor(k, opt.pressedColor ?? theme.palette.pressed);
|
|
258
260
|
const textColor = toColor(k, opt.textColor ?? theme.palette.text);
|
|
@@ -270,7 +272,7 @@
|
|
|
270
272
|
k.pos(pos),
|
|
271
273
|
k.area(),
|
|
272
274
|
k.scale(),
|
|
273
|
-
k.color(baseColor),
|
|
275
|
+
k.color(disabled ? toColor(k, theme.palette.disabled) : baseColor),
|
|
274
276
|
k.anchor("center")
|
|
275
277
|
];
|
|
276
278
|
const button = opt.parent ? opt.parent.add(components) : k.add(components);
|
|
@@ -335,6 +337,7 @@
|
|
|
335
337
|
layout();
|
|
336
338
|
if (opt.width === void 0 || opt.height === void 0 || opt.icon !== void 0 || opt.background !== void 0) button.onUpdate(layout);
|
|
337
339
|
button.onClick(() => {
|
|
340
|
+
if (disabled) return;
|
|
338
341
|
k.wait(0, () => {
|
|
339
342
|
button.color = toColor(k, pressedColor);
|
|
340
343
|
if (icon && opt.pressedIcon) {
|
|
@@ -346,12 +349,18 @@
|
|
|
346
349
|
background.sprite = opt.pressedBackground;
|
|
347
350
|
}
|
|
348
351
|
opt.onClick?.();
|
|
352
|
+
// v8 ignore if
|
|
353
|
+
if (disabled) k.setCursor("not-allowed");
|
|
354
|
+
else if (!button.isHovering()) k.setCursor("default");
|
|
349
355
|
});
|
|
350
356
|
});
|
|
351
357
|
button.onHover(() => {
|
|
358
|
+
if (disabled) {
|
|
359
|
+
k.setCursor("not-allowed");
|
|
360
|
+
return;
|
|
361
|
+
} else k.setCursor("pointer");
|
|
352
362
|
button.color = toColor(k, hoverColor);
|
|
353
363
|
button.scale = k.vec2(1.05, 1.05);
|
|
354
|
-
k.setCursor("pointer");
|
|
355
364
|
if (icon && opt.hoverIcon) {
|
|
356
365
|
iconState = "hover";
|
|
357
366
|
icon.sprite = opt.hoverIcon;
|
|
@@ -362,9 +371,10 @@
|
|
|
362
371
|
}
|
|
363
372
|
});
|
|
364
373
|
button.onHoverEnd(() => {
|
|
374
|
+
k.setCursor("default");
|
|
375
|
+
if (disabled) return;
|
|
365
376
|
button.color = baseColor;
|
|
366
377
|
button.scale = k.vec2(1, 1);
|
|
367
|
-
k.setCursor("default");
|
|
368
378
|
if (icon && opt.icon) {
|
|
369
379
|
iconState = "base";
|
|
370
380
|
icon.sprite = opt.icon;
|
|
@@ -375,11 +385,29 @@
|
|
|
375
385
|
}
|
|
376
386
|
if (icon || background) layout();
|
|
377
387
|
});
|
|
378
|
-
|
|
388
|
+
button.onDestroy(() => {
|
|
389
|
+
if (button.isHovering()) k.setCursor("default");
|
|
390
|
+
});
|
|
391
|
+
function applyDisabledState() {
|
|
392
|
+
button.color = disabled ? toColor(k, theme.palette.disabled) : baseColor;
|
|
393
|
+
}
|
|
394
|
+
applyDisabledState();
|
|
395
|
+
const assignedButton = Object.assign(button, {
|
|
379
396
|
background,
|
|
380
397
|
icon,
|
|
381
398
|
label
|
|
382
399
|
});
|
|
400
|
+
return Object.defineProperty(assignedButton, "disabled", {
|
|
401
|
+
get() {
|
|
402
|
+
return disabled;
|
|
403
|
+
},
|
|
404
|
+
set(value) {
|
|
405
|
+
disabled = value;
|
|
406
|
+
applyDisabledState();
|
|
407
|
+
},
|
|
408
|
+
enumerable: true,
|
|
409
|
+
configurable: true
|
|
410
|
+
});
|
|
383
411
|
}
|
|
384
412
|
// src/theme.ts
|
|
385
413
|
const PRIMARY = "#4f8cff";
|
package/dist/plugin.umd.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.umd.js","names":[],"sources":["../src/modal.ts","../src/utils.ts","../src/button.ts","../src/theme.ts","../src/plugin.ts"],"sourcesContent":["import type { GameObj, KAPLAYCtx } from 'kaplay';\n\nimport { createButton } from './button';\nimport type { ModalOpt, Theme } from './types';\nimport { getLast, isModal, setPaused, toColor, uniqueTag } from './utils';\n\n/** Tag applied to modal game objects. */\nexport const MODAL_TAG = uniqueTag('modal');\n\ntype SizedGameObj = GameObj & {\n height: number;\n width: number;\n};\n\nexport interface ModalHooks {\n isTopmost: (modal: GameObj) => boolean;\n onClose: (modal: GameObj) => void;\n onOpen: (modal: GameObj) => void;\n}\n\nfunction isSized(obj: GameObj): obj is SizedGameObj {\n const sized = obj as Partial<SizedGameObj>;\n return typeof sized.width === 'number' && typeof sized.height === 'number';\n}\n\nexport function createModal(\n k: KAPLAYCtx,\n theme: Theme,\n opt: ModalOpt,\n hooks: ModalHooks,\n) {\n const {\n backdropOpacity,\n borderRadius,\n gap,\n maxWidth,\n minWidth,\n paddingX,\n paddingY,\n } = theme.components.modal;\n const fontSize = theme.typography.fontSize;\n const titleSize = Math.round(fontSize * 1.25);\n const font = theme.typography.fontFamily;\n const dismissible = opt.dismissible ?? true;\n\n const modal = k.add([\n k.pos(k.center()),\n k.fixed(),\n k.z(opt.z ?? 1000),\n MODAL_TAG,\n ]);\n\n modal.onDestroy(() => {\n hooks.onClose(modal);\n });\n\n hooks.onOpen(modal);\n\n const backdrop = modal.add([\n k.rect(k.width(), k.height()),\n k.pos(),\n k.color(0, 0, 0),\n k.opacity(backdropOpacity),\n k.anchor('center'),\n k.area(),\n ]);\n\n const content = modal.add([\n k.rect(0, 0, borderRadius > 0 ? { radius: borderRadius } : undefined),\n k.pos(),\n k.color(toColor(k, theme.palette.surface)),\n k.anchor('center'),\n k.area(),\n ]);\n\n const actions = content.add([k.pos()]);\n\n for (const action of opt.actions ?? []) {\n createButton(k, theme, action.text, {\n parent: actions,\n onClick: () => {\n if (action.close !== false) {\n modal.destroy();\n }\n\n action.onClick?.();\n },\n });\n }\n\n const titleMeasure = opt.title\n ? k.text(opt.title, { size: titleSize, font })\n : undefined;\n const bodyMeasure = opt.text\n ? k.text(opt.text, { size: fontSize, font })\n : undefined;\n const textWrapWidth = Math.max(0, (opt.width ?? maxWidth) - paddingX * 2);\n\n const title = opt.title\n ? content.add([\n k.text(opt.title, {\n size: titleSize,\n font,\n width: textWrapWidth > 0 ? textWrapWidth : undefined,\n align: 'center',\n }),\n k.pos(),\n k.color(toColor(k, theme.palette.text)),\n k.anchor('center'),\n ])\n : undefined;\n\n const body = opt.text\n ? content.add([\n k.text(opt.text, {\n size: fontSize,\n font,\n width: textWrapWidth > 0 ? textWrapWidth : undefined,\n align: 'center',\n }),\n k.pos(),\n k.color(toColor(k, theme.palette.textSecondary)),\n k.anchor('center'),\n ])\n : undefined;\n\n /**\n * Update modal layout:\n *\n * - fits the backdrop to the screen,\n * - sizes the content to its title, text, and actions,\n * - repositions the title, text, and actions.\n */\n function layout() {\n const screenWidth = k.width();\n const screenHeight = k.height();\n\n if (backdrop.width !== screenWidth) {\n backdrop.width = screenWidth;\n }\n\n if (backdrop.height !== screenHeight) {\n backdrop.height = screenHeight;\n }\n\n modal.pos = k.center();\n\n const buttons = actions.children.filter(isSized);\n const actionsWidth =\n buttons.reduce((width, button) => width + button.width, 0) +\n gap * Math.max(0, buttons.length - 1);\n const actionsHeight = Math.max(\n 0,\n ...buttons.map((button) => button.height),\n );\n\n const textContentWidth = Math.max(\n titleMeasure?.width ?? 0,\n bodyMeasure?.width ?? 0,\n );\n const nextWidth =\n opt.width ??\n Math.max(\n Math.min(textContentWidth, maxWidth - paddingX * 2) + paddingX * 2,\n actionsWidth + paddingX * 2,\n minWidth,\n );\n\n const titleHeight = title?.height ?? 0;\n const bodyHeight = body?.height ?? 0;\n const hasTitle = title !== undefined;\n const hasBody = body !== undefined;\n const hasActions = buttons.length > 0;\n const blockCount = Number(hasTitle) + Number(hasBody) + Number(hasActions);\n const contentHeight =\n titleHeight +\n bodyHeight +\n actionsHeight +\n gap * Math.max(0, blockCount - 1);\n const nextHeight = opt.height ?? contentHeight + paddingY * 2;\n\n if (content.width !== nextWidth) {\n content.width = nextWidth;\n }\n\n if (content.height !== nextHeight) {\n content.height = nextHeight;\n }\n\n let y = -nextHeight / 2 + (nextHeight - contentHeight) / 2;\n\n if (title) {\n title.pos = k.vec2(0, y + title.height / 2);\n y += title.height + gap;\n }\n\n if (body) {\n body.pos = k.vec2(0, y + body.height / 2);\n y += body.height + gap;\n }\n\n if (hasActions) {\n let x = -actionsWidth / 2;\n\n for (const button of buttons) {\n button.pos = k.vec2(x + button.width / 2, y + actionsHeight / 2);\n x += button.width + gap;\n }\n }\n }\n\n layout();\n modal.onUpdate(layout);\n\n if (dismissible) {\n // prevent click-through so modal isn't dismissed right after creation\n k.wait(0, () => {\n backdrop.onClick(() => {\n if (!hooks.isTopmost(modal)) {\n return;\n }\n\n if (content.hasPoint(k.mousePos())) {\n return;\n }\n\n modal.destroy();\n });\n });\n\n modal.onKeyPress('escape', () => {\n if (hooks.isTopmost(modal)) {\n modal.destroy();\n }\n });\n }\n\n return Object.assign(modal, {\n actions,\n content,\n });\n}\n\nexport interface ModalManager {\n openModal: ModalHooks['onOpen'];\n closeModal: ModalHooks['onClose'];\n isTopmost: ModalHooks['isTopmost'];\n isInsideModal: (gameObj: GameObj) => boolean;\n}\n\nexport function createModalManager(k: KAPLAYCtx): ModalManager {\n const modals: GameObj[] = [];\n let pausedScene: Map<GameObj, boolean> | undefined;\n let cancelPauseOnAdd: (() => void) | undefined;\n\n const isInsideModal = (gameObj: GameObj) =>\n modals.some((modal) => modal === gameObj || modal.isAncestorOf(gameObj));\n\n function pauseSceneTree(gameObj: GameObj) {\n if (isModal(gameObj) || isInsideModal(gameObj)) {\n return;\n }\n\n pausedScene ??= new Map();\n\n // v8 ignore next\n if (!pausedScene.has(gameObj)) {\n pausedScene.set(gameObj, gameObj.paused);\n }\n\n setPaused(gameObj);\n gameObj.children.forEach(pauseSceneTree);\n }\n\n function openModal(modal: GameObj) {\n if (modals.length > 0) {\n setPaused(getLast(modals), true, true);\n }\n\n modals.push(modal);\n\n if (modals.length === 1) {\n pausedScene = new Map();\n k.getTreeRoot().children.forEach(pauseSceneTree);\n\n const controller = k.onAdd((gameObj) => {\n if (!isModal(gameObj) && !isInsideModal(gameObj)) {\n pauseSceneTree(gameObj);\n }\n });\n\n cancelPauseOnAdd = () => {\n controller.cancel();\n };\n }\n }\n\n function closeModal(modal: GameObj) {\n const index = modals.indexOf(modal);\n\n if (index === -1) {\n return;\n }\n\n const wasTopmost = index === modals.length - 1;\n\n modals.splice(index, 1);\n\n if (!modals.length) {\n cancelPauseOnAdd?.();\n cancelPauseOnAdd = undefined;\n\n // v8 ignore next\n if (pausedScene) {\n for (const [gameObj, paused] of pausedScene) {\n if (gameObj.exists()) {\n setPaused(gameObj, paused);\n }\n }\n\n pausedScene = undefined;\n }\n } else if (wasTopmost) {\n setPaused(getLast(modals), false, true);\n }\n }\n\n const isTopmost = (modal: GameObj) => getLast(modals) === modal;\n\n return { openModal, closeModal, isTopmost, isInsideModal };\n}\n","import type { Color, GameObj, KAPLAYCtx } from 'kaplay';\n\nimport { MODAL_TAG } from './modal';\n\n/**\n * Get the last item in an array.\n *\n * @param items - The items.\n * @returns The last item.\n */\nexport function getLast<T>(items: T[]): T {\n return items[items.length - 1];\n}\n\n/**\n * Check if the game object is a modal.\n *\n * @param gameObj - The game object.\n * @returns Whether game object is a modal.\n */\nexport function isModal(gameObj: GameObj): boolean {\n return gameObj.is(MODAL_TAG);\n}\n\n/**\n * Pause or unpause game object(s).\n *\n * @param gameObj - The root game object.\n * @param isPaused - Whether it should be paused.\n * @param includeTree - Whether to pause all game objects in tree.\n */\nexport function setPaused(\n gameObj: GameObj,\n isPaused = true,\n includeTree = false,\n): void {\n gameObj.paused = isPaused;\n\n if (includeTree) {\n gameObj.children.forEach((childGameObj) => {\n setPaused(childGameObj, isPaused, includeTree);\n });\n }\n}\n\n/**\n * Convert a hex string or color to color.\n *\n * @param k - The Kaplay context.\n * @param color - The hex string or color.\n * @returns The color.\n */\nexport function toColor(k: KAPLAYCtx, color: string | Color): Color {\n return typeof color === 'string' ? k.rgb(color) : color;\n}\n\n/**\n * Generate a unique tag.\n *\n * @param id - The identifier.\n * @returns The tag.\n */\nexport function uniqueTag(id: string): string {\n return [\n 'kaplay-plugin-ui',\n id,\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n crypto?.randomUUID?.(),\n ].join(':');\n}\n","import type { Color, KAPLAYCtx, Vec2 } from 'kaplay';\n\nimport type { ButtonOpt, Theme } from './types';\nimport { toColor } from './utils';\n\nexport function createButton(\n k: KAPLAYCtx,\n theme: Theme,\n text: string,\n opt: ButtonOpt & {\n pos?: Vec2;\n onClick?: () => void;\n color?: string | Color;\n hoverColor?: string | Color;\n pressedColor?: string | Color;\n textColor?: string | Color;\n },\n) {\n const radius = opt.radius ?? theme.shape.borderRadius;\n const { minWidth, paddingX, paddingY } = theme.components.button;\n const baseColor = toColor(k, opt.color ?? opt.fill ?? theme.palette.primary);\n const hoverColor = toColor(k, opt.hoverColor ?? theme.palette.hover);\n const pressedColor = toColor(k, opt.pressedColor ?? theme.palette.pressed);\n const textColor = toColor(k, 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 const textComp = k.text(text, { size, font });\n const isIconOnly = opt.icon !== undefined && !text.length;\n const hasBackground = opt.background !== undefined;\n const showFill = !hasBackground && !isIconOnly;\n const rectOpt = showFill\n ? radius > 0\n ? { radius }\n : undefined\n : { fill: false };\n\n const components = [\n k.rect(0, 0, rectOpt),\n k.pos(pos),\n k.area(),\n k.scale(),\n k.color(baseColor),\n k.anchor('center'),\n ];\n const button = opt.parent ? opt.parent.add(components) : k.add(components);\n\n const background = opt.background\n ? button.add([k.sprite(opt.background), k.pos(), k.anchor('center')])\n : undefined;\n\n const icon = opt.icon\n ? button.add([k.sprite(opt.icon), k.pos(), k.anchor('center')])\n : undefined;\n\n const label = button.add([\n textComp,\n k.color(textColor as never),\n k.pos(),\n k.anchor('center'),\n ]);\n\n let iconWidth = 0;\n let iconHeight = 0;\n let iconState: 'base' | 'hover' | 'pressed' = 'base';\n let backgroundWidth = 0;\n let backgroundHeight = 0;\n let backgroundMeasured = false;\n let backgroundState: 'base' | 'hover' | 'pressed' = 'base';\n\n /**\n * Update button layout:\n *\n * - caches the base icon/background dimensions,\n * - calculates content width/height,\n * - updates `button.width` / `button.height`,\n * - stretches the background and repositions the icon and label.\n */\n function layout() {\n if (icon && iconState === 'base') {\n iconWidth = icon.width;\n iconHeight = icon.height;\n }\n\n if (\n background &&\n backgroundState === 'base' &&\n !backgroundMeasured &&\n (background.width > 0 || background.height > 0)\n ) {\n backgroundWidth = background.width;\n backgroundHeight = background.height;\n backgroundMeasured = true;\n }\n\n const gap = icon && iconWidth > 0 && label.width > 0 ? theme.spacing : 0;\n const contentWidth = iconWidth + gap + label.width;\n const contentHeight = Math.max(iconHeight, label.height);\n const contentBoxWidth = contentWidth + paddingX * 2;\n const contentBoxHeight = contentHeight + paddingY * 2;\n\n const nextWidth =\n opt.width ??\n (background\n ? Math.max(contentBoxWidth, backgroundWidth)\n : Math.max(contentBoxWidth, minWidth));\n const nextHeight =\n opt.height ??\n (background\n ? Math.max(contentBoxHeight, backgroundHeight)\n : contentBoxHeight);\n\n if (button.width !== nextWidth) {\n button.width = nextWidth;\n }\n\n if (button.height !== nextHeight) {\n button.height = nextHeight;\n }\n\n if (background && backgroundMeasured) {\n background.width = nextWidth;\n background.height = nextHeight;\n background.pos = k.vec2();\n }\n\n if (icon) {\n icon.pos = k.vec2(-contentWidth / 2 + iconWidth / 2, 0);\n }\n\n label.pos = k.vec2(\n -contentWidth / 2 + iconWidth + gap + label.width / 2,\n 0,\n );\n }\n\n layout();\n\n if (\n opt.width === undefined ||\n opt.height === undefined ||\n opt.icon !== undefined ||\n opt.background !== undefined\n ) {\n button.onUpdate(layout);\n }\n\n button.onClick(() => {\n // prevent stray click from click-through\n k.wait(0, () => {\n button.color = toColor(k, pressedColor);\n\n if (icon && opt.pressedIcon) {\n iconState = 'pressed';\n icon.sprite = opt.pressedIcon as string;\n }\n\n if (background && opt.pressedBackground) {\n backgroundState = 'pressed';\n background.sprite = opt.pressedBackground as string;\n }\n\n opt.onClick?.();\n });\n });\n\n button.onHover(() => {\n button.color = toColor(k, hoverColor);\n button.scale = k.vec2(1.05, 1.05);\n k.setCursor('pointer');\n\n if (icon && opt.hoverIcon) {\n iconState = 'hover';\n icon.sprite = opt.hoverIcon as string;\n }\n\n if (background && opt.hoverBackground) {\n backgroundState = 'hover';\n background.sprite = opt.hoverBackground as string;\n }\n });\n\n button.onHoverEnd(() => {\n button.color = baseColor;\n button.scale = k.vec2(1, 1);\n k.setCursor('default');\n\n if (icon && opt.icon) {\n iconState = 'base';\n icon.sprite = opt.icon as string;\n }\n\n if (background && opt.background) {\n backgroundState = 'base';\n background.sprite = opt.background as string;\n }\n\n if (icon || background) {\n layout();\n }\n });\n\n return Object.assign(button, {\n background,\n icon,\n label,\n });\n}\n","import type { DeepPartial, Theme } from './types';\n\nconst PRIMARY = '#4f8cff';\nconst TEXT = '#ffffff';\n\n/** Default theme. */\nconst theme: Theme = {\n palette: {\n primary: PRIMARY,\n secondary: PRIMARY,\n background: '#111827',\n surface: '#1f2937',\n text: TEXT,\n textSecondary: TEXT,\n hover: PRIMARY,\n pressed: 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 minWidth: 120,\n paddingX: 12,\n paddingY: 8,\n textTransform: 'none',\n },\n modal: {\n backdropOpacity: 0.5,\n borderRadius: 8,\n gap: 16,\n maxWidth: 480,\n minWidth: 280,\n paddingX: 24,\n paddingY: 20,\n },\n tooltip: {\n background: '#111827',\n textColor: 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: { ...theme.palette, ...input?.palette },\n typography: { ...theme.typography, ...input?.typography },\n shape: { ...theme.shape, ...input?.shape },\n spacing: input?.spacing ?? theme.spacing,\n components: {\n button: {\n ...theme.components.button,\n ...input?.components?.button,\n },\n modal: {\n ...theme.components.modal,\n ...input?.components?.modal,\n },\n tooltip: {\n ...theme.components.tooltip,\n ...input?.components?.tooltip,\n },\n },\n };\n}\n","import type { KAPLAYCtx } from 'kaplay';\n\nimport { createButton } from './button';\nimport { createModal, createModalManager } from './modal';\nimport { resolveTheme } from './theme';\nimport type {\n AddButton,\n AddModal,\n DeepPartial,\n GetTheme,\n SetTheme,\n Theme,\n} from './types';\n\n/**\n * Kaplay UI plugin.\n *\n * @param k - The Kaplay context.\n * @param theme - The theme config.\n * @returns The plugin methods.\n */\nexport function uiPlugin(k: KAPLAYCtx, theme: DeepPartial<Theme> = {}) {\n let userTheme: DeepPartial<Theme> = theme;\n let currentTheme: Theme = resolveTheme(userTheme);\n\n const getTheme: GetTheme = () => currentTheme;\n\n const setTheme: SetTheme = (next) => {\n userTheme = { ...userTheme, ...next };\n currentTheme = resolveTheme(userTheme);\n };\n\n const addButton: AddButton = (text = '', opt = {}) => {\n return createButton(k, currentTheme, text, opt);\n };\n\n const modalManager = createModalManager(k);\n\n const addModal: AddModal = (opt = {}) => {\n return createModal(k, currentTheme, opt, {\n isTopmost: modalManager.isTopmost,\n onClose: modalManager.closeModal,\n onOpen: modalManager.openModal,\n });\n };\n\n return {\n addButton,\n addModal,\n setTheme,\n getTheme,\n };\n}\n"],"mappings":";;;;;;CAOA,MAAa,YAAY,UAAU,OAAO;CAa1C,SAAS,QAAQ,KAAmC;EAClD,MAAM,QAAQ;EACd,OAAO,OAAO,MAAM,UAAU,YAAY,OAAO,MAAM,WAAW;CACpE;CAEA,SAAgB,YACd,GACA,OACA,KACA,OACA;EACA,MAAM,EACJ,iBACA,cACA,KACA,UACA,UACA,UACA,aACE,MAAM,WAAW;EACrB,MAAM,WAAW,MAAM,WAAW;EAClC,MAAM,YAAY,KAAK,MAAM,WAAW,IAAI;EAC5C,MAAM,OAAO,MAAM,WAAW;EAC9B,MAAM,cAAc,IAAI,eAAe;EAEvC,MAAM,QAAQ,EAAE,IAAI;GAClB,EAAE,IAAI,EAAE,OAAO,CAAC;GAChB,EAAE,MAAM;GACR,EAAE,EAAE,IAAI,KAAK,GAAI;GACjB;EACF,CAAC;EAED,MAAM,gBAAgB;GACpB,MAAM,QAAQ,KAAK;EACrB,CAAC;EAED,MAAM,OAAO,KAAK;EAElB,MAAM,WAAW,MAAM,IAAI;GACzB,EAAE,KAAK,EAAE,MAAM,GAAG,EAAE,OAAO,CAAC;GAC5B,EAAE,IAAI;GACN,EAAE,MAAM,GAAG,GAAG,CAAC;GACf,EAAE,QAAQ,eAAe;GACzB,EAAE,OAAO,QAAQ;GACjB,EAAE,KAAK;EACT,CAAC;EAED,MAAM,UAAU,MAAM,IAAI;GACxB,EAAE,KAAK,GAAG,GAAG,eAAe,IAAI,EAAE,QAAQ,aAAa,IAAI,KAAA,CAAS;GACpE,EAAE,IAAI;GACN,EAAE,MAAM,QAAQ,GAAG,MAAM,QAAQ,OAAO,CAAC;GACzC,EAAE,OAAO,QAAQ;GACjB,EAAE,KAAK;EACT,CAAC;EAED,MAAM,UAAU,QAAQ,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC;EAErC,KAAK,MAAM,UAAU,IAAI,WAAW,CAAC,GACnC,aAAa,GAAG,OAAO,OAAO,MAAM;GAClC,QAAQ;GACR,eAAe;IACb,IAAI,OAAO,UAAU,OACnB,MAAM,QAAQ;IAGhB,OAAO,UAAU;GACnB;EACF,CAAC;EAGH,MAAM,eAAe,IAAI,QACrB,EAAE,KAAK,IAAI,OAAO;GAAE,MAAM;GAAW;EAAK,CAAC,IAC3C,KAAA;EACJ,MAAM,cAAc,IAAI,OACpB,EAAE,KAAK,IAAI,MAAM;GAAE,MAAM;GAAU;EAAK,CAAC,IACzC,KAAA;EACJ,MAAM,gBAAgB,KAAK,IAAI,IAAI,IAAI,SAAS,YAAY,WAAW,CAAC;EAExE,MAAM,QAAQ,IAAI,QACd,QAAQ,IAAI;GACV,EAAE,KAAK,IAAI,OAAO;IAChB,MAAM;IACN;IACA,OAAO,gBAAgB,IAAI,gBAAgB,KAAA;IAC3C,OAAO;GACT,CAAC;GACD,EAAE,IAAI;GACN,EAAE,MAAM,QAAQ,GAAG,MAAM,QAAQ,IAAI,CAAC;GACtC,EAAE,OAAO,QAAQ;EACnB,CAAC,IACD,KAAA;EAEJ,MAAM,OAAO,IAAI,OACb,QAAQ,IAAI;GACV,EAAE,KAAK,IAAI,MAAM;IACf,MAAM;IACN;IACA,OAAO,gBAAgB,IAAI,gBAAgB,KAAA;IAC3C,OAAO;GACT,CAAC;GACD,EAAE,IAAI;GACN,EAAE,MAAM,QAAQ,GAAG,MAAM,QAAQ,aAAa,CAAC;GAC/C,EAAE,OAAO,QAAQ;EACnB,CAAC,IACD,KAAA;;;;;;;;EASJ,SAAS,SAAS;GAChB,MAAM,cAAc,EAAE,MAAM;GAC5B,MAAM,eAAe,EAAE,OAAO;GAE9B,IAAI,SAAS,UAAU,aACrB,SAAS,QAAQ;GAGnB,IAAI,SAAS,WAAW,cACtB,SAAS,SAAS;GAGpB,MAAM,MAAM,EAAE,OAAO;GAErB,MAAM,UAAU,QAAQ,SAAS,OAAO,OAAO;GAC/C,MAAM,eACJ,QAAQ,QAAQ,OAAO,WAAW,QAAQ,OAAO,OAAO,CAAC,IACzD,MAAM,KAAK,IAAI,GAAG,QAAQ,SAAS,CAAC;GACtC,MAAM,gBAAgB,KAAK,IACzB,GACA,GAAG,QAAQ,KAAK,WAAW,OAAO,MAAM,CAC1C;GAEA,MAAM,mBAAmB,KAAK,IAC5B,cAAc,SAAS,GACvB,aAAa,SAAS,CACxB;GACA,MAAM,YACJ,IAAI,SACJ,KAAK,IACH,KAAK,IAAI,kBAAkB,WAAW,WAAW,CAAC,IAAI,WAAW,GACjE,eAAe,WAAW,GAC1B,QACF;GAEF,MAAM,cAAc,OAAO,UAAU;GACrC,MAAM,aAAa,MAAM,UAAU;GACnC,MAAM,WAAW,UAAU,KAAA;GAC3B,MAAM,UAAU,SAAS,KAAA;GACzB,MAAM,aAAa,QAAQ,SAAS;GACpC,MAAM,aAAa,OAAO,QAAQ,IAAI,OAAO,OAAO,IAAI,OAAO,UAAU;GACzE,MAAM,gBACJ,cACA,aACA,gBACA,MAAM,KAAK,IAAI,GAAG,aAAa,CAAC;GAClC,MAAM,aAAa,IAAI,UAAU,gBAAgB,WAAW;GAE5D,IAAI,QAAQ,UAAU,WACpB,QAAQ,QAAQ;GAGlB,IAAI,QAAQ,WAAW,YACrB,QAAQ,SAAS;GAGnB,IAAI,IAAI,CAAC,aAAa,KAAK,aAAa,iBAAiB;GAEzD,IAAI,OAAO;IACT,MAAM,MAAM,EAAE,KAAK,GAAG,IAAI,MAAM,SAAS,CAAC;IAC1C,KAAK,MAAM,SAAS;GACtB;GAEA,IAAI,MAAM;IACR,KAAK,MAAM,EAAE,KAAK,GAAG,IAAI,KAAK,SAAS,CAAC;IACxC,KAAK,KAAK,SAAS;GACrB;GAEA,IAAI,YAAY;IACd,IAAI,IAAI,CAAC,eAAe;IAExB,KAAK,MAAM,UAAU,SAAS;KAC5B,OAAO,MAAM,EAAE,KAAK,IAAI,OAAO,QAAQ,GAAG,IAAI,gBAAgB,CAAC;KAC/D,KAAK,OAAO,QAAQ;IACtB;GACF;EACF;EAEA,OAAO;EACP,MAAM,SAAS,MAAM;EAErB,IAAI,aAAa;GAEf,EAAE,KAAK,SAAS;IACd,SAAS,cAAc;KACrB,IAAI,CAAC,MAAM,UAAU,KAAK,GACxB;KAGF,IAAI,QAAQ,SAAS,EAAE,SAAS,CAAC,GAC/B;KAGF,MAAM,QAAQ;IAChB,CAAC;GACH,CAAC;GAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,MAAM,UAAU,KAAK,GACvB,MAAM,QAAQ;GAElB,CAAC;EACH;EAEA,OAAO,OAAO,OAAO,OAAO;GAC1B;GACA;EACF,CAAC;CACH;CASA,SAAgB,mBAAmB,GAA4B;EAC7D,MAAM,SAAoB,CAAC;EAC3B,IAAI;EACJ,IAAI;EAEJ,MAAM,iBAAiB,YACrB,OAAO,MAAM,UAAU,UAAU,WAAW,MAAM,aAAa,OAAO,CAAC;EAEzE,SAAS,eAAe,SAAkB;GACxC,IAAI,QAAQ,OAAO,KAAK,cAAc,OAAO,GAC3C;GAGF,gCAAgB,IAAI,IAAI;;GAGxB,IAAI,CAAC,YAAY,IAAI,OAAO,GAC1B,YAAY,IAAI,SAAS,QAAQ,MAAM;GAGzC,UAAU,OAAO;GACjB,QAAQ,SAAS,QAAQ,cAAc;EACzC;EAEA,SAAS,UAAU,OAAgB;GACjC,IAAI,OAAO,SAAS,GAClB,UAAU,QAAQ,MAAM,GAAG,MAAM,IAAI;GAGvC,OAAO,KAAK,KAAK;GAEjB,IAAI,OAAO,WAAW,GAAG;IACvB,8BAAc,IAAI,IAAI;IACtB,EAAE,YAAY,CAAC,CAAC,SAAS,QAAQ,cAAc;IAE/C,MAAM,aAAa,EAAE,OAAO,YAAY;KACtC,IAAI,CAAC,QAAQ,OAAO,KAAK,CAAC,cAAc,OAAO,GAC7C,eAAe,OAAO;IAE1B,CAAC;IAED,yBAAyB;KACvB,WAAW,OAAO;IACpB;GACF;EACF;EAEA,SAAS,WAAW,OAAgB;GAClC,MAAM,QAAQ,OAAO,QAAQ,KAAK;GAElC,IAAI,UAAU,IACZ;GAGF,MAAM,aAAa,UAAU,OAAO,SAAS;GAE7C,OAAO,OAAO,OAAO,CAAC;GAEtB,IAAI,CAAC,OAAO,QAAQ;IAClB,mBAAmB;IACnB,mBAAmB,KAAA;;IAGnB,IAAI,aAAa;KACf,KAAK,MAAM,CAAC,SAAS,WAAW,aAC9B,IAAI,QAAQ,OAAO,GACjB,UAAU,SAAS,MAAM;KAI7B,cAAc,KAAA;IAChB;GACF,OAAO,IAAI,YACT,UAAU,QAAQ,MAAM,GAAG,OAAO,IAAI;EAE1C;EAEA,MAAM,aAAa,UAAmB,QAAQ,MAAM,MAAM;EAE1D,OAAO;GAAE;GAAW;GAAY;GAAW;EAAc;CAC3D;;;;;;;;CChUA,SAAgB,QAAW,OAAe;EACxC,OAAO,MAAM,MAAM,SAAS;CAC9B;;;;;;;CAQA,SAAgB,QAAQ,SAA2B;EACjD,OAAO,QAAQ,GAAG,SAAS;CAC7B;;;;;;;;CASA,SAAgB,UACd,SACA,WAAW,MACX,cAAc,OACR;EACN,QAAQ,SAAS;EAEjB,IAAI,aACF,QAAQ,SAAS,SAAS,iBAAiB;GACzC,UAAU,cAAc,UAAU,WAAW;EAC/C,CAAC;CAEL;;;;;;;;CASA,SAAgB,QAAQ,GAAc,OAA8B;EAClE,OAAO,OAAO,UAAU,WAAW,EAAE,IAAI,KAAK,IAAI;CACpD;;;;;;;CAQA,SAAgB,UAAU,IAAoB;EAC5C,OAAO;GACL;GACA;GAEA,QAAQ,aAAa;EACvB,CAAC,CAAC,KAAK,GAAG;CACZ;;CChEA,SAAgB,aACd,GACA,OACA,MACA,KAQA;EACA,MAAM,SAAS,IAAI,UAAU,MAAM,MAAM;EACzC,MAAM,EAAE,UAAU,UAAU,aAAa,MAAM,WAAW;EAC1D,MAAM,YAAY,QAAQ,GAAG,IAAI,SAAS,IAAI,QAAQ,MAAM,QAAQ,OAAO;EAC3E,MAAM,aAAa,QAAQ,GAAG,IAAI,cAAc,MAAM,QAAQ,KAAK;EACnE,MAAM,eAAe,QAAQ,GAAG,IAAI,gBAAgB,MAAM,QAAQ,OAAO;EACzE,MAAM,YAAY,QAAQ,GAAG,IAAI,aAAa,MAAM,QAAQ,IAAI;EAChE,MAAM,OAAO,IAAI,QAAQ,MAAM,WAAW;EAC1C,MAAM,OAAO,IAAI,QAAQ,MAAM,WAAW;EAC1C,MAAM,MAAM,IAAI,OAAO,EAAE,KAAK;EAE9B,MAAM,WAAW,EAAE,KAAK,MAAM;GAAE;GAAM;EAAK,CAAC;EAC5C,MAAM,aAAa,IAAI,SAAS,KAAA,KAAa,CAAC,KAAK;EAGnD,MAAM,UADW,EADK,IAAI,eAAe,KAAA,MACN,CAAC,aAEhC,SAAS,IACP,EAAE,OAAO,IACT,KAAA,IACF,EAAE,MAAM,MAAM;EAElB,MAAM,aAAa;GACjB,EAAE,KAAK,GAAG,GAAG,OAAO;GACpB,EAAE,IAAI,GAAG;GACT,EAAE,KAAK;GACP,EAAE,MAAM;GACR,EAAE,MAAM,SAAS;GACjB,EAAE,OAAO,QAAQ;EACnB;EACA,MAAM,SAAS,IAAI,SAAS,IAAI,OAAO,IAAI,UAAU,IAAI,EAAE,IAAI,UAAU;EAEzE,MAAM,aAAa,IAAI,aACnB,OAAO,IAAI;GAAC,EAAE,OAAO,IAAI,UAAU;GAAG,EAAE,IAAI;GAAG,EAAE,OAAO,QAAQ;EAAC,CAAC,IAClE,KAAA;EAEJ,MAAM,OAAO,IAAI,OACb,OAAO,IAAI;GAAC,EAAE,OAAO,IAAI,IAAI;GAAG,EAAE,IAAI;GAAG,EAAE,OAAO,QAAQ;EAAC,CAAC,IAC5D,KAAA;EAEJ,MAAM,QAAQ,OAAO,IAAI;GACvB;GACA,EAAE,MAAM,SAAkB;GAC1B,EAAE,IAAI;GACN,EAAE,OAAO,QAAQ;EACnB,CAAC;EAED,IAAI,YAAY;EAChB,IAAI,aAAa;EACjB,IAAI,YAA0C;EAC9C,IAAI,kBAAkB;EACtB,IAAI,mBAAmB;EACvB,IAAI,qBAAqB;EACzB,IAAI,kBAAgD;;;;;;;;;EAUpD,SAAS,SAAS;GAChB,IAAI,QAAQ,cAAc,QAAQ;IAChC,YAAY,KAAK;IACjB,aAAa,KAAK;GACpB;GAEA,IACE,cACA,oBAAoB,UACpB,CAAC,uBACA,WAAW,QAAQ,KAAK,WAAW,SAAS,IAC7C;IACA,kBAAkB,WAAW;IAC7B,mBAAmB,WAAW;IAC9B,qBAAqB;GACvB;GAEA,MAAM,MAAM,QAAQ,YAAY,KAAK,MAAM,QAAQ,IAAI,MAAM,UAAU;GACvE,MAAM,eAAe,YAAY,MAAM,MAAM;GAC7C,MAAM,gBAAgB,KAAK,IAAI,YAAY,MAAM,MAAM;GACvD,MAAM,kBAAkB,eAAe,WAAW;GAClD,MAAM,mBAAmB,gBAAgB,WAAW;GAEpD,MAAM,YACJ,IAAI,UACH,aACG,KAAK,IAAI,iBAAiB,eAAe,IACzC,KAAK,IAAI,iBAAiB,QAAQ;GACxC,MAAM,aACJ,IAAI,WACH,aACG,KAAK,IAAI,kBAAkB,gBAAgB,IAC3C;GAEN,IAAI,OAAO,UAAU,WACnB,OAAO,QAAQ;GAGjB,IAAI,OAAO,WAAW,YACpB,OAAO,SAAS;GAGlB,IAAI,cAAc,oBAAoB;IACpC,WAAW,QAAQ;IACnB,WAAW,SAAS;IACpB,WAAW,MAAM,EAAE,KAAK;GAC1B;GAEA,IAAI,MACF,KAAK,MAAM,EAAE,KAAK,CAAC,eAAe,IAAI,YAAY,GAAG,CAAC;GAGxD,MAAM,MAAM,EAAE,KACZ,CAAC,eAAe,IAAI,YAAY,MAAM,MAAM,QAAQ,GACpD,CACF;EACF;EAEA,OAAO;EAEP,IACE,IAAI,UAAU,KAAA,KACd,IAAI,WAAW,KAAA,KACf,IAAI,SAAS,KAAA,KACb,IAAI,eAAe,KAAA,GAEnB,OAAO,SAAS,MAAM;EAGxB,OAAO,cAAc;GAEnB,EAAE,KAAK,SAAS;IACd,OAAO,QAAQ,QAAQ,GAAG,YAAY;IAEtC,IAAI,QAAQ,IAAI,aAAa;KAC3B,YAAY;KACZ,KAAK,SAAS,IAAI;IACpB;IAEA,IAAI,cAAc,IAAI,mBAAmB;KACvC,kBAAkB;KAClB,WAAW,SAAS,IAAI;IAC1B;IAEA,IAAI,UAAU;GAChB,CAAC;EACH,CAAC;EAED,OAAO,cAAc;GACnB,OAAO,QAAQ,QAAQ,GAAG,UAAU;GACpC,OAAO,QAAQ,EAAE,KAAK,MAAM,IAAI;GAChC,EAAE,UAAU,SAAS;GAErB,IAAI,QAAQ,IAAI,WAAW;IACzB,YAAY;IACZ,KAAK,SAAS,IAAI;GACpB;GAEA,IAAI,cAAc,IAAI,iBAAiB;IACrC,kBAAkB;IAClB,WAAW,SAAS,IAAI;GAC1B;EACF,CAAC;EAED,OAAO,iBAAiB;GACtB,OAAO,QAAQ;GACf,OAAO,QAAQ,EAAE,KAAK,GAAG,CAAC;GAC1B,EAAE,UAAU,SAAS;GAErB,IAAI,QAAQ,IAAI,MAAM;IACpB,YAAY;IACZ,KAAK,SAAS,IAAI;GACpB;GAEA,IAAI,cAAc,IAAI,YAAY;IAChC,kBAAkB;IAClB,WAAW,SAAS,IAAI;GAC1B;GAEA,IAAI,QAAQ,YACV,OAAO;EAEX,CAAC;EAED,OAAO,OAAO,OAAO,QAAQ;GAC3B;GACA;GACA;EACF,CAAC;CACH;;CC9MA,MAAM,UAAU;CAChB,MAAM,OAAO;;CAGb,MAAM,QAAe;EACnB,SAAS;GACP,SAAS;GACT,WAAW;GACX,YAAY;GACZ,SAAS;GACT,MAAM;GACN,eAAe;GACf,OAAO;GACP,SAAS;GACT,UAAU;GACV,OAAO;EACT;EACA,YAAY;GACV,YAAY;GACZ,UAAU;GACV,YAAY;GACZ,YAAY;EACd;EACA,OAAO,EACL,cAAc,EAChB;EACA,SAAS;EACT,YAAY;GACV,QAAQ;IACN,UAAU;IACV,UAAU;IACV,UAAU;IACV,eAAe;GACjB;GACA,OAAO;IACL,iBAAiB;IACjB,cAAc;IACd,KAAK;IACL,UAAU;IACV,UAAU;IACV,UAAU;IACV,UAAU;GACZ;GACA,SAAS;IACP,YAAY;IACZ,WAAW;IACX,OAAO;GACT;EACF;CACF;;CAGA,SAAgB,aAAa,OAAmC;EAC9D,OAAO;GACL,SAAS;IAAE,GAAG,MAAM;IAAS,GAAG,OAAO;GAAQ;GAC/C,YAAY;IAAE,GAAG,MAAM;IAAY,GAAG,OAAO;GAAW;GACxD,OAAO;IAAE,GAAG,MAAM;IAAO,GAAG,OAAO;GAAM;GACzC,SAAS,OAAO,WAAW,MAAM;GACjC,YAAY;IACV,QAAQ;KACN,GAAG,MAAM,WAAW;KACpB,GAAG,OAAO,YAAY;IACxB;IACA,OAAO;KACL,GAAG,MAAM,WAAW;KACpB,GAAG,OAAO,YAAY;IACxB;IACA,SAAS;KACP,GAAG,MAAM,WAAW;KACpB,GAAG,OAAO,YAAY;IACxB;GACF;EACF;CACF;;;;;;;;;CCtDA,SAAgB,SAAS,GAAc,QAA4B,CAAC,GAAG;EACrE,IAAI,YAAgC;EACpC,IAAI,eAAsB,aAAa,SAAS;EAEhD,MAAM,iBAA2B;EAEjC,MAAM,YAAsB,SAAS;GACnC,YAAY;IAAE,GAAG;IAAW,GAAG;GAAK;GACpC,eAAe,aAAa,SAAS;EACvC;EAEA,MAAM,aAAwB,OAAO,IAAI,MAAM,CAAC,MAAM;GACpD,OAAO,aAAa,GAAG,cAAc,MAAM,GAAG;EAChD;EAEA,MAAM,eAAe,mBAAmB,CAAC;EAEzC,MAAM,YAAsB,MAAM,CAAC,MAAM;GACvC,OAAO,YAAY,GAAG,cAAc,KAAK;IACvC,WAAW,aAAa;IACxB,SAAS,aAAa;IACtB,QAAQ,aAAa;GACvB,CAAC;EACH;EAEA,OAAO;GACL;GACA;GACA;GACA;EACF;CACF"}
|
|
1
|
+
{"version":3,"file":"plugin.umd.js","names":[],"sources":["../src/modal.ts","../src/utils.ts","../src/button.ts","../src/theme.ts","../src/plugin.ts"],"sourcesContent":["import type { GameObj, KAPLAYCtx } from 'kaplay';\n\nimport { createButton } from './button';\nimport type { ModalOpt, Theme } from './types';\nimport { getLast, isModal, setPaused, toColor, uniqueTag } from './utils';\n\n/** Tag applied to modal game objects. */\nexport const MODAL_TAG = uniqueTag('modal');\n\ntype SizedGameObj = GameObj & {\n height: number;\n width: number;\n};\n\nexport interface ModalHooks {\n isTopmost: (modal: GameObj) => boolean;\n onClose: (modal: GameObj) => void;\n onOpen: (modal: GameObj) => void;\n}\n\nfunction isSized(obj: GameObj): obj is SizedGameObj {\n const sized = obj as Partial<SizedGameObj>;\n return typeof sized.width === 'number' && typeof sized.height === 'number';\n}\n\nexport function createModal(\n k: KAPLAYCtx,\n theme: Theme,\n opt: ModalOpt,\n hooks: ModalHooks,\n) {\n const {\n backdropOpacity,\n borderRadius,\n gap,\n maxWidth,\n minWidth,\n paddingX,\n paddingY,\n } = theme.components.modal;\n const fontSize = theme.typography.fontSize;\n const titleSize = Math.round(fontSize * 1.25);\n const font = theme.typography.fontFamily;\n const dismissible = opt.dismissible ?? true;\n\n const modal = k.add([\n k.pos(k.center()),\n k.fixed(),\n k.z(opt.z ?? 1000),\n MODAL_TAG,\n ]);\n\n modal.onDestroy(() => {\n hooks.onClose(modal);\n });\n\n hooks.onOpen(modal);\n\n const backdrop = modal.add([\n k.rect(k.width(), k.height()),\n k.pos(),\n k.color(0, 0, 0),\n k.opacity(backdropOpacity),\n k.anchor('center'),\n k.area(),\n ]);\n\n const content = modal.add([\n k.rect(0, 0, borderRadius > 0 ? { radius: borderRadius } : undefined),\n k.pos(),\n k.color(toColor(k, theme.palette.surface)),\n k.anchor('center'),\n k.area(),\n ]);\n\n const actions = content.add([k.pos()]);\n\n for (const action of opt.actions ?? []) {\n createButton(k, theme, action.text, {\n parent: actions,\n onClick: () => {\n if (action.close !== false) {\n modal.destroy();\n }\n\n action.onClick?.();\n },\n });\n }\n\n const titleMeasure = opt.title\n ? k.text(opt.title, { size: titleSize, font })\n : undefined;\n const bodyMeasure = opt.text\n ? k.text(opt.text, { size: fontSize, font })\n : undefined;\n const textWrapWidth = Math.max(0, (opt.width ?? maxWidth) - paddingX * 2);\n\n const title = opt.title\n ? content.add([\n k.text(opt.title, {\n size: titleSize,\n font,\n width: textWrapWidth > 0 ? textWrapWidth : undefined,\n align: 'center',\n }),\n k.pos(),\n k.color(toColor(k, theme.palette.text)),\n k.anchor('center'),\n ])\n : undefined;\n\n const body = opt.text\n ? content.add([\n k.text(opt.text, {\n size: fontSize,\n font,\n width: textWrapWidth > 0 ? textWrapWidth : undefined,\n align: 'center',\n }),\n k.pos(),\n k.color(toColor(k, theme.palette.textSecondary)),\n k.anchor('center'),\n ])\n : undefined;\n\n /**\n * Update modal layout:\n *\n * - fits the backdrop to the screen,\n * - sizes the content to its title, text, and actions,\n * - repositions the title, text, and actions.\n */\n function layout() {\n const screenWidth = k.width();\n const screenHeight = k.height();\n\n if (backdrop.width !== screenWidth) {\n backdrop.width = screenWidth;\n }\n\n if (backdrop.height !== screenHeight) {\n backdrop.height = screenHeight;\n }\n\n modal.pos = k.center();\n\n const buttons = actions.children.filter(isSized);\n const actionsWidth =\n buttons.reduce((width, button) => width + button.width, 0) +\n gap * Math.max(0, buttons.length - 1);\n const actionsHeight = Math.max(\n 0,\n ...buttons.map((button) => button.height),\n );\n\n const textContentWidth = Math.max(\n titleMeasure?.width ?? 0,\n bodyMeasure?.width ?? 0,\n );\n const nextWidth =\n opt.width ??\n Math.max(\n Math.min(textContentWidth, maxWidth - paddingX * 2) + paddingX * 2,\n actionsWidth + paddingX * 2,\n minWidth,\n );\n\n const titleHeight = title?.height ?? 0;\n const bodyHeight = body?.height ?? 0;\n const hasTitle = title !== undefined;\n const hasBody = body !== undefined;\n const hasActions = buttons.length > 0;\n const blockCount = Number(hasTitle) + Number(hasBody) + Number(hasActions);\n const contentHeight =\n titleHeight +\n bodyHeight +\n actionsHeight +\n gap * Math.max(0, blockCount - 1);\n const nextHeight = opt.height ?? contentHeight + paddingY * 2;\n\n if (content.width !== nextWidth) {\n content.width = nextWidth;\n }\n\n if (content.height !== nextHeight) {\n content.height = nextHeight;\n }\n\n let y = -nextHeight / 2 + (nextHeight - contentHeight) / 2;\n\n if (title) {\n title.pos = k.vec2(0, y + title.height / 2);\n y += title.height + gap;\n }\n\n if (body) {\n body.pos = k.vec2(0, y + body.height / 2);\n y += body.height + gap;\n }\n\n if (hasActions) {\n let x = -actionsWidth / 2;\n\n for (const button of buttons) {\n button.pos = k.vec2(x + button.width / 2, y + actionsHeight / 2);\n x += button.width + gap;\n }\n }\n }\n\n layout();\n modal.onUpdate(layout);\n\n if (dismissible) {\n // prevent click-through so modal isn't dismissed right after creation\n k.wait(0, () => {\n backdrop.onClick(() => {\n if (!hooks.isTopmost(modal)) {\n return;\n }\n\n if (content.hasPoint(k.mousePos())) {\n return;\n }\n\n modal.destroy();\n });\n });\n\n modal.onKeyPress('escape', () => {\n if (hooks.isTopmost(modal)) {\n modal.destroy();\n }\n });\n }\n\n return Object.assign(modal, {\n actions,\n content,\n });\n}\n\nexport interface ModalManager {\n openModal: ModalHooks['onOpen'];\n closeModal: ModalHooks['onClose'];\n isTopmost: ModalHooks['isTopmost'];\n isInsideModal: (gameObj: GameObj) => boolean;\n}\n\nexport function createModalManager(k: KAPLAYCtx): ModalManager {\n const modals: GameObj[] = [];\n let pausedScene: Map<GameObj, boolean> | undefined;\n let cancelPauseOnAdd: (() => void) | undefined;\n\n const isInsideModal = (gameObj: GameObj) =>\n modals.some((modal) => modal === gameObj || modal.isAncestorOf(gameObj));\n\n function pauseSceneTree(gameObj: GameObj) {\n if (isModal(gameObj) || isInsideModal(gameObj)) {\n return;\n }\n\n pausedScene ??= new Map();\n\n // v8 ignore if\n if (!pausedScene.has(gameObj)) {\n pausedScene.set(gameObj, gameObj.paused);\n }\n\n setPaused(gameObj);\n gameObj.children.forEach(pauseSceneTree);\n }\n\n function openModal(modal: GameObj) {\n k.setCursor('default');\n\n if (modals.length > 0) {\n setPaused(getLast(modals), true, true);\n }\n\n modals.push(modal);\n\n if (modals.length === 1) {\n pausedScene = new Map();\n k.getTreeRoot().children.forEach(pauseSceneTree);\n\n const controller = k.onAdd((gameObj) => {\n if (!isModal(gameObj) && !isInsideModal(gameObj)) {\n pauseSceneTree(gameObj);\n }\n });\n\n cancelPauseOnAdd = () => {\n controller.cancel();\n };\n }\n }\n\n function closeModal(modal: GameObj) {\n const index = modals.indexOf(modal);\n\n if (index === -1) {\n return;\n }\n\n const wasTopmost = index === modals.length - 1;\n\n modals.splice(index, 1);\n\n if (!modals.length) {\n cancelPauseOnAdd?.();\n cancelPauseOnAdd = undefined;\n\n // v8 ignore if\n if (pausedScene) {\n for (const [gameObj, paused] of pausedScene) {\n if (gameObj.exists()) {\n setPaused(gameObj, paused);\n }\n }\n\n pausedScene = undefined;\n }\n } else if (wasTopmost) {\n setPaused(getLast(modals), false, true);\n }\n }\n\n const isTopmost = (modal: GameObj) => getLast(modals) === modal;\n\n return { openModal, closeModal, isTopmost, isInsideModal };\n}\n","import type { Color, GameObj, KAPLAYCtx } from 'kaplay';\n\nimport { MODAL_TAG } from './modal';\n\n/**\n * Get the last item in an array.\n *\n * @param items - The items.\n * @returns The last item.\n */\nexport function getLast<T>(items: T[]): T {\n return items[items.length - 1];\n}\n\n/**\n * Check if the game object is a modal.\n *\n * @param gameObj - The game object.\n * @returns Whether game object is a modal.\n */\nexport function isModal(gameObj: GameObj): boolean {\n return gameObj.is(MODAL_TAG);\n}\n\n/**\n * Pause or unpause game object(s).\n *\n * @param gameObj - The root game object.\n * @param isPaused - Whether it should be paused.\n * @param includeTree - Whether to pause all game objects in tree.\n */\nexport function setPaused(\n gameObj: GameObj,\n isPaused = true,\n includeTree = false,\n): void {\n gameObj.paused = isPaused;\n\n if (includeTree) {\n gameObj.children.forEach((childGameObj) => {\n setPaused(childGameObj, isPaused, includeTree);\n });\n }\n}\n\n/**\n * Convert a hex string or color to color.\n *\n * @param k - The Kaplay context.\n * @param color - The hex string or color.\n * @returns The color.\n */\nexport function toColor(k: KAPLAYCtx, color: string | Color): Color {\n return typeof color === 'string' ? k.rgb(color) : color;\n}\n\n/**\n * Generate a unique tag.\n *\n * @param id - The identifier.\n * @returns The tag.\n */\nexport function uniqueTag(id: string): string {\n return [\n 'kaplay-plugin-ui',\n id,\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n crypto?.randomUUID?.(),\n ].join(':');\n}\n","import type { Color, KAPLAYCtx, Vec2 } from 'kaplay';\n\nimport type { ButtonOpt, Theme } from './types';\nimport { toColor } from './utils';\n\nexport function createButton(\n k: KAPLAYCtx,\n theme: Theme,\n text: string,\n opt: ButtonOpt & {\n pos?: Vec2;\n onClick?: () => void;\n color?: string | Color;\n hoverColor?: string | Color;\n pressedColor?: string | Color;\n textColor?: string | Color;\n },\n) {\n const radius = opt.radius ?? theme.shape.borderRadius;\n const { minWidth, paddingX, paddingY } = theme.components.button;\n const baseColor = toColor(k, opt.color ?? opt.fill ?? theme.palette.primary);\n let disabled = opt.disabled ?? false;\n const hoverColor = toColor(k, opt.hoverColor ?? theme.palette.hover);\n const pressedColor = toColor(k, opt.pressedColor ?? theme.palette.pressed);\n const textColor = toColor(k, 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 const textComp = k.text(text, { size, font });\n const isIconOnly = opt.icon !== undefined && !text.length;\n const hasBackground = opt.background !== undefined;\n const showFill = !hasBackground && !isIconOnly;\n const rectOpt = showFill\n ? radius > 0\n ? { radius }\n : undefined\n : { fill: false };\n\n const components = [\n k.rect(0, 0, rectOpt),\n k.pos(pos),\n k.area(),\n k.scale(),\n k.color(disabled ? toColor(k, theme.palette.disabled) : baseColor),\n k.anchor('center'),\n ];\n const button = opt.parent ? opt.parent.add(components) : k.add(components);\n\n const background = opt.background\n ? button.add([k.sprite(opt.background), k.pos(), k.anchor('center')])\n : undefined;\n\n const icon = opt.icon\n ? button.add([k.sprite(opt.icon), k.pos(), k.anchor('center')])\n : undefined;\n\n const label = button.add([\n textComp,\n k.color(textColor as never),\n k.pos(),\n k.anchor('center'),\n ]);\n\n let iconWidth = 0;\n let iconHeight = 0;\n let iconState: 'base' | 'hover' | 'pressed' = 'base';\n let backgroundWidth = 0;\n let backgroundHeight = 0;\n let backgroundMeasured = false;\n let backgroundState: 'base' | 'hover' | 'pressed' = 'base';\n\n /**\n * Update button layout:\n *\n * - caches the base icon/background dimensions,\n * - calculates content width/height,\n * - updates `button.width` / `button.height`,\n * - stretches the background and repositions the icon and label.\n */\n function layout() {\n if (icon && iconState === 'base') {\n iconWidth = icon.width;\n iconHeight = icon.height;\n }\n\n if (\n background &&\n backgroundState === 'base' &&\n !backgroundMeasured &&\n (background.width > 0 || background.height > 0)\n ) {\n backgroundWidth = background.width;\n backgroundHeight = background.height;\n backgroundMeasured = true;\n }\n\n const gap = icon && iconWidth > 0 && label.width > 0 ? theme.spacing : 0;\n const contentWidth = iconWidth + gap + label.width;\n const contentHeight = Math.max(iconHeight, label.height);\n const contentBoxWidth = contentWidth + paddingX * 2;\n const contentBoxHeight = contentHeight + paddingY * 2;\n\n const nextWidth =\n opt.width ??\n (background\n ? Math.max(contentBoxWidth, backgroundWidth)\n : Math.max(contentBoxWidth, minWidth));\n const nextHeight =\n opt.height ??\n (background\n ? Math.max(contentBoxHeight, backgroundHeight)\n : contentBoxHeight);\n\n if (button.width !== nextWidth) {\n button.width = nextWidth;\n }\n\n if (button.height !== nextHeight) {\n button.height = nextHeight;\n }\n\n if (background && backgroundMeasured) {\n background.width = nextWidth;\n background.height = nextHeight;\n background.pos = k.vec2();\n }\n\n if (icon) {\n icon.pos = k.vec2(-contentWidth / 2 + iconWidth / 2, 0);\n }\n\n label.pos = k.vec2(\n -contentWidth / 2 + iconWidth + gap + label.width / 2,\n 0,\n );\n }\n\n layout();\n\n if (\n opt.width === undefined ||\n opt.height === undefined ||\n opt.icon !== undefined ||\n opt.background !== undefined\n ) {\n button.onUpdate(layout);\n }\n\n button.onClick(() => {\n if (disabled) {\n return;\n }\n\n // prevent click-through\n k.wait(0, () => {\n button.color = toColor(k, pressedColor);\n\n if (icon && opt.pressedIcon) {\n iconState = 'pressed';\n icon.sprite = opt.pressedIcon as string;\n }\n\n if (background && opt.pressedBackground) {\n backgroundState = 'pressed';\n background.sprite = opt.pressedBackground as string;\n }\n\n opt.onClick?.();\n\n // v8 ignore if\n if (disabled) {\n k.setCursor('not-allowed');\n } else if (!button.isHovering()) {\n k.setCursor('default');\n }\n });\n });\n\n button.onHover(() => {\n if (disabled) {\n k.setCursor('not-allowed');\n return;\n } else {\n k.setCursor('pointer');\n }\n\n button.color = toColor(k, hoverColor);\n button.scale = k.vec2(1.05, 1.05);\n\n if (icon && opt.hoverIcon) {\n iconState = 'hover';\n icon.sprite = opt.hoverIcon as string;\n }\n\n if (background && opt.hoverBackground) {\n backgroundState = 'hover';\n background.sprite = opt.hoverBackground as string;\n }\n });\n\n button.onHoverEnd(() => {\n k.setCursor('default');\n\n if (disabled) {\n return;\n }\n\n button.color = baseColor;\n button.scale = k.vec2(1, 1);\n\n if (icon && opt.icon) {\n iconState = 'base';\n icon.sprite = opt.icon as string;\n }\n\n if (background && opt.background) {\n backgroundState = 'base';\n background.sprite = opt.background as string;\n }\n\n if (icon || background) {\n layout();\n }\n });\n\n button.onDestroy(() => {\n if (button.isHovering()) {\n k.setCursor('default');\n }\n });\n\n function applyDisabledState() {\n button.color = disabled ? toColor(k, theme.palette.disabled) : baseColor;\n }\n\n applyDisabledState();\n\n const assignedButton = Object.assign(button, {\n background,\n icon,\n label,\n });\n\n const definedButton = Object.defineProperty(assignedButton, 'disabled', {\n get() {\n return disabled;\n },\n set(value: boolean) {\n disabled = value;\n applyDisabledState();\n },\n enumerable: true,\n configurable: true,\n }) as typeof assignedButton & { disabled: boolean };\n\n return definedButton;\n}\n","import type { DeepPartial, Theme } from './types';\n\nconst PRIMARY = '#4f8cff';\nconst TEXT = '#ffffff';\n\n/** Default theme. */\nconst theme: Theme = {\n palette: {\n primary: PRIMARY,\n secondary: PRIMARY,\n background: '#111827',\n surface: '#1f2937',\n text: TEXT,\n textSecondary: TEXT,\n hover: PRIMARY,\n pressed: 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 minWidth: 120,\n paddingX: 12,\n paddingY: 8,\n textTransform: 'none',\n },\n modal: {\n backdropOpacity: 0.5,\n borderRadius: 8,\n gap: 16,\n maxWidth: 480,\n minWidth: 280,\n paddingX: 24,\n paddingY: 20,\n },\n tooltip: {\n background: '#111827',\n textColor: 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: { ...theme.palette, ...input?.palette },\n typography: { ...theme.typography, ...input?.typography },\n shape: { ...theme.shape, ...input?.shape },\n spacing: input?.spacing ?? theme.spacing,\n components: {\n button: {\n ...theme.components.button,\n ...input?.components?.button,\n },\n modal: {\n ...theme.components.modal,\n ...input?.components?.modal,\n },\n tooltip: {\n ...theme.components.tooltip,\n ...input?.components?.tooltip,\n },\n },\n };\n}\n","import type { KAPLAYCtx } from 'kaplay';\n\nimport { createButton } from './button';\nimport { createModal, createModalManager } from './modal';\nimport { resolveTheme } from './theme';\nimport type {\n AddButton,\n AddModal,\n DeepPartial,\n GetTheme,\n SetTheme,\n Theme,\n} from './types';\n\n/**\n * Kaplay UI plugin.\n *\n * @param k - The Kaplay context.\n * @param theme - The theme config.\n * @returns The plugin methods.\n */\nexport function uiPlugin(k: KAPLAYCtx, theme: DeepPartial<Theme> = {}) {\n let userTheme: DeepPartial<Theme> = theme;\n let currentTheme: Theme = resolveTheme(userTheme);\n\n const getTheme: GetTheme = () => currentTheme;\n\n const setTheme: SetTheme = (next) => {\n userTheme = { ...userTheme, ...next };\n currentTheme = resolveTheme(userTheme);\n };\n\n const addButton: AddButton = (text = '', opt = {}) => {\n return createButton(k, currentTheme, text, opt);\n };\n\n const modalManager = createModalManager(k);\n\n const addModal: AddModal = (opt = {}) => {\n return createModal(k, currentTheme, opt, {\n isTopmost: modalManager.isTopmost,\n onClose: modalManager.closeModal,\n onOpen: modalManager.openModal,\n });\n };\n\n return {\n addButton,\n addModal,\n setTheme,\n getTheme,\n };\n}\n"],"mappings":"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"}
|
package/dist/plugin.umd.min.js
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
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`});let t=l(`modal`);function n(e){let t=e;return typeof t.width==`number`&&typeof t.height==`number`}function r(e,r,i,a){let{backdropOpacity:o,borderRadius:s,gap:l,maxWidth:d,minWidth:f,paddingX:p,paddingY:m}=r.components.modal,h=r.typography.fontSize,g=Math.round(h*1.25),_=r.typography.fontFamily,v=i.dismissible??!0,y=e.add([e.pos(e.center()),e.fixed(),e.z(i.z??1e3),t]);y.onDestroy(()=>{a.onClose(y)}),a.onOpen(y);let b=y.add([e.rect(e.width(),e.height()),e.pos(),e.color(0,0,0),e.opacity(o),e.anchor(`center`),e.area()]),x=y.add([e.rect(0,0,s>0?{radius:s}:void 0),e.pos(),e.color(c(e,r.palette.surface)),e.anchor(`center`),e.area()]),S=x.add([e.pos()]);for(let t of i.actions??[])u(e,r,t.text,{parent:S,onClick:()=>{t.close!==!1&&y.destroy(),t.onClick?.()}});let C=i.title?e.text(i.title,{size:g,font:_}):void 0,w=i.text?e.text(i.text,{size:h,font:_}):void 0,T=Math.max(0,(i.width??d)-p*2),E=i.title?x.add([e.text(i.title,{size:g,font:_,width:T>0?T:void 0,align:`center`}),e.pos(),e.color(c(e,r.palette.text)),e.anchor(`center`)]):void 0,D=i.text?x.add([e.text(i.text,{size:h,font:_,width:T>0?T:void 0,align:`center`}),e.pos(),e.color(c(e,r.palette.textSecondary)),e.anchor(`center`)]):void 0;function O(){let t=e.width(),r=e.height();b.width!==t&&(b.width=t),b.height!==r&&(b.height=r),y.pos=e.center();let a=S.children.filter(n),o=a.reduce((e,t)=>e+t.width,0)+l*Math.max(0,a.length-1),s=Math.max(0,...a.map(e=>e.height)),c=Math.max(C?.width??0,w?.width??0),u=i.width??Math.max(Math.min(c,d-p*2)+p*2,o+p*2,f),h=E?.height??0,g=D?.height??0,_=E!==void 0,v=D!==void 0,T=a.length>0,O=Number(_)+Number(v)+Number(T),k=h+g+s+l*Math.max(0,O-1),A=i.height??k+m*2;x.width!==u&&(x.width=u),x.height!==A&&(x.height=A);let j=-A/2+(A-k)/2;if(E&&(E.pos=e.vec2(0,j+E.height/2),j+=E.height+l),D&&(D.pos=e.vec2(0,j+D.height/2),j+=D.height+l),T){let t=-o/2;for(let n of a)n.pos=e.vec2(t+n.width/2,j+s/2),t+=n.width+l}}return O(),y.onUpdate(O),v&&(e.wait(0,()=>{b.onClick(()=>{a.isTopmost(y)&&(x.hasPoint(e.mousePos())||y.destroy())})}),y.onKeyPress(`escape`,()=>{a.isTopmost(y)&&y.destroy()})),Object.assign(y,{actions:S,content:x})}function i(e){let t=[],n,r,i=e=>t.some(t=>t===e||t.isAncestorOf(e));function c(e){o(e)||i(e)||(n??=/* @__PURE__ */ new Map,n.has(e)||n.set(e,e.paused),s(e),e.children.forEach(c))}function l(l){if(t.length>0&&s(a(t),!0,!0),t.push(l),t.length===1){n=/* @__PURE__ */ new Map,e.getTreeRoot().children.forEach(c);let t=e.onAdd(e=>{!o(e)&&!i(e)&&c(e)});r=()=>{t.cancel()}}}function u(e){let i=t.indexOf(e);if(i===-1)return;let o=i===t.length-1;if(t.splice(i,1),t.length)o&&s(a(t),!1,!0);else if(r?.(),r=void 0,n){for(let[e,t]of n)e.exists()&&s(e,t);n=void 0}}return{openModal:l,closeModal:u,isTopmost:e=>a(t)===e,isInsideModal:i}}function a(e){return e[e.length-1]}function o(e){return e.is(t)}function s(e,t=!0,n=!1){e.paused=t,n&&e.children.forEach(e=>{s(e,t,n)})}function c(e,t){return typeof t==`string`?e.rgb(t):t}function l(e){return[`kaplay-plugin-ui`,e,crypto?.randomUUID?.()].join(`:`)}function u(e,t,n,r){let i=r.radius??t.shape.borderRadius,{minWidth:a,paddingX:o,paddingY:s}=t.components.button,l=c(e,r.color??r.fill??t.palette.primary),u=c(e,r.hoverColor??t.palette.hover),
|
|
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`});let t=l(`modal`);function n(e){let t=e;return typeof t.width==`number`&&typeof t.height==`number`}function r(e,r,i,a){let{backdropOpacity:o,borderRadius:s,gap:l,maxWidth:d,minWidth:f,paddingX:p,paddingY:m}=r.components.modal,h=r.typography.fontSize,g=Math.round(h*1.25),_=r.typography.fontFamily,v=i.dismissible??!0,y=e.add([e.pos(e.center()),e.fixed(),e.z(i.z??1e3),t]);y.onDestroy(()=>{a.onClose(y)}),a.onOpen(y);let b=y.add([e.rect(e.width(),e.height()),e.pos(),e.color(0,0,0),e.opacity(o),e.anchor(`center`),e.area()]),x=y.add([e.rect(0,0,s>0?{radius:s}:void 0),e.pos(),e.color(c(e,r.palette.surface)),e.anchor(`center`),e.area()]),S=x.add([e.pos()]);for(let t of i.actions??[])u(e,r,t.text,{parent:S,onClick:()=>{t.close!==!1&&y.destroy(),t.onClick?.()}});let C=i.title?e.text(i.title,{size:g,font:_}):void 0,w=i.text?e.text(i.text,{size:h,font:_}):void 0,T=Math.max(0,(i.width??d)-p*2),E=i.title?x.add([e.text(i.title,{size:g,font:_,width:T>0?T:void 0,align:`center`}),e.pos(),e.color(c(e,r.palette.text)),e.anchor(`center`)]):void 0,D=i.text?x.add([e.text(i.text,{size:h,font:_,width:T>0?T:void 0,align:`center`}),e.pos(),e.color(c(e,r.palette.textSecondary)),e.anchor(`center`)]):void 0;function O(){let t=e.width(),r=e.height();b.width!==t&&(b.width=t),b.height!==r&&(b.height=r),y.pos=e.center();let a=S.children.filter(n),o=a.reduce((e,t)=>e+t.width,0)+l*Math.max(0,a.length-1),s=Math.max(0,...a.map(e=>e.height)),c=Math.max(C?.width??0,w?.width??0),u=i.width??Math.max(Math.min(c,d-p*2)+p*2,o+p*2,f),h=E?.height??0,g=D?.height??0,_=E!==void 0,v=D!==void 0,T=a.length>0,O=Number(_)+Number(v)+Number(T),k=h+g+s+l*Math.max(0,O-1),A=i.height??k+m*2;x.width!==u&&(x.width=u),x.height!==A&&(x.height=A);let j=-A/2+(A-k)/2;if(E&&(E.pos=e.vec2(0,j+E.height/2),j+=E.height+l),D&&(D.pos=e.vec2(0,j+D.height/2),j+=D.height+l),T){let t=-o/2;for(let n of a)n.pos=e.vec2(t+n.width/2,j+s/2),t+=n.width+l}}return O(),y.onUpdate(O),v&&(e.wait(0,()=>{b.onClick(()=>{a.isTopmost(y)&&(x.hasPoint(e.mousePos())||y.destroy())})}),y.onKeyPress(`escape`,()=>{a.isTopmost(y)&&y.destroy()})),Object.assign(y,{actions:S,content:x})}function i(e){let t=[],n,r,i=e=>t.some(t=>t===e||t.isAncestorOf(e));function c(e){o(e)||i(e)||(n??=/* @__PURE__ */ new Map,n.has(e)||n.set(e,e.paused),s(e),e.children.forEach(c))}function l(l){if(e.setCursor(`default`),t.length>0&&s(a(t),!0,!0),t.push(l),t.length===1){n=/* @__PURE__ */ new Map,e.getTreeRoot().children.forEach(c);let t=e.onAdd(e=>{!o(e)&&!i(e)&&c(e)});r=()=>{t.cancel()}}}function u(e){let i=t.indexOf(e);if(i===-1)return;let o=i===t.length-1;if(t.splice(i,1),t.length)o&&s(a(t),!1,!0);else if(r?.(),r=void 0,n){for(let[e,t]of n)e.exists()&&s(e,t);n=void 0}}return{openModal:l,closeModal:u,isTopmost:e=>a(t)===e,isInsideModal:i}}function a(e){return e[e.length-1]}function o(e){return e.is(t)}function s(e,t=!0,n=!1){e.paused=t,n&&e.children.forEach(e=>{s(e,t,n)})}function c(e,t){return typeof t==`string`?e.rgb(t):t}function l(e){return[`kaplay-plugin-ui`,e,crypto?.randomUUID?.()].join(`:`)}function u(e,t,n,r){let i=r.radius??t.shape.borderRadius,{minWidth:a,paddingX:o,paddingY:s}=t.components.button,l=c(e,r.color??r.fill??t.palette.primary),u=r.disabled??!1,d=c(e,r.hoverColor??t.palette.hover),f=c(e,r.pressedColor??t.palette.pressed),p=c(e,r.textColor??t.palette.text),m=r.size??t.typography.fontSize,h=r.font??t.typography.fontFamily,g=r.pos??e.vec2(),_=e.text(n,{size:m,font:h}),v=r.icon!==void 0&&!n.length,y=r.background===void 0&&!v?i>0?{radius:i}:void 0:{fill:!1},b=[e.rect(0,0,y),e.pos(g),e.area(),e.scale(),e.color(u?c(e,t.palette.disabled):l),e.anchor(`center`)],x=r.parent?r.parent.add(b):e.add(b),S=r.background?x.add([e.sprite(r.background),e.pos(),e.anchor(`center`)]):void 0,C=r.icon?x.add([e.sprite(r.icon),e.pos(),e.anchor(`center`)]):void 0,w=x.add([_,e.color(p),e.pos(),e.anchor(`center`)]),T=0,E=0,D=`base`,O=0,k=0,A=!1,j=`base`;function M(){C&&D===`base`&&(T=C.width,E=C.height),S&&j===`base`&&!A&&(S.width>0||S.height>0)&&(O=S.width,k=S.height,A=!0);let n=C&&T>0&&w.width>0?t.spacing:0,i=T+n+w.width,c=Math.max(E,w.height),l=i+o*2,u=c+s*2,d=r.width??(S?Math.max(l,O):Math.max(l,a)),f=r.height??(S?Math.max(u,k):u);x.width!==d&&(x.width=d),x.height!==f&&(x.height=f),S&&A&&(S.width=d,S.height=f,S.pos=e.vec2()),C&&(C.pos=e.vec2(-i/2+T/2,0)),w.pos=e.vec2(-i/2+T+n+w.width/2,0)}M(),(r.width===void 0||r.height===void 0||r.icon!==void 0||r.background!==void 0)&&x.onUpdate(M),x.onClick(()=>{u||e.wait(0,()=>{
|
|
2
|
+
// v8 ignore if
|
|
3
|
+
x.color=c(e,f),C&&r.pressedIcon&&(D=`pressed`,C.sprite=r.pressedIcon),S&&r.pressedBackground&&(j=`pressed`,S.sprite=r.pressedBackground),r.onClick?.(),u?e.setCursor(`not-allowed`):x.isHovering()||e.setCursor(`default`)})}),x.onHover(()=>{u?e.setCursor(`not-allowed`):(e.setCursor(`pointer`),x.color=c(e,d),x.scale=e.vec2(1.05,1.05),C&&r.hoverIcon&&(D=`hover`,C.sprite=r.hoverIcon),S&&r.hoverBackground&&(j=`hover`,S.sprite=r.hoverBackground))}),x.onHoverEnd(()=>{e.setCursor(`default`),!u&&(x.color=l,x.scale=e.vec2(1,1),C&&r.icon&&(D=`base`,C.sprite=r.icon),S&&r.background&&(j=`base`,S.sprite=r.background),(C||S)&&M())}),x.onDestroy(()=>{x.isHovering()&&e.setCursor(`default`)});function N(){x.color=u?c(e,t.palette.disabled):l}N();let P=Object.assign(x,{background:S,icon:C,label:w});return Object.defineProperty(P,"disabled",{get(){return u},set(e){u=e,N()},enumerable:!0,configurable:!0})}let d=`#4f8cff`,f=`#ffffff`,p={palette:{primary:d,secondary:d,background:`#111827`,surface:`#1f2937`,text:f,textSecondary:f,hover:d,pressed:d,disabled:`#6b7280`,error:`#ef4444`},typography:{fontFamily:`sans-serif`,fontSize:18,fontWeight:400,lineHeight:1.2},shape:{borderRadius:0},spacing:8,components:{button:{minWidth:120,paddingX:12,paddingY:8,textTransform:`none`},modal:{backdropOpacity:.5,borderRadius:8,gap:16,maxWidth:480,minWidth:280,paddingX:24,paddingY:20},tooltip:{background:`#111827`,textColor:f,delay:300}}};function m(e){return{palette:{...p.palette,...e?.palette},typography:{...p.typography,...e?.typography},shape:{...p.shape,...e?.shape},spacing:e?.spacing??p.spacing,components:{button:{...p.components.button,...e?.components?.button},modal:{...p.components.modal,...e?.components?.modal},tooltip:{...p.components.tooltip,...e?.components?.tooltip}}}}function h(e,t={}){let n=t,a=m(n),o=()=>a,s=e=>{n={...n,...e},a=m(n)},c=(t=``,n={})=>u(e,a,t,n),l=i(e);return{addButton:c,addModal:(t={})=>r(e,a,t,{isTopmost:l.isTopmost,onClose:l.closeModal,onOpen:l.openModal}),setTheme:s,getTheme:o}}e.uiPlugin=h});
|
|
2
4
|
//# sourceMappingURL=plugin.umd.min.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.umd.min.js","names":["factory","global","exports","MODAL_TAG","uniqueTag","isSized","sized","obj","createModal","theme","fontSize","titleSize","font","dismissible","opt","modal","k","hooks","backdrop","backdropOpacity","content","borderRadius","toColor","actions","action","createButton","titleMeasure","bodyMeasure","textWrapWidth","maxWidth","paddingX","title","body","layout","screenWidth","screenHeight","buttons","actionsWidth","width","button","gap","actionsHeight","textContentWidth","nextWidth","minWidth","titleHeight","bodyHeight","hasTitle","hasBody","hasActions","blockCount","contentHeight","nextHeight","paddingY","y","x","createModalManager","modals","pausedScene","cancelPauseOnAdd","isInsideModal","gameObj","pauseSceneTree","isModal","setPaused","openModal","getLast","controller","closeModal","index","wasTopmost","paused","items","isPaused","includeTree","childGameObj","color","id","radius","baseColor","hoverColor","pressedColor","textColor","size","pos","textComp","text","isIconOnly","rectOpt","components","background","icon","label","iconWidth","iconHeight","iconState","backgroundWidth","backgroundHeight","backgroundMeasured","backgroundState","contentWidth","contentBoxWidth","contentBoxHeight","PRIMARY","TEXT","resolveTheme","input","uiPlugin","userTheme","currentTheme","getTheme","setTheme","next","addButton","modalManager"],"sources":["../src/modal.ts","../src/utils.ts","../src/button.ts","../src/theme.ts","../src/plugin.ts"],"sourcesContent":["import type { GameObj, KAPLAYCtx } from 'kaplay';\n\nimport { createButton } from './button';\nimport type { ModalOpt, Theme } from './types';\nimport { getLast, isModal, setPaused, toColor, uniqueTag } from './utils';\n\n/** Tag applied to modal game objects. */\nexport const MODAL_TAG = uniqueTag('modal');\n\ntype SizedGameObj = GameObj & {\n height: number;\n width: number;\n};\n\nexport interface ModalHooks {\n isTopmost: (modal: GameObj) => boolean;\n onClose: (modal: GameObj) => void;\n onOpen: (modal: GameObj) => void;\n}\n\nfunction isSized(obj: GameObj): obj is SizedGameObj {\n const sized = obj as Partial<SizedGameObj>;\n return typeof sized.width === 'number' && typeof sized.height === 'number';\n}\n\nexport function createModal(\n k: KAPLAYCtx,\n theme: Theme,\n opt: ModalOpt,\n hooks: ModalHooks,\n) {\n const {\n backdropOpacity,\n borderRadius,\n gap,\n maxWidth,\n minWidth,\n paddingX,\n paddingY,\n } = theme.components.modal;\n const fontSize = theme.typography.fontSize;\n const titleSize = Math.round(fontSize * 1.25);\n const font = theme.typography.fontFamily;\n const dismissible = opt.dismissible ?? true;\n\n const modal = k.add([\n k.pos(k.center()),\n k.fixed(),\n k.z(opt.z ?? 1000),\n MODAL_TAG,\n ]);\n\n modal.onDestroy(() => {\n hooks.onClose(modal);\n });\n\n hooks.onOpen(modal);\n\n const backdrop = modal.add([\n k.rect(k.width(), k.height()),\n k.pos(),\n k.color(0, 0, 0),\n k.opacity(backdropOpacity),\n k.anchor('center'),\n k.area(),\n ]);\n\n const content = modal.add([\n k.rect(0, 0, borderRadius > 0 ? { radius: borderRadius } : undefined),\n k.pos(),\n k.color(toColor(k, theme.palette.surface)),\n k.anchor('center'),\n k.area(),\n ]);\n\n const actions = content.add([k.pos()]);\n\n for (const action of opt.actions ?? []) {\n createButton(k, theme, action.text, {\n parent: actions,\n onClick: () => {\n if (action.close !== false) {\n modal.destroy();\n }\n\n action.onClick?.();\n },\n });\n }\n\n const titleMeasure = opt.title\n ? k.text(opt.title, { size: titleSize, font })\n : undefined;\n const bodyMeasure = opt.text\n ? k.text(opt.text, { size: fontSize, font })\n : undefined;\n const textWrapWidth = Math.max(0, (opt.width ?? maxWidth) - paddingX * 2);\n\n const title = opt.title\n ? content.add([\n k.text(opt.title, {\n size: titleSize,\n font,\n width: textWrapWidth > 0 ? textWrapWidth : undefined,\n align: 'center',\n }),\n k.pos(),\n k.color(toColor(k, theme.palette.text)),\n k.anchor('center'),\n ])\n : undefined;\n\n const body = opt.text\n ? content.add([\n k.text(opt.text, {\n size: fontSize,\n font,\n width: textWrapWidth > 0 ? textWrapWidth : undefined,\n align: 'center',\n }),\n k.pos(),\n k.color(toColor(k, theme.palette.textSecondary)),\n k.anchor('center'),\n ])\n : undefined;\n\n /**\n * Update modal layout:\n *\n * - fits the backdrop to the screen,\n * - sizes the content to its title, text, and actions,\n * - repositions the title, text, and actions.\n */\n function layout() {\n const screenWidth = k.width();\n const screenHeight = k.height();\n\n if (backdrop.width !== screenWidth) {\n backdrop.width = screenWidth;\n }\n\n if (backdrop.height !== screenHeight) {\n backdrop.height = screenHeight;\n }\n\n modal.pos = k.center();\n\n const buttons = actions.children.filter(isSized);\n const actionsWidth =\n buttons.reduce((width, button) => width + button.width, 0) +\n gap * Math.max(0, buttons.length - 1);\n const actionsHeight = Math.max(\n 0,\n ...buttons.map((button) => button.height),\n );\n\n const textContentWidth = Math.max(\n titleMeasure?.width ?? 0,\n bodyMeasure?.width ?? 0,\n );\n const nextWidth =\n opt.width ??\n Math.max(\n Math.min(textContentWidth, maxWidth - paddingX * 2) + paddingX * 2,\n actionsWidth + paddingX * 2,\n minWidth,\n );\n\n const titleHeight = title?.height ?? 0;\n const bodyHeight = body?.height ?? 0;\n const hasTitle = title !== undefined;\n const hasBody = body !== undefined;\n const hasActions = buttons.length > 0;\n const blockCount = Number(hasTitle) + Number(hasBody) + Number(hasActions);\n const contentHeight =\n titleHeight +\n bodyHeight +\n actionsHeight +\n gap * Math.max(0, blockCount - 1);\n const nextHeight = opt.height ?? contentHeight + paddingY * 2;\n\n if (content.width !== nextWidth) {\n content.width = nextWidth;\n }\n\n if (content.height !== nextHeight) {\n content.height = nextHeight;\n }\n\n let y = -nextHeight / 2 + (nextHeight - contentHeight) / 2;\n\n if (title) {\n title.pos = k.vec2(0, y + title.height / 2);\n y += title.height + gap;\n }\n\n if (body) {\n body.pos = k.vec2(0, y + body.height / 2);\n y += body.height + gap;\n }\n\n if (hasActions) {\n let x = -actionsWidth / 2;\n\n for (const button of buttons) {\n button.pos = k.vec2(x + button.width / 2, y + actionsHeight / 2);\n x += button.width + gap;\n }\n }\n }\n\n layout();\n modal.onUpdate(layout);\n\n if (dismissible) {\n // prevent click-through so modal isn't dismissed right after creation\n k.wait(0, () => {\n backdrop.onClick(() => {\n if (!hooks.isTopmost(modal)) {\n return;\n }\n\n if (content.hasPoint(k.mousePos())) {\n return;\n }\n\n modal.destroy();\n });\n });\n\n modal.onKeyPress('escape', () => {\n if (hooks.isTopmost(modal)) {\n modal.destroy();\n }\n });\n }\n\n return Object.assign(modal, {\n actions,\n content,\n });\n}\n\nexport interface ModalManager {\n openModal: ModalHooks['onOpen'];\n closeModal: ModalHooks['onClose'];\n isTopmost: ModalHooks['isTopmost'];\n isInsideModal: (gameObj: GameObj) => boolean;\n}\n\nexport function createModalManager(k: KAPLAYCtx): ModalManager {\n const modals: GameObj[] = [];\n let pausedScene: Map<GameObj, boolean> | undefined;\n let cancelPauseOnAdd: (() => void) | undefined;\n\n const isInsideModal = (gameObj: GameObj) =>\n modals.some((modal) => modal === gameObj || modal.isAncestorOf(gameObj));\n\n function pauseSceneTree(gameObj: GameObj) {\n if (isModal(gameObj) || isInsideModal(gameObj)) {\n return;\n }\n\n pausedScene ??= new Map();\n\n // v8 ignore next\n if (!pausedScene.has(gameObj)) {\n pausedScene.set(gameObj, gameObj.paused);\n }\n\n setPaused(gameObj);\n gameObj.children.forEach(pauseSceneTree);\n }\n\n function openModal(modal: GameObj) {\n if (modals.length > 0) {\n setPaused(getLast(modals), true, true);\n }\n\n modals.push(modal);\n\n if (modals.length === 1) {\n pausedScene = new Map();\n k.getTreeRoot().children.forEach(pauseSceneTree);\n\n const controller = k.onAdd((gameObj) => {\n if (!isModal(gameObj) && !isInsideModal(gameObj)) {\n pauseSceneTree(gameObj);\n }\n });\n\n cancelPauseOnAdd = () => {\n controller.cancel();\n };\n }\n }\n\n function closeModal(modal: GameObj) {\n const index = modals.indexOf(modal);\n\n if (index === -1) {\n return;\n }\n\n const wasTopmost = index === modals.length - 1;\n\n modals.splice(index, 1);\n\n if (!modals.length) {\n cancelPauseOnAdd?.();\n cancelPauseOnAdd = undefined;\n\n // v8 ignore next\n if (pausedScene) {\n for (const [gameObj, paused] of pausedScene) {\n if (gameObj.exists()) {\n setPaused(gameObj, paused);\n }\n }\n\n pausedScene = undefined;\n }\n } else if (wasTopmost) {\n setPaused(getLast(modals), false, true);\n }\n }\n\n const isTopmost = (modal: GameObj) => getLast(modals) === modal;\n\n return { openModal, closeModal, isTopmost, isInsideModal };\n}\n","import type { Color, GameObj, KAPLAYCtx } from 'kaplay';\n\nimport { MODAL_TAG } from './modal';\n\n/**\n * Get the last item in an array.\n *\n * @param items - The items.\n * @returns The last item.\n */\nexport function getLast<T>(items: T[]): T {\n return items[items.length - 1];\n}\n\n/**\n * Check if the game object is a modal.\n *\n * @param gameObj - The game object.\n * @returns Whether game object is a modal.\n */\nexport function isModal(gameObj: GameObj): boolean {\n return gameObj.is(MODAL_TAG);\n}\n\n/**\n * Pause or unpause game object(s).\n *\n * @param gameObj - The root game object.\n * @param isPaused - Whether it should be paused.\n * @param includeTree - Whether to pause all game objects in tree.\n */\nexport function setPaused(\n gameObj: GameObj,\n isPaused = true,\n includeTree = false,\n): void {\n gameObj.paused = isPaused;\n\n if (includeTree) {\n gameObj.children.forEach((childGameObj) => {\n setPaused(childGameObj, isPaused, includeTree);\n });\n }\n}\n\n/**\n * Convert a hex string or color to color.\n *\n * @param k - The Kaplay context.\n * @param color - The hex string or color.\n * @returns The color.\n */\nexport function toColor(k: KAPLAYCtx, color: string | Color): Color {\n return typeof color === 'string' ? k.rgb(color) : color;\n}\n\n/**\n * Generate a unique tag.\n *\n * @param id - The identifier.\n * @returns The tag.\n */\nexport function uniqueTag(id: string): string {\n return [\n 'kaplay-plugin-ui',\n id,\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n crypto?.randomUUID?.(),\n ].join(':');\n}\n","import type { Color, KAPLAYCtx, Vec2 } from 'kaplay';\n\nimport type { ButtonOpt, Theme } from './types';\nimport { toColor } from './utils';\n\nexport function createButton(\n k: KAPLAYCtx,\n theme: Theme,\n text: string,\n opt: ButtonOpt & {\n pos?: Vec2;\n onClick?: () => void;\n color?: string | Color;\n hoverColor?: string | Color;\n pressedColor?: string | Color;\n textColor?: string | Color;\n },\n) {\n const radius = opt.radius ?? theme.shape.borderRadius;\n const { minWidth, paddingX, paddingY } = theme.components.button;\n const baseColor = toColor(k, opt.color ?? opt.fill ?? theme.palette.primary);\n const hoverColor = toColor(k, opt.hoverColor ?? theme.palette.hover);\n const pressedColor = toColor(k, opt.pressedColor ?? theme.palette.pressed);\n const textColor = toColor(k, 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 const textComp = k.text(text, { size, font });\n const isIconOnly = opt.icon !== undefined && !text.length;\n const hasBackground = opt.background !== undefined;\n const showFill = !hasBackground && !isIconOnly;\n const rectOpt = showFill\n ? radius > 0\n ? { radius }\n : undefined\n : { fill: false };\n\n const components = [\n k.rect(0, 0, rectOpt),\n k.pos(pos),\n k.area(),\n k.scale(),\n k.color(baseColor),\n k.anchor('center'),\n ];\n const button = opt.parent ? opt.parent.add(components) : k.add(components);\n\n const background = opt.background\n ? button.add([k.sprite(opt.background), k.pos(), k.anchor('center')])\n : undefined;\n\n const icon = opt.icon\n ? button.add([k.sprite(opt.icon), k.pos(), k.anchor('center')])\n : undefined;\n\n const label = button.add([\n textComp,\n k.color(textColor as never),\n k.pos(),\n k.anchor('center'),\n ]);\n\n let iconWidth = 0;\n let iconHeight = 0;\n let iconState: 'base' | 'hover' | 'pressed' = 'base';\n let backgroundWidth = 0;\n let backgroundHeight = 0;\n let backgroundMeasured = false;\n let backgroundState: 'base' | 'hover' | 'pressed' = 'base';\n\n /**\n * Update button layout:\n *\n * - caches the base icon/background dimensions,\n * - calculates content width/height,\n * - updates `button.width` / `button.height`,\n * - stretches the background and repositions the icon and label.\n */\n function layout() {\n if (icon && iconState === 'base') {\n iconWidth = icon.width;\n iconHeight = icon.height;\n }\n\n if (\n background &&\n backgroundState === 'base' &&\n !backgroundMeasured &&\n (background.width > 0 || background.height > 0)\n ) {\n backgroundWidth = background.width;\n backgroundHeight = background.height;\n backgroundMeasured = true;\n }\n\n const gap = icon && iconWidth > 0 && label.width > 0 ? theme.spacing : 0;\n const contentWidth = iconWidth + gap + label.width;\n const contentHeight = Math.max(iconHeight, label.height);\n const contentBoxWidth = contentWidth + paddingX * 2;\n const contentBoxHeight = contentHeight + paddingY * 2;\n\n const nextWidth =\n opt.width ??\n (background\n ? Math.max(contentBoxWidth, backgroundWidth)\n : Math.max(contentBoxWidth, minWidth));\n const nextHeight =\n opt.height ??\n (background\n ? Math.max(contentBoxHeight, backgroundHeight)\n : contentBoxHeight);\n\n if (button.width !== nextWidth) {\n button.width = nextWidth;\n }\n\n if (button.height !== nextHeight) {\n button.height = nextHeight;\n }\n\n if (background && backgroundMeasured) {\n background.width = nextWidth;\n background.height = nextHeight;\n background.pos = k.vec2();\n }\n\n if (icon) {\n icon.pos = k.vec2(-contentWidth / 2 + iconWidth / 2, 0);\n }\n\n label.pos = k.vec2(\n -contentWidth / 2 + iconWidth + gap + label.width / 2,\n 0,\n );\n }\n\n layout();\n\n if (\n opt.width === undefined ||\n opt.height === undefined ||\n opt.icon !== undefined ||\n opt.background !== undefined\n ) {\n button.onUpdate(layout);\n }\n\n button.onClick(() => {\n // prevent stray click from click-through\n k.wait(0, () => {\n button.color = toColor(k, pressedColor);\n\n if (icon && opt.pressedIcon) {\n iconState = 'pressed';\n icon.sprite = opt.pressedIcon as string;\n }\n\n if (background && opt.pressedBackground) {\n backgroundState = 'pressed';\n background.sprite = opt.pressedBackground as string;\n }\n\n opt.onClick?.();\n });\n });\n\n button.onHover(() => {\n button.color = toColor(k, hoverColor);\n button.scale = k.vec2(1.05, 1.05);\n k.setCursor('pointer');\n\n if (icon && opt.hoverIcon) {\n iconState = 'hover';\n icon.sprite = opt.hoverIcon as string;\n }\n\n if (background && opt.hoverBackground) {\n backgroundState = 'hover';\n background.sprite = opt.hoverBackground as string;\n }\n });\n\n button.onHoverEnd(() => {\n button.color = baseColor;\n button.scale = k.vec2(1, 1);\n k.setCursor('default');\n\n if (icon && opt.icon) {\n iconState = 'base';\n icon.sprite = opt.icon as string;\n }\n\n if (background && opt.background) {\n backgroundState = 'base';\n background.sprite = opt.background as string;\n }\n\n if (icon || background) {\n layout();\n }\n });\n\n return Object.assign(button, {\n background,\n icon,\n label,\n });\n}\n","import type { DeepPartial, Theme } from './types';\n\nconst PRIMARY = '#4f8cff';\nconst TEXT = '#ffffff';\n\n/** Default theme. */\nconst theme: Theme = {\n palette: {\n primary: PRIMARY,\n secondary: PRIMARY,\n background: '#111827',\n surface: '#1f2937',\n text: TEXT,\n textSecondary: TEXT,\n hover: PRIMARY,\n pressed: 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 minWidth: 120,\n paddingX: 12,\n paddingY: 8,\n textTransform: 'none',\n },\n modal: {\n backdropOpacity: 0.5,\n borderRadius: 8,\n gap: 16,\n maxWidth: 480,\n minWidth: 280,\n paddingX: 24,\n paddingY: 20,\n },\n tooltip: {\n background: '#111827',\n textColor: 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: { ...theme.palette, ...input?.palette },\n typography: { ...theme.typography, ...input?.typography },\n shape: { ...theme.shape, ...input?.shape },\n spacing: input?.spacing ?? theme.spacing,\n components: {\n button: {\n ...theme.components.button,\n ...input?.components?.button,\n },\n modal: {\n ...theme.components.modal,\n ...input?.components?.modal,\n },\n tooltip: {\n ...theme.components.tooltip,\n ...input?.components?.tooltip,\n },\n },\n };\n}\n","import type { KAPLAYCtx } from 'kaplay';\n\nimport { createButton } from './button';\nimport { createModal, createModalManager } from './modal';\nimport { resolveTheme } from './theme';\nimport type {\n AddButton,\n AddModal,\n DeepPartial,\n GetTheme,\n SetTheme,\n Theme,\n} from './types';\n\n/**\n * Kaplay UI plugin.\n *\n * @param k - The Kaplay context.\n * @param theme - The theme config.\n * @returns The plugin methods.\n */\nexport function uiPlugin(k: KAPLAYCtx, theme: DeepPartial<Theme> = {}) {\n let userTheme: DeepPartial<Theme> = theme;\n let currentTheme: Theme = resolveTheme(userTheme);\n\n const getTheme: GetTheme = () => currentTheme;\n\n const setTheme: SetTheme = (next) => {\n userTheme = { ...userTheme, ...next };\n currentTheme = resolveTheme(userTheme);\n };\n\n const addButton: AddButton = (text = '', opt = {}) => {\n return createButton(k, currentTheme, text, opt);\n };\n\n const modalManager = createModalManager(k);\n\n const addModal: AddModal = (opt = {}) => {\n return createModal(k, currentTheme, opt, {\n isTopmost: modalManager.isTopmost,\n onClose: modalManager.closeModal,\n onOpen: modalManager.openModal,\n });\n };\n\n return {\n addButton,\n addModal,\n setTheme,\n getTheme,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"plugin.umd.min.js","names":["factory","global","exports","MODAL_TAG","uniqueTag","isSized","sized","obj","createModal","theme","fontSize","titleSize","font","dismissible","opt","modal","k","hooks","backdrop","backdropOpacity","content","borderRadius","toColor","actions","action","createButton","titleMeasure","bodyMeasure","textWrapWidth","maxWidth","paddingX","title","body","layout","screenWidth","screenHeight","buttons","actionsWidth","width","button","gap","actionsHeight","textContentWidth","nextWidth","minWidth","titleHeight","bodyHeight","hasTitle","hasBody","hasActions","blockCount","contentHeight","nextHeight","paddingY","y","x","createModalManager","modals","pausedScene","cancelPauseOnAdd","isInsideModal","gameObj","pauseSceneTree","isModal","setPaused","openModal","getLast","controller","closeModal","index","wasTopmost","paused","items","isPaused","includeTree","childGameObj","color","id","radius","baseColor","disabled","hoverColor","pressedColor","textColor","size","pos","textComp","text","isIconOnly","rectOpt","components","background","icon","label","iconWidth","iconHeight","iconState","backgroundWidth","backgroundHeight","backgroundMeasured","backgroundState","contentWidth","contentBoxWidth","contentBoxHeight","applyDisabledState","assignedButton","value","PRIMARY","TEXT","resolveTheme","input","uiPlugin","userTheme","currentTheme","getTheme","setTheme","next","addButton","modalManager"],"sources":["../src/modal.ts","../src/utils.ts","../src/button.ts","../src/theme.ts","../src/plugin.ts"],"sourcesContent":["import type { GameObj, KAPLAYCtx } from 'kaplay';\n\nimport { createButton } from './button';\nimport type { ModalOpt, Theme } from './types';\nimport { getLast, isModal, setPaused, toColor, uniqueTag } from './utils';\n\n/** Tag applied to modal game objects. */\nexport const MODAL_TAG = uniqueTag('modal');\n\ntype SizedGameObj = GameObj & {\n height: number;\n width: number;\n};\n\nexport interface ModalHooks {\n isTopmost: (modal: GameObj) => boolean;\n onClose: (modal: GameObj) => void;\n onOpen: (modal: GameObj) => void;\n}\n\nfunction isSized(obj: GameObj): obj is SizedGameObj {\n const sized = obj as Partial<SizedGameObj>;\n return typeof sized.width === 'number' && typeof sized.height === 'number';\n}\n\nexport function createModal(\n k: KAPLAYCtx,\n theme: Theme,\n opt: ModalOpt,\n hooks: ModalHooks,\n) {\n const {\n backdropOpacity,\n borderRadius,\n gap,\n maxWidth,\n minWidth,\n paddingX,\n paddingY,\n } = theme.components.modal;\n const fontSize = theme.typography.fontSize;\n const titleSize = Math.round(fontSize * 1.25);\n const font = theme.typography.fontFamily;\n const dismissible = opt.dismissible ?? true;\n\n const modal = k.add([\n k.pos(k.center()),\n k.fixed(),\n k.z(opt.z ?? 1000),\n MODAL_TAG,\n ]);\n\n modal.onDestroy(() => {\n hooks.onClose(modal);\n });\n\n hooks.onOpen(modal);\n\n const backdrop = modal.add([\n k.rect(k.width(), k.height()),\n k.pos(),\n k.color(0, 0, 0),\n k.opacity(backdropOpacity),\n k.anchor('center'),\n k.area(),\n ]);\n\n const content = modal.add([\n k.rect(0, 0, borderRadius > 0 ? { radius: borderRadius } : undefined),\n k.pos(),\n k.color(toColor(k, theme.palette.surface)),\n k.anchor('center'),\n k.area(),\n ]);\n\n const actions = content.add([k.pos()]);\n\n for (const action of opt.actions ?? []) {\n createButton(k, theme, action.text, {\n parent: actions,\n onClick: () => {\n if (action.close !== false) {\n modal.destroy();\n }\n\n action.onClick?.();\n },\n });\n }\n\n const titleMeasure = opt.title\n ? k.text(opt.title, { size: titleSize, font })\n : undefined;\n const bodyMeasure = opt.text\n ? k.text(opt.text, { size: fontSize, font })\n : undefined;\n const textWrapWidth = Math.max(0, (opt.width ?? maxWidth) - paddingX * 2);\n\n const title = opt.title\n ? content.add([\n k.text(opt.title, {\n size: titleSize,\n font,\n width: textWrapWidth > 0 ? textWrapWidth : undefined,\n align: 'center',\n }),\n k.pos(),\n k.color(toColor(k, theme.palette.text)),\n k.anchor('center'),\n ])\n : undefined;\n\n const body = opt.text\n ? content.add([\n k.text(opt.text, {\n size: fontSize,\n font,\n width: textWrapWidth > 0 ? textWrapWidth : undefined,\n align: 'center',\n }),\n k.pos(),\n k.color(toColor(k, theme.palette.textSecondary)),\n k.anchor('center'),\n ])\n : undefined;\n\n /**\n * Update modal layout:\n *\n * - fits the backdrop to the screen,\n * - sizes the content to its title, text, and actions,\n * - repositions the title, text, and actions.\n */\n function layout() {\n const screenWidth = k.width();\n const screenHeight = k.height();\n\n if (backdrop.width !== screenWidth) {\n backdrop.width = screenWidth;\n }\n\n if (backdrop.height !== screenHeight) {\n backdrop.height = screenHeight;\n }\n\n modal.pos = k.center();\n\n const buttons = actions.children.filter(isSized);\n const actionsWidth =\n buttons.reduce((width, button) => width + button.width, 0) +\n gap * Math.max(0, buttons.length - 1);\n const actionsHeight = Math.max(\n 0,\n ...buttons.map((button) => button.height),\n );\n\n const textContentWidth = Math.max(\n titleMeasure?.width ?? 0,\n bodyMeasure?.width ?? 0,\n );\n const nextWidth =\n opt.width ??\n Math.max(\n Math.min(textContentWidth, maxWidth - paddingX * 2) + paddingX * 2,\n actionsWidth + paddingX * 2,\n minWidth,\n );\n\n const titleHeight = title?.height ?? 0;\n const bodyHeight = body?.height ?? 0;\n const hasTitle = title !== undefined;\n const hasBody = body !== undefined;\n const hasActions = buttons.length > 0;\n const blockCount = Number(hasTitle) + Number(hasBody) + Number(hasActions);\n const contentHeight =\n titleHeight +\n bodyHeight +\n actionsHeight +\n gap * Math.max(0, blockCount - 1);\n const nextHeight = opt.height ?? contentHeight + paddingY * 2;\n\n if (content.width !== nextWidth) {\n content.width = nextWidth;\n }\n\n if (content.height !== nextHeight) {\n content.height = nextHeight;\n }\n\n let y = -nextHeight / 2 + (nextHeight - contentHeight) / 2;\n\n if (title) {\n title.pos = k.vec2(0, y + title.height / 2);\n y += title.height + gap;\n }\n\n if (body) {\n body.pos = k.vec2(0, y + body.height / 2);\n y += body.height + gap;\n }\n\n if (hasActions) {\n let x = -actionsWidth / 2;\n\n for (const button of buttons) {\n button.pos = k.vec2(x + button.width / 2, y + actionsHeight / 2);\n x += button.width + gap;\n }\n }\n }\n\n layout();\n modal.onUpdate(layout);\n\n if (dismissible) {\n // prevent click-through so modal isn't dismissed right after creation\n k.wait(0, () => {\n backdrop.onClick(() => {\n if (!hooks.isTopmost(modal)) {\n return;\n }\n\n if (content.hasPoint(k.mousePos())) {\n return;\n }\n\n modal.destroy();\n });\n });\n\n modal.onKeyPress('escape', () => {\n if (hooks.isTopmost(modal)) {\n modal.destroy();\n }\n });\n }\n\n return Object.assign(modal, {\n actions,\n content,\n });\n}\n\nexport interface ModalManager {\n openModal: ModalHooks['onOpen'];\n closeModal: ModalHooks['onClose'];\n isTopmost: ModalHooks['isTopmost'];\n isInsideModal: (gameObj: GameObj) => boolean;\n}\n\nexport function createModalManager(k: KAPLAYCtx): ModalManager {\n const modals: GameObj[] = [];\n let pausedScene: Map<GameObj, boolean> | undefined;\n let cancelPauseOnAdd: (() => void) | undefined;\n\n const isInsideModal = (gameObj: GameObj) =>\n modals.some((modal) => modal === gameObj || modal.isAncestorOf(gameObj));\n\n function pauseSceneTree(gameObj: GameObj) {\n if (isModal(gameObj) || isInsideModal(gameObj)) {\n return;\n }\n\n pausedScene ??= new Map();\n\n // v8 ignore if\n if (!pausedScene.has(gameObj)) {\n pausedScene.set(gameObj, gameObj.paused);\n }\n\n setPaused(gameObj);\n gameObj.children.forEach(pauseSceneTree);\n }\n\n function openModal(modal: GameObj) {\n k.setCursor('default');\n\n if (modals.length > 0) {\n setPaused(getLast(modals), true, true);\n }\n\n modals.push(modal);\n\n if (modals.length === 1) {\n pausedScene = new Map();\n k.getTreeRoot().children.forEach(pauseSceneTree);\n\n const controller = k.onAdd((gameObj) => {\n if (!isModal(gameObj) && !isInsideModal(gameObj)) {\n pauseSceneTree(gameObj);\n }\n });\n\n cancelPauseOnAdd = () => {\n controller.cancel();\n };\n }\n }\n\n function closeModal(modal: GameObj) {\n const index = modals.indexOf(modal);\n\n if (index === -1) {\n return;\n }\n\n const wasTopmost = index === modals.length - 1;\n\n modals.splice(index, 1);\n\n if (!modals.length) {\n cancelPauseOnAdd?.();\n cancelPauseOnAdd = undefined;\n\n // v8 ignore if\n if (pausedScene) {\n for (const [gameObj, paused] of pausedScene) {\n if (gameObj.exists()) {\n setPaused(gameObj, paused);\n }\n }\n\n pausedScene = undefined;\n }\n } else if (wasTopmost) {\n setPaused(getLast(modals), false, true);\n }\n }\n\n const isTopmost = (modal: GameObj) => getLast(modals) === modal;\n\n return { openModal, closeModal, isTopmost, isInsideModal };\n}\n","import type { Color, GameObj, KAPLAYCtx } from 'kaplay';\n\nimport { MODAL_TAG } from './modal';\n\n/**\n * Get the last item in an array.\n *\n * @param items - The items.\n * @returns The last item.\n */\nexport function getLast<T>(items: T[]): T {\n return items[items.length - 1];\n}\n\n/**\n * Check if the game object is a modal.\n *\n * @param gameObj - The game object.\n * @returns Whether game object is a modal.\n */\nexport function isModal(gameObj: GameObj): boolean {\n return gameObj.is(MODAL_TAG);\n}\n\n/**\n * Pause or unpause game object(s).\n *\n * @param gameObj - The root game object.\n * @param isPaused - Whether it should be paused.\n * @param includeTree - Whether to pause all game objects in tree.\n */\nexport function setPaused(\n gameObj: GameObj,\n isPaused = true,\n includeTree = false,\n): void {\n gameObj.paused = isPaused;\n\n if (includeTree) {\n gameObj.children.forEach((childGameObj) => {\n setPaused(childGameObj, isPaused, includeTree);\n });\n }\n}\n\n/**\n * Convert a hex string or color to color.\n *\n * @param k - The Kaplay context.\n * @param color - The hex string or color.\n * @returns The color.\n */\nexport function toColor(k: KAPLAYCtx, color: string | Color): Color {\n return typeof color === 'string' ? k.rgb(color) : color;\n}\n\n/**\n * Generate a unique tag.\n *\n * @param id - The identifier.\n * @returns The tag.\n */\nexport function uniqueTag(id: string): string {\n return [\n 'kaplay-plugin-ui',\n id,\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n crypto?.randomUUID?.(),\n ].join(':');\n}\n","import type { Color, KAPLAYCtx, Vec2 } from 'kaplay';\n\nimport type { ButtonOpt, Theme } from './types';\nimport { toColor } from './utils';\n\nexport function createButton(\n k: KAPLAYCtx,\n theme: Theme,\n text: string,\n opt: ButtonOpt & {\n pos?: Vec2;\n onClick?: () => void;\n color?: string | Color;\n hoverColor?: string | Color;\n pressedColor?: string | Color;\n textColor?: string | Color;\n },\n) {\n const radius = opt.radius ?? theme.shape.borderRadius;\n const { minWidth, paddingX, paddingY } = theme.components.button;\n const baseColor = toColor(k, opt.color ?? opt.fill ?? theme.palette.primary);\n let disabled = opt.disabled ?? false;\n const hoverColor = toColor(k, opt.hoverColor ?? theme.palette.hover);\n const pressedColor = toColor(k, opt.pressedColor ?? theme.palette.pressed);\n const textColor = toColor(k, 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 const textComp = k.text(text, { size, font });\n const isIconOnly = opt.icon !== undefined && !text.length;\n const hasBackground = opt.background !== undefined;\n const showFill = !hasBackground && !isIconOnly;\n const rectOpt = showFill\n ? radius > 0\n ? { radius }\n : undefined\n : { fill: false };\n\n const components = [\n k.rect(0, 0, rectOpt),\n k.pos(pos),\n k.area(),\n k.scale(),\n k.color(disabled ? toColor(k, theme.palette.disabled) : baseColor),\n k.anchor('center'),\n ];\n const button = opt.parent ? opt.parent.add(components) : k.add(components);\n\n const background = opt.background\n ? button.add([k.sprite(opt.background), k.pos(), k.anchor('center')])\n : undefined;\n\n const icon = opt.icon\n ? button.add([k.sprite(opt.icon), k.pos(), k.anchor('center')])\n : undefined;\n\n const label = button.add([\n textComp,\n k.color(textColor as never),\n k.pos(),\n k.anchor('center'),\n ]);\n\n let iconWidth = 0;\n let iconHeight = 0;\n let iconState: 'base' | 'hover' | 'pressed' = 'base';\n let backgroundWidth = 0;\n let backgroundHeight = 0;\n let backgroundMeasured = false;\n let backgroundState: 'base' | 'hover' | 'pressed' = 'base';\n\n /**\n * Update button layout:\n *\n * - caches the base icon/background dimensions,\n * - calculates content width/height,\n * - updates `button.width` / `button.height`,\n * - stretches the background and repositions the icon and label.\n */\n function layout() {\n if (icon && iconState === 'base') {\n iconWidth = icon.width;\n iconHeight = icon.height;\n }\n\n if (\n background &&\n backgroundState === 'base' &&\n !backgroundMeasured &&\n (background.width > 0 || background.height > 0)\n ) {\n backgroundWidth = background.width;\n backgroundHeight = background.height;\n backgroundMeasured = true;\n }\n\n const gap = icon && iconWidth > 0 && label.width > 0 ? theme.spacing : 0;\n const contentWidth = iconWidth + gap + label.width;\n const contentHeight = Math.max(iconHeight, label.height);\n const contentBoxWidth = contentWidth + paddingX * 2;\n const contentBoxHeight = contentHeight + paddingY * 2;\n\n const nextWidth =\n opt.width ??\n (background\n ? Math.max(contentBoxWidth, backgroundWidth)\n : Math.max(contentBoxWidth, minWidth));\n const nextHeight =\n opt.height ??\n (background\n ? Math.max(contentBoxHeight, backgroundHeight)\n : contentBoxHeight);\n\n if (button.width !== nextWidth) {\n button.width = nextWidth;\n }\n\n if (button.height !== nextHeight) {\n button.height = nextHeight;\n }\n\n if (background && backgroundMeasured) {\n background.width = nextWidth;\n background.height = nextHeight;\n background.pos = k.vec2();\n }\n\n if (icon) {\n icon.pos = k.vec2(-contentWidth / 2 + iconWidth / 2, 0);\n }\n\n label.pos = k.vec2(\n -contentWidth / 2 + iconWidth + gap + label.width / 2,\n 0,\n );\n }\n\n layout();\n\n if (\n opt.width === undefined ||\n opt.height === undefined ||\n opt.icon !== undefined ||\n opt.background !== undefined\n ) {\n button.onUpdate(layout);\n }\n\n button.onClick(() => {\n if (disabled) {\n return;\n }\n\n // prevent click-through\n k.wait(0, () => {\n button.color = toColor(k, pressedColor);\n\n if (icon && opt.pressedIcon) {\n iconState = 'pressed';\n icon.sprite = opt.pressedIcon as string;\n }\n\n if (background && opt.pressedBackground) {\n backgroundState = 'pressed';\n background.sprite = opt.pressedBackground as string;\n }\n\n opt.onClick?.();\n\n // v8 ignore if\n if (disabled) {\n k.setCursor('not-allowed');\n } else if (!button.isHovering()) {\n k.setCursor('default');\n }\n });\n });\n\n button.onHover(() => {\n if (disabled) {\n k.setCursor('not-allowed');\n return;\n } else {\n k.setCursor('pointer');\n }\n\n button.color = toColor(k, hoverColor);\n button.scale = k.vec2(1.05, 1.05);\n\n if (icon && opt.hoverIcon) {\n iconState = 'hover';\n icon.sprite = opt.hoverIcon as string;\n }\n\n if (background && opt.hoverBackground) {\n backgroundState = 'hover';\n background.sprite = opt.hoverBackground as string;\n }\n });\n\n button.onHoverEnd(() => {\n k.setCursor('default');\n\n if (disabled) {\n return;\n }\n\n button.color = baseColor;\n button.scale = k.vec2(1, 1);\n\n if (icon && opt.icon) {\n iconState = 'base';\n icon.sprite = opt.icon as string;\n }\n\n if (background && opt.background) {\n backgroundState = 'base';\n background.sprite = opt.background as string;\n }\n\n if (icon || background) {\n layout();\n }\n });\n\n button.onDestroy(() => {\n if (button.isHovering()) {\n k.setCursor('default');\n }\n });\n\n function applyDisabledState() {\n button.color = disabled ? toColor(k, theme.palette.disabled) : baseColor;\n }\n\n applyDisabledState();\n\n const assignedButton = Object.assign(button, {\n background,\n icon,\n label,\n });\n\n const definedButton = Object.defineProperty(assignedButton, 'disabled', {\n get() {\n return disabled;\n },\n set(value: boolean) {\n disabled = value;\n applyDisabledState();\n },\n enumerable: true,\n configurable: true,\n }) as typeof assignedButton & { disabled: boolean };\n\n return definedButton;\n}\n","import type { DeepPartial, Theme } from './types';\n\nconst PRIMARY = '#4f8cff';\nconst TEXT = '#ffffff';\n\n/** Default theme. */\nconst theme: Theme = {\n palette: {\n primary: PRIMARY,\n secondary: PRIMARY,\n background: '#111827',\n surface: '#1f2937',\n text: TEXT,\n textSecondary: TEXT,\n hover: PRIMARY,\n pressed: 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 minWidth: 120,\n paddingX: 12,\n paddingY: 8,\n textTransform: 'none',\n },\n modal: {\n backdropOpacity: 0.5,\n borderRadius: 8,\n gap: 16,\n maxWidth: 480,\n minWidth: 280,\n paddingX: 24,\n paddingY: 20,\n },\n tooltip: {\n background: '#111827',\n textColor: 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: { ...theme.palette, ...input?.palette },\n typography: { ...theme.typography, ...input?.typography },\n shape: { ...theme.shape, ...input?.shape },\n spacing: input?.spacing ?? theme.spacing,\n components: {\n button: {\n ...theme.components.button,\n ...input?.components?.button,\n },\n modal: {\n ...theme.components.modal,\n ...input?.components?.modal,\n },\n tooltip: {\n ...theme.components.tooltip,\n ...input?.components?.tooltip,\n },\n },\n };\n}\n","import type { KAPLAYCtx } from 'kaplay';\n\nimport { createButton } from './button';\nimport { createModal, createModalManager } from './modal';\nimport { resolveTheme } from './theme';\nimport type {\n AddButton,\n AddModal,\n DeepPartial,\n GetTheme,\n SetTheme,\n Theme,\n} from './types';\n\n/**\n * Kaplay UI plugin.\n *\n * @param k - The Kaplay context.\n * @param theme - The theme config.\n * @returns The plugin methods.\n */\nexport function uiPlugin(k: KAPLAYCtx, theme: DeepPartial<Theme> = {}) {\n let userTheme: DeepPartial<Theme> = theme;\n let currentTheme: Theme = resolveTheme(userTheme);\n\n const getTheme: GetTheme = () => currentTheme;\n\n const setTheme: SetTheme = (next) => {\n userTheme = { ...userTheme, ...next };\n currentTheme = resolveTheme(userTheme);\n };\n\n const addButton: AddButton = (text = '', opt = {}) => {\n return createButton(k, currentTheme, text, opt);\n };\n\n const modalManager = createModalManager(k);\n\n const addModal: AddModal = (opt = {}) => {\n return createModal(k, currentTheme, opt, {\n isTopmost: modalManager.isTopmost,\n onClose: modalManager.closeModal,\n onOpen: modalManager.openModal,\n });\n };\n\n return {\n addButton,\n addModal,\n setTheme,\n getTheme,\n };\n}\n"],"mappings":"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"}
|