mirror of
https://github.com/docker/getting-started-app.git
synced 2025-07-04 16:31:09 -04:00
first commit
This commit is contained in:
179
src/static/js/app.js
Normal file
179
src/static/js/app.js
Normal file
@ -0,0 +1,179 @@
|
||||
function App() {
|
||||
const { Container, Row, Col } = ReactBootstrap;
|
||||
return (
|
||||
<Container>
|
||||
<Row>
|
||||
<Col md={{ offset: 3, span: 6 }}>
|
||||
<TodoListCard />
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
function TodoListCard() {
|
||||
const [items, setItems] = React.useState(null);
|
||||
|
||||
React.useEffect(() => {
|
||||
fetch('/items')
|
||||
.then(r => r.json())
|
||||
.then(setItems);
|
||||
}, []);
|
||||
|
||||
const onNewItem = React.useCallback(
|
||||
newItem => {
|
||||
setItems([...items, newItem]);
|
||||
},
|
||||
[items],
|
||||
);
|
||||
|
||||
const onItemUpdate = React.useCallback(
|
||||
item => {
|
||||
const index = items.findIndex(i => i.id === item.id);
|
||||
setItems([
|
||||
...items.slice(0, index),
|
||||
item,
|
||||
...items.slice(index + 1),
|
||||
]);
|
||||
},
|
||||
[items],
|
||||
);
|
||||
|
||||
const onItemRemoval = React.useCallback(
|
||||
item => {
|
||||
const index = items.findIndex(i => i.id === item.id);
|
||||
setItems([...items.slice(0, index), ...items.slice(index + 1)]);
|
||||
},
|
||||
[items],
|
||||
);
|
||||
|
||||
if (items === null) return 'Loading...';
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<AddItemForm onNewItem={onNewItem} />
|
||||
{items.length === 0 && (
|
||||
<p className="text-center">No items yet! Add one above!</p>
|
||||
)}
|
||||
{items.map(item => (
|
||||
<ItemDisplay
|
||||
item={item}
|
||||
key={item.id}
|
||||
onItemUpdate={onItemUpdate}
|
||||
onItemRemoval={onItemRemoval}
|
||||
/>
|
||||
))}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
function AddItemForm({ onNewItem }) {
|
||||
const { Form, InputGroup, Button } = ReactBootstrap;
|
||||
|
||||
const [newItem, setNewItem] = React.useState('');
|
||||
const [submitting, setSubmitting] = React.useState(false);
|
||||
|
||||
const submitNewItem = e => {
|
||||
e.preventDefault();
|
||||
setSubmitting(true);
|
||||
fetch('/items', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ name: newItem }),
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
})
|
||||
.then(r => r.json())
|
||||
.then(item => {
|
||||
onNewItem(item);
|
||||
setSubmitting(false);
|
||||
setNewItem('');
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Form onSubmit={submitNewItem}>
|
||||
<InputGroup className="mb-3">
|
||||
<Form.Control
|
||||
value={newItem}
|
||||
onChange={e => setNewItem(e.target.value)}
|
||||
type="text"
|
||||
placeholder="New Item"
|
||||
aria-describedby="basic-addon1"
|
||||
/>
|
||||
<InputGroup.Append>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="success"
|
||||
disabled={!newItem.length}
|
||||
className={submitting ? 'disabled' : ''}
|
||||
>
|
||||
{submitting ? 'Adding...' : 'Add Item'}
|
||||
</Button>
|
||||
</InputGroup.Append>
|
||||
</InputGroup>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
|
||||
function ItemDisplay({ item, onItemUpdate, onItemRemoval }) {
|
||||
const { Container, Row, Col, Button } = ReactBootstrap;
|
||||
|
||||
const toggleCompletion = () => {
|
||||
fetch(`/items/${item.id}`, {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify({
|
||||
name: item.name,
|
||||
completed: !item.completed,
|
||||
}),
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
})
|
||||
.then(r => r.json())
|
||||
.then(onItemUpdate);
|
||||
};
|
||||
|
||||
const removeItem = () => {
|
||||
fetch(`/items/${item.id}`, { method: 'DELETE' }).then(() =>
|
||||
onItemRemoval(item),
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Container fluid className={`item ${item.completed && 'completed'}`}>
|
||||
<Row>
|
||||
<Col xs={1} className="text-center">
|
||||
<Button
|
||||
className="toggles"
|
||||
size="sm"
|
||||
variant="link"
|
||||
onClick={toggleCompletion}
|
||||
aria-label={
|
||||
item.completed
|
||||
? 'Mark item as incomplete'
|
||||
: 'Mark item as complete'
|
||||
}
|
||||
>
|
||||
<i
|
||||
className={`far ${
|
||||
item.completed ? 'fa-check-square' : 'fa-square'
|
||||
}`}
|
||||
/>
|
||||
</Button>
|
||||
</Col>
|
||||
<Col xs={10} className="name">
|
||||
{item.name}
|
||||
</Col>
|
||||
<Col xs={1} className="text-center remove">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="link"
|
||||
onClick={removeItem}
|
||||
aria-label="Remove Item"
|
||||
>
|
||||
<i className="fa fa-trash text-danger" />
|
||||
</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
ReactDOM.render(<App />, document.getElementById('root'));
|
Reference in New Issue
Block a user