@gramlot/gramlot-examples 0.2.14 → 0.2.15

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
@@ -12,7 +12,7 @@ npm install @gramlot/gramlot-examples
12
12
  [![PyPI](https://img.shields.io/pypi/v/gramlot-examples)](https://pypi.org/project/gramlot-examples/)
13
13
  [![npm](https://img.shields.io/npm/v/@gramlot/gramlot-examples)](https://www.npmjs.com/package/@gramlot/gramlot-examples)
14
14
 
15
- Current release: 0.2.14.
15
+ Current release: 0.2.15.
16
16
 
17
17
  [Documentation and learning sequence](https://gramlot-examples.readthedocs.io/en/latest/)
18
18
  include every lesson, its Python and JavaScript source, and the gallery guide.
@@ -32,10 +32,11 @@ same-name README:
32
32
 
33
33
  - `html_svg/`: thirteen HTML and SVG pages, without binding (`e01`–`e13`);
34
34
  - `binding/`: eleven pages whose DOM follows the Data (`b01`–`b11`);
35
- - `controllers/`: nineteen pages with formulas, controllers, named logic, events, the
35
+ - `controllers/`: twenty-one pages with formulas, controllers, named logic, events, the
36
36
  end-to-end story, `dataRpc` calls to the endpoints of the page, `remote` fragments and
37
- `dataRemote` values (`c01`–`c19`). The last two introduce: topics,
38
- authored references, native ask and parent/child messages. They require core 0.2.18 or later.
37
+ `dataRemote` values (`c01`–`c21`). Later lessons introduce topics, authored references, native ask,
38
+ parent/child messages, plain HTML buttons and delayed Gramlot clicks. The package
39
+ requires core 0.2.19 or later; each lesson states which features it uses.
39
40
 
40
41
  Each `NN_name.js` exports `Page` and `Logic`; the Python page `NN_name.py` takes its
41
42
  `Logic` from the module beside it.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gramlot/gramlot-examples",
3
- "version": "0.2.14",
3
+ "version": "0.2.15",
4
4
  "description": "Gramlot example pages, the gallery page and its catalogue.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -14,7 +14,7 @@
14
14
  "prepack": "node scripts/build-browser.mjs"
15
15
  },
16
16
  "dependencies": {
17
- "@gramlot/gramlot": ">=0.2.18"
17
+ "@gramlot/gramlot": ">=0.2.19"
18
18
  },
19
19
  "devDependencies": {
20
20
  "@genrojs/bag": ">=0.11.0",
@@ -304,13 +304,25 @@
304
304
  "key": "c18",
305
305
  "title": "Publish and subscribe",
306
306
  "folder": "18_publish_subscribe",
307
- "description": "Development core 0.2.18: revisit button actions with page topics, self receivers, references and native ask."
307
+ "description": "Core 0.2.18: revisit button actions with page topics, self receivers, references and native ask."
308
308
  },
309
309
  {
310
310
  "key": "c19",
311
311
  "title": "Parent and child messages",
312
312
  "folder": "19_frame_messages",
313
- "description": "Development core 0.2.18: local-first publish and direct parent/child postMessage exchange."
313
+ "description": "Core 0.2.18: local-first publish and direct parent/child postMessage exchange."
314
+ },
315
+ {
316
+ "key": "c20",
317
+ "title": "Actions, confirmations and references",
318
+ "folder": "20_actions_ask_references",
319
+ "description": "Core 0.2.18: action, conditional ask and explicit Source/DOM references."
320
+ },
321
+ {
322
+ "key": "c21",
323
+ "title": "Plain and delayed buttons",
324
+ "folder": "21_plain_and_delayed_buttons",
325
+ "description": "Core 0.2.19: native html_button and grouped clicks with _delay."
314
326
  }
315
327
  ]
316
328
  }
@@ -0,0 +1,40 @@
1
+ import {Page as BasePage} from '@gramlot/gramlot/page';
2
+
3
+ export class Page extends BasePage {
4
+ static title = 'Actions, confirmations and references';
5
+ static css = ['/themes/gramlot-base/theme.css'];
6
+ main(root) {
7
+ const pane = root.main({class: 'example-page stack', datapath: 'buttons'});
8
+ pane.h1('One action, with or without a question');
9
+ pane.dataSetter({destination_path: '.name', value: 'Ada'});
10
+ pane.dataSetter({destination_path: '.confirm', value: true});
11
+ pane.dataSetter({destination_path: '.result', value: 'No action yet'});
12
+ pane.html_label('Ask before running', {for: 'confirm-needed'});
13
+ pane.input({id: 'confirm-needed', type: 'checkbox', value: '^.confirm'});
14
+ pane.button('Run action', {id: 'action', name: '=.name', action: "this.SET('.result', 'Hello ' + name)"});
15
+ pane.button('Ask for a name', {id: 'action-ask', name: '=.name', action: "this.SET('.result', 'Hello ' + name)",
16
+ ask: {title: 'Greeting', fields: [{name: 'name', lbl: 'Name', required: true}]}});
17
+ pane.button('Conditional action', {id: 'conditional-action', name: '=.name', confirm_needed: '=.confirm',
18
+ action: "this.SET('.result', 'Hello ' + name)", ask: 'Send this greeting?', _ask_if: 'confirm_needed'});
19
+ pane.button('Conditional controller', {id: 'conditional-controller'}).dataController({func: 'greet', name: '=.name',
20
+ confirm_needed: '=.confirm', _ask: 'Send this greeting?', _ask_if: 'confirm_needed'});
21
+ pane.button('Conditional fields', {id: 'conditional-fields', name: '=.name', confirm_needed: '=.confirm',
22
+ action: "this.SET('.result', 'Hello ' + name)",
23
+ ask: {title: 'Greeting', _if: 'confirm_needed', fields: [{name: 'name', lbl: 'Name'}]}});
24
+ pane.button('Legacy askIf', {id: 'legacy-askif', name: '=.name', action: "this.SET('.result', 'Hello ' + name)",
25
+ ask: {msg: 'askIf=false alone still asks in legacy semantics.', askIf: 'false'}});
26
+ pane.p('^.result', {id: 'result'});
27
+ pane.h2('Pass the element, without looking up an ID');
28
+ const target = pane.p('Original text', {id: 'reference-target'});
29
+ pane.button('Use Source reference', {id: 'source-reference'}).dataController({func: 'changeSource', target});
30
+ pane.button('Read DOM reference', {id: 'dom-reference'}).dataController({func: 'describeDom',
31
+ target: target.builder.reference(target, 'dom')});
32
+ pane.p('^.reference', {id: 'reference-result'});
33
+ }
34
+ }
35
+
36
+ export class Logic {
37
+ greet(node, {name}) { node.SET('.result', `Hello ${name}`); }
38
+ changeSource(node, {target}) { target.setValue('Changed through Source'); }
39
+ describeDom(node, {target}) { node.SET('.reference', `DOM element: ${target.tagName}`); }
40
+ }
@@ -0,0 +1,20 @@
1
+ # 20 · Actions, confirmations and references
2
+
3
+ The same greeting runs immediately, after a question, or after collecting a name. Switch **Ask before running** off and on to see when the question appears. Then pass a paragraph to a controller as a Source reference or as a DOM reference.
4
+
5
+ **Uses:** Source, Data, buttons, inline actions, named controllers, native ask and local references — core 0.2.18. No endpoints or cross-frame references. This revisits the earlier button lesson with the 0.2.18 additions.
6
+
7
+ **Try it:** choose **Ask for a name**, enter Grace and confirm. The result becomes Hello Grace; Cancel keeps the old result. Turn the checkbox off: both conditional buttons run immediately. Turn it on: they wait for confirmation. **Conditional fields** applies the same choice to a dialog with a field.
8
+
9
+ <details>
10
+ <summary>More detail</summary>
11
+
12
+ `action` is a small JavaScript expression on the button. `ask` belongs to that action; `_ask` belongs to its nested controller. The string forms use `_ask_if`. The dictionary form uses `_if` to skip the dialog when false. These inline conditions/actions require the gallery's permissive CSP; named controller methods remain the usual choice for reusable behavior.
13
+
14
+ Legacy `askIf` is not the skip condition: `askIf="false"` alone still asks. The dedicated button demonstrates this difference. Modifier-based `askOn`/`skipOn` are not supported. Use `_ask_if`, or dictionary `_if`, when a false condition must run without asking.
15
+
16
+ Python passes `target=target` to obtain the live Source node. Its controller changes the paragraph through Source. `target.builder.reference(target, kind="dom")` explicitly requests the rendered element; the second controller reads its tag name. JavaScript spells this `reference(target, 'dom')`. Neither requires an application ID lookup. These references remain local to the page and cannot be sent as RPC or interframe payloads.
17
+
18
+ **Exercise:** change the initial name to Linus and turn the checkbox off. **Result:** the conditional controls use Linus without opening a dialog; no field collection has taken place.
19
+
20
+ </details>
@@ -0,0 +1,36 @@
1
+ import {Page as BasePage} from '@gramlot/gramlot/page';
2
+
3
+ export class Page extends BasePage {
4
+ static title = 'Plain and delayed buttons';
5
+ static css = ['/themes/gramlot-base/theme.css'];
6
+ main(root) {
7
+ const pane = root.main({class: 'example-page stack', datapath: 'clicks'});
8
+ pane.h1('Native HTML, then Gramlot behavior');
9
+ const form = pane.form({class: 'card stack'});
10
+ form.html_label('Native value', {for: 'native-value'});
11
+ form.input({id: 'native-value', value: 'Original'});
12
+ form.html_button('Native reset', {type: 'reset', id: 'native-reset'});
13
+ pane.p('Edit the field and Reset: the browser restores its initial value, without a Data controller.');
14
+ pane.dataSetter({destination_path: '.count', value: 0});
15
+ pane.dataSetter({destination_path: '.runs', value: 0});
16
+ pane.button('Immediate action', {id: 'immediate',
17
+ action: "this.SET('.count', _counter); this.SET('.runs', this.GET('.runs') + 1)"});
18
+ pane.button('Delayed action', {id: 'delayed', _delay: 600,
19
+ action: "this.SET('.count', _counter); this.SET('.runs', this.GET('.runs') + 1)"});
20
+ pane.button('Delayed controller', {id: 'delayed-controller', _delay: 600}).dataController({func: 'countBurst'});
21
+ pane.button('Delayed confirmation', {id: 'delayed-ask', _delay: 600,
22
+ action: "this.SET('.count', _counter); this.SET('.runs', this.GET('.runs') + 1)", ask: 'Run this group of clicks?'});
23
+ pane.p('Click a delayed button several times quickly. Wait: one execution reports the number of clicks in that group.');
24
+ pane.html_label('Clicks', {for: 'count'});
25
+ pane.output('^.count', {id: 'count'});
26
+ pane.html_label('Executions', {for: 'runs'});
27
+ pane.output('^.runs', {id: 'runs'});
28
+ }
29
+ }
30
+
31
+ export class Logic {
32
+ countBurst(node, {button_counter}) {
33
+ node.SET('.count', button_counter);
34
+ node.SET('.runs', node.GET('.runs') + 1);
35
+ }
36
+ }
@@ -0,0 +1,20 @@
1
+ # 21 · Plain and delayed buttons
2
+
3
+ Reset an ordinary HTML form, then compare the same Gramlot action with and without a delay. Several quick clicks become one execution, with the size of the group displayed below.
4
+
5
+ **Uses:** Source, Data and button actions/controllers; core 0.2.19 additions: `html_button` and button `_delay`. No Web Components or endpoint calls. Earlier lessons keep their original release scope.
6
+
7
+ **Try it:** edit Native value and press Native reset. Click Immediate action twice: Executions increases twice. Click Delayed action three times quickly: after 600 ms from the last click, Executions increases once and Clicks shows 3. A new group starts again from 1. Delayed controller does the same with a named method. Delayed confirmation opens one question after the group; Cancel does not run the action.
8
+
9
+ <details>
10
+ <summary>More detail</summary>
11
+
12
+ `html_button` renders a native `<button>` with no implicit type or Gramlot click mechanism. Here `type="reset"` lets the browser reset an unbound field. Use `type="button"` for a plain button that must not submit a form; explicit events use `connect_onclick`. `action`, `ask`, `fire` and `_delay` belong to `button`, not `html_button`.
13
+
14
+ Both immediate and delayed buttons render as `<button>`. The delayed example adds only `_delay=600`; the action stays the same. Every click restarts the timer. The last event supplies modifiers, and the delayed action reads its arguments when it runs. `_counter` is the group count; a nested controller receives it as `button_counter`. Without delay, the existing counter counts the button's whole lifetime.
15
+
16
+ A container can supply inherited `_delay`; an explicit zero on a button makes it immediate. Button delay precedes action, ask, fire and the nested provider. A controller's own `_delay` separately delays its Data/subscription triggers. Explicit native event listeners still run on each click. Removing the button or closing its page cancels pending work. The old legacy 200 ms disabled guard for immediate clicks is not introduced.
17
+
18
+ **Exercise:** change only `_delay` from 600 to 1200. **Result:** the last click postpones the same action for 1.2 seconds; the action and its count logic do not change.
19
+
20
+ </details>
@@ -65,3 +65,9 @@ The date initialization issue in c17 and named-resource-host prerequisites remai
65
65
  explicit gaps; the local file lesson does not imply HTTP service or mail delivery.
66
66
 
67
67
  </details>
68
+
69
+
70
+ After the messaging lessons, **c20 · Actions, confirmations and references**
71
+ completes the 0.2.18 button examples. **c21 · Plain and delayed buttons** then
72
+ shows the 0.2.19 additions, comparing the same action before
73
+ and after adding `_delay`. It needs core 0.2.19 or later.