@rpgjs/client 5.0.0-beta.24 → 5.0.0-beta.25
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/CHANGELOG.md +16 -0
- package/dist/Gui/Gui.js +5 -0
- package/dist/Gui/Gui.js.map +1 -1
- package/dist/components/gui/dialogbox/index.ce.js +23 -2
- package/dist/components/gui/dialogbox/index.ce.js.map +1 -1
- package/dist/components/gui/index.d.ts +3 -1
- package/dist/components/gui/index.js +2 -0
- package/dist/components/gui/input-field.ce.js +123 -0
- package/dist/components/gui/input-field.ce.js.map +1 -0
- package/dist/components/gui/input.ce.js +40 -0
- package/dist/components/gui/input.ce.js.map +1 -0
- package/dist/i18n.d.ts +11 -0
- package/dist/i18n.js +11 -0
- package/dist/i18n.js.map +1 -1
- package/dist/index.js +13 -11
- package/package.json +4 -4
- package/src/Gui/Gui.spec.ts +5 -0
- package/src/Gui/Gui.ts +5 -1
- package/src/components/gui/dialogbox/index.ce +26 -1
- package/src/components/gui/index.ts +5 -1
- package/src/components/gui/input-field.ce +106 -0
- package/src/components/gui/input.ce +26 -0
- package/src/i18n.ts +11 -0
- package/vite.config.ts +5 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @rpgjs/client
|
|
2
2
|
|
|
3
|
+
## 5.0.0-beta.25
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 0512640: Add reusable typed input and textarea controls for standalone forms and dialog boxes, with shared server-side validation and an RPGJS Studio block that stores the submitted value.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- ccb9495: Fix TypeScript declaration errors across the package build, align multi-target declaration exports, complete movement API overloads, and make package and root builds fail when declaration generation reports a type error.
|
|
12
|
+
- Updated dependencies [ccb9495]
|
|
13
|
+
- Updated dependencies [f6aa046]
|
|
14
|
+
- Updated dependencies [0512640]
|
|
15
|
+
- @rpgjs/common@5.0.0-beta.24
|
|
16
|
+
- @rpgjs/server@5.0.0-beta.25
|
|
17
|
+
- @rpgjs/ui-css@5.0.0-beta.23
|
|
18
|
+
|
|
3
19
|
## 5.0.0-beta.24
|
|
4
20
|
|
|
5
21
|
### Patch Changes
|
package/dist/Gui/Gui.js
CHANGED
|
@@ -7,6 +7,7 @@ import __ce_component$3 from "../components/gui/menu/main-menu.ce.js";
|
|
|
7
7
|
import __ce_component$4 from "../components/gui/notification/notification.ce.js";
|
|
8
8
|
import __ce_component$5 from "../components/gui/title-screen.ce.js";
|
|
9
9
|
import __ce_component$6 from "../components/gui/gameover.ce.js";
|
|
10
|
+
import __ce_component$7 from "../components/gui/input.ce.js";
|
|
10
11
|
import "../components/gui/index.js";
|
|
11
12
|
import { signal } from "canvasengine";
|
|
12
13
|
import { PrebuiltGui } from "@rpgjs/common";
|
|
@@ -113,6 +114,10 @@ var RpgGui = class {
|
|
|
113
114
|
name: PrebuiltGui.Gameover,
|
|
114
115
|
component: __ce_component$6
|
|
115
116
|
});
|
|
117
|
+
this.add({
|
|
118
|
+
name: PrebuiltGui.Input,
|
|
119
|
+
component: __ce_component$7
|
|
120
|
+
});
|
|
116
121
|
this.registerOptimisticReducer(PrebuiltGui.MainMenu, mainMenuOptimisticReducer);
|
|
117
122
|
}
|
|
118
123
|
async _initialize() {
|
package/dist/Gui/Gui.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Gui.js","names":[],"sources":["../../src/Gui/Gui.ts"],"sourcesContent":["import { Context, inject } from \"@signe/di\";\nimport { signal, Signal, WritableSignal } from \"canvasengine\";\nimport { AbstractWebsocket, WebSocketToken } from \"../services/AbstractSocket\";\nimport { DialogboxComponent, ShopComponent, SaveLoadComponent, MainMenuComponent, NotificationComponent, TitleScreenComponent, GameoverComponent } from \"../components/gui\";\nimport { combineLatest, Subscription } from \"rxjs\";\nimport { PrebuiltGui } from \"@rpgjs/common\";\n\ninterface GuiOptions {\n name?: string;\n id?: string;\n component?: any;\n display?: boolean;\n data?: any;\n /**\n * Auto display the GUI when added to the system\n * @default false\n */\n autoDisplay?: boolean;\n /**\n * Function that returns an array of Signal dependencies\n * The GUI will only display when all dependencies are resolved (!= undefined)\n * @returns Array of Signal dependencies\n */\n dependencies?: () => Signal[];\n /**\n * Attach the GUI to sprites instead of displaying globally\n * When true, the GUI will be rendered in character.ce for each sprite\n * @default false\n */\n attachToSprite?: boolean;\n /**\n * Vue v4 compatibility flag. Prefer attachToSprite in v5 projects.\n */\n rpgAttachToSprite?: boolean;\n}\n\nexport interface GuiInstance {\n name: string;\n component: any;\n display: WritableSignal<boolean>;\n data: WritableSignal<any>;\n openId?: string;\n autoDisplay: boolean;\n dependencies?: Signal[];\n subscription?: Subscription;\n attachToSprite?: boolean;\n}\n\ntype GuiState = {\n name: string;\n component: any;\n display: boolean;\n data: any;\n openId?: string;\n attachToSprite: boolean;\n};\n\ntype VueGuiBridge = {\n updateGuiState?: (state: GuiState) => void;\n initializeGuiStates?: (states: GuiState[]) => void;\n};\n\ninterface GuiAction {\n guiId: string;\n name: string;\n data: any;\n clientActionId: string;\n}\n\ntype OptimisticReducer = (data: any, action: GuiAction) => any;\n\nconst throwError = (id: string) => {\n throw `The GUI named ${id} is non-existent. Please add the component in the gui property of the decorator @RpgClient`;\n};\n\nconst updateItemQuantity = (items: any[], id: string) => {\n const index = items.findIndex((item) => item?.id === id);\n if (index === -1) return items;\n const item = items[index];\n if (item?.usable === false) return items;\n if (item?.consumable === false) return items;\n const quantity = typeof item?.quantity === \"number\" ? item.quantity : 1;\n const nextQuantity = Math.max(0, quantity - 1);\n if (nextQuantity === quantity) return items;\n if (nextQuantity <= 0) {\n return items.filter((_, idx) => idx !== index);\n }\n const nextItems = items.slice();\n nextItems[index] = { ...item, quantity: nextQuantity };\n return nextItems;\n};\n\nconst updateEquippedFlag = (items: any[], id: string, equip: boolean) => {\n const index = items.findIndex((item) => item?.id === id);\n if (index === -1) return items;\n const item = items[index];\n if (item?.equipped === equip) return items;\n const nextItems = items.slice();\n nextItems[index] = { ...item, equipped: equip };\n return nextItems;\n};\n\nconst mainMenuOptimisticReducer: OptimisticReducer = (data, action) => {\n if (!data || typeof data !== \"object\") return data;\n if (action.name === \"useItem\") {\n if (!Array.isArray(data.items)) return data;\n const id = action.data?.id;\n if (!id) return data;\n const nextItems = updateItemQuantity(data.items, id);\n if (nextItems === data.items) return data;\n return { ...data, items: nextItems };\n }\n if (action.name === \"equipItem\") {\n const id = action.data?.id;\n if (!id || typeof action.data?.equip !== \"boolean\") return data;\n const equip = action.data.equip;\n let nextItems = data.items;\n let nextEquips = data.equips;\n if (Array.isArray(data.items)) {\n nextItems = updateEquippedFlag(data.items, id, equip);\n }\n if (Array.isArray(data.equips)) {\n nextEquips = updateEquippedFlag(data.equips, id, equip);\n }\n if (nextItems === data.items && nextEquips === data.equips) return data;\n return {\n ...data,\n ...(nextItems !== data.items ? { items: nextItems } : {}),\n ...(nextEquips !== data.equips ? { equips: nextEquips } : {})\n };\n }\n return data;\n};\n\nexport class RpgGui {\n private webSocket: AbstractWebsocket;\n gui = signal<Record<string, GuiInstance>>({});\n extraGuis: GuiInstance[] = [];\n private vueGuiInstance: VueGuiBridge | null = null;\n private optimisticReducers = new Map<string, OptimisticReducer[]>();\n private pendingActions = new Map<string, GuiAction[]>();\n /**\n * Signal tracking which player IDs should display attached GUIs\n * Key: player ID, Value: boolean (true = show, false = hide)\n */\n attachedGuiDisplayState = signal<Record<string, boolean>>({});\n\n constructor(private context: Context) {\n this.webSocket = inject(context, WebSocketToken);\n this.add({\n name: \"rpg-dialog\",\n component: DialogboxComponent,\n });\n this.add({\n name: PrebuiltGui.MainMenu,\n component: MainMenuComponent,\n });\n this.add({\n name: PrebuiltGui.Shop,\n component: ShopComponent,\n });\n this.add({\n name: PrebuiltGui.Notification,\n component: NotificationComponent,\n autoDisplay: true,\n });\n this.add({\n name: PrebuiltGui.Save,\n component: SaveLoadComponent,\n });\n this.add({\n name: PrebuiltGui.TitleScreen,\n component: TitleScreenComponent,\n });\n this.add({\n name: PrebuiltGui.Gameover,\n component: GameoverComponent,\n });\n\n this.registerOptimisticReducer(PrebuiltGui.MainMenu, mainMenuOptimisticReducer);\n }\n\n async _initialize() {\n this.webSocket.on(\"gui.open\", (data: { guiId: string; data: any; guiOpenId?: string }) => {\n this.clearPendingActions(data.guiId);\n this.display(data.guiId, data.data, [], data.guiOpenId);\n });\n\n this.webSocket.on(\"gui.exit\", (payload: string | { guiId: string; guiOpenId?: string }) => {\n const guiId = typeof payload === \"string\" ? payload : payload.guiId;\n const guiOpenId = typeof payload === \"string\" ? undefined : payload.guiOpenId;\n const current = this.get(guiId);\n if (guiOpenId && current?.openId && current.openId !== guiOpenId) {\n return;\n }\n this.hide(guiId);\n });\n\n this.webSocket.on(\"gui.update\", (payload: { guiId: string; data: any; clientActionId?: string }) => {\n this.applyServerUpdate(payload.guiId, payload.data, payload.clientActionId);\n });\n\n /**\n * Listen for tooltip display state changes from server\n * This is triggered by showAttachedGui/hideAttachedGui on the server\n */\n this.webSocket.on(\"gui.tooltip\", (data: { players: string[]; display: boolean }) => {\n const currentState = { ...this.attachedGuiDisplayState() };\n data.players.forEach((playerId) => {\n currentState[playerId] = data.display;\n });\n this.attachedGuiDisplayState.set(currentState);\n });\n }\n\n /**\n * Set the VueGui instance reference for Vue component management\n * This is called by VueGui when it's initialized\n * \n * @param vueGuiInstance - The VueGui instance\n */\n _setVueGuiInstance(vueGuiInstance: any) {\n this.vueGuiInstance = vueGuiInstance;\n this._initializeVueComponents();\n }\n\n /**\n * Notify VueGui about GUI state changes\n * This synchronizes the Vue component display state\n * \n * @param guiId - The GUI component ID\n * @param display - Display state\n * @param data - Component data\n */\n private _notifyVueGui(guiId: string, display: boolean, data: any = {}) {\n const extraGui = this.extraGuis.find(gui => gui.name === guiId);\n if (!extraGui) return;\n this.vueGuiInstance?.updateGuiState?.(this.toGuiState(extraGui, display, data));\n }\n\n /**\n * Initialize Vue components in the VueGui instance\n * This should be called after VueGui is mounted\n */\n _initializeVueComponents() {\n this.vueGuiInstance?.initializeGuiStates?.(\n this.extraGuis.map(gui => this.toGuiState(gui))\n );\n }\n\n guiInteraction(guiId: string, name: string, data: any) {\n const clientActionId = globalThis.crypto?.randomUUID?.() || `${Date.now()}-${Math.random()}`;\n const actionData = { ...(data || {}), clientActionId };\n this.applyOptimisticAction({\n guiId,\n name,\n data: actionData,\n clientActionId\n });\n this.webSocket.emit(\"gui.interaction\", {\n guiId,\n name,\n data: actionData,\n });\n }\n\n guiClose(guiId: string, data?: any, guiOpenId?: unknown) {\n const normalizedOpenId =\n typeof guiOpenId === \"string\" && guiOpenId.length > 0 ? guiOpenId : undefined;\n this.webSocket.emit(\"gui.exit\", {\n guiId,\n guiOpenId: normalizedOpenId,\n data,\n });\n }\n\n /**\n * Add a GUI component to the system\n * \n * By default, only CanvasEngine components (.ce files) are accepted.\n * Vue components should be handled by the @rpgjs/vue package.\n * \n * @param gui - GUI configuration options\n * @param gui.name - Name or ID of the GUI component\n * @param gui.id - Alternative ID if name is not provided\n * @param gui.component - The component to render (must be a CanvasEngine component)\n * @param gui.display - Initial display state (default: false)\n * @param gui.data - Initial data for the component\n * @param gui.autoDisplay - Auto display when added (default: false)\n * @param gui.dependencies - Function returning Signal dependencies\n * @param gui.attachToSprite - Attach GUI to sprites instead of global display (default: false)\n * \n * @example\n * ```ts\n * gui.add({\n * name: 'inventory',\n * component: InventoryComponent, // Must be a .ce component\n * autoDisplay: true,\n * dependencies: () => [playerSignal, inventorySignal]\n * });\n * \n * // Attach to sprites\n * gui.add({\n * name: 'tooltip',\n * component: TooltipComponent,\n * attachToSprite: true\n * });\n * ```\n */\n add(gui: GuiOptions | any) {\n const component = this.resolveComponent(gui);\n const guiId = this.resolveGuiId(gui, component);\n if (!guiId) {\n throw new Error(\"GUI must have a name or id\");\n }\n const attachToSprite = this.resolveAttachToSprite(gui, component);\n const guiInstance: GuiInstance = {\n name: guiId,\n component,\n display: signal<boolean>(gui.display || false),\n data: signal<any>(gui.data || {}),\n openId: undefined,\n autoDisplay: gui.autoDisplay || false,\n dependencies: gui.dependencies ? gui.dependencies() : [],\n attachToSprite,\n };\n\n if (this.isVueComponentInstance(guiInstance)) {\n this.removeCanvasGui(guiId);\n const existingIndex = this.extraGuis.findIndex(existing => existing.name === guiId);\n if (existingIndex >= 0) {\n this.extraGuis[existingIndex].subscription?.unsubscribe();\n this.extraGuis[existingIndex] = guiInstance;\n } else {\n this.extraGuis.push(guiInstance);\n }\n\n this._initializeVueComponents();\n \n if (guiInstance.autoDisplay) {\n this.display(guiId, gui.data);\n } else {\n this._notifyVueGui(guiId, guiInstance.display(), guiInstance.data());\n }\n return;\n }\n\n this.removeVueGui(guiId);\n this.gui()[guiId] = guiInstance;\n this._initializeVueComponents();\n\n // Auto display if enabled and it's a CanvasEngine component\n if (guiInstance.autoDisplay && typeof gui.component === 'function') {\n this.display(guiId, gui.data);\n }\n }\n\n registerOptimisticReducer(guiId: string, reducer: OptimisticReducer) {\n const existing = this.optimisticReducers.get(guiId) || [];\n this.optimisticReducers.set(guiId, existing.concat(reducer));\n }\n\n /**\n * Get all attached GUI components (attachToSprite: true)\n * \n * Returns all GUI instances that are configured to be attached to sprites.\n * These GUIs should be rendered in character.ce instead of canvas.ce.\n * \n * @returns Array of GUI instances with attachToSprite: true\n * \n * @example\n * ```ts\n * const attachedGuis = gui.getAttachedGuis();\n * // Use in character.ce to render tooltips\n * ```\n */\n getAttachedGuis(): GuiInstance[] {\n return Object.values(this.gui()).filter(gui => gui.attachToSprite === true);\n }\n\n getVueGuis(): GuiInstance[] {\n return [...this.extraGuis];\n }\n\n getAttachedVueGuis(): GuiInstance[] {\n return this.extraGuis.filter(gui => gui.attachToSprite === true);\n }\n\n /**\n * Check if a player should display attached GUIs\n * \n * @param playerId - The player ID to check\n * @returns true if attached GUIs should be displayed for this player\n */\n shouldDisplayAttachedGui(playerId: string): boolean {\n return this.attachedGuiDisplayState()[playerId] === true;\n }\n\n get(id: string): GuiInstance | undefined {\n // Check CanvasEngine GUIs first\n const canvasGui = this.gui()[id];\n if (canvasGui) {\n return canvasGui;\n }\n \n // Check Vue GUIs in extraGuis\n return this.extraGuis.find(gui => gui.name === id);\n }\n\n exists(id: string): boolean {\n return !!this.get(id);\n }\n\n getAll(): Record<string, GuiInstance> {\n const allGuis = { ...this.gui() };\n \n // Add extraGuis to the result\n this.extraGuis.forEach(gui => {\n allGuis[gui.name] = gui;\n });\n \n return allGuis;\n }\n\n /**\n * Display a GUI component\n * \n * Displays the GUI immediately if no dependencies are configured,\n * or waits for all dependencies to be resolved if dependencies are present.\n * Automatically manages subscriptions to prevent memory leaks.\n * Works with both CanvasEngine components and Vue components.\n * \n * @param id - The GUI component ID\n * @param data - Data to pass to the component\n * @param dependencies - Optional runtime dependencies (overrides config dependencies)\n * \n * @example\n * ```ts\n * // Display immediately\n * gui.display('inventory', { items: [] });\n * \n * // Display with runtime dependencies\n * gui.display('shop', { shopId: 1 }, [playerSignal, shopSignal]);\n * ```\n */\n display(id: string, data = {}, dependencies: Signal[] = [], openId?: string) {\n if (!this.exists(id)) {\n throw throwError(id);\n }\n\n const guiInstance = this.get(id)!;\n const isVueComponent = this.extraGuis.some(gui => gui.name === id);\n\n if (guiInstance.subscription) {\n guiInstance.subscription.unsubscribe();\n guiInstance.subscription = undefined;\n }\n\n const show = () => {\n guiInstance.openId = openId;\n guiInstance.data.set(data);\n guiInstance.display.set(true);\n if (isVueComponent) {\n this._notifyVueGui(id, true, data);\n }\n };\n\n const deps = dependencies.length > 0\n ? dependencies\n : (guiInstance.dependencies ?? []);\n\n if (deps.length > 0) {\n const values = deps.map(dependency => dependency());\n const subscription = new Subscription();\n const showIfReady = () => {\n if (values.every(value => value !== undefined)) {\n show();\n }\n };\n\n deps.forEach((dependency, index) => {\n subscription.add(dependency.observable.subscribe((value) => {\n values[index] = value;\n showIfReady();\n }));\n });\n\n guiInstance.subscription = subscription;\n showIfReady();\n return;\n }\n\n show();\n }\n\n isDisplaying(id: string): boolean {\n const guiInstance = this.get(id);\n if (!guiInstance) return false;\n return guiInstance.display();\n }\n\n /**\n * Handle Vue component display logic\n * \n * @param id - GUI component ID\n * @param data - Component data\n * @param dependencies - Runtime dependencies\n * @param guiInstance - GUI instance\n */\n private _handleVueComponentDisplay(id: string, data: any, dependencies: Signal[], guiInstance: GuiInstance, openId?: string) {\n // Unsubscribe from previous subscription if exists\n if (guiInstance.subscription) {\n guiInstance.subscription.unsubscribe();\n guiInstance.subscription = undefined;\n }\n\n // Use runtime dependencies or config dependencies\n const deps = dependencies.length > 0 \n ? dependencies \n : (guiInstance.dependencies ?? []);\n\n if (deps.length > 0) {\n const values = deps.map(dependency => dependency());\n const subscription = new Subscription();\n const showIfReady = () => {\n if (values.every(value => value !== undefined)) {\n guiInstance.openId = openId;\n guiInstance.data.set(data);\n guiInstance.display.set(true);\n this._notifyVueGui(id, true, data);\n }\n };\n\n deps.forEach((dependency, index) => {\n subscription.add(dependency.observable.subscribe((value) => {\n values[index] = value;\n showIfReady();\n }));\n });\n\n guiInstance.subscription = subscription;\n showIfReady();\n return;\n }\n\n // No dependencies, display immediately\n guiInstance.openId = openId;\n guiInstance.data.set(data);\n guiInstance.display.set(true);\n this._notifyVueGui(id, true, data);\n }\n\n /**\n * Hide a GUI component\n * \n * Hides the GUI and cleans up any active subscriptions.\n * Works with both CanvasEngine components and Vue components.\n * \n * @param id - The GUI component ID\n * \n * @example\n * ```ts\n * gui.hide('inventory');\n * ```\n */\n hide(id: string) {\n if (!this.exists(id)) {\n throw throwError(id);\n }\n\n const guiInstance = this.get(id)!;\n \n // Unsubscribe if there's an active subscription\n if (guiInstance.subscription) {\n guiInstance.subscription.unsubscribe();\n guiInstance.subscription = undefined;\n }\n\n guiInstance.display.set(false)\n guiInstance.openId = undefined;\n \n // Check if it's a Vue component and notify VueGui\n const isVueComponent = this.extraGuis.some(gui => gui.name === id);\n if (isVueComponent) {\n this._notifyVueGui(id, false);\n }\n }\n\n private isVueComponent(id: string) {\n return this.extraGuis.some(gui => gui.name === id);\n }\n\n private isVueComponentInstance(gui: GuiInstance) {\n return typeof gui.component !== \"function\";\n }\n\n private removeCanvasGui(guiId: string) {\n const current = this.gui();\n if (!(guiId in current)) return;\n const next = { ...current };\n delete next[guiId];\n this.gui.set(next);\n }\n\n private removeVueGui(guiId: string) {\n const removed = this.extraGuis.filter(existing => existing.name === guiId);\n removed.forEach(gui => gui.subscription?.unsubscribe());\n if (removed.length > 0) {\n this.extraGuis = this.extraGuis.filter(existing => existing.name !== guiId);\n }\n }\n\n private resolveComponent(gui: GuiOptions | any) {\n return gui?.component ?? gui;\n }\n\n private resolveGuiId(gui: GuiOptions | any, component: any) {\n return gui?.name || gui?.id || component?.name || component?.__name;\n }\n\n private resolveAttachToSprite(gui: GuiOptions | any, component: any) {\n return !!(gui?.attachToSprite || gui?.rpgAttachToSprite || component?.attachToSprite || component?.rpgAttachToSprite);\n }\n\n private toGuiState(gui: GuiInstance, display = gui.display(), data = gui.data()): GuiState {\n return {\n name: gui.name,\n component: gui.component,\n display,\n data,\n openId: gui.openId,\n attachToSprite: gui.attachToSprite || false,\n };\n }\n\n private clearPendingActions(guiId: string) {\n this.pendingActions.delete(guiId);\n }\n\n private applyReducers(guiId: string, data: any, actions: GuiAction[]) {\n const reducers = this.optimisticReducers.get(guiId);\n if (!reducers || reducers.length === 0) return data;\n let next = data;\n for (const action of actions) {\n for (const reducer of reducers) {\n const updated = reducer(next, action);\n if (updated !== undefined && updated !== null && updated !== next) {\n next = updated;\n }\n }\n }\n return next;\n }\n\n private applyOptimisticAction(action: GuiAction) {\n const guiInstance = this.get(action.guiId);\n if (!guiInstance) return;\n const reducers = this.optimisticReducers.get(action.guiId);\n if (!reducers || reducers.length === 0) return;\n const currentData = guiInstance.data();\n const nextData = this.applyReducers(action.guiId, currentData, [action]);\n if (nextData === currentData) return;\n guiInstance.data.set(nextData);\n const pending = this.pendingActions.get(action.guiId) || [];\n pending.push(action);\n this.pendingActions.set(action.guiId, pending);\n if (this.isVueComponent(action.guiId)) {\n this._notifyVueGui(action.guiId, guiInstance.display(), nextData);\n }\n }\n\n private applyServerUpdate(guiId: string, data: any, clientActionId?: string) {\n const guiInstance = this.get(guiId);\n if (!guiInstance) return;\n let pending = this.pendingActions.get(guiId) || [];\n if (clientActionId) {\n pending = pending.filter(action => action.clientActionId !== clientActionId);\n } else {\n pending = [];\n }\n let nextData = data;\n if (pending.length) {\n nextData = this.applyReducers(guiId, nextData, pending);\n }\n guiInstance.data.set(nextData);\n this.pendingActions.set(guiId, pending);\n if (this.isVueComponent(guiId)) {\n this._notifyVueGui(guiId, guiInstance.display(), nextData);\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Gui.js","names":[],"sources":["../../src/Gui/Gui.ts"],"sourcesContent":["import { Context, inject } from \"@signe/di\";\nimport { signal, Signal, WritableSignal } from \"canvasengine\";\nimport { AbstractWebsocket, WebSocketToken } from \"../services/AbstractSocket\";\nimport { DialogboxComponent, ShopComponent, SaveLoadComponent, MainMenuComponent, NotificationComponent, TitleScreenComponent, GameoverComponent, InputComponent } from \"../components/gui\";\nimport { combineLatest, Subscription } from \"rxjs\";\nimport { PrebuiltGui } from \"@rpgjs/common\";\n\ninterface GuiOptions {\n name?: string;\n id?: string;\n component?: any;\n display?: boolean;\n data?: any;\n /**\n * Auto display the GUI when added to the system\n * @default false\n */\n autoDisplay?: boolean;\n /**\n * Function that returns an array of Signal dependencies\n * The GUI will only display when all dependencies are resolved (!= undefined)\n * @returns Array of Signal dependencies\n */\n dependencies?: () => Signal[];\n /**\n * Attach the GUI to sprites instead of displaying globally\n * When true, the GUI will be rendered in character.ce for each sprite\n * @default false\n */\n attachToSprite?: boolean;\n /**\n * Vue v4 compatibility flag. Prefer attachToSprite in v5 projects.\n */\n rpgAttachToSprite?: boolean;\n}\n\nexport interface GuiInstance {\n name: string;\n component: any;\n display: WritableSignal<boolean>;\n data: WritableSignal<any>;\n openId?: string;\n autoDisplay: boolean;\n dependencies?: Signal[];\n subscription?: Subscription;\n attachToSprite?: boolean;\n}\n\ntype GuiState = {\n name: string;\n component: any;\n display: boolean;\n data: any;\n openId?: string;\n attachToSprite: boolean;\n};\n\ntype VueGuiBridge = {\n updateGuiState?: (state: GuiState) => void;\n initializeGuiStates?: (states: GuiState[]) => void;\n};\n\ninterface GuiAction {\n guiId: string;\n name: string;\n data: any;\n clientActionId: string;\n}\n\ntype OptimisticReducer = (data: any, action: GuiAction) => any;\n\nconst throwError = (id: string) => {\n throw `The GUI named ${id} is non-existent. Please add the component in the gui property of the decorator @RpgClient`;\n};\n\nconst updateItemQuantity = (items: any[], id: string) => {\n const index = items.findIndex((item) => item?.id === id);\n if (index === -1) return items;\n const item = items[index];\n if (item?.usable === false) return items;\n if (item?.consumable === false) return items;\n const quantity = typeof item?.quantity === \"number\" ? item.quantity : 1;\n const nextQuantity = Math.max(0, quantity - 1);\n if (nextQuantity === quantity) return items;\n if (nextQuantity <= 0) {\n return items.filter((_, idx) => idx !== index);\n }\n const nextItems = items.slice();\n nextItems[index] = { ...item, quantity: nextQuantity };\n return nextItems;\n};\n\nconst updateEquippedFlag = (items: any[], id: string, equip: boolean) => {\n const index = items.findIndex((item) => item?.id === id);\n if (index === -1) return items;\n const item = items[index];\n if (item?.equipped === equip) return items;\n const nextItems = items.slice();\n nextItems[index] = { ...item, equipped: equip };\n return nextItems;\n};\n\nconst mainMenuOptimisticReducer: OptimisticReducer = (data, action) => {\n if (!data || typeof data !== \"object\") return data;\n if (action.name === \"useItem\") {\n if (!Array.isArray(data.items)) return data;\n const id = action.data?.id;\n if (!id) return data;\n const nextItems = updateItemQuantity(data.items, id);\n if (nextItems === data.items) return data;\n return { ...data, items: nextItems };\n }\n if (action.name === \"equipItem\") {\n const id = action.data?.id;\n if (!id || typeof action.data?.equip !== \"boolean\") return data;\n const equip = action.data.equip;\n let nextItems = data.items;\n let nextEquips = data.equips;\n if (Array.isArray(data.items)) {\n nextItems = updateEquippedFlag(data.items, id, equip);\n }\n if (Array.isArray(data.equips)) {\n nextEquips = updateEquippedFlag(data.equips, id, equip);\n }\n if (nextItems === data.items && nextEquips === data.equips) return data;\n return {\n ...data,\n ...(nextItems !== data.items ? { items: nextItems } : {}),\n ...(nextEquips !== data.equips ? { equips: nextEquips } : {})\n };\n }\n return data;\n};\n\nexport class RpgGui {\n private webSocket: AbstractWebsocket;\n gui = signal<Record<string, GuiInstance>>({});\n extraGuis: GuiInstance[] = [];\n private vueGuiInstance: VueGuiBridge | null = null;\n private optimisticReducers = new Map<string, OptimisticReducer[]>();\n private pendingActions = new Map<string, GuiAction[]>();\n /**\n * Signal tracking which player IDs should display attached GUIs\n * Key: player ID, Value: boolean (true = show, false = hide)\n */\n attachedGuiDisplayState = signal<Record<string, boolean>>({});\n\n constructor(private context: Context) {\n this.webSocket = inject(context, WebSocketToken);\n this.add({\n name: \"rpg-dialog\",\n component: DialogboxComponent,\n });\n this.add({\n name: PrebuiltGui.MainMenu,\n component: MainMenuComponent,\n });\n this.add({\n name: PrebuiltGui.Shop,\n component: ShopComponent,\n });\n this.add({\n name: PrebuiltGui.Notification,\n component: NotificationComponent,\n autoDisplay: true,\n });\n this.add({\n name: PrebuiltGui.Save,\n component: SaveLoadComponent,\n });\n this.add({\n name: PrebuiltGui.TitleScreen,\n component: TitleScreenComponent,\n });\n this.add({\n name: PrebuiltGui.Gameover,\n component: GameoverComponent,\n });\n this.add({\n name: PrebuiltGui.Input,\n component: InputComponent,\n });\n\n this.registerOptimisticReducer(PrebuiltGui.MainMenu, mainMenuOptimisticReducer);\n }\n\n async _initialize() {\n this.webSocket.on(\"gui.open\", (data: { guiId: string; data: any; guiOpenId?: string }) => {\n this.clearPendingActions(data.guiId);\n this.display(data.guiId, data.data, [], data.guiOpenId);\n });\n\n this.webSocket.on(\"gui.exit\", (payload: string | { guiId: string; guiOpenId?: string }) => {\n const guiId = typeof payload === \"string\" ? payload : payload.guiId;\n const guiOpenId = typeof payload === \"string\" ? undefined : payload.guiOpenId;\n const current = this.get(guiId);\n if (guiOpenId && current?.openId && current.openId !== guiOpenId) {\n return;\n }\n this.hide(guiId);\n });\n\n this.webSocket.on(\"gui.update\", (payload: { guiId: string; data: any; clientActionId?: string }) => {\n this.applyServerUpdate(payload.guiId, payload.data, payload.clientActionId);\n });\n\n /**\n * Listen for tooltip display state changes from server\n * This is triggered by showAttachedGui/hideAttachedGui on the server\n */\n this.webSocket.on(\"gui.tooltip\", (data: { players: string[]; display: boolean }) => {\n const currentState = { ...this.attachedGuiDisplayState() };\n data.players.forEach((playerId) => {\n currentState[playerId] = data.display;\n });\n this.attachedGuiDisplayState.set(currentState);\n });\n }\n\n /**\n * Set the VueGui instance reference for Vue component management\n * This is called by VueGui when it's initialized\n * \n * @param vueGuiInstance - The VueGui instance\n */\n _setVueGuiInstance(vueGuiInstance: any) {\n this.vueGuiInstance = vueGuiInstance;\n this._initializeVueComponents();\n }\n\n /**\n * Notify VueGui about GUI state changes\n * This synchronizes the Vue component display state\n * \n * @param guiId - The GUI component ID\n * @param display - Display state\n * @param data - Component data\n */\n private _notifyVueGui(guiId: string, display: boolean, data: any = {}) {\n const extraGui = this.extraGuis.find(gui => gui.name === guiId);\n if (!extraGui) return;\n this.vueGuiInstance?.updateGuiState?.(this.toGuiState(extraGui, display, data));\n }\n\n /**\n * Initialize Vue components in the VueGui instance\n * This should be called after VueGui is mounted\n */\n _initializeVueComponents() {\n this.vueGuiInstance?.initializeGuiStates?.(\n this.extraGuis.map(gui => this.toGuiState(gui))\n );\n }\n\n guiInteraction(guiId: string, name: string, data: any) {\n const clientActionId = globalThis.crypto?.randomUUID?.() || `${Date.now()}-${Math.random()}`;\n const actionData = { ...(data || {}), clientActionId };\n this.applyOptimisticAction({\n guiId,\n name,\n data: actionData,\n clientActionId\n });\n this.webSocket.emit(\"gui.interaction\", {\n guiId,\n name,\n data: actionData,\n });\n }\n\n guiClose(guiId: string, data?: any, guiOpenId?: unknown) {\n const normalizedOpenId =\n typeof guiOpenId === \"string\" && guiOpenId.length > 0 ? guiOpenId : undefined;\n this.webSocket.emit(\"gui.exit\", {\n guiId,\n guiOpenId: normalizedOpenId,\n data,\n });\n }\n\n /**\n * Add a GUI component to the system\n * \n * By default, only CanvasEngine components (.ce files) are accepted.\n * Vue components should be handled by the @rpgjs/vue package.\n * \n * @param gui - GUI configuration options\n * @param gui.name - Name or ID of the GUI component\n * @param gui.id - Alternative ID if name is not provided\n * @param gui.component - The component to render (must be a CanvasEngine component)\n * @param gui.display - Initial display state (default: false)\n * @param gui.data - Initial data for the component\n * @param gui.autoDisplay - Auto display when added (default: false)\n * @param gui.dependencies - Function returning Signal dependencies\n * @param gui.attachToSprite - Attach GUI to sprites instead of global display (default: false)\n * \n * @example\n * ```ts\n * gui.add({\n * name: 'inventory',\n * component: InventoryComponent, // Must be a .ce component\n * autoDisplay: true,\n * dependencies: () => [playerSignal, inventorySignal]\n * });\n * \n * // Attach to sprites\n * gui.add({\n * name: 'tooltip',\n * component: TooltipComponent,\n * attachToSprite: true\n * });\n * ```\n */\n add(gui: GuiOptions | any) {\n const component = this.resolveComponent(gui);\n const guiId = this.resolveGuiId(gui, component);\n if (!guiId) {\n throw new Error(\"GUI must have a name or id\");\n }\n const attachToSprite = this.resolveAttachToSprite(gui, component);\n const guiInstance: GuiInstance = {\n name: guiId,\n component,\n display: signal<boolean>(gui.display || false),\n data: signal<any>(gui.data || {}),\n openId: undefined,\n autoDisplay: gui.autoDisplay || false,\n dependencies: gui.dependencies ? gui.dependencies() : [],\n attachToSprite,\n };\n\n if (this.isVueComponentInstance(guiInstance)) {\n this.removeCanvasGui(guiId);\n const existingIndex = this.extraGuis.findIndex(existing => existing.name === guiId);\n if (existingIndex >= 0) {\n this.extraGuis[existingIndex].subscription?.unsubscribe();\n this.extraGuis[existingIndex] = guiInstance;\n } else {\n this.extraGuis.push(guiInstance);\n }\n\n this._initializeVueComponents();\n \n if (guiInstance.autoDisplay) {\n this.display(guiId, gui.data);\n } else {\n this._notifyVueGui(guiId, guiInstance.display(), guiInstance.data());\n }\n return;\n }\n\n this.removeVueGui(guiId);\n this.gui()[guiId] = guiInstance;\n this._initializeVueComponents();\n\n // Auto display if enabled and it's a CanvasEngine component\n if (guiInstance.autoDisplay && typeof gui.component === 'function') {\n this.display(guiId, gui.data);\n }\n }\n\n registerOptimisticReducer(guiId: string, reducer: OptimisticReducer) {\n const existing = this.optimisticReducers.get(guiId) || [];\n this.optimisticReducers.set(guiId, existing.concat(reducer));\n }\n\n /**\n * Get all attached GUI components (attachToSprite: true)\n * \n * Returns all GUI instances that are configured to be attached to sprites.\n * These GUIs should be rendered in character.ce instead of canvas.ce.\n * \n * @returns Array of GUI instances with attachToSprite: true\n * \n * @example\n * ```ts\n * const attachedGuis = gui.getAttachedGuis();\n * // Use in character.ce to render tooltips\n * ```\n */\n getAttachedGuis(): GuiInstance[] {\n return Object.values(this.gui()).filter(gui => gui.attachToSprite === true);\n }\n\n getVueGuis(): GuiInstance[] {\n return [...this.extraGuis];\n }\n\n getAttachedVueGuis(): GuiInstance[] {\n return this.extraGuis.filter(gui => gui.attachToSprite === true);\n }\n\n /**\n * Check if a player should display attached GUIs\n * \n * @param playerId - The player ID to check\n * @returns true if attached GUIs should be displayed for this player\n */\n shouldDisplayAttachedGui(playerId: string): boolean {\n return this.attachedGuiDisplayState()[playerId] === true;\n }\n\n get(id: string): GuiInstance | undefined {\n // Check CanvasEngine GUIs first\n const canvasGui = this.gui()[id];\n if (canvasGui) {\n return canvasGui;\n }\n \n // Check Vue GUIs in extraGuis\n return this.extraGuis.find(gui => gui.name === id);\n }\n\n exists(id: string): boolean {\n return !!this.get(id);\n }\n\n getAll(): Record<string, GuiInstance> {\n const allGuis = { ...this.gui() };\n \n // Add extraGuis to the result\n this.extraGuis.forEach(gui => {\n allGuis[gui.name] = gui;\n });\n \n return allGuis;\n }\n\n /**\n * Display a GUI component\n * \n * Displays the GUI immediately if no dependencies are configured,\n * or waits for all dependencies to be resolved if dependencies are present.\n * Automatically manages subscriptions to prevent memory leaks.\n * Works with both CanvasEngine components and Vue components.\n * \n * @param id - The GUI component ID\n * @param data - Data to pass to the component\n * @param dependencies - Optional runtime dependencies (overrides config dependencies)\n * \n * @example\n * ```ts\n * // Display immediately\n * gui.display('inventory', { items: [] });\n * \n * // Display with runtime dependencies\n * gui.display('shop', { shopId: 1 }, [playerSignal, shopSignal]);\n * ```\n */\n display(id: string, data = {}, dependencies: Signal[] = [], openId?: string) {\n if (!this.exists(id)) {\n throw throwError(id);\n }\n\n const guiInstance = this.get(id)!;\n const isVueComponent = this.extraGuis.some(gui => gui.name === id);\n\n if (guiInstance.subscription) {\n guiInstance.subscription.unsubscribe();\n guiInstance.subscription = undefined;\n }\n\n const show = () => {\n guiInstance.openId = openId;\n guiInstance.data.set(data);\n guiInstance.display.set(true);\n if (isVueComponent) {\n this._notifyVueGui(id, true, data);\n }\n };\n\n const deps = dependencies.length > 0\n ? dependencies\n : (guiInstance.dependencies ?? []);\n\n if (deps.length > 0) {\n const values = deps.map(dependency => dependency());\n const subscription = new Subscription();\n const showIfReady = () => {\n if (values.every(value => value !== undefined)) {\n show();\n }\n };\n\n deps.forEach((dependency, index) => {\n subscription.add(dependency.observable.subscribe((value) => {\n values[index] = value;\n showIfReady();\n }));\n });\n\n guiInstance.subscription = subscription;\n showIfReady();\n return;\n }\n\n show();\n }\n\n isDisplaying(id: string): boolean {\n const guiInstance = this.get(id);\n if (!guiInstance) return false;\n return guiInstance.display();\n }\n\n /**\n * Handle Vue component display logic\n * \n * @param id - GUI component ID\n * @param data - Component data\n * @param dependencies - Runtime dependencies\n * @param guiInstance - GUI instance\n */\n private _handleVueComponentDisplay(id: string, data: any, dependencies: Signal[], guiInstance: GuiInstance, openId?: string) {\n // Unsubscribe from previous subscription if exists\n if (guiInstance.subscription) {\n guiInstance.subscription.unsubscribe();\n guiInstance.subscription = undefined;\n }\n\n // Use runtime dependencies or config dependencies\n const deps = dependencies.length > 0 \n ? dependencies \n : (guiInstance.dependencies ?? []);\n\n if (deps.length > 0) {\n const values = deps.map(dependency => dependency());\n const subscription = new Subscription();\n const showIfReady = () => {\n if (values.every(value => value !== undefined)) {\n guiInstance.openId = openId;\n guiInstance.data.set(data);\n guiInstance.display.set(true);\n this._notifyVueGui(id, true, data);\n }\n };\n\n deps.forEach((dependency, index) => {\n subscription.add(dependency.observable.subscribe((value) => {\n values[index] = value;\n showIfReady();\n }));\n });\n\n guiInstance.subscription = subscription;\n showIfReady();\n return;\n }\n\n // No dependencies, display immediately\n guiInstance.openId = openId;\n guiInstance.data.set(data);\n guiInstance.display.set(true);\n this._notifyVueGui(id, true, data);\n }\n\n /**\n * Hide a GUI component\n * \n * Hides the GUI and cleans up any active subscriptions.\n * Works with both CanvasEngine components and Vue components.\n * \n * @param id - The GUI component ID\n * \n * @example\n * ```ts\n * gui.hide('inventory');\n * ```\n */\n hide(id: string) {\n if (!this.exists(id)) {\n throw throwError(id);\n }\n\n const guiInstance = this.get(id)!;\n \n // Unsubscribe if there's an active subscription\n if (guiInstance.subscription) {\n guiInstance.subscription.unsubscribe();\n guiInstance.subscription = undefined;\n }\n\n guiInstance.display.set(false)\n guiInstance.openId = undefined;\n \n // Check if it's a Vue component and notify VueGui\n const isVueComponent = this.extraGuis.some(gui => gui.name === id);\n if (isVueComponent) {\n this._notifyVueGui(id, false);\n }\n }\n\n private isVueComponent(id: string) {\n return this.extraGuis.some(gui => gui.name === id);\n }\n\n private isVueComponentInstance(gui: GuiInstance) {\n return typeof gui.component !== \"function\";\n }\n\n private removeCanvasGui(guiId: string) {\n const current = this.gui();\n if (!(guiId in current)) return;\n const next = { ...current };\n delete next[guiId];\n this.gui.set(next);\n }\n\n private removeVueGui(guiId: string) {\n const removed = this.extraGuis.filter(existing => existing.name === guiId);\n removed.forEach(gui => gui.subscription?.unsubscribe());\n if (removed.length > 0) {\n this.extraGuis = this.extraGuis.filter(existing => existing.name !== guiId);\n }\n }\n\n private resolveComponent(gui: GuiOptions | any) {\n return gui?.component ?? gui;\n }\n\n private resolveGuiId(gui: GuiOptions | any, component: any) {\n return gui?.name || gui?.id || component?.name || component?.__name;\n }\n\n private resolveAttachToSprite(gui: GuiOptions | any, component: any) {\n return !!(gui?.attachToSprite || gui?.rpgAttachToSprite || component?.attachToSprite || component?.rpgAttachToSprite);\n }\n\n private toGuiState(gui: GuiInstance, display = gui.display(), data = gui.data()): GuiState {\n return {\n name: gui.name,\n component: gui.component,\n display,\n data,\n openId: gui.openId,\n attachToSprite: gui.attachToSprite || false,\n };\n }\n\n private clearPendingActions(guiId: string) {\n this.pendingActions.delete(guiId);\n }\n\n private applyReducers(guiId: string, data: any, actions: GuiAction[]) {\n const reducers = this.optimisticReducers.get(guiId);\n if (!reducers || reducers.length === 0) return data;\n let next = data;\n for (const action of actions) {\n for (const reducer of reducers) {\n const updated = reducer(next, action);\n if (updated !== undefined && updated !== null && updated !== next) {\n next = updated;\n }\n }\n }\n return next;\n }\n\n private applyOptimisticAction(action: GuiAction) {\n const guiInstance = this.get(action.guiId);\n if (!guiInstance) return;\n const reducers = this.optimisticReducers.get(action.guiId);\n if (!reducers || reducers.length === 0) return;\n const currentData = guiInstance.data();\n const nextData = this.applyReducers(action.guiId, currentData, [action]);\n if (nextData === currentData) return;\n guiInstance.data.set(nextData);\n const pending = this.pendingActions.get(action.guiId) || [];\n pending.push(action);\n this.pendingActions.set(action.guiId, pending);\n if (this.isVueComponent(action.guiId)) {\n this._notifyVueGui(action.guiId, guiInstance.display(), nextData);\n }\n }\n\n private applyServerUpdate(guiId: string, data: any, clientActionId?: string) {\n const guiInstance = this.get(guiId);\n if (!guiInstance) return;\n let pending = this.pendingActions.get(guiId) || [];\n if (clientActionId) {\n pending = pending.filter(action => action.clientActionId !== clientActionId);\n } else {\n pending = [];\n }\n let nextData = data;\n if (pending.length) {\n nextData = this.applyReducers(guiId, nextData, pending);\n }\n guiInstance.data.set(nextData);\n this.pendingActions.set(guiId, pending);\n if (this.isVueComponent(guiId)) {\n this._notifyVueGui(guiId, guiInstance.display(), nextData);\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { inject } from "../../../core/inject.js";
|
|
2
2
|
import { getKeyboardControlBind } from "../../../services/actionInput.js";
|
|
3
|
+
import __ce_component$1 from "../input-field.ce.js";
|
|
3
4
|
import { RpgClientEngine } from "../../../RpgClientEngine.js";
|
|
4
5
|
import { DOMContainer, DOMElement, DOMSprite, Navigation, computed, cond, createTabindexNavigator, effect, h, loop, mount, signal, useDefineEmits, useDefineProps, useProps } from "canvasengine";
|
|
5
6
|
import { delay } from "@rpgjs/common";
|
|
@@ -23,6 +24,8 @@ function component($$props) {
|
|
|
23
24
|
const position = computed(() => dialogData().position);
|
|
24
25
|
const typewriterEffect = computed(() => dialogData().typewriterEffect);
|
|
25
26
|
const fullWidth = computed(() => dialogData().fullWidth || false);
|
|
27
|
+
const dialogInput = computed(() => dialogData().input || null);
|
|
28
|
+
const hasInput = computed(() => !!dialogInput());
|
|
26
29
|
const resolveProp = (value) => typeof value === "function" ? value() : value;
|
|
27
30
|
const normalizeOpenId = (value) => {
|
|
28
31
|
const resolved = resolveProp(value);
|
|
@@ -76,7 +79,7 @@ function component($$props) {
|
|
|
76
79
|
startTypewriter(text);
|
|
77
80
|
});
|
|
78
81
|
const hasChoices = computed(() => dialogChoices().length > 0);
|
|
79
|
-
const showIndicator = computed(() => !hasChoices() && !isTyping());
|
|
82
|
+
const showIndicator = computed(() => !hasChoices() && !hasInput() && !isTyping());
|
|
80
83
|
const nav = createTabindexNavigator(selectedItem, { count: () => dialogChoices().length }, "wrap");
|
|
81
84
|
function selectChoice(index) {
|
|
82
85
|
return function() {
|
|
@@ -91,6 +94,10 @@ function component($$props) {
|
|
|
91
94
|
event?.stopPropagation?.();
|
|
92
95
|
};
|
|
93
96
|
const closeDialog = () => {
|
|
97
|
+
if (hasInput()) {
|
|
98
|
+
onInteraction?.("cancel");
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
94
101
|
_onFinish();
|
|
95
102
|
};
|
|
96
103
|
const closeDialogFromPointer = (event) => {
|
|
@@ -100,6 +107,12 @@ function component($$props) {
|
|
|
100
107
|
const onSelect = (index) => {
|
|
101
108
|
_onFinish(index);
|
|
102
109
|
};
|
|
110
|
+
const submitInput = (payload) => {
|
|
111
|
+
onInteraction?.("submit", payload);
|
|
112
|
+
};
|
|
113
|
+
const cancelInput = () => {
|
|
114
|
+
onInteraction?.("cancel");
|
|
115
|
+
};
|
|
103
116
|
const canAcceptAction = () => Date.now() - openedAt >= ACTION_OPEN_GUARD_MS;
|
|
104
117
|
const controls = signal({
|
|
105
118
|
up: {
|
|
@@ -142,6 +155,7 @@ function component($$props) {
|
|
|
142
155
|
finishTyping();
|
|
143
156
|
return;
|
|
144
157
|
}
|
|
158
|
+
if (hasInput()) return;
|
|
145
159
|
if (hasChoices()) return;
|
|
146
160
|
_onFinish();
|
|
147
161
|
}
|
|
@@ -223,7 +237,14 @@ function component($$props) {
|
|
|
223
237
|
click: selectChoice(index)
|
|
224
238
|
},
|
|
225
239
|
textContent: choice.text
|
|
226
|
-
})))))
|
|
240
|
+
}))))),
|
|
241
|
+
cond(computed(() => hasInput()), () => h(__ce_component$1, {
|
|
242
|
+
data: dialogInput,
|
|
243
|
+
layout: "dialog",
|
|
244
|
+
showMessage: false,
|
|
245
|
+
submit: submitInput,
|
|
246
|
+
cancel: cancelInput
|
|
247
|
+
}))
|
|
227
248
|
]), cond(computed(() => hasFace()), () => h(DOMElement, {
|
|
228
249
|
element: "div",
|
|
229
250
|
attrs: { class: "rpg-ui-dialog-face" }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.ce.js","names":[],"sources":["../../../../src/components/gui/dialogbox/index.ce"],"sourcesContent":["<DOMContainer width=\"100%\" height=\"100%\" controls={dialogControls}>\n <div class=\"rpg-ui-dialog-layer\" click={closeDialogFromPointer}>\n <button class=\"rpg-ui-close-button\" type=\"button\" title=\"Close\" aria-label=\"Close dialog\" click={closeDialogFromPointer}>x</button>\n <div\n class=\"rpg-ui-dialog-container\"\n data-position={dialogPosition()}\n data-full-width={isFullWidth() ? \"true\" : \"false\"}\n data-has-face={hasFace() ? \"true\" : \"false\"}\n >\n <div class=\"rpg-ui-dialog rpg-anim-fade-in\" click={stopEvent}>\n <div class=\"rpg-ui-dialog-body\">\n <div>\n @if (speakerName()) {\n <div class=\"rpg-ui-dialog-speaker\">{speakerName()}</div>\n }\n <div class=\"rpg-ui-dialog-content\">\n {displayMessage()}\n </div>\n @if (hasChoices()) {\n <Navigation tabindex={selectedItem} controls={controls}>\n <div class=\"rpg-ui-dialog-choices\">\n @for ((choice,index) of dialogChoices()) {\n <div\n class=\"rpg-ui-dialog-choice\"\n class={{active: selectedItem() === index}}\n tabindex={index}\n data-choice-index={index}\n click={selectChoice(index)}\n >{{ choice.text }}</div>\n }\n </div>\n </Navigation>\n }\n </div>\n @if (hasFace()) {\n <div class=\"rpg-ui-dialog-face\">\n <DOMSprite\n sheet={faceSheet(dialogFace())}\n width=\"100%\"\n height=\"100%\"\n objectFit=\"contain\"\n />\n </div>\n }\n </div>\n @if (showIndicator) {\n <div class=\"rpg-ui-dialog-indicator\"></div>\n }\n </div>\n </div>\n </div>\n</DOMContainer>\n \n<script>\n import { effect, signal, computed, createTabindexNavigator, mount } from \"canvasengine\";\n import { inject } from \"../../../core/inject\";\n import { RpgClientEngine } from \"../../../RpgClientEngine\";\n import { delay } from \"@rpgjs/common\";\n import { getKeyboardControlBind } from \"../../../services/actionInput\";\n\n const engine = inject(RpgClientEngine);\n const currentPlayer = engine.scene.currentPlayer\n const keyboardControls = engine.globalConfig.keyboardControls;\n\n engine.stopProcessingInput = true;\n\n const selectedItem = signal(0)\n const ACTION_OPEN_GUARD_MS = 150;\n const openedAt = Date.now();\n\n const {\n data,\n onFinish,\n onInteraction,\n guiOpenId\n } = defineProps();\n\n const dialogData = computed(() => data() || {});\n const dialogChoices = computed(() => Array.isArray(dialogData().choices) ? dialogData().choices : []);\n const message = computed(() => dialogData().message);\n const speaker = computed(() => dialogData().speaker);\n const position = computed(() => dialogData().position);\n const typewriterEffect = computed(() => dialogData().typewriterEffect);\n const fullWidth = computed(() => dialogData().fullWidth || false);\n\n const resolveProp = (value) => typeof value === \"function\" ? value() : value;\n const normalizeOpenId = (value) => {\n const resolved = resolveProp(value);\n return typeof resolved === \"string\" && resolved.length > 0 ? resolved : undefined;\n };\n\n const speakerName = computed(() => {\n const value = resolveProp(speaker());\n return value ? String(value) : \"\";\n });\n\n const dialogPosition = computed(() => resolveProp(position()) || \"bottom\");\n const isFullWidth = computed(() => resolveProp(fullWidth) !== false);\n const dialogFace = computed(() => resolveProp(dialogData().face));\n const hasFace = computed(() => {\n const value = dialogFace();\n return !!(value && value.id);\n });\n\n const displayMessage = signal(\"\");\n const fullMessage = signal(\"\");\n const isTyping = signal(false);\n let typewriterTimer = null;\n let rootElement = null;\n\n mount((element) => {\n rootElement = element;\n });\n\n const startTypewriter = (text) => {\n if (typewriterTimer) clearInterval(typewriterTimer);\n displayMessage.set(\"\");\n if (!text) return;\n let index = 0;\n isTyping.set(true);\n typewriterTimer = setInterval(() => {\n index += 1;\n displayMessage.set(text.slice(0, index));\n if (index >= text.length) {\n clearInterval(typewriterTimer);\n typewriterTimer = null;\n isTyping.set(false);\n }\n }, 20);\n };\n\n const finishTyping = () => {\n if (typewriterTimer) clearInterval(typewriterTimer);\n typewriterTimer = null;\n displayMessage.set(fullMessage());\n isTyping.set(false);\n };\n\n effect(() => {\n const text = resolveProp(message()) || \"\";\n fullMessage.set(text);\n const useTypewriter = resolveProp(typewriterEffect()) !== false;\n if (!useTypewriter) {\n finishTyping();\n return;\n }\n startTypewriter(text);\n });\n\n\n const hasChoices = computed(() => dialogChoices().length > 0);\n const showIndicator = computed(() => !hasChoices() && !isTyping());\n const nav = createTabindexNavigator(selectedItem, { count: () => dialogChoices().length }, 'wrap');\n\n function selectChoice(index) {\n return function() {\n selectedItem.set(index);\n onSelect(index);\n }\n }\n\n function _onFinish(value) {\n if (onFinish) onFinish(value, normalizeOpenId(guiOpenId));\n }\n\n const stopEvent = (event) => {\n event?.stopPropagation?.();\n };\n\n const closeDialog = () => {\n _onFinish();\n };\n\n const closeDialogFromPointer = (event) => {\n stopEvent(event);\n closeDialog();\n };\n\n const onSelect = (index) => {\n _onFinish(index);\n };\n\n const canAcceptAction = () => Date.now() - openedAt >= ACTION_OPEN_GUARD_MS;\n\n const controls = signal({\n up: {\n repeat: true,\n bind: keyboardControls.up,\n throttle: 150,\n keyDown() {\n if (!hasChoices()) return;\n nav.next(-1);\n }\n },\n down: {\n repeat: true,\n bind: keyboardControls.down,\n throttle: 150,\n keyDown() {\n if (!hasChoices()) return;\n nav.next(1);\n }\n },\n action: {\n bind: getKeyboardControlBind(keyboardControls.action),\n keyDown() {\n if (!canAcceptAction()) {\n return;\n }\n if (isTyping()) {\n finishTyping();\n return;\n }\n if (!hasChoices()) return;\n onSelect(selectedItem());\n }\n },\n gamepad: {\n enabled: true\n }\n });\n \n const dialogControls = signal({\n action: {\n bind: getKeyboardControlBind(keyboardControls.action),\n keyDown() {\n if (!canAcceptAction()) {\n return;\n }\n if (isTyping()) {\n finishTyping();\n return;\n }\n if (hasChoices()) return;\n _onFinish();\n }\n },\n })\n\n const faceSheet = (faceValue) => {\n if (!faceValue || !faceValue.id) {\n return undefined;\n }\n return {\n definition: engine.getSpriteSheet(faceValue.id),\n playing: faceValue.expression || \"default\",\n };\n }\n\n mount((element) => {\n return () => {\n // Wait destroy is finished before start processing input\n delay(() => {\n engine.stopProcessingInput = false;\n })\n }\n })\n</script>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.ce.js","names":[],"sources":["../../../../src/components/gui/dialogbox/index.ce"],"sourcesContent":["<DOMContainer width=\"100%\" height=\"100%\" controls={dialogControls}>\n <div class=\"rpg-ui-dialog-layer\" click={closeDialogFromPointer}>\n <button class=\"rpg-ui-close-button\" type=\"button\" title=\"Close\" aria-label=\"Close dialog\" click={closeDialogFromPointer}>x</button>\n <div\n class=\"rpg-ui-dialog-container\"\n data-position={dialogPosition()}\n data-full-width={isFullWidth() ? \"true\" : \"false\"}\n data-has-face={hasFace() ? \"true\" : \"false\"}\n >\n <div class=\"rpg-ui-dialog rpg-anim-fade-in\" click={stopEvent}>\n <div class=\"rpg-ui-dialog-body\">\n <div>\n @if (speakerName()) {\n <div class=\"rpg-ui-dialog-speaker\">{speakerName()}</div>\n }\n <div class=\"rpg-ui-dialog-content\">\n {displayMessage()}\n </div>\n @if (hasChoices()) {\n <Navigation tabindex={selectedItem} controls={controls}>\n <div class=\"rpg-ui-dialog-choices\">\n @for ((choice,index) of dialogChoices()) {\n <div\n class=\"rpg-ui-dialog-choice\"\n class={{active: selectedItem() === index}}\n tabindex={index}\n data-choice-index={index}\n click={selectChoice(index)}\n >{{ choice.text }}</div>\n }\n </div>\n </Navigation>\n }\n @if (hasInput()) {\n <InputField\n data={dialogInput}\n layout=\"dialog\"\n showMessage={false}\n submit={submitInput}\n cancel={cancelInput}\n />\n }\n </div>\n @if (hasFace()) {\n <div class=\"rpg-ui-dialog-face\">\n <DOMSprite\n sheet={faceSheet(dialogFace())}\n width=\"100%\"\n height=\"100%\"\n objectFit=\"contain\"\n />\n </div>\n }\n </div>\n @if (showIndicator) {\n <div class=\"rpg-ui-dialog-indicator\"></div>\n }\n </div>\n </div>\n </div>\n</DOMContainer>\n \n<script>\n import { effect, signal, computed, createTabindexNavigator, mount } from \"canvasengine\";\n import { inject } from \"../../../core/inject\";\n import { RpgClientEngine } from \"../../../RpgClientEngine\";\n import { delay } from \"@rpgjs/common\";\n import { getKeyboardControlBind } from \"../../../services/actionInput\";\n import InputField from \"../input-field.ce\";\n\n const engine = inject(RpgClientEngine);\n const currentPlayer = engine.scene.currentPlayer\n const keyboardControls = engine.globalConfig.keyboardControls;\n\n engine.stopProcessingInput = true;\n\n const selectedItem = signal(0)\n const ACTION_OPEN_GUARD_MS = 150;\n const openedAt = Date.now();\n\n const {\n data,\n onFinish,\n onInteraction,\n guiOpenId\n } = defineProps();\n\n const dialogData = computed(() => data() || {});\n const dialogChoices = computed(() => Array.isArray(dialogData().choices) ? dialogData().choices : []);\n const message = computed(() => dialogData().message);\n const speaker = computed(() => dialogData().speaker);\n const position = computed(() => dialogData().position);\n const typewriterEffect = computed(() => dialogData().typewriterEffect);\n const fullWidth = computed(() => dialogData().fullWidth || false);\n const dialogInput = computed(() => dialogData().input || null);\n const hasInput = computed(() => !!dialogInput());\n\n const resolveProp = (value) => typeof value === \"function\" ? value() : value;\n const normalizeOpenId = (value) => {\n const resolved = resolveProp(value);\n return typeof resolved === \"string\" && resolved.length > 0 ? resolved : undefined;\n };\n\n const speakerName = computed(() => {\n const value = resolveProp(speaker());\n return value ? String(value) : \"\";\n });\n\n const dialogPosition = computed(() => resolveProp(position()) || \"bottom\");\n const isFullWidth = computed(() => resolveProp(fullWidth) !== false);\n const dialogFace = computed(() => resolveProp(dialogData().face));\n const hasFace = computed(() => {\n const value = dialogFace();\n return !!(value && value.id);\n });\n\n const displayMessage = signal(\"\");\n const fullMessage = signal(\"\");\n const isTyping = signal(false);\n let typewriterTimer = null;\n let rootElement = null;\n\n mount((element) => {\n rootElement = element;\n });\n\n const startTypewriter = (text) => {\n if (typewriterTimer) clearInterval(typewriterTimer);\n displayMessage.set(\"\");\n if (!text) return;\n let index = 0;\n isTyping.set(true);\n typewriterTimer = setInterval(() => {\n index += 1;\n displayMessage.set(text.slice(0, index));\n if (index >= text.length) {\n clearInterval(typewriterTimer);\n typewriterTimer = null;\n isTyping.set(false);\n }\n }, 20);\n };\n\n const finishTyping = () => {\n if (typewriterTimer) clearInterval(typewriterTimer);\n typewriterTimer = null;\n displayMessage.set(fullMessage());\n isTyping.set(false);\n };\n\n effect(() => {\n const text = resolveProp(message()) || \"\";\n fullMessage.set(text);\n const useTypewriter = resolveProp(typewriterEffect()) !== false;\n if (!useTypewriter) {\n finishTyping();\n return;\n }\n startTypewriter(text);\n });\n\n\n const hasChoices = computed(() => dialogChoices().length > 0);\n const showIndicator = computed(() => !hasChoices() && !hasInput() && !isTyping());\n const nav = createTabindexNavigator(selectedItem, { count: () => dialogChoices().length }, 'wrap');\n\n function selectChoice(index) {\n return function() {\n selectedItem.set(index);\n onSelect(index);\n }\n }\n\n function _onFinish(value) {\n if (onFinish) onFinish(value, normalizeOpenId(guiOpenId));\n }\n\n const stopEvent = (event) => {\n event?.stopPropagation?.();\n };\n\n const closeDialog = () => {\n if (hasInput()) {\n onInteraction?.(\"cancel\");\n return;\n }\n _onFinish();\n };\n\n const closeDialogFromPointer = (event) => {\n stopEvent(event);\n closeDialog();\n };\n\n const onSelect = (index) => {\n _onFinish(index);\n };\n\n const submitInput = (payload) => {\n onInteraction?.(\"submit\", payload);\n };\n\n const cancelInput = () => {\n onInteraction?.(\"cancel\");\n };\n\n const canAcceptAction = () => Date.now() - openedAt >= ACTION_OPEN_GUARD_MS;\n\n const controls = signal({\n up: {\n repeat: true,\n bind: keyboardControls.up,\n throttle: 150,\n keyDown() {\n if (!hasChoices()) return;\n nav.next(-1);\n }\n },\n down: {\n repeat: true,\n bind: keyboardControls.down,\n throttle: 150,\n keyDown() {\n if (!hasChoices()) return;\n nav.next(1);\n }\n },\n action: {\n bind: getKeyboardControlBind(keyboardControls.action),\n keyDown() {\n if (!canAcceptAction()) {\n return;\n }\n if (isTyping()) {\n finishTyping();\n return;\n }\n if (!hasChoices()) return;\n onSelect(selectedItem());\n }\n },\n gamepad: {\n enabled: true\n }\n });\n \n const dialogControls = signal({\n action: {\n bind: getKeyboardControlBind(keyboardControls.action),\n keyDown() {\n if (!canAcceptAction()) {\n return;\n }\n if (isTyping()) {\n finishTyping();\n return;\n }\n if (hasInput()) return;\n if (hasChoices()) return;\n _onFinish();\n }\n },\n })\n\n const faceSheet = (faceValue) => {\n if (!faceValue || !faceValue.id) {\n return undefined;\n }\n return {\n definition: engine.getSpriteSheet(faceValue.id),\n playing: faceValue.expression || \"default\",\n };\n }\n\n mount((element) => {\n return () => {\n // Wait destroy is finished before start processing input\n delay(() => {\n engine.stopProcessingInput = false;\n })\n }\n })\n</script>\n"],"mappings":"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"}
|
|
@@ -12,4 +12,6 @@ import { default as ExitMenuComponent } from './menu/exit-menu.ce';
|
|
|
12
12
|
import { default as NotificationComponent } from './notification/notification.ce';
|
|
13
13
|
import { default as TitleScreenComponent } from './title-screen.ce';
|
|
14
14
|
import { default as GameoverComponent } from './gameover.ce';
|
|
15
|
-
|
|
15
|
+
import { default as InputComponent } from './input.ce';
|
|
16
|
+
import { default as InputFieldComponent } from './input-field.ce';
|
|
17
|
+
export { DialogboxComponent, BoxComponent, ShopComponent, HudComponent, SaveLoadComponent, MainMenuComponent, ItemsMenuComponent, SkillsMenuComponent, EquipMenuComponent, OptionsMenuComponent, ExitMenuComponent, NotificationComponent, TitleScreenComponent, GameoverComponent, InputComponent, InputFieldComponent };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import "./input-field.ce.js";
|
|
1
2
|
import "./dialogbox/index.ce.js";
|
|
2
3
|
import "./box.ce.js";
|
|
3
4
|
import "./shop/shop.ce.js";
|
|
@@ -12,3 +13,4 @@ import "./menu/main-menu.ce.js";
|
|
|
12
13
|
import "./notification/notification.ce.js";
|
|
13
14
|
import "./title-screen.ce.js";
|
|
14
15
|
import "./gameover.ce.js";
|
|
16
|
+
import "./input.ce.js";
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { inject } from "../../core/inject.js";
|
|
2
|
+
import { RpgClientEngine } from "../../RpgClientEngine.js";
|
|
3
|
+
import { DOMElement, computed, cond, effect, h, signal, useDefineEmits, useDefineProps, useProps } from "canvasengine";
|
|
4
|
+
//#region src/components/gui/input-field.ce
|
|
5
|
+
function component($$props) {
|
|
6
|
+
useProps($$props);
|
|
7
|
+
const defineProps = useDefineProps($$props);
|
|
8
|
+
const defineEmits = useDefineEmits($$props);
|
|
9
|
+
const { t } = inject(RpgClientEngine).i18n();
|
|
10
|
+
const { data, layout, showMessage } = defineProps({
|
|
11
|
+
layout: "standalone",
|
|
12
|
+
showMessage: true
|
|
13
|
+
});
|
|
14
|
+
const { submit, cancel } = defineEmits();
|
|
15
|
+
const formData = computed(() => data() || {});
|
|
16
|
+
const value = signal(String(formData().defaultValue ?? ""));
|
|
17
|
+
const fieldId = computed(() => `rpg-input-value-${layout()}`);
|
|
18
|
+
const errorText = computed(() => formData().errorKey ? t(formData().errorKey, formData().errorParams || {}) : "");
|
|
19
|
+
const showCancelButton = computed(() => formData().cancelButton !== false);
|
|
20
|
+
let focused = false;
|
|
21
|
+
function submitForm(event, collectedData) {
|
|
22
|
+
const submittedValue = collectedData && Object.prototype.hasOwnProperty.call(collectedData, "value") ? collectedData.value : event?.currentTarget?.elements?.value?.value ?? value();
|
|
23
|
+
value.set(String(submittedValue ?? ""));
|
|
24
|
+
submit({ value: submittedValue ?? "" });
|
|
25
|
+
}
|
|
26
|
+
function cancelForm() {
|
|
27
|
+
cancel();
|
|
28
|
+
}
|
|
29
|
+
function handleKeydown(event) {
|
|
30
|
+
if (event?.key !== "Escape") return;
|
|
31
|
+
event.preventDefault?.();
|
|
32
|
+
event.stopPropagation?.();
|
|
33
|
+
cancelForm();
|
|
34
|
+
}
|
|
35
|
+
effect(() => {
|
|
36
|
+
if (focused) return;
|
|
37
|
+
focused = true;
|
|
38
|
+
setTimeout(() => {
|
|
39
|
+
document.getElementById(fieldId())?.focus?.();
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
return h(DOMElement, {
|
|
43
|
+
element: "form",
|
|
44
|
+
attrs: {
|
|
45
|
+
class: "rpg-ui-input-form",
|
|
46
|
+
"data-layout": computed(() => layout()),
|
|
47
|
+
submit: submitForm,
|
|
48
|
+
keydown: handleKeydown
|
|
49
|
+
}
|
|
50
|
+
}, [
|
|
51
|
+
cond(computed(() => showMessage()), () => h(DOMElement, {
|
|
52
|
+
element: "label",
|
|
53
|
+
attrs: {
|
|
54
|
+
class: "rpg-ui-input-message",
|
|
55
|
+
for: computed(() => fieldId())
|
|
56
|
+
},
|
|
57
|
+
textContent: computed(() => formData().message)
|
|
58
|
+
})),
|
|
59
|
+
cond(computed(() => formData().control === "textarea"), () => h(DOMElement, {
|
|
60
|
+
element: "textarea",
|
|
61
|
+
attrs: {
|
|
62
|
+
id: computed(() => fieldId()),
|
|
63
|
+
class: "rpg-ui-input rpg-ui-input-dialog-field",
|
|
64
|
+
name: "value",
|
|
65
|
+
value,
|
|
66
|
+
placeholder: computed(() => formData().placeholder || ""),
|
|
67
|
+
rows: computed(() => formData().rows || 4),
|
|
68
|
+
minlength: computed(() => formData().minLength),
|
|
69
|
+
maxlength: computed(() => formData().maxLength),
|
|
70
|
+
"aria-required": computed(() => formData().required ? "true" : "false")
|
|
71
|
+
}
|
|
72
|
+
}), () => h(DOMElement, {
|
|
73
|
+
element: "input",
|
|
74
|
+
attrs: {
|
|
75
|
+
id: computed(() => fieldId()),
|
|
76
|
+
class: "rpg-ui-input rpg-ui-input-dialog-field",
|
|
77
|
+
name: "value",
|
|
78
|
+
type: computed(() => formData().type || "text"),
|
|
79
|
+
value,
|
|
80
|
+
placeholder: computed(() => formData().placeholder || ""),
|
|
81
|
+
min: computed(() => formData().min),
|
|
82
|
+
max: computed(() => formData().max),
|
|
83
|
+
step: computed(() => formData().step),
|
|
84
|
+
minlength: computed(() => formData().minLength),
|
|
85
|
+
maxlength: computed(() => formData().maxLength),
|
|
86
|
+
"aria-required": computed(() => formData().required ? "true" : "false")
|
|
87
|
+
}
|
|
88
|
+
})),
|
|
89
|
+
cond(computed(() => errorText()), () => h(DOMElement, {
|
|
90
|
+
element: "div",
|
|
91
|
+
attrs: {
|
|
92
|
+
class: "rpg-ui-input-error",
|
|
93
|
+
role: "alert"
|
|
94
|
+
},
|
|
95
|
+
textContent: computed(() => errorText())
|
|
96
|
+
})),
|
|
97
|
+
h(DOMElement, {
|
|
98
|
+
element: "div",
|
|
99
|
+
attrs: { class: "rpg-ui-input-actions" }
|
|
100
|
+
}, [cond(computed(() => showCancelButton()), () => h(DOMElement, {
|
|
101
|
+
element: "button",
|
|
102
|
+
attrs: {
|
|
103
|
+
class: "rpg-ui-btn rpg-ui-input-button rpg-ui-input-button-cancel",
|
|
104
|
+
type: "button",
|
|
105
|
+
click: cancelForm
|
|
106
|
+
},
|
|
107
|
+
textContent: computed(() => formData().cancelText || t("rpg.input.cancel"))
|
|
108
|
+
})), h(DOMElement, {
|
|
109
|
+
element: "button",
|
|
110
|
+
attrs: {
|
|
111
|
+
class: "rpg-ui-btn rpg-ui-input-button rpg-ui-input-button-confirm",
|
|
112
|
+
"data-variant": "primary",
|
|
113
|
+
type: "submit"
|
|
114
|
+
},
|
|
115
|
+
textContent: computed(() => formData().confirmText || t("rpg.input.confirm"))
|
|
116
|
+
})])
|
|
117
|
+
]);
|
|
118
|
+
}
|
|
119
|
+
var __ce_component = component;
|
|
120
|
+
//#endregion
|
|
121
|
+
export { __ce_component as default };
|
|
122
|
+
|
|
123
|
+
//# sourceMappingURL=input-field.ce.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input-field.ce.js","names":[],"sources":["../../../src/components/gui/input-field.ce"],"sourcesContent":["<form\n class=\"rpg-ui-input-form\"\n data-layout={layout()}\n submit={submitForm}\n keydown={handleKeydown}\n>\n @if (showMessage()) {\n <label class=\"rpg-ui-input-message\" for={fieldId()}>{formData().message}</label>\n }\n @if (formData().control === \"textarea\") {\n <textarea\n id={fieldId()}\n class=\"rpg-ui-input rpg-ui-input-dialog-field\"\n name=\"value\"\n value={value}\n placeholder={formData().placeholder || \"\"}\n rows={formData().rows || 4}\n minlength={formData().minLength}\n maxlength={formData().maxLength}\n aria-required={formData().required ? \"true\" : \"false\"}\n ></textarea>\n }\n @else {\n <input\n id={fieldId()}\n class=\"rpg-ui-input rpg-ui-input-dialog-field\"\n name=\"value\"\n type={formData().type || \"text\"}\n value={value}\n placeholder={formData().placeholder || \"\"}\n min={formData().min}\n max={formData().max}\n step={formData().step}\n minlength={formData().minLength}\n maxlength={formData().maxLength}\n aria-required={formData().required ? \"true\" : \"false\"}\n />\n }\n @if (errorText()) {\n <div class=\"rpg-ui-input-error\" role=\"alert\">{errorText()}</div>\n }\n <div class=\"rpg-ui-input-actions\">\n @if (showCancelButton()) {\n <button class=\"rpg-ui-btn rpg-ui-input-button rpg-ui-input-button-cancel\" type=\"button\" click={cancelForm}>\n {formData().cancelText || t(\"rpg.input.cancel\")}\n </button>\n }\n <button\n class=\"rpg-ui-btn rpg-ui-input-button rpg-ui-input-button-confirm\"\n data-variant=\"primary\"\n type=\"submit\"\n >\n {formData().confirmText || t(\"rpg.input.confirm\")}\n </button>\n </div>\n</form>\n\n<script>\n import { computed, effect, signal } from \"canvasengine\";\n import { inject } from \"../../core/inject\";\n import { RpgClientEngine } from \"../../RpgClientEngine\";\n\n const engine = inject(RpgClientEngine);\n const { t } = engine.i18n();\n const { data, layout, showMessage } = defineProps({\n layout: \"standalone\",\n showMessage: true,\n });\n const { submit, cancel } = defineEmits();\n const formData = computed(() => data() || {});\n const value = signal(String(formData().defaultValue ?? \"\"));\n const fieldId = computed(() => `rpg-input-value-${layout()}`);\n const errorText = computed(() => formData().errorKey\n ? t(formData().errorKey, formData().errorParams || {})\n : \"\"\n );\n const showCancelButton = computed(() => formData().cancelButton !== false);\n let focused = false;\n\n function submitForm(event, collectedData) {\n const submittedValue = collectedData && Object.prototype.hasOwnProperty.call(collectedData, \"value\")\n ? collectedData.value\n : event?.currentTarget?.elements?.value?.value ?? value();\n value.set(String(submittedValue ?? \"\"));\n submit({ value: submittedValue ?? \"\" });\n }\n\n function cancelForm() {\n cancel();\n }\n\n function handleKeydown(event) {\n if (event?.key !== \"Escape\") return;\n event.preventDefault?.();\n event.stopPropagation?.();\n cancelForm();\n }\n\n effect(() => {\n if (focused) return;\n focused = true;\n setTimeout(() => {\n document.getElementById(fieldId())?.focus?.();\n });\n });\n</script>\n"],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import __ce_component$1 from "./input-field.ce.js";
|
|
2
|
+
import { DOMContainer, DOMElement, computed, cond, h, useDefineEmits, useDefineProps, useProps } from "canvasengine";
|
|
3
|
+
//#region src/components/gui/input.ce
|
|
4
|
+
function component($$props) {
|
|
5
|
+
useProps($$props);
|
|
6
|
+
const defineProps = useDefineProps($$props);
|
|
7
|
+
useDefineEmits($$props);
|
|
8
|
+
const { data, onInteraction } = defineProps();
|
|
9
|
+
const title = computed(() => data().title || "");
|
|
10
|
+
function submit(payload) {
|
|
11
|
+
onInteraction?.("submit", payload);
|
|
12
|
+
}
|
|
13
|
+
function cancel() {
|
|
14
|
+
onInteraction?.("cancel");
|
|
15
|
+
}
|
|
16
|
+
return h(DOMContainer, {
|
|
17
|
+
width: "100%",
|
|
18
|
+
height: "100%"
|
|
19
|
+
}, h(DOMElement, {
|
|
20
|
+
element: "div",
|
|
21
|
+
attrs: { class: "rpg-ui-input-layer" }
|
|
22
|
+
}, h(DOMElement, {
|
|
23
|
+
element: "div",
|
|
24
|
+
attrs: { class: "rpg-ui-input-dialog rpg-ui-window rpg-anim-fade-in" }
|
|
25
|
+
}, [cond(computed(() => title()), () => h(DOMElement, {
|
|
26
|
+
element: "div",
|
|
27
|
+
attrs: { class: "rpg-ui-window-title" },
|
|
28
|
+
textContent: computed(() => title())
|
|
29
|
+
})), h(__ce_component$1, {
|
|
30
|
+
data,
|
|
31
|
+
layout: "standalone",
|
|
32
|
+
submit,
|
|
33
|
+
cancel
|
|
34
|
+
})])));
|
|
35
|
+
}
|
|
36
|
+
var __ce_component = component;
|
|
37
|
+
//#endregion
|
|
38
|
+
export { __ce_component as default };
|
|
39
|
+
|
|
40
|
+
//# sourceMappingURL=input.ce.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input.ce.js","names":[],"sources":["../../../src/components/gui/input.ce"],"sourcesContent":["<DOMContainer width=\"100%\" height=\"100%\">\n <div class=\"rpg-ui-input-layer\">\n <div class=\"rpg-ui-input-dialog rpg-ui-window rpg-anim-fade-in\">\n @if (title()) {\n <div class=\"rpg-ui-window-title\">{title()}</div>\n }\n <InputField data={data} layout=\"standalone\" submit={submit} cancel={cancel} />\n </div>\n </div>\n</DOMContainer>\n\n<script>\n import { computed } from \"canvasengine\";\n import InputField from \"./input-field.ce\";\n\n const { data, onInteraction } = defineProps();\n const title = computed(() => data().title || \"\");\n\n function submit(payload) {\n onInteraction?.(\"submit\", payload);\n }\n\n function cancel() {\n onInteraction?.(\"cancel\");\n }\n</script>\n"],"mappings":";;;AAQI,SAAG,UAAA,SAAA;CACO,SAAA,OAAA;;CAEP,eAAA,OAAA;CACC,MAAG,EAAA,MAAW,kBAAkB,YAAC;CACzC,MAAE,QAAO,eAAmB,KAAM,EAAA,SAAS,EAAA;;EAEzC,gBAAc,UAAc,OAAI;CAClC;;EAEE,gBAAgB,QAAS;CAC3B;QACE,EAAA,cAAA;EAAA,OAAA;EAAA,QAAA;CAAA,GAAA,EAAA,YAAA;EAAA,SAAA;EAAA,OAAA,EAAA,OAAA,qBAAA;CAAA,GAAA,EAAA,YAAA;EAAA,SAAA;EAAA,OAAA,EAAA,OAAA,qDAAA;CAAA,GAAA,CAAA,KAAA,eAAA,MAAA,CAAA,SAAA,EAAA,YAAA;EAAA,SAAA;EAAA,OAAA,EAAA,OAAA,sBAAA;EAAA,aAAA,eAAA,MAAA,CAAA;CAAA,CAAA,CAAA,GAAA,EAAA,kBAAA;EAAA;EAAA,QAAA;EAAA;EAAA;CAAA,CAAA,CAAA,CAAA,CAAA,CAAA;AAEA;AAEA,IAAA,iBAAA"}
|
package/dist/i18n.d.ts
CHANGED
|
@@ -39,6 +39,17 @@ export declare const RpgClientBuiltinI18n: {
|
|
|
39
39
|
"rpg.gameover.title": string;
|
|
40
40
|
"rpg.gameover.title-screen": string;
|
|
41
41
|
"rpg.gameover.load-game": string;
|
|
42
|
+
"rpg.input.confirm": string;
|
|
43
|
+
"rpg.input.cancel": string;
|
|
44
|
+
"rpg.input.error.invalid": string;
|
|
45
|
+
"rpg.input.error.required": string;
|
|
46
|
+
"rpg.input.error.number": string;
|
|
47
|
+
"rpg.input.error.min": string;
|
|
48
|
+
"rpg.input.error.max": string;
|
|
49
|
+
"rpg.input.error.step": string;
|
|
50
|
+
"rpg.input.error.min-length": string;
|
|
51
|
+
"rpg.input.error.max-length": string;
|
|
52
|
+
"rpg.input.error.email": string;
|
|
42
53
|
"rpg.shop.default-message": string;
|
|
43
54
|
"rpg.shop.choose-action": string;
|
|
44
55
|
"rpg.shop.buy": string;
|
package/dist/i18n.js
CHANGED
|
@@ -39,6 +39,17 @@ var RpgClientBuiltinI18n = { en: {
|
|
|
39
39
|
"rpg.gameover.title": "Game Over",
|
|
40
40
|
"rpg.gameover.title-screen": "Title Screen",
|
|
41
41
|
"rpg.gameover.load-game": "Load Game",
|
|
42
|
+
"rpg.input.confirm": "Confirm",
|
|
43
|
+
"rpg.input.cancel": "Cancel",
|
|
44
|
+
"rpg.input.error.invalid": "Invalid value.",
|
|
45
|
+
"rpg.input.error.required": "A value is required.",
|
|
46
|
+
"rpg.input.error.number": "Enter a valid number.",
|
|
47
|
+
"rpg.input.error.min": "The value must be at least {min}.",
|
|
48
|
+
"rpg.input.error.max": "The value must be at most {max}.",
|
|
49
|
+
"rpg.input.error.step": "The value must use increments of {step}.",
|
|
50
|
+
"rpg.input.error.min-length": "Enter at least {minLength} characters.",
|
|
51
|
+
"rpg.input.error.max-length": "Enter no more than {maxLength} characters.",
|
|
52
|
+
"rpg.input.error.email": "Enter a valid email address.",
|
|
42
53
|
"rpg.shop.default-message": "Welcome to my shop!",
|
|
43
54
|
"rpg.shop.choose-action": "Choose an action",
|
|
44
55
|
"rpg.shop.buy": "Buy",
|
package/dist/i18n.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"i18n.js","names":[],"sources":["../src/i18n.ts"],"sourcesContent":["import { createI18nProvider, type I18nConfig } from \"@rpgjs/common\";\n\nexport const RpgClientBuiltinI18n = {\n en: {\n \"rpg.menu.title\": \"Menu\",\n \"rpg.menu.status\": \"Status\",\n \"rpg.menu.level\": \"Level\",\n \"rpg.menu.gold\": \"Gold\",\n \"rpg.menu.parameters\": \"Parameters\",\n \"rpg.menu.items\": \"Items\",\n \"rpg.menu.skills\": \"Skills\",\n \"rpg.menu.equip\": \"Equip\",\n \"rpg.menu.options\": \"Options\",\n \"rpg.menu.save\": \"Save\",\n \"rpg.menu.exit\": \"Exit\",\n \"rpg.menu.weapons\": \"Weapons\",\n \"rpg.menu.armor\": \"Armor\",\n \"rpg.menu.use\": \"Use\",\n \"rpg.menu.cancel\": \"Cancel\",\n \"rpg.menu.unequip\": \"Unequip\",\n \"rpg.menu.remove-equipment\": \"Remove the current equipment\",\n \"rpg.menu.empty\": \"Empty\",\n \"rpg.menu.leave-game\": \"Leave the game?\",\n \"rpg.menu.exit-help\": \"Press Action to confirm or Escape to go back.\",\n \"rpg.menu.options-help\": \"Configure your preferences here.\",\n \"rpg.save.title\": \"Save Game\",\n \"rpg.save.subtitle\": \"Choose a slot to overwrite or create.\",\n \"rpg.load.title\": \"Load Game\",\n \"rpg.load.subtitle\": \"Select a slot to load your progress.\",\n \"rpg.save.auto\": \"Auto Save\",\n \"rpg.save.slot\": \"Slot {index}\",\n \"rpg.save.empty-slot\": \"Empty Slot\",\n \"rpg.save.level\": \"Level\",\n \"rpg.save.exp\": \"Exp\",\n \"rpg.save.map\": \"Map\",\n \"rpg.save.date\": \"Date\",\n \"rpg.title.default\": \"RPG\",\n \"rpg.title.start\": \"Start\",\n \"rpg.title.load\": \"Load\",\n \"rpg.gameover.title\": \"Game Over\",\n \"rpg.gameover.title-screen\": \"Title Screen\",\n \"rpg.gameover.load-game\": \"Load Game\",\n \"rpg.shop.default-message\": \"Welcome to my shop!\",\n \"rpg.shop.choose-action\": \"Choose an action\",\n \"rpg.shop.buy\": \"Buy\",\n \"rpg.shop.sell\": \"Sell\",\n \"rpg.shop.back\": \"Back\",\n \"rpg.shop.equipped\": \"Equipped\",\n \"rpg.shop.already-equipped\": \"Already equipped\",\n \"rpg.shop.qty\": \"Qty\",\n \"rpg.shop.available\": \"Available\",\n \"rpg.shop.quantity\": \"Quantity\",\n \"rpg.shop.total\": \"Total\",\n },\n};\n\nexport function provideI18n(config: I18nConfig = {}) {\n return createI18nProvider(config);\n}\n"],"mappings":";;AAEA,IAAa,uBAAuB,EAClC,IAAI;CACF,kBAAkB;CAClB,mBAAmB;CACnB,kBAAkB;CAClB,iBAAiB;CACjB,uBAAuB;CACvB,kBAAkB;CAClB,mBAAmB;CACnB,kBAAkB;CAClB,oBAAoB;CACpB,iBAAiB;CACjB,iBAAiB;CACjB,oBAAoB;CACpB,kBAAkB;CAClB,gBAAgB;CAChB,mBAAmB;CACnB,oBAAoB;CACpB,6BAA6B;CAC7B,kBAAkB;CAClB,uBAAuB;CACvB,sBAAsB;CACtB,yBAAyB;CACzB,kBAAkB;CAClB,qBAAqB;CACrB,kBAAkB;CAClB,qBAAqB;CACrB,iBAAiB;CACjB,iBAAiB;CACjB,uBAAuB;CACvB,kBAAkB;CAClB,gBAAgB;CAChB,gBAAgB;CAChB,iBAAiB;CACjB,qBAAqB;CACrB,mBAAmB;CACnB,kBAAkB;CAClB,sBAAsB;CACtB,6BAA6B;CAC7B,0BAA0B;CAC1B,4BAA4B;CAC5B,0BAA0B;CAC1B,gBAAgB;CAChB,iBAAiB;CACjB,iBAAiB;CACjB,qBAAqB;CACrB,6BAA6B;CAC7B,gBAAgB;CAChB,sBAAsB;CACtB,qBAAqB;CACrB,kBAAkB;AACpB,EACF;AAEA,SAAgB,YAAY,SAAqB,CAAC,GAAG;CACnD,OAAO,mBAAmB,MAAM;AAClC"}
|
|
1
|
+
{"version":3,"file":"i18n.js","names":[],"sources":["../src/i18n.ts"],"sourcesContent":["import { createI18nProvider, type I18nConfig } from \"@rpgjs/common\";\n\nexport const RpgClientBuiltinI18n = {\n en: {\n \"rpg.menu.title\": \"Menu\",\n \"rpg.menu.status\": \"Status\",\n \"rpg.menu.level\": \"Level\",\n \"rpg.menu.gold\": \"Gold\",\n \"rpg.menu.parameters\": \"Parameters\",\n \"rpg.menu.items\": \"Items\",\n \"rpg.menu.skills\": \"Skills\",\n \"rpg.menu.equip\": \"Equip\",\n \"rpg.menu.options\": \"Options\",\n \"rpg.menu.save\": \"Save\",\n \"rpg.menu.exit\": \"Exit\",\n \"rpg.menu.weapons\": \"Weapons\",\n \"rpg.menu.armor\": \"Armor\",\n \"rpg.menu.use\": \"Use\",\n \"rpg.menu.cancel\": \"Cancel\",\n \"rpg.menu.unequip\": \"Unequip\",\n \"rpg.menu.remove-equipment\": \"Remove the current equipment\",\n \"rpg.menu.empty\": \"Empty\",\n \"rpg.menu.leave-game\": \"Leave the game?\",\n \"rpg.menu.exit-help\": \"Press Action to confirm or Escape to go back.\",\n \"rpg.menu.options-help\": \"Configure your preferences here.\",\n \"rpg.save.title\": \"Save Game\",\n \"rpg.save.subtitle\": \"Choose a slot to overwrite or create.\",\n \"rpg.load.title\": \"Load Game\",\n \"rpg.load.subtitle\": \"Select a slot to load your progress.\",\n \"rpg.save.auto\": \"Auto Save\",\n \"rpg.save.slot\": \"Slot {index}\",\n \"rpg.save.empty-slot\": \"Empty Slot\",\n \"rpg.save.level\": \"Level\",\n \"rpg.save.exp\": \"Exp\",\n \"rpg.save.map\": \"Map\",\n \"rpg.save.date\": \"Date\",\n \"rpg.title.default\": \"RPG\",\n \"rpg.title.start\": \"Start\",\n \"rpg.title.load\": \"Load\",\n \"rpg.gameover.title\": \"Game Over\",\n \"rpg.gameover.title-screen\": \"Title Screen\",\n \"rpg.gameover.load-game\": \"Load Game\",\n \"rpg.input.confirm\": \"Confirm\",\n \"rpg.input.cancel\": \"Cancel\",\n \"rpg.input.error.invalid\": \"Invalid value.\",\n \"rpg.input.error.required\": \"A value is required.\",\n \"rpg.input.error.number\": \"Enter a valid number.\",\n \"rpg.input.error.min\": \"The value must be at least {min}.\",\n \"rpg.input.error.max\": \"The value must be at most {max}.\",\n \"rpg.input.error.step\": \"The value must use increments of {step}.\",\n \"rpg.input.error.min-length\": \"Enter at least {minLength} characters.\",\n \"rpg.input.error.max-length\": \"Enter no more than {maxLength} characters.\",\n \"rpg.input.error.email\": \"Enter a valid email address.\",\n \"rpg.shop.default-message\": \"Welcome to my shop!\",\n \"rpg.shop.choose-action\": \"Choose an action\",\n \"rpg.shop.buy\": \"Buy\",\n \"rpg.shop.sell\": \"Sell\",\n \"rpg.shop.back\": \"Back\",\n \"rpg.shop.equipped\": \"Equipped\",\n \"rpg.shop.already-equipped\": \"Already equipped\",\n \"rpg.shop.qty\": \"Qty\",\n \"rpg.shop.available\": \"Available\",\n \"rpg.shop.quantity\": \"Quantity\",\n \"rpg.shop.total\": \"Total\",\n },\n};\n\nexport function provideI18n(config: I18nConfig = {}) {\n return createI18nProvider(config);\n}\n"],"mappings":";;AAEA,IAAa,uBAAuB,EAClC,IAAI;CACF,kBAAkB;CAClB,mBAAmB;CACnB,kBAAkB;CAClB,iBAAiB;CACjB,uBAAuB;CACvB,kBAAkB;CAClB,mBAAmB;CACnB,kBAAkB;CAClB,oBAAoB;CACpB,iBAAiB;CACjB,iBAAiB;CACjB,oBAAoB;CACpB,kBAAkB;CAClB,gBAAgB;CAChB,mBAAmB;CACnB,oBAAoB;CACpB,6BAA6B;CAC7B,kBAAkB;CAClB,uBAAuB;CACvB,sBAAsB;CACtB,yBAAyB;CACzB,kBAAkB;CAClB,qBAAqB;CACrB,kBAAkB;CAClB,qBAAqB;CACrB,iBAAiB;CACjB,iBAAiB;CACjB,uBAAuB;CACvB,kBAAkB;CAClB,gBAAgB;CAChB,gBAAgB;CAChB,iBAAiB;CACjB,qBAAqB;CACrB,mBAAmB;CACnB,kBAAkB;CAClB,sBAAsB;CACtB,6BAA6B;CAC7B,0BAA0B;CAC1B,qBAAqB;CACrB,oBAAoB;CACpB,2BAA2B;CAC3B,4BAA4B;CAC5B,0BAA0B;CAC1B,uBAAuB;CACvB,uBAAuB;CACvB,wBAAwB;CACxB,8BAA8B;CAC9B,8BAA8B;CAC9B,yBAAyB;CACzB,4BAA4B;CAC5B,0BAA0B;CAC1B,gBAAgB;CAChB,iBAAiB;CACjB,iBAAiB;CACjB,qBAAqB;CACrB,6BAA6B;CAC7B,gBAAgB;CAChB,sBAAsB;CACtB,qBAAqB;CACrB,kBAAkB;AACpB,EACF;AAEA,SAAgB,YAAY,SAAqB,CAAC,GAAG;CACnD,OAAO,mBAAmB,MAAM;AAClC"}
|
package/dist/index.js
CHANGED
|
@@ -2,25 +2,27 @@ import { Context } from "./node_modules/.pnpm/@signe_di@3.1.0/node_modules/@sign
|
|
|
2
2
|
import { clearInject, context, inject, setInject } from "./core/inject.js";
|
|
3
3
|
import { AbstractWebsocket, WebSocketToken } from "./services/AbstractSocket.js";
|
|
4
4
|
import { getKeyboardControlBind, isKeyboardActionConfig, keyboardEventMatchesBind, normalizeActionInput, resolveKeyboardActionInput, resolveKeyboardDirectionInput } from "./services/actionInput.js";
|
|
5
|
+
import __ce_component$10 from "./components/gui/input-field.ce.js";
|
|
5
6
|
import __ce_component$2 from "./components/gui/dialogbox/index.ce.js";
|
|
6
7
|
import __ce_component from "./components/gui/box.ce.js";
|
|
7
|
-
import __ce_component$
|
|
8
|
+
import __ce_component$18 from "./components/gui/shop/shop.ce.js";
|
|
8
9
|
import { getEntityProp } from "./utils/getEntityProp.js";
|
|
9
10
|
import __ce_component$8 from "./components/gui/hud/hud.ce.js";
|
|
10
11
|
import { SaveClientService, SaveClientToken, provideSaveClient } from "./services/save.js";
|
|
11
|
-
import __ce_component$
|
|
12
|
-
import __ce_component$
|
|
13
|
-
import __ce_component$
|
|
12
|
+
import __ce_component$16 from "./components/gui/save-load.ce.js";
|
|
13
|
+
import __ce_component$11 from "./components/gui/menu/items-menu.ce.js";
|
|
14
|
+
import __ce_component$19 from "./components/gui/menu/skills-menu.ce.js";
|
|
14
15
|
import __ce_component$3 from "./components/gui/menu/equip-menu.ce.js";
|
|
15
|
-
import __ce_component$
|
|
16
|
+
import __ce_component$15 from "./components/gui/menu/options-menu.ce.js";
|
|
16
17
|
import __ce_component$5 from "./components/gui/menu/exit-menu.ce.js";
|
|
17
|
-
import __ce_component$
|
|
18
|
-
import __ce_component$
|
|
19
|
-
import __ce_component$
|
|
18
|
+
import __ce_component$13 from "./components/gui/menu/main-menu.ce.js";
|
|
19
|
+
import __ce_component$14 from "./components/gui/notification/notification.ce.js";
|
|
20
|
+
import __ce_component$20 from "./components/gui/title-screen.ce.js";
|
|
20
21
|
import __ce_component$6 from "./components/gui/gameover.ce.js";
|
|
22
|
+
import __ce_component$9 from "./components/gui/input.ce.js";
|
|
21
23
|
import "./components/gui/index.js";
|
|
22
24
|
import { RpgGui } from "./Gui/Gui.js";
|
|
23
|
-
import __ce_component$
|
|
25
|
+
import __ce_component$17 from "./components/scenes/draw-map.ce.js";
|
|
24
26
|
import { LoadMapService, LoadMapToken, provideLoadMap } from "./services/loadMap.js";
|
|
25
27
|
import { RpgSound, Sound, getSoundMetadata } from "./Sound.js";
|
|
26
28
|
import { RpgResource } from "./Resource.js";
|
|
@@ -40,7 +42,7 @@ import { provideRpg } from "./services/standalone.js";
|
|
|
40
42
|
import { BridgeWebsocket, provideMmorpg } from "./services/mmorpg.js";
|
|
41
43
|
import { startGame } from "./core/setup.js";
|
|
42
44
|
import __ce_component$7 from "./components/prebuilt/hp-bar.ce.js";
|
|
43
|
-
import __ce_component$
|
|
45
|
+
import __ce_component$12 from "./components/prebuilt/light-halo.ce.js";
|
|
44
46
|
import "./components/prebuilt/index.js";
|
|
45
47
|
import { Presets } from "./presets/index.js";
|
|
46
48
|
import __ce_component$1 from "./components/character.ce.js";
|
|
@@ -49,4 +51,4 @@ import "./components/index.js";
|
|
|
49
51
|
import { Spritesheet } from "./decorators/spritesheet.js";
|
|
50
52
|
import { withMobile } from "./components/gui/mobile/index.js";
|
|
51
53
|
import { Input, KeyboardControls } from "canvasengine";
|
|
52
|
-
export { AbstractWebsocket, __ce_component as BoxComponent, BridgeWebsocket, __ce_component$1 as CharacterComponent, ClientVisualRegistry, Context, Control, __ce_component$2 as DialogboxComponent, __ce_component$3 as EquipMenuComponent, __ce_component$4 as EventLayerComponent, __ce_component$5 as ExitMenuComponent, __ce_component$6 as GameoverComponent, GlobalConfigToken, __ce_component$7 as HpBar, __ce_component$8 as HudComponent, Input, __ce_component$9 as ItemsMenuComponent, KeyboardControls, __ce_component$
|
|
54
|
+
export { AbstractWebsocket, __ce_component as BoxComponent, BridgeWebsocket, __ce_component$1 as CharacterComponent, ClientVisualRegistry, Context, Control, __ce_component$2 as DialogboxComponent, __ce_component$3 as EquipMenuComponent, __ce_component$4 as EventLayerComponent, __ce_component$5 as ExitMenuComponent, __ce_component$6 as GameoverComponent, GlobalConfigToken, __ce_component$7 as HpBar, __ce_component$8 as HudComponent, Input, __ce_component$9 as InputComponent, __ce_component$10 as InputFieldComponent, __ce_component$11 as ItemsMenuComponent, KeyboardControls, __ce_component$12 as LightHalo, LoadMapService, LoadMapToken, __ce_component$13 as MainMenuComponent, __ce_component$14 as NotificationComponent, __ce_component$15 as OptionsMenuComponent, PrebuiltComponentAnimations, Presets, ProjectileManager, RpgClientBuiltinI18n, RpgClientEngine, RpgClientEvent, RpgClientInteractions, RpgClientObject, RpgClientPlayer, RpgGui, RpgResource, RpgSound, SaveClientService, SaveClientToken, __ce_component$16 as SaveLoadComponent, __ce_component$17 as SceneMap, __ce_component$18 as ShopComponent, __ce_component$19 as SkillsMenuComponent, Sound, Spritesheet, __ce_component$20 as TitleScreenComponent, WebSocketToken, clearInject, context, createClientPointerContext, dragToTile, draggable, getEntityProp, getKeyboardControlBind, getSoundMetadata, hoverPopover, inject, isKeyboardActionConfig, keyboardEventMatchesBind, normalizeActionInput, provideClientGlobalConfig, provideClientModules, provideGlobalConfig, provideI18n, provideLoadMap, provideMmorpg, provideRpg, provideSaveClient, resolveKeyboardActionInput, resolveKeyboardDirectionInput, selectable, setInject, startGame, withMobile };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rpgjs/client",
|
|
3
|
-
"version": "5.0.0-beta.
|
|
3
|
+
"version": "5.0.0-beta.25",
|
|
4
4
|
"description": "RPGJS is a framework for creating RPG/MMORPG games",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -22,9 +22,9 @@
|
|
|
22
22
|
"pixi.js": "^8.9.2"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@rpgjs/common": "5.0.0-beta.
|
|
26
|
-
"@rpgjs/server": "5.0.0-beta.
|
|
27
|
-
"@rpgjs/ui-css": "5.0.0-beta.
|
|
25
|
+
"@rpgjs/common": "5.0.0-beta.24",
|
|
26
|
+
"@rpgjs/server": "5.0.0-beta.25",
|
|
27
|
+
"@rpgjs/ui-css": "5.0.0-beta.23",
|
|
28
28
|
"@signe/di": "3.1.0",
|
|
29
29
|
"@signe/room": "3.1.0",
|
|
30
30
|
"@signe/sync": "3.1.0",
|
package/src/Gui/Gui.spec.ts
CHANGED
|
@@ -14,6 +14,7 @@ vi.mock("../components/gui", () => {
|
|
|
14
14
|
NotificationComponent: component,
|
|
15
15
|
TitleScreenComponent: component,
|
|
16
16
|
GameoverComponent: component,
|
|
17
|
+
InputComponent: component,
|
|
17
18
|
};
|
|
18
19
|
});
|
|
19
20
|
|
|
@@ -64,6 +65,10 @@ const VueTooltip = {
|
|
|
64
65
|
};
|
|
65
66
|
|
|
66
67
|
describe("RpgGui Vue integration", () => {
|
|
68
|
+
test("registers the prebuilt input GUI", async () => {
|
|
69
|
+
const { gui } = await createGui();
|
|
70
|
+
expect(gui.get(PrebuiltGui.Input)).toBeDefined();
|
|
71
|
+
});
|
|
67
72
|
test("tracks GUI open ids and sends them back when closing", async () => {
|
|
68
73
|
const { gui, socket } = await createGui();
|
|
69
74
|
await gui._initialize();
|
package/src/Gui/Gui.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Context, inject } from "@signe/di";
|
|
2
2
|
import { signal, Signal, WritableSignal } from "canvasengine";
|
|
3
3
|
import { AbstractWebsocket, WebSocketToken } from "../services/AbstractSocket";
|
|
4
|
-
import { DialogboxComponent, ShopComponent, SaveLoadComponent, MainMenuComponent, NotificationComponent, TitleScreenComponent, GameoverComponent } from "../components/gui";
|
|
4
|
+
import { DialogboxComponent, ShopComponent, SaveLoadComponent, MainMenuComponent, NotificationComponent, TitleScreenComponent, GameoverComponent, InputComponent } from "../components/gui";
|
|
5
5
|
import { combineLatest, Subscription } from "rxjs";
|
|
6
6
|
import { PrebuiltGui } from "@rpgjs/common";
|
|
7
7
|
|
|
@@ -176,6 +176,10 @@ export class RpgGui {
|
|
|
176
176
|
name: PrebuiltGui.Gameover,
|
|
177
177
|
component: GameoverComponent,
|
|
178
178
|
});
|
|
179
|
+
this.add({
|
|
180
|
+
name: PrebuiltGui.Input,
|
|
181
|
+
component: InputComponent,
|
|
182
|
+
});
|
|
179
183
|
|
|
180
184
|
this.registerOptimisticReducer(PrebuiltGui.MainMenu, mainMenuOptimisticReducer);
|
|
181
185
|
}
|
|
@@ -31,6 +31,15 @@
|
|
|
31
31
|
</div>
|
|
32
32
|
</Navigation>
|
|
33
33
|
}
|
|
34
|
+
@if (hasInput()) {
|
|
35
|
+
<InputField
|
|
36
|
+
data={dialogInput}
|
|
37
|
+
layout="dialog"
|
|
38
|
+
showMessage={false}
|
|
39
|
+
submit={submitInput}
|
|
40
|
+
cancel={cancelInput}
|
|
41
|
+
/>
|
|
42
|
+
}
|
|
34
43
|
</div>
|
|
35
44
|
@if (hasFace()) {
|
|
36
45
|
<div class="rpg-ui-dialog-face">
|
|
@@ -57,6 +66,7 @@
|
|
|
57
66
|
import { RpgClientEngine } from "../../../RpgClientEngine";
|
|
58
67
|
import { delay } from "@rpgjs/common";
|
|
59
68
|
import { getKeyboardControlBind } from "../../../services/actionInput";
|
|
69
|
+
import InputField from "../input-field.ce";
|
|
60
70
|
|
|
61
71
|
const engine = inject(RpgClientEngine);
|
|
62
72
|
const currentPlayer = engine.scene.currentPlayer
|
|
@@ -82,6 +92,8 @@
|
|
|
82
92
|
const position = computed(() => dialogData().position);
|
|
83
93
|
const typewriterEffect = computed(() => dialogData().typewriterEffect);
|
|
84
94
|
const fullWidth = computed(() => dialogData().fullWidth || false);
|
|
95
|
+
const dialogInput = computed(() => dialogData().input || null);
|
|
96
|
+
const hasInput = computed(() => !!dialogInput());
|
|
85
97
|
|
|
86
98
|
const resolveProp = (value) => typeof value === "function" ? value() : value;
|
|
87
99
|
const normalizeOpenId = (value) => {
|
|
@@ -149,7 +161,7 @@
|
|
|
149
161
|
|
|
150
162
|
|
|
151
163
|
const hasChoices = computed(() => dialogChoices().length > 0);
|
|
152
|
-
const showIndicator = computed(() => !hasChoices() && !isTyping());
|
|
164
|
+
const showIndicator = computed(() => !hasChoices() && !hasInput() && !isTyping());
|
|
153
165
|
const nav = createTabindexNavigator(selectedItem, { count: () => dialogChoices().length }, 'wrap');
|
|
154
166
|
|
|
155
167
|
function selectChoice(index) {
|
|
@@ -168,6 +180,10 @@
|
|
|
168
180
|
};
|
|
169
181
|
|
|
170
182
|
const closeDialog = () => {
|
|
183
|
+
if (hasInput()) {
|
|
184
|
+
onInteraction?.("cancel");
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
171
187
|
_onFinish();
|
|
172
188
|
};
|
|
173
189
|
|
|
@@ -180,6 +196,14 @@
|
|
|
180
196
|
_onFinish(index);
|
|
181
197
|
};
|
|
182
198
|
|
|
199
|
+
const submitInput = (payload) => {
|
|
200
|
+
onInteraction?.("submit", payload);
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
const cancelInput = () => {
|
|
204
|
+
onInteraction?.("cancel");
|
|
205
|
+
};
|
|
206
|
+
|
|
183
207
|
const canAcceptAction = () => Date.now() - openedAt >= ACTION_OPEN_GUARD_MS;
|
|
184
208
|
|
|
185
209
|
const controls = signal({
|
|
@@ -231,6 +255,7 @@
|
|
|
231
255
|
finishTyping();
|
|
232
256
|
return;
|
|
233
257
|
}
|
|
258
|
+
if (hasInput()) return;
|
|
234
259
|
if (hasChoices()) return;
|
|
235
260
|
_onFinish();
|
|
236
261
|
}
|
|
@@ -12,6 +12,8 @@ import ExitMenuComponent from "./menu/exit-menu.ce";
|
|
|
12
12
|
import NotificationComponent from "./notification/notification.ce";
|
|
13
13
|
import TitleScreenComponent from "./title-screen.ce";
|
|
14
14
|
import GameoverComponent from "./gameover.ce";
|
|
15
|
+
import InputComponent from "./input.ce";
|
|
16
|
+
import InputFieldComponent from "./input-field.ce";
|
|
15
17
|
|
|
16
18
|
export {
|
|
17
19
|
DialogboxComponent,
|
|
@@ -27,5 +29,7 @@ export {
|
|
|
27
29
|
ExitMenuComponent,
|
|
28
30
|
NotificationComponent,
|
|
29
31
|
TitleScreenComponent,
|
|
30
|
-
GameoverComponent
|
|
32
|
+
GameoverComponent,
|
|
33
|
+
InputComponent,
|
|
34
|
+
InputFieldComponent
|
|
31
35
|
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
<form
|
|
2
|
+
class="rpg-ui-input-form"
|
|
3
|
+
data-layout={layout()}
|
|
4
|
+
submit={submitForm}
|
|
5
|
+
keydown={handleKeydown}
|
|
6
|
+
>
|
|
7
|
+
@if (showMessage()) {
|
|
8
|
+
<label class="rpg-ui-input-message" for={fieldId()}>{formData().message}</label>
|
|
9
|
+
}
|
|
10
|
+
@if (formData().control === "textarea") {
|
|
11
|
+
<textarea
|
|
12
|
+
id={fieldId()}
|
|
13
|
+
class="rpg-ui-input rpg-ui-input-dialog-field"
|
|
14
|
+
name="value"
|
|
15
|
+
value={value}
|
|
16
|
+
placeholder={formData().placeholder || ""}
|
|
17
|
+
rows={formData().rows || 4}
|
|
18
|
+
minlength={formData().minLength}
|
|
19
|
+
maxlength={formData().maxLength}
|
|
20
|
+
aria-required={formData().required ? "true" : "false"}
|
|
21
|
+
></textarea>
|
|
22
|
+
}
|
|
23
|
+
@else {
|
|
24
|
+
<input
|
|
25
|
+
id={fieldId()}
|
|
26
|
+
class="rpg-ui-input rpg-ui-input-dialog-field"
|
|
27
|
+
name="value"
|
|
28
|
+
type={formData().type || "text"}
|
|
29
|
+
value={value}
|
|
30
|
+
placeholder={formData().placeholder || ""}
|
|
31
|
+
min={formData().min}
|
|
32
|
+
max={formData().max}
|
|
33
|
+
step={formData().step}
|
|
34
|
+
minlength={formData().minLength}
|
|
35
|
+
maxlength={formData().maxLength}
|
|
36
|
+
aria-required={formData().required ? "true" : "false"}
|
|
37
|
+
/>
|
|
38
|
+
}
|
|
39
|
+
@if (errorText()) {
|
|
40
|
+
<div class="rpg-ui-input-error" role="alert">{errorText()}</div>
|
|
41
|
+
}
|
|
42
|
+
<div class="rpg-ui-input-actions">
|
|
43
|
+
@if (showCancelButton()) {
|
|
44
|
+
<button class="rpg-ui-btn rpg-ui-input-button rpg-ui-input-button-cancel" type="button" click={cancelForm}>
|
|
45
|
+
{formData().cancelText || t("rpg.input.cancel")}
|
|
46
|
+
</button>
|
|
47
|
+
}
|
|
48
|
+
<button
|
|
49
|
+
class="rpg-ui-btn rpg-ui-input-button rpg-ui-input-button-confirm"
|
|
50
|
+
data-variant="primary"
|
|
51
|
+
type="submit"
|
|
52
|
+
>
|
|
53
|
+
{formData().confirmText || t("rpg.input.confirm")}
|
|
54
|
+
</button>
|
|
55
|
+
</div>
|
|
56
|
+
</form>
|
|
57
|
+
|
|
58
|
+
<script>
|
|
59
|
+
import { computed, effect, signal } from "canvasengine";
|
|
60
|
+
import { inject } from "../../core/inject";
|
|
61
|
+
import { RpgClientEngine } from "../../RpgClientEngine";
|
|
62
|
+
|
|
63
|
+
const engine = inject(RpgClientEngine);
|
|
64
|
+
const { t } = engine.i18n();
|
|
65
|
+
const { data, layout, showMessage } = defineProps({
|
|
66
|
+
layout: "standalone",
|
|
67
|
+
showMessage: true,
|
|
68
|
+
});
|
|
69
|
+
const { submit, cancel } = defineEmits();
|
|
70
|
+
const formData = computed(() => data() || {});
|
|
71
|
+
const value = signal(String(formData().defaultValue ?? ""));
|
|
72
|
+
const fieldId = computed(() => `rpg-input-value-${layout()}`);
|
|
73
|
+
const errorText = computed(() => formData().errorKey
|
|
74
|
+
? t(formData().errorKey, formData().errorParams || {})
|
|
75
|
+
: ""
|
|
76
|
+
);
|
|
77
|
+
const showCancelButton = computed(() => formData().cancelButton !== false);
|
|
78
|
+
let focused = false;
|
|
79
|
+
|
|
80
|
+
function submitForm(event, collectedData) {
|
|
81
|
+
const submittedValue = collectedData && Object.prototype.hasOwnProperty.call(collectedData, "value")
|
|
82
|
+
? collectedData.value
|
|
83
|
+
: event?.currentTarget?.elements?.value?.value ?? value();
|
|
84
|
+
value.set(String(submittedValue ?? ""));
|
|
85
|
+
submit({ value: submittedValue ?? "" });
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function cancelForm() {
|
|
89
|
+
cancel();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function handleKeydown(event) {
|
|
93
|
+
if (event?.key !== "Escape") return;
|
|
94
|
+
event.preventDefault?.();
|
|
95
|
+
event.stopPropagation?.();
|
|
96
|
+
cancelForm();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
effect(() => {
|
|
100
|
+
if (focused) return;
|
|
101
|
+
focused = true;
|
|
102
|
+
setTimeout(() => {
|
|
103
|
+
document.getElementById(fieldId())?.focus?.();
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
</script>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<DOMContainer width="100%" height="100%">
|
|
2
|
+
<div class="rpg-ui-input-layer">
|
|
3
|
+
<div class="rpg-ui-input-dialog rpg-ui-window rpg-anim-fade-in">
|
|
4
|
+
@if (title()) {
|
|
5
|
+
<div class="rpg-ui-window-title">{title()}</div>
|
|
6
|
+
}
|
|
7
|
+
<InputField data={data} layout="standalone" submit={submit} cancel={cancel} />
|
|
8
|
+
</div>
|
|
9
|
+
</div>
|
|
10
|
+
</DOMContainer>
|
|
11
|
+
|
|
12
|
+
<script>
|
|
13
|
+
import { computed } from "canvasengine";
|
|
14
|
+
import InputField from "./input-field.ce";
|
|
15
|
+
|
|
16
|
+
const { data, onInteraction } = defineProps();
|
|
17
|
+
const title = computed(() => data().title || "");
|
|
18
|
+
|
|
19
|
+
function submit(payload) {
|
|
20
|
+
onInteraction?.("submit", payload);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function cancel() {
|
|
24
|
+
onInteraction?.("cancel");
|
|
25
|
+
}
|
|
26
|
+
</script>
|
package/src/i18n.ts
CHANGED
|
@@ -40,6 +40,17 @@ export const RpgClientBuiltinI18n = {
|
|
|
40
40
|
"rpg.gameover.title": "Game Over",
|
|
41
41
|
"rpg.gameover.title-screen": "Title Screen",
|
|
42
42
|
"rpg.gameover.load-game": "Load Game",
|
|
43
|
+
"rpg.input.confirm": "Confirm",
|
|
44
|
+
"rpg.input.cancel": "Cancel",
|
|
45
|
+
"rpg.input.error.invalid": "Invalid value.",
|
|
46
|
+
"rpg.input.error.required": "A value is required.",
|
|
47
|
+
"rpg.input.error.number": "Enter a valid number.",
|
|
48
|
+
"rpg.input.error.min": "The value must be at least {min}.",
|
|
49
|
+
"rpg.input.error.max": "The value must be at most {max}.",
|
|
50
|
+
"rpg.input.error.step": "The value must use increments of {step}.",
|
|
51
|
+
"rpg.input.error.min-length": "Enter at least {minLength} characters.",
|
|
52
|
+
"rpg.input.error.max-length": "Enter no more than {maxLength} characters.",
|
|
53
|
+
"rpg.input.error.email": "Enter a valid email address.",
|
|
43
54
|
"rpg.shop.default-message": "Welcome to my shop!",
|
|
44
55
|
"rpg.shop.choose-action": "Choose an action",
|
|
45
56
|
"rpg.shop.buy": "Buy",
|
package/vite.config.ts
CHANGED
|
@@ -11,7 +11,10 @@ export default defineConfig({
|
|
|
11
11
|
canvasengine(),
|
|
12
12
|
dts({
|
|
13
13
|
include: ['src/**/*.ts'],
|
|
14
|
-
|
|
14
|
+
outDirs: 'dist',
|
|
15
|
+
afterDiagnostic(diagnostics) {
|
|
16
|
+
if (diagnostics.length > 0) throw new Error(`Declaration generation failed with ${diagnostics.length} TypeScript diagnostic(s)`)
|
|
17
|
+
}
|
|
15
18
|
})
|
|
16
19
|
],
|
|
17
20
|
build: {
|
|
@@ -33,4 +36,4 @@ export default defineConfig({
|
|
|
33
36
|
}
|
|
34
37
|
},
|
|
35
38
|
},
|
|
36
|
-
})
|
|
39
|
+
})
|