@yuneta/gobj-ui 5.3.3 → 5.5.0
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/README.md +114 -1
- package/dist/gobj-ui.cjs.js +475 -212
- package/dist/gobj-ui.es.js +473 -213
- package/index.js +5 -0
- package/package.json +44 -44
- package/src/c_yui_node.js +142 -6
- package/src/c_yui_service_view.js +362 -0
- package/src/c_yui_shell.js +42 -14
- package/src/node_tree_model.js +67 -0
- package/src/node_tree_model.test.js +70 -0
package/README.md
CHANGED
|
@@ -162,7 +162,11 @@ seen*:
|
|
|
162
162
|
"content": {"gclass": "C_MY_LANDING", "kw": {}},
|
|
163
163
|
"children": [
|
|
164
164
|
{"id": "energy", "label": "Energy", "icon": "yi-bolt",
|
|
165
|
-
"projection": {
|
|
165
|
+
"projection": {
|
|
166
|
+
"index": {"layout": "cards"},
|
|
167
|
+
"chrome": [{"layout": "tabs", "show_on": ">=tablet"},
|
|
168
|
+
{"layout": "backbar", "show_on": "<tablet"}]
|
|
169
|
+
},
|
|
166
170
|
"children": [ /* … any depth … */ ]}
|
|
167
171
|
]
|
|
168
172
|
}
|
|
@@ -173,6 +177,16 @@ seen*:
|
|
|
173
177
|
icon-bar/backbar and `show_on` all work unchanged) in two modes: `index`
|
|
174
178
|
when the node is the tip of the path — the projection IS the page — and
|
|
175
179
|
`chrome` when a child is showing — the projection is the strip around it.
|
|
180
|
+
- **Chrome belongs to the node that declares it — so every branch declares its
|
|
181
|
+
own.** A node's `chrome` strip lists *that* node's children, and its backbar
|
|
182
|
+
goes back to *that* node's route (`back_route = my_route`). A branch that
|
|
183
|
+
declares no `chrome` therefore contributes no strip, and the only ← the user
|
|
184
|
+
can reach is the nearest ancestor that did declare one. Declaring the pair
|
|
185
|
+
**only at the root** is the mistake this rule exists to name: the whole
|
|
186
|
+
subtree then shows one "← root" that says the same thing at every depth,
|
|
187
|
+
instead of one ← per level going up exactly one level. Repeat the same
|
|
188
|
+
`chrome` on every branch that can have a tip below it — that is what
|
|
189
|
+
`test-app`'s `/cards` does, and what makes its ← hierarchical.
|
|
176
190
|
- **`content` and `children` are not exclusive**: a section with its own page
|
|
177
191
|
and sub-pages is one node.
|
|
178
192
|
- **The route table does not grow.** The host declares ONE route; everything
|
|
@@ -187,6 +201,30 @@ seen*:
|
|
|
187
201
|
`projection.path`) while the rest of the tree keeps its tabs. Note the
|
|
188
202
|
asymmetry that makes it a third mode and not a layout: `index` and `chrome`
|
|
189
203
|
project a node's CHILDREN; `path` projects the way in.
|
|
204
|
+
- **`nav_mode` — the three shapes as one runtime knob.** The two bullets above
|
|
205
|
+
describe what a tree *declares*; `nav_mode` is how a user *chooses* between
|
|
206
|
+
the shapes without the app rewriting anything:
|
|
207
|
+
|
|
208
|
+
| mode | what it shows | equivalent declaration |
|
|
209
|
+
|---|---|---|
|
|
210
|
+
| `"stack"` (default) | one strip per ancestor | whatever each branch declares |
|
|
211
|
+
| `"back"` | only the tip's parent, as a `← parent` | `chrome: {"layout":"backbar"}` everywhere + `chrome_depth: 1` |
|
|
212
|
+
| `"path"` | the trail as ONE line | `path: {"layout":"breadcrumb"}` on the root + `chrome_depth: 0` |
|
|
213
|
+
|
|
214
|
+
It belongs to the **root** (`yui_node_set_nav_mode(root, "path")`, or
|
|
215
|
+
`"nav_mode"` in its declaration) and the whole tree reads it from there —
|
|
216
|
+
ancestors stacking strips while a descendant drew a breadcrumb would be
|
|
217
|
+
saying the same thing twice in two languages. Set on a middle node it is
|
|
218
|
+
refused, loudly, rather than accepted and ignored.
|
|
219
|
+
|
|
220
|
+
A mode **filters the renders as they are asked for; it never rewrites what
|
|
221
|
+
the app declared.** That is what makes `"stack"` an exact restore: a branch
|
|
222
|
+
that declared `vertical` chrome comes back as `vertical`, not as the tabs a
|
|
223
|
+
canonical "stacked" shape would have imposed. The `index` projection is
|
|
224
|
+
never touched by a mode — how a node shows its own children when it IS the
|
|
225
|
+
page is not a statement about depth. Modes are per tree, so an app can run
|
|
226
|
+
`/admin` as a breadcrumb and `/alarms` as a backbar; `test-app`'s node lab
|
|
227
|
+
cycles all three on the live tree.
|
|
190
228
|
- **`chrome_depth`** caps the stacked chrome: with every ancestor painting its
|
|
191
229
|
own strip, depth N shows N-1 of them. A node declares how many its corner of
|
|
192
230
|
the tree deserves (`0` = none, omit = all), the **deepest declaration on the
|
|
@@ -199,6 +237,7 @@ seen*:
|
|
|
199
237
|
yui_node_add(node, spec, index) yui_node_remove(node, node_id)
|
|
200
238
|
yui_node_set_projection(node, proj) yui_node_set_content(node, content)
|
|
201
239
|
yui_node_set_chrome_depth(node, n) yui_node_tree_version(node)
|
|
240
|
+
yui_node_set_nav_mode(root, mode) yui_node_nav_mode(node)
|
|
202
241
|
yui_node_find(node, "energy/north") yui_node_route(node)
|
|
203
242
|
```
|
|
204
243
|
|
|
@@ -283,6 +322,61 @@ Runnable reference: the **Cards** chapter of `test-app` (four levels plus a
|
|
|
283
322
|
panel that mutates the live tree), driven by `test-app/_qa_nodetree.mjs` and
|
|
284
323
|
`test-app/_qa_extra.mjs`.
|
|
285
324
|
|
|
325
|
+
### C_YUI_SERVICE_VIEW — mounting a view that talks to a backend
|
|
326
|
+
|
|
327
|
+
A view asks the backend for data with `gobj_command(remote, …, src = itself)`,
|
|
328
|
+
and `C_IEVENT_CLI` routes the answer back with
|
|
329
|
+
`gobj_find_service(gobj_name(src))` — **which only finds registered services**.
|
|
330
|
+
Neither host creates one, so a backend-talking view mounted directly at a route
|
|
331
|
+
never receives a single answer: it sits empty while the ievent logs *"service
|
|
332
|
+
not found"* once per answer. And a route's `target.kw` is static JSON, so it
|
|
333
|
+
cannot carry the live transport pointer either.
|
|
334
|
+
|
|
335
|
+
Two shapes, because the callers are not alike:
|
|
336
|
+
|
|
337
|
+
```js
|
|
338
|
+
/* A route with NO extras: declare the host, name the view it hosts. */
|
|
339
|
+
{ gclass: "C_YUI_SERVICE_VIEW", kw: {
|
|
340
|
+
view_gclass: "C_MY_VIEW",
|
|
341
|
+
service_name: "#my-view", // UNIQUE per mount — see below
|
|
342
|
+
view_kw: { title: "…" }
|
|
343
|
+
}}
|
|
344
|
+
|
|
345
|
+
/* A wrapper that keeps its own extras (url segments into the hosted view,
|
|
346
|
+
* rebinding it when a connection drops): drop only the boilerplate. */
|
|
347
|
+
let view = yui_mount_service_view(gobj, {
|
|
348
|
+
gclass: "C_YUI_TREEDB_TOPICS",
|
|
349
|
+
name: service_name(gobj),
|
|
350
|
+
kw: {...},
|
|
351
|
+
transport: remote // already resolved (e.g. per connection);
|
|
352
|
+
}); // omit it for "__remote_service__"
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
The hosted gclass must declare the attr the transport is injected under
|
|
356
|
+
(`gobj_remote_yuno` by default), build its `$container` in `mt_create`, and flag
|
|
357
|
+
`EVF_PUBLIC_EVENT` on whatever arrives from the backend — the ievent drops
|
|
358
|
+
events that are not public.
|
|
359
|
+
|
|
360
|
+
**The service name must be unique per mount**, and a duplicate is dangerous
|
|
361
|
+
precisely because it is not fatal: gobj-js logs *"service ALREADY REGISTERED.
|
|
362
|
+
Will be UPDATED"* and **rebinds the name**, so two mounts of one route would
|
|
363
|
+
cross their answers. Derive it from the route (or from whatever else makes the
|
|
364
|
+
mount unique — a connection id, a workspace), never from the gclass alone.
|
|
365
|
+
|
|
366
|
+
> **Why the hosts do not just create services.** It would make every routed view
|
|
367
|
+
> an inter-yuno endpoint by default, against the framework's rule that only
|
|
368
|
+
> named services are; most views never talk to a backend; and the collision
|
|
369
|
+
> above would become the default failure mode. Opt-in per route instead.
|
|
370
|
+
>
|
|
371
|
+
> **Known asymmetry, deliberately left alone:** `C_YUI_SHELL` mounts a view with
|
|
372
|
+
> `gobj_create()` (a plain child) and `C_YUI_NODE` with
|
|
373
|
+
> `gobj_create_pure_child()`. The flag decides whether a gclass that consults
|
|
374
|
+
> `gobj_is_pure_child()` sends its output event straight to the parent or
|
|
375
|
+
> publishes it. Today nothing consults it *on a view* (only `c_ievent_cli` and
|
|
376
|
+
> `c_timer` do, and always about themselves), so the difference has no observed
|
|
377
|
+
> consequence — but the same view gclass does get a different flag depending on
|
|
378
|
+
> who mounted it. Align it the day it bites, with the case that bit.
|
|
379
|
+
|
|
286
380
|
### C_YUI_JSON — lazy JSON tree viewer
|
|
287
381
|
|
|
288
382
|
Indentation follows the house rule: four characters per level, plus a **guide
|
|
@@ -401,6 +495,25 @@ shapes, and the fix for each:
|
|
|
401
495
|
| A composed string (`` `${key} · ${t(mode)}` ``) | carries no key at all | split it: the translatable halves get their own key. (Note `createElement2` **trims** text nodes — space a `·` separator with CSS, not with spaces.) |
|
|
402
496
|
| `title` / `aria-label` set with `t()` | tooltip stuck in the old language | `data-i18n-title` / `data-i18n-aria-label` |
|
|
403
497
|
| Anything a WIDGET renders (a Tabulator header, its paginator, a formatter) | drawn once; no attribute reaches it | subscribe to the shell and re-render (below) |
|
|
498
|
+
| DOM built AFTER start up (a node's strips, a dropdown panel) | renders the raw key — indistinguishable from a MISSING key | `yui_shell_translate(shell, $el)` right after building it (below) |
|
|
499
|
+
|
|
500
|
+
**Carrying the key is not enough for the FIRST render.** A node is born holding
|
|
501
|
+
the raw English key, and the app's `refresh_language()` passes only walk what
|
|
502
|
+
already exists: the shell tree at start up, `document.body` on a language
|
|
503
|
+
switch. Anything built later — a `C_YUI_NODE` strip rendered when you walk into
|
|
504
|
+
it, a lazily-built toolbar panel — is reached by neither, so it renders the key:
|
|
505
|
+
lower-case English that never changes language, which is exactly what a missing
|
|
506
|
+
key looks like. Division of labour:
|
|
507
|
+
|
|
508
|
+
```js
|
|
509
|
+
yui_shell_translate(shell, $el); // LIBRARY-built DOM, right after building it
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
and **app view gclasses translate their own DOM** — they own a `t`, so they call
|
|
513
|
+
`refresh_language($container, t)` at the end of their build (this is why the
|
|
514
|
+
shell does not translate a mounted view: see `mount_view` in `c_yui_shell.js`).
|
|
515
|
+
`yui_shell_translate` is a no-op when the app registered no translator, so
|
|
516
|
+
behaviour is unchanged for apps that never call `yui_shell_set_translator`.
|
|
404
517
|
|
|
405
518
|
**The contract.** The app owns the locales: it switches its i18next and calls
|
|
406
519
|
|