@yuneta/gobj-ui 7.23.55 → 7.23.57

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.
@@ -5900,18 +5900,18 @@ function refresh_clear($input) {
5900
5900
  * Copyright (c) 2026, ArtGins.
5901
5901
  * All Rights Reserved.
5902
5902
  ***********************************************************************/
5903
- var LEFT_BUTTONS = [
5904
- "save",
5903
+ var LEFT_BUTTONS = ["copy", "paste"];
5904
+ var RIGHT_BUTTONS = [
5905
5905
  "undo",
5906
- "clear"
5906
+ "clear",
5907
+ "save"
5907
5908
  ];
5908
- var RIGHT_BUTTONS = ["copy", "paste"];
5909
5909
  var DEFAULT_TOOLBAR = [
5910
- "save",
5910
+ "copy",
5911
+ "paste",
5911
5912
  "undo",
5912
5913
  "clear",
5913
- "copy",
5914
- "paste"
5914
+ "save"
5915
5915
  ];
5916
5916
  /***************************************************************
5917
5917
  * plan_toolbar(wanted) -> {left, right, unknown}
@@ -7515,7 +7515,7 @@ function create_form_field(gobj, $form, prefix, field_desc, { tag, inputType, na
7515
7515
  $extend.dataset.form_tag = tag;
7516
7516
  $extend.dataset.form_input_type = inputType;
7517
7517
  $extend.classList.add("yui-form-data-input");
7518
- apply_readonly_to_control(readonly, tag, $extend);
7518
+ apply_readonly_to_control(readonly, tag, inputType, $extend);
7519
7519
  }
7520
7520
  return $element;
7521
7521
  }
@@ -7536,7 +7536,7 @@ function create_form_field(gobj, $form, prefix, field_desc, { tag, inputType, na
7536
7536
  * control is still read -- unlike in a native submit, where it would
7537
7537
  * be dropped.
7538
7538
  ******************************************************************/
7539
- function apply_readonly_to_control(readonly, tag, $extend) {
7539
+ function apply_readonly_to_control(readonly, tag, inputType, $extend) {
7540
7540
  if (!readonly) return;
7541
7541
  switch (tag) {
7542
7542
  case "select":
@@ -7547,6 +7547,11 @@ function apply_readonly_to_control(readonly, tag, $extend) {
7547
7547
  case "select2":
7548
7548
  $extend.setAttribute("disabled", "disabled");
7549
7549
  if ($extend.tom_select) $extend.tom_select.disable();
7550
+ break;
7551
+ case "jsoneditor":
7552
+ $extend.classList.add("yui-jse-readonly");
7553
+ break;
7554
+ case "input": if (inputType === "color") $extend.setAttribute("disabled", "disabled");
7550
7555
  }
7551
7556
  }
7552
7557
  /******************************************************************
@@ -5895,18 +5895,18 @@ function refresh_clear($input) {
5895
5895
  * Copyright (c) 2026, ArtGins.
5896
5896
  * All Rights Reserved.
5897
5897
  ***********************************************************************/
5898
- var LEFT_BUTTONS = [
5899
- "save",
5898
+ var LEFT_BUTTONS = ["copy", "paste"];
5899
+ var RIGHT_BUTTONS = [
5900
5900
  "undo",
5901
- "clear"
5901
+ "clear",
5902
+ "save"
5902
5903
  ];
5903
- var RIGHT_BUTTONS = ["copy", "paste"];
5904
5904
  var DEFAULT_TOOLBAR = [
5905
- "save",
5905
+ "copy",
5906
+ "paste",
5906
5907
  "undo",
5907
5908
  "clear",
5908
- "copy",
5909
- "paste"
5909
+ "save"
5910
5910
  ];
5911
5911
  /***************************************************************
5912
5912
  * plan_toolbar(wanted) -> {left, right, unknown}
@@ -7510,7 +7510,7 @@ function create_form_field(gobj, $form, prefix, field_desc, { tag, inputType, na
7510
7510
  $extend.dataset.form_tag = tag;
7511
7511
  $extend.dataset.form_input_type = inputType;
7512
7512
  $extend.classList.add("yui-form-data-input");
7513
- apply_readonly_to_control(readonly, tag, $extend);
7513
+ apply_readonly_to_control(readonly, tag, inputType, $extend);
7514
7514
  }
7515
7515
  return $element;
7516
7516
  }
@@ -7531,7 +7531,7 @@ function create_form_field(gobj, $form, prefix, field_desc, { tag, inputType, na
7531
7531
  * control is still read -- unlike in a native submit, where it would
7532
7532
  * be dropped.
7533
7533
  ******************************************************************/
7534
- function apply_readonly_to_control(readonly, tag, $extend) {
7534
+ function apply_readonly_to_control(readonly, tag, inputType, $extend) {
7535
7535
  if (!readonly) return;
7536
7536
  switch (tag) {
7537
7537
  case "select":
@@ -7542,6 +7542,11 @@ function apply_readonly_to_control(readonly, tag, $extend) {
7542
7542
  case "select2":
7543
7543
  $extend.setAttribute("disabled", "disabled");
7544
7544
  if ($extend.tom_select) $extend.tom_select.disable();
7545
+ break;
7546
+ case "jsoneditor":
7547
+ $extend.classList.add("yui-jse-readonly");
7548
+ break;
7549
+ case "input": if (inputType === "color") $extend.setAttribute("disabled", "disabled");
7545
7550
  }
7546
7551
  }
7547
7552
  /******************************************************************
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yuneta/gobj-ui",
3
- "version": "7.23.55",
3
+ "version": "7.23.57",
4
4
  "type": "module",
5
5
  "main": "dist/gobj-ui.cjs.js",
6
6
  "module": "dist/gobj-ui.es.js",
@@ -57,3 +57,25 @@
57
57
  overscroll-behavior: contain;
58
58
  }
59
59
 
60
+
61
+ /* A colour that cannot be changed. `readonly` does not apply to a colour
62
+ * input (the spec lists the types it applies to and colour is not one),
63
+ * so it is `disabled` instead -- and a disabled swatch is drawn by the
64
+ * browser as a pale ghost of itself, which loses the one thing the field
65
+ * is for: seeing the colour. Keep the swatch, lose the affordance. */
66
+ .C_YUI_FORM input[type="color"][disabled] {
67
+ opacity: 1;
68
+ cursor: default;
69
+ border-color: var(--bulma-border, #dbdbdb);
70
+ }
71
+
72
+ /* A json document that cannot be edited. The editor is read-only for
73
+ * real (`readOnly` at build time); this is what makes it LOOK it --
74
+ * the menu bar is the whole invitation, and it offers nothing that
75
+ * reading needs. */
76
+ .C_YUI_FORM .yui-jse-readonly .jse-menu {
77
+ display: none !important;
78
+ }
79
+ .C_YUI_FORM .yui-jse-readonly {
80
+ background-color: var(--bulma-background, #f5f5f5);
81
+ }
package/src/c_yui_form.js CHANGED
@@ -1450,7 +1450,7 @@ function create_form_field(
1450
1450
  $extend.dataset.form_input_type = inputType;
1451
1451
  $extend.classList.add('yui-form-data-input');
1452
1452
 
1453
- apply_readonly_to_control(readonly, tag, $extend);
1453
+ apply_readonly_to_control(readonly, tag, inputType, $extend);
1454
1454
  }
1455
1455
 
1456
1456
  return $element;
@@ -1473,7 +1473,7 @@ function create_form_field(
1473
1473
  * control is still read -- unlike in a native submit, where it would
1474
1474
  * be dropped.
1475
1475
  ******************************************************************/
1476
- function apply_readonly_to_control(readonly, tag, $extend)
1476
+ function apply_readonly_to_control(readonly, tag, inputType, $extend)
1477
1477
  {
1478
1478
  if(!readonly) {
1479
1479
  return;
@@ -1491,9 +1491,26 @@ function apply_readonly_to_control(readonly, tag, $extend)
1491
1491
  $extend.tom_select.disable();
1492
1492
  }
1493
1493
  break;
1494
+ case 'jsoneditor':
1495
+ /* It IS read-only -- `readOnly` went to the editor when it was
1496
+ * built -- and it did not LOOK it: a json editor with its menu
1497
+ * bar on top invites an edit it will discard. The class mutes
1498
+ * it (c_yui_form.css), which is the same thing a readonly
1499
+ * `input` has done since it existed. */
1500
+ $extend.classList.add('yui-jse-readonly');
1501
+ break;
1502
+ case 'input':
1503
+ /* `readonly` does not apply to a colour input -- the spec lists
1504
+ * the types it applies to and colour is not one of them -- so
1505
+ * the swatch opens its picker and the value changes, and in a
1506
+ * form that only sends writable cols the change is then
1507
+ * silently dropped. Date and time DO honour it, and keep it:
1508
+ * readonly stays selectable and copyable, disabled does not. */
1509
+ if(inputType === 'color') {
1510
+ $extend.setAttribute("disabled", "disabled");
1511
+ }
1512
+ break;
1494
1513
  default:
1495
- /* A text control already carries `readonly`, which is the
1496
- * better of the two: it stays selectable and copyable. */
1497
1514
  break;
1498
1515
  }
1499
1516
  }
@@ -10,13 +10,25 @@
10
10
  * All Rights Reserved.
11
11
  ***********************************************************************/
12
12
 
13
- /* Save/undo/clear sit on the left of the bar, copy/paste on the right.
14
- * That split is what the layout has always drawn, and keeping it means
15
- * a caller who drops one group does not leave a hole in the middle. */
16
- const LEFT_BUTTONS = ["save", "undo", "clear"];
17
- const RIGHT_BUTTONS = ["copy", "paste"];
13
+ /* Two groups, and the split is a real one: `undo`, `clear` and `save`
14
+ * act on the RECORD being edited, `copy` and `paste` move it in and out
15
+ * of the clipboard. Keeping them apart means a caller who drops one
16
+ * group does not leave a hole in the middle.
17
+ *
18
+ * WHICH SIDE EACH ONE TAKES CHANGED (2026-09-04), and the reason is that
19
+ * the form changed shape: it was an embedded panel and it opens as a
20
+ * MODAL DIALOG now. In a dialog the primary action goes bottom-RIGHT --
21
+ * every platform puts it there, and it is where the eye ends after
22
+ * reading the fields. `save` sat hard against the left edge, which is
23
+ * where a person looks last.
24
+ *
25
+ * So: clipboard on the left, record actions on the right, and `save`
26
+ * LAST of all. The order inside a group is the order asked for, which
27
+ * is why DEFAULT_TOOLBAR is written in the order it is drawn. */
28
+ const LEFT_BUTTONS = ["copy", "paste"];
29
+ const RIGHT_BUTTONS = ["undo", "clear", "save"];
18
30
 
19
- const DEFAULT_TOOLBAR = ["save", "undo", "clear", "copy", "paste"];
31
+ const DEFAULT_TOOLBAR = ["copy", "paste", "undo", "clear", "save"];
20
32
 
21
33
 
22
34
  /***************************************************************
@@ -5,17 +5,26 @@ describe("plan_toolbar", () => {
5
5
 
6
6
  it("says nothing -> the five buttons this gclass always had", () => {
7
7
  const p = plan_toolbar(undefined);
8
- expect(p.left).toEqual(["save", "undo", "clear"]);
9
- expect(p.right).toEqual(["copy", "paste"]);
8
+ expect(p.left).toEqual(["copy", "paste"]);
9
+ expect(p.right).toEqual(["undo", "clear", "save"]);
10
10
  expect(p.unknown).toEqual([]);
11
11
  expect(DEFAULT_TOOLBAR.length).toBe(5);
12
12
  });
13
13
 
14
+ it("save is the LAST button of the bar", () => {
15
+ /* The form opens as a modal dialog and a dialog's primary action
16
+ * goes bottom-right. Asserted on the plan and not on the CSS,
17
+ * because the plan is what decides the two groups. */
18
+ const p = plan_toolbar(undefined);
19
+ expect(p.right[p.right.length - 1]).toBe("save");
20
+ expect(p.left).not.toContain("save");
21
+ });
22
+
14
23
  it("a non-array is not an empty toolbar: it is the default", () => {
15
24
  /* Reading a JSON attr that was never set must not silently
16
25
  * leave a form with no save button. */
17
26
  for(const v of [null, "save", 0, {}]) {
18
- expect(plan_toolbar(v).left).toContain("save");
27
+ expect(plan_toolbar(v).right).toContain("save");
19
28
  }
20
29
  });
21
30
 
@@ -28,21 +37,21 @@ describe("plan_toolbar", () => {
28
37
 
29
38
  it("one action: a dialog asks for save only", () => {
30
39
  const p = plan_toolbar(["save"]);
31
- expect(p.left).toEqual(["save"]);
32
- expect(p.right).toEqual([]);
40
+ expect(p.right).toEqual(["save"]);
41
+ expect(p.left).toEqual([]);
33
42
  });
34
43
 
35
44
  it("keeps the order asked for inside each side", () => {
36
45
  const p = plan_toolbar(["clear", "save", "paste", "copy"]);
37
- expect(p.left).toEqual(["clear", "save"]);
38
- expect(p.right).toEqual(["paste", "copy"]);
46
+ expect(p.right).toEqual(["clear", "save"]);
47
+ expect(p.left).toEqual(["paste", "copy"]);
39
48
  });
40
49
 
41
50
  it("an unknown name is REPORTED, never silently dropped", () => {
42
51
  /* A typo here would otherwise remove a button and leave no
43
52
  * trace of why it is missing. */
44
53
  const p = plan_toolbar(["save", "sav", "undo"]);
45
- expect(p.left).toEqual(["save", "undo"]);
54
+ expect(p.right).toEqual(["save", "undo"]);
46
55
  expect(p.unknown).toEqual(["sav"]);
47
56
  });
48
57
 
@@ -59,7 +68,7 @@ describe("plan_toolbar", () => {
59
68
 
60
69
  it("drops one whole side without disturbing the other", () => {
61
70
  const p = plan_toolbar(["save", "undo", "clear"]);
62
- expect(p.left).toEqual(["save", "undo", "clear"]);
63
- expect(p.right).toEqual([]);
71
+ expect(p.right).toEqual(["save", "undo", "clear"]);
72
+ expect(p.left).toEqual([]);
64
73
  });
65
74
  });