diff --git a/play.pokemonshowdown.com/preactalpha.template.html b/play.pokemonshowdown.com/preactalpha.template.html index ee20a7daa..af1623907 100644 --- a/play.pokemonshowdown.com/preactalpha.template.html +++ b/play.pokemonshowdown.com/preactalpha.template.html @@ -1,4 +1,4 @@ - + - + Showdown! @@ -104,7 +104,7 @@ https://psim.us/dev - + diff --git a/play.pokemonshowdown.com/src/battle-tooltips.ts b/play.pokemonshowdown.com/src/battle-tooltips.ts index 0463fb21c..2663baf94 100644 --- a/play.pokemonshowdown.com/src/battle-tooltips.ts +++ b/play.pokemonshowdown.com/src/battle-tooltips.ts @@ -419,7 +419,7 @@ export class BattleTooltips { $wrapper.removeClass('tooltip-locked'); } $wrapper.css({ - left: x, + left: Math.min(x, document.documentElement.clientWidth - 400), top: y, }); innerHTML = `
${innerHTML}
`; @@ -448,8 +448,11 @@ export class BattleTooltips { } let width = $(BattleTooltips.elem).outerWidth()!; - if (x > document.documentElement.clientWidth - width - 2) { - x = document.documentElement.clientWidth - width - 2; + const availableWidth = document.documentElement.clientWidth + window.scrollX; + if (x > availableWidth - width - 2) { + x = availableWidth - width - 2; + $wrapper.css('left', x); + } else if (x > document.documentElement.clientWidth - 400) { $wrapper.css('left', x); } diff --git a/play.pokemonshowdown.com/src/client-main.ts b/play.pokemonshowdown.com/src/client-main.ts index f2bd91f2c..370ad74a4 100644 --- a/play.pokemonshowdown.com/src/client-main.ts +++ b/play.pokemonshowdown.com/src/client-main.ts @@ -882,21 +882,21 @@ export const PS = new class extends PSModel { panel: PSRoom = null!; /** * Not to be confused with PSPrefs.onepanel, which is permanent. - * PS.onePanelMode will be true if one-panel mode is on, but it will - * also be true if the right panel is temporarily hidden (by opening - * the Rooms panel and clicking "Hide") + * Normally tracks PSPrefs.onepanel, but will become 'vertical' + * at low widths. * * Will NOT be true if only one panel fits onto the screen at the * moment, but resizing will display multiple panels – for that, * check `PS.leftRoomWidth === 0` */ - onePanelMode = false; + onePanelMode: boolean | 'vertical' = false; /** - * 0 = only one panel visible. + * * 0 = only one panel visible + * * null = vertical nav layout * n.b. PS will only update if the left room width changes. Resizes * that don't change the left room width will not trigger an update. */ - leftPanelWidth = 0; + leftPanelWidth: number | null = 0; mainmenu: MainMenuRoom = null!; /** @@ -988,21 +988,25 @@ export const PS = new class extends PSModel { }; } updateLayout(alreadyUpdating?: boolean) { - const leftRoomWidth = this.calculateLeftRoomWidth(); - let roomHeight = document.body.offsetHeight - 56; - let totalWidth = document.body.offsetWidth; - if (leftRoomWidth) { - this.leftPanel.width = leftRoomWidth; + const leftPanelWidth = this.calculateLeftPanelWidth(); + const totalWidth = document.body.offsetWidth; + const totalHeight = document.body.offsetHeight; + const roomHeight = totalHeight - 56; + if (leftPanelWidth === null) { + this.panel.width = totalWidth - 200; + this.panel.height = totalHeight; + } else if (leftPanelWidth) { + this.leftPanel.width = leftPanelWidth; this.leftPanel.height = roomHeight; - this.rightPanel!.width = totalWidth + 1 - leftRoomWidth; + this.rightPanel!.width = totalWidth + 1 - leftPanelWidth; this.rightPanel!.height = roomHeight; } else { this.panel.width = totalWidth; this.panel.height = roomHeight; } - if (this.leftPanelWidth !== leftRoomWidth) { - this.leftPanelWidth = leftRoomWidth; + if (this.leftPanelWidth !== leftPanelWidth) { + this.leftPanelWidth = leftPanelWidth; if (!alreadyUpdating) this.update(true); } } @@ -1119,7 +1123,7 @@ export const PS = new class extends PSModel { this.connection.send(fullMsg); } isVisible(room: PSRoom) { - if (this.leftPanelWidth === 0) { + if (!this.leftPanelWidth) { // one panel visible return room === this.panel; } else { @@ -1127,7 +1131,11 @@ export const PS = new class extends PSModel { return room === this.rightPanel || room === this.leftPanel; } } - calculateLeftRoomWidth() { + calculateLeftPanelWidth() { + const available = document.body.offsetWidth; + if (available < 800) { + return null; + } // If we don't have both a left room and a right room, obviously // just show one room if (!this.leftPanel || !this.rightPanel || this.onePanelMode) { @@ -1139,7 +1147,6 @@ export const PS = new class extends PSModel { const left = this.getWidthFor(this.leftPanel); const right = this.getWidthFor(this.rightPanel); - const available = document.body.offsetWidth; let excess = available - (left.width + right.width); if (excess >= 0) { @@ -1252,12 +1259,12 @@ export const PS = new class extends PSModel { if (!this.isVisible(room)) { room.hiddenInit = true; } - if (room.location === 'left') { - this.leftPanel = this.panel = room; - while (this.popups.length) this.leave(this.popups.pop()!); - this.room = room; - } else if (room.location === 'right') { - this.rightPanel = this.panel = room; + if (PS.isNormalRoom(room)) { + if (room.location === 'right') { + this.rightPanel = this.panel = room; + } else { + this.leftPanel = this.panel = room; + } while (this.popups.length) this.leave(this.popups.pop()!); this.room = room; } else { // popup or mini-window @@ -1272,6 +1279,9 @@ export const PS = new class extends PSModel { return true; } horizontalNav(room = this.room) { + if (this.leftPanelWidth === null) { + return { rooms: [], index: -1 }; + } const rooms = this.leftRoomList.concat(this.rightRoomList); const miniRoom = this.miniRoomList[0] !== 'news' ? this.miniRoomList[0] : null; if (miniRoom) rooms.splice(1, 0, miniRoom); @@ -1282,6 +1292,11 @@ export const PS = new class extends PSModel { return { rooms, index }; } verticalNav(room = this.room) { + if (this.leftPanelWidth === null) { + const rooms = ['' as RoomID, ...this.miniRoomList, ...this.leftRoomList.slice(1), ...this.rightRoomList]; + const index = rooms.indexOf(room.id); + return { rooms, index }; + } if (room.location !== 'mini-window') { return { rooms: [], index: -1 }; } @@ -1354,9 +1369,11 @@ export const PS = new class extends PSModel { if (!upRoom && !downRoom) return ''; let buf = ' '; - if (upRoom) buf += `Alt+\u2191 ${upRoom.title}`; + // const altLabel = navigator.platform?.startsWith('Mac') ? '⌥' : 'ᴀʟᴛ'; + const altLabel = navigator.platform?.startsWith('Mac') ? 'ᴏᴘᴛ' : 'ᴀʟᴛ'; + if (upRoom) buf += `${altLabel}\u2191 ${upRoom.title}`; buf += " | "; - if (downRoom) buf += `${downRoom.title} Alt+\u2193`; + if (downRoom) buf += `${altLabel}\u2193 ${downRoom.title}`; return buf; } @@ -1441,11 +1458,11 @@ export const PS = new class extends PSModel { } } roomVisible(room: PSRoom): boolean { - if (room.location === 'left' || room.location === 'right') { - return this.leftPanelWidth === 0 ? room === this.panel : room === this.leftPanel || room === this.rightPanel; + if (PS.isNormalRoom(room)) { + return !this.leftPanelWidth ? room === this.panel : room === this.leftPanel || room === this.rightPanel; } if (room.location === 'mini-window') { - return this.leftPanelWidth === 0 ? this.mainmenu === this.panel : this.mainmenu === this.leftPanel; + return !this.leftPanelWidth ? this.mainmenu === this.panel : this.mainmenu === this.leftPanel; } // some kind of popup return true; @@ -1470,6 +1487,15 @@ export const PS = new class extends PSModel { this.update(); } + isPopup(room: PSRoom | undefined | null) { + if (!room) return false; + return room.location === 'popup' || room.location === 'semimodal-popup' || room.location === 'modal-popup'; + } + isNormalRoom(room: PSRoom | undefined | null) { + if (!room) return false; + return room.location === 'left' || room.location === 'right' || + (room.location === 'mini-window' && PS.leftPanelWidth === null); + } moveRoom(room: PSRoom, location: PSRoomLocation, background?: boolean, index?: number) { if (room.location === location && index === undefined) { if (background === true) { @@ -1486,7 +1512,7 @@ export const PS = new class extends PSModel { return; } const POPUPS = ['popup', 'semimodal-popup', 'modal-popup']; - if (POPUPS.includes(room.location) && POPUPS.includes(location)) { + if (this.isPopup(room) && POPUPS.includes(location)) { room.location = location; return; } @@ -1548,6 +1574,7 @@ export const PS = new class extends PSModel { if (location === 'left') this.leftPanel = this.panel = room; if (location === 'right') this.rightPanel = this.panel = room; if (location === 'mini-window') this.leftPanel = this.panel = this.mainmenu; + if (location === 'mini-window' && PS.leftPanelWidth === null) this.leftPanel = this.panel = room; this.room = room; } } @@ -1599,6 +1626,13 @@ export const PS = new class extends PSModel { this.leave(this.popups[this.popups.length - 1]); if (!skipUpdate) this.update(); } + closeAllPopups(skipUpdate?: boolean) { + // this is more sensibly a while-loop, but I don't want accidental infinite loops + for (let i = this.popups.length - 1; i >= 0; i--) { + this.leave(this.popups[i]); + } + if (!skipUpdate) this.update(); + } join(roomid: RoomID, options?: Partial | null, noFocus?: boolean) { if (this.room.id === roomid) return; if (PS.rooms[roomid]) { diff --git a/play.pokemonshowdown.com/src/panel-battle.tsx b/play.pokemonshowdown.com/src/panel-battle.tsx index b79e8f2ba..3185c91d3 100644 --- a/play.pokemonshowdown.com/src/panel-battle.tsx +++ b/play.pokemonshowdown.com/src/panel-battle.tsx @@ -311,19 +311,35 @@ class BattlePanel extends PSRoomPanel { override componentDidMount() { const room = this.props.room; const $elem = $(this.base!); - const battle = room.battle || new Battle({ + const battle = (room.battle ||= new Battle({ + id: room.id as any, $frame: $elem.find('.battle'), $logFrame: $elem.find('.battle-log'), log: room.backlog?.map(args => '|' + args.join('|')), - }); + })); + const scene = battle.scene as BattleScene; room.backlog = null; - room.battle ||= battle; - room.log ||= battle.scene.log; - (battle.scene as BattleScene).tooltips.listen($elem.find('.battle-controls')); + room.log ||= scene.log; + scene.tooltips.listen($elem.find('.battle-controls-container')); + scene.tooltips.listen(scene.log.elem); super.componentDidMount(); battle.seekTurn(Infinity); battle.subscribe(() => this.forceUpdate()); } + battleHeight = 360; + updateLayout() { + if (!this.base) return; + const room = this.props.room; + const width = this.base.offsetWidth; + if (width && width < 640) { + const scale = (width / 640); + room.battle?.scene.$frame!.css('transform', `scale(${scale})`); + this.battleHeight = Math.round(360 * scale); + } else { + room.battle?.scene.$frame!.css('transform', 'none'); + this.battleHeight = 360; + } + } override receiveLine(args: Args) { const room = this.props.room; switch (args[0]) { @@ -371,8 +387,8 @@ class BattlePanel extends PSRoomPanel { renderControls() { const room = this.props.room; if (!room.battle) return null; - if (room.side) { - return this.renderPlayerControls(); + if (room.side && room.request) { + return this.renderPlayerControls(room.request); } const atEnd = room.battle.atQueueEnd; return
@@ -589,11 +605,9 @@ class BattlePanel extends PSRoomPanel { } return buf; } - renderPlayerControls() { + renderPlayerControls(request: BattleRequest) { const room = this.props.room; - const request = room.request; let choices = room.choices; - if (!request) return 'Error: Missing request'; if (!choices) return 'Error: Missing BattleChoiceBuilder'; if (choices.request !== request) { choices = new BattleChoiceBuilder(request); @@ -746,8 +760,25 @@ class BattlePanel extends PSRoomPanel { } override render() { const room = this.props.room; + this.updateLayout(); - return + if (room.width < 700) { + return + + + + + + +
+
; + } + + return { -