Skip to content

Commit

Permalink
popup work
Browse files Browse the repository at this point in the history
  • Loading branch information
jacklehamster committed Mar 4, 2024
1 parent fd7f95c commit bfe2e40
Show file tree
Hide file tree
Showing 20 changed files with 468 additions and 268 deletions.
359 changes: 228 additions & 131 deletions example/dist/index.js

Large diffs are not rendered by default.

33 changes: 27 additions & 6 deletions src/demo/DemoGame.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ import { goBackAction } from "world/aux/GoBack";
import { BodyModel } from "world/sprite/body/BodyModel";
import { PositionStep } from "world/aux/PositionStep";
import { IKeyboard } from "controls/IKeyboard";
import { closePopupAction, popupActions } from "ui/popup/PopupInterface";

enum Assets {
DOBUKI = 0,
Expand Down Expand Up @@ -423,17 +422,39 @@ export class DemoGame extends AuxiliaryHolder {
size: [undefined, 100],
positionFromRight: true,
items: [
{ label: "good", action: closePopupAction() },
{ label: "bad", action: closePopupAction() },
{
label: "good",
action: [
ui => ui.closePopup(),
ui => ui.openDialog({
position: [10, 0],
size: [300, 300],
conversation: {
messages: [
{ text: "That's nice to know!" },
]
},
}),
ui => ui.nextMessage(),
],
},
{
label: "bad",
action: [
ui => console.log(ui.selection),
ui => ui.closePopup(),
ui => ui.nextMessage(),
]
},
],
}),
},
{ text: "Bye bye." },
{
action: popupActions(
closePopupAction(),
action: [
ui => ui.closePopup(),
goBackAction(heroPos),
),
],
},
]
},
Expand Down
8 changes: 6 additions & 2 deletions src/ui/GameContextType.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export interface GameContextType {
closePopup(): void;
controls?: IControls;
topPopupUid: string;
onSelection: (selection: number) => void;
}

export const DEFAULT_GAME_CONTEXT: GameContextType = {
Expand All @@ -19,14 +20,17 @@ export const DEFAULT_GAME_CONTEXT: GameContextType = {
removeControlsLock: function (_uid: string): void {
throw new Error('Function not implemented.');
},
openMenu: function (_value: MenuData): void {
openMenu: function (_value: MenuData): Promise<number> {
throw new Error('Function not implemented.');
},
openDialog: function (_value: DialogData | undefined): void {
openDialog: function (_value: DialogData | undefined): Promise<void> {
throw new Error('Function not implemented.');
},
closePopup(): void {
throw new Error('Function not implemented.');
},
topPopupUid: '',
onSelection(selection) {
throw new Error('Function not implemented');
},
};
19 changes: 13 additions & 6 deletions src/ui/Hud.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,22 +42,29 @@ export class Hud extends AuxiliaryHolder implements UserInterface {
this.#rootElem.style.pointerEvents = 'none';
}

openDialog(dialog: DialogData): void {
const type = 'dialog';
const uid = type + '-' + uuidv4();
this.#popupManager.openDialog?.({ uid, type, ...dialog });
async openDialog(dialog: DialogData) {
return this.#popupManager.openDialog?.(dialog);
}

openMenu(menuData: MenuData): void {
async openMenu(menuData: MenuData) {
return this.#popupManager.openMenu?.(menuData);
const type = 'menu';
const uid = type + '-' + uuidv4();
this.#popupManager.openMenu?.({ uid, type, ...menuData });
return this.#popupManager.openMenu?.({ uid, type, ...menuData });
}

nextMessage(): void {
this.#popupManager.nextMessage();
}

closePopup(): void {
this.#popupManager.closePopup?.();
}

get selection(): number {
return this.#popupManager.selection;
}

activate(): void {
super.activate();
document.body.appendChild(this.#rootElem);
Expand Down
47 changes: 38 additions & 9 deletions src/ui/HudContent.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { useEffect, useMemo } from 'react';
import { useEffect, useMemo, useState } from 'react';
import { Provider } from './Provider';
import { PopupManager } from './popup/PopupManager';
import { GameContextType } from './GameContextType';
import { IControls } from 'controls/IControls';
import { usePopupManager } from './popup/usePopupManager';
import { PopupContainer } from './popup/PopupContainer';
import { v4 as uuidv4 } from 'uuid';

interface Props {
dialogManager: PopupManager;
Expand All @@ -13,30 +14,58 @@ interface Props {

export function HudContent({ dialogManager, controls }: Props) {
const { popups, addPopup, closePopup, topPopupUid } = usePopupManager();
const [selection, setSelection] = useState(0);
const [onClose, setOnClose] = useState<() => void>();

const gameContext: GameContextType = useMemo<GameContextType>(
() => ({
addControlsLock: dialogManager.addControlsLock,
removeControlsLock: dialogManager.removeControlsLock,
openMenu: addPopup,
openDialog: addPopup,
openMenu: (data) => {
const type = 'menu';
const uid = type + '-' + uuidv4();
addPopup({ uid, type, ...data });
},
openDialog: (data) => {
const type = 'dialog';
const uid = type + '-' + uuidv4();
addPopup({ uid, type, ...data });
},
closePopup,
controls,
topPopupUid,
onSelection: setSelection,
}),
[dialogManager, controls, addPopup, closePopup, topPopupUid],
[dialogManager, controls, addPopup, closePopup, topPopupUid, setSelection],
);

useEffect(() => {
dialogManager.openMenu = gameContext.openMenu;
dialogManager.openDialog = gameContext.openDialog;
dialogManager.closePopup = gameContext.closePopup;
}, [dialogManager, gameContext]);
dialogManager.openMenu = async (data) => {
gameContext.openMenu(data);
return new Promise((resolve) => {
setOnClose(() => resolve);
});
};
dialogManager.openDialog = async (data) => {
gameContext.openDialog(data);
return new Promise((resolve) => {
setOnClose(() => resolve);
});
};
dialogManager.closePopup = () => {
gameContext.closePopup();
setOnClose((previousOnClose) => {
previousOnClose?.();
return undefined;
});
};
dialogManager.selection = selection;
}, [dialogManager, gameContext, selection]);

return (
<Provider context={gameContext}>
<div>Title</div>
<PopupContainer popups={popups} />
<PopupContainer popups={popups} ui={dialogManager} />
</Provider>
);
}
6 changes: 4 additions & 2 deletions src/ui/UserInterface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import { MenuData } from "./menu/model/MenuData";
import { DialogData } from "./dialog/model/DialogData";

export interface UserInterface extends Auxiliary {
openMenu(menu: MenuData): void;
openDialog(dialog: DialogData): void;
openMenu(menu: MenuData): Promise<void>;
openDialog(dialog: DialogData): Promise<void>;
closePopup(): void;
nextMessage(): void;
get selection(): number
}
4 changes: 4 additions & 0 deletions src/ui/actions/PopAction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { UserInterface } from "ui/UserInterface";


export type PopAction = (ui: UserInterface) => void | Promise<void>;
16 changes: 16 additions & 0 deletions src/ui/actions/useActions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { useCallback } from "react";
import { UserInterface } from "ui/UserInterface";
import { PopAction } from "ui/actions/PopAction";

interface Props {
ui: UserInterface;
}

export function useActions({ ui }: Props) {
const performActions = useCallback(async (actions: (PopAction | undefined)[]) => {
for (let action of actions) {
await action?.(ui);
}
}, [ui]);
return { performActions };
}
34 changes: 18 additions & 16 deletions src/ui/dialog/Dialog.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { Popup } from 'ui/popup/Popup';
import { DialogData } from './model/DialogData';
import { useDialog } from './useDialog';
import { UserInterface } from 'ui/UserInterface';
import './text/ProgressiveText';

interface Props {
dialogData: DialogData;
ui: UserInterface;
}

export function Dialog({ dialogData }: Props): JSX.Element {
const { text } = useDialog({ dialogData });
export function Dialog({ dialogData, ui }: Props): JSX.Element {
const { text } = useDialog({ dialogData, ui });

const position: [number, number] = [
dialogData?.position?.[0] ?? 50,
Expand All @@ -19,20 +21,20 @@ export function Dialog({ dialogData }: Props): JSX.Element {
dialogData?.size?.[1],
];

const { fontSize, positionFromRight, positionFromBottom, zIndex } =
dialogData;
return (
dialogData && (
<Popup
position={position}
size={size}
fontSize={dialogData.fontSize}
positionFromBottom={!!dialogData.positionFromBottom}
positionFromRight={!!dialogData.positionFromRight}
zIndex={dialogData.zIndex}
>
<div style={{ padding: 10 }}>
<progressive-text period="30">{text}</progressive-text>
</div>
</Popup>
)
<Popup
position={position}
size={size}
fontSize={fontSize}
positionFromBottom={positionFromBottom}
positionFromRight={positionFromRight}
zIndex={zIndex}
>
<div style={{ padding: 10 }}>
<progressive-text period="30">{text}</progressive-text>
</div>
</Popup>
);
}
4 changes: 2 additions & 2 deletions src/ui/dialog/model/Message.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { PopupInterface } from "ui/popup/PopupInterface";
import { PopAction } from "../../actions/PopAction";

export interface Message {
id?: string;
text?: string;
action?(ui: PopupInterface): void;
action?: PopAction | (PopAction | undefined)[];
}
8 changes: 4 additions & 4 deletions src/ui/dialog/text/ProgressiveText.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ declare global {

export class ProgressiveText extends HTMLElement {
readonly #observer;
readonly #containerText;
readonly #hiddenText;
#animationFrameRequest = 0;
#containerText;
#hiddenText;

constructor() {
super();
const shadowRoot = this.attachShadow({ mode: 'open' });
this.#containerText = shadowRoot.appendChild(document.createElement('div'));
this.#hiddenText = shadowRoot.appendChild(document.createElement('div'));
this.#containerText = shadowRoot.appendChild(document.createElement('span'));
this.#hiddenText = shadowRoot.appendChild(document.createElement('span'));
this.#hiddenText.style.color = "#00000020";

this.#observer = new MutationObserver(mutationsList => this.handleMutation(mutationsList));
Expand Down
27 changes: 16 additions & 11 deletions src/ui/dialog/useDialog.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
import { usePopup } from "ui/popup/usePopup";
import { useCallback, useEffect, useMemo, useState } from "react";
import { DialogData } from "./model/DialogData";
import { useControlsLock } from "ui/useControlsLock";
import { useGameContext } from "ui/Provider";
import { ControlsListener } from "controls/ControlsListener";
import { UserInterface } from "ui/UserInterface";
import { useActions } from "ui/actions/useActions";

interface Props {
dialogData: DialogData;
ui: UserInterface;
}

interface Result {
text?: string;
}

export function useDialog({ dialogData }: Props): Result {
export function useDialog({ dialogData, ui }: Props): Result {
const { lock, unlock, inControl } = useControlsLock(dialogData.uid);
const { controls } = useGameContext();
const [index, setIndex] = useState(0);
const { popupInterface } = usePopup({ popupData: dialogData });
const { performActions } = useActions({ ui });

const nextMessage = useCallback(() => setIndex((value) => value + 1), [setIndex]);
const nextMessage = useCallback(() => setIndex(value => value + 1), [setIndex]);

useEffect(() => ui.nextMessage = nextMessage, [nextMessage, ui]);

useEffect(() => {
if (dialogData && controls) {
Expand All @@ -43,23 +47,24 @@ export function useDialog({ dialogData }: Props): Result {
}
}, [dialogData, nextMessage, lock, unlock, controls, inControl]);

useEffect(() => {
setIndex(0);
}, [dialogData]);
useEffect(() => setIndex(0), [dialogData]);

const messages = useMemo(() => dialogData.conversation.messages, [dialogData]);
const message = useMemo(() => messages.at(index), [messages, index]);

useEffect(() => {
const numMessages = messages.length.valueOf();
if (index >= numMessages) {
popupInterface.close();
ui.closePopup();
}
}, [index, popupInterface, messages]);
}, [index, ui, messages]);

useEffect(() => {
message?.action?.(popupInterface);
}, [message, popupInterface]);
if (message?.action) {
const actions = Array.isArray(message.action) ? message.action : [message.action];
performActions(actions);
}
}, [message, performActions]);

return {
text: message?.text,
Expand Down
Loading

0 comments on commit bfe2e40

Please sign in to comment.