@powerhousedao/academy 2.5.0-dev.2 → 2.5.0-dev.21
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 +124 -0
- package/docs/academy/01-GetStarted/00-ExploreDemoPackage.md +18 -10
- package/docs/academy/01-GetStarted/01-CreateNewPowerhouseProject.md +36 -39
- package/docs/academy/01-GetStarted/02-DefineToDoListDocumentModel.md +21 -6
- package/docs/academy/01-GetStarted/03-ImplementOperationReducers.md +5 -0
- package/docs/academy/01-GetStarted/04-BuildToDoListEditor.md +148 -420
- package/docs/academy/01-GetStarted/_04-BuildToDoListEditor +495 -0
- package/docs/academy/01-GetStarted/home.mdx +15 -15
- package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/01-WhatIsADocumentModel.md +11 -2
- package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/02-SpecifyTheStateSchema.md +5 -0
- package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/01-BuildingDocumentEditors.md +5 -37
- package/docs/academy/02-MasteryTrack/05-Launch/02-PublishYourProject.md +70 -5
- package/docs/academy/02-MasteryTrack/05-Launch/03-SetupEnvironment.md +159 -73
- package/docs/academy/02-MasteryTrack/05-Launch/{03-RunOnACloudServer.md → _03-RunOnACloudServer} +8 -5
- package/docs/academy/04-APIReferences/00-PowerhouseCLI.md +7 -40
- package/docs/academy/06-ComponentLibrary/00-DocumentEngineering.md +67 -21
- package/docs/academy/06-ComponentLibrary/02-CreateCustomScalars.md +403 -0
- package/docs/academy/06-ComponentLibrary/{02-BuildingWithScalars.md → 02-ScalarComponent.mdx} +10 -12
- package/docs/academy/06-ComponentLibrary/{04-Complex-Components/01-sidebar.mdx → 03-ComplexComponent.mdx} +3 -1
- package/docs/academy/06-ComponentLibrary/03-IntegrateIntoAReactComponent.md +1 -0
- package/docs/academy/06-ComponentLibrary/04-LayoutComponent.mdx +5 -0
- package/docs/academy/06-ComponentLibrary/05-FragmentsComponent.mdx +5 -0
- package/docs/academy/07-Cookbook.md +0 -4
- package/docs/academy/08-Glossary.md +4 -1
- package/docs/academy/09-AIResources +23 -0
- package/package.json +1 -1
- package/sidebars.ts +6 -45
- package/docs/academy/06-ComponentLibrary/03-Scalar-Components/01-phid-field.mdx +0 -72
- package/docs/academy/06-ComponentLibrary/03-Scalar-Components/02-input-field.mdx +0 -0
- package/docs/academy/06-ComponentLibrary/05-Layout-Components/01-test-toupdate.mdx +0 -61
- package/docs/academy/06-ComponentLibrary/06-Fragments/01-test-toupdate.mdx +0 -61
- /package/docs/academy/02-MasteryTrack/05-Launch/{02-IntroductionToPackages.md → 01-IntroductionToPackages.md} +0 -0
- /package/docs/academy/02-MasteryTrack/05-Launch/{00-IntegrateInAFront-End → _00-IntegrateInAFront-End} +0 -0
- /package/docs/academy/02-MasteryTrack/05-Launch/{01-IntroducingFusion → _01-IntroducingFusion} +0 -0
- /package/docs/academy/02-MasteryTrack/05-Launch/{04-GraphQLNamespacing → _04-GraphQLNamespacing} +0 -0
- /package/docs/academy/02-MasteryTrack/05-Launch/{05-LaunchYourBackend.md → _05-LaunchYourBackend} +0 -0
- /package/docs/academy/02-MasteryTrack/05-Launch/{06-LaunchYourFrontend.md → _06-LaunchYourFrontend} +0 -0
- /package/docs/academy/04-APIReferences/{01-ReactHooks.md → 01-ReactHooks} +0 -0
- /package/docs/academy/04-APIReferences/{02-ReactorAPI.md → 02-ReactorAPI} +0 -0
- /package/docs/academy/04-APIReferences/{03-Configuration.md → 03-Configuration} +0 -0
|
@@ -4,7 +4,8 @@ In this chapter we will continue with the interface or editor implementation of
|
|
|
4
4
|
|
|
5
5
|
## Generate the editor template
|
|
6
6
|
|
|
7
|
-
Run the command below to generate the editor template for the **ToDoList** document model.
|
|
7
|
+
Run the command below to generate the editor template for the **ToDoList** document model.
|
|
8
|
+
This command reads the **ToDoList** document model definition from the `document-models` folder and generates the editor template in the `editors/to-do-list` folder as `editor.tsx`.
|
|
8
9
|
|
|
9
10
|
Notice the `--editor` flag which specifies the **ToDoList** document model, and the `--document-types` flag defines the document type `powerhouse/todolist`.
|
|
10
11
|
|
|
@@ -19,452 +20,171 @@ Once complete, navigate to the `editors/to-do-list/editor.tsx` file and open it
|
|
|
19
20
|
|
|
20
21
|
When building your editor component within the Powerhouse ecosystem, you have several options for styling, allowing you to leverage your preferred methods:
|
|
21
22
|
|
|
22
|
-
1. **Default HTML Styling:** Standard HTML tags (`<h1>`, `<p>`, `<button>`, etc.) will render with default
|
|
23
|
+
1. **Default HTML Styling:** Standard HTML tags (`<h1>`, `<p>`, `<button>`, etc.) will render with default styles offered through the boilerplate.
|
|
23
24
|
2. **Tailwind CSS:** Connect Studio comes with Tailwind CSS integrated. You can directly use Tailwind utility classes for rapid, consistent styling without writing separate CSS files.
|
|
24
25
|
3. **Custom CSS Files:** You can import traditional CSS files (`.css`) to apply custom styles or integrate existing style libraries.
|
|
25
26
|
|
|
26
|
-
Connect Studio provides a dynamic local environment
|
|
27
|
-
|
|
28
|
-
Let's look at a simple example component structure and how each styling method can be applied.
|
|
29
|
-
|
|
30
|
-
**Example Component Structure (Base HTML with inline styles)**
|
|
31
|
-
|
|
32
|
-
<details>
|
|
33
|
-
<summary>Base HTML Example</summary>
|
|
34
|
-
|
|
35
|
-
Here's a basic editor structure using only standard HTML tags.
|
|
36
|
-
This demonstrates how elements look with very minimal default styling:
|
|
37
|
-
|
|
38
|
-
```typescript
|
|
39
|
-
import { EditorProps } from 'document-model';
|
|
40
|
-
// Assuming a simple document model for demonstration
|
|
41
|
-
// import { ExampleDocument, actions } from '../../document-models/example';
|
|
42
|
-
|
|
43
|
-
// Replace with your actual document type props if needed
|
|
44
|
-
export type IProps = EditorProps<any>;
|
|
45
|
-
|
|
46
|
-
export default function Editor({ document, dispatch }: IProps) {
|
|
47
|
-
return (
|
|
48
|
-
<div>
|
|
49
|
-
<h1 style={{ fontWeight: 'bold' }}>Document Title</h1>
|
|
50
|
-
<h2>Document Subtitle</h2>
|
|
51
|
-
<input
|
|
52
|
-
type="text"
|
|
53
|
-
placeholder="Small text input"
|
|
54
|
-
style={{ border: '1px solid gray', marginBottom: '0.5rem' }}
|
|
55
|
-
/>
|
|
56
|
-
<textarea
|
|
57
|
-
placeholder="Large text area"
|
|
58
|
-
rows={4}
|
|
59
|
-
style={{ border: '1px solid gray', display: 'block', marginBottom: '0.5rem' }}
|
|
60
|
-
/>
|
|
61
|
-
<button style={{ backgroundColor: 'yellow' }}>
|
|
62
|
-
Submit
|
|
63
|
-
</button>
|
|
64
|
-
</div>
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
```
|
|
68
|
-
</details>
|
|
69
|
-
|
|
70
|
-
*Run `ph connect` to see the default styles applied to components in real-time.*
|
|
71
|
-
|
|
72
|
-
**Styling with Tailwind CSS**
|
|
73
|
-
|
|
74
|
-
<details>
|
|
75
|
-
<summary>Tailwind CSS Example</summary>
|
|
76
|
-
|
|
77
|
-
Now, let's add Tailwind utility classes to the same structure for styling:
|
|
78
|
-
|
|
79
|
-
```typescript
|
|
80
|
-
import { EditorProps } from 'document-model';
|
|
81
|
-
// import { ExampleDocument, actions } from '../../document-models/example';
|
|
82
|
-
|
|
83
|
-
export type IProps = EditorProps<any>;
|
|
84
|
-
|
|
85
|
-
export default function Editor({ document, dispatch }: IProps) {
|
|
86
|
-
return (
|
|
87
|
-
<div className="p-4 space-y-4"> {/* Add padding and spacing */}
|
|
88
|
-
<h1 className="text-2xl font-bold">Document Title</h1> {/* Style heading */}
|
|
89
|
-
<h2 className="text-lg text-gray-600 mb-4">Document Subtitle</h2> {/* Style subheading */}
|
|
90
|
-
<input
|
|
91
|
-
type="text"
|
|
92
|
-
placeholder="Small text input"
|
|
93
|
-
className="w-full p-2 border rounded focus:outline-none focus:ring-2 focus:ring-blue-500 mb-4" // Style input
|
|
94
|
-
/>
|
|
95
|
-
<textarea
|
|
96
|
-
placeholder="Large text area"
|
|
97
|
-
rows={4}
|
|
98
|
-
className="w-full p-2 border rounded focus:outline-none focus:ring-2 focus:ring-blue-500" // Style textarea
|
|
99
|
-
/>
|
|
100
|
-
<button className="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded transition-colors"> {/* Style button */}
|
|
101
|
-
Submit
|
|
102
|
-
</button>
|
|
103
|
-
</div>
|
|
104
|
-
);
|
|
105
|
-
}
|
|
106
|
-
```
|
|
107
|
-
</details>
|
|
108
|
-
|
|
109
|
-
*Run `ph connect` to see these Tailwind styles applied in real-time.*
|
|
110
|
-
|
|
111
|
-
**Styling with a Custom CSS File**
|
|
112
|
-
|
|
113
|
-
<details>
|
|
114
|
-
<summary>Custom CSS File Example</summary>
|
|
115
|
-
|
|
116
|
-
You can also import a standard CSS file.
|
|
117
|
-
|
|
118
|
-
1. Create a CSS file (e.g., `editor.css`) in the same directory as your `editor.tsx`:
|
|
119
|
-
|
|
120
|
-
```css
|
|
121
|
-
/* editors/your-editor/editor.css */
|
|
122
|
-
.editor-container {
|
|
123
|
-
padding: 1rem;
|
|
124
|
-
border: 1px solid #ccc;
|
|
125
|
-
border-radius: 4px;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
.editor-title {
|
|
129
|
-
color: rgb(51, 51, 54);
|
|
130
|
-
font-size: 2rem;
|
|
131
|
-
margin-bottom: 4px;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
.editor-subtitle {
|
|
135
|
-
color: rgb(51, 51, 54);
|
|
136
|
-
font-size: 1.5rem;
|
|
137
|
-
margin-bottom: 4px;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
.editor-button {
|
|
141
|
-
background-color: green;
|
|
142
|
-
color: white;
|
|
143
|
-
padding: 0.5rem 1rem;
|
|
144
|
-
border: none;
|
|
145
|
-
border-radius: 4px;
|
|
146
|
-
cursor: pointer;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
.editor-button:hover {
|
|
150
|
-
background-color: darkgreen;
|
|
151
|
-
}
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
2. Import the CSS file and use the classes in your component:
|
|
155
|
-
|
|
156
|
-
```typescript
|
|
157
|
-
import { EditorProps } from 'document-model';
|
|
158
|
-
// import { ExampleDocument, actions } from '../../document-models/example';
|
|
159
|
-
import './editor.css'; // Import the CSS file
|
|
160
|
-
|
|
161
|
-
export type IProps = EditorProps<any>;
|
|
162
|
-
|
|
163
|
-
export default function Editor({ document, dispatch }: IProps) {
|
|
164
|
-
return (
|
|
165
|
-
<div className="editor-container"> {/* Use custom class */}
|
|
166
|
-
<h1 className="editor-title">Document Title</h1> {/* Use custom class */}
|
|
167
|
-
<h2 className="editor-subtitle">Document Subtitle</h2> {/* Default or other styles */}
|
|
168
|
-
<input
|
|
169
|
-
type="text"
|
|
170
|
-
placeholder="Small text input"
|
|
171
|
-
className="w-full p-2 border rounded mb-4" // Can mix with Tailwind/defaults
|
|
172
|
-
/>
|
|
173
|
-
<textarea
|
|
174
|
-
placeholder="Large text area"
|
|
175
|
-
rows={4}
|
|
176
|
-
className="w-full p-2 border rounded mb-4" // Can mix with Tailwind/defaults
|
|
177
|
-
/>
|
|
178
|
-
<button className="editor-button"> {/* Use custom class */}
|
|
179
|
-
Submit
|
|
180
|
-
</button>
|
|
181
|
-
</div>
|
|
182
|
-
);
|
|
183
|
-
}
|
|
184
|
-
```
|
|
185
|
-
</details>
|
|
186
|
-
|
|
187
|
-
*Run `ph connect` to see your custom CSS styles applied.*
|
|
188
|
-
|
|
189
|
-
---
|
|
27
|
+
Connect Studio provides a dynamic local environment, by running `ph connect` to visualize your components instantly as you build them, regardless of the styling method you choose.
|
|
28
|
+
Manual build steps are typically only needed when publishing packages.
|
|
190
29
|
|
|
191
30
|
## ToDoList Editor
|
|
192
31
|
|
|
193
|
-
|
|
194
|
-
### Implementing Components
|
|
195
|
-
The editor we are about to implement makes use of some components from **Powerhouse Document Engineering**.
|
|
196
|
-
When you add the editor code, you'll see it makes use of two components, the `Checkbox` and `InputField`.
|
|
197
|
-
These are imported from the Powerhouse Document Engineering design system (`@powerhousedao/document-engineering/scalars`).
|
|
198
|
-
|
|
199
|
-
This system provides a library of reusable components to ensure consistency and speed up development.
|
|
200
|
-
You can explore available components, see usage examples, and understand their properties (props) using our Storybook instance. For a detailed guide on how to leverage the Document Engineering design system and Storybook, see [Using the Powerhouse Document Engineering](/academy/ComponentLibrary/DocumentEngineering) page.
|
|
201
|
-
|
|
202
|
-
For this tutorial, create a `components` folder inside `editors/to-do-list`. Then, within this new `components` folder, create the files for the `Checkbox` and `InputField` components (e.g., `Checkbox.tsx` and `InputField.tsx`) with the following code:
|
|
203
|
-
:::
|
|
204
|
-
|
|
205
|
-
<details>
|
|
206
|
-
<summary>Checkbox</summary>
|
|
207
|
-
```typescript
|
|
208
|
-
import { Form, BooleanField } from "@powerhousedao/document-engineering/scalars";
|
|
209
|
-
|
|
210
|
-
interface CheckboxProps {
|
|
211
|
-
value: boolean;
|
|
212
|
-
onChange: (value: boolean) => void;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
export const Checkbox = ({ value, onChange }: CheckboxProps) => {
|
|
216
|
-
return (
|
|
217
|
-
<Form onSubmit={() => {}}>
|
|
218
|
-
<BooleanField
|
|
219
|
-
name="checked"
|
|
220
|
-
description="Check this box to mark the todo as completed"
|
|
221
|
-
value={value}
|
|
222
|
-
onChange={onChange}
|
|
223
|
-
/>
|
|
224
|
-
</Form>
|
|
225
|
-
);
|
|
226
|
-
};
|
|
227
|
-
```
|
|
228
|
-
</details>
|
|
229
|
-
|
|
230
|
-
<details>
|
|
231
|
-
<summary>Inputfield</summary>
|
|
232
|
-
```typescript
|
|
233
|
-
import { Form, StringField } from "@powerhousedao/document-engineering/scalars";
|
|
234
|
-
|
|
235
|
-
interface InputFieldProps {
|
|
236
|
-
input: string;
|
|
237
|
-
value: string;
|
|
238
|
-
label?: string;
|
|
239
|
-
onKeyDown: (e: React.KeyboardEvent<HTMLTextAreaElement>) => void;
|
|
240
|
-
handleInputChange: (e: React.ChangeEvent<HTMLTextAreaElement>) => void;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
export const InputField = (props: InputFieldProps) => {
|
|
244
|
-
const { input, value, label, onKeyDown, handleInputChange } = props;
|
|
245
|
-
|
|
246
|
-
return (
|
|
247
|
-
<Form
|
|
248
|
-
defaultValues={{
|
|
249
|
-
input: input,
|
|
250
|
-
}}
|
|
251
|
-
onSubmit={() => {}}
|
|
252
|
-
resetOnSuccessfulSubmit
|
|
253
|
-
>
|
|
254
|
-
<StringField
|
|
255
|
-
style={{
|
|
256
|
-
color: "black",
|
|
257
|
-
}}
|
|
258
|
-
label={label}
|
|
259
|
-
name="input"
|
|
260
|
-
value={value}
|
|
261
|
-
onKeyDown={onKeyDown}
|
|
262
|
-
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
|
263
|
-
handleInputChange(e);
|
|
264
|
-
}}
|
|
265
|
-
/>
|
|
266
|
-
</Form>
|
|
267
|
-
);
|
|
268
|
-
};
|
|
269
|
-
```
|
|
270
|
-
</details>
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
Below is the complete code for the To-Do List editor. It primarily uses Tailwind CSS for styling and imports the local `Checkbox` and `InputField` components you created in the previous step. These local components, in turn, utilize elements from the Powerhouse Document Engineering design system.
|
|
32
|
+
Below is the complete code for the To-Do List editor.
|
|
274
33
|
|
|
275
34
|
<details>
|
|
276
35
|
<summary>Complete ToDoList Editor Example (using Tailwind CSS)</summary>
|
|
277
36
|
|
|
278
37
|
```typescript
|
|
279
|
-
|
|
280
|
-
import { EditorProps } from 'document-model'; // Core type for editor components.
|
|
38
|
+
import { EditorProps } from 'document-model';
|
|
281
39
|
import {
|
|
282
|
-
ToDoListState,
|
|
283
|
-
ToDoListAction,
|
|
284
|
-
ToDoListLocalState,
|
|
285
|
-
ToDoItem,
|
|
286
|
-
actions,
|
|
287
|
-
ToDoListDocument
|
|
288
|
-
} from '../../document-models/to-do-list
|
|
289
|
-
import { useState } from 'react';
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
// Define the props expected by this Editor component. It extends EditorProps with our specific document type.
|
|
40
|
+
ToDoListState,
|
|
41
|
+
ToDoListAction,
|
|
42
|
+
ToDoListLocalState,
|
|
43
|
+
ToDoItem,
|
|
44
|
+
actions,
|
|
45
|
+
ToDoListDocument,
|
|
46
|
+
} from '../../document-models/to-do-list';
|
|
47
|
+
import { useState } from 'react';
|
|
48
|
+
|
|
49
|
+
// EditorProps is a generic type that provides the document and a dispatch function.
|
|
50
|
+
// The dispatch function is used to send actions to the document's reducer to update the state.
|
|
294
51
|
export type IProps = EditorProps<ToDoListDocument>;
|
|
295
52
|
|
|
296
|
-
// Define the main Editor component function.
|
|
297
53
|
export default function Editor(props: IProps) {
|
|
298
|
-
// Destructure
|
|
54
|
+
// Destructure document and dispatch from props.
|
|
299
55
|
const { document, dispatch } = props;
|
|
300
|
-
//
|
|
301
|
-
const {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
56
|
+
// Get the global state from the document. This state is shared across all editors of this document.
|
|
57
|
+
const {
|
|
58
|
+
state: { global: state },
|
|
59
|
+
} = document;
|
|
60
|
+
|
|
61
|
+
// React's useState hook is used for local component state.
|
|
62
|
+
// This state is not shared with other components.
|
|
63
|
+
// `todoItem` stores the text of the new to-do item being added.
|
|
305
64
|
const [todoItem, setTodoItem] = useState('');
|
|
306
|
-
//
|
|
65
|
+
// `editingItemId` stores the ID of the item currently being edited.
|
|
307
66
|
const [editingItemId, setEditingItemId] = useState<string | null>(null);
|
|
308
|
-
//
|
|
67
|
+
// `editedText` stores the text of the item while it's being edited.
|
|
309
68
|
const [editedText, setEditedText] = useState('');
|
|
310
69
|
|
|
311
|
-
// --- JSX Structure (What gets rendered) ---
|
|
312
70
|
return (
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
{/* `hover:bg-blue-600`: Changes background color on hover. */}
|
|
349
|
-
{/* `text-white`: Sets text color to white. */}
|
|
350
|
-
{/* `px-4`: Adds horizontal padding (4 units). */}
|
|
351
|
-
{/* `py-1.5`: Adds vertical padding (1.5 units). */}
|
|
352
|
-
{/* `rounded`: Applies rounded corners. */}
|
|
353
|
-
{/* `transition-colors`: Smoothly animates color changes. */}
|
|
354
|
-
<button
|
|
355
|
-
className="bg-blue-500 hover:bg-blue-600 text-white px-4 py-1.5 rounded transition-colors"
|
|
356
|
-
onClick={() => { // Handle button click to add item.
|
|
357
|
-
if (todoItem.trim()) { // Check if input is not empty
|
|
358
|
-
dispatch(actions.addTodoItem({ // Dispatch action to add item.
|
|
359
|
-
id: Math.random().toString(), // Simple unique ID.
|
|
360
|
-
text: todoItem,
|
|
361
|
-
}));
|
|
362
|
-
setTodoItem(''); // Clear the input field.
|
|
363
|
-
}
|
|
364
|
-
}}
|
|
365
|
-
>
|
|
366
|
-
Add
|
|
367
|
-
</button>
|
|
368
|
-
</div>
|
|
369
|
-
|
|
370
|
-
{/* Unordered list to display the to-do items */}
|
|
371
|
-
{/* `list-none`: Removes default list bullet points. */}
|
|
372
|
-
{/* `p-0`: Removes default padding. */}
|
|
373
|
-
<ul className="list-none p-0">
|
|
374
|
-
{/* Map over the items array in the global state to render each item */}
|
|
375
|
-
{state.items.map((item: ToDoItem) => (
|
|
376
|
-
// List item element for each to-do.
|
|
377
|
-
// `key={item.id}`: React requires a unique key for list items for efficient updates.
|
|
378
|
-
// `flex`: Enables flexbox layout (checkbox, text, delete icon in a row).
|
|
379
|
-
// `items-center`: Aligns items vertically in the center.
|
|
380
|
-
// `p-2`: Adds padding.
|
|
381
|
-
// `relative`: Needed for positioning the delete icon absolutely (if we were doing that).
|
|
382
|
-
// `border-b`: Adds a bottom border.
|
|
383
|
-
// `border-gray-100`: Sets border color to light gray.
|
|
384
|
-
<li
|
|
385
|
-
key={item.id}
|
|
386
|
-
className="flex items-center p-2 relative border-b border-gray-100"
|
|
71
|
+
<div className="p-4 font-sans max-w-lg mx-auto">
|
|
72
|
+
<h1 className="text-2xl font-bold mb-4 text-center">To-do List</h1>
|
|
73
|
+
<div className="w-96 mx-auto">
|
|
74
|
+
<div className="flex mb-4">
|
|
75
|
+
<input
|
|
76
|
+
className="border border-gray-300 p-2 rounded-l-md flex-grow"
|
|
77
|
+
placeholder="Insert task here..."
|
|
78
|
+
value={todoItem}
|
|
79
|
+
onChange={e => setTodoItem(e.target.value)}
|
|
80
|
+
onKeyDown={e => {
|
|
81
|
+
if (e.key === 'Enter') {
|
|
82
|
+
// Dispatch an action to add a new to-do item.
|
|
83
|
+
// `actions.addTodoItem` is an action creator from our document model.
|
|
84
|
+
dispatch(
|
|
85
|
+
actions.addTodoItem({
|
|
86
|
+
id: Math.random().toString(), // In a real app, use a more robust ID generation.
|
|
87
|
+
text: todoItem,
|
|
88
|
+
})
|
|
89
|
+
);
|
|
90
|
+
setTodoItem('');
|
|
91
|
+
}
|
|
92
|
+
}}
|
|
93
|
+
/>
|
|
94
|
+
<button
|
|
95
|
+
className="bg-blue-500 hover:bg-blue-600 text-white p-2 rounded-r-md"
|
|
96
|
+
onClick={() => {
|
|
97
|
+
// Also add item on button click.
|
|
98
|
+
dispatch(
|
|
99
|
+
actions.addTodoItem({
|
|
100
|
+
id: Math.random().toString(),
|
|
101
|
+
text: todoItem,
|
|
102
|
+
})
|
|
103
|
+
);
|
|
104
|
+
setTodoItem('');
|
|
105
|
+
}}
|
|
387
106
|
>
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
{/* Conditional Rendering: Show input field or text based on editing state */}
|
|
400
|
-
{editingItemId === item.id ? (
|
|
401
|
-
// --- Editing State ---
|
|
402
|
-
// Input field shown when this item is being edited.
|
|
403
|
-
// `ml-2`: Adds left margin.
|
|
404
|
-
// `flex-grow`: Allows input to take available horizontal space.
|
|
405
|
-
// `p-1`: Adds small padding.
|
|
406
|
-
// `border`: Adds a default border.
|
|
407
|
-
// `rounded`: Applies rounded corners.
|
|
408
|
-
// `focus:outline-none`: Removes the default browser focus outline.
|
|
409
|
-
// `focus:ring-1 focus:ring-blue-500`: Adds a custom blue ring when focused.
|
|
107
|
+
Add
|
|
108
|
+
</button>
|
|
109
|
+
</div>
|
|
110
|
+
<ul className="list-none p-0">
|
|
111
|
+
{/* Map over the items in the global state to render each to-do item. */}
|
|
112
|
+
{state.items.map((item: ToDoItem) => (
|
|
113
|
+
<li
|
|
114
|
+
key={item.id}
|
|
115
|
+
className="flex items-center p-2 relative border-b border-gray-200"
|
|
116
|
+
>
|
|
410
117
|
<input
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
118
|
+
type="checkbox"
|
|
119
|
+
checked={item.checked}
|
|
120
|
+
className="mr-3"
|
|
121
|
+
onChange={() => {
|
|
122
|
+
// Dispatch an action to update the checked status of an item.
|
|
123
|
+
dispatch(
|
|
124
|
+
actions.updateTodoItem({
|
|
417
125
|
id: item.id,
|
|
418
|
-
|
|
419
|
-
})
|
|
420
|
-
|
|
421
|
-
}
|
|
126
|
+
checked: !item.checked,
|
|
127
|
+
})
|
|
128
|
+
);
|
|
422
129
|
}}
|
|
423
|
-
autoFocus // Automatically focus the input when it appears.
|
|
424
130
|
/>
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
131
|
+
{/* Conditional rendering: show an input field if the item is being edited, otherwise show the text. */}
|
|
132
|
+
{editingItemId === item.id ? (
|
|
133
|
+
<input
|
|
134
|
+
value={editedText}
|
|
135
|
+
onChange={e =>
|
|
136
|
+
setEditedText(e.target.value)
|
|
137
|
+
}
|
|
138
|
+
onKeyDown={e => {
|
|
139
|
+
if (e.key === 'Enter') {
|
|
140
|
+
// Dispatch an action to update the item's text.
|
|
141
|
+
dispatch(
|
|
142
|
+
actions.updateTodoItem({
|
|
143
|
+
id: item.id,
|
|
144
|
+
text: editedText,
|
|
145
|
+
})
|
|
146
|
+
);
|
|
147
|
+
// Exit editing mode.
|
|
148
|
+
setEditingItemId(null);
|
|
149
|
+
}
|
|
443
150
|
}}
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
151
|
+
className="flex-grow"
|
|
152
|
+
autoFocus // Automatically focus the input when it appears.
|
|
153
|
+
/>
|
|
154
|
+
) : (
|
|
155
|
+
<div className="flex items-center flex-grow gap-1">
|
|
156
|
+
<span
|
|
157
|
+
onClick={() => {
|
|
158
|
+
// Enter editing mode when the text is clicked.
|
|
159
|
+
setEditingItemId(item.id);
|
|
160
|
+
setEditedText(item.text);
|
|
161
|
+
}}
|
|
162
|
+
className={`cursor-pointer ${
|
|
163
|
+
item.checked
|
|
164
|
+
? 'line-through text-gray-500'
|
|
165
|
+
: ''
|
|
166
|
+
}`}
|
|
167
|
+
>
|
|
168
|
+
{item.text}
|
|
169
|
+
</span>
|
|
170
|
+
<span
|
|
171
|
+
onClick={() =>
|
|
172
|
+
dispatch(
|
|
173
|
+
actions.deleteTodoItem({
|
|
174
|
+
id: item.id,
|
|
175
|
+
})
|
|
176
|
+
)
|
|
177
|
+
}
|
|
178
|
+
className="text-gray-400 cursor-pointer opacity-40 transition-all duration-200 text-base font-bold inline-flex items-center pl-1 hover:opacity-100 hover:text-red-500"
|
|
179
|
+
>
|
|
180
|
+
×
|
|
181
|
+
</span>
|
|
182
|
+
</div>
|
|
183
|
+
)}
|
|
184
|
+
</li>
|
|
185
|
+
))}
|
|
186
|
+
</ul>
|
|
187
|
+
</div>
|
|
468
188
|
</div>
|
|
469
189
|
);
|
|
470
190
|
}
|
|
@@ -487,8 +207,16 @@ The editor will update dynamically, so you can play around with your editor styl
|
|
|
487
207
|
Congratulations!
|
|
488
208
|
If you managed to follow this tutorial until this point, you have successfully implemented the **ToDoList** document model with its reducer operations and editor.
|
|
489
209
|
|
|
210
|
+
### Up Next: Drive Explorers & Drive Apps
|
|
211
|
+
|
|
490
212
|
Now you can move on to creating a [custom drive explorer](/academy/MasteryTrack/BuildingUserExperiences/BuildingADriveExplorer) for your ToDoList document.
|
|
491
213
|
Imagine you have many ToDoLists sitting in a drive. A custom drive explorer will allow you to organize and track them at a glance, opening up a new world of possibilities to increase the functionality of your documents!
|
|
492
214
|
|
|
493
215
|
|
|
216
|
+
### Up Next: Mastery Track
|
|
217
|
+
|
|
218
|
+
In the [Mastery Track chapther: Document Model Creation](/academy/MasteryTrack/DocumentModelCreation/WhatIsADocumentModel) we guide you through the theoretics of the previous steps while created a more advanced ToDoList.
|
|
219
|
+
You will learn:
|
|
220
|
+
- The in's & out's of a document model.
|
|
221
|
+
- How to use UI & Scalar components from the Document Engineering system.
|
|
494
222
|
|