@fusion.dev/helix-core 0.166.0 → 0.168.0

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.
@@ -6060,6 +6060,99 @@
6060
6060
  },
6061
6061
  "description": "Attaches to the box the first time there is one to attach to."
6062
6062
  },
6063
+ {
6064
+ "kind": "method",
6065
+ "name": "across",
6066
+ "privacy": "private",
6067
+ "return": {
6068
+ "type": {
6069
+ "text": "boolean"
6070
+ }
6071
+ },
6072
+ "description": "Whether the scroll axis is the inline one."
6073
+ },
6074
+ {
6075
+ "kind": "method",
6076
+ "name": "scrolled",
6077
+ "privacy": "private",
6078
+ "return": {
6079
+ "type": {
6080
+ "text": "number"
6081
+ }
6082
+ },
6083
+ "parameters": [
6084
+ {
6085
+ "name": "box",
6086
+ "type": {
6087
+ "text": "HTMLElement"
6088
+ }
6089
+ }
6090
+ ],
6091
+ "description": "The box's scroll offset from the start of the set."
6092
+ },
6093
+ {
6094
+ "kind": "method",
6095
+ "name": "scrollBox",
6096
+ "privacy": "private",
6097
+ "return": {
6098
+ "type": {
6099
+ "text": "void"
6100
+ }
6101
+ },
6102
+ "parameters": [
6103
+ {
6104
+ "name": "box",
6105
+ "type": {
6106
+ "text": "HTMLElement"
6107
+ }
6108
+ },
6109
+ {
6110
+ "name": "to",
6111
+ "type": {
6112
+ "text": "number"
6113
+ }
6114
+ }
6115
+ ],
6116
+ "description": "Scrolls the box to `to` from the start of the set, and notes it. Sideways\non a right-to-left page the offset counts down from zero."
6117
+ },
6118
+ {
6119
+ "kind": "method",
6120
+ "name": "span",
6121
+ "privacy": "private",
6122
+ "return": {
6123
+ "type": {
6124
+ "text": "number"
6125
+ }
6126
+ },
6127
+ "parameters": [
6128
+ {
6129
+ "name": "box",
6130
+ "type": {
6131
+ "text": "HTMLElement"
6132
+ }
6133
+ }
6134
+ ],
6135
+ "description": "How long the box is along the scroll axis."
6136
+ },
6137
+ {
6138
+ "kind": "method",
6139
+ "name": "length",
6140
+ "privacy": "private",
6141
+ "return": {
6142
+ "type": {
6143
+ "text": "number"
6144
+ }
6145
+ },
6146
+ "parameters": [
6147
+ {
6148
+ "name": "element",
6149
+ "type": {
6150
+ "text": "Element"
6151
+ }
6152
+ }
6153
+ ],
6154
+ "description": "How long an element is along the scroll axis."
6155
+ },
6063
6156
  {
6064
6157
  "kind": "field",
6065
6158
  "name": "handleScroll",
@@ -6078,6 +6171,17 @@
6078
6171
  "privacy": "private",
6079
6172
  "readonly": true
6080
6173
  },
6174
+ {
6175
+ "kind": "method",
6176
+ "name": "scrolledNow",
6177
+ "privacy": "private",
6178
+ "return": {
6179
+ "type": {
6180
+ "text": "number"
6181
+ }
6182
+ },
6183
+ "description": "The box's scroll offset now, or the last one noted where there is no box."
6184
+ },
6081
6185
  {
6082
6186
  "kind": "method",
6083
6187
  "name": "scrolling",
@@ -6935,6 +7039,44 @@
6935
7039
  }
6936
7040
  ]
6937
7041
  },
7042
+ {
7043
+ "kind": "javascript-module",
7044
+ "path": "src/events/before-paste.ts",
7045
+ "declarations": [
7046
+ {
7047
+ "kind": "class",
7048
+ "description": "Cards are about to be pasted onto a board, and can be refused.\n\nThe board adds the cards and connections in the detail by default, as\n`helix-canvas-node` and `helix-canvas-edge` elements. A page that renders\nits cards from its own state calls `preventDefault()` here and adds the\ndetail's document to that state, through `fromJsonCanvas`.",
7049
+ "name": "HelixBeforePasteEvent",
7050
+ "members": [
7051
+ {
7052
+ "kind": "field",
7053
+ "name": "eventName",
7054
+ "type": {
7055
+ "text": "string"
7056
+ },
7057
+ "static": true,
7058
+ "readonly": true,
7059
+ "default": "'helix-before-paste'"
7060
+ }
7061
+ ],
7062
+ "superclass": {
7063
+ "name": "CustomEvent",
7064
+ "module": "src/events/before-paste.ts"
7065
+ },
7066
+ "helix": {}
7067
+ }
7068
+ ],
7069
+ "exports": [
7070
+ {
7071
+ "kind": "js",
7072
+ "name": "HelixBeforePasteEvent",
7073
+ "declaration": {
7074
+ "name": "HelixBeforePasteEvent",
7075
+ "module": "src/events/before-paste.ts"
7076
+ }
7077
+ }
7078
+ ]
7079
+ },
6938
7080
  {
6939
7081
  "kind": "javascript-module",
6940
7082
  "path": "src/events/before-pin.ts",
@@ -8096,6 +8238,14 @@
8096
8238
  "module": "./details.js"
8097
8239
  }
8098
8240
  },
8241
+ {
8242
+ "kind": "js",
8243
+ "name": "PasteDetail",
8244
+ "declaration": {
8245
+ "name": "PasteDetail",
8246
+ "module": "./details.js"
8247
+ }
8248
+ },
8099
8249
  {
8100
8250
  "kind": "js",
8101
8251
  "name": "PressDetail",
@@ -8408,6 +8558,14 @@
8408
8558
  "module": "src/events/events.ts"
8409
8559
  }
8410
8560
  },
8561
+ {
8562
+ "kind": "js",
8563
+ "name": "HelixBeforePasteEvent",
8564
+ "declaration": {
8565
+ "name": "HelixBeforePasteEvent",
8566
+ "module": "src/events/events.ts"
8567
+ }
8568
+ },
8411
8569
  {
8412
8570
  "kind": "js",
8413
8571
  "name": "HelixBeforePinEvent",
@@ -8792,6 +8950,14 @@
8792
8950
  "module": "src/events/events.ts"
8793
8951
  }
8794
8952
  },
8953
+ {
8954
+ "kind": "js",
8955
+ "name": "HelixPastedEvent",
8956
+ "declaration": {
8957
+ "name": "HelixPastedEvent",
8958
+ "module": "src/events/events.ts"
8959
+ }
8960
+ },
8795
8961
  {
8796
8962
  "kind": "js",
8797
8963
  "name": "HelixPinEvent",
@@ -9888,7 +10054,7 @@
9888
10054
  "type": {
9889
10055
  "text": "ReadonlySet<string>"
9890
10056
  },
9891
- "default": "new Set( ( 'action activate aura-fallback band before-activate before-change before-close ' + 'before-collapse before-edit before-move before-open before-panel-resize before-pin ' + 'before-reconnect before-relabel before-reorder before-resize before-split before-toggle ' + 'canvas-edit canvas-pan canvas-view canvas-zoom caret clear collapse complete ' + 'connect console copy copy-error create dismiss edit end expand facet filter ' + 'filtered graph-filter graph-layout graph-pan graph-part-change graph-ready ' + 'graph-signal graph-zoom history load load-more map-cluster map-pan map-part-change ' + 'map-ready map-rotate map-tilt map-zoom move navigate next panel-resize pin press ' + 'preview previous range-change read reading-change reading-press reconnect reject ' + 'relabel remove reorder ' + 'reordered resize resized sample-end sample-start scrolled select selected send ' + 'shortcut sort sorted split strands-fallback toggle tour-ask tour-status typing' ) .split(' ') .map((name) => `helix-${name}`), )",
10057
+ "default": "new Set( ( 'action activate aura-fallback band before-activate before-change before-close ' + 'before-collapse before-edit before-move before-open before-panel-resize before-paste before-pin ' + 'before-reconnect before-relabel before-reorder before-resize before-split before-toggle ' + 'canvas-edit canvas-pan canvas-view canvas-zoom caret clear collapse complete ' + 'connect console copy copy-error create dismiss edit end expand facet filter ' + 'filtered graph-filter graph-layout graph-pan graph-part-change graph-ready ' + 'graph-signal graph-zoom history load load-more map-cluster map-pan map-part-change ' + 'map-ready map-rotate map-tilt map-zoom move navigate next panel-resize pasted pin press ' + 'preview previous range-change read reading-change reading-press reconnect reject ' + 'relabel remove reorder ' + 'reordered resize resized sample-end sample-start scrolled select selected send ' + 'shortcut sort sorted split strands-fallback toggle tour-ask tour-status typing' ) .split(' ') .map((name) => `helix-${name}`), )",
9892
10058
  "description": "Every name this library dispatches.\n\nExported so a runtime check can ask whether a name is one of ours without\nkeeping a second list of them. `HelixElement` uses it to tell a consumer that\nthe handler prop they wrote is attached to nothing.\n\n**Written as strings, in a module of its own, and not read off the\nclasses in `registry`.** Every element carries this set through its React\nprop audit, and a set read off the classes kept every class alive in every\nbundle: a Badge shipped all eighty of them. As strings, a family ships the\nclasses it dispatches and no others. `events.pure.spec.ts` holds this list to\nthe classes, so a class added without its name here fails.\n\n**Without the shared `helix-`, and separated by spaces.** Every element carries this\nlist, and as quoted strings each paid its quotes, its comma and the prefix."
9893
10059
  }
9894
10060
  ],
@@ -10017,6 +10183,44 @@
10017
10183
  }
10018
10184
  ]
10019
10185
  },
10186
+ {
10187
+ "kind": "javascript-module",
10188
+ "path": "src/events/pasted.ts",
10189
+ "declarations": [
10190
+ {
10191
+ "kind": "class",
10192
+ "description": "Cards and connections were pasted onto a board, which now holds them.",
10193
+ "name": "HelixPastedEvent",
10194
+ "members": [
10195
+ {
10196
+ "kind": "field",
10197
+ "name": "eventName",
10198
+ "type": {
10199
+ "text": "string"
10200
+ },
10201
+ "static": true,
10202
+ "readonly": true,
10203
+ "default": "'helix-pasted'"
10204
+ }
10205
+ ],
10206
+ "superclass": {
10207
+ "name": "CustomEvent",
10208
+ "module": "src/events/pasted.ts"
10209
+ },
10210
+ "helix": {}
10211
+ }
10212
+ ],
10213
+ "exports": [
10214
+ {
10215
+ "kind": "js",
10216
+ "name": "HelixPastedEvent",
10217
+ "declaration": {
10218
+ "name": "HelixPastedEvent",
10219
+ "module": "src/events/pasted.ts"
10220
+ }
10221
+ }
10222
+ ]
10223
+ },
10020
10224
  {
10021
10225
  "kind": "javascript-module",
10022
10226
  "path": "src/events/pin.ts",
@@ -11967,6 +12171,42 @@
11967
12171
  "description": "The message the browser would show for the control, or an empty string.",
11968
12172
  "readonly": true
11969
12173
  },
12174
+ {
12175
+ "kind": "field",
12176
+ "name": "updateRunning",
12177
+ "type": {
12178
+ "text": "boolean"
12179
+ },
12180
+ "privacy": "private",
12181
+ "default": "false",
12182
+ "description": "Whether an update is running, so `runPendingUpdate` never starts one inside another."
12183
+ },
12184
+ {
12185
+ "kind": "method",
12186
+ "name": "performUpdate",
12187
+ "privacy": "protected",
12188
+ "return": {
12189
+ "type": {
12190
+ "text": "void"
12191
+ }
12192
+ },
12193
+ "description": "The first update, timed where a page asked to measure what the components\ncost, and a property read where it did not: see `reportRender`.",
12194
+ "inheritedFrom": {
12195
+ "name": "HelixElement",
12196
+ "module": "src/helix-element/helix-element.ts"
12197
+ }
12198
+ },
12199
+ {
12200
+ "kind": "method",
12201
+ "name": "runPendingUpdate",
12202
+ "privacy": "private",
12203
+ "return": {
12204
+ "type": {
12205
+ "text": "void"
12206
+ }
12207
+ },
12208
+ "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."
12209
+ },
11970
12210
  {
11971
12211
  "kind": "field",
11972
12212
  "name": "willValidate",
@@ -11984,7 +12224,17 @@
11984
12224
  "text": "boolean"
11985
12225
  }
11986
12226
  },
11987
- "description": "Returns whether the control is valid, and fires `invalid` on it when it is\nnot."
12227
+ "description": "Returns whether the control is valid, and fires `invalid` on it when it is\nnot. The control shows no message for it: `reportValidity()` does that."
12228
+ },
12229
+ {
12230
+ "kind": "field",
12231
+ "name": "checkInProgress",
12232
+ "type": {
12233
+ "text": "boolean"
12234
+ },
12235
+ "privacy": "private",
12236
+ "default": "false",
12237
+ "description": "Whether `checkValidity()` is running, set only for the `invalid` it fires."
11988
12238
  },
11989
12239
  {
11990
12240
  "kind": "method",
@@ -12423,21 +12673,6 @@
12423
12673
  "name": "HelixElement",
12424
12674
  "module": "src/helix-element/helix-element.ts"
12425
12675
  }
12426
- },
12427
- {
12428
- "kind": "method",
12429
- "name": "performUpdate",
12430
- "privacy": "protected",
12431
- "return": {
12432
- "type": {
12433
- "text": "void"
12434
- }
12435
- },
12436
- "description": "The first update, timed where a page asked to measure what the components\ncost, and a property read where it did not: see `reportRender`.",
12437
- "inheritedFrom": {
12438
- "name": "HelixElement",
12439
- "module": "src/helix-element/helix-element.ts"
12440
- }
12441
12676
  }
12442
12677
  ],
12443
12678
  "events": [],
@@ -13476,6 +13711,14 @@
13476
13711
  "module": "./events/events.js"
13477
13712
  }
13478
13713
  },
13714
+ {
13715
+ "kind": "js",
13716
+ "name": "HelixBeforePasteEvent",
13717
+ "declaration": {
13718
+ "name": "HelixBeforePasteEvent",
13719
+ "module": "./events/events.js"
13720
+ }
13721
+ },
13479
13722
  {
13480
13723
  "kind": "js",
13481
13724
  "name": "HelixBeforePinEvent",
@@ -13860,6 +14103,14 @@
13860
14103
  "module": "./events/events.js"
13861
14104
  }
13862
14105
  },
14106
+ {
14107
+ "kind": "js",
14108
+ "name": "HelixPastedEvent",
14109
+ "declaration": {
14110
+ "name": "HelixPastedEvent",
14111
+ "module": "./events/events.js"
14112
+ }
14113
+ },
13863
14114
  {
13864
14115
  "kind": "js",
13865
14116
  "name": "HelixPinEvent",
@@ -14180,6 +14431,14 @@
14180
14431
  "module": "./events/events.js"
14181
14432
  }
14182
14433
  },
14434
+ {
14435
+ "kind": "js",
14436
+ "name": "PasteDetail",
14437
+ "declaration": {
14438
+ "name": "PasteDetail",
14439
+ "module": "./events/events.js"
14440
+ }
14441
+ },
14183
14442
  {
14184
14443
  "kind": "js",
14185
14444
  "name": "PressDetail",
@@ -20275,7 +20534,7 @@
20275
20534
  },
20276
20535
  "privacy": "private",
20277
20536
  "default": "false",
20278
- "description": "Whether the reader has had a go at the field yet.\n\n**Not `:user-invalid`**, which is the platform's own answer to \"is this\nfield wrong *yet*\" and cannot work here, for a structural reason rather\nthan a browser bug: it tracks interaction with the element it is on, and\nnobody interacts with this element. The reader types into an `<input>`\ninside its shadow root, so the host's interaction flag is never set and\nthe selector never matches. Measured after submitting an invalid required\nfield and again after editing one and leaving it: false both times, while\n`:invalid` matched both times.\n\nSo the moments are listened for instead, and both of them demonstrably\nreach the host: `invalid` when a form is submitted with this control\nfailing, and `change` when the reader has edited and committed. Focus\nalone is deliberately not one of them. Tabbing past an empty required\nfield should not be answered with an error.\n\n**A composite control has to construct its own `change`, and this mixin\ndeliberately does not do it for them.** A native `change` is\n`composed: false`, so a control built from several inner inputs never\nrelays one to its host: `Input` gets this free from its one control,\nwhile a `PinInput` of four boxes has no inner `change` that means the\ncode is entered, so a half-entered code is never `touched` and can never\nsay what is wrong with it.\n\n**The obvious shared fix is for `touched` to take a `focusout`, and it is\nthe wrong one**, because it is the paragraph above undone: a `focusout`\nfires on the reader who tabbed past and typed nothing, which is exactly\nthe case this refuses to call an error. What a composite needs is not a\nfocus moment, it is the native meaning of `change`, \"edited, then left\",\nand the editing half is the part a mixin up here cannot see.\n\n**Nor is there one shape to extract yet.** The two composites solve it\ndifferently and both are right: `PinInput` is focus-driven, taking a\nbaseline when focus arrives from outside the row and announcing on the\nway out if it moved, and `DateInput` is value-driven, stopping its inner\n`change` and announcing once the date itself moves. Only one of them has\na leaving moment at all, so generalizing from the pair is generalizing\nfrom one. The trigger for lifting it is a third control that needs the\nfocus-driven shape, at which point what moves up is the\nbaseline-and-commit pair rather than `touched`."
20537
+ "description": "Whether the reader has had a go at the field yet.\n\n**Not `:user-invalid`**, which is the platform's own answer to \"is this\nfield wrong *yet*\" and cannot work here, for a structural reason rather\nthan a browser bug: it tracks interaction with the element it is on, and\nnobody interacts with this element. The reader types into an `<input>`\ninside its shadow root, so the host's interaction flag is never set and\nthe selector never matches. Measured after submitting an invalid required\nfield and again after editing one and leaving it: false both times, while\n`:invalid` matched both times.\n\nSo the moments are listened for instead, and both of them demonstrably\nreach the host: `invalid` when a form is submitted with this control\nfailing, and `change` when the reader has edited and committed. The\n`invalid` a script's `checkValidity()` fires is not one of them, as it\nis not for a native control. Focus\nalone is deliberately not one of them. Tabbing past an empty required\nfield should not be answered with an error.\n\n**A composite control has to construct its own `change`, and this mixin\ndeliberately does not do it for them.** A native `change` is\n`composed: false`, so a control built from several inner inputs never\nrelays one to its host: `Input` gets this free from its one control,\nwhile a `PinInput` of four boxes has no inner `change` that means the\ncode is entered, so a half-entered code is never `touched` and can never\nsay what is wrong with it.\n\n**The obvious shared fix is for `touched` to take a `focusout`, and it is\nthe wrong one**, because it is the paragraph above undone: a `focusout`\nfires on the reader who tabbed past and typed nothing, which is exactly\nthe case this refuses to call an error. What a composite needs is not a\nfocus moment, it is the native meaning of `change`, \"edited, then left\",\nand the editing half is the part a mixin up here cannot see.\n\n**Nor is there one shape to extract yet.** The two composites solve it\ndifferently and both are right: `PinInput` is focus-driven, taking a\nbaseline when focus arrives from outside the row and announcing on the\nway out if it moved, and `DateInput` is value-driven, stopping its inner\n`change` and announcing once the date itself moves. Only one of them has\na leaving moment at all, so generalizing from the pair is generalizing\nfrom one. The trigger for lifting it is a third control that needs the\nfocus-driven shape, at which point what moves up is the\nbaseline-and-commit pair rather than `touched`."
20279
20538
  },
20280
20539
  {
20281
20540
  "kind": "field",
@@ -21564,7 +21823,7 @@
21564
21823
  },
21565
21824
  {
21566
21825
  "kind": "mixin",
21567
- "description": "Grafts the segmented field React Aria's DateField and TimeField are, and Web\nAwesome's time input has, onto a picker field: one `spinbutton` for each part\nof the date or the time, in the order the locale writes them.\n\n**Typing into a text field asks the reader to know the format.** Date Input\nand Time Input read ISO, `2026-08-04` and `14:30`, because `Intl` formats and\ndoes not parse, and a reader in Germany who types `4.8.2026` is refused. A\nsegment needs no format: the month is a month, set with digits or the arrow\nkeys, and the layout puts it where the locale does, so the field reads\n`04.08.2026` in German and `08/04/2026` in English with nothing to parse.\n\n**What a segment does with a key**, which is React Aria's: digits fill it and\nmove on once it cannot take another, Up and Down step it and wrap, Page Up\nand Page Down step it by a page, a week of days, five years or fifteen\nminutes, Home and End go to its ends, Backspace and Delete empty it, Left and Right move between\nsegments along the line of text, and Alt+Down opens the picker. Each segment\nis a tab stop, as each is in React Aria.\n\n**The value is whole or empty.** A date with no year is no date, so the value\nstays empty while any segment is, and the field reports `badInput` as a\nnative date input does with a half-typed date. The arithmetic is\n`date-segments`, so this draws and listens and nothing else.\n\n**Opt-in, with `segmented`**, because the text field is what every example and\nevery form already holds, and a row of segments draws differently.",
21826
+ "description": "Grafts the segmented field React Aria's DateField and TimeField are, and Web\nAwesome's time input has, onto a picker field: one `spinbutton` for each part\nof the date or the time, in the order the locale writes them.\n\n**Typing into a text field asks the reader to know the format.** Date Input\nand Time Input read ISO, `2026-08-04` and `14:30`, because `Intl` formats and\ndoes not parse, and a reader in Germany who types `4.8.2026` is refused. A\nsegment needs no format: the month is a month, set with digits or the arrow\nkeys, and the layout puts it where the locale does, so the field reads\n`04.08.2026` in German and `08/04/2026` in English with nothing to parse.\n\n**What a segment does with a key**, which is React Aria's: digits fill it and\nmove on once it cannot take another, Up and Down step it and wrap, Page Up\nand Page Down step it by a page, a week of days, five years or fifteen\nminutes, Home and End go to its ends, Backspace and Delete take off its\nlast digit or move back from an empty segment, Left and Right move between\nsegments along the line of text, and Alt+Down opens the picker. Each segment\nis a tab stop, as each is in React Aria.\n\n**The value is whole or empty.** A date with no year is no date, so the value\nstays empty while any segment is, and the field reports `badInput` as a\nnative date input does with a half-typed date. The arithmetic is\n`date-segments`, so this draws and listens and nothing else.\n\n**Opt-in, with `segmented`**, because the text field is what every example and\nevery form already holds, and a row of segments draws differently.",
21568
21827
  "name": "MixinSegmentedField",
21569
21828
  "members": [
21570
21829
  {
@@ -21941,6 +22200,25 @@
21941
22200
  ],
21942
22201
  "description": "Applies an editing key to the segment, the arrows, Backspace, a digit or\na letter, and says whether it edited anything."
21943
22202
  },
22203
+ {
22204
+ "kind": "method",
22205
+ "name": "erase",
22206
+ "privacy": "private",
22207
+ "return": {
22208
+ "type": {
22209
+ "text": "void"
22210
+ }
22211
+ },
22212
+ "parameters": [
22213
+ {
22214
+ "name": "element",
22215
+ "type": {
22216
+ "text": "HTMLElement"
22217
+ }
22218
+ }
22219
+ ],
22220
+ "description": "Takes the last digit off the segment, or moves to the segment before it\nwhere this one is already empty, as React Aria's segments do. A reader\nclearing a date from its end holds Backspace and it empties right to\nleft."
22221
+ },
21944
22222
  {
21945
22223
  "kind": "field",
21946
22224
  "name": "composing",
@@ -26611,7 +26889,7 @@
26611
26889
  }
26612
26890
  }
26613
26891
  ],
26614
- "description": "What an editing key does to a segment, or `null` for a key that edits\nnothing.\n\nThe arrows step it and wrap, Page Up and Page Down step it by a page, Home\nand End take it to its lowest and highest, Backspace and Delete empty it, a\ndigit is typed as `typeDigit` types it, and on the meridiem a letter picks\nthe half whose word starts with it. `advance` says the segment is full and\nfocus moves on."
26892
+ "description": "What an editing key does to a segment, or `null` for a key that edits\nnothing.\n\nThe arrows step it and wrap, Page Up and Page Down step it by a page, Home\nand End take it to its lowest and highest, Backspace and Delete take off its\nlast digit and empty it from the last one, a\ndigit is typed as `typeDigit` types it, and on the meridiem a letter picks\nthe half whose word starts with it. `advance` says the segment is full and\nfocus moves on."
26615
26893
  },
26616
26894
  {
26617
26895
  "kind": "function",
@@ -33311,7 +33589,7 @@
33311
33589
  "type": {
33312
33590
  "text": "string"
33313
33591
  },
33314
- "default": "'0.166.0'",
33592
+ "default": "'0.168.0'",
33315
33593
  "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."
33316
33594
  }
33317
33595
  ],
@@ -33871,6 +34149,6 @@
33871
34149
  ],
33872
34150
  "helix": {
33873
34151
  "package": "@fusion.dev/helix-core",
33874
- "version": "0.166.0"
34152
+ "version": "0.168.0"
33875
34153
  }
33876
34154
  }
@@ -13,6 +13,13 @@ export interface VirtualizeOptions {
13
13
  * its own size. Zero by default.
14
14
  */
15
15
  gap?: () => number;
16
+ /**
17
+ * Whether the set runs across the box, scrolled sideways. The scroll axis is
18
+ * then the inline one: offsets are `scrollLeft` from the start of the line,
19
+ * which runs right to left on a right-to-left page, and items are measured
20
+ * by their width. Down the box by default.
21
+ */
22
+ horizontal?: () => boolean;
16
23
  /**
17
24
  * Rows kept either side of what fits, so a scroll reveals something drawn
18
25
  * rather than something blank. Four by default.
@@ -206,9 +213,24 @@ export declare class VirtualizeController implements ReactiveController {
206
213
  hostDisconnected(): void;
207
214
  /** Attaches to the box the first time there is one to attach to. */
208
215
  private watch;
216
+ /** Whether the scroll axis is the inline one. */
217
+ private across;
218
+ /** The box's scroll offset from the start of the set. */
219
+ private scrolled;
220
+ /**
221
+ * Scrolls the box to `to` from the start of the set, and notes it. Sideways
222
+ * on a right-to-left page the offset counts down from zero.
223
+ */
224
+ private scrollBox;
225
+ /** How long the box is along the scroll axis. */
226
+ private span;
227
+ /** How long an element is along the scroll axis. */
228
+ private length;
209
229
  private readonly handleScroll;
210
230
  private readonly handleTouchStart;
211
231
  private readonly handleTouchEnd;
232
+ /** The box's scroll offset now, or the last one noted where there is no box. */
233
+ private scrolledNow;
212
234
  /**
213
235
  * Whether the reader is scrolling: a finger is on the box, the box moved
214
236
  * within the last 150 milliseconds, or it has moved and its `scroll` event
@@ -21,7 +21,7 @@ export class VirtualizeController {
21
21
  if (box) {
22
22
  this.noteClamp(box);
23
23
  }
24
- const top = box?.scrollTop ?? 0;
24
+ const top = box ? this.scrolled(box) : 0;
25
25
  if (top !== this.lastTop) {
26
26
  this.heading = top > this.lastTop ? 1 : -1;
27
27
  this.lastTop = top;
@@ -90,29 +90,53 @@ export class VirtualizeController {
90
90
  box.addEventListener('touchend', this.handleTouchEnd, { passive: true });
91
91
  box.addEventListener('touchcancel', this.handleTouchEnd, { passive: true });
92
92
  box.addEventListener('scrollend', this.handleTouchEnd, { passive: true });
93
- this.lastTop = box.scrollTop;
93
+ this.lastTop = this.scrolled(box);
94
94
  box.setAttribute('data-virtual', '');
95
95
  this.watching = true;
96
96
  }
97
+ across() {
98
+ return this.options.horizontal?.() ?? false;
99
+ }
100
+ scrolled(box) {
101
+ return this.across() ? Math.abs(box.scrollLeft) : box.scrollTop;
102
+ }
103
+ scrollBox(box, to) {
104
+ if (this.across()) {
105
+ box.scrollLeft = getComputedStyle(box).direction === 'rtl' ? -to : to;
106
+ }
107
+ else {
108
+ box.scrollTop = to;
109
+ }
110
+ this.lastTop = this.scrolled(box);
111
+ }
112
+ span(box) {
113
+ return this.across() ? box.clientWidth : box.clientHeight;
114
+ }
115
+ length(element) {
116
+ const box = element.getBoundingClientRect();
117
+ return this.across() ? box.width : box.height;
118
+ }
119
+ scrolledNow() {
120
+ const box = this.options.container();
121
+ return box ? this.scrolled(box) : this.lastTop;
122
+ }
97
123
  scrolling() {
98
- return (this.touching ||
99
- this.resting !== undefined ||
100
- (this.options.container()?.scrollTop ?? this.lastTop) !== this.lastTop);
124
+ return this.touching || this.resting !== undefined || this.scrolledNow() !== this.lastTop;
101
125
  }
102
126
  noteClamp(box) {
103
127
  const was = this.lastTop;
104
128
  if (!this.touching &&
105
- box.scrollTop < was &&
106
- box.scrollTop >= box.scrollHeight - box.clientHeight - 1) {
107
- this.lastTop = box.scrollTop;
129
+ this.scrolled(box) < was &&
130
+ this.scrolled(box) >=
131
+ (this.across() ? box.scrollWidth : box.scrollHeight) - this.span(box) - 1) {
132
+ this.lastTop = this.scrolled(box);
108
133
  }
109
134
  return was;
110
135
  }
111
136
  square() {
112
137
  const box = this.options.container();
113
138
  if (this.drift > 0 && box) {
114
- box.scrollTop -= this.drift;
115
- this.lastTop = box.scrollTop;
139
+ this.scrollBox(box, this.scrolled(box) - this.drift);
116
140
  }
117
141
  else if (this.drift !== 0) {
118
142
  this.owed -= this.drift;
@@ -121,10 +145,10 @@ export class VirtualizeController {
121
145
  this.drift = 0;
122
146
  }
123
147
  offset(box) {
124
- return box.scrollTop + this.owed - this.drift;
148
+ return this.scrolled(box) + this.owed - this.drift;
125
149
  }
126
150
  lead(box) {
127
- return this.heading === 0 ? 0 : box.clientHeight;
151
+ return this.heading === 0 ? 0 : this.span(box);
128
152
  }
129
153
  measure() {
130
154
  this.watch();
@@ -168,7 +192,7 @@ export class VirtualizeController {
168
192
  const extra = Math.ceil(this.lead(box) / size);
169
193
  const at = Math.min(Math.floor(this.offset(box) / size), total);
170
194
  const first = Math.max(0, at - overscan - (this.heading < 0 ? extra : 0));
171
- const fits = Math.ceil(box.clientHeight / size) + overscan * 2 + (this.heading === 0 ? 0 : extra);
195
+ const fits = Math.ceil(this.span(box) / size) + overscan * 2 + (this.heading === 0 ? 0 : extra);
172
196
  const last = Math.min(total, first + fits);
173
197
  return { after: Math.max(0, (total - last) * size), before: first * size, first, last };
174
198
  }
@@ -183,7 +207,7 @@ export class VirtualizeController {
183
207
  const estimate = Math.max(1, this.options.estimate());
184
208
  const heights = new Map();
185
209
  for (const { element, index } of this.options.rows?.() ?? []) {
186
- heights.set(index, (heights.get(index) ?? 0) + element.getBoundingClientRect().height);
210
+ heights.set(index, (heights.get(index) ?? 0) + this.length(element));
187
211
  }
188
212
  const seen = [...heights.values()].filter((height) => height > 0);
189
213
  if (seen.length === 0) {
@@ -196,9 +220,8 @@ export class VirtualizeController {
196
220
  this.sample = { estimate, size };
197
221
  if (Math.abs(size - was) >= 0.5) {
198
222
  const box = this.options.container();
199
- if (box && box.scrollTop > 0) {
200
- box.scrollTop = (box.scrollTop / was) * size;
201
- this.lastTop = box.scrollTop;
223
+ if (box && this.scrolled(box) > 0) {
224
+ this.scrollBox(box, (this.scrolled(box) / was) * size);
202
225
  }
203
226
  this.settling = 0;
204
227
  if (!uneven) {
@@ -219,7 +242,7 @@ export class VirtualizeController {
219
242
  const lead = this.lead(box);
220
243
  const from = this.heading < 0 ? top - lead : top;
221
244
  const first = Math.max(0, this.heights.indexAt(from, total) - overscan);
222
- const bottom = top + box.clientHeight + (this.heading > 0 ? lead : 0);
245
+ const bottom = top + this.span(box) + (this.heading > 0 ? lead : 0);
223
246
  let last = this.heights.indexAt(top, total);
224
247
  while (last < total && this.heights.before(last, total) < bottom) {
225
248
  last += 1;
@@ -239,7 +262,7 @@ export class VirtualizeController {
239
262
  return { first: 0, last: 0 };
240
263
  }
241
264
  const top = Math.max(0, this.offset(box));
242
- const bottom = top + box.clientHeight;
265
+ const bottom = top + this.span(box);
243
266
  if (!this.measuring()) {
244
267
  const size = this.line();
245
268
  const first = Math.min(Math.floor(top / size), total);
@@ -259,15 +282,16 @@ export class VirtualizeController {
259
282
  return 0;
260
283
  }
261
284
  const length = this.measuring() ? this.heights.total(total) : total * this.line();
262
- return Math.max(0, length - Math.max(0, this.offset(box)) - box.clientHeight);
285
+ return Math.max(0, length - Math.max(0, this.offset(box)) - this.span(box));
263
286
  }
264
287
  endInSight(length = Math.max(0, this.options.count())) {
288
+ const box = this.options.container();
265
289
  if (this.options.count() <= 0 ||
266
290
  !endInSight({
267
291
  askedAt: this.askedAt,
268
292
  count: length,
269
293
  remaining: this.remaining(),
270
- screen: this.options.container()?.clientHeight ?? 0,
294
+ screen: box ? this.span(box) : 0,
271
295
  })) {
272
296
  return false;
273
297
  }
@@ -289,8 +313,7 @@ export class VirtualizeController {
289
313
  if (this.owed !== 0) {
290
314
  const box = this.options.container();
291
315
  if (box) {
292
- box.scrollTop += this.owed;
293
- this.lastTop = box.scrollTop;
316
+ this.scrollBox(box, this.scrolled(box) + this.owed);
294
317
  }
295
318
  this.owed = 0;
296
319
  }
@@ -313,7 +336,7 @@ export class VirtualizeController {
313
336
  const was = this.heights.before(anchor, total);
314
337
  let news = false;
315
338
  for (const { element, index } of rows) {
316
- if (this.heights.record(index, element.getBoundingClientRect().height + this.gap())) {
339
+ if (this.heights.record(index, this.length(element) + this.gap())) {
317
340
  news = true;
318
341
  }
319
342
  }
@@ -327,8 +350,7 @@ export class VirtualizeController {
327
350
  this.drift -= moved;
328
351
  }
329
352
  else {
330
- box.scrollTop = from + moved;
331
- this.lastTop = box.scrollTop;
353
+ this.scrollBox(box, from + moved);
332
354
  }
333
355
  }
334
356
  this.measure();
@@ -358,7 +380,9 @@ export class VirtualizeController {
358
380
  }
359
381
  const top = this.offsetOf(index);
360
382
  const size = this.sizeOf(index);
361
- const room = box.clientHeight - inset;
383
+ const span = this.span(box);
384
+ const at = this.scrolled(box);
385
+ const room = span - inset;
362
386
  let to;
363
387
  if (align === 'start') {
364
388
  to = top;
@@ -367,24 +391,25 @@ export class VirtualizeController {
367
391
  to = top - inset - (room - size) / 2;
368
392
  }
369
393
  else if (align === 'end') {
370
- to = top + size + inset - box.clientHeight;
394
+ to = top + size + inset - span;
371
395
  }
372
- else if (top < box.scrollTop) {
396
+ else if (top < at) {
373
397
  to = top;
374
398
  }
375
- else if (top + size + inset > box.scrollTop + box.clientHeight) {
376
- to = top + size + inset - box.clientHeight;
399
+ else if (top + size + inset > at + span) {
400
+ to = top + size + inset - span;
377
401
  }
378
402
  else {
379
403
  return;
380
404
  }
381
405
  const next = Math.max(0, to);
382
406
  if (behavior && typeof box.scrollTo === 'function') {
383
- box.scrollTo({ behavior, top: next });
407
+ const across = this.across();
408
+ const rtl = across && getComputedStyle(box).direction === 'rtl';
409
+ box.scrollTo(across ? { behavior, left: rtl ? -next : next } : { behavior, top: next });
384
410
  }
385
411
  else {
386
- box.scrollTop = next;
387
- this.lastTop = box.scrollTop;
412
+ this.scrollBox(box, next);
388
413
  this.target = index;
389
414
  }
390
415
  }
@@ -0,0 +1,13 @@
1
+ import type { PasteDetail } from './details.js';
2
+ /**
3
+ * Cards are about to be pasted onto a board, and can be refused.
4
+ *
5
+ * The board adds the cards and connections in the detail by default, as
6
+ * `helix-canvas-node` and `helix-canvas-edge` elements. A page that renders
7
+ * its cards from its own state calls `preventDefault()` here and adds the
8
+ * detail's document to that state, through `fromJsonCanvas`.
9
+ */
10
+ export declare class HelixBeforePasteEvent extends CustomEvent<PasteDetail> {
11
+ static readonly eventName = "helix-before-paste";
12
+ constructor(detail: PasteDetail);
13
+ }
@@ -0,0 +1,11 @@
1
+ export class HelixBeforePasteEvent extends CustomEvent {
2
+ static { this.eventName = 'helix-before-paste'; }
3
+ constructor(detail) {
4
+ super(HelixBeforePasteEvent.eventName, {
5
+ bubbles: true,
6
+ cancelable: true,
7
+ composed: true,
8
+ detail,
9
+ });
10
+ }
11
+ }
@@ -62,6 +62,45 @@ export interface ConnectDetail {
62
62
  /** The side nearest where they let go, where they let go over a card. */
63
63
  toSide?: 'top' | 'right' | 'bottom' | 'left' | undefined;
64
64
  }
65
+ /** A side of a card, as JSON Canvas names it. */
66
+ type PasteSide = 'top' | 'right' | 'bottom' | 'left';
67
+ /**
68
+ * What a paste adds to a board: a JSON Canvas document whose ids the board
69
+ * does not hold yet, its cards moved clear of the ones they were copied from.
70
+ * The shape is the format's, which `fromJsonCanvas` reads.
71
+ */
72
+ export interface PasteDetail {
73
+ document: {
74
+ edges?: Array<{
75
+ color?: string | undefined;
76
+ fromEnd?: 'none' | 'arrow' | undefined;
77
+ fromNode: string;
78
+ fromSide?: PasteSide | undefined;
79
+ id: string;
80
+ label?: string | undefined;
81
+ path?: 'curve' | 'straight' | 'step' | undefined;
82
+ toEnd?: 'none' | 'arrow' | undefined;
83
+ toNode: string;
84
+ toSide?: PasteSide | undefined;
85
+ }>;
86
+ nodes?: Array<{
87
+ background?: string | undefined;
88
+ backgroundStyle?: 'cover' | 'ratio' | 'repeat' | undefined;
89
+ color?: string | undefined;
90
+ file?: string | undefined;
91
+ height: number;
92
+ id: string;
93
+ label?: string | undefined;
94
+ subpath?: string | undefined;
95
+ text?: string | undefined;
96
+ type: 'text' | 'file' | 'link' | 'group';
97
+ url?: string | undefined;
98
+ width: number;
99
+ x: number;
100
+ y: number;
101
+ }>;
102
+ };
103
+ }
65
104
  /** Where a connection's ends are going, and where they were. */
66
105
  export interface ReconnectDetail {
67
106
  /** The connection's name, where it has one. */
@@ -522,3 +561,4 @@ export interface ShortcutDetail {
522
561
  */
523
562
  keys: string;
524
563
  }
564
+ export {};
@@ -40,6 +40,7 @@ import { HelixBeforeEditEvent } from './before-edit.js';
40
40
  import { HelixBeforeMoveEvent } from './before-move.js';
41
41
  import { HelixBeforeOpenEvent } from './before-open.js';
42
42
  import { HelixBeforePanelResizeEvent } from './before-panel-resize.js';
43
+ import { HelixBeforePasteEvent } from './before-paste.js';
43
44
  import { HelixBeforePinEvent } from './before-pin.js';
44
45
  import { HelixBeforeReconnectEvent } from './before-reconnect.js';
45
46
  import { HelixBeforeRelabelEvent } from './before-relabel.js';
@@ -88,6 +89,7 @@ import { HelixMoveEvent } from './move.js';
88
89
  import { HelixNavigateEvent } from './navigate.js';
89
90
  import { HelixNextEvent } from './next.js';
90
91
  import { HelixPanelResizeEvent } from './panel-resize.js';
92
+ import { HelixPastedEvent } from './pasted.js';
91
93
  import { HelixPinEvent } from './pin.js';
92
94
  import { HelixPressEvent } from './press.js';
93
95
  import { HelixPreviewEvent } from './preview.js';
@@ -119,9 +121,9 @@ import { HelixToggleEvent } from './toggle.js';
119
121
  import { HelixTourAskEvent } from './tour-ask.js';
120
122
  import { HelixTourStatusEvent } from './tour-status.js';
121
123
  import { HelixTypingEvent } from './typing.js';
122
- export type { ActivateDetail, AuraFallbackDetail, BandDetail, CanvasView, CaretDetail, ChangeDetail, CompleteDetail, ConnectDetail, ConsoleDetail, CopyDetail, CreateDetail, DismissDetail, FacetDetail, FilterDetail, GraphFilterDetail, GraphLayoutDetail, GraphPartChangeDetail, GraphSignalDetail, MapClusterDetail, MapPanDetail, MapPartChangeDetail, MapRotateDetail, MapTiltDetail, MapZoomDetail, MoveDetail, NavigateDetail, OpenDetail, PressDetail, PressPointerType, RangeDetail, ReadDetail, ReadingDetail, ReconnectDetail, Refusal, RefusedFile, RejectDetail, RelabelDetail, RemoveDetail, ReorderDetail, ResizeDetail, ScrolledDetail, SelectDetail, SendDetail, ShortcutDetail, SkipDetail, SortDetail, StrandsFallbackDetail, ToggleDetail, TourAskDetail, TourStatusDetail, TypingDetail, ViewportPanDetail, ViewportZoomDetail, } from './details.js';
124
+ export type { ActivateDetail, AuraFallbackDetail, BandDetail, CanvasView, CaretDetail, ChangeDetail, CompleteDetail, ConnectDetail, ConsoleDetail, CopyDetail, CreateDetail, DismissDetail, FacetDetail, FilterDetail, GraphFilterDetail, GraphLayoutDetail, GraphPartChangeDetail, GraphSignalDetail, MapClusterDetail, MapPanDetail, MapPartChangeDetail, MapRotateDetail, MapTiltDetail, MapZoomDetail, MoveDetail, NavigateDetail, OpenDetail, PasteDetail, PressDetail, PressPointerType, RangeDetail, ReadDetail, ReadingDetail, ReconnectDetail, Refusal, RefusedFile, RejectDetail, RelabelDetail, RemoveDetail, ReorderDetail, ResizeDetail, ScrolledDetail, SelectDetail, SendDetail, ShortcutDetail, SkipDetail, SortDetail, StrandsFallbackDetail, ToggleDetail, TourAskDetail, TourStatusDetail, TypingDetail, ViewportPanDetail, ViewportZoomDetail, } from './details.js';
123
125
  export { helixEventNames } from './names.js';
124
- export { HelixActionEvent, HelixActivateEvent, HelixAuraFallbackEvent, HelixBandEvent, HelixBeforeActivateEvent, HelixBeforeChangeEvent, HelixBeforeCloseEvent, HelixBeforeCollapseEvent, HelixBeforeEditEvent, HelixBeforeMoveEvent, HelixBeforeOpenEvent, HelixBeforePanelResizeEvent, HelixBeforePinEvent, HelixBeforeReconnectEvent, HelixBeforeRelabelEvent, HelixBeforeReorderEvent, HelixBeforeResizeEvent, HelixBeforeSplitEvent, HelixBeforeToggleEvent, HelixCanvasEditEvent, HelixCanvasPanEvent, HelixCanvasViewEvent, HelixCanvasZoomEvent, HelixCaretEvent, HelixClearEvent, HelixCollapseEvent, HelixCompleteEvent, HelixConnectEvent, HelixConsoleEvent, HelixCopyErrorEvent, HelixCopyEvent, HelixCreateEvent, HelixDismissEvent, HelixEditEvent, HelixEndEvent, HelixExpandEvent, HelixFacetEvent, HelixFilterEvent, HelixFilteredEvent, HelixGraphFilterEvent, HelixGraphLayoutEvent, HelixGraphPanEvent, HelixGraphPartChangeEvent, HelixGraphReadyEvent, HelixGraphSignalEvent, HelixGraphZoomEvent, HelixHistoryEvent, HelixLoadEvent, HelixLoadMoreEvent, HelixMapClusterEvent, HelixMapPanEvent, HelixMapPartChangeEvent, HelixMapReadyEvent, HelixMapRotateEvent, HelixMapTiltEvent, HelixMapZoomEvent, HelixMoveEvent, HelixNavigateEvent, HelixNextEvent, HelixPanelResizeEvent, HelixPinEvent, HelixPressEvent, HelixPreviewEvent, HelixPreviousEvent, HelixRangeChangeEvent, HelixReadEvent, HelixReadingChangeEvent, HelixReadingPressEvent, HelixReconnectEvent, HelixRejectEvent, HelixRelabelEvent, HelixRemoveEvent, HelixReorderEvent, HelixReorderedEvent, HelixResizedEvent, HelixResizeEvent, HelixSampleEndEvent, HelixSampleStartEvent, HelixScrolledEvent, HelixSelectEvent, HelixSelectedEvent, HelixSendEvent, HelixShortcutEvent, HelixSortEvent, HelixSortedEvent, HelixSplitEvent, HelixStrandsFallbackEvent, HelixToggleEvent, HelixTourAskEvent, HelixTourStatusEvent, HelixTypingEvent, };
126
+ export { HelixActionEvent, HelixActivateEvent, HelixAuraFallbackEvent, HelixBandEvent, HelixBeforeActivateEvent, HelixBeforeChangeEvent, HelixBeforeCloseEvent, HelixBeforeCollapseEvent, HelixBeforeEditEvent, HelixBeforeMoveEvent, HelixBeforeOpenEvent, HelixBeforePanelResizeEvent, HelixBeforePasteEvent, HelixBeforePinEvent, HelixBeforeReconnectEvent, HelixBeforeRelabelEvent, HelixBeforeReorderEvent, HelixBeforeResizeEvent, HelixBeforeSplitEvent, HelixBeforeToggleEvent, HelixCanvasEditEvent, HelixCanvasPanEvent, HelixCanvasViewEvent, HelixCanvasZoomEvent, HelixCaretEvent, HelixClearEvent, HelixCollapseEvent, HelixCompleteEvent, HelixConnectEvent, HelixConsoleEvent, HelixCopyErrorEvent, HelixCopyEvent, HelixCreateEvent, HelixDismissEvent, HelixEditEvent, HelixEndEvent, HelixExpandEvent, HelixFacetEvent, HelixFilterEvent, HelixFilteredEvent, HelixGraphFilterEvent, HelixGraphLayoutEvent, HelixGraphPanEvent, HelixGraphPartChangeEvent, HelixGraphReadyEvent, HelixGraphSignalEvent, HelixGraphZoomEvent, HelixHistoryEvent, HelixLoadEvent, HelixLoadMoreEvent, HelixMapClusterEvent, HelixMapPanEvent, HelixMapPartChangeEvent, HelixMapReadyEvent, HelixMapRotateEvent, HelixMapTiltEvent, HelixMapZoomEvent, HelixMoveEvent, HelixNavigateEvent, HelixNextEvent, HelixPanelResizeEvent, HelixPastedEvent, HelixPinEvent, HelixPressEvent, HelixPreviewEvent, HelixPreviousEvent, HelixRangeChangeEvent, HelixReadEvent, HelixReadingChangeEvent, HelixReadingPressEvent, HelixReconnectEvent, HelixRejectEvent, HelixRelabelEvent, HelixRemoveEvent, HelixReorderEvent, HelixReorderedEvent, HelixResizedEvent, HelixResizeEvent, HelixSampleEndEvent, HelixSampleStartEvent, HelixScrolledEvent, HelixSelectEvent, HelixSelectedEvent, HelixSendEvent, HelixShortcutEvent, HelixSortEvent, HelixSortedEvent, HelixSplitEvent, HelixStrandsFallbackEvent, HelixToggleEvent, HelixTourAskEvent, HelixTourStatusEvent, HelixTypingEvent, };
125
127
  /**
126
128
  * Every event class, and the one place a new one is registered: a class in a
127
129
  * module of its own, imported above, and named here and in `names.ts`.
@@ -147,6 +149,7 @@ declare const registry: {
147
149
  readonly HelixBeforeMoveEvent: typeof HelixBeforeMoveEvent;
148
150
  readonly HelixBeforeOpenEvent: typeof HelixBeforeOpenEvent;
149
151
  readonly HelixBeforePanelResizeEvent: typeof HelixBeforePanelResizeEvent;
152
+ readonly HelixBeforePasteEvent: typeof HelixBeforePasteEvent;
150
153
  readonly HelixBeforePinEvent: typeof HelixBeforePinEvent;
151
154
  readonly HelixBeforeReconnectEvent: typeof HelixBeforeReconnectEvent;
152
155
  readonly HelixBeforeRelabelEvent: typeof HelixBeforeRelabelEvent;
@@ -195,6 +198,7 @@ declare const registry: {
195
198
  readonly HelixNavigateEvent: typeof HelixNavigateEvent;
196
199
  readonly HelixNextEvent: typeof HelixNextEvent;
197
200
  readonly HelixPanelResizeEvent: typeof HelixPanelResizeEvent;
201
+ readonly HelixPastedEvent: typeof HelixPastedEvent;
198
202
  readonly HelixPinEvent: typeof HelixPinEvent;
199
203
  readonly HelixPressEvent: typeof HelixPressEvent;
200
204
  readonly HelixPreviewEvent: typeof HelixPreviewEvent;
@@ -10,6 +10,7 @@ import { HelixBeforeEditEvent } from './before-edit.js';
10
10
  import { HelixBeforeMoveEvent } from './before-move.js';
11
11
  import { HelixBeforeOpenEvent } from './before-open.js';
12
12
  import { HelixBeforePanelResizeEvent } from './before-panel-resize.js';
13
+ import { HelixBeforePasteEvent } from './before-paste.js';
13
14
  import { HelixBeforePinEvent } from './before-pin.js';
14
15
  import { HelixBeforeReconnectEvent } from './before-reconnect.js';
15
16
  import { HelixBeforeRelabelEvent } from './before-relabel.js';
@@ -58,6 +59,7 @@ import { HelixMoveEvent } from './move.js';
58
59
  import { HelixNavigateEvent } from './navigate.js';
59
60
  import { HelixNextEvent } from './next.js';
60
61
  import { HelixPanelResizeEvent } from './panel-resize.js';
62
+ import { HelixPastedEvent } from './pasted.js';
61
63
  import { HelixPinEvent } from './pin.js';
62
64
  import { HelixPressEvent } from './press.js';
63
65
  import { HelixPreviewEvent } from './preview.js';
@@ -90,7 +92,7 @@ import { HelixTourAskEvent } from './tour-ask.js';
90
92
  import { HelixTourStatusEvent } from './tour-status.js';
91
93
  import { HelixTypingEvent } from './typing.js';
92
94
  export { helixEventNames } from './names.js';
93
- export { HelixActionEvent, HelixActivateEvent, HelixAuraFallbackEvent, HelixBandEvent, HelixBeforeActivateEvent, HelixBeforeChangeEvent, HelixBeforeCloseEvent, HelixBeforeCollapseEvent, HelixBeforeEditEvent, HelixBeforeMoveEvent, HelixBeforeOpenEvent, HelixBeforePanelResizeEvent, HelixBeforePinEvent, HelixBeforeReconnectEvent, HelixBeforeRelabelEvent, HelixBeforeReorderEvent, HelixBeforeResizeEvent, HelixBeforeSplitEvent, HelixBeforeToggleEvent, HelixCanvasEditEvent, HelixCanvasPanEvent, HelixCanvasViewEvent, HelixCanvasZoomEvent, HelixCaretEvent, HelixClearEvent, HelixCollapseEvent, HelixCompleteEvent, HelixConnectEvent, HelixConsoleEvent, HelixCopyErrorEvent, HelixCopyEvent, HelixCreateEvent, HelixDismissEvent, HelixEditEvent, HelixEndEvent, HelixExpandEvent, HelixFacetEvent, HelixFilterEvent, HelixFilteredEvent, HelixGraphFilterEvent, HelixGraphLayoutEvent, HelixGraphPanEvent, HelixGraphPartChangeEvent, HelixGraphReadyEvent, HelixGraphSignalEvent, HelixGraphZoomEvent, HelixHistoryEvent, HelixLoadEvent, HelixLoadMoreEvent, HelixMapClusterEvent, HelixMapPanEvent, HelixMapPartChangeEvent, HelixMapReadyEvent, HelixMapRotateEvent, HelixMapTiltEvent, HelixMapZoomEvent, HelixMoveEvent, HelixNavigateEvent, HelixNextEvent, HelixPanelResizeEvent, HelixPinEvent, HelixPressEvent, HelixPreviewEvent, HelixPreviousEvent, HelixRangeChangeEvent, HelixReadEvent, HelixReadingChangeEvent, HelixReadingPressEvent, HelixReconnectEvent, HelixRejectEvent, HelixRelabelEvent, HelixRemoveEvent, HelixReorderEvent, HelixReorderedEvent, HelixResizedEvent, HelixResizeEvent, HelixSampleEndEvent, HelixSampleStartEvent, HelixScrolledEvent, HelixSelectEvent, HelixSelectedEvent, HelixSendEvent, HelixShortcutEvent, HelixSortEvent, HelixSortedEvent, HelixSplitEvent, HelixStrandsFallbackEvent, HelixToggleEvent, HelixTourAskEvent, HelixTourStatusEvent, HelixTypingEvent, };
95
+ export { HelixActionEvent, HelixActivateEvent, HelixAuraFallbackEvent, HelixBandEvent, HelixBeforeActivateEvent, HelixBeforeChangeEvent, HelixBeforeCloseEvent, HelixBeforeCollapseEvent, HelixBeforeEditEvent, HelixBeforeMoveEvent, HelixBeforeOpenEvent, HelixBeforePanelResizeEvent, HelixBeforePasteEvent, HelixBeforePinEvent, HelixBeforeReconnectEvent, HelixBeforeRelabelEvent, HelixBeforeReorderEvent, HelixBeforeResizeEvent, HelixBeforeSplitEvent, HelixBeforeToggleEvent, HelixCanvasEditEvent, HelixCanvasPanEvent, HelixCanvasViewEvent, HelixCanvasZoomEvent, HelixCaretEvent, HelixClearEvent, HelixCollapseEvent, HelixCompleteEvent, HelixConnectEvent, HelixConsoleEvent, HelixCopyErrorEvent, HelixCopyEvent, HelixCreateEvent, HelixDismissEvent, HelixEditEvent, HelixEndEvent, HelixExpandEvent, HelixFacetEvent, HelixFilterEvent, HelixFilteredEvent, HelixGraphFilterEvent, HelixGraphLayoutEvent, HelixGraphPanEvent, HelixGraphPartChangeEvent, HelixGraphReadyEvent, HelixGraphSignalEvent, HelixGraphZoomEvent, HelixHistoryEvent, HelixLoadEvent, HelixLoadMoreEvent, HelixMapClusterEvent, HelixMapPanEvent, HelixMapPartChangeEvent, HelixMapReadyEvent, HelixMapRotateEvent, HelixMapTiltEvent, HelixMapZoomEvent, HelixMoveEvent, HelixNavigateEvent, HelixNextEvent, HelixPanelResizeEvent, HelixPastedEvent, HelixPinEvent, HelixPressEvent, HelixPreviewEvent, HelixPreviousEvent, HelixRangeChangeEvent, HelixReadEvent, HelixReadingChangeEvent, HelixReadingPressEvent, HelixReconnectEvent, HelixRejectEvent, HelixRelabelEvent, HelixRemoveEvent, HelixReorderEvent, HelixReorderedEvent, HelixResizedEvent, HelixResizeEvent, HelixSampleEndEvent, HelixSampleStartEvent, HelixScrolledEvent, HelixSelectEvent, HelixSelectedEvent, HelixSendEvent, HelixShortcutEvent, HelixSortEvent, HelixSortedEvent, HelixSplitEvent, HelixStrandsFallbackEvent, HelixToggleEvent, HelixTourAskEvent, HelixTourStatusEvent, HelixTypingEvent, };
94
96
  const registry = {
95
97
  HelixActionEvent,
96
98
  HelixActivateEvent,
@@ -104,6 +106,7 @@ const registry = {
104
106
  HelixBeforeMoveEvent,
105
107
  HelixBeforeOpenEvent,
106
108
  HelixBeforePanelResizeEvent,
109
+ HelixBeforePasteEvent,
107
110
  HelixBeforePinEvent,
108
111
  HelixBeforeReconnectEvent,
109
112
  HelixBeforeRelabelEvent,
@@ -152,6 +155,7 @@ const registry = {
152
155
  HelixNavigateEvent,
153
156
  HelixNextEvent,
154
157
  HelixPanelResizeEvent,
158
+ HelixPastedEvent,
155
159
  HelixPinEvent,
156
160
  HelixPressEvent,
157
161
  HelixPreviewEvent,
@@ -1,11 +1,11 @@
1
1
  export const helixEventNames = new Set(('action activate aura-fallback band before-activate before-change before-close ' +
2
- 'before-collapse before-edit before-move before-open before-panel-resize before-pin ' +
2
+ 'before-collapse before-edit before-move before-open before-panel-resize before-paste before-pin ' +
3
3
  'before-reconnect before-relabel before-reorder before-resize before-split before-toggle ' +
4
4
  'canvas-edit canvas-pan canvas-view canvas-zoom caret clear collapse complete ' +
5
5
  'connect console copy copy-error create dismiss edit end expand facet filter ' +
6
6
  'filtered graph-filter graph-layout graph-pan graph-part-change graph-ready ' +
7
7
  'graph-signal graph-zoom history load load-more map-cluster map-pan map-part-change ' +
8
- 'map-ready map-rotate map-tilt map-zoom move navigate next panel-resize pin press ' +
8
+ 'map-ready map-rotate map-tilt map-zoom move navigate next panel-resize pasted pin press ' +
9
9
  'preview previous range-change read reading-change reading-press reconnect reject ' +
10
10
  'relabel remove reorder ' +
11
11
  'reordered resize resized sample-end sample-start scrolled select selected send ' +
@@ -0,0 +1,6 @@
1
+ import type { PasteDetail } from './details.js';
2
+ /** Cards and connections were pasted onto a board, which now holds them. */
3
+ export declare class HelixPastedEvent extends CustomEvent<PasteDetail> {
4
+ static readonly eventName = "helix-pasted";
5
+ constructor(detail: PasteDetail);
6
+ }
@@ -0,0 +1,11 @@
1
+ export class HelixPastedEvent extends CustomEvent {
2
+ static { this.eventName = 'helix-pasted'; }
3
+ constructor(detail) {
4
+ super(HelixPastedEvent.eventName, {
5
+ bubbles: true,
6
+ cancelable: false,
7
+ composed: true,
8
+ detail,
9
+ });
10
+ }
11
+ }
@@ -214,13 +214,42 @@ export declare class HelixFormElement extends HelixElement {
214
214
  get validity(): ValidityState;
215
215
  /** The message the browser would show for the control, or an empty string. */
216
216
  get validationMessage(): string;
217
+ /** Whether an update is running, so `runPendingUpdate` never starts one inside another. */
218
+ private updateRunning;
219
+ protected performUpdate(): void;
220
+ /**
221
+ * Runs an update that is on its way, so the verdict read next is the one
222
+ * for what the control holds now.
223
+ *
224
+ * **A control reports its verdict as it updates, and an update waits for a
225
+ * microtask**, so a script that wrote `value` and asked at once was answered
226
+ * about the value before: measured on Time Input, `value = '18:00'` past a
227
+ * `max` of 17:00 and `checkValidity()` returned true until `updateComplete`.
228
+ * A native control answers about the value it was just given, so the
229
+ * questions a script can ask run the pending update first. Lit's
230
+ * `performUpdate` is the documented way to run one synchronously.
231
+ */
232
+ private runPendingUpdate;
217
233
  /** Whether the control is checked when its form is submitted. */
218
234
  get willValidate(): boolean;
219
235
  /**
220
236
  * Returns whether the control is valid, and fires `invalid` on it when it is
221
- * not.
237
+ * not. The control shows no message for it: `reportValidity()` does that.
222
238
  */
223
239
  checkValidity(): boolean;
240
+ /** Whether `checkValidity()` is running, set only for the `invalid` it fires. */
241
+ private checkInProgress;
242
+ /**
243
+ * @internal Whether the `invalid` firing now comes from `checkValidity()`.
244
+ *
245
+ * **Such an `invalid` reports and shows nothing**, as on a native control: a
246
+ * required `<input>` checked from a script fires `invalid` and does not match
247
+ * `:user-invalid`, measured in Chromium. Every layer that takes `invalid` as
248
+ * 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.
251
+ */
252
+ protected get checkingValidity(): boolean;
224
253
  /**
225
254
  * Returns whether the control is valid, and when it is not, fires `invalid`
226
255
  * and shows the browser's message at the control.
@@ -85,6 +85,8 @@ export class HelixFormElement extends HelixElement {
85
85
  this.reported = { anchor: undefined, flags: {}, message: '' };
86
86
  this.customValidity = '';
87
87
  this.appliedFeedback = '';
88
+ this.updateRunning = false;
89
+ this.checkInProgress = false;
88
90
  this.focusFromLabel = (event) => {
89
91
  if (!event.isTrusted || event.composedPath()[0] !== this || this.isDisabled) {
90
92
  return;
@@ -135,18 +137,45 @@ export class HelixFormElement extends HelixElement {
135
137
  return this.focusTarget ?? anchor;
136
138
  }
137
139
  get validity() {
140
+ this.runPendingUpdate();
138
141
  return this.internals.validity;
139
142
  }
140
143
  get validationMessage() {
144
+ this.runPendingUpdate();
141
145
  return this.internals.validationMessage;
142
146
  }
147
+ performUpdate() {
148
+ this.updateRunning = true;
149
+ try {
150
+ super.performUpdate();
151
+ }
152
+ finally {
153
+ this.updateRunning = false;
154
+ }
155
+ }
156
+ runPendingUpdate() {
157
+ if (this.hasUpdated && this.isUpdatePending && !this.updateRunning) {
158
+ this.performUpdate();
159
+ }
160
+ }
143
161
  get willValidate() {
144
162
  return this.internals.willValidate;
145
163
  }
146
164
  checkValidity() {
147
- return this.internals.checkValidity();
165
+ this.runPendingUpdate();
166
+ this.checkInProgress = true;
167
+ try {
168
+ return this.internals.checkValidity();
169
+ }
170
+ finally {
171
+ this.checkInProgress = false;
172
+ }
173
+ }
174
+ get checkingValidity() {
175
+ return this.checkInProgress;
148
176
  }
149
177
  reportValidity() {
178
+ this.runPendingUpdate();
150
179
  this.applyValidity();
151
180
  return this.internals.reportValidity();
152
181
  }
package/dist/index.d.ts CHANGED
@@ -34,7 +34,7 @@ export { type TokenColor, TokenColorController, } from './controllers/token-colo
34
34
  export { UpgradeController, type UpgradeOptions, } from './controllers/upgrade-controller/upgrade-controller.js';
35
35
  export { type Rendered, VirtualizeController, type VirtualizeOptions, type Window as VirtualizeWindow, } from './controllers/virtualize-controller/virtualize-controller.js';
36
36
  export { eventsForTag, registerElementEvents } from './events/element-events.js';
37
- export { type ActivateDetail, type AuraFallbackDetail, type BandDetail, type CanvasView, type CaretDetail, type ChangeDetail, type CompleteDetail, type ConnectDetail, type ConsoleDetail, type CopyDetail, type CreateDetail, type DismissDetail, type FacetDetail, type FilterDetail, type GraphFilterDetail, type GraphLayoutDetail, type GraphPartChangeDetail, type GraphSignalDetail, HelixActionEvent, HelixActivateEvent, HelixAuraFallbackEvent, HelixBandEvent, HelixBeforeActivateEvent, HelixBeforeChangeEvent, HelixBeforeCloseEvent, HelixBeforeCollapseEvent, HelixBeforeEditEvent, HelixBeforeMoveEvent, HelixBeforeOpenEvent, HelixBeforePanelResizeEvent, HelixBeforePinEvent, HelixBeforeReconnectEvent, HelixBeforeRelabelEvent, HelixBeforeReorderEvent, HelixBeforeResizeEvent, HelixBeforeSplitEvent, HelixBeforeToggleEvent, HelixCanvasEditEvent, HelixCanvasPanEvent, HelixCanvasViewEvent, HelixCanvasZoomEvent, HelixCaretEvent, HelixClearEvent, HelixCollapseEvent, HelixCompleteEvent, HelixConnectEvent, HelixConsoleEvent, HelixCopyErrorEvent, HelixCopyEvent, HelixCreateEvent, HelixDismissEvent, HelixEditEvent, HelixEndEvent, HelixExpandEvent, HelixFacetEvent, HelixFilterEvent, HelixFilteredEvent, HelixGraphFilterEvent, HelixGraphLayoutEvent, HelixGraphPanEvent, HelixGraphPartChangeEvent, HelixGraphReadyEvent, HelixGraphSignalEvent, HelixGraphZoomEvent, HelixHistoryEvent, HelixLoadEvent, HelixLoadMoreEvent, HelixMapClusterEvent, HelixMapPanEvent, HelixMapPartChangeEvent, HelixMapReadyEvent, HelixMapRotateEvent, HelixMapTiltEvent, HelixMapZoomEvent, HelixMoveEvent, HelixNavigateEvent, HelixNextEvent, HelixPanelResizeEvent, HelixPinEvent, HelixPressEvent, HelixPreviewEvent, HelixPreviousEvent, HelixRangeChangeEvent, HelixReadEvent, HelixReadingChangeEvent, HelixReadingPressEvent, HelixReconnectEvent, HelixRejectEvent, HelixRelabelEvent, HelixRemoveEvent, HelixReorderEvent, HelixReorderedEvent, HelixResizedEvent, HelixResizeEvent, HelixSampleEndEvent, HelixSampleStartEvent, HelixScrolledEvent, HelixSelectEvent, HelixSelectedEvent, HelixSendEvent, HelixShortcutEvent, HelixSortEvent, HelixSortedEvent, HelixSplitEvent, HelixStrandsFallbackEvent, HelixToggleEvent, HelixTourAskEvent, HelixTourStatusEvent, HelixTypingEvent, type MapClusterDetail, type MapPanDetail, type MapPartChangeDetail, type MapRotateDetail, type MapTiltDetail, type MapZoomDetail, type MoveDetail, type NavigateDetail, type OpenDetail, type PressDetail, type PressPointerType, type RangeDetail, type ReadDetail, type ReadingDetail, type ReconnectDetail, type Refusal, type RefusedFile, type RejectDetail, type RelabelDetail, type RemoveDetail, type ReorderDetail, type ResizeDetail, type ScrolledDetail, type SelectDetail, type SendDetail, type ShortcutDetail, type SkipDetail, type SortDetail, type StrandsFallbackDetail, type ToggleDetail, type TourStatusDetail, type TypingDetail, type ViewportPanDetail, type ViewportZoomDetail, } from './events/events.js';
37
+ export { type ActivateDetail, type AuraFallbackDetail, type BandDetail, type CanvasView, type CaretDetail, type ChangeDetail, type CompleteDetail, type ConnectDetail, type ConsoleDetail, type CopyDetail, type CreateDetail, type DismissDetail, type FacetDetail, type FilterDetail, type GraphFilterDetail, type GraphLayoutDetail, type GraphPartChangeDetail, type GraphSignalDetail, HelixActionEvent, HelixActivateEvent, HelixAuraFallbackEvent, HelixBandEvent, HelixBeforeActivateEvent, HelixBeforeChangeEvent, HelixBeforeCloseEvent, HelixBeforeCollapseEvent, HelixBeforeEditEvent, HelixBeforeMoveEvent, HelixBeforeOpenEvent, HelixBeforePanelResizeEvent, HelixBeforePasteEvent, HelixBeforePinEvent, HelixBeforeReconnectEvent, HelixBeforeRelabelEvent, HelixBeforeReorderEvent, HelixBeforeResizeEvent, HelixBeforeSplitEvent, HelixBeforeToggleEvent, HelixCanvasEditEvent, HelixCanvasPanEvent, HelixCanvasViewEvent, HelixCanvasZoomEvent, HelixCaretEvent, HelixClearEvent, HelixCollapseEvent, HelixCompleteEvent, HelixConnectEvent, HelixConsoleEvent, HelixCopyErrorEvent, HelixCopyEvent, HelixCreateEvent, HelixDismissEvent, HelixEditEvent, HelixEndEvent, HelixExpandEvent, HelixFacetEvent, HelixFilterEvent, HelixFilteredEvent, HelixGraphFilterEvent, HelixGraphLayoutEvent, HelixGraphPanEvent, HelixGraphPartChangeEvent, HelixGraphReadyEvent, HelixGraphSignalEvent, HelixGraphZoomEvent, HelixHistoryEvent, HelixLoadEvent, HelixLoadMoreEvent, HelixMapClusterEvent, HelixMapPanEvent, HelixMapPartChangeEvent, HelixMapReadyEvent, HelixMapRotateEvent, HelixMapTiltEvent, HelixMapZoomEvent, HelixMoveEvent, HelixNavigateEvent, HelixNextEvent, HelixPanelResizeEvent, HelixPastedEvent, HelixPinEvent, HelixPressEvent, HelixPreviewEvent, HelixPreviousEvent, HelixRangeChangeEvent, HelixReadEvent, HelixReadingChangeEvent, HelixReadingPressEvent, HelixReconnectEvent, HelixRejectEvent, HelixRelabelEvent, HelixRemoveEvent, HelixReorderEvent, HelixReorderedEvent, HelixResizedEvent, HelixResizeEvent, HelixSampleEndEvent, HelixSampleStartEvent, HelixScrolledEvent, HelixSelectEvent, HelixSelectedEvent, HelixSendEvent, HelixShortcutEvent, HelixSortEvent, HelixSortedEvent, HelixSplitEvent, HelixStrandsFallbackEvent, HelixToggleEvent, HelixTourAskEvent, HelixTourStatusEvent, HelixTypingEvent, type MapClusterDetail, type MapPanDetail, type MapPartChangeDetail, type MapRotateDetail, type MapTiltDetail, type MapZoomDetail, type MoveDetail, type NavigateDetail, type OpenDetail, type PasteDetail, type PressDetail, type PressPointerType, type RangeDetail, type ReadDetail, type ReadingDetail, type ReconnectDetail, type Refusal, type RefusedFile, type RejectDetail, type RelabelDetail, type RemoveDetail, type ReorderDetail, type ResizeDetail, type ScrolledDetail, type SelectDetail, type SendDetail, type ShortcutDetail, type SkipDetail, type SortDetail, type StrandsFallbackDetail, type ToggleDetail, type TourStatusDetail, type TypingDetail, type ViewportPanDetail, type ViewportZoomDetail, } from './events/events.js';
38
38
  export { HelixElement, whenHydrated } from './helix-element/helix-element.js';
39
39
  export { HelixFormElement } from './helix-form-element/helix-form-element.js';
40
40
  export { MixinChartSource, type MixinChartSourceInterface, PALETTE, PLOT_HEIGHT, PLOT_WIDTH, POSITION_LABEL, seriesIndex, seriesNames, } from './mixins/mixin-chart-source/mixin-chart-source.js';
package/dist/index.js CHANGED
@@ -34,7 +34,7 @@ export { TokenColorController, } from './controllers/token-color-controller/toke
34
34
  export { UpgradeController, } from './controllers/upgrade-controller/upgrade-controller.js';
35
35
  export { VirtualizeController, } from './controllers/virtualize-controller/virtualize-controller.js';
36
36
  export { eventsForTag, registerElementEvents } from './events/element-events.js';
37
- export { HelixActionEvent, HelixActivateEvent, HelixAuraFallbackEvent, HelixBandEvent, HelixBeforeActivateEvent, HelixBeforeChangeEvent, HelixBeforeCloseEvent, HelixBeforeCollapseEvent, HelixBeforeEditEvent, HelixBeforeMoveEvent, HelixBeforeOpenEvent, HelixBeforePanelResizeEvent, HelixBeforePinEvent, HelixBeforeReconnectEvent, HelixBeforeRelabelEvent, HelixBeforeReorderEvent, HelixBeforeResizeEvent, HelixBeforeSplitEvent, HelixBeforeToggleEvent, HelixCanvasEditEvent, HelixCanvasPanEvent, HelixCanvasViewEvent, HelixCanvasZoomEvent, HelixCaretEvent, HelixClearEvent, HelixCollapseEvent, HelixCompleteEvent, HelixConnectEvent, HelixConsoleEvent, HelixCopyErrorEvent, HelixCopyEvent, HelixCreateEvent, HelixDismissEvent, HelixEditEvent, HelixEndEvent, HelixExpandEvent, HelixFacetEvent, HelixFilterEvent, HelixFilteredEvent, HelixGraphFilterEvent, HelixGraphLayoutEvent, HelixGraphPanEvent, HelixGraphPartChangeEvent, HelixGraphReadyEvent, HelixGraphSignalEvent, HelixGraphZoomEvent, HelixHistoryEvent, HelixLoadEvent, HelixLoadMoreEvent, HelixMapClusterEvent, HelixMapPanEvent, HelixMapPartChangeEvent, HelixMapReadyEvent, HelixMapRotateEvent, HelixMapTiltEvent, HelixMapZoomEvent, HelixMoveEvent, HelixNavigateEvent, HelixNextEvent, HelixPanelResizeEvent, HelixPinEvent, HelixPressEvent, HelixPreviewEvent, HelixPreviousEvent, HelixRangeChangeEvent, HelixReadEvent, HelixReadingChangeEvent, HelixReadingPressEvent, HelixReconnectEvent, HelixRejectEvent, HelixRelabelEvent, HelixRemoveEvent, HelixReorderEvent, HelixReorderedEvent, HelixResizedEvent, HelixResizeEvent, HelixSampleEndEvent, HelixSampleStartEvent, HelixScrolledEvent, HelixSelectEvent, HelixSelectedEvent, HelixSendEvent, HelixShortcutEvent, HelixSortEvent, HelixSortedEvent, HelixSplitEvent, HelixStrandsFallbackEvent, HelixToggleEvent, HelixTourAskEvent, HelixTourStatusEvent, HelixTypingEvent, } from './events/events.js';
37
+ export { HelixActionEvent, HelixActivateEvent, HelixAuraFallbackEvent, HelixBandEvent, HelixBeforeActivateEvent, HelixBeforeChangeEvent, HelixBeforeCloseEvent, HelixBeforeCollapseEvent, HelixBeforeEditEvent, HelixBeforeMoveEvent, HelixBeforeOpenEvent, HelixBeforePanelResizeEvent, HelixBeforePasteEvent, HelixBeforePinEvent, HelixBeforeReconnectEvent, HelixBeforeRelabelEvent, HelixBeforeReorderEvent, HelixBeforeResizeEvent, HelixBeforeSplitEvent, HelixBeforeToggleEvent, HelixCanvasEditEvent, HelixCanvasPanEvent, HelixCanvasViewEvent, HelixCanvasZoomEvent, HelixCaretEvent, HelixClearEvent, HelixCollapseEvent, HelixCompleteEvent, HelixConnectEvent, HelixConsoleEvent, HelixCopyErrorEvent, HelixCopyEvent, HelixCreateEvent, HelixDismissEvent, HelixEditEvent, HelixEndEvent, HelixExpandEvent, HelixFacetEvent, HelixFilterEvent, HelixFilteredEvent, HelixGraphFilterEvent, HelixGraphLayoutEvent, HelixGraphPanEvent, HelixGraphPartChangeEvent, HelixGraphReadyEvent, HelixGraphSignalEvent, HelixGraphZoomEvent, HelixHistoryEvent, HelixLoadEvent, HelixLoadMoreEvent, HelixMapClusterEvent, HelixMapPanEvent, HelixMapPartChangeEvent, HelixMapReadyEvent, HelixMapRotateEvent, HelixMapTiltEvent, HelixMapZoomEvent, HelixMoveEvent, HelixNavigateEvent, HelixNextEvent, HelixPanelResizeEvent, HelixPastedEvent, HelixPinEvent, HelixPressEvent, HelixPreviewEvent, HelixPreviousEvent, HelixRangeChangeEvent, HelixReadEvent, HelixReadingChangeEvent, HelixReadingPressEvent, HelixReconnectEvent, HelixRejectEvent, HelixRelabelEvent, HelixRemoveEvent, HelixReorderEvent, HelixReorderedEvent, HelixResizedEvent, HelixResizeEvent, HelixSampleEndEvent, HelixSampleStartEvent, HelixScrolledEvent, HelixSelectEvent, HelixSelectedEvent, HelixSendEvent, HelixShortcutEvent, HelixSortEvent, HelixSortedEvent, HelixSplitEvent, HelixStrandsFallbackEvent, HelixToggleEvent, HelixTourAskEvent, HelixTourStatusEvent, HelixTypingEvent, } from './events/events.js';
38
38
  export { HelixElement, whenHydrated } from './helix-element/helix-element.js';
39
39
  export { HelixFormElement } from './helix-form-element/helix-form-element.js';
40
40
  export { MixinChartSource, PALETTE, PLOT_HEIGHT, PLOT_WIDTH, POSITION_LABEL, seriesIndex, seriesNames, } from './mixins/mixin-chart-source/mixin-chart-source.js';
@@ -164,7 +164,11 @@ export const MixinField = (Base) => {
164
164
  const touch = () => {
165
165
  this.touched = true;
166
166
  };
167
- this.addEventListener('invalid', touch);
167
+ this.addEventListener('invalid', () => {
168
+ if (!this.checkingValidity) {
169
+ touch();
170
+ }
171
+ });
168
172
  this.addEventListener('change', touch);
169
173
  this.renderRoot.addEventListener('slotchange', this.noteSlotted);
170
174
  auditName(this, () => this.namedControl, 'Give it a `label`, an `aria-label`, words in its label, or a `<label for>` that names it.', () => this.labeled ||
@@ -34,7 +34,11 @@ export const MixinPickerFeedback = (Base) => {
34
34
  const touch = () => {
35
35
  this.touched = true;
36
36
  };
37
- this.addEventListener('invalid', touch);
37
+ this.addEventListener('invalid', () => {
38
+ if (!this.checkingValidity) {
39
+ touch();
40
+ }
41
+ });
38
42
  this.addEventListener('change', touch);
39
43
  }
40
44
  get validityFeedback() {
@@ -76,7 +76,8 @@ export declare class MixinSegmentedFieldInterface {
76
76
  * **What a segment does with a key**, which is React Aria's: digits fill it and
77
77
  * move on once it cannot take another, Up and Down step it and wrap, Page Up
78
78
  * and Page Down step it by a page, a week of days, five years or fifteen
79
- * minutes, Home and End go to its ends, Backspace and Delete empty it, Left and Right move between
79
+ * minutes, Home and End go to its ends, Backspace and Delete take off its
80
+ * last digit or move back from an empty segment, Left and Right move between
80
81
  * segments along the line of text, and Alt+Down opens the picker. Each segment
81
82
  * is a tab stop, as each is in React Aria.
82
83
  *
@@ -38,8 +38,7 @@ export const MixinSegmentedField = (Base) => {
38
38
  event.preventDefault();
39
39
  if (event.inputType === 'deleteContentBackward' ||
40
40
  event.inputType === 'deleteContentForward') {
41
- this.typed = '';
42
- this.edit(element, 'Backspace');
41
+ this.erase(element);
43
42
  return;
44
43
  }
45
44
  for (const character of latinDigits(event.data ?? '')) {
@@ -105,6 +104,11 @@ export const MixinSegmentedField = (Base) => {
105
104
  this.typed = '';
106
105
  return;
107
106
  }
107
+ if (event.key === 'Backspace' || event.key === 'Delete') {
108
+ event.preventDefault();
109
+ this.erase(element);
110
+ return;
111
+ }
108
112
  if (this.edit(element, latinDigits(event.key))) {
109
113
  event.preventDefault();
110
114
  }
@@ -233,6 +237,15 @@ export const MixinSegmentedField = (Base) => {
233
237
  }
234
238
  return true;
235
239
  }
240
+ erase(element) {
241
+ if (this.edit(element, 'Backspace')) {
242
+ return;
243
+ }
244
+ if (!this.readOnly && !this.isDisabled) {
245
+ this.typed = '';
246
+ this.moveFocus(element, -1);
247
+ }
248
+ }
236
249
  willUpdate(changed) {
237
250
  super.willUpdate(changed);
238
251
  const own = this.own;
@@ -91,7 +91,8 @@ export interface SegmentKeyState {
91
91
  * nothing.
92
92
  *
93
93
  * The arrows step it and wrap, Page Up and Page Down step it by a page, Home
94
- * and End take it to its lowest and highest, Backspace and Delete empty it, a
94
+ * and End take it to its lowest and highest, Backspace and Delete take off its
95
+ * last digit and empty it from the last one, a
95
96
  * digit is typed as `typeDigit` types it, and on the meridiem a letter picks
96
97
  * the half whose word starts with it. `advance` says the segment is full and
97
98
  * focus moves on.
@@ -185,7 +185,15 @@ export const segmentKey = (key, { kind, locale, segments, system, twelve, typed
185
185
  return done(setSegment(segments, kind, key === 'Home' ? min : max, twelve, system));
186
186
  }
187
187
  if (key === 'Backspace' || key === 'Delete') {
188
- return done(setSegment(segments, kind, undefined, twelve, system));
188
+ const shown = shownValue(kind, segments, twelve);
189
+ if (shown === undefined || Number.isNaN(shown)) {
190
+ return null;
191
+ }
192
+ const left = kind === 'dayPeriod' ? '' : String(shown).slice(0, -1);
193
+ const rest = Number(left);
194
+ return left === '' || rest === 0
195
+ ? done(setSegment(segments, kind, undefined, twelve, system))
196
+ : { advance: false, segments: setSegment(segments, kind, rest, twelve, system), typed: left };
189
197
  }
190
198
  if (/^\d$/.test(key) && kind !== 'dayPeriod') {
191
199
  return typeDigit(segments, kind, typed, key, twelve, system);
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.166.0";
9
+ export declare const VERSION = "0.168.0";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = '0.166.0';
1
+ export const VERSION = '0.168.0';
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.166.0",
4
+ "version": "0.168.0",
5
5
  "license": "MIT",
6
6
  "author": "Ross Sheppard <ross.sheppard@fusion.dev>",
7
7
  "repository": {
@@ -56,11 +56,11 @@
56
56
  "bugs": "https://github.com/project-fusion/fusion/issues",
57
57
  "dependencies": {
58
58
  "@floating-ui/dom": "^1.8.0",
59
- "@fusion.dev/helix-icons": "0.166.0"
59
+ "@fusion.dev/helix-icons": "0.168.0"
60
60
  },
61
61
  "devDependencies": {
62
- "@fusion.dev/helix-cem": "0.166.0",
63
- "@fusion.dev/helix-testing": "0.166.0",
62
+ "@fusion.dev/helix-cem": "0.168.0",
63
+ "@fusion.dev/helix-testing": "0.168.0",
64
64
  "@lit-labs/ssr": "^4.1.0",
65
65
  "lit": "^3.2.1"
66
66
  },