editor: experiment with Material UI for sprite library implementation
This commit is contained in:
parent
6c8f2d780a
commit
1605e743f4
8 changed files with 238 additions and 7 deletions
|
@ -13,6 +13,10 @@ module.exports = {
|
|||
test: /\.css$/,
|
||||
use: [{ loader: 'style-loader' }, { loader: 'css-loader' }],
|
||||
},
|
||||
{
|
||||
test: /\.(woff|woff2|eot|ttf|otf)$/,
|
||||
use: 'file-loader',
|
||||
},
|
||||
{
|
||||
test: /\.(vs|fs)$/,
|
||||
use: 'raw-loader',
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
"eslint-plugin-prettier": "^3.1.2",
|
||||
"eslint-plugin-react": "^7.19.0",
|
||||
"eslint-plugin-react-hooks": "^2.5.0",
|
||||
"file-loader": "^6.0.0",
|
||||
"prettier": "^1.19.1",
|
||||
"raw-loader": "^4.0.0",
|
||||
"style-loader": "^1.1.3",
|
||||
|
@ -55,7 +56,9 @@
|
|||
"dependencies": {
|
||||
"@material-ui/core": "^4.9.5",
|
||||
"gl-matrix": "^3.2.1",
|
||||
"immutable": "^4.0.0-rc.12",
|
||||
"react": "^16.13.0",
|
||||
"react-dom": "^16.13.0"
|
||||
"react-dom": "^16.13.0",
|
||||
"typeface-roboto": "^0.0.75"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,64 @@
|
|||
import * as React from 'react';
|
||||
import Button from '@material-ui/core/Button';
|
||||
|
||||
import 'typeface-roboto';
|
||||
|
||||
import { createMuiTheme, makeStyles, Theme, ThemeProvider } from '@material-ui/core/styles';
|
||||
|
||||
import CssBaseline from '@material-ui/core/CssBaseline';
|
||||
import Drawer from '@material-ui/core/Drawer';
|
||||
|
||||
import { Library } from './library';
|
||||
|
||||
const drawerWidth = 240;
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) => ({
|
||||
root: {
|
||||
display: 'flex',
|
||||
},
|
||||
drawer: {
|
||||
width: drawerWidth,
|
||||
flexShrink: 0,
|
||||
},
|
||||
drawerPaper: {
|
||||
width: drawerWidth,
|
||||
},
|
||||
content: {
|
||||
flexGrow: 1,
|
||||
padding: theme.spacing(3),
|
||||
},
|
||||
}));
|
||||
|
||||
function EditorLayout(): JSX.Element {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
<CssBaseline />
|
||||
<Drawer
|
||||
className={classes.drawer}
|
||||
variant='permanent'
|
||||
classes={{
|
||||
paper: classes.drawerPaper,
|
||||
}}
|
||||
anchor='left'
|
||||
></Drawer>
|
||||
<main className={classes.content}>
|
||||
<Library />
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const theme = createMuiTheme({
|
||||
palette: {
|
||||
type: 'dark',
|
||||
},
|
||||
});
|
||||
|
||||
export function Editor(): JSX.Element {
|
||||
return (
|
||||
<Button variant='contained' color='primary'>
|
||||
Hello World
|
||||
</Button>
|
||||
<ThemeProvider theme={theme}>
|
||||
<EditorLayout />
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
|
|
83
src/renderer/editor/library.tsx
Normal file
83
src/renderer/editor/library.tsx
Normal file
|
@ -0,0 +1,83 @@
|
|||
import * as React from 'react';
|
||||
|
||||
import { makeStyles, Theme } from '@material-ui/core/styles';
|
||||
|
||||
import Box from '@material-ui/core/Box';
|
||||
import Container from '@material-ui/core/Container';
|
||||
|
||||
import { Tiling } from './types';
|
||||
import { useReadFile } from './util';
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) => ({
|
||||
container: {
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
},
|
||||
tile: {
|
||||
imageRendering: 'pixelated',
|
||||
border: `solid 1px ${theme.palette.divider}`,
|
||||
background: theme.palette.background.paper,
|
||||
width: 128,
|
||||
height: 128,
|
||||
margin: 4,
|
||||
},
|
||||
}));
|
||||
|
||||
const tilings: Tiling[] = [
|
||||
{
|
||||
size: 1,
|
||||
sprite: { name: 'dirt' },
|
||||
},
|
||||
{
|
||||
size: 1,
|
||||
sprite: { name: 'grass' },
|
||||
},
|
||||
{
|
||||
size: 1,
|
||||
sprite: { name: 'road_left' },
|
||||
},
|
||||
{
|
||||
size: 1,
|
||||
sprite: { name: 'road_right' },
|
||||
},
|
||||
];
|
||||
|
||||
interface TilingDisplayProps {
|
||||
tiling: Tiling;
|
||||
}
|
||||
|
||||
function TilingDisplay({ tiling }: TilingDisplayProps): JSX.Element | null {
|
||||
const classes = useStyles();
|
||||
|
||||
let name: string;
|
||||
|
||||
switch (tiling.size) {
|
||||
case 1:
|
||||
name = tiling.sprite.name;
|
||||
break;
|
||||
case 3:
|
||||
name = tiling.sprites[1][1].name;
|
||||
break;
|
||||
}
|
||||
|
||||
const path = `static/resources/sprite/tile/${name}.png`;
|
||||
const image = useReadFile(path, 'base64');
|
||||
|
||||
return <img src={image ? `data:image/png;base64,${image}` : undefined} className={classes.tile} />;
|
||||
}
|
||||
|
||||
export function Library(): JSX.Element {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Box display='inline-block'>
|
||||
<div className={classes.container}>
|
||||
{tilings.map((tiling, i) => (
|
||||
<TilingDisplay key={i} tiling={tiling} />
|
||||
))}
|
||||
</div>
|
||||
</Box>
|
||||
</Container>
|
||||
);
|
||||
}
|
16
src/renderer/editor/types.ts
Normal file
16
src/renderer/editor/types.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
export type Tuple3<T> = [T, T, T];
|
||||
export type Grid3x3<T> = Tuple3<Tuple3<T>>;
|
||||
|
||||
export type Sprite = {
|
||||
name: string;
|
||||
};
|
||||
|
||||
export type Tiling =
|
||||
| {
|
||||
size: 1;
|
||||
sprite: Sprite;
|
||||
}
|
||||
| {
|
||||
size: 3;
|
||||
sprites: Grid3x3<Sprite>;
|
||||
};
|
31
src/renderer/editor/util.ts
Normal file
31
src/renderer/editor/util.ts
Normal file
|
@ -0,0 +1,31 @@
|
|||
import { useCallback, useEffect, useState } from 'react';
|
||||
|
||||
import * as fs from 'fs';
|
||||
|
||||
export function usePromise<T>(f: () => Promise<T>): T | null {
|
||||
const [value, setValue] = useState<T | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
setValue(null);
|
||||
|
||||
let cancelled = false;
|
||||
|
||||
(async (): Promise<void> => {
|
||||
const v = await f();
|
||||
if (!cancelled) {
|
||||
setValue(v);
|
||||
}
|
||||
})();
|
||||
|
||||
return (): void => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [f]);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
export function useReadFile(path: string, encoding: BufferEncoding): string | null {
|
||||
const readFile = useCallback(() => fs.promises.readFile(path, encoding), [path, encoding]);
|
||||
return usePromise(readFile);
|
||||
}
|
|
@ -5,6 +5,12 @@
|
|||
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" />
|
||||
<meta name="viewport" content="minimum-scale=1, initial-scale=1, width=device-width" />
|
||||
<title>RPGedit</title>
|
||||
<style>
|
||||
html, body, #app {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
|
38
yarn.lock
38
yarn.lock
|
@ -2535,6 +2535,14 @@ file-entry-cache@^5.0.1:
|
|||
dependencies:
|
||||
flat-cache "^2.0.1"
|
||||
|
||||
file-loader@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-6.0.0.tgz#97bbfaab7a2460c07bcbd72d3a6922407f67649f"
|
||||
integrity sha512-/aMOAYEFXDdjG0wytpTL5YQLfZnnTmLNjn+AIrJ/6HVnTfDqLsVKUUwkDf4I4kgex36BvjuXEn/TX9B/1ESyqQ==
|
||||
dependencies:
|
||||
loader-utils "^2.0.0"
|
||||
schema-utils "^2.6.5"
|
||||
|
||||
file-uri-to-path@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
|
||||
|
@ -3205,6 +3213,11 @@ ignore@^4.0.6:
|
|||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
|
||||
integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==
|
||||
|
||||
immutable@^4.0.0-rc.12:
|
||||
version "4.0.0-rc.12"
|
||||
resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.0.0-rc.12.tgz#ca59a7e4c19ae8d9bf74a97bdf0f6e2f2a5d0217"
|
||||
integrity sha512-0M2XxkZLx/mi3t8NVwIm1g8nHoEmM9p9UBl/G9k4+hm0kBgOVdMV/B3CY5dQ8qG8qc80NN4gDV4HQv6FTJ5q7A==
|
||||
|
||||
import-fresh@^3.0.0:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.1.tgz#633ff618506e793af5ac91bf48b72677e15cbe66"
|
||||
|
@ -3609,6 +3622,13 @@ json5@^1.0.1:
|
|||
dependencies:
|
||||
minimist "^1.2.0"
|
||||
|
||||
json5@^2.1.2:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.2.tgz#43ef1f0af9835dd624751a6b7fa48874fb2d608e"
|
||||
integrity sha512-MoUOQ4WdiN3yxhm7NEVJSJrieAo5hNSLQ5sj05OTRHPL9HOBy8u4Bu88jsC1jvqAdN+E1bJmsUcZH+1HQxliqQ==
|
||||
dependencies:
|
||||
minimist "^1.2.5"
|
||||
|
||||
jsonfile@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
|
||||
|
@ -3792,6 +3812,15 @@ loader-utils@^1.0.2, loader-utils@^1.2.3:
|
|||
emojis-list "^3.0.0"
|
||||
json5 "^1.0.1"
|
||||
|
||||
loader-utils@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.0.tgz#e4cace5b816d425a166b5f097e10cd12b36064b0"
|
||||
integrity sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==
|
||||
dependencies:
|
||||
big.js "^5.2.2"
|
||||
emojis-list "^3.0.0"
|
||||
json5 "^2.1.2"
|
||||
|
||||
locate-path@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"
|
||||
|
@ -4091,7 +4120,7 @@ minimist@0.0.8:
|
|||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
|
||||
integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
|
||||
|
||||
minimist@^1.1.0, minimist@^1.1.3, minimist@^1.2.0:
|
||||
minimist@^1.1.0, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5:
|
||||
version "1.2.5"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
|
||||
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
|
||||
|
@ -5525,7 +5554,7 @@ schema-utils@^1.0.0:
|
|||
ajv-errors "^1.0.0"
|
||||
ajv-keywords "^3.1.0"
|
||||
|
||||
schema-utils@^2.5.0, schema-utils@^2.6.0, schema-utils@^2.6.4:
|
||||
schema-utils@^2.5.0, schema-utils@^2.6.0, schema-utils@^2.6.4, schema-utils@^2.6.5:
|
||||
version "2.6.5"
|
||||
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.6.5.tgz#c758f0a7e624263073d396e29cd40aa101152d8a"
|
||||
integrity sha512-5KXuwKziQrTVHh8j/Uxz+QUbxkaLW9X/86NBlx/gnKgtsZA2GIVMUn17qWhRFwF8jdYb3Dig5hRO/W5mZqy6SQ==
|
||||
|
@ -6322,6 +6351,11 @@ typedarray@^0.0.6:
|
|||
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
||||
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
|
||||
|
||||
typeface-roboto@^0.0.75:
|
||||
version "0.0.75"
|
||||
resolved "https://registry.yarnpkg.com/typeface-roboto/-/typeface-roboto-0.0.75.tgz#98d5ba35ec234bbc7172374c8297277099cc712b"
|
||||
integrity sha512-VrR/IiH00Z1tFP4vDGfwZ1esNqTiDMchBEXYY9kilT6wRGgFoCAlgkEUMHb1E3mB0FsfZhv756IF0+R+SFPfdg==
|
||||
|
||||
typescript@^3.8.3:
|
||||
version "3.8.3"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.3.tgz#409eb8544ea0335711205869ec458ab109ee1061"
|
||||
|
|
Reference in a new issue