Parcourir la source

frontend-demo: serve the four client modules and add a stm-prefill profile section

The /statum.js readfile route served a file that no longer exists; one regex route now serves js/statum-core.js, statum-document.js, statum-markdown.js and statum-forms.js with the same content-type handling. index.html loads the four modules in canonical order with the toast-override script kept immediately after (still before DOMContentLoaded).

New Profile section exercising the stm-prefill control matrix end to end: text input, select, checkbox and a radio group (each member carrying the same expression), prefilled from a session-backed profile slot, saved by the form's own stm-action, cleared by stm-clear-on-success, with an stm-text echo row showing the saved state. Verified with the PHP dev server: module URLs 200, entrypoint returns the profile set, and the save action round-trips name/theme/news/cadence plus a success notify.
saddle-ClankerSaddle il y a 3 semaines
Parent
commit
a71c587e44
2 fichiers modifiés avec 97 ajouts et 6 suppressions
  1. 52 2
      frontend-demo/index.html
  2. 45 4
      frontend-demo/server.php

+ 52 - 2
frontend-demo/index.html

@@ -363,6 +363,51 @@
         <div class="md" stm-markdown="markdown.body">Rendering markdown…</div>
       </section>
 
+      <!--
+        Profile: stm-prefill writes state into control PROPERTIES (never
+        attributes), covering the whole control matrix: text input, select,
+        checkbox (checked), and a radio group (every member carries the same
+        expression; the one whose value matches is checked). Controls the user
+        has edited are dirty and skipped until the form resets.
+        stm-clear-on-success resets this form when its own stm-action succeeds
+        (the reset also un-dirties the controls, so the fresh prefill below can
+        re-apply). The stm-text line echoes the saved state back.
+      -->
+      <section>
+        <h2>Profile <span class="muted">(stm-prefill · dirty tracking · stm-clear-on-success)</span></h2>
+        <form stm-action="profile.saveAction" stm-clear-on-success>
+          <p>
+            <label>Name <input type="text" name="name" stm-prefill="profile.name"></label>
+          </p>
+          <p>
+            <label>Theme
+              <select name="theme" stm-prefill="profile.theme">
+                <option value="light">Light</option>
+                <option value="dark">Dark</option>
+                <option value="auto">Auto</option>
+              </select>
+            </label>
+          </p>
+          <p>
+            <label><input type="checkbox" name="news" value="1" stm-prefill="profile.news"> Email me news</label>
+          </p>
+          <p>
+            Digest:
+            <label><input type="radio" name="cadence" value="daily" stm-prefill="profile.cadence"> Daily</label>
+            <label><input type="radio" name="cadence" value="weekly" stm-prefill="profile.cadence"> Weekly</label>
+            <label><input type="radio" name="cadence" value="never" stm-prefill="profile.cadence"> Never</label>
+          </p>
+          <p><button type="submit">Save profile</button></p>
+        </form>
+        <p class="muted">
+          Saved:
+          <span stm-text="profile.name">…</span>
+          · theme <span stm-text="profile.theme">…</span>
+          · news <span stm-text="profile.news ? 'on' : 'off'">…</span>
+          · digest <span stm-text="profile.cadence">…</span>
+        </p>
+      </section>
+
       <!--
         Live channel: subscribe/unsubscribe directives drive a realtime
         Server-Sent Events connection; broadcasting pushes a `set` to every
@@ -401,8 +446,13 @@
   <!-- Container for the toast notifications rendered by the handlers below. -->
   <div id="toasts"></div>
 
-  <!-- The library auto-initialises on DOMContentLoaded. -->
-  <script src="/statum.js"></script>
+  <!-- The library auto-initialises on DOMContentLoaded. Four modules, in load
+       order: core first (it installs the seams the later modules register
+       into), then document, markdown, forms. -->
+  <script src="/statum-core.js"></script>
+  <script src="/statum-document.js"></script>
+  <script src="/statum-markdown.js"></script>
+  <script src="/statum-forms.js"></script>
   <script>
     // Override the default UI handlers so `notify` directives (and errors)
     // render as styled toasts instead of using the browser's native alert().

+ 45 - 4
frontend-demo/server.php

@@ -29,7 +29,7 @@ $CATALOG = [
     ['id' => 4, 'name' => 'Gizmo',   'price' => 29.99, 'featured' => true,  'inStock' => true],
 ];
 
-$TYPES = ['counter', 'products', 'cart', 'user', 'notice', 'markdown'];
+$TYPES = ['counter', 'products', 'cart', 'user', 'notice', 'markdown', 'profile'];
 
 // ---------------------------------------------------------------------------
 // Helpers
@@ -234,6 +234,24 @@ MD;
     return ['body' => $body];
 }
 
+/**
+ * Profile/settings form state for the stm-prefill section. Every control of
+ * the type matrix (text, select, checkbox, radio group) round-trips through
+ * the session; `saveAction` is the form's stm-action and the same snapshot
+ * re-sets after the save so the prefill bindings and the echo update.
+ */
+function build_profile(): array
+{
+    $p = $_SESSION['profile'] ?? [];
+    return [
+        'name'    => (string)($p['name'] ?? 'Ada Lovelace'),
+        'theme'   => (string)($p['theme'] ?? 'light'),
+        'news'    => (bool)($p['news'] ?? true),
+        'cadence' => (string)($p['cadence'] ?? 'weekly'),
+        'saveAction' => ['uri' => '/api/profile/save', 'method' => 'POST'],
+    ];
+}
+
 /** Build the snapshot for a type with its demo scope/options. */
 function snap(string $type): ?array
 {
@@ -244,6 +262,7 @@ function snap(string $type): ?array
         case 'user':     return make_snapshot('user', 'session', build_user(), ['stale' => true]);
         case 'notice':   return make_snapshot('notice', 'flow', build_notice());
         case 'markdown': return make_snapshot('markdown', 'session', build_markdown());
+        case 'profile':  return make_snapshot('profile', 'session', build_profile());
     }
     return null;
 }
@@ -358,10 +377,11 @@ function initial_frame_for_key(string $key): ?array
 $method = $_SERVER['REQUEST_METHOD'];
 $path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
 
-// Static assets.
-if ($path === '/statum.js') {
+// Static assets: the Statum client modules, in load order (core first — the
+// later modules register into the seams it installs).
+if (preg_match('#^/(statum-(core|document|markdown|forms))\.js$#', $path, $m)) {
     header('Content-Type: application/javascript');
-    readfile(__DIR__ . '/../js/statum.js');
+    readfile(__DIR__ . '/../js/' . $m[1] . '.js');
     exit;
 }
 
@@ -545,6 +565,27 @@ if ($path === '/api/user/toggle' && $method === 'POST') {
     statum_respond([set_dir(snap('user'))]);
 }
 
+// The stm-prefill demo form's save. The form's controls carry the session
+// values via stm-prefill; this action stores the submitted edits and re-sets
+// the profile snapshot (which also re-runs the prefill bindings — on the
+// non-dirty controls — and the stm-text echo below the form).
+if ($path === '/api/profile/save' && $method === 'POST') {
+    $theme = (string)($_POST['theme'] ?? 'light');
+    $cadence = (string)($_POST['cadence'] ?? 'weekly');
+    $allowedThemes = ['light', 'dark', 'auto'];
+    $allowedCadences = ['daily', 'weekly', 'never'];
+    $_SESSION['profile'] = [
+        'name'    => trim((string)($_POST['name'] ?? '')),
+        'theme'   => in_array($theme, $allowedThemes, true) ? $theme : 'light',
+        'news'    => ($_POST['news'] ?? '') !== '',
+        'cadence' => in_array($cadence, $allowedCadences, true) ? $cadence : 'weekly',
+    ];
+    statum_respond([
+        set_dir(snap('profile')),
+        ['type' => 'notify', 'kind' => 'success', 'message' => 'Profile saved'],
+    ]);
+}
+
 if ($path === '/api/notice/set' && $method === 'POST') {
     $_SESSION['notice'] = '<div class="ok"><strong>Notice:</strong> updated at ' . date('H:i:s') . '</div>';
     statum_respond([set_dir(snap('notice'))]);