wasp/waspc/lang-design/todoApp.wasp
2020-02-20 12:30:58 +01:00

132 lines
4.5 KiB
JavaScript

// Goal of this file is to re-create a TODO app from http://todomvc.com
// This file has "advanced" features in the sense that they might not yet be implemented by Wasp: this is just a proposal.
app todoMVC {
title: "ToDo MVC"
}
entity Task {
description :: string,
isDone :: boolean
}
// IDEA: `@connect Task as taskList` -> this would make it more obvious what is available, also we don't need to automatically try to guess what to import.
page Main {
route: "/",
content: {=jsx
<div className="mainContainer">
<h1>todos</h1>
<div className="createTaskForm">
{/* Toggle all */}
<button onClick={() => {
const setIsDoneTo = false
if (this.props.taskList.some(t => !t.isDone)) setIsDoneTo = true
this.props.taskList.map(t => this.props.updateTask(t, { isDone: setIsDoneTo }))
}}>
<Icon id="arrow-down" />
</button>
<CreateTaskForm />
</div>
<div className="taskListContainer">
<TaskList filter={this.state.taskFilter} /> { /* Filter here -> that is not supported by TaskList yet. */ }
</div>
<div className="footer">
{ /* TODO: This is maybe not very nice, inlined like this.
Also, we directly address taskList here, while in TaskList and in CreateTaskForm we address
it implicitly. */ }
<span>
{ this.props.taskList.filter(task => !task.isDone).length } items left
</span>
{ /* TODO: Can we also make this nicer? */ }
<button onClick={() => this.setState({ taskFilter: () => true })}> All </button>
<button onClick={() => this.setState({ taskFilter: task => !task.isDone })}> Active </button>
<button onClick={() => this.setState({ taskFilter: task => task.isDone })}> Completed </button>
{/* Clear completed */}
{ this.props.taskList.some(t => t.isDone) &&
<button onClick={() => { this.state.taskList.map(t => if (t.isDone) this.props.deleteTask(t)) }}>Clear completed</button>
}
</div>
</div>
jsx=},
style: {=css
div {
color: green;
}
.mainContainer {
display: flex;
flex-direction: column;
align-items: center;
}
.taskListContainer {
width: 60%;
}
css=}
}
// TODO: This part is not currently supported at all.
entity-form<Task> CreateTaskForm {
fields: {
description: {
placeholder: "What do you want to do?"
},
isDone: {
show: false,
defaultValue: false // Although not shown, this field will be set to "false".
}
},
submit: {
button: { show: false },
onEnter: true,
// Resets fields to their initial values after the submission.
resetAllFields: true
}
}
// TODO: This part is not currently supported at all.
entity-list<Task> TaskList {
allowItemEditing: true,
allowItemDeletion: true, // Items can be deleted, and this also deletes them for real.
fields: {
description: {
// The contract for render is: user can provide a function that:
// - receives a task as an input
// - returns a React Node or something that can be rendered by JSX
// - does not depend on any outer context
render: {=js
(task) => {
if (task.isDone) return (<s>{task.description}</s>)
return task.description
}
js=}
}
}
}
// TODO: This part is not currently supported at all.
// Idea would be to generate a script (bash) that does deployment and would be called
// with `npm deploy`, from generated frontend.
// NOTE: For now we don't care about environments (staging) yet, there is just one environment to deploy to (production).
deployment {
frontend: {
// TODO: In case of github, this would go into CNAME file.
// NOTE: optional.
customDomain: 'todo-app.examples.wasp-lang.dev',
github: { // NOTE: For now we allow only one deployment method (in this case "github") at once.
branch: 'gh-pages' // NOTE: optional.
}
}
}