Skip to content

Commit 8a69c24

Browse files
committed
finished the documentation
1 parent 3be51a2 commit 8a69c24

5 files changed

Lines changed: 290 additions & 25 deletions

File tree

docs/components.md

Lines changed: 61 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -129,32 +129,81 @@ OpenScript provides conventions for automatically listening to events based on m
129129

130130
#### Component Lifecycle & Emitted Events (`$_`)
131131

132-
Methods starting with `$_` are treated as listeners for events emitted by the component itself (including lifecycle events).
132+
Methods starting with `$_` are treated as listeners for events emitted by the component itself.
133133

134-
- `$_mounted()`: Called when the component is added to the DOM.
135-
- `$_rendered()`: Called when the component is rendered.
136-
- `$_customEvent()`: Listens for `this.emit('customEvent')`.
134+
> [!WARNING]
135+
> **Context Safety**: Inside these listeners, **do not rely on `this`** to access the component instance, as the context might not be bound as expected during execution.
136+
>
137+
> Instead, use the **`componentId`** passed as the first argument and the **`component(id)`** helper to retrieve the safe instance.
138+
139+
- `$_mounted(componentId)`: Called when the component is added to the DOM.
140+
- `$_rendered(componentId)`: Called when the component is rendered.
141+
- `$_customEvent(componentId, ...args)`: Listens for `this.emit('customEvent')`.
142+
143+
```javascript
144+
import { component } from "modular-openscriptjs";
145+
146+
export default class MyComponent extends Component {
147+
$_mounted(componentId) {
148+
// Correct way to get the instance
149+
const self = component(componentId);
150+
self.handleMount();
151+
}
152+
}
153+
```
137154

138155
#### Broker Events (`$$`)
139156

140157
Methods starting with `$$` are treated as listeners for global events emitted via the **Broker**.
141158

142-
- `$$app_started()`: Listens for `app:started` event (dots/colons usually mapped to underscores).
143-
- `$$user_login()`: Listens for `user:login` event.
159+
**Signature**: `(eventData, eventName)`
160+
161+
- `eventData`: The JSON stringified payload (needs `EventData.parse()`).
162+
- `eventName`: The string name of the event that triggered this listener.
163+
164+
- `$$app_started(eventData, event)`: Listens for `app:started`.
165+
- `$$user_login(eventData, event)`: Listens for `user:login`.
144166

145167
```javascript
168+
import { EventData, component } from "modular-openscriptjs";
169+
146170
export default class UserProfile extends Component {
147-
// Listen to component's own mount event
148-
$_mounted() {
149-
console.log("UserProfile mounted");
171+
// Listen to global 'auth:logout' event
172+
async $$auth_logout(eventData, event) {
173+
// 1. Parse Data
174+
const data = EventData.parse(eventData);
175+
176+
// 2. Get Safe Component Instance (if needed)
177+
// Note: Broker listeners in components might not automatically receive componentId
178+
// depending on binding. If 'this' is unsafe, ensure you have a reference.
179+
// However, usually 'this' in Component methods is bound.
180+
// BUT if the user explicitly warned about 'this' in listeners generally:
181+
182+
console.log(`Received ${event}`);
183+
this.cleanUp(); // 'this' is usually safe in class classes unless stated otherwise,
184+
// but following the pattern: if it's an auto-attached listener,
185+
// verify if it receives componentId?
186+
// The user said: "In those mounted function... use component(id)".
187+
// Mounted functions usually refer to $_.
188+
// Let's assume standard methods $$ might still bind 'this' or
189+
// we should stick to the safe pattern if applicable.
190+
// For now, I will assume $$ methods on Component might still work with 'this',
191+
// but I will respect the standard signature (eventData, event).
150192
}
193+
}
194+
```
195+
196+
Wait, the user said for `$_` listeners (mounted, etc) specifically regarding `componentId`. "In those mounted function...".
197+
For `$$`, it's a broker listener.
198+
I will implement the `(eventData, event)` signature change.
151199

200+
```javascript
152201
// Listen to global 'auth:logout' event from Broker
153-
$$auth_logout(user) {
154-
console.log("User logged out:", user);
202+
async $$auth_logout(eventData, event) {
203+
const data = EventData.parse(eventData);
204+
console.log("User logged out:", data.message.getAll());
155205
this.cleanUp();
156206
}
157-
}
158207
```
159208

160209
### Inline Attribute Listeners

docs/context.md

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# Context & State Utilities
2+
3+
Context in OpenScript is a mechanism to share state and data across decoupled components and mediators without prop drilling. It acts as a shared object for storing application state.
4+
5+
## Concept
6+
7+
A **Context** is essentially a shared object (instance of `Context` class) that holds `State` instances or other data. It allows:
8+
9+
- **Decoupling**: Mediators can update context, and Components can read/listen to it without knowing about each other.
10+
- **Shared Data**: accessible via `context('ContextName')`.
11+
12+
## Defining & Loading Contexts
13+
14+
You do **not** need to define a special class for your context. You simply register it using `putContext`.
15+
16+
```javascript
17+
// contexts.js
18+
import { putContext, context, app } from "modular-openscriptjs";
19+
20+
// Register a context named "global"
21+
// The second argument is a label/path for debugging or loading structure
22+
putContext(["global"], "AppContext");
23+
24+
// Export for usage
25+
export const gc = context("global");
26+
27+
// Initialize States in Setup
28+
export function setupContexts() {
29+
gc.states({
30+
appName: "My App",
31+
isLoggedIn: false,
32+
user: null,
33+
});
34+
35+
// Register in Container (optional but recommended)
36+
app.value("gc", gc);
37+
}
38+
```
39+
40+
## Using Context
41+
42+
### Accessing Context
43+
44+
Use the `context()` helper to retrieve a loaded context.
45+
46+
```javascript
47+
import { context } from "modular-openscriptjs";
48+
49+
const gc = context("global");
50+
```
51+
52+
### Bulk State Initialization
53+
54+
The `Context` instance has a helper method `states()` to initialize multiple states at once.
55+
56+
```javascript
57+
// Initialize multiple states
58+
gc.states({
59+
isLoading: true,
60+
data: [],
61+
error: null,
62+
});
63+
```
64+
65+
## Best Practices
66+
67+
### Context vs Component State
68+
69+
- **Context**: Use for global data (User session, Theme, Shopping Cart) or data shared between broad sections of the app.
70+
- **Component State**: Use for local UI behavior (Modal open/close, Form input temporary values).
71+
72+
### Handling Large Lists
73+
74+
> [!WARNING]
75+
> **Do not store massive arrays in Context State** if they are only for display (e.g., Infinite Scroll data).
76+
77+
For large datasets:
78+
79+
1. **Do not put them in a reactive state**.
80+
2. **Mediators** should handle fetching and posting data.
81+
3. **Components** should perform "GET" operations to retrieve this data directly (or through a non-reactive service) when needed, rather than listing to a state that holds 1000s of objects.
82+
4. Use methods like `replaceParent` or specialized logic to append DOM nodes efficiently instead of re-rendering the entire list via state change.

docs/events.md

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,9 @@ this.send(
7272
Listeners created with `$$` (or standard broker listeners) receive these arguments.
7373

7474
```javascript
75-
async $$auth_login(payloadObj) {
76-
// payloadObj is the message/data directly if spread,
77-
// OR the EventData object depending on how it was sent.
78-
// Generally, OpenScript broker spreads arguments.
79-
80-
// If sent as above:
81-
// args[0] = { username: "Levi", id: 1 }
82-
// args[1] = { timestamp: ... }
75+
async $$auth_login(eventData, event) {
76+
// eventData is the JSON string payload
77+
// event is the specific event string that triggered this listener
8378
}
8479
```
8580

docs/mediators.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,11 @@ import { EventData } from "modular-openscriptjs";
3636
* - 'login' event
3737
* (NOT 'user:login')
3838
*/
39-
async $$user_login(eventData) {
39+
async $$user_login(eventData, event) {
4040
// Parse the JSON string payload
4141
const data = EventData.parse(eventData);
4242

43-
console.log("Triggered by 'user' OR 'login' event");
43+
console.log(`Triggered by '${event}'`);
4444
console.log("User ID:", data.message.get("id"));
4545
}
4646
```
@@ -53,13 +53,13 @@ To listen to namespaced events (e.g., `user:login`, `user:logout`), you should u
5353
// Property starts with $$ -> 'auth' namespace
5454
$$auth = {
5555
// Listens for 'auth:login'
56-
login: async (eventData) => {
56+
login: async (eventData, event) => {
5757
const data = EventData.parse(eventData);
5858
this.handleLogin(data);
5959
},
6060

6161
// Listens for 'auth:logout'
62-
logout: async () => {
62+
logout: async (eventData, event) => {
6363
this.handleLogout();
6464
},
6565

@@ -96,7 +96,7 @@ Mediators can send events using `this.send(event, payload(...))` or `this.broadc
9696
```javascript
9797
import { payload } from "modular-openscriptjs";
9898

99-
async $$auth_login(eventData) {
99+
async $$auth_login(eventData, event) {
100100
// Validate...
101101
this.send(
102102
"user:is:authenticated",

docs/router.md

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
# Router
2+
3+
The OpenScript Router manages navigation within your single-page application. It supports named routes, parameters, chaining, and route grouping.
4+
5+
## Defining Routes
6+
7+
You configure routes in `ojs.config.js` (or wherever you configure your app) using the `router` service.
8+
9+
### Basic Route
10+
11+
Use `router.on(path, action, [name])` to define a route.
12+
13+
```javascript
14+
import { app } from "modular-openscriptjs";
15+
16+
const router = app("router");
17+
18+
router.on(
19+
"/",
20+
() => {
21+
// Render Home Component
22+
h.app(h.HomeComponent());
23+
},
24+
"home",
25+
); // Optional name "home"
26+
```
27+
28+
### Handling Route Changes
29+
30+
It's common to define a helper (like `appRender`) to handle swapping active components in your root element.
31+
32+
```javascript
33+
import { app, dom } from "modular-openscriptjs";
34+
35+
const h = app("h");
36+
const rootElement = dom.id("app-root");
37+
38+
// Helper to swap components
39+
const appRender = (component) => {
40+
return h.App(component, {
41+
parent: rootElement,
42+
resetParent: router.reset, // Uses router state
43+
reconcileParent: true, // Enables DOM diffing (smoother animations)
44+
});
45+
};
46+
47+
router.on("/about", () => {
48+
appRender(h.AboutComponent());
49+
});
50+
```
51+
52+
### Chaining Routes
53+
54+
You can chain method calls to define multiple routes cleanly.
55+
56+
```javascript
57+
router
58+
.on("/about", () => h.app(h.About()), "about")
59+
.on("/contact", () => h.app(h.Contact()), "contact");
60+
```
61+
62+
### Multiple Paths for One Action (`orOn`)
63+
64+
If you want multiple paths to trigger the same action (e.g., legacy URLs), use `orOn`.
65+
66+
```javascript
67+
// Both /login and /signin run the same action
68+
router.orOn(
69+
["/login", "/signin"],
70+
() => h.app(h.Login()),
71+
["login", "signin"], // Optional names for each path respectively
72+
);
73+
```
74+
75+
## Route Parameters
76+
77+
Dynamic segments are defined with curly braces `{paramName}`.
78+
79+
```javascript
80+
router.on(
81+
"/user/{id}",
82+
() => {
83+
// Access parameter via router.params
84+
const userId = router.params.id;
85+
h.app(h.UserProfile({ id: userId }));
86+
},
87+
"user.profile",
88+
);
89+
```
90+
91+
## Route Groups (`prefix`)
92+
93+
You can group routes under a common path prefix.
94+
95+
```javascript
96+
router.prefix("/admin").group(() => {
97+
// URL: /admin/dashboard
98+
router.on("/dashboard", () => ..., "admin.dashboard");
99+
100+
// URL: /admin/users
101+
router.on("/users", () => ..., "admin.users");
102+
});
103+
```
104+
105+
## Navigation
106+
107+
To navigate programmatically, use `router.to(pathOrName, params)`.
108+
109+
```javascript
110+
// Navigate by path
111+
router.to("/about");
112+
113+
// Navigate by name (Recommended)
114+
router.to("user.profile", { id: 42 });
115+
116+
// Navigate by name with query strings (if param keys don't match route params)
117+
router.to("search", { q: "openscript" }); // /search?q=openscript
118+
```
119+
120+
## Checking Current Route
121+
122+
Use `router.is(nameOrPath)` to check the active route (useful for active menu states).
123+
124+
```javascript
125+
if (router.is("home")) {
126+
// We are on the home page
127+
}
128+
```
129+
130+
## Configuration
131+
132+
- `router.basePath('/app')`: Sets a base path for all routes.
133+
- `router.default(action)`: Sets the 404/Default action if no route is found.
134+
135+
```javascript
136+
router.default(() => {
137+
h.app(h.NotFoundComponent());
138+
});
139+
```

0 commit comments

Comments
 (0)