pict-section-form 1.1.0 → 1.1.2

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.
Files changed (45) hide show
  1. package/docs/_cover.md +3 -0
  2. package/docs/_sidebar.md +5 -0
  3. package/docs/_version.json +3 -3
  4. package/docs/examples/README.md +3 -0
  5. package/docs/examples/change_tracking/change_tracking.js +9437 -9427
  6. package/docs/examples/diagram_form/README.md +116 -0
  7. package/docs/examples/diagram_form/diagram_form_example.min.js +42 -0
  8. package/docs/examples/diagram_form/excalidraw-iframe-host.html +25 -0
  9. package/docs/examples/diagram_form/excalidraw-wrapper.css +1 -0
  10. package/docs/examples/diagram_form/excalidraw-wrapper.min.js +3327 -0
  11. package/docs/examples/diagram_form/index.html +116 -0
  12. package/docs/examples/diagram_form/manifest.json +13 -0
  13. package/docs/examples/diagram_form/react-vendor.min.js +9 -0
  14. package/docs/examples/dynamic_analysis/dynamic_analysis_application.js +6549 -6550
  15. package/docs/examples/gradebook/gradebook_application.min.js +2 -2
  16. package/docs/examples/ndt_field_test/ndt_field_test.js +9437 -9427
  17. package/docs/examples/richtext_form/README.md +132 -0
  18. package/docs/examples/richtext_form/index.html +82 -0
  19. package/docs/examples/richtext_form/richtext_form_example.min.js +42 -0
  20. package/docs/examples/scope_mathematics/scope_mathematics.js +9437 -9427
  21. package/docs/examples/simple_table/simple_tabular_application.min.js +2 -2
  22. package/docs/examples/superhero_studio/README.md +199 -0
  23. package/docs/examples/superhero_studio/excalidraw-iframe-host.html +25 -0
  24. package/docs/examples/superhero_studio/excalidraw-wrapper.css +1 -0
  25. package/docs/examples/superhero_studio/excalidraw-wrapper.min.js +3327 -0
  26. package/docs/examples/superhero_studio/index.html +166 -0
  27. package/docs/examples/superhero_studio/manifest.json +13 -0
  28. package/docs/examples/superhero_studio/react-vendor.min.js +9 -0
  29. package/docs/examples/superhero_studio/superhero_studio_example.min.js +42 -0
  30. package/docs/index.html +2 -2
  31. package/docs/retold-catalog.json +45 -1
  32. package/docs/retold-keyword-index.json +11843 -7677
  33. package/example_applications/diagram_form/DiagramForm-Example-Application.js +145 -0
  34. package/example_applications/diagram_form/html/index.html +116 -0
  35. package/example_applications/diagram_form/package.json +29 -0
  36. package/example_applications/richtext_form/RichTextForm-Example-Application.js +176 -0
  37. package/example_applications/richtext_form/html/index.html +82 -0
  38. package/example_applications/richtext_form/package.json +28 -0
  39. package/example_applications/superhero_studio/SuperheroStudio-Example-Application.js +313 -0
  40. package/example_applications/superhero_studio/html/index.html +166 -0
  41. package/example_applications/superhero_studio/package.json +29 -0
  42. package/example_applications/superhero_studio/superheroes/Superheroes.js +386 -0
  43. package/package.json +3 -9
  44. package/source/providers/inputs/Pict-Provider-Input-Diagram.js +1 -31
  45. package/source/providers/inputs/Pict-Provider-Input-RichText.js +1 -32
@@ -0,0 +1,199 @@
1
+ # Superhero Studio
2
+
3
+ <!-- docuserve:example-launch:start -->
4
+ > **[Launch the live app](examples/superhero%5Fstudio/index.html)** - runs in your browser, opens in a new tab.
5
+ <!-- docuserve:example-launch:end -->
6
+
7
+ **Superhero Studio** is the showcase for the two new complex InputTypes
8
+ in pict-section-form — `RichText`
9
+ ([docs](https://fable-retold.github.io/pict-section-form/#/page/input%5Fproviders/014-rich-text))
10
+ and `Diagram`
11
+ ([docs](https://fable-retold.github.io/pict-section-form/#/page/input%5Fproviders/015-diagram))
12
+ — both living inside the same form alongside stock `Text`, `Number`,
13
+ `Option`, and `TextArea` inputs. A dropdown at the top loads one of five
14
+ pre-baked superheroes; the form's portrait recolors with the active theme
15
+ in real time.
16
+
17
+ Both providers live in pict-section-form and lazy-require their host
18
+ editor modules (pict-section-markdowneditor for RichText,
19
+ pict-section-excalidraw for Diagram) on the first edit toggle, so the
20
+ view-by-default boot loads neither CodeMirror nor the Excalidraw vendor.
21
+
22
+ If the [Diagram Form](../diagram%5Fform/README.md) and [RichText Form Input](../richtext%5Fform/README.md)
23
+ demos each show one InputType in isolation, this one is what happens when
24
+ you let them share a record.
25
+
26
+ ## What it demonstrates
27
+
28
+ | Capability | Where you see it |
29
+ |------------|------------------|
30
+ | Compound form, mixed InputTypes | One `DefaultFormManifest` with `Text` / `Number` / `Option` / `TextArea` / `RichText` / `Diagram` fields |
31
+ | Per-field runtime mode toggle | `provider.toggleMode(hash, cb)` flips Portrait or Origin Story between view and edit; the rest of the form stays put |
32
+ | View-by-default boot | The page loads with rendered markdown + inline SVG — no CodeMirror, no Excalidraw — until you click Edit |
33
+ | Themeified SVG | Portraits use `var(--diagram-ink, #...)` etc. with hex fallbacks; theme swatches recolor every portrait without a re-render |
34
+ | Record swap without leaking instances | Loading a new hero from the dropdown calls `setMode('view')` on any open editors first, then swaps `AppData` and re-marshals |
35
+ | Pluggable image uploader for RichText | `PictForm.RichText.ImageUploader: 'uploadImage'` routes pastes through `pict.PictApplication.uploadImage(file, descriptor, cb)` |
36
+
37
+ ## Key files
38
+
39
+ - `SuperheroStudio-Example-Application.js` — the app. Registers both input providers, defines the `_Descriptors` map (12 fields), owns the dropdown / toggle handlers, and stubs the `uploadImage` callback.
40
+ - `superheroes/Superheroes.js` — five hero dossiers. Each entry carries identity + powers stats, a hand-illustrated themed SVG portrait, and a markdown origin story.
41
+ - `html/index.html` — page chrome: the hero-select dropdown, four theme swatches that hot-swap `--diagram-*` CSS variables, and the per-field Edit/Done buttons that call `provider.toggleMode`.
42
+ - `preview/portraits.html` — a static, build-free preview of the five portraits under all four themes. Useful for inspecting the SVGs without building the full app.
43
+
44
+ ## The form manifest
45
+
46
+ Four sections, twelve fields. Each `Descriptor` selects its `InputType`:
47
+
48
+ ```javascript
49
+ const _Descriptors =
50
+ {
51
+ 'Name': { Name: 'Name', DataType: 'String', PictForm: { Section: 'Identity', Width: 6, InputType: 'Text' } },
52
+ 'AlterEgo': { Name: 'Alter Ego', DataType: 'String', PictForm: { Section: 'Identity', Width: 6, InputType: 'Text' } },
53
+ 'ShoeSize': { Name: 'Shoe Size', DataType: 'Number', PictForm: { Section: 'Identity', Width: 3, InputType: 'Number', Min: 1, Max: 22, Step: 0.5 } },
54
+ 'OriginYear': { Name: 'Origin Year', DataType: 'Number', PictForm: { Section: 'Identity', Width: 3, InputType: 'Number', Min: 1900, Max: 2099 } },
55
+
56
+ 'PowerLevel': { Name: 'Power Level', DataType: 'Number', PictForm: { Section: 'Powers', Width: 3, InputType: 'Number', Min: 1, Max: 10 } },
57
+ 'PrimaryPower': { Name: 'Primary Power',DataType: 'String', PictForm: { Section: 'Powers', Width: 5, InputType: 'Option', SelectOptions: _PowerOptions } },
58
+ 'ThemeColor': { Name: 'Theme Color', DataType: 'String', PictForm: { Section: 'Powers', Width: 4, InputType: 'Option', SelectOptions: _ColorOptions } },
59
+ 'Catchphrase': { Name: 'Catchphrase', DataType: 'String', PictForm: { Section: 'Powers', Width: 12, InputType: 'TextArea', TextAreaRows: 2 } },
60
+
61
+ 'Portrait': { Name: 'Portrait', DataType: 'String',
62
+ PictForm:
63
+ {
64
+ Section: 'Portrait',
65
+ Width: 12,
66
+ InputType: 'Diagram',
67
+ Diagram: { ThemeColors: true, Height: '320px', EditorImplementation: 'react' }
68
+ }
69
+ },
70
+
71
+ 'OriginStory': { Name: 'Origin Story', DataType: 'String',
72
+ PictForm:
73
+ {
74
+ Section: 'OriginStory',
75
+ Width: 12,
76
+ InputType: 'RichText',
77
+ RichText: { AllowImages: true, ImageUploader: 'uploadImage', Height: '380px' }
78
+ }
79
+ }
80
+ };
81
+ ```
82
+
83
+ The two complex InputTypes don't need anything more elaborate than the stock ones — they just look up their template hash in pict-section-form's `DynamicTemplates` registry (which the providers populated at construction time).
84
+
85
+ ## Feature 1 — View-by-default with lazy editor mount
86
+
87
+ The form boots with **every** RichText and Diagram field in view mode. The page renders pre-parsed markdown into the Origin Story slot and writes the saved SVG verbatim into the Portrait slot — that's it. Neither CodeMirror nor the Excalidraw React bundle has been touched. Open the DevTools Network tab on a fresh load and confirm: no `vendor`-side requests fire.
88
+
89
+ A field flips into edit mode on user demand:
90
+
91
+ ```javascript
92
+ demo_toggleMode(pInputHash)
93
+ {
94
+ let tmpProvider = this._providerForInput(pInputHash);
95
+ tmpProvider.toggleMode(pInputHash, (pErr) =>
96
+ {
97
+ this._refreshToggleLabels(); // updates the button text "Edit" ↔ "Done"
98
+ });
99
+ }
100
+ ```
101
+
102
+ The provider's `setMode('edit', cb)` is what triggers the lazy load. Form-mostly-read consumers (dashboards, audit logs, read-only beacons) pay for neither editor bundle.
103
+
104
+ ## Feature 2 — Themeified portrait SVGs
105
+
106
+ The portraits use a small palette of theme tokens, each paired with a hex fallback so the SVG is correct standalone (file viewer, `<img>` tag, etc.):
107
+
108
+ ```html
109
+ <rect ... stroke="var(--diagram-ink, #1B1F23)" fill="var(--diagram-paper, #FDFCF7)"/>
110
+ <path ... stroke="var(--diagram-accent, #E66C2C)"/>
111
+ ```
112
+
113
+ The page defines the tokens in a `<style id="diagram-theme">` block and `applyTheme(name)` rewrites it. The portrait SVGs re-color instantly — no app interaction, no re-render. The same `themeifySVG()` pass is the **default-on save path** in the Diagram input provider, so user-drawn portraits inherit the same theme contract automatically.
114
+
115
+ Try it: notebook → dark → blueprint → sepia. Vector Vince's CAD grid adapts. The Modal Avenger's title bar tracks the accent. Captain Verbose's thought-bubble dotted outline stays legible against either light or dark paper.
116
+
117
+ ## Feature 3 — Swapping records cleanly
118
+
119
+ When the dropdown changes, the app needs to:
120
+
121
+ 1. Fold any open editors back to view mode (so the next hero doesn't inherit yesterday's CodeMirror instance).
122
+ 2. Replace `AppData.SuperheroForm` with the new hero's record.
123
+ 3. Push the new record into the rendered form via `marshalDataFromAppDataToView()`.
124
+
125
+ The implementation:
126
+
127
+ ```javascript
128
+ demo_loadHero(pSlug)
129
+ {
130
+ let tmpHero = libSuperheroes[pSlug];
131
+ if (!tmpHero) return;
132
+ this._currentHeroSlug = pSlug;
133
+
134
+ this._foldAllEditors();
135
+ this.pict.AppData.SuperheroForm = JSON.parse(JSON.stringify(tmpHero));
136
+ this.marshalDataFromAppDataToView();
137
+
138
+ this._refreshToggleLabels();
139
+ }
140
+ ```
141
+
142
+ `_foldAllEditors()` walks the known complex-input hashes and calls `provider.setMode('view')` on any that are currently `'edit'`. The provider tears down the editor view, frees its AppData stash, and re-renders the slot in view mode using the value it had last seen. Then the marshal step paints the new hero on top.
143
+
144
+ ## Feature 4 — Image upload routed through the application
145
+
146
+ The Origin Story field has `AllowImages: true` and names an uploader. When the user pastes or drops an image, the RichText provider dispatches to `pict.PictApplication.uploadImage`:
147
+
148
+ ```javascript
149
+ uploadImage(pFile, pInputDescriptor, fCallback)
150
+ {
151
+ setTimeout(() =>
152
+ {
153
+ let tmpFakeURL = '/uploads/heroes/' + Date.now() + '-' +
154
+ (pFile.name || 'image.png').replace(/[^A-Za-z0-9._-]/g, '_');
155
+ fCallback(null, tmpFakeURL);
156
+ }, 600);
157
+ return true; // we are handling this asynchronously
158
+ }
159
+ ```
160
+
161
+ Returning `true` signals to the underlying `pict-section-markdowneditor` that the caller will resolve the URL. A real app would POST to its storage endpoint here. Set `AllowImages: false` to refuse images entirely; the editor surfaces the rejection in a status pill instead of inserting a half-baked data URI.
162
+
163
+ ## The five superheroes
164
+
165
+ Each carries enough material to fill the form and demonstrate the rendering paths:
166
+
167
+ | Hero | Alter Ego | Why they're in the demo |
168
+ |---|---|---|
169
+ | Captain Verbose | Quintus Pedantic | Stress-tests the markdown viewer with a long, structured origin (headings, blockquotes, lists) |
170
+ | The Notebook | Margot Quill | Triple-quote code block + the manifestation-via-art metaphor that frames the studio itself |
171
+ | Markdown Marauder | Hex Brackets | Tables, inline code, `~~strikethrough~~`, and a `>` blockquote that recursively quotes itself |
172
+ | The Modal Avenger | Alice DialogBox | A short story carrying the Z-index humour; portrait has live Confirm/Cancel buttons inside the SVG |
173
+ | Vector Vince | Vince Bezier | Geometric primitives only — checks that strict-shape portraits don't bleed across themes |
174
+
175
+ ## Running the example
176
+
177
+ ```sh
178
+ cd modules/pict/pict-section-form/example_applications/superhero_studio
179
+ npm install
180
+ npx quack build && npx quack copy
181
+ open dist/index.html
182
+ ```
183
+
184
+ The Excalidraw vendor bundle must already be present at `pict-section-excalidraw/vendor/excalidraw-built/`. If it isn't, run `npm run build:vendor` from the module root once.
185
+
186
+ ## Takeaways
187
+
188
+ 1. **Both new InputTypes work alongside the stock catalog without ceremony.** A form descriptor sets `PictForm.InputType: 'RichText'` or `'Diagram'` and the rest is the same shape as `Text`, `Number`, etc.
189
+ 2. **View-by-default isn't just a default — it's a load-time guarantee.** Until you toggle, no editor bundle is fetched. That's the design pivot that makes these complex inputs cheap to scatter through a large form.
190
+ 3. **Theme is data, not paint.** Saving a portrait runs it through `themeifySVG()` so the stored SVG carries theme tokens with hex fallbacks. The host can rewrite tokens after the fact; the portrait re-colors live.
191
+ 4. **Record swaps need an editor-fold step.** Without it, the next record inherits a stale CodeMirror or Excalidraw instance. `_foldAllEditors()` is two lines and worth it.
192
+ 5. **The image uploader is the host's contract.** Every consuming app can plug its own storage backend in — no PR to pict-section-markdowneditor required.
193
+
194
+ ## Related documentation
195
+
196
+ - [Diagram Form](../diagram%5Fform/README.md) — the Diagram InputType in isolation.
197
+ - [RichText Form Input](../richtext%5Fform/README.md) — the RichText InputType in isolation.
198
+ - [Notebook Studio ↗](https://fable-retold.github.io/pict-section-excalidraw/#/page/examples/notebook%5Fstudio/README) — programmatic scene generation in the excalidraw module; pair with the Diagram InputType for pre-baked-then-editable diagrams.
199
+ - [pict-section-form input types](https://fable-retold.github.io/pict-section-form/#/page/Input%5FTypes) — the stock InputType catalog.
@@ -0,0 +1,25 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>Excalidraw — pict-section-excalidraw iframe host</title>
7
+ <style>
8
+ :root, html, body { margin: 0; padding: 0; width: 100%; height: 100%; }
9
+ body { background: #FFFFFF; color: #1A1A1A; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
10
+ #root { width: 100vw; height: 100vh; }
11
+ #status { position: fixed; inset: 0; display: flex; align-items: center; justify-content: center; font-size: 14px; color: #4A4A4A; background: #FFFFFF; z-index: 9999; }
12
+ #status.hidden { display: none; }
13
+ </style>
14
+ <link rel="stylesheet" href="./excalidraw-wrapper.css" />
15
+ </head>
16
+ <body>
17
+ <div id="status">Initializing Excalidraw…</div>
18
+ <div id="root"></div>
19
+ <!-- React + ReactDOM globals (loaded first) -->
20
+ <script src="./react-vendor.min.js"></script>
21
+ <!-- Excalidraw wrapper (reads React off window) -->
22
+ <script src="./excalidraw-wrapper.min.js"></script>
23
+ <script src="./excalidraw-iframe-host.js"></script>
24
+ </body>
25
+ </html>