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.
- package/docs/_cover.md +3 -0
- package/docs/_sidebar.md +5 -0
- package/docs/_version.json +3 -3
- package/docs/examples/README.md +3 -0
- package/docs/examples/change_tracking/change_tracking.js +9437 -9427
- package/docs/examples/diagram_form/README.md +116 -0
- package/docs/examples/diagram_form/diagram_form_example.min.js +42 -0
- package/docs/examples/diagram_form/excalidraw-iframe-host.html +25 -0
- package/docs/examples/diagram_form/excalidraw-wrapper.css +1 -0
- package/docs/examples/diagram_form/excalidraw-wrapper.min.js +3327 -0
- package/docs/examples/diagram_form/index.html +116 -0
- package/docs/examples/diagram_form/manifest.json +13 -0
- package/docs/examples/diagram_form/react-vendor.min.js +9 -0
- package/docs/examples/dynamic_analysis/dynamic_analysis_application.js +6549 -6550
- package/docs/examples/gradebook/gradebook_application.min.js +2 -2
- package/docs/examples/ndt_field_test/ndt_field_test.js +9437 -9427
- package/docs/examples/richtext_form/README.md +132 -0
- package/docs/examples/richtext_form/index.html +82 -0
- package/docs/examples/richtext_form/richtext_form_example.min.js +42 -0
- package/docs/examples/scope_mathematics/scope_mathematics.js +9437 -9427
- package/docs/examples/simple_table/simple_tabular_application.min.js +2 -2
- package/docs/examples/superhero_studio/README.md +199 -0
- package/docs/examples/superhero_studio/excalidraw-iframe-host.html +25 -0
- package/docs/examples/superhero_studio/excalidraw-wrapper.css +1 -0
- package/docs/examples/superhero_studio/excalidraw-wrapper.min.js +3327 -0
- package/docs/examples/superhero_studio/index.html +166 -0
- package/docs/examples/superhero_studio/manifest.json +13 -0
- package/docs/examples/superhero_studio/react-vendor.min.js +9 -0
- package/docs/examples/superhero_studio/superhero_studio_example.min.js +42 -0
- package/docs/index.html +2 -2
- package/docs/retold-catalog.json +45 -1
- package/docs/retold-keyword-index.json +11843 -7677
- package/example_applications/diagram_form/DiagramForm-Example-Application.js +145 -0
- package/example_applications/diagram_form/html/index.html +116 -0
- package/example_applications/diagram_form/package.json +29 -0
- package/example_applications/richtext_form/RichTextForm-Example-Application.js +176 -0
- package/example_applications/richtext_form/html/index.html +82 -0
- package/example_applications/richtext_form/package.json +28 -0
- package/example_applications/superhero_studio/SuperheroStudio-Example-Application.js +313 -0
- package/example_applications/superhero_studio/html/index.html +166 -0
- package/example_applications/superhero_studio/package.json +29 -0
- package/example_applications/superhero_studio/superheroes/Superheroes.js +386 -0
- package/package.json +3 -9
- package/source/providers/inputs/Pict-Provider-Input-Diagram.js +1 -31
- package/source/providers/inputs/Pict-Provider-Input-RichText.js +1 -32
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SuperheroStudio-Example-Application.js
|
|
3
|
+
*
|
|
4
|
+
* The "Superhero Studio" — a pict-section-form showcase that exercises both
|
|
5
|
+
* new complex InputTypes in one place:
|
|
6
|
+
*
|
|
7
|
+
* - RichText (origin story) — markdown editor / rendered markdown
|
|
8
|
+
* - Diagram (portrait) — Excalidraw / inline themed SVG
|
|
9
|
+
*
|
|
10
|
+
* Plus a bunch of stock inputs (Text, Number, Select, TextArea) so the form
|
|
11
|
+
* feels like a real character-sheet UI.
|
|
12
|
+
*
|
|
13
|
+
* A dropdown at the top loads one of five pre-baked superheroes — their
|
|
14
|
+
* stats, portraits, and origin stories all flow into the form via
|
|
15
|
+
* marshalDataFromAppDataToDynamicViews(). Try toggling the portrait or origin into
|
|
16
|
+
* edit mode and editing them — the rest of the form keeps working.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const libPictApplication = require('pict-application');
|
|
20
|
+
const libPictSectionForm = require('../../source/Pict-Section-Form.js');
|
|
21
|
+
|
|
22
|
+
const libSuperheroes = require('./superheroes/Superheroes.js');
|
|
23
|
+
|
|
24
|
+
// ----- Form manifest -------------------------------------------------------
|
|
25
|
+
|
|
26
|
+
const _PowerOptions =
|
|
27
|
+
[
|
|
28
|
+
{ id: 'Telepathy', text: 'Telepathy' },
|
|
29
|
+
{ id: 'Manifestation', text: 'Manifestation' },
|
|
30
|
+
{ id: 'Syntax Highlighting', text: 'Syntax Highlighting' },
|
|
31
|
+
{ id: 'Z-Index Manipulation',text: 'Z-Index Manipulation' },
|
|
32
|
+
{ id: 'Snap to Grid', text: 'Snap to Grid' },
|
|
33
|
+
{ id: 'Flight', text: 'Flight' },
|
|
34
|
+
{ id: 'Super Strength', text: 'Super Strength' },
|
|
35
|
+
{ id: 'Invisibility', text: 'Invisibility' },
|
|
36
|
+
{ id: 'Time Travel', text: 'Time Travel' },
|
|
37
|
+
{ id: 'Pyrokinesis', text: 'Pyrokinesis' }
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
const _ColorOptions =
|
|
41
|
+
[
|
|
42
|
+
{ id: 'Red', text: 'Red' },
|
|
43
|
+
{ id: 'Blue', text: 'Blue' },
|
|
44
|
+
{ id: 'Green', text: 'Green' },
|
|
45
|
+
{ id: 'Black', text: 'Black' },
|
|
46
|
+
{ id: 'Yellow', text: 'Yellow' },
|
|
47
|
+
{ id: 'Purple', text: 'Purple' }
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
const _Descriptors =
|
|
51
|
+
{
|
|
52
|
+
// ---- Identity ----
|
|
53
|
+
'Name': {
|
|
54
|
+
Name: 'Name', Hash: 'Name', DataType: 'String',
|
|
55
|
+
PictForm: { Section: 'Identity', Row: 1, Width: 6, InputType: 'Text' }
|
|
56
|
+
},
|
|
57
|
+
'AlterEgo': {
|
|
58
|
+
Name: 'Alter Ego', Hash: 'AlterEgo', DataType: 'String',
|
|
59
|
+
PictForm: { Section: 'Identity', Row: 1, Width: 6, InputType: 'Text' }
|
|
60
|
+
},
|
|
61
|
+
'ShoeSize': {
|
|
62
|
+
Name: 'Shoe Size (US)', Hash: 'ShoeSize', DataType: 'Number',
|
|
63
|
+
PictForm: { Section: 'Identity', Row: 2, Width: 3, InputType: 'Number', Min: 1, Max: 22, Step: 0.5 }
|
|
64
|
+
},
|
|
65
|
+
'OriginYear': {
|
|
66
|
+
Name: 'Year of Origin', Hash: 'OriginYear', DataType: 'Number',
|
|
67
|
+
PictForm: { Section: 'Identity', Row: 2, Width: 3, InputType: 'Number', Min: 1900, Max: 2099 }
|
|
68
|
+
},
|
|
69
|
+
'CoffeeCupsPerDay': {
|
|
70
|
+
Name: 'Coffee Cups / Day', Hash: 'CoffeeCupsPerDay', DataType: 'Number',
|
|
71
|
+
PictForm: { Section: 'Identity', Row: 2, Width: 3, InputType: 'Number', Min: 0, Max: 99 }
|
|
72
|
+
},
|
|
73
|
+
'NumberOfSidekicks': {
|
|
74
|
+
Name: 'Active Sidekicks', Hash: 'NumberOfSidekicks', DataType: 'Number',
|
|
75
|
+
PictForm: { Section: 'Identity', Row: 2, Width: 3, InputType: 'Number', Min: 0, Max: 99 }
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
// ---- Powers ----
|
|
79
|
+
'PowerLevel': {
|
|
80
|
+
Name: 'Power Level (1–10)', Hash: 'PowerLevel', DataType: 'Number',
|
|
81
|
+
PictForm: { Section: 'Powers', Row: 1, Width: 3, InputType: 'Number', Min: 1, Max: 10 }
|
|
82
|
+
},
|
|
83
|
+
'PrimaryPower': {
|
|
84
|
+
Name: 'Primary Power', Hash: 'PrimaryPower', DataType: 'String',
|
|
85
|
+
PictForm: { Section: 'Powers', Row: 1, Width: 5, InputType: 'Option', SelectOptions: _PowerOptions }
|
|
86
|
+
},
|
|
87
|
+
'ThemeColor': {
|
|
88
|
+
Name: 'Theme Color', Hash: 'ThemeColor', DataType: 'String',
|
|
89
|
+
PictForm: { Section: 'Powers', Row: 1, Width: 4, InputType: 'Option', SelectOptions: _ColorOptions }
|
|
90
|
+
},
|
|
91
|
+
'Weakness': {
|
|
92
|
+
Name: 'Known Weakness', Hash: 'Weakness', DataType: 'String',
|
|
93
|
+
PictForm: { Section: 'Powers', Row: 2, Width: 6, InputType: 'Text' }
|
|
94
|
+
},
|
|
95
|
+
'FavoriteSnack': {
|
|
96
|
+
Name: 'Favorite Snack', Hash: 'FavoriteSnack', DataType: 'String',
|
|
97
|
+
PictForm: { Section: 'Powers', Row: 2, Width: 6, InputType: 'Text' }
|
|
98
|
+
},
|
|
99
|
+
'Catchphrase': {
|
|
100
|
+
Name: 'Signature Catchphrase', Hash: 'Catchphrase', DataType: 'String',
|
|
101
|
+
PictForm: { Section: 'Powers', Row: 3, Width: 12, InputType: 'TextArea', TextAreaRows: 2 }
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
// ---- Portrait (Diagram InputType) ----
|
|
105
|
+
'Portrait': {
|
|
106
|
+
Name: 'Portrait', Hash: 'Portrait', DataType: 'String',
|
|
107
|
+
PictForm:
|
|
108
|
+
{
|
|
109
|
+
Section: 'Portrait', Row: 1, Width: 12,
|
|
110
|
+
InputType: 'Diagram',
|
|
111
|
+
Diagram:
|
|
112
|
+
{
|
|
113
|
+
ThemeColors: true,
|
|
114
|
+
Height: '320px',
|
|
115
|
+
EditorImplementation: 'react'
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
// ---- Origin Story (RichText InputType) ----
|
|
121
|
+
'OriginStory': {
|
|
122
|
+
Name: 'Origin Story', Hash: 'OriginStory', DataType: 'String',
|
|
123
|
+
PictForm:
|
|
124
|
+
{
|
|
125
|
+
Section: 'OriginStory', Row: 1, Width: 12,
|
|
126
|
+
InputType: 'RichText',
|
|
127
|
+
RichText:
|
|
128
|
+
{
|
|
129
|
+
AllowImages: true,
|
|
130
|
+
ImageUploader: 'uploadImage',
|
|
131
|
+
Height: '380px'
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const _FormManifest =
|
|
138
|
+
{
|
|
139
|
+
Scope: 'SuperheroForm',
|
|
140
|
+
Sections:
|
|
141
|
+
[
|
|
142
|
+
{ Hash: 'Identity', Name: 'Identity' },
|
|
143
|
+
{ Hash: 'Powers', Name: 'Powers & Loadout' },
|
|
144
|
+
{ Hash: 'Portrait', Name: 'Portrait' },
|
|
145
|
+
{ Hash: 'OriginStory', Name: 'Origin Story' }
|
|
146
|
+
],
|
|
147
|
+
Descriptors: _Descriptors
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
// ----- Application ---------------------------------------------------------
|
|
151
|
+
|
|
152
|
+
class SuperheroStudioApplication extends libPictSectionForm.PictFormApplication
|
|
153
|
+
{
|
|
154
|
+
constructor(pFable, pOptions, pServiceHash)
|
|
155
|
+
{
|
|
156
|
+
super(pFable, pOptions, pServiceHash);
|
|
157
|
+
|
|
158
|
+
// Register the RichText input provider (from pict-section-markdowneditor).
|
|
159
|
+
// Both complex InputType providers live in pict-section-form. The heavy
|
|
160
|
+
// editor deps (markdowneditor → CodeMirror, excalidraw → vendor bundle)
|
|
161
|
+
// lazy-load on the first setMode('edit'), so this view-by-default form
|
|
162
|
+
// pays for neither editor at boot.
|
|
163
|
+
let libRichTextInput = libPictSectionForm.RichTextInput;
|
|
164
|
+
this.pict.addProvider(
|
|
165
|
+
libRichTextInput.default_configuration.ProviderIdentifier,
|
|
166
|
+
libRichTextInput.default_configuration,
|
|
167
|
+
libRichTextInput);
|
|
168
|
+
|
|
169
|
+
let libDiagramInput = libPictSectionForm.DiagramInput;
|
|
170
|
+
this.pict.addProvider(
|
|
171
|
+
libDiagramInput.default_configuration.ProviderIdentifier,
|
|
172
|
+
libDiagramInput.default_configuration,
|
|
173
|
+
libDiagramInput);
|
|
174
|
+
|
|
175
|
+
// Seed with the first hero so the form has data on load.
|
|
176
|
+
this._currentHeroSlug = 'captain-verbose';
|
|
177
|
+
this.pict.AppData.SuperheroForm =
|
|
178
|
+
JSON.parse(JSON.stringify(libSuperheroes[this._currentHeroSlug]));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
onAfterInitializeAsync(fCallback)
|
|
182
|
+
{
|
|
183
|
+
// Point the form metacontroller at our AppData branch BEFORE super so
|
|
184
|
+
// the initial render reads from the right place.
|
|
185
|
+
if (this.pict.views.PictFormMetacontroller)
|
|
186
|
+
{
|
|
187
|
+
this.pict.views.PictFormMetacontroller.viewMarshalDestination = 'AppData.SuperheroForm';
|
|
188
|
+
}
|
|
189
|
+
super.onAfterInitializeAsync(() =>
|
|
190
|
+
{
|
|
191
|
+
try { this.marshalDataFromAppDataToDynamicViews(); }
|
|
192
|
+
catch (pErr) { if (this.log) this.log.warn('[superhero_studio] initial marshal failed', { error: pErr.message }); }
|
|
193
|
+
return fCallback();
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// -----------------------------------------------------------------
|
|
198
|
+
// Dropdown / page handlers (called from index.html inline onclick)
|
|
199
|
+
// -----------------------------------------------------------------
|
|
200
|
+
|
|
201
|
+
demo_loadHero(pSlug)
|
|
202
|
+
{
|
|
203
|
+
let tmpHero = libSuperheroes[pSlug];
|
|
204
|
+
if (!tmpHero) return;
|
|
205
|
+
this._currentHeroSlug = pSlug;
|
|
206
|
+
|
|
207
|
+
// Tear down any currently-open editor instances so the swap doesn't
|
|
208
|
+
// leave a stale CodeMirror/Excalidraw painting yesterday's hero.
|
|
209
|
+
this._foldAllEditors();
|
|
210
|
+
|
|
211
|
+
// Replace the form's source-of-truth record.
|
|
212
|
+
this.pict.AppData.SuperheroForm = JSON.parse(JSON.stringify(tmpHero));
|
|
213
|
+
|
|
214
|
+
// Push the new record into the rendered form.
|
|
215
|
+
try { this.marshalDataFromAppDataToDynamicViews(); }
|
|
216
|
+
catch (pErr) { if (this.log) this.log.warn('[superhero_studio] marshal failed', { error: pErr.message }); }
|
|
217
|
+
|
|
218
|
+
this._refreshToggleLabels();
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
demo_toggleMode(pInputHash)
|
|
222
|
+
{
|
|
223
|
+
let tmpProvider = this._providerForInput(pInputHash);
|
|
224
|
+
if (!tmpProvider) return;
|
|
225
|
+
tmpProvider.toggleMode(pInputHash, (pErr) =>
|
|
226
|
+
{
|
|
227
|
+
if (pErr && this.log) this.log.warn('[superhero_studio] toggleMode error',
|
|
228
|
+
{ error: pErr.message, inputHash: pInputHash });
|
|
229
|
+
this._refreshToggleLabels();
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Demo image uploader for the RichText origin-story field. In a real app
|
|
235
|
+
* this would POST to your image-storage endpoint and call back with the
|
|
236
|
+
* permanent URL. Here we mint a fake CDN URL after a brief delay.
|
|
237
|
+
*/
|
|
238
|
+
uploadImage(pFile, pInputDescriptor, fCallback)
|
|
239
|
+
{
|
|
240
|
+
setTimeout(() =>
|
|
241
|
+
{
|
|
242
|
+
let tmpFakeURL = '/uploads/heroes/' + Date.now() + '-' +
|
|
243
|
+
(pFile.name || 'image.png').replace(/[^A-Za-z0-9._-]/g, '_');
|
|
244
|
+
fCallback(null, tmpFakeURL);
|
|
245
|
+
if (this.log) this.log.info('[superhero_studio] uploadImage resolved', { url: tmpFakeURL });
|
|
246
|
+
}, 600);
|
|
247
|
+
return true;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// -----------------------------------------------------------------
|
|
251
|
+
// Internals
|
|
252
|
+
// -----------------------------------------------------------------
|
|
253
|
+
|
|
254
|
+
_providerForInput(pInputHash)
|
|
255
|
+
{
|
|
256
|
+
let tmpDescriptor = _Descriptors[pInputHash];
|
|
257
|
+
if (!tmpDescriptor) return null;
|
|
258
|
+
let tmpType = tmpDescriptor.PictForm && tmpDescriptor.PictForm.InputType;
|
|
259
|
+
if (tmpType === 'RichText') return this.pict.providers['Pict-Input-RichText'];
|
|
260
|
+
if (tmpType === 'Diagram') return this.pict.providers['Pict-Input-Diagram'];
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
_foldAllEditors()
|
|
265
|
+
{
|
|
266
|
+
let tmpHashes = ['Portrait', 'OriginStory'];
|
|
267
|
+
for (let i = 0; i < tmpHashes.length; i++)
|
|
268
|
+
{
|
|
269
|
+
let tmpProvider = this._providerForInput(tmpHashes[i]);
|
|
270
|
+
if (tmpProvider && tmpProvider.getMode(tmpHashes[i]) === 'edit')
|
|
271
|
+
{
|
|
272
|
+
try { tmpProvider.setMode(tmpHashes[i], 'view', () => {}); }
|
|
273
|
+
catch (pErr) { /* swallow — happens if not yet mounted */ }
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
_refreshToggleLabels()
|
|
279
|
+
{
|
|
280
|
+
if (typeof document === 'undefined') return;
|
|
281
|
+
let tmpHashes = ['Portrait', 'OriginStory'];
|
|
282
|
+
for (let i = 0; i < tmpHashes.length; i++)
|
|
283
|
+
{
|
|
284
|
+
let tmpHash = tmpHashes[i];
|
|
285
|
+
let tmpProvider = this._providerForInput(tmpHash);
|
|
286
|
+
let tmpBtn = document.getElementById('toggle-' + tmpHash);
|
|
287
|
+
if (!tmpBtn) continue;
|
|
288
|
+
let tmpMode = tmpProvider ? tmpProvider.getMode(tmpHash) : null;
|
|
289
|
+
tmpBtn.textContent = (tmpMode === 'edit') ? 'Done' : 'Edit';
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
module.exports = SuperheroStudioApplication;
|
|
295
|
+
|
|
296
|
+
// Extend the parent's default_configuration so MainViewportViewIdentifier
|
|
297
|
+
// (= "PictFormMetacontroller") survives — without it the form has no
|
|
298
|
+
// auto-render target and the page comes up blank.
|
|
299
|
+
module.exports.default_configuration = Object.assign({},
|
|
300
|
+
libPictSectionForm.PictFormApplication.default_configuration,
|
|
301
|
+
{
|
|
302
|
+
Name: 'Superhero Studio',
|
|
303
|
+
Hash: 'SuperheroStudio',
|
|
304
|
+
pict_configuration:
|
|
305
|
+
{
|
|
306
|
+
Product: 'SuperheroStudio-Example',
|
|
307
|
+
DefaultFormManifest: _FormManifest
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
// Expose the slug list so the HTML dropdown can render its options dynamically.
|
|
312
|
+
module.exports.HeroSlugs = Object.keys(libSuperheroes);
|
|
313
|
+
module.exports.Heroes = libSuperheroes;
|
|
@@ -0,0 +1,166 @@
|
|
|
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>Superhero Studio — Pict Example</title>
|
|
7
|
+
|
|
8
|
+
<style id="PICT-CSS"></style>
|
|
9
|
+
<style id="diagram-theme">
|
|
10
|
+
:root
|
|
11
|
+
{
|
|
12
|
+
--diagram-ink: #1B1F23;
|
|
13
|
+
--diagram-paper: #FDFCF7;
|
|
14
|
+
--diagram-accent: #E66C2C;
|
|
15
|
+
--diagram-highlight: #FFD966;
|
|
16
|
+
--diagram-deemphasis: #A0A0A0;
|
|
17
|
+
--diagram-link: #2E7D74;
|
|
18
|
+
}
|
|
19
|
+
</style>
|
|
20
|
+
<style>
|
|
21
|
+
*, *::before, *::after { box-sizing: border-box; }
|
|
22
|
+
body { margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; background: #F4F1ED; color: #2A2520; }
|
|
23
|
+
|
|
24
|
+
.pict-example-header { display: flex; align-items: stretch; background: #264653; border-bottom: 3px solid #E76F51; }
|
|
25
|
+
.pict-example-badge { background: #E76F51; color: #FAEDCD; padding: 0.6rem 1rem; font-size: 0.72rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.1em; }
|
|
26
|
+
.pict-example-app-name { padding: 0.6rem 1rem; color: #FAEDCD; font-size: 1.1rem; font-weight: 600; }
|
|
27
|
+
.pict-example-module { margin-left: auto; padding: 0.6rem 1rem; color: #D4A373; font-size: 0.75rem; letter-spacing: 0.03em; align-self: center; }
|
|
28
|
+
|
|
29
|
+
.page-content { padding: 1.6rem 2rem 4rem; max-width: 1080px; margin: 0 auto; }
|
|
30
|
+
|
|
31
|
+
.hero-bar
|
|
32
|
+
{
|
|
33
|
+
background: #FFFFFF; border: 1px solid #D4C4A8; border-radius: 8px;
|
|
34
|
+
padding: 16px 20px; margin-bottom: 22px;
|
|
35
|
+
display: flex; flex-wrap: wrap; gap: 14px; align-items: center;
|
|
36
|
+
box-shadow: 0 1px 3px rgba(0,0,0,0.04);
|
|
37
|
+
}
|
|
38
|
+
.hero-bar label { font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.08em; color: #5A4A3C; font-weight: 600; }
|
|
39
|
+
.hero-bar select
|
|
40
|
+
{
|
|
41
|
+
padding: 6px 12px; border: 1px solid #D4C4A8; border-radius: 4px;
|
|
42
|
+
background: #FBF7EE; font-size: 0.95rem; min-width: 220px;
|
|
43
|
+
}
|
|
44
|
+
.hero-bar .theme-group { margin-left: auto; display: flex; gap: 8px; align-items: center; }
|
|
45
|
+
.hero-bar .swatch
|
|
46
|
+
{
|
|
47
|
+
width: 22px; height: 22px; border-radius: 4px; cursor: pointer;
|
|
48
|
+
border: 2px solid rgba(0,0,0,0.1); transition: transform 0.08s ease;
|
|
49
|
+
}
|
|
50
|
+
.hero-bar .swatch:hover { transform: scale(1.1); border-color: #2E7D74; }
|
|
51
|
+
|
|
52
|
+
.field-action-bar
|
|
53
|
+
{
|
|
54
|
+
display: flex; align-items: center; gap: 10px;
|
|
55
|
+
margin-top: -8px; margin-bottom: 12px; padding: 0 4px;
|
|
56
|
+
}
|
|
57
|
+
.field-action-bar button
|
|
58
|
+
{
|
|
59
|
+
padding: 4px 12px; font-size: 0.78rem; font-weight: 600;
|
|
60
|
+
color: #FAEDCD; background: #264653; border: none; border-radius: 4px;
|
|
61
|
+
cursor: pointer;
|
|
62
|
+
}
|
|
63
|
+
.field-action-bar button:hover { background: #E76F51; }
|
|
64
|
+
.field-action-bar .hint { font-size: 0.72rem; color: #8A7F72; }
|
|
65
|
+
|
|
66
|
+
#Pict-Form-Container
|
|
67
|
+
{
|
|
68
|
+
background: #FFFFFF; border: 1px solid #D4C4A8; border-radius: 8px;
|
|
69
|
+
padding: 24px 26px; box-shadow: 0 1px 3px rgba(0,0,0,0.04);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.notes
|
|
73
|
+
{
|
|
74
|
+
margin-top: 24px; padding: 14px 18px;
|
|
75
|
+
background: #FFF8E7; border-left: 3px solid #E9C46A; border-radius: 4px;
|
|
76
|
+
font-size: 0.85rem; color: #5A4A3C;
|
|
77
|
+
}
|
|
78
|
+
.notes strong { color: #2A2520; }
|
|
79
|
+
.notes code { font-family: 'SFMono-Regular', Menlo, monospace; background: #F0E8DA; padding: 1px 5px; border-radius: 3px; font-size: 0.9em; }
|
|
80
|
+
</style>
|
|
81
|
+
</head>
|
|
82
|
+
<body>
|
|
83
|
+
<header class="pict-example-header">
|
|
84
|
+
<span class="pict-example-badge">Pict Example</span>
|
|
85
|
+
<span class="pict-example-app-name">Superhero Studio</span>
|
|
86
|
+
<span class="pict-example-module">pict-section-excalidraw × pict-section-markdowneditor</span>
|
|
87
|
+
</header>
|
|
88
|
+
|
|
89
|
+
<main class="page-content">
|
|
90
|
+
<div class="hero-bar">
|
|
91
|
+
<label for="hero-select">Load Superhero</label>
|
|
92
|
+
<select id="hero-select" onchange="_Pict.PictApplication.demo_loadHero(this.value)">
|
|
93
|
+
<option value="captain-verbose">Captain Verbose</option>
|
|
94
|
+
<option value="the-notebook">The Notebook</option>
|
|
95
|
+
<option value="markdown-marauder">Markdown Marauder</option>
|
|
96
|
+
<option value="the-modal-avenger">The Modal Avenger</option>
|
|
97
|
+
<option value="vector-vince">Vector Vince</option>
|
|
98
|
+
</select>
|
|
99
|
+
|
|
100
|
+
<div class="theme-group">
|
|
101
|
+
<span style="font-size:0.78rem; text-transform:uppercase; letter-spacing:0.08em; color:#5A4A3C; font-weight:600;">Portrait theme</span>
|
|
102
|
+
<span class="swatch" title="Notebook" style="background:#FDFCF7; border-color:#1B1F23;" onclick="window.applyTheme('notebook')"></span>
|
|
103
|
+
<span class="swatch" title="Dark" style="background:#1B1F23;" onclick="window.applyTheme('dark')"></span>
|
|
104
|
+
<span class="swatch" title="Blueprint" style="background:#5DA9E9;" onclick="window.applyTheme('blueprint')"></span>
|
|
105
|
+
<span class="swatch" title="Sepia" style="background:#B08968;" onclick="window.applyTheme('sepia')"></span>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
|
|
109
|
+
<div class="field-action-bar">
|
|
110
|
+
<button id="toggle-Portrait" onclick="_Pict.PictApplication.demo_toggleMode('Portrait')">Edit</button>
|
|
111
|
+
<span class="hint">Toggle the Portrait field into Excalidraw — first click lazy-loads the editor bundle.</span>
|
|
112
|
+
|
|
113
|
+
<button id="toggle-OriginStory" onclick="_Pict.PictApplication.demo_toggleMode('OriginStory')" style="margin-left:18px;">Edit</button>
|
|
114
|
+
<span class="hint">Toggle the Origin Story field into the markdown editor.</span>
|
|
115
|
+
</div>
|
|
116
|
+
|
|
117
|
+
<div id="Pict-Form-Container"></div>
|
|
118
|
+
|
|
119
|
+
<div class="notes">
|
|
120
|
+
<strong>What's happening:</strong>
|
|
121
|
+
Stock inputs (<code>Text</code>, <code>Number</code>, <code>Option</code>, <code>TextArea</code>) sit next to two complex InputTypes —
|
|
122
|
+
<code>Diagram</code> (Portrait) and <code>RichText</code> (Origin Story) — in the same form, all driven by the same
|
|
123
|
+
<code>marshalDataFromAppDataToView</code> pipeline. The portrait SVGs use <code>var(--diagram-ink, …)</code> with hex fallbacks,
|
|
124
|
+
so swapping the theme swatches recolors them live without re-rendering. Loading a new hero from the dropdown folds any open editors back
|
|
125
|
+
into view mode first, so the swap doesn't strand CodeMirror or Excalidraw on yesterday's character.
|
|
126
|
+
</div>
|
|
127
|
+
</main>
|
|
128
|
+
|
|
129
|
+
<link rel="stylesheet" href="./excalidraw-wrapper.css">
|
|
130
|
+
<script src="./pict.min.js"></script>
|
|
131
|
+
<!-- Excalidraw vendor bundle (React + Excalidraw). The Diagram InputType
|
|
132
|
+
looks for window.PictSectionExcalidrawVendor when the Portrait field
|
|
133
|
+
flips to edit; without these tags Excalidraw won't mount. -->
|
|
134
|
+
<script src="./react-vendor.min.js"></script>
|
|
135
|
+
<script src="./excalidraw-wrapper.min.js"></script>
|
|
136
|
+
<script src="./superhero_studio_example.min.js"></script>
|
|
137
|
+
<script>
|
|
138
|
+
const _Themes =
|
|
139
|
+
{
|
|
140
|
+
notebook: { ink:'#1B1F23', paper:'#FDFCF7', accent:'#E66C2C', highlight:'#FFD966', deemphasis:'#A0A0A0', link:'#2E7D74' },
|
|
141
|
+
dark: { ink:'#EAEAEA', paper:'#1B1F23', accent:'#FFB570', highlight:'#FFD966', deemphasis:'#666666', link:'#7FC4BD' },
|
|
142
|
+
blueprint: { ink:'#08296B', paper:'#E5F0FB', accent:'#5DA9E9', highlight:'#F4D35E', deemphasis:'#8AA3B6', link:'#1F77B4' },
|
|
143
|
+
sepia: { ink:'#3D2B1F', paper:'#F1E3C9', accent:'#B07043', highlight:'#E2C58B', deemphasis:'#A48A6E', link:'#7A5A36' }
|
|
144
|
+
};
|
|
145
|
+
window.applyTheme = function(pName)
|
|
146
|
+
{
|
|
147
|
+
let tmpStyle = document.getElementById('diagram-theme');
|
|
148
|
+
let tmpT = _Themes[pName] || _Themes.notebook;
|
|
149
|
+
tmpStyle.textContent =
|
|
150
|
+
':root { ' +
|
|
151
|
+
'--diagram-ink:' + tmpT.ink + ';' +
|
|
152
|
+
'--diagram-paper:' + tmpT.paper + ';' +
|
|
153
|
+
'--diagram-accent:' + tmpT.accent + ';' +
|
|
154
|
+
'--diagram-highlight:' + tmpT.highlight + ';' +
|
|
155
|
+
'--diagram-deemphasis:' + tmpT.deemphasis + ';' +
|
|
156
|
+
'--diagram-link:' + tmpT.link + ';' +
|
|
157
|
+
' }';
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
Pict.safeOnDocumentReady(() =>
|
|
161
|
+
{
|
|
162
|
+
Pict.safeLoadPictApplication(window.SuperheroStudioExample, 1);
|
|
163
|
+
});
|
|
164
|
+
</script>
|
|
165
|
+
</body>
|
|
166
|
+
</html>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "superhero_studio_example",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Pict-section-form showcase that exercises both new complex InputTypes (RichText for the origin story, Diagram for the portrait) inside a superhero character-sheet form.",
|
|
5
|
+
"main": "SuperheroStudio-Example-Application.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"start": "node SuperheroStudio-Example-Application.js",
|
|
8
|
+
"build": "npx quack build && npx quack copy"
|
|
9
|
+
},
|
|
10
|
+
"author": "steven velozo <steven@velozo.com>",
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"devDependencies": {},
|
|
13
|
+
"copyFilesSettings": {
|
|
14
|
+
"whenFileExists": "overwrite"
|
|
15
|
+
},
|
|
16
|
+
"copyFiles": [
|
|
17
|
+
{ "from": "./html/*", "to": "./dist/" },
|
|
18
|
+
{ "from": "../../node_modules/pict/dist/*", "to": "./dist/" },
|
|
19
|
+
{ "from": "../../node_modules/pict-section-excalidraw/vendor/excalidraw-built/*", "to": "./dist/" }
|
|
20
|
+
],
|
|
21
|
+
"retold": {
|
|
22
|
+
"ExampleApplication": {
|
|
23
|
+
"Stage": true,
|
|
24
|
+
"Title": "Superhero Studio",
|
|
25
|
+
"Summary": "A character-sheet form that combines both new complex InputTypes — RichText for the origin story (markdown editor + rendered view) and Diagram for the portrait (Excalidraw + inline themed SVG) — alongside stock inputs (Text, Number, Option, TextArea). A dropdown at the top loads five hand-drawn superheroes; each one's portrait recolors when you swap themes. Boots in view-mode, edit on demand.",
|
|
26
|
+
"Complexity": "Advanced"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|