@fusion.dev/helix-core 0.177.0 → 0.177.1

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.
@@ -12298,13 +12298,13 @@
12298
12298
  {
12299
12299
  "kind": "method",
12300
12300
  "name": "runPendingUpdate",
12301
- "privacy": "private",
12301
+ "privacy": "protected",
12302
12302
  "return": {
12303
12303
  "type": {
12304
12304
  "text": "void"
12305
12305
  }
12306
12306
  },
12307
- "description": "Runs an update that is on its way, so the verdict read next is the one\nfor what the control holds now.\n\n**A control reports its verdict as it updates, and an update waits for a\nmicrotask**, so a script that wrote `value` and asked at once was answered\nabout the value before: measured on Time Input, `value = '18:00'` past a\n`max` of 17:00 and `checkValidity()` returned true until `updateComplete`.\nA native control answers about the value it was just given, so the\nquestions a script can ask run the pending update first. Lit's\n`performUpdate` is the documented way to run one synchronously."
12307
+ "description": "Runs an update that is on its way, so the verdict read next is the one\nfor what the control holds now.\n\n**A control reports its verdict as it updates, and an update waits for a\nmicrotask**, so a script that wrote `value` and asked at once was answered\nabout the value before: measured on Time Input, `value = '18:00'` past a\n`max` of 17:00 and `checkValidity()` returned true until `updateComplete`.\nA native control answers about the value it was just given, so the\nquestions a script can ask run the pending update first. Lit's\n`performUpdate` is the documented way to run one synchronously.\n\nProtected for a control that submits on its own, which has to hand the\nform the verdict for what it holds now: Pin Input's `autosubmit`."
12308
12308
  },
12309
12309
  {
12310
12310
  "kind": "field",
@@ -12415,6 +12415,24 @@
12415
12415
  "privacy": "private",
12416
12416
  "description": "Stops listening for the labels in this control's tree."
12417
12417
  },
12418
+ {
12419
+ "kind": "method",
12420
+ "name": "formAssociatedCallback",
12421
+ "return": {
12422
+ "type": {
12423
+ "text": "void"
12424
+ }
12425
+ },
12426
+ "parameters": [
12427
+ {
12428
+ "name": "form",
12429
+ "type": {
12430
+ "text": "HTMLFormElement | null"
12431
+ }
12432
+ }
12433
+ ],
12434
+ "description": "Called by the browser when the control's form changes: watches the new\nform's `checkValidity()`, as connecting does for the form it starts in."
12435
+ },
12418
12436
  {
12419
12437
  "kind": "field",
12420
12438
  "name": "form",
@@ -21044,6 +21062,17 @@
21044
21062
  "privacy": "protected",
21045
21063
  "readonly": true,
21046
21064
  "description": "A press that focuses the field opens the picker.\n\nA reader who presses into this control is choosing, and making them find\nthe trigger afterwards is a second action for something they have already\nasked for. Typing still works with it up, because the picker follows the\nfield rather than the other way round. Focus by Tab or by `focus()` opens\nnothing: see the mixin's note.\n\n**Only where focus arrived from outside this element**, which is the whole\nof the difficulty: picking shuts the panel and puts the caret back in the\nfield, so an unguarded version reopens the picker the reader just\nanswered. `relatedTarget` is where focus came from, so a hand-off from the\npicker or the trigger is left alone and only a genuine arrival opens\nanything. `returning` covers the hand-off `relatedTarget` cannot see, and\n`MixinPickerField` says why.\n\nOn the input rather than on the field, too. Focus does not bubble, so a\nlistener there covers the text and not the trigger, which is what keeps\npressing the trigger from opening the panel on focus and then shutting it\nagain on the click."
21065
+ },
21066
+ {
21067
+ "kind": "method",
21068
+ "name": "selectOnArrival",
21069
+ "privacy": "private",
21070
+ "return": {
21071
+ "type": {
21072
+ "text": "void"
21073
+ }
21074
+ },
21075
+ "description": "Selects the text a reader tabbed into, once the field shows it for\nediting, as Tab into a native `<input>` holding text selects all of it,\nfrom either side. Not when this element hands the caret back itself.\n\n**After the update**, because focus is what turns editing on, and the\ntext the field then writes put the caret at its end. Measured in\nChromium before this: Tab into a Time Input holding `14:30` left the\nselection at 5 to 5, and typing `9am` gave `14:309am`. A press keeps\nits caret where the pointer put it."
21047
21076
  }
21048
21077
  ],
21049
21078
  "attributes": [
@@ -33929,7 +33958,7 @@
33929
33958
  "type": {
33930
33959
  "text": "string"
33931
33960
  },
33932
- "default": "'0.177.0'",
33961
+ "default": "'0.177.1'",
33933
33962
  "description": "The version of the package this file ships in.\n\n**Written by `fusion/scripts/version.mjs`** wherever it writes the package's\n`package.json`, and stamped back into the repository beside it after a\nrelease, so the two never disagree; a spec beside this holds them to it.\nNothing else in the running code knows which release it is."
33934
33963
  }
33935
33964
  ],
@@ -34565,6 +34594,6 @@
34565
34594
  ],
34566
34595
  "helix": {
34567
34596
  "package": "@fusion.dev/helix-core",
34568
- "version": "0.177.0"
34597
+ "version": "0.177.1"
34569
34598
  }
34570
34599
  }
@@ -228,8 +228,11 @@ export declare class HelixFormElement extends HelixElement {
228
228
  * A native control answers about the value it was just given, so the
229
229
  * questions a script can ask run the pending update first. Lit's
230
230
  * `performUpdate` is the documented way to run one synchronously.
231
+ *
232
+ * Protected for a control that submits on its own, which has to hand the
233
+ * form the verdict for what it holds now: Pin Input's `autosubmit`.
231
234
  */
232
- private runPendingUpdate;
235
+ protected runPendingUpdate(): void;
233
236
  /** Whether the control is checked when its form is submitted. */
234
237
  get willValidate(): boolean;
235
238
  /**
@@ -246,8 +249,10 @@ export declare class HelixFormElement extends HelixElement {
246
249
  * required `<input>` checked from a script fires `invalid` and does not match
247
250
  * `:user-invalid`, measured in Chromium. Every layer that takes `invalid` as
248
251
  * a reader's attempt asks this first, so a script that checks an untouched
249
- * field does not mark it wrong. A submission, `reportValidity()`, and the
250
- * reader's own edits still do.
252
+ * field does not mark it wrong. The same holds for `form.checkValidity()`,
253
+ * which fires `invalid` on every control in the form and marks no native
254
+ * one. A submission, `reportValidity()`, and the reader's own edits still
255
+ * mark it.
251
256
  */
252
257
  protected get checkingValidity(): boolean;
253
258
  /**
@@ -290,6 +295,11 @@ export declare class HelixFormElement extends HelixElement {
290
295
  private unwatchLabels;
291
296
  connectedCallback(): void;
292
297
  disconnectedCallback(): void;
298
+ /**
299
+ * Called by the browser when the control's form changes: watches the new
300
+ * form's `checkValidity()`, as connecting does for the form it starts in.
301
+ */
302
+ formAssociatedCallback(form: HTMLFormElement | null): void;
293
303
  /**
294
304
  * @internal Whether this control is a field that counts against implicit
295
305
  * submission: a single line of text, as `<input type="text">` is. Read by
@@ -41,6 +41,24 @@ const firstTabStop = (node) => {
41
41
  }
42
42
  return null;
43
43
  };
44
+ let formChecks = 0;
45
+ const watchedForms = new WeakSet();
46
+ const watchFormChecks = (form) => {
47
+ if (!form || watchedForms.has(form)) {
48
+ return;
49
+ }
50
+ watchedForms.add(form);
51
+ const check = form.checkValidity;
52
+ form.checkValidity = function checkValidity() {
53
+ formChecks += 1;
54
+ try {
55
+ return check.call(this);
56
+ }
57
+ finally {
58
+ formChecks -= 1;
59
+ }
60
+ };
61
+ };
44
62
  export class HelixFormElement extends HelixElement {
45
63
  static { this.formAssociated = true; }
46
64
  get isDisabled() {
@@ -172,7 +190,7 @@ export class HelixFormElement extends HelixElement {
172
190
  }
173
191
  }
174
192
  get checkingValidity() {
175
- return this.checkInProgress;
193
+ return this.checkInProgress || formChecks > 0;
176
194
  }
177
195
  reportValidity() {
178
196
  this.runPendingUpdate();
@@ -198,6 +216,7 @@ export class HelixFormElement extends HelixElement {
198
216
  }
199
217
  connectedCallback() {
200
218
  super.connectedCallback();
219
+ watchFormChecks(this.internals.form);
201
220
  this.addEventListener('click', this.focusFromLabel);
202
221
  this.addEventListener('invalid', this.refreshAnchor);
203
222
  this.unwatchLabels = watchLabels(this, () => this.requestUpdate());
@@ -209,6 +228,9 @@ export class HelixFormElement extends HelixElement {
209
228
  this.unwatchLabels?.();
210
229
  this.unwatchLabels = undefined;
211
230
  }
231
+ formAssociatedCallback(form) {
232
+ watchFormChecks(form);
233
+ }
212
234
  get blocksImplicitSubmission() {
213
235
  return false;
214
236
  }
@@ -55,10 +55,11 @@ export const MixinPickerEditing = (Base) => {
55
55
  const pressed = this.pressing;
56
56
  this.pressing = false;
57
57
  const from = event.relatedTarget;
58
- if (!pressed || this.open || this.isDisabled || this.returning) {
59
- return;
58
+ const inside = from instanceof Node && Boolean(this.renderRoot?.contains?.(from));
59
+ if (!pressed && !this.returning) {
60
+ this.selectOnArrival();
60
61
  }
61
- if (from instanceof Node && this.renderRoot?.contains?.(from)) {
62
+ if (!pressed || this.open || this.isDisabled || this.returning || inside) {
62
63
  return;
63
64
  }
64
65
  this.requestOpen(true);
@@ -90,6 +91,15 @@ export const MixinPickerEditing = (Base) => {
90
91
  this.removeEventListener('click', this.handlePress);
91
92
  this.removeEventListener('keydown', this.handleEditKey);
92
93
  }
94
+ selectOnArrival() {
95
+ void this.updateComplete.then(() => {
96
+ const control = this.control;
97
+ const root = this.renderRoot;
98
+ if (control && root?.activeElement === control) {
99
+ control.select();
100
+ }
101
+ });
102
+ }
93
103
  }
94
104
  __decorate([
95
105
  property({ attribute: 'readonly', reflect: true, type: Boolean })
@@ -32,8 +32,9 @@ export const parseNumber = (text, locale = '', options) => {
32
32
  const parts = format.formatToParts(-12345.6);
33
33
  const one = format.formatToParts(1);
34
34
  const of = (type, from = parts) => from.filter((part) => part.type === type).map((part) => part.value);
35
- const group = of('group')[0] ?? ',';
36
- const decimal = of('decimal')[0] ?? '.';
35
+ const plain = formatter(locale, undefined).formatToParts(-12345.6);
36
+ const group = of('group')[0] ?? of('group', plain)[0] ?? ',';
37
+ const decimal = of('decimal')[0] ?? of('decimal', plain)[0] ?? '.';
37
38
  const named = (from) => [
38
39
  ...of('currency', from),
39
40
  ...of('percentSign', from),
package/dist/version.d.ts CHANGED
@@ -6,4 +6,4 @@
6
6
  * release, so the two never disagree; a spec beside this holds them to it.
7
7
  * Nothing else in the running code knows which release it is.
8
8
  */
9
- export declare const VERSION = "0.177.0";
9
+ export declare const VERSION = "0.177.1";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = '0.177.0';
1
+ export const VERSION = '0.177.1';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@fusion.dev/helix-core",
3
3
  "description": "Helix element primitives: base classes, mixins, decorators, utilities.",
4
- "version": "0.177.0",
4
+ "version": "0.177.1",
5
5
  "license": "MIT",
6
6
  "author": "Ross Sheppard <ross.sheppard@fusion.dev>",
7
7
  "repository": {
@@ -68,11 +68,11 @@
68
68
  "bugs": "https://github.com/project-fusion/fusion/issues",
69
69
  "dependencies": {
70
70
  "@floating-ui/dom": "^1.8.0",
71
- "@fusion.dev/helix-icons": "0.177.0"
71
+ "@fusion.dev/helix-icons": "0.177.1"
72
72
  },
73
73
  "devDependencies": {
74
- "@fusion.dev/helix-cem": "0.177.0",
75
- "@fusion.dev/helix-testing": "0.177.0",
74
+ "@fusion.dev/helix-cem": "0.177.1",
75
+ "@fusion.dev/helix-testing": "0.177.1",
76
76
  "@lit-labs/ssr": "^4.1.0",
77
77
  "lit": "^3.2.1"
78
78
  },