@powerhousedao/academy 2.5.0-dev.4 → 2.5.0-dev.40
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 +237 -0
- package/docs/academy/01-GetStarted/00-ExploreDemoPackage.md +19 -15
- package/docs/academy/01-GetStarted/01-CreateNewPowerhouseProject.md +39 -40
- package/docs/academy/01-GetStarted/02-DefineToDoListDocumentModel.md +22 -7
- package/docs/academy/01-GetStarted/03-ImplementOperationReducers.md +9 -4
- package/docs/academy/01-GetStarted/04-BuildToDoListEditor.md +146 -422
- package/docs/academy/01-GetStarted/_04-BuildToDoListEditor +360 -0
- package/docs/academy/01-GetStarted/home.mdx +16 -24
- package/docs/academy/01-GetStarted/styles.module.css +31 -0
- package/docs/academy/02-MasteryTrack/01-BuilderEnvironment/01-Prerequisites.md +0 -18
- package/docs/academy/02-MasteryTrack/01-BuilderEnvironment/02-StandardDocumentModelWorkflow.md +10 -6
- package/docs/academy/02-MasteryTrack/01-BuilderEnvironment/03-BuilderTools.md +1 -1
- package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/01-WhatIsADocumentModel.md +33 -16
- package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/02-SpecifyTheStateSchema.md +73 -0
- package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/03-SpecifyDocumentOperations.md +59 -4
- package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/04-UseTheDocumentModelGenerator.md +32 -12
- package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/05-ImplementDocumentReducers.md +103 -38
- package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/06-ImplementDocumentModelTests.md +90 -228
- package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/07-ExampleToDoListRepository.md +41 -1
- package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/01-BuildingDocumentEditors.md +342 -67
- package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/02-ConfiguringDrives.md +5 -3
- package/docs/academy/02-MasteryTrack/05-Launch/02-PublishYourProject.md +70 -5
- package/docs/academy/02-MasteryTrack/05-Launch/03-SetupEnvironment.md +162 -73
- package/docs/academy/02-MasteryTrack/05-Launch/{03-RunOnACloudServer.md → _03-RunOnACloudServer} +8 -5
- package/docs/academy/03-ExampleUsecases/Chatroom/02-CreateNewPowerhouseProject.md +10 -9
- package/docs/academy/03-ExampleUsecases/Chatroom/03-DefineChatroomDocumentModel.md +3 -4
- package/docs/academy/03-ExampleUsecases/Chatroom/05-ImplementChatroomEditor.md +1 -1
- package/docs/academy/03-ExampleUsecases/Chatroom/_category_.json +1 -1
- package/docs/academy/04-APIReferences/00-PowerhouseCLI.md +13 -49
- package/docs/academy/05-Architecture/00-PowerhouseArchitecture.md +3 -0
- package/docs/academy/05-Architecture/images/PowerhouseArchitecture.png +0 -0
- package/docs/academy/06-ComponentLibrary/00-DocumentEngineering.md +85 -30
- package/docs/academy/06-ComponentLibrary/02-CreateCustomScalars.md +382 -0
- package/docs/academy/06-ComponentLibrary/03-IntegrateIntoAReactComponent.md +124 -0
- package/docs/academy/07-Cookbook.md +252 -4
- package/docs/academy/08-Glossary.md +20 -18
- package/docs/academy/09-AIResources +131 -0
- package/docusaurus.config.ts +4 -0
- package/package.json +1 -1
- package/sidebars.ts +3 -45
- package/src/css/custom.css +23 -1
- package/docs/academy/03-ExampleUsecases/Chatroom/01-SetupBuilderEnvironment.md +0 -216
- package/docs/academy/06-ComponentLibrary/02-BuildingWithScalars.md +0 -54
- 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/04-Complex-Components/01-sidebar.mdx +0 -36
- 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
package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/01-BuildingDocumentEditors.md
CHANGED
|
@@ -6,7 +6,8 @@ At Powerhouse, frontend development for document editors follows a simple and fa
|
|
|
6
6
|
|
|
7
7
|
### Development Environment
|
|
8
8
|
|
|
9
|
-
Connect Studio is your primary tool for development.
|
|
9
|
+
Connect Studio is your primary tool for development.
|
|
10
|
+
When you run `ph connect`, it provides a dynamic, local environment where you can define and preview your document models and their editors live. This replaces the need for tools like Storybook for editor development, though Storybook remains invaluable for exploring the [Powerhouse Component Library](#powerhouse-component-library).
|
|
10
11
|
|
|
11
12
|
Key aspects of the Powerhouse development environment:
|
|
12
13
|
- **React Foundation**: Build your editor UIs using React components, just as you would in any standard React project.
|
|
@@ -20,7 +21,8 @@ Powerhouse aims to keep your developer experience clean, familiar, and focused:
|
|
|
20
21
|
|
|
21
22
|
### Generating Your Editor Template
|
|
22
23
|
|
|
23
|
-
To kickstart your editor development, Powerhouse provides a command to generate a basic editor template. This command reads your document model
|
|
24
|
+
To kickstart your editor development, Powerhouse provides a command to generate a basic editor template. This command reads your document model specifications and creates the initial `editor.tsx` file.
|
|
25
|
+
If you want a refresher on how to define your document model specification please read the chapter on [specifying the State Schema](/academy/MasteryTrack/DocumentModelCreation/SpecifyTheStateSchema)
|
|
24
26
|
|
|
25
27
|
For example, to generate an editor for a `ToDoList` document model with a document type `powerhouse/todolist`:
|
|
26
28
|
```bash
|
|
@@ -72,40 +74,6 @@ You have several options for styling your editor components:
|
|
|
72
74
|
|
|
73
75
|
Choose the method or combination of methods that best suits your project needs and team preferences. Connect Studio (`ph connect`) will allow you to see your styles applied in real-time.
|
|
74
76
|
|
|
75
|
-
<details>
|
|
76
|
-
<summary>Refresher on React Hooks</summary>
|
|
77
|
-
|
|
78
|
-
All of the Powerhouse React Hooks can be found here: [Powerhouse React Hooks API Reference](docs/academy/APIReferences/ReactHooks)
|
|
79
|
-
|
|
80
|
-
React Hooks allow you to use various React features directly within your functional components. You can use built-in Hooks or combine them to create your own custom Hooks.
|
|
81
|
-
|
|
82
|
-
**What are Custom Hooks?**
|
|
83
|
-
A custom hook is a JavaScript function whose name starts with "use" and that calls other Hooks. They are used to:
|
|
84
|
-
- Reuse stateful logic between components.
|
|
85
|
-
- Abstract complex logic into a simpler interface.
|
|
86
|
-
- Isolate side effects, particularly those managed by `useEffect`.
|
|
87
|
-
|
|
88
|
-
**Key Built-in Hooks Examples:**
|
|
89
|
-
- `useState`: Lets a component "remember" information (state).
|
|
90
|
-
- `useEffect`: Lets a component perform side effects (e.g., data fetching, subscriptions, manually changing the DOM).
|
|
91
|
-
- `useContext`: Lets a component receive information from distant parent components without explicitly passing props through every level of the component tree.
|
|
92
|
-
|
|
93
|
-
**Naming Convention:**
|
|
94
|
-
Hook names must always start with `use` followed by a capital letter (e.g., `useState`, `useOnlineStatus`).
|
|
95
|
-
|
|
96
|
-
**Rules of Hooks:**
|
|
97
|
-
1. **Only Call Hooks at the Top Level**: Don't call Hooks inside loops, conditions, or nested functions.
|
|
98
|
-
2. **Only Call Hooks from React Functions**: Call Hooks from React functional components or from custom Hooks.
|
|
99
|
-
|
|
100
|
-
It's important to note that a function should only be named and treated as a hook if it actually utilizes one or more built-in React hooks. If a function (even if named `useSomething`) doesn't call any built-in hooks, it behaves like a regular JavaScript function, and making it a "hook" offers no specific React advantages.
|
|
101
|
-
|
|
102
|
-
For more details, see the official documentation and our API reference:
|
|
103
|
-
- [Reusing Logic with Custom Hooks (react.dev)](https://react.dev/learn/reusing-logic-with-custom-hooks)
|
|
104
|
-
- [Rules of Hooks (react.dev)](https://react.dev/reference/rules/rules-of-hooks)
|
|
105
|
-
- [Powerhouse React Hooks API Reference](docs/academy/APIReferences/ReactHooks)
|
|
106
|
-
|
|
107
|
-
</details>
|
|
108
|
-
|
|
109
77
|
### State Management in Editors
|
|
110
78
|
|
|
111
79
|
When you build an editor in Powerhouse, your main editor component receives `EditorProps`. These props are crucial for interacting with the document:
|
|
@@ -176,26 +144,69 @@ Storybook allows you to:
|
|
|
176
144
|
</Form>
|
|
177
145
|
```
|
|
178
146
|
|
|
179
|
-
|
|
180
|
-
|
|
147
|
+
<details>
|
|
148
|
+
<summary>Tutorial: Implementing the `ToDoList` Editor</summary>
|
|
149
|
+
|
|
150
|
+
## Build a ToDoList Editor
|
|
151
|
+
|
|
152
|
+
In this final part of our tutorial we will continue with the interface or editor implementation of the **ToDoList** document model. This means you will create a simple user interface for the **ToDoList** document model which will be used inside the Connect app to create, update and delete your ToDoList items, but also dispaly the statistics we've implemented in our reducers.
|
|
153
|
+
|
|
154
|
+
## Generate the editor template
|
|
155
|
+
|
|
156
|
+
Run the command below to generate the editor template for the **ToDoList** document model.
|
|
157
|
+
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`.
|
|
158
|
+
|
|
159
|
+
Notice the `--editor` flag which specifies the **ToDoList** document model, and the `--document-types` flag defines the document type `powerhouse/todolist`.
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
ph generate --editor ToDoList --document-types powerhouse/todolist
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Once complete, navigate to the `editors/to-do-list/editor.tsx` file and open it in your editor.
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
### Editor Implementation Options
|
|
169
|
+
|
|
170
|
+
When building your editor component within the Powerhouse ecosystem, you have several options for styling, allowing you to leverage your preferred methods:
|
|
171
|
+
|
|
172
|
+
1. **Default HTML Styling:** Standard HTML tags (`<h1>`, `<p>`, `<button>`, etc.) will render with default styles offered through the boilerplate.
|
|
173
|
+
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.
|
|
174
|
+
3. **Custom CSS Files:** You can import traditional CSS files (`.css`) to apply custom styles or integrate existing style libraries.
|
|
175
|
+
|
|
176
|
+
Connect Studio provides a dynamic local environment (`ph connect`) to visualize your components instantly as you build them, regardless of the styling method you choose. Manual build steps are typically only needed when publishing packages.
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## ToDoList Editor
|
|
181
|
+
|
|
182
|
+
:::tip
|
|
183
|
+
### Implementing Components
|
|
184
|
+
The editor we are about to implement makes use of some components from **Powerhouse Document Engineering**.
|
|
185
|
+
When you add the editor code, you'll see it makes use of two components, the `Checkbox` and `InputField`.
|
|
186
|
+
These are imported from the Powerhouse Document Engineering design system (`@powerhousedao/document-engineering/scalars`).
|
|
187
|
+
|
|
188
|
+
This system provides a library of reusable components to ensure consistency and speed up development.
|
|
189
|
+
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.
|
|
181
190
|
|
|
182
|
-
|
|
191
|
+
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:
|
|
192
|
+
:::
|
|
193
|
+
|
|
194
|
+
<details>
|
|
195
|
+
<summary>Checkbox</summary>
|
|
183
196
|
```typescript
|
|
184
|
-
// editors/to-do-list/components/checkbox.tsx
|
|
185
197
|
import { Form, BooleanField } from "@powerhousedao/document-engineering/scalars";
|
|
186
198
|
|
|
187
|
-
interface
|
|
199
|
+
interface CheckboxProps {
|
|
188
200
|
value: boolean;
|
|
189
201
|
onChange: (value: boolean) => void;
|
|
190
|
-
label?: string; // Added custom prop or passed through
|
|
191
202
|
}
|
|
192
203
|
|
|
193
|
-
export const Checkbox = ({ value, onChange
|
|
204
|
+
export const Checkbox = ({ value, onChange }: CheckboxProps) => {
|
|
194
205
|
return (
|
|
195
|
-
<Form onSubmit={() => {
|
|
206
|
+
<Form onSubmit={() => {}}>
|
|
196
207
|
<BooleanField
|
|
197
|
-
name="
|
|
198
|
-
description=
|
|
208
|
+
name="checked"
|
|
209
|
+
description="Check this box to mark the todo as completed"
|
|
199
210
|
value={value}
|
|
200
211
|
onChange={onChange}
|
|
201
212
|
/>
|
|
@@ -203,31 +214,295 @@ export const Checkbox = ({ value, onChange, label }: CustomCheckboxProps) => {
|
|
|
203
214
|
);
|
|
204
215
|
};
|
|
205
216
|
```
|
|
206
|
-
|
|
217
|
+
</details>
|
|
218
|
+
|
|
219
|
+
<details>
|
|
220
|
+
<summary>Inputfield</summary>
|
|
221
|
+
```typescript
|
|
222
|
+
import { Form, StringField } from "@powerhousedao/document-engineering/scalars";
|
|
223
|
+
|
|
224
|
+
interface InputFieldProps {
|
|
225
|
+
input: string;
|
|
226
|
+
value: string;
|
|
227
|
+
label?: string;
|
|
228
|
+
onKeyDown: (e: React.KeyboardEvent<HTMLTextAreaElement>) => void;
|
|
229
|
+
handleInputChange: (e: React.ChangeEvent<HTMLTextAreaElement>) => void;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export const InputField = (props: InputFieldProps) => {
|
|
233
|
+
const { input, value, label, onKeyDown, handleInputChange } = props;
|
|
234
|
+
|
|
235
|
+
return (
|
|
236
|
+
<Form
|
|
237
|
+
defaultValues={{
|
|
238
|
+
input: input,
|
|
239
|
+
}}
|
|
240
|
+
onSubmit={() => {}}
|
|
241
|
+
resetOnSuccessfulSubmit
|
|
242
|
+
>
|
|
243
|
+
<StringField
|
|
244
|
+
style={{
|
|
245
|
+
color: "black",
|
|
246
|
+
}}
|
|
247
|
+
label={label}
|
|
248
|
+
name="input"
|
|
249
|
+
value={value}
|
|
250
|
+
onKeyDown={onKeyDown}
|
|
251
|
+
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
|
252
|
+
handleInputChange(e);
|
|
253
|
+
}}
|
|
254
|
+
/>
|
|
255
|
+
</Form>
|
|
256
|
+
);
|
|
257
|
+
};
|
|
258
|
+
```
|
|
259
|
+
</details>
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
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.
|
|
263
|
+
|
|
264
|
+
<details>
|
|
265
|
+
<summary>Complete ToDoList Editor Example (using Tailwind CSS)</summary>
|
|
266
|
+
|
|
267
|
+
```typescript
|
|
268
|
+
import { EditorProps } from 'document-model'; // Core type for editor components.
|
|
269
|
+
import {
|
|
270
|
+
ToDoListState, // Type for the global state of the ToDoList.
|
|
271
|
+
ToDoListAction, // Type for actions that can modify the ToDoList state.
|
|
272
|
+
ToDoListLocalState, // Type for local (non-shared) editor state (if needed).
|
|
273
|
+
ToDoItem, // Type for a single item in the list.
|
|
274
|
+
actions, // Object containing action creators for dispatching changes.
|
|
275
|
+
ToDoListDocument // The complete document structure including state and metadata.
|
|
276
|
+
} from '../document-models/to-do-list/index.js'; // Path to your document model definition.
|
|
277
|
+
import { useState } from 'react'; // React hook for managing component-local state.
|
|
278
|
+
import { Checkbox } from './components/checkbox.js'; // Custom Checkbox component.
|
|
279
|
+
import { InputField } from './components/inputfield.js'; // Custom InputField component.
|
|
280
|
+
|
|
281
|
+
// Define the props expected by this Editor component. It extends EditorProps with our specific document type.
|
|
282
|
+
export type IProps = EditorProps<ToDoListDocument>;
|
|
283
|
+
|
|
284
|
+
// Define the main Editor component function.
|
|
285
|
+
export default function Editor(props: IProps) {
|
|
286
|
+
// Destructure props for easier access.
|
|
287
|
+
const { document, dispatch } = props;
|
|
288
|
+
// Access the global state from the document object.
|
|
289
|
+
const { state: { global: state } } = document;
|
|
290
|
+
|
|
291
|
+
// --- Component State ---
|
|
292
|
+
// State for the text input field where new tasks are typed.
|
|
293
|
+
const [todoItem, setTodoItem] = useState('');
|
|
294
|
+
// State to track which item is currently being edited (null if none). Stores the item's ID.
|
|
295
|
+
const [editingItemId, setEditingItemId] = useState<string | null>(null);
|
|
296
|
+
// State to hold the text of the item currently being edited.
|
|
297
|
+
const [editedText, setEditedText] = useState('');
|
|
298
|
+
|
|
299
|
+
// Sort items to show unchecked items first
|
|
300
|
+
const sortedItems: ToDoItem[] = [...state.items].sort((a, b) => {
|
|
301
|
+
return (a.checked ? 1 : 0) - (b.checked ? 1 : 0);
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
// --- JSX Structure (What gets rendered) ---
|
|
305
|
+
return (
|
|
306
|
+
// Main container div.
|
|
307
|
+
// `container`: Sets max-width based on viewport breakpoints.
|
|
308
|
+
// `mx-auto`: Centers the container horizontally.
|
|
309
|
+
// `p-4`: Adds padding on all sides (4 units, typically 1rem).
|
|
310
|
+
// `max-w-sm`: Sets a maximum width (small size).
|
|
311
|
+
<div className="container mx-auto p-4 max-w-xs">
|
|
312
|
+
{/* Heading for the editor */}
|
|
313
|
+
{/* `text-2xl`: Sets font size to extra-large. */}
|
|
314
|
+
{/* `font-bold`: Makes the text bold. */}
|
|
315
|
+
{/* `mb-4`: Adds margin to the bottom. */}
|
|
316
|
+
<h1 className="text-2xl font-bold mb-4">To-do List</h1>
|
|
317
|
+
|
|
318
|
+
{/* Stats Section */}
|
|
319
|
+
{state.items.length >= 2 && (
|
|
320
|
+
<div className="mb-4 bg-white rounded-lg px-3 py-2 shadow-md">
|
|
321
|
+
<div className="grid grid-cols-3 gap-3">
|
|
322
|
+
<div>
|
|
323
|
+
<div className="text-xs text-slate-500 mb-0.5">Total</div>
|
|
324
|
+
<div className="text-lg font-semibold text-slate-800">{state.stats.total}</div>
|
|
325
|
+
</div>
|
|
326
|
+
<div>
|
|
327
|
+
<div className="text-xs text-slate-500 mb-0.5">Completed</div>
|
|
328
|
+
<div className="text-lg font-semibold text-green-600">{state.stats.checked}</div>
|
|
329
|
+
</div>
|
|
330
|
+
<div>
|
|
331
|
+
<div className="text-xs text-slate-500 mb-0.5">Remaining</div>
|
|
332
|
+
<div className="text-lg font-semibold text-orange-600">{state.stats.unchecked}</div>
|
|
333
|
+
</div>
|
|
334
|
+
</div>
|
|
335
|
+
</div>
|
|
336
|
+
)}
|
|
337
|
+
|
|
338
|
+
{/* Container for the input field and "Add" button */}
|
|
339
|
+
{/* `flex items-end`: Enables flexbox layout for children with bottom alignment. */}
|
|
340
|
+
{/* `gap-2`: Adds a small gap between flex items. */}
|
|
341
|
+
{/* `mb-4`: Adds margin to the bottom. */}
|
|
342
|
+
<div className="flex items-end gap-2 mb-4">
|
|
343
|
+
{/* Custom InputField component */}
|
|
344
|
+
<div className="flex-grow">
|
|
345
|
+
<InputField
|
|
346
|
+
label="New Task" // Prop for accessibility/placeholder.
|
|
347
|
+
input={todoItem} // Current value from state.
|
|
348
|
+
value={todoItem} // Controlled component value.
|
|
349
|
+
handleInputChange={(e) => setTodoItem(e.target.value)} // Update state on change.
|
|
350
|
+
onKeyDown={(e) => { // Handle "Enter" key press to add item.
|
|
351
|
+
if (e.key === 'Enter' && todoItem.trim()) { // Check if key is Enter and input is not empty
|
|
352
|
+
dispatch(actions.addTodoItem({ // Dispatch action to add item.
|
|
353
|
+
id: Math.random().toString(), // Generate a simple unique ID (use a better method in production!).
|
|
354
|
+
text: todoItem,
|
|
355
|
+
}));
|
|
356
|
+
setTodoItem(''); // Clear the input field.
|
|
357
|
+
}
|
|
358
|
+
}}
|
|
359
|
+
/>
|
|
360
|
+
</div>
|
|
361
|
+
{/* "Add" button */}
|
|
362
|
+
{/* `bg-blue-500`: Sets background color to blue. */}
|
|
363
|
+
{/* `hover:bg-blue-600`: Changes background color on hover. */}
|
|
364
|
+
{/* `text-white`: Sets text color to white. */}
|
|
365
|
+
{/* `px-4`: Adds horizontal padding (4 units). */}
|
|
366
|
+
{/* `py-1.5`: Adds vertical padding (1.5 units). */}
|
|
367
|
+
{/* `rounded`: Applies rounded corners. */}
|
|
368
|
+
{/* `transition-colors`: Smoothly animates color changes. */}
|
|
369
|
+
<button
|
|
370
|
+
className="bg-blue-500 hover:bg-blue-600 text-white px-4 py-1.5 rounded transition-colors"
|
|
371
|
+
onClick={() => { // Handle button click to add item.
|
|
372
|
+
if (todoItem.trim()) { // Check if input is not empty
|
|
373
|
+
dispatch(actions.addTodoItem({ // Dispatch action to add item.
|
|
374
|
+
id: Math.random().toString(), // Simple unique ID.
|
|
375
|
+
text: todoItem,
|
|
376
|
+
}));
|
|
377
|
+
setTodoItem(''); // Clear the input field.
|
|
378
|
+
}
|
|
379
|
+
}}
|
|
380
|
+
>
|
|
381
|
+
Add
|
|
382
|
+
</button>
|
|
383
|
+
</div>
|
|
384
|
+
|
|
385
|
+
{/* Unordered list to display the to-do items */}
|
|
386
|
+
{/* `list-none`: Removes default list bullet points. */}
|
|
387
|
+
{/* `p-0`: Removes default padding. */}
|
|
388
|
+
<ul className="list-none p-0">
|
|
389
|
+
{/* Map over the items array in the global state to render each item */}
|
|
390
|
+
{sortedItems.map((item: ToDoItem) => (
|
|
391
|
+
// List item element for each to-do.
|
|
392
|
+
// `key={item.id}`: React requires a unique key for list items for efficient updates.
|
|
393
|
+
// `flex`: Enables flexbox layout (checkbox, text, delete icon in a row).
|
|
394
|
+
// `items-center`: Aligns items vertically in the center.
|
|
395
|
+
// `p-2`: Adds padding.
|
|
396
|
+
// `relative`: Needed for positioning the delete icon absolutely (if we were doing that).
|
|
397
|
+
// `border-b`: Adds a bottom border.
|
|
398
|
+
// `border-gray-100`: Sets border color to light gray.
|
|
399
|
+
<li
|
|
400
|
+
key={item.id}
|
|
401
|
+
className="flex items-center p-2 relative border-b border-gray-100"
|
|
402
|
+
>
|
|
403
|
+
{/* Custom Checkbox component */}
|
|
404
|
+
<Checkbox
|
|
405
|
+
value={item.checked} // Bind checked state to item's checked property.
|
|
406
|
+
onChange={() => { // Handle checkbox click.
|
|
407
|
+
dispatch(actions.updateTodoItem({ // Dispatch action to update item.
|
|
408
|
+
id: item.id,
|
|
409
|
+
checked: !item.checked, // Toggle the checked state.
|
|
410
|
+
}));
|
|
411
|
+
}}
|
|
412
|
+
/>
|
|
413
|
+
|
|
414
|
+
{/* Conditional Rendering: Show input field or text based on editing state */}
|
|
415
|
+
{editingItemId === item.id ? (
|
|
416
|
+
// --- Editing State ---
|
|
417
|
+
// Input field shown when this item is being edited.
|
|
418
|
+
// `ml-2`: Adds left margin.
|
|
419
|
+
// `flex-grow`: Allows input to take available horizontal space.
|
|
420
|
+
// `p-1`: Adds small padding.
|
|
421
|
+
// `border`: Adds a default border.
|
|
422
|
+
// `rounded`: Applies rounded corners.
|
|
423
|
+
// `focus:outline-none`: Removes the default browser focus outline.
|
|
424
|
+
// `focus:ring-1 focus:ring-blue-500`: Adds a custom blue ring when focused.
|
|
425
|
+
<input
|
|
426
|
+
className="ml-2 flex-grow p-1 border rounded focus:outline-none focus:ring-1 focus:ring-blue-500"
|
|
427
|
+
value={editedText} // Controlled input value from editedText state.
|
|
428
|
+
onChange={(e) => setEditedText(e.target.value)} // Update editedText state.
|
|
429
|
+
onKeyDown={(e) => { // Handle "Enter" key to save changes.
|
|
430
|
+
if (e.key === 'Enter') {
|
|
431
|
+
dispatch(actions.updateTodoItem({ // Dispatch update action.
|
|
432
|
+
id: item.id,
|
|
433
|
+
text: editedText, // Save the edited text.
|
|
434
|
+
}));
|
|
435
|
+
setEditingItemId(null); // Exit editing mode.
|
|
436
|
+
}
|
|
437
|
+
}}
|
|
438
|
+
autoFocus // Automatically focus the input when it appears.
|
|
439
|
+
/>
|
|
440
|
+
) : (
|
|
441
|
+
// --- Display State ---
|
|
442
|
+
// Container for the item text and delete icon when not editing.
|
|
443
|
+
// `ml-2`: Adds left margin.
|
|
444
|
+
// `flex items-center`: Aligns text and icon vertically.
|
|
445
|
+
// `flex-grow`: Allows this container to take available space.
|
|
446
|
+
// `gap-1`: Adds a small gap between text and icon.
|
|
447
|
+
<div className="ml-2 flex items-center flex-grow gap-1">
|
|
448
|
+
{/* The actual to-do item text */}
|
|
449
|
+
{/* `cursor-pointer`: Shows a pointer cursor on hover, indicating clickability. */}
|
|
450
|
+
{/* Conditional class: Apply line-through and gray text if item is checked. */}
|
|
451
|
+
{/* `line-through`: Strikes through the text. */}
|
|
452
|
+
{/* `text-gray-500`: Sets text color to gray. */}
|
|
453
|
+
<span
|
|
454
|
+
className={`cursor-pointer ${item.checked ? 'line-through text-gray-500' : ''}`}
|
|
455
|
+
onClick={() => { // Handle click to enter editing mode.
|
|
456
|
+
setEditingItemId(item.id); // Set the ID of the item being edited.
|
|
457
|
+
setEditedText(item.text); // Initialize the input with current text.
|
|
458
|
+
}}
|
|
459
|
+
>
|
|
460
|
+
{item.text} {/* Display the item's text */}
|
|
461
|
+
</span>
|
|
462
|
+
{/* Delete "button" (using a span styled as a button) */}
|
|
463
|
+
{/* `text-gray-400`: Sets default text color to light gray. */}
|
|
464
|
+
{/* `cursor-pointer`: Shows pointer cursor. */}
|
|
465
|
+
{/* `opacity-40`: Makes it semi-transparent by default. */}
|
|
466
|
+
{/* `transition-all duration-200`: Smoothly animates all changes (opacity, color). */}
|
|
467
|
+
{/* `text-base font-bold`: Sets text size and weight. */}
|
|
468
|
+
{/* `inline-flex items-center`: Needed for proper alignment if using an icon font/SVG. */}
|
|
469
|
+
{/* `pl-1`: Adds small left padding. */}
|
|
470
|
+
{/* `hover:opacity-100`: Makes it fully opaque on hover. */}
|
|
471
|
+
{/* `hover:text-red-500`: Changes text color to red on hover. */}
|
|
472
|
+
<span
|
|
473
|
+
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"
|
|
474
|
+
onClick={() => dispatch(actions.deleteTodoItem({ id: item.id }))} // Dispatch delete action on click.
|
|
475
|
+
>
|
|
476
|
+
× {/* Simple multiplication sign used as delete icon */}
|
|
477
|
+
</span>
|
|
478
|
+
</div>
|
|
479
|
+
)}
|
|
480
|
+
</li>
|
|
481
|
+
))}
|
|
482
|
+
</ul>
|
|
483
|
+
</div>
|
|
484
|
+
);
|
|
485
|
+
}
|
|
486
|
+
```
|
|
487
|
+
</details>
|
|
207
488
|
|
|
208
|
-
|
|
489
|
+
Now you can run the Connect app and see the **ToDoList** editor in action.
|
|
209
490
|
|
|
210
|
-
|
|
491
|
+
```bash
|
|
492
|
+
ph connect
|
|
493
|
+
```
|
|
211
494
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
* Use an `InputField` (or a `StringField` from the library) for text entry.
|
|
215
|
-
* On "Add" button click or "Enter" key press, `dispatch` an `addTodoItem` action with the current input value.
|
|
495
|
+
In Connect, in the bottom right corner you'll find a new Document Model that you can create: **ToDoList**.
|
|
496
|
+
Click on it to create a new ToDoList document.
|
|
216
497
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
* The `Checkbox` `value` should be bound to `item.checked`.
|
|
221
|
-
* Its `onChange` handler should `dispatch` an `updateTodoItem` action to toggle the `checked` status.
|
|
498
|
+
:::info
|
|
499
|
+
The editor will update dynamically, so you can play around with your editor styling while seeing your results appear in Connect Studio.
|
|
500
|
+
:::
|
|
222
501
|
|
|
223
|
-
|
|
224
|
-
* Implement local state (`editingItemId`, `editedText`) to manage which item is being edited and its current text.
|
|
225
|
-
* Conditionally render either the item text (display mode) or an input field (edit mode).
|
|
226
|
-
* When entering edit mode, populate `editedText` with the item's current text.
|
|
227
|
-
* On saving the edit (e.g., "Enter" key in the input), `dispatch` an `updateTodoItem` action with the new text.
|
|
502
|
+
</details>
|
|
228
503
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
* Its `onClick` handler should `dispatch` a `deleteTodoItem` action with the item's `id`.
|
|
504
|
+
Congratulations!
|
|
505
|
+
If you managed to follow this tutorial until this point, you have successfully implemented the **ToDoList** document model with its reducer operations and editor.
|
|
232
506
|
|
|
233
|
-
|
|
507
|
+
Now you can move on to creating a [custom drive explorer](/academy/MasteryTrack/BuildingUserExperiences/BuildingADriveExplorer) for your ToDoList document.
|
|
508
|
+
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!
|
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
A drive in Powerhouse is a container or a wrapper for documents and data. It's a place where you can organize and store your documents and share them with others. This guide will walk you through the process of configuring and managing drives in your Powerhouse environment.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
:::info **Prerequisites**
|
|
6
6
|
|
|
7
7
|
Before configuring a drive, ensure you have:
|
|
8
8
|
- Powerhouse [CLI installed](/academy/MasteryTrack/BuilderEnvironment/BuilderTools)
|
|
9
9
|
- Access to a Powerhouse instance
|
|
10
10
|
- Appropriate permissions to create and manage drives
|
|
11
|
+
:::
|
|
11
12
|
|
|
12
13
|
## Understanding Drives
|
|
13
14
|
|
|
@@ -23,7 +24,7 @@ Remote drives in Powerhouse allow you to connect to and work with data stored in
|
|
|
23
24
|
- **Cloud Storage**: For centralized, scalable data management.
|
|
24
25
|
- **Decentralized Storage**: Such as Ceramic or IPFS, enabling distributed and blockchain-based storage options.
|
|
25
26
|
|
|
26
|
-
:::tip
|
|
27
|
+
:::tip **Explainer**
|
|
27
28
|
**Powerhouse Reactors** are the nodes in the network that store and synchronise documents & drives , resolve conflicts and rerun operations to verify document event histories.
|
|
28
29
|
Reactors can be configured for local storage, centralized cloud storage or on a decentralized storage network.
|
|
29
30
|
|
|
@@ -57,10 +58,11 @@ To create a new drive in Powerhouse, follow these steps:
|
|
|
57
58
|
|
|
58
59
|
You can also add a new remote drive to your Connect environment programmatically using GraphQL mutations. This is especially useful for automation, scripting, or integrating with external systems.
|
|
59
60
|
|
|
60
|
-
|
|
61
|
+
:::info **Prerequisites**
|
|
61
62
|
- Access to the Switchboard or remote reactor (server node) of your Connect instance.
|
|
62
63
|
- The GraphQL endpoint for your instance. For example, for the staging environment, use: `https://staging.switchboard.phd/graphql/system` (this is a supergraph gateway).
|
|
63
64
|
- Appropriate permissions to perform mutations.
|
|
65
|
+
:::
|
|
64
66
|
|
|
65
67
|
### Steps
|
|
66
68
|
1. **Navigate to the GraphQL Playground or use a GraphQL client**
|
|
@@ -145,9 +145,10 @@ This command will **build** the project and create a build directory with the ou
|
|
|
145
145
|
|
|
146
146
|
This command will **start a local server** and serve the build output.
|
|
147
147
|
Inspect the build output and verify that the document models are working correctly.
|
|
148
|
+
Instead of `pnpm serve`, we'll be using:
|
|
148
149
|
|
|
149
150
|
```bash
|
|
150
|
-
|
|
151
|
+
ph connect
|
|
151
152
|
```
|
|
152
153
|
|
|
153
154
|
### 1.4 Storing your project in a git repository
|
|
@@ -202,15 +203,79 @@ If you're publishing a package under a scope (like @your-org/my-package), you mi
|
|
|
202
203
|
}
|
|
203
204
|
```
|
|
204
205
|
|
|
205
|
-
|
|
206
|
+
### 2.1 Versioning, Tagging, and Publishing Your Package
|
|
207
|
+
|
|
208
|
+
Before publishing, it's crucial to version your package correctly and tag the release in your Git repository. This helps track changes and allows users to depend on specific versions.
|
|
209
|
+
|
|
210
|
+
**1. Versioning with PNPM**
|
|
211
|
+
|
|
212
|
+
Use the `pnpm version` command to update your package version according to semantic versioning rules (`patch` for bugfixes, `minor` for new features, `major` for breaking changes). This command will:
|
|
213
|
+
- Update the `version` in your `package.json`.
|
|
214
|
+
- Create a Git commit for the version change.
|
|
215
|
+
- Create a Git tag for the new version (e.g., `v1.0.1`).
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
# For a patch release (e.g., from 1.0.0 to 1.0.1)
|
|
219
|
+
pnpm version patch
|
|
220
|
+
|
|
221
|
+
# For a minor release (e.g., from 1.0.1 to 1.1.0)
|
|
222
|
+
pnpm version minor
|
|
223
|
+
|
|
224
|
+
# For a major release (e.g., from 1.1.0 to 2.0.0)
|
|
225
|
+
pnpm version major
|
|
226
|
+
```
|
|
227
|
+
Take note of the new version tag created (e.g., `v1.0.1`), as you'll need it in the next step.
|
|
228
|
+
|
|
229
|
+
**2. Pushing Changes to Git**
|
|
230
|
+
|
|
231
|
+
Next, push your commits and the new version tag to your remote Git repository:
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
# Push your current branch (e.g., main or master)
|
|
235
|
+
# Replace 'main' with your default branch name if different
|
|
236
|
+
git push origin main
|
|
237
|
+
|
|
238
|
+
# Push the specific tag created by pnpm version
|
|
239
|
+
# Replace vX.Y.Z with the actual tag name (e.g., v1.0.1)
|
|
240
|
+
git push origin vX.Y.Z
|
|
241
|
+
```
|
|
242
|
+
The specific tag name (e.g., `v1.0.1`) is usually output by the `pnpm version` command. Pushing the specific tag is recommended to avoid unintentionally pushing other local tags.
|
|
243
|
+
|
|
244
|
+
Alternatively, to push all new local tags (use with caution):
|
|
245
|
+
```bash
|
|
246
|
+
# git push --tags
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
**3. Understanding Git Tags vs. NPM Distributor Tags**
|
|
250
|
+
|
|
251
|
+
It's important to distinguish between Git tags and NPM distributor tags (dist-tags):
|
|
252
|
+
|
|
253
|
+
- **Git Tags**: These are markers in your Git repository's history. They are primarily for developers to pinpoint specific release versions in the codebase (e.g., `v1.0.0`, `v1.0.1`). The `pnpm version` command creates these.
|
|
254
|
+
- **NPM Distributor Tags (dist-tags)**: These are labels used by the NPM registry to point to specific published versions of your package. Common NPM tags include:
|
|
255
|
+
- `latest`: This is the default tag. When someone runs `pnpm install my-package`, NPM installs the version tagged as `latest`.
|
|
256
|
+
- `beta`, `next`, `alpha`: Often used for pre-release versions.
|
|
257
|
+
When you publish a package without specifying an NPM tag, it usually gets the `latest` tag by default.
|
|
258
|
+
|
|
259
|
+
**4. Publishing to NPM**
|
|
260
|
+
|
|
261
|
+
Now you are ready to publish your package to the NPM registry. Ensure you are logged into NPM (the `npm login` command shown in previous steps should be used, or `pnpm login` which is an alias).
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
pnpm publish
|
|
265
|
+
```
|
|
266
|
+
This command will publish the version of your package that is currently specified in your `package.json`. By default, this will also set the `latest` NPM dist-tag for this version.
|
|
267
|
+
|
|
268
|
+
If your package is scoped (e.g., `@your-org/my-package`) and intended to be public, ensure your `package.json` includes the `publishConfig` shown earlier. If this is not set in `package.json` (and your package is scoped), you might need to use:
|
|
206
269
|
```bash
|
|
207
|
-
|
|
270
|
+
pnpm publish --access public
|
|
208
271
|
```
|
|
209
272
|
|
|
210
|
-
|
|
273
|
+
You can also publish a version to a specific NPM dist-tag. For example, to publish a beta version:
|
|
211
274
|
```bash
|
|
212
|
-
|
|
275
|
+
# Ensure your package.json version reflects the beta (e.g., 1.1.0-beta.0)
|
|
276
|
+
pnpm publish --tag beta
|
|
213
277
|
```
|
|
278
|
+
This is useful for testing releases before making them `latest`.
|
|
214
279
|
|
|
215
280
|
Now let's verify that the package(s) get published in the package repository, next to pre-existing packages that you might have been publishing before.
|
|
216
281
|
|