@zthun/romulator-web 1.4.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/dist/assets/{index-Com8X0qx.js → index-DUOIpN_h.js} +1666 -3734
- package/dist/index.html +1 -1
- package/package.json +14 -14
- package/src/games/games-list.tsx +74 -0
- package/src/games/games-service.mts +25 -0
- package/src/systems/system-page.cm.mts +18 -7
- package/src/systems/system-page.spec.tsx +98 -15
- package/src/systems/system-page.tsx +59 -9
- package/src/systems/systems-page.tsx +5 -24
- package/src/systems/system-avatar-card.cm.mts +0 -17
- package/src/systems/system-avatar-card.tsx +0 -39
package/dist/index.html
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>Romulator: Organize your Games</title>
|
|
7
|
-
<script type="module" crossorigin src="/assets/index-
|
|
7
|
+
<script type="module" crossorigin src="/assets/index-DUOIpN_h.js"></script>
|
|
8
8
|
</head>
|
|
9
9
|
<body>
|
|
10
10
|
<div id="zthunworks-romulator"></div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zthun/romulator-web",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "Romulator frontend",
|
|
5
5
|
"author": "Anthony Bonta",
|
|
6
6
|
"license": "MIT",
|
|
@@ -18,29 +18,29 @@
|
|
|
18
18
|
"access": "public"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
|
-
"@types/node": "^24.
|
|
21
|
+
"@types/node": "^24.8.1",
|
|
22
22
|
"@zthun/cirque": "^7.1.10",
|
|
23
23
|
"@zthun/cirque-du-react": "^7.1.10",
|
|
24
|
-
"@zthun/fashion-boutique": "^
|
|
25
|
-
"@zthun/fashion-tailor": "^
|
|
26
|
-
"@zthun/fashion-theme": "^
|
|
27
|
-
"@zthun/helpful-fn": "^9.
|
|
28
|
-
"@zthun/helpful-query": "^9.
|
|
29
|
-
"@zthun/helpful-react": "^9.
|
|
24
|
+
"@zthun/fashion-boutique": "^11.0.1",
|
|
25
|
+
"@zthun/fashion-tailor": "^11.0.1",
|
|
26
|
+
"@zthun/fashion-theme": "^11.0.1",
|
|
27
|
+
"@zthun/helpful-fn": "^9.6.0",
|
|
28
|
+
"@zthun/helpful-query": "^9.7.1",
|
|
29
|
+
"@zthun/helpful-react": "^9.7.1",
|
|
30
30
|
"@zthun/janitor-build-config": "^19.3.6",
|
|
31
|
-
"@zthun/romulator-client": "^1.
|
|
32
|
-
"@zthun/webigail-http": "^4.0.
|
|
33
|
-
"@zthun/webigail-rest": "^4.0.
|
|
34
|
-
"@zthun/webigail-url": "^4.0.
|
|
31
|
+
"@zthun/romulator-client": "^1.6.0",
|
|
32
|
+
"@zthun/webigail-http": "^4.0.9",
|
|
33
|
+
"@zthun/webigail-rest": "^4.0.9",
|
|
34
|
+
"@zthun/webigail-url": "^4.0.9",
|
|
35
35
|
"history": "^5.3.0",
|
|
36
36
|
"lodash-es": "^4.17.21",
|
|
37
37
|
"react": "^19.2.0",
|
|
38
38
|
"react-dom": "^19.2.0",
|
|
39
39
|
"tsconfig-paths": "^4.2.0",
|
|
40
40
|
"typescript": "^5.9.3",
|
|
41
|
-
"vite": "^7.1.
|
|
41
|
+
"vite": "^7.1.10",
|
|
42
42
|
"vitest": "^3.2.4",
|
|
43
43
|
"vitest-mock-extended": "^3.1.0"
|
|
44
44
|
},
|
|
45
|
-
"gitHead": "
|
|
45
|
+
"gitHead": "9f3e9d22ba3ef2561e18c72a11b8c85f45052438"
|
|
46
46
|
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useFashionTheme,
|
|
3
|
+
useNavigate,
|
|
4
|
+
ZBox,
|
|
5
|
+
ZGridView,
|
|
6
|
+
ZImageSource,
|
|
7
|
+
ZStack,
|
|
8
|
+
type IZComponentValue,
|
|
9
|
+
} from "@zthun/fashion-boutique";
|
|
10
|
+
import { ZSizeFixed, ZSizeVaried } from "@zthun/fashion-tailor";
|
|
11
|
+
import { ZDataRequestBuilder, type IZDataRequest } from "@zthun/helpful-query";
|
|
12
|
+
import { useAmbassadorState } from "@zthun/helpful-react";
|
|
13
|
+
import type { IZRomulatorGame } from "@zthun/romulator-client";
|
|
14
|
+
import { ZRomulatorEnvironmentBuilder } from "../environment/environment.mjs";
|
|
15
|
+
import { useGamesService } from "./games-service.mjs";
|
|
16
|
+
|
|
17
|
+
export interface IZRomulatorGamesList extends IZComponentValue<IZDataRequest> {}
|
|
18
|
+
|
|
19
|
+
export function ZRomulatorGamesList(props: IZRomulatorGamesList) {
|
|
20
|
+
const { value, onValueChange } = props;
|
|
21
|
+
const [request, setRequest] = useAmbassadorState(
|
|
22
|
+
value,
|
|
23
|
+
onValueChange,
|
|
24
|
+
new ZDataRequestBuilder().build(),
|
|
25
|
+
);
|
|
26
|
+
const games = useGamesService();
|
|
27
|
+
const { body } = useFashionTheme();
|
|
28
|
+
const navigate = useNavigate();
|
|
29
|
+
|
|
30
|
+
const renderTile = (value: IZRomulatorGame) => {
|
|
31
|
+
const { api } = new ZRomulatorEnvironmentBuilder().build();
|
|
32
|
+
const id = `${value.id}-marquees`;
|
|
33
|
+
const wheel = `${api}/media/${id}`;
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<ZBox
|
|
37
|
+
className="ZRomulatorGameTile-root"
|
|
38
|
+
fashion={body}
|
|
39
|
+
interactive
|
|
40
|
+
key={value.id}
|
|
41
|
+
cursor="pointer"
|
|
42
|
+
padding={ZSizeFixed.Small}
|
|
43
|
+
data-name={value.id}
|
|
44
|
+
onClick={() => navigate(`/games/${value.id}`)}
|
|
45
|
+
>
|
|
46
|
+
<ZStack
|
|
47
|
+
justify={{ content: "center" }}
|
|
48
|
+
align={{ items: "center" }}
|
|
49
|
+
height={ZSizeVaried.Full}
|
|
50
|
+
>
|
|
51
|
+
<ZImageSource src={wheel} width={ZSizeVaried.Full} />
|
|
52
|
+
</ZStack>
|
|
53
|
+
</ZBox>
|
|
54
|
+
);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<ZGridView
|
|
59
|
+
GridProps={{
|
|
60
|
+
columns: {
|
|
61
|
+
xl: "1fr 1fr 1fr 1fr",
|
|
62
|
+
lg: "1fr 1fr 1fr",
|
|
63
|
+
md: "1fr 1fr",
|
|
64
|
+
sm: "1fr",
|
|
65
|
+
},
|
|
66
|
+
gap: ZSizeFixed.Medium,
|
|
67
|
+
}}
|
|
68
|
+
dataSource={games}
|
|
69
|
+
renderItem={renderTile}
|
|
70
|
+
value={request}
|
|
71
|
+
onValueChange={setRequest}
|
|
72
|
+
/>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { IZDataSource } from "@zthun/helpful-query";
|
|
2
|
+
import type { IZRomulatorGame } from "@zthun/romulator-client";
|
|
3
|
+
import { ZHttpService } from "@zthun/webigail-http";
|
|
4
|
+
import type { IZRestfulGet } from "@zthun/webigail-rest";
|
|
5
|
+
import { ZRestfulService } from "@zthun/webigail-rest";
|
|
6
|
+
import { ZUrlBuilder } from "@zthun/webigail-url";
|
|
7
|
+
import { createContext, useContext } from "react";
|
|
8
|
+
import { ZRomulatorEnvironmentBuilder } from "../environment/environment.mjs";
|
|
9
|
+
|
|
10
|
+
export interface IZRomulatorGamesService
|
|
11
|
+
extends IZRestfulGet<IZRomulatorGame>,
|
|
12
|
+
IZDataSource<IZRomulatorGame> {}
|
|
13
|
+
|
|
14
|
+
export function createDefaultGamesService(): IZRomulatorGamesService {
|
|
15
|
+
const { api } = new ZRomulatorEnvironmentBuilder().build();
|
|
16
|
+
const endpoint = new ZUrlBuilder().parse(api).append("games").build();
|
|
17
|
+
const http = new ZHttpService();
|
|
18
|
+
return new ZRestfulService<IZRomulatorGame>(http, endpoint);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const ZRomulatorGamesServiceContext = createContext(
|
|
22
|
+
createDefaultGamesService(),
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
export const useGamesService = () => useContext(ZRomulatorGamesServiceContext);
|
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
import { ZCircusBy, ZCircusComponentModel } from "@zthun/cirque";
|
|
2
2
|
import {
|
|
3
3
|
ZAlertComponentModel,
|
|
4
|
+
ZBoxComponentModel,
|
|
5
|
+
ZCardComponentModel,
|
|
6
|
+
ZGridViewComponentModel,
|
|
4
7
|
ZSuspenseComponentModel,
|
|
5
8
|
} from "@zthun/fashion-boutique";
|
|
6
|
-
import { ZRomulatorSystemAvatarCardComponentModel } from "./system-avatar-card.cm.mjs";
|
|
7
9
|
|
|
8
10
|
export class ZRomulatorSystemPageComponentModel extends ZCircusComponentModel {
|
|
9
11
|
public static readonly Selector = ".ZRomulatorSystemPage-root";
|
|
10
12
|
|
|
11
13
|
public async loader(): Promise<ZSuspenseComponentModel | null> {
|
|
12
|
-
return ZCircusBy.optional(
|
|
14
|
+
return ZCircusBy.optional(
|
|
15
|
+
this.driver,
|
|
16
|
+
ZSuspenseComponentModel,
|
|
17
|
+
"system-loading",
|
|
18
|
+
);
|
|
13
19
|
}
|
|
14
20
|
|
|
15
21
|
public async loading(): Promise<boolean> {
|
|
@@ -24,10 +30,15 @@ export class ZRomulatorSystemPageComponentModel extends ZCircusComponentModel {
|
|
|
24
30
|
return ZCircusBy.optional(this.driver, ZAlertComponentModel);
|
|
25
31
|
}
|
|
26
32
|
|
|
27
|
-
public system(): Promise<
|
|
28
|
-
return ZCircusBy.optional(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
33
|
+
public system(): Promise<ZCardComponentModel | null> {
|
|
34
|
+
return ZCircusBy.optional(this.driver, ZCardComponentModel, "system-info");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public games(): Promise<ZGridViewComponentModel | null> {
|
|
38
|
+
return ZCircusBy.optional(this.driver, ZGridViewComponentModel);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public game(id: string): Promise<ZBoxComponentModel | null> {
|
|
42
|
+
return ZCircusBy.optional(this.driver, ZBoxComponentModel, id);
|
|
32
43
|
}
|
|
33
44
|
}
|
|
@@ -13,7 +13,9 @@ import {
|
|
|
13
13
|
ZDataSourceStatic,
|
|
14
14
|
ZFilterBinaryBuilder,
|
|
15
15
|
} from "@zthun/helpful-query";
|
|
16
|
+
import type { IZRomulatorGame } from "@zthun/romulator-client";
|
|
16
17
|
import {
|
|
18
|
+
ZRomulatorGameBuilder,
|
|
17
19
|
ZRomulatorSystemBuilder,
|
|
18
20
|
ZRomulatorSystemId,
|
|
19
21
|
} from "@zthun/romulator-client";
|
|
@@ -23,6 +25,10 @@ import { noop } from "lodash-es";
|
|
|
23
25
|
import type { Mocked } from "vitest";
|
|
24
26
|
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
25
27
|
import { mock } from "vitest-mock-extended";
|
|
28
|
+
import {
|
|
29
|
+
ZRomulatorGamesServiceContext,
|
|
30
|
+
type IZRomulatorGamesService,
|
|
31
|
+
} from "../games/games-service.mjs";
|
|
26
32
|
import { ZRomulatorSystemPageComponentModel } from "./system-page.cm.mjs";
|
|
27
33
|
import { ZRomulatorSystemPage } from "./system-page.js";
|
|
28
34
|
import type { IZRomulatorSystemsService } from "./systems-service.mjs";
|
|
@@ -35,11 +41,29 @@ interface ZRomulatorSystemPageProps {
|
|
|
35
41
|
describe("SystemPage", () => {
|
|
36
42
|
const nes = new ZRomulatorSystemBuilder()
|
|
37
43
|
.id(ZRomulatorSystemId.Nintendo)
|
|
44
|
+
.name("Nintendo Entertainment System")
|
|
45
|
+
.build();
|
|
46
|
+
|
|
47
|
+
const batman = new ZRomulatorGameBuilder()
|
|
48
|
+
.id("nes-batman")
|
|
49
|
+
.name("Batman")
|
|
50
|
+
.system(ZRomulatorSystemId.Nintendo)
|
|
51
|
+
.build();
|
|
52
|
+
const mario = new ZRomulatorGameBuilder()
|
|
53
|
+
.id("nes-super-mario-bros")
|
|
54
|
+
.name("Super Mario Bros.")
|
|
55
|
+
.system(ZRomulatorSystemId.Nintendo)
|
|
56
|
+
.build();
|
|
57
|
+
const superMetroid = new ZRomulatorGameBuilder()
|
|
58
|
+
.id("snes-super-metroid")
|
|
59
|
+
.name("Super Metroid")
|
|
60
|
+
.system(ZRomulatorSystemId.SuperNintendo)
|
|
38
61
|
.build();
|
|
39
62
|
|
|
40
63
|
let _driver: IZCircusDriver;
|
|
41
64
|
let _renderer: IZCircusSetup;
|
|
42
65
|
let _systems: Mocked<IZRomulatorSystemsService>;
|
|
66
|
+
let _games: Mocked<IZRomulatorGamesService>;
|
|
43
67
|
|
|
44
68
|
beforeEach(() => {
|
|
45
69
|
const source = new ZDataSourceStatic([nes]);
|
|
@@ -56,6 +80,16 @@ describe("SystemPage", () => {
|
|
|
56
80
|
|
|
57
81
|
return required(item);
|
|
58
82
|
});
|
|
83
|
+
|
|
84
|
+
const __games = new ZDataSourceStatic<IZRomulatorGame>([
|
|
85
|
+
batman,
|
|
86
|
+
mario,
|
|
87
|
+
superMetroid,
|
|
88
|
+
]);
|
|
89
|
+
|
|
90
|
+
_games = mock<IZRomulatorGamesService>();
|
|
91
|
+
_games.retrieve.mockImplementation(async (req) => __games.retrieve(req));
|
|
92
|
+
_games.count.mockImplementation(async (req) => __games.count(req));
|
|
59
93
|
});
|
|
60
94
|
|
|
61
95
|
afterEach(async () => {
|
|
@@ -63,19 +97,23 @@ describe("SystemPage", () => {
|
|
|
63
97
|
await _renderer?.destroy?.call(_renderer);
|
|
64
98
|
});
|
|
65
99
|
|
|
100
|
+
function createSystemMemoryHistory(system: string) {
|
|
101
|
+
return createMemoryHistory({ initialEntries: [`/systems/${system}`] });
|
|
102
|
+
}
|
|
103
|
+
|
|
66
104
|
async function createTestTarget(props: ZRomulatorSystemPageProps = {}) {
|
|
67
|
-
const {
|
|
68
|
-
history = createMemoryHistory({ initialEntries: [`/systems/${nes.id}`] }),
|
|
69
|
-
} = props;
|
|
105
|
+
const { history = createSystemMemoryHistory(nes.id) } = props;
|
|
70
106
|
|
|
71
107
|
const element = (
|
|
72
108
|
<ZRomulatorSystemsServiceContext value={_systems}>
|
|
73
|
-
<
|
|
74
|
-
<
|
|
75
|
-
<
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
109
|
+
<ZRomulatorGamesServiceContext value={_games}>
|
|
110
|
+
<ZTestRouter navigator={history} location={history.location}>
|
|
111
|
+
<ZRouteMap>
|
|
112
|
+
<ZRoute path="/systems/:id" element={<ZRomulatorSystemPage />} />
|
|
113
|
+
<ZRoute path="*" element={<ZNotFound />} />
|
|
114
|
+
</ZRouteMap>
|
|
115
|
+
</ZTestRouter>
|
|
116
|
+
</ZRomulatorGamesServiceContext>
|
|
79
117
|
</ZRomulatorSystemsServiceContext>
|
|
80
118
|
);
|
|
81
119
|
|
|
@@ -101,9 +139,7 @@ describe("SystemPage", () => {
|
|
|
101
139
|
describe("Error", () => {
|
|
102
140
|
it("should show an error alert if the system cannot be found", async () => {
|
|
103
141
|
// Arrange.
|
|
104
|
-
const history =
|
|
105
|
-
initialEntries: ["/systems/does-not-exist"],
|
|
106
|
-
});
|
|
142
|
+
const history = createSystemMemoryHistory("does-not-exist");
|
|
107
143
|
const target = await createTestTarget({ history });
|
|
108
144
|
await target.load();
|
|
109
145
|
|
|
@@ -122,11 +158,58 @@ describe("SystemPage", () => {
|
|
|
122
158
|
await target.load();
|
|
123
159
|
|
|
124
160
|
// Act.
|
|
125
|
-
const
|
|
126
|
-
|
|
161
|
+
const actual = await target.system();
|
|
162
|
+
|
|
163
|
+
// Assert.
|
|
164
|
+
expect(actual).toBeTruthy();
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it("should render the games list", async () => {
|
|
168
|
+
// Arrange.
|
|
169
|
+
const target = await createTestTarget();
|
|
170
|
+
await target.load();
|
|
171
|
+
|
|
172
|
+
// Act.
|
|
173
|
+
const actual = target.games();
|
|
174
|
+
|
|
175
|
+
// Assert.
|
|
176
|
+
expect(actual).toBeTruthy();
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
describe("Games", () => {
|
|
181
|
+
it("should only load games that are assigned to the given system", async () => {
|
|
182
|
+
// Arrange.
|
|
183
|
+
const target = await createTestTarget();
|
|
184
|
+
await target.load();
|
|
185
|
+
const games = await target.games();
|
|
186
|
+
await games?.load();
|
|
187
|
+
|
|
188
|
+
// Act.
|
|
189
|
+
const _batman = await target.game(batman.id);
|
|
190
|
+
const _mario = await target.game(mario.id);
|
|
191
|
+
const _metroid = await target.game(superMetroid.id);
|
|
192
|
+
|
|
193
|
+
// Assert.
|
|
194
|
+
expect(_batman).toBeTruthy();
|
|
195
|
+
expect(_mario).toBeTruthy();
|
|
196
|
+
expect(_metroid).toBeFalsy();
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it("should navigate to the games page when clicked", async () => {
|
|
200
|
+
// Arrange.
|
|
201
|
+
const history = createSystemMemoryHistory(nes.id);
|
|
202
|
+
const target = await createTestTarget({ history });
|
|
203
|
+
await target.load();
|
|
204
|
+
const games = await target.games();
|
|
205
|
+
await games?.load();
|
|
206
|
+
|
|
207
|
+
// Act.
|
|
208
|
+
const _batman = await target.game(batman.id);
|
|
209
|
+
await _batman?.click();
|
|
127
210
|
|
|
128
211
|
// Assert.
|
|
129
|
-
expect(
|
|
212
|
+
expect(history.location.pathname).toEqual(`/games/${batman.id}`);
|
|
130
213
|
});
|
|
131
214
|
});
|
|
132
215
|
});
|
|
@@ -3,23 +3,48 @@ import {
|
|
|
3
3
|
useParams,
|
|
4
4
|
ZAlert,
|
|
5
5
|
ZBreadcrumbsLocation,
|
|
6
|
+
ZCard,
|
|
7
|
+
ZIconFontAwesome,
|
|
6
8
|
ZStack,
|
|
7
9
|
ZSuspenseProgress,
|
|
8
10
|
} from "@zthun/fashion-boutique";
|
|
9
11
|
import { ZSizeFixed } from "@zthun/fashion-tailor";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
|
|
12
|
+
import { firstDefined } from "@zthun/helpful-fn";
|
|
13
|
+
import {
|
|
14
|
+
ZDataRequestBuilder,
|
|
15
|
+
ZFilterBinaryBuilder,
|
|
16
|
+
} from "@zthun/helpful-query";
|
|
17
|
+
import {
|
|
18
|
+
isStateErrored,
|
|
19
|
+
isStateLoading,
|
|
20
|
+
useSyncState,
|
|
21
|
+
} from "@zthun/helpful-react";
|
|
22
|
+
import { useMemo } from "react";
|
|
23
|
+
import { ZRomulatorGamesList } from "../games/games-list.js";
|
|
13
24
|
import { useSystem } from "./systems-service.mjs";
|
|
14
25
|
|
|
15
26
|
export function ZRomulatorSystemPage() {
|
|
16
27
|
const { id } = useParams();
|
|
17
28
|
const { error } = useFashionTheme();
|
|
18
29
|
const [system] = useSystem(firstDefined("", id));
|
|
30
|
+
const gameFilter = useMemo(
|
|
31
|
+
() =>
|
|
32
|
+
new ZFilterBinaryBuilder().subject("system").equal().value(id).build(),
|
|
33
|
+
[id],
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
const baseGameRequest = useMemo(
|
|
37
|
+
() => new ZDataRequestBuilder().filter(gameFilter).build(),
|
|
38
|
+
[gameFilter],
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
const [userRequest, setGameRequest] = useSyncState(baseGameRequest);
|
|
19
42
|
|
|
20
43
|
const renderSystemInformation = () => {
|
|
21
44
|
if (isStateLoading(system)) {
|
|
22
|
-
return
|
|
45
|
+
return (
|
|
46
|
+
<ZSuspenseProgress name="system-loading" height={ZSizeFixed.Large} />
|
|
47
|
+
);
|
|
23
48
|
}
|
|
24
49
|
|
|
25
50
|
if (isStateErrored(system)) {
|
|
@@ -32,14 +57,39 @@ export function ZRomulatorSystemPage() {
|
|
|
32
57
|
);
|
|
33
58
|
}
|
|
34
59
|
|
|
35
|
-
return
|
|
60
|
+
return (
|
|
61
|
+
<ZStack gap={ZSizeFixed.Medium}>
|
|
62
|
+
<ZCard
|
|
63
|
+
name="system-info"
|
|
64
|
+
TitleProps={{
|
|
65
|
+
avatar: (
|
|
66
|
+
<ZIconFontAwesome name="puzzle-piece" width={ZSizeFixed.Medium} />
|
|
67
|
+
),
|
|
68
|
+
heading: system.name,
|
|
69
|
+
subHeading: `Generation ${system.generation}`,
|
|
70
|
+
}}
|
|
71
|
+
/>
|
|
72
|
+
<ZCard
|
|
73
|
+
name="game-list"
|
|
74
|
+
TitleProps={{
|
|
75
|
+
avatar: (
|
|
76
|
+
<ZIconFontAwesome name="gamepad" width={ZSizeFixed.Medium} />
|
|
77
|
+
),
|
|
78
|
+
heading: "Games",
|
|
79
|
+
subHeading: `Your ${system.name} Games`,
|
|
80
|
+
}}
|
|
81
|
+
>
|
|
82
|
+
<ZRomulatorGamesList
|
|
83
|
+
value={userRequest}
|
|
84
|
+
onValueChange={setGameRequest}
|
|
85
|
+
/>
|
|
86
|
+
</ZCard>
|
|
87
|
+
</ZStack>
|
|
88
|
+
);
|
|
36
89
|
};
|
|
37
90
|
|
|
38
91
|
return (
|
|
39
|
-
<ZStack
|
|
40
|
-
gap={ZSizeFixed.Medium}
|
|
41
|
-
className={cssJoinDefined("ZRomulatorSystemPage-root")}
|
|
42
|
-
>
|
|
92
|
+
<ZStack gap={ZSizeFixed.Medium} className={"ZRomulatorSystemPage-root"}>
|
|
43
93
|
<ZBreadcrumbsLocation />
|
|
44
94
|
|
|
45
95
|
{renderSystemInformation()}
|
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
|
-
useCss,
|
|
3
2
|
useFashionTheme,
|
|
4
3
|
useNavigate,
|
|
5
4
|
ZBox,
|
|
6
5
|
ZBreadcrumbsLocation,
|
|
7
6
|
ZCard,
|
|
8
|
-
ZContentTitle,
|
|
9
7
|
ZGridView,
|
|
10
8
|
ZIconFontAwesome,
|
|
11
9
|
ZImageSource,
|
|
12
10
|
ZStack,
|
|
13
11
|
} from "@zthun/fashion-boutique";
|
|
14
12
|
import { ZSizeFixed, ZSizeVaried } from "@zthun/fashion-tailor";
|
|
15
|
-
import { css, cssJoinDefined, ZOrientation } from "@zthun/helpful-fn";
|
|
16
13
|
import { ZDataRequestBuilder, ZSortBuilder } from "@zthun/helpful-query";
|
|
17
14
|
import type { IZRomulatorSystem } from "@zthun/romulator-client";
|
|
18
15
|
import { useState } from "react";
|
|
@@ -31,15 +28,9 @@ const DefaultSystemRequest = new ZDataRequestBuilder()
|
|
|
31
28
|
export function ZRomulatorSystemsPage() {
|
|
32
29
|
const { body } = useFashionTheme();
|
|
33
30
|
const navigate = useNavigate();
|
|
34
|
-
const
|
|
31
|
+
const systems = useSystemsService();
|
|
35
32
|
const [request, setRequest] = useState(DefaultSystemRequest);
|
|
36
33
|
|
|
37
|
-
const tile = useCss(css`
|
|
38
|
-
& {
|
|
39
|
-
height: 100%;
|
|
40
|
-
}
|
|
41
|
-
`);
|
|
42
|
-
|
|
43
34
|
const renderTile = (system: IZRomulatorSystem) => {
|
|
44
35
|
const { api } = new ZRomulatorEnvironmentBuilder().build();
|
|
45
36
|
const id = `${system.id}-wheel`;
|
|
@@ -57,21 +48,11 @@ export function ZRomulatorSystemsPage() {
|
|
|
57
48
|
onClick={() => navigate(system.id)}
|
|
58
49
|
>
|
|
59
50
|
<ZStack
|
|
60
|
-
className={cssJoinDefined(tile)}
|
|
61
|
-
gap={ZSizeFixed.Medium}
|
|
62
|
-
orientation={ZOrientation.Horizontal}
|
|
63
51
|
justify={{ content: "center" }}
|
|
64
52
|
align={{ items: "center" }}
|
|
53
|
+
height={ZSizeVaried.Full}
|
|
65
54
|
>
|
|
66
|
-
<
|
|
67
|
-
avatar={
|
|
68
|
-
<ZImageSource
|
|
69
|
-
src={wheel}
|
|
70
|
-
height={ZSizeVaried.Full}
|
|
71
|
-
width={ZSizeVaried.Full}
|
|
72
|
-
/>
|
|
73
|
-
}
|
|
74
|
-
/>
|
|
55
|
+
<ZImageSource src={wheel} width={ZSizeVaried.Full} />
|
|
75
56
|
</ZStack>
|
|
76
57
|
</ZBox>
|
|
77
58
|
);
|
|
@@ -101,10 +82,10 @@ export function ZRomulatorSystemsPage() {
|
|
|
101
82
|
},
|
|
102
83
|
gap: ZSizeFixed.Medium,
|
|
103
84
|
}}
|
|
85
|
+
dataSource={systems}
|
|
86
|
+
renderItem={renderTile}
|
|
104
87
|
value={request}
|
|
105
88
|
onValueChange={setRequest}
|
|
106
|
-
dataSource={source}
|
|
107
|
-
renderItem={renderTile}
|
|
108
89
|
/>
|
|
109
90
|
</ZCard>
|
|
110
91
|
</ZStack>
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { ZCircusComponentModel } from "@zthun/cirque";
|
|
2
|
-
import { ZCardComponentModel } from "@zthun/fashion-boutique";
|
|
3
|
-
import { firstDefined } from "@zthun/helpful-fn";
|
|
4
|
-
|
|
5
|
-
export class ZRomulatorSystemAvatarCardComponentModel extends ZCircusComponentModel {
|
|
6
|
-
public static readonly Selector = ".ZRomulatorSystemCard-root";
|
|
7
|
-
|
|
8
|
-
public card(): Promise<ZCardComponentModel> {
|
|
9
|
-
return Promise.resolve(new ZCardComponentModel(this.driver));
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
public async id(): Promise<string> {
|
|
13
|
-
const card = await this.card();
|
|
14
|
-
const name = await card.driver.attribute("data-name");
|
|
15
|
-
return firstDefined("", name);
|
|
16
|
-
}
|
|
17
|
-
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import type { IZCard } from "@zthun/fashion-boutique";
|
|
2
|
-
import { useCss, ZCard, ZImageSource, ZStack } from "@zthun/fashion-boutique";
|
|
3
|
-
import { ZSizeFixed } from "@zthun/fashion-tailor";
|
|
4
|
-
import { css, cssJoinDefined, ZOrientation } from "@zthun/helpful-fn";
|
|
5
|
-
|
|
6
|
-
export interface IZRomulatorSystemAvatarCard {
|
|
7
|
-
system: any;
|
|
8
|
-
|
|
9
|
-
CardProps?: Pick<IZCard, "footer">;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function ZRomulatorSystemAvatarCard(props: IZRomulatorSystemAvatarCard) {
|
|
13
|
-
const { system, CardProps } = props;
|
|
14
|
-
const src = `/systems/wheel/${system.id}.png`;
|
|
15
|
-
|
|
16
|
-
const _className = useCss(css`
|
|
17
|
-
&.ZRomulatorSystemCard-root .ZRomulatorSystemCard-avatar {
|
|
18
|
-
height: 100%;
|
|
19
|
-
width: 100%;
|
|
20
|
-
}
|
|
21
|
-
`);
|
|
22
|
-
|
|
23
|
-
return (
|
|
24
|
-
<ZCard
|
|
25
|
-
className={cssJoinDefined("ZRomulatorSystemCard-root", _className)}
|
|
26
|
-
name={system.id}
|
|
27
|
-
{...CardProps}
|
|
28
|
-
>
|
|
29
|
-
<ZStack
|
|
30
|
-
className="ZRomulatorSystemCard-avatar"
|
|
31
|
-
orientation={ZOrientation.Horizontal}
|
|
32
|
-
justify={{ content: "center", items: "center" }}
|
|
33
|
-
align={{ content: "center", items: "center" }}
|
|
34
|
-
>
|
|
35
|
-
<ZImageSource src={src} width={ZSizeFixed.ExtraLarge} />
|
|
36
|
-
</ZStack>
|
|
37
|
-
</ZCard>
|
|
38
|
-
);
|
|
39
|
-
}
|