@gramlot/gramlot-examples 0.3.6 → 0.3.8

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.3.6.
15
+ Current release: 0.3.8.
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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gramlot/gramlot-examples",
3
- "version": "0.3.6",
3
+ "version": "0.3.8",
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.3.3"
17
+ "@gramlot/gramlot": ">=0.3.5"
18
18
  },
19
19
  "devDependencies": {
20
20
  "@genrojs/bag": ">=0.11.0",
@@ -24299,6 +24299,19 @@ var rangeParameters = {
24299
24299
  };
24300
24300
  function rangeEditor(vertical) {
24301
24301
  return (Base) => class extends Base {
24302
+ #sourceContext;
24303
+ #correcting = false;
24304
+ setSourceContext(context) {
24305
+ this.#sourceContext = context;
24306
+ }
24307
+ projectAttributes(attrs, adapt) {
24308
+ const previous = this.parameters;
24309
+ const rest = super.projectAttributes(attrs, adapt);
24310
+ if (previous && ["min", "max", "step"].some((name) => previous[name] !== this.parameters[name])) {
24311
+ this.writeControlValue(this.parameters.value);
24312
+ }
24313
+ return rest;
24314
+ }
24302
24315
  createControl() {
24303
24316
  const control = this.ownerDocument.createElement("input");
24304
24317
  control.type = "range";
@@ -24312,7 +24325,16 @@ function rangeEditor(vertical) {
24312
24325
  return this.control.valueAsNumber;
24313
24326
  }
24314
24327
  writeControlValue(value) {
24315
- if (this.control.valueAsNumber !== value) this.control.value = String(value);
24328
+ this.control.value = String(value);
24329
+ const corrected = this.control.valueAsNumber;
24330
+ if (!this.#correcting && Number.isFinite(value) && Number.isFinite(corrected) && corrected !== value) {
24331
+ this.#correcting = true;
24332
+ try {
24333
+ this.#sourceContext.writeParameter("value", corrected);
24334
+ } finally {
24335
+ this.#correcting = false;
24336
+ }
24337
+ }
24316
24338
  }
24317
24339
  };
24318
24340
  }
@@ -24322,6 +24344,7 @@ var HorizontalSlider = defineComponent({
24322
24344
  base: NativeControl,
24323
24345
  mixins: [Labelbox, FieldState],
24324
24346
  parameters: rangeParameters,
24347
+ sourceContext: true,
24325
24348
  implementation: rangeEditor(false)
24326
24349
  });
24327
24350
  var VerticalSlider = defineComponent({
@@ -24330,6 +24353,7 @@ var VerticalSlider = defineComponent({
24330
24353
  base: NativeControl,
24331
24354
  mixins: [Labelbox, FieldState],
24332
24355
  parameters: rangeParameters,
24356
+ sourceContext: true,
24333
24357
  implementation: rangeEditor(true)
24334
24358
  });
24335
24359
  var NumberTextBox = defineComponent({
@@ -253,7 +253,7 @@ var SIDES = ["top", "bottom", "left", "right"];
253
253
  var REGIONS = [...SIDES, "center"];
254
254
  var borderRegions = /* @__PURE__ */ new WeakMap();
255
255
  var horizontal = (side) => side === "top" || side === "bottom";
256
- function projectBorderChild(element, attrs, { adapt, invoke, publish, publishParent }) {
256
+ function projectBorderChild(element, attrs, { adapt, invoke, publish, publishParent, writeParameter }) {
257
257
  const rest = { ...attrs }, splitter = {}, drawer = {};
258
258
  for (const [name, value] of Object.entries(rest)) {
259
259
  for (const [prefix, group] of [["splitter_", splitter], ["drawer_", drawer]]) {
@@ -272,7 +272,15 @@ function projectBorderChild(element, attrs, { adapt, invoke, publish, publishPar
272
272
  if (![void 0, null, false, true, "close"].includes(rest.drawer)) throw new TypeError("drawer must be true, false or close");
273
273
  const probe = element.ownerDocument.createElement("div");
274
274
  probe.style.cssText = rest.style ?? "";
275
- const size = probe.style[horizontal(region) ? "height" : "width"];
275
+ const dimension = horizontal(region) ? "height" : "width";
276
+ const numeric = typeof rest[dimension] === "number";
277
+ if (numeric) {
278
+ if (!Number.isFinite(rest[dimension]) || rest[dimension] < 0) throw new TypeError("Border numeric dimensions must be finite nonnegative pixels");
279
+ probe.style[dimension] = `${rest[dimension]}px`;
280
+ }
281
+ const size = probe.style[dimension];
282
+ delete rest.width;
283
+ delete rest.height;
276
284
  probe.style.width = "100%";
277
285
  probe.style.height = "100%";
278
286
  rest.style = probe.style.cssText;
@@ -297,6 +305,9 @@ function projectBorderChild(element, attrs, { adapt, invoke, publish, publishPar
297
305
  label,
298
306
  icon,
299
307
  minimize,
308
+ writeParameter: (size2) => {
309
+ if (numeric) writeParameter(dimension, size2);
310
+ },
300
311
  notifications: {
301
312
  closableChange: (payload) => publish("closable_change", payload),
302
313
  splitterResize: (payload) => publishParent("splitter_resize", payload)
@@ -334,7 +345,7 @@ var BorderContainer = defineComponent({
334
345
  tag: "gramlot-border-container",
335
346
  base: Container,
336
347
  subTags: "*",
337
- childParameters: { region: { required: true, enum: REGIONS, unique: true }, drawer_onclick: { inline: ["evt", "show"] } },
348
+ childParameters: { region: { required: true, enum: REGIONS, unique: true }, width: {}, height: {}, drawer_onclick: { inline: ["evt", "show"] } },
338
349
  parameters: {
339
350
  design: { dtype: "T", default: "headline", binding: "read", enum: ["headline", "sidebar"] },
340
351
  persist: { dtype: "B", default: false, binding: "read" }
@@ -636,6 +647,7 @@ var BorderContainer = defineComponent({
636
647
  const size = Math.max(Math.min(c.min, available), Math.min(desired, c.max, available));
637
648
  state.size = `${size}px`;
638
649
  this.#render(side);
650
+ c.writeParameter(size);
639
651
  }
640
652
  #keyResize(side, evt) {
641
653
  const state = this.#regions.get(side);
@@ -82,7 +82,7 @@ export class Page extends BasePage {
82
82
  });
83
83
  const header = outer.contentPane({
84
84
  region: 'top',
85
- height: '^regions.outer.top:{}px',
85
+ height: '^regions.outer.top',
86
86
  minSize: 40,
87
87
  maxSize: 110,
88
88
  drawer_label: 'Header',
@@ -94,7 +94,7 @@ export class Page extends BasePage {
94
94
  header.span('^customer');
95
95
  const footer = outer.contentPane({
96
96
  region: 'bottom',
97
- height: '^regions.outer.bottom:{}px',
97
+ height: '^regions.outer.bottom',
98
98
  minSize: 35,
99
99
  maxSize: 130,
100
100
  drawer_label: 'Status',
@@ -107,7 +107,7 @@ export class Page extends BasePage {
107
107
  const nav = outer.borderContainer({
108
108
  id: 'lab-navigation',
109
109
  region: 'left',
110
- width: '^regions.outer.left:{}px',
110
+ width: '^regions.outer.left',
111
111
  minSize: 90,
112
112
  maxSize: 240,
113
113
  drawer_label: 'Navigation',
@@ -129,7 +129,7 @@ export class Page extends BasePage {
129
129
  const details = outer.borderContainer({
130
130
  id: 'lab-details',
131
131
  region: 'right',
132
- width: '^regions.outer.right:{}px',
132
+ width: '^regions.outer.right',
133
133
  minSize: 90,
134
134
  maxSize: 240,
135
135
  drawer_label: 'Details',
@@ -159,7 +159,7 @@ export class Page extends BasePage {
159
159
  });
160
160
  const tools = work.contentPane({
161
161
  region: 'left',
162
- width: '^regions.workspace.left:{}px',
162
+ width: '^regions.workspace.left',
163
163
  minSize: 70,
164
164
  maxSize: 200,
165
165
  drawer_label: 'Tools',
@@ -172,7 +172,7 @@ export class Page extends BasePage {
172
172
  tools.checkbox({ value: '^readonly', lbl: 'Editor', label: 'Read only' });
173
173
  const preview = work.contentPane({
174
174
  region: 'bottom',
175
- height: '^regions.workspace.bottom:{}px',
175
+ height: '^regions.workspace.bottom',
176
176
  minSize: 50,
177
177
  maxSize: 180,
178
178
  drawer_label: 'Preview',
@@ -213,7 +213,7 @@ export class Page extends BasePage {
213
213
  fields.numberTextBox({ id: 'lab-quantity', value: '^quantity', lbl: 'Quantity', width: '100%' });
214
214
  const notes = editor.contentPane({
215
215
  region: 'bottom',
216
- height: '^regions.editor.bottom:{}px',
216
+ height: '^regions.editor.bottom',
217
217
  minSize: 60,
218
218
  maxSize: 180,
219
219
  drawer_label: 'Notes',
@@ -8,6 +8,8 @@ If the page looks complicated at first, start with just one slider. You do not n
8
8
 
9
9
  Move one region-size slider a little, then back again. Follow the boundary that moves. Does it belong to the outer workspace, or to a layout inside it? Repeat with a slider for a different level.
10
10
 
11
+ Next, drag the same separator. Its slider moves immediately: both controls update the same number. Open the Inspector on Data to follow `regions.outer.left` while moving either the Navigation width slider or its separator. Try a nested region too.
12
+
11
13
  Now enter a customer name and a note. Change a date or delivery option too. Look for the places where those values are repeated. They are views of shared Data, not separate copies of the customer.
12
14
 
13
15
  Move the sliders again. The regions become wider, narrower, taller or shorter while your edits remain. Close one drawer, rearrange another area, and reopen it. Hiding a panel has not discarded its contents.
@@ -16,7 +18,21 @@ Finally, try the design and corner-radius controls. These change presentation wh
16
18
 
17
19
  ## How a number becomes a size
18
20
 
19
- A slider produces a number. A region width needs a size such as 180px. The example connects them with a pointer template: it reads the slider's Data value and adds the unit. You can find these declarations by looking for the region-size paths in **Code**.
21
+ The slider and the region both use a plain pointer to the same number. Numeric region widths and heights are pixels; the borderContainer converts them to CSS internally. Dragging the splitter writes back the constrained number through the binding, so the slider follows immediately.
22
+
23
+ ```python
24
+ sliders.horizontalSlider(value="^regions.outer.left", min=90, max=240, step=1, live=True)
25
+ nav = outer.borderContainer(region="left", width="^regions.outer.left",
26
+ splitter=True, minSize=90, maxSize=240)
27
+ ```
28
+
29
+ Each size slider uses `step=1`. It also corrects numbers received through the binding:
30
+ a fractional splitter measurement such as `176.73` becomes `177` in Data, then the
31
+ panel and label follow that same value. In Inspector, set `regions.outer.left`
32
+ to `176.73` and observe it settle at `177`. The slider writes only when correction
33
+ is needed; a number already on the step grid is not written again.
34
+
35
+ There is no formatting suffix or synchronization controller. `regions` is just the Data branch chosen by this example; it is not a special container parameter. You can find all seven shared size bindings in **Code**.
20
36
 
21
37
  The nested containers still have a simple responsibility: each lays out its own immediate children. The outer container does not calculate the positions of every field inside the editor.
22
38