Nodification of app. #1

Merged
florian merged 3 commits from feature/ihm-improvement into master 2020-07-12 13:22:37 +02:00
13 changed files with 6723 additions and 99 deletions

2
.gitignore vendored
View File

@@ -1 +1,3 @@
**/node_modules
public/bundle.js
**/dist

17
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,17 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch Program",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}/src/back/app.js"
}
]
}

6513
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -4,15 +4,21 @@
"description": "",
"main": "index.js",
"scripts": {
"start": "webpack --watch",
"start-front": "webpack --watch",
"start-back": "node src/back/app.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"nodemon": "^2.0.4",
"uglifyjs-webpack-plugin": "^2.2.0",
"webpack": "^4.41.2",
"webpack-cli": "^3.3.10",
"webpack-dev-server": "^3.9.0"
},
"dependencies": {
"express": "^4.17.1",
"mustache-express": "^1.3.0"
}
}

36
public/css/styles.css Normal file
View File

@@ -0,0 +1,36 @@
body {
background-color: #222;
color: white;
}
.choice-btn {
padding: 5px 10px;
border-radius: 3px;
border: 1px solid black;
margin: 2px;
}
.choice-btn:hover {
background-color: #ccc;
}
.choice-div {
border: 1px green solid;
margin-left: auto;
}
.node-div {
border: 1px red solid;
}
#content {
width: 500px;
margin: auto;
}
#history div {
width: 450px;
margin-top: 10px;
margin-bottom: 10px;
padding: 5px;
}

View File

@@ -1,30 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Textual game</title>
<meta name="description" content="The HTML5 Herald">
<meta name="author" content="SitePoint">
<link rel="stylesheet" href="css/styles.css?v=1.0">
<style type="text/css">
.choice-btn {
padding: 5px 10px;
border-radius: 3px;
border: 1px solid black;
margin: 2px;
}
.choice-btn:hover {
background-color: #ccc;
}
</style>
</head>
<body>
<h1 id="active-node"></h1>
<div id="choices"></div>
<script src="./bundle.js"></script>
</body>
</html>

1
public/js/app-bundle.js Normal file

File diff suppressed because one or more lines are too long

23
src/back/app.js Normal file
View File

@@ -0,0 +1,23 @@
const mustacheExpress = require('mustache-express');
const express = require('express');
const nodes = require('./nodes.json');
const app = express();
// Register '.mustache' extension with The Mustache Express
app.engine('mustache', mustacheExpress());
app.set('view engine', 'mustache');
app.set('views', __dirname + '/views');
app.use(express.static('public'));
app.get('/', (request, response) => {
response.render('home', { title: 'Textual game!' });
});
app.get('/nodes', (request, response) => {
response.setHeader('Content-Type', 'application/json');
response.end(JSON.stringify(nodes));
});
app.listen(3000, () => {
console.log('Example app listening on port 3000!');
});

43
src/back/nodes.json Normal file
View File

@@ -0,0 +1,43 @@
[
{
"id": "node-1",
"text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
"choices": [
{
"id": "choice-1",
"text": "Sed ut perspiciatis unde omnis.",
"nextNode": "node-2"
},
{
"id": "choice-2",
"text": "Nemo enim ipsam voluptatem",
"nextNode": "node-exit"
},
{
"id": "choice-3",
"text": "Ut enim ad minima veniam",
"nextNode": "node-exit"
}
]
},
{
"id": "node-2",
"text": "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
"choices": [
{
"id": "choice-4",
"text": "Quis autem vel eum iure",
"nextNode": "node-exit"
},
{
"id": "choice-5",
"text": "At vero eos et accusamus",
"nextNode": "node-exit"
}
]
},
{
"id": "node-exit",
"text": "Game over!"
}
]

View File

@@ -0,0 +1,20 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Textual game</title>
<meta name="description" content="The HTML5 Herald">
<meta name="author" content="SitePoint">
<link rel="stylesheet" href="css/styles.css">
</head>
<body>
<div id="content">
<h1>{{title}}</h1>
<div id="history"></div>
<div id="choices"></div>
</div>
<script src="js/app-bundle.js"></script>
</body>
</html>

53
src/front/index.js Normal file
View File

@@ -0,0 +1,53 @@
let jsonContent = [];
function printChoices(node) {
if (node.choices) {
return node.choices.forEach(choice => {
const newBtn = document.createElement('button');
newBtn.onclick = () => selectChoice(choice.id);
newBtn.innerHTML = choice.text;
newBtn.classList.add('choice-btn');
document.getElementById('choices').appendChild(newBtn);
});
}
}
function selectChoice(choiceId) {
console.log('Click on choice ', choiceId);
const allChoices = jsonContent.flatMap(node =>
!node.choices ? [] : node.choices
);
const selectedChoice = allChoices.find(choice => choice.id === choiceId);
const nextNode = jsonContent.find(node => node.id === selectedChoice.nextNode);
addChoiceToHistory(selectedChoice);
updateActiveNode(nextNode);
}
function addChoiceToHistory(selectedChoice) {
const choiceDiv = document.createElement('div');
choiceDiv.innerHTML = selectedChoice.text;
choiceDiv.classList.add('choice-div');
document.getElementById('history').appendChild(choiceDiv);
}
function updateActiveNode(node) {
const nodeDiv = document.createElement('div');
nodeDiv.innerHTML = node.text;
nodeDiv.classList.add('node-div');
document.getElementById('history').appendChild(nodeDiv);
document.getElementById('choices').innerHTML = '';
printChoices(node);
}
window.onload = () => {
fetch('/nodes', {
method: 'GET'
})
.then(response => response.json())
.then(data => {
jsonContent = data;
updateActiveNode(jsonContent[0]);
});
}

View File

@@ -1,58 +0,0 @@
const jsonContent = [{
id: 'node-1',
text: 'Node 1',
choices: [{
id: 'choice-1',
text: 'Choice 1',
nextNode: 'node-2'
}, {
id: 'choice-2',
text: 'Choice 2',
nextNode: 'node-exit'
}]
}, {
id: 'node-2',
text: 'Node 2',
choices: [{
id: 'choice-3',
text: 'Choice 3',
nextNode: 'node-exit'
}, {
id: 'choice-4',
text: 'Choice 4',
nextNode: 'node-exit'
}]
}, {
id: 'node-exit',
text: 'Game over!'
}];
function printChoices(node) {
return node.choices.forEach(choice => {
const newBtn = document.createElement('button');
newBtn.onclick = () => selectChoice(choice.id);
newBtn.innerHTML = choice.text;
newBtn.classList.add('choice-btn');
document.getElementById('choices').appendChild(newBtn);
});
}
function selectChoice(choiceId) {
console.log('Click on choice ', choiceId);
const allChoices = jsonContent.flatMap(node =>
!node.choices ? [] : node.choices
);
const selectedChoice = allChoices.find(choice => choice.id === choiceId);
const nextNode = jsonContent.find(node => node.id === selectedChoice.nextNode);
updateActiveNode(nextNode);
}
function updateActiveNode(node) {
document.getElementById('active-node').innerHTML = node.text;
document.getElementById('choices').innerHTML = '';
printChoices(node);
}
window.onload = () => {
updateActiveNode(jsonContent[0]);
}

View File

@@ -2,15 +2,16 @@ const webpack = require("webpack");
const path = require("path");
const UglifyJSPlugin = require("uglifyjs-webpack-plugin");
let config = {
entry: "./src/index.js",
module.exports = {
entry: {
app: "./src/front/index.js",
},
output: {
path: path.resolve(__dirname, "./public"),
filename: "./bundle.js"
path: path.resolve(__dirname, "./public/js"),
filename: "[name]-bundle.js"
},
devServer: {
contentBase: path.resolve(__dirname, "./public"),
contentBase: path.resolve(__dirname, "./public/js"),
historyApiFallback: true,
inline: true,
open: true,
@@ -21,7 +22,4 @@ let config = {
new webpack.SourceMapDevToolPlugin({})
],
devtool: "eval-source-map"
}
module.exports = config;
};