@yuneta/gobj-ui 5.4.0 → 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 CHANGED
@@ -322,6 +322,61 @@ Runnable reference: the **Cards** chapter of `test-app` (four levels plus a
322
322
  panel that mutates the live tree), driven by `test-app/_qa_nodetree.mjs` and
323
323
  `test-app/_qa_extra.mjs`.
324
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
+
325
380
  ### C_YUI_JSON — lazy JSON tree viewer
326
381
 
327
382
  Indentation follows the house rule: four characters per level, plus a **guide
@@ -440,6 +495,25 @@ shapes, and the fix for each:
440
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.) |
441
496
  | `title` / `aria-label` set with `t()` | tooltip stuck in the old language | `data-i18n-title` / `data-i18n-aria-label` |
442
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`.
443
517
 
444
518
  **The contract.** The app owns the locales: it switches its i18next and calls
445
519