@fusion.dev/helix-core 0.23.0 → 0.24.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.
@@ -1810,6 +1810,24 @@
1810
1810
  ],
1811
1811
  "description": "The last item that starts at or before `offset`.\n\nA binary search rather than a walk, so a reader dragging the thumb to the\nend of ten thousand rows costs fourteen comparisons rather than ten\nthousand."
1812
1812
  },
1813
+ {
1814
+ "kind": "method",
1815
+ "name": "shift",
1816
+ "return": {
1817
+ "type": {
1818
+ "text": "void"
1819
+ }
1820
+ },
1821
+ "parameters": [
1822
+ {
1823
+ "name": "by",
1824
+ "type": {
1825
+ "text": "number"
1826
+ }
1827
+ }
1828
+ ],
1829
+ "description": "Moves every height along by `by` places, for a set that grew at its start.\n\nA transcript loading its history prepends, and every item already measured\nis now `by` further down the set. Moved rather than forgotten, because the\nmeasurements are still true of the same items: forgetting would put every\none of them back at the estimate at the moment the reader is looking at them."
1830
+ },
1813
1831
  {
1814
1832
  "kind": "method",
1815
1833
  "name": "forget",
@@ -1949,6 +1967,16 @@
1949
1967
  "description": "Where each item starts, once any of them has been seen.",
1950
1968
  "default": "new Heights(() => this.options.estimate())"
1951
1969
  },
1970
+ {
1971
+ "kind": "field",
1972
+ "name": "owed",
1973
+ "type": {
1974
+ "text": "number"
1975
+ },
1976
+ "privacy": "private",
1977
+ "default": "0",
1978
+ "description": "Room `shift` added above the window, owed to the scroll offset once drawn."
1979
+ },
1952
1980
  {
1953
1981
  "kind": "method",
1954
1982
  "name": "hostConnected",
@@ -2083,6 +2111,24 @@
2083
2111
  },
2084
2112
  "description": "Measures what was just drawn, and holds the reader still if it moved.\n\n**This is the pass the fixed-estimate note warns about, made to terminate.**\nMeasuring after a render, and re-windowing on what was measured, is a loop\nthat feeds itself. What bounds it is that a height is news exactly once:\n`record` answers false for a size it already holds, so an item can move the\nwindow on the render that first shows it and never again. A set of ten\nthousand can therefore surprise this ten thousand times over its life and\nnot twice for the same row.\n\n**The scroll offset is corrected before anything is drawn again.** An item\nabove the window turning out taller than its estimate moves everything\nbelow it, including what the reader is looking at. Adding the same\ndifference to `scrollTop` puts the content back under them, which is why a\nlist of variable rows can be scrolled through without it walking away.\nSkipped at the very top, where there is nothing above to have moved."
2085
2113
  },
2114
+ {
2115
+ "kind": "method",
2116
+ "name": "shift",
2117
+ "return": {
2118
+ "type": {
2119
+ "text": "void"
2120
+ }
2121
+ },
2122
+ "parameters": [
2123
+ {
2124
+ "name": "by",
2125
+ "type": {
2126
+ "text": "number"
2127
+ }
2128
+ }
2129
+ ],
2130
+ "description": "Tells the controller the set grew by `by` items at its start.\n\nFor a host that prepends, which is what a transcript loading its history\ndoes. Every index the controller knew now names the item `by` places later,\nso the heights and the window move with them, and the scroll offset is moved\nby the room the new items take once they have been drawn. The reader stays\non the turn they were reading rather than on whatever now sits at their old\noffset, which is what loading history upward has to look like.\n\nCall it from `willUpdate`, before the render that draws the grown set."
2131
+ },
2086
2132
  {
2087
2133
  "kind": "method",
2088
2134
  "name": "forget",
@@ -2821,6 +2867,28 @@
2821
2867
  },
2822
2868
  "helix": {}
2823
2869
  },
2870
+ {
2871
+ "kind": "class",
2872
+ "description": "Fired when the reader changes which values a field is filtered by.\n\n**Not `helix-filter`, which is a different thing already taken.** That one\nsays a list narrowed itself to a string, and `Data Table` and `Listbox` both\ndispatch it with exactly that meaning. This one carries a structured filter:\na field, a comparison and a set of values, which is a shape neither of those\nhas and neither should grow.\n\nCancelable, and refusing it is how a consumer takes the decision for one\ninteraction, the way `requestChange` documents for every stateful change here.",
2873
+ "name": "HelixFacetEvent",
2874
+ "members": [
2875
+ {
2876
+ "kind": "field",
2877
+ "name": "eventName",
2878
+ "type": {
2879
+ "text": "string"
2880
+ },
2881
+ "static": true,
2882
+ "readonly": true,
2883
+ "default": "'helix-facet'"
2884
+ }
2885
+ ],
2886
+ "superclass": {
2887
+ "name": "CustomEvent",
2888
+ "module": "src/events/events.ts"
2889
+ },
2890
+ "helix": {}
2891
+ },
2824
2892
  {
2825
2893
  "kind": "class",
2826
2894
  "description": "Fired when a component narrows what it is showing.\n\nFiltering hides rather than removes, so nothing about the value changes and\n`helix-change` would be a lie. What a consumer wants to know is that the list\nin front of the reader is no longer the whole list.",
@@ -2887,6 +2955,50 @@
2887
2955
  },
2888
2956
  "helix": {}
2889
2957
  },
2958
+ {
2959
+ "kind": "class",
2960
+ "description": "Fired when the reader asks for the columns in a different order.\n\nCancelable, and refusing it leaves the columns where they were, so a consumer\nwho stores the order, in a URL or a preference, can take the decision the way\na server-side sort takes `helix-sort`. The detail is the whole order as\nspace-separated fields rather than one move, because the order is the fact a\nconsumer stores, and a move is only meaningful against an order they would\nthen have to keep in step.\n\nNot `helix-sort`: that reorders the rows, this reorders the columns, and a\nconsumer listening for one should not hear the other.",
2961
+ "name": "HelixReorderEvent",
2962
+ "members": [
2963
+ {
2964
+ "kind": "field",
2965
+ "name": "eventName",
2966
+ "type": {
2967
+ "text": "string"
2968
+ },
2969
+ "static": true,
2970
+ "readonly": true,
2971
+ "default": "'helix-reorder'"
2972
+ }
2973
+ ],
2974
+ "superclass": {
2975
+ "name": "CustomEvent",
2976
+ "module": "src/events/events.ts"
2977
+ },
2978
+ "helix": {}
2979
+ },
2980
+ {
2981
+ "kind": "class",
2982
+ "description": "Fired when a reader reaches the oldest turn a transcript holds, while there are\nolder ones to fetch.\n\n**A report of where the reader is, not a request to be granted**, so it is not\ncancelable: there is nothing for the transcript to do if nobody refuses, and\nnothing to stop if somebody does. The consumer answers by fetching, and says\nso through the transcript's `history` while the older turns are on their way.\n\nNo detail. The consumer holds the transcript and knows its oldest turn, which\nis the one thing a fetch needs.",
2983
+ "name": "HelixHistoryEvent",
2984
+ "members": [
2985
+ {
2986
+ "kind": "field",
2987
+ "name": "eventName",
2988
+ "type": {
2989
+ "text": "string"
2990
+ },
2991
+ "static": true,
2992
+ "readonly": true,
2993
+ "default": "'helix-history'"
2994
+ }
2995
+ ],
2996
+ "superclass": {
2997
+ "name": "CustomEvent",
2998
+ "module": "src/events/events.ts"
2999
+ },
3000
+ "helix": {}
3001
+ },
2890
3002
  {
2891
3003
  "kind": "variable",
2892
3004
  "name": "helixEventNames",
@@ -3130,6 +3242,14 @@
3130
3242
  "module": "src/events/events.ts"
3131
3243
  }
3132
3244
  },
3245
+ {
3246
+ "kind": "js",
3247
+ "name": "HelixFacetEvent",
3248
+ "declaration": {
3249
+ "name": "HelixFacetEvent",
3250
+ "module": "src/events/events.ts"
3251
+ }
3252
+ },
3133
3253
  {
3134
3254
  "kind": "js",
3135
3255
  "name": "HelixFilterEvent",
@@ -3154,6 +3274,22 @@
3154
3274
  "module": "src/events/events.ts"
3155
3275
  }
3156
3276
  },
3277
+ {
3278
+ "kind": "js",
3279
+ "name": "HelixReorderEvent",
3280
+ "declaration": {
3281
+ "name": "HelixReorderEvent",
3282
+ "module": "src/events/events.ts"
3283
+ }
3284
+ },
3285
+ {
3286
+ "kind": "js",
3287
+ "name": "HelixHistoryEvent",
3288
+ "declaration": {
3289
+ "name": "HelixHistoryEvent",
3290
+ "module": "src/events/events.ts"
3291
+ }
3292
+ },
3157
3293
  {
3158
3294
  "kind": "js",
3159
3295
  "name": "helixEventNames",
@@ -3256,6 +3392,92 @@
3256
3392
  "privacy": "private",
3257
3393
  "description": "Resolves `whenSettled`, once, from the frame that records the state."
3258
3394
  },
3395
+ {
3396
+ "kind": "field",
3397
+ "name": "fromServer",
3398
+ "type": {
3399
+ "text": "boolean"
3400
+ },
3401
+ "privacy": "private",
3402
+ "default": "false",
3403
+ "description": "Whether the shadow root was a server's, parsed from declarative shadow DOM."
3404
+ },
3405
+ {
3406
+ "kind": "field",
3407
+ "name": "hydrating",
3408
+ "type": {
3409
+ "text": "boolean"
3410
+ },
3411
+ "privacy": "protected",
3412
+ "description": "Whether this update is hydrating a server's markup, and so has to render\nexactly what the server rendered.\n\n**Lit hydrates by assuming the markup already shows the first render.** A\nvalue that differs is recorded and never written, so the page keeps the\nserver's, and a template that differs throws. The server has no children,\nso anything a component reads from its children waits for the update after\nhydration, which `finishHydration` asks for. Without a server's markup the\nfirst render reads them and there is one render.",
3413
+ "readonly": true
3414
+ },
3415
+ {
3416
+ "kind": "method",
3417
+ "name": "finishHydration",
3418
+ "privacy": "protected",
3419
+ "return": {
3420
+ "type": {
3421
+ "text": "void"
3422
+ }
3423
+ },
3424
+ "description": "Asks for the update a hydration put off, so the reads it skipped happen.\n\nCalled from `firstUpdated`, the end of the hydrating update. The render it\nschedules can only follow that one, which is the exception Lit's\nchange-in-update warning names, and it happens only on a page a server\nrendered."
3425
+ },
3426
+ {
3427
+ "kind": "field",
3428
+ "name": "childrenKnown",
3429
+ "type": {
3430
+ "text": "boolean"
3431
+ },
3432
+ "privacy": "protected",
3433
+ "description": "Whether this render can know the children.\n\nA server cannot: its element shim carries attributes and no tree, so\n`children` is undefined there. A render hydrating what a server sent must\nnot, because it has to be the server's render. Anywhere else it can. A\ncomponent whose render depends on its children reads them through\n`knownChildren`, and treats \"not known\" as its own answer rather than as\n\"none\": a list that renders its empty state because nobody has looked yet is\nthe one thing an empty state must never do.",
3434
+ "readonly": true
3435
+ },
3436
+ {
3437
+ "kind": "field",
3438
+ "name": "knownChildren",
3439
+ "type": {
3440
+ "text": "Element[]"
3441
+ },
3442
+ "privacy": "protected",
3443
+ "description": "The children a render may read: all of them where they are known, and none otherwise.",
3444
+ "readonly": true
3445
+ },
3446
+ {
3447
+ "kind": "field",
3448
+ "name": "assignsByName",
3449
+ "type": {
3450
+ "text": "boolean"
3451
+ },
3452
+ "privacy": "protected",
3453
+ "description": "Whether this element's slots route by name although it asked to assign them\nby hand.\n\n**A declarative shadow root is always the by-name kind.** HTML has no way to\nask for manual assignment, so a component that assigns by hand and hydrates\na server's root gets one where `slot.assign()` does nothing, and its\nchildren land wherever their `slot` attribute sends them. The way through is\nto write that attribute, which is what `route` does. Only there: on a root\nthis element made, assignment stays manual and the consumer's markup is\nnever touched, which is what a React consumer's hydration requires.",
3454
+ "readonly": true
3455
+ },
3456
+ {
3457
+ "kind": "method",
3458
+ "name": "route",
3459
+ "privacy": "protected",
3460
+ "return": {
3461
+ "type": {
3462
+ "text": "void"
3463
+ }
3464
+ },
3465
+ "parameters": [
3466
+ {
3467
+ "name": "child",
3468
+ "type": {
3469
+ "text": "Element"
3470
+ }
3471
+ },
3472
+ {
3473
+ "name": "name",
3474
+ "type": {
3475
+ "text": "string | undefined"
3476
+ }
3477
+ }
3478
+ ],
3479
+ "description": "Sends a child to a slot by name, on a root that routes by name.\n\n`undefined` is the default slot. Written only when it differs, so routing\nthe same child again on every update writes nothing."
3480
+ },
3259
3481
  {
3260
3482
  "kind": "field",
3261
3483
  "name": "stopInner",
@@ -3548,6 +3770,120 @@
3548
3770
  "module": "src/helix-element/helix-element.ts"
3549
3771
  }
3550
3772
  },
3773
+ {
3774
+ "kind": "field",
3775
+ "name": "fromServer",
3776
+ "type": {
3777
+ "text": "boolean"
3778
+ },
3779
+ "privacy": "private",
3780
+ "default": "false",
3781
+ "description": "Whether the shadow root was a server's, parsed from declarative shadow DOM.",
3782
+ "inheritedFrom": {
3783
+ "name": "HelixElement",
3784
+ "module": "src/helix-element/helix-element.ts"
3785
+ }
3786
+ },
3787
+ {
3788
+ "kind": "field",
3789
+ "name": "hydrating",
3790
+ "type": {
3791
+ "text": "boolean"
3792
+ },
3793
+ "privacy": "protected",
3794
+ "description": "Whether this update is hydrating a server's markup, and so has to render\nexactly what the server rendered.\n\n**Lit hydrates by assuming the markup already shows the first render.** A\nvalue that differs is recorded and never written, so the page keeps the\nserver's, and a template that differs throws. The server has no children,\nso anything a component reads from its children waits for the update after\nhydration, which `finishHydration` asks for. Without a server's markup the\nfirst render reads them and there is one render.",
3795
+ "readonly": true,
3796
+ "inheritedFrom": {
3797
+ "name": "HelixElement",
3798
+ "module": "src/helix-element/helix-element.ts"
3799
+ }
3800
+ },
3801
+ {
3802
+ "kind": "method",
3803
+ "name": "finishHydration",
3804
+ "privacy": "protected",
3805
+ "return": {
3806
+ "type": {
3807
+ "text": "void"
3808
+ }
3809
+ },
3810
+ "description": "Asks for the update a hydration put off, so the reads it skipped happen.\n\nCalled from `firstUpdated`, the end of the hydrating update. The render it\nschedules can only follow that one, which is the exception Lit's\nchange-in-update warning names, and it happens only on a page a server\nrendered.",
3811
+ "inheritedFrom": {
3812
+ "name": "HelixElement",
3813
+ "module": "src/helix-element/helix-element.ts"
3814
+ }
3815
+ },
3816
+ {
3817
+ "kind": "field",
3818
+ "name": "childrenKnown",
3819
+ "type": {
3820
+ "text": "boolean"
3821
+ },
3822
+ "privacy": "protected",
3823
+ "description": "Whether this render can know the children.\n\nA server cannot: its element shim carries attributes and no tree, so\n`children` is undefined there. A render hydrating what a server sent must\nnot, because it has to be the server's render. Anywhere else it can. A\ncomponent whose render depends on its children reads them through\n`knownChildren`, and treats \"not known\" as its own answer rather than as\n\"none\": a list that renders its empty state because nobody has looked yet is\nthe one thing an empty state must never do.",
3824
+ "readonly": true,
3825
+ "inheritedFrom": {
3826
+ "name": "HelixElement",
3827
+ "module": "src/helix-element/helix-element.ts"
3828
+ }
3829
+ },
3830
+ {
3831
+ "kind": "field",
3832
+ "name": "knownChildren",
3833
+ "type": {
3834
+ "text": "Element[]"
3835
+ },
3836
+ "privacy": "protected",
3837
+ "description": "The children a render may read: all of them where they are known, and none otherwise.",
3838
+ "readonly": true,
3839
+ "inheritedFrom": {
3840
+ "name": "HelixElement",
3841
+ "module": "src/helix-element/helix-element.ts"
3842
+ }
3843
+ },
3844
+ {
3845
+ "kind": "field",
3846
+ "name": "assignsByName",
3847
+ "type": {
3848
+ "text": "boolean"
3849
+ },
3850
+ "privacy": "protected",
3851
+ "description": "Whether this element's slots route by name although it asked to assign them\nby hand.\n\n**A declarative shadow root is always the by-name kind.** HTML has no way to\nask for manual assignment, so a component that assigns by hand and hydrates\na server's root gets one where `slot.assign()` does nothing, and its\nchildren land wherever their `slot` attribute sends them. The way through is\nto write that attribute, which is what `route` does. Only there: on a root\nthis element made, assignment stays manual and the consumer's markup is\nnever touched, which is what a React consumer's hydration requires.",
3852
+ "readonly": true,
3853
+ "inheritedFrom": {
3854
+ "name": "HelixElement",
3855
+ "module": "src/helix-element/helix-element.ts"
3856
+ }
3857
+ },
3858
+ {
3859
+ "kind": "method",
3860
+ "name": "route",
3861
+ "privacy": "protected",
3862
+ "return": {
3863
+ "type": {
3864
+ "text": "void"
3865
+ }
3866
+ },
3867
+ "parameters": [
3868
+ {
3869
+ "name": "child",
3870
+ "type": {
3871
+ "text": "Element"
3872
+ }
3873
+ },
3874
+ {
3875
+ "name": "name",
3876
+ "type": {
3877
+ "text": "string | undefined"
3878
+ }
3879
+ }
3880
+ ],
3881
+ "description": "Sends a child to a slot by name, on a root that routes by name.\n\n`undefined` is the default slot. Written only when it differs, so routing\nthe same child again on every update writes nothing.",
3882
+ "inheritedFrom": {
3883
+ "name": "HelixElement",
3884
+ "module": "src/helix-element/helix-element.ts"
3885
+ }
3886
+ },
3551
3887
  {
3552
3888
  "kind": "field",
3553
3889
  "name": "stopInner",
@@ -4017,6 +4353,14 @@
4017
4353
  "module": "./events/events.js"
4018
4354
  }
4019
4355
  },
4356
+ {
4357
+ "kind": "js",
4358
+ "name": "FacetDetail",
4359
+ "declaration": {
4360
+ "name": "FacetDetail",
4361
+ "module": "./events/events.js"
4362
+ }
4363
+ },
4020
4364
  {
4021
4365
  "kind": "js",
4022
4366
  "name": "FilterDetail",
@@ -4169,6 +4513,14 @@
4169
4513
  "module": "./events/events.js"
4170
4514
  }
4171
4515
  },
4516
+ {
4517
+ "kind": "js",
4518
+ "name": "HelixFacetEvent",
4519
+ "declaration": {
4520
+ "name": "HelixFacetEvent",
4521
+ "module": "./events/events.js"
4522
+ }
4523
+ },
4172
4524
  {
4173
4525
  "kind": "js",
4174
4526
  "name": "HelixFilterEvent",
@@ -4177,6 +4529,14 @@
4177
4529
  "module": "./events/events.js"
4178
4530
  }
4179
4531
  },
4532
+ {
4533
+ "kind": "js",
4534
+ "name": "HelixHistoryEvent",
4535
+ "declaration": {
4536
+ "name": "HelixHistoryEvent",
4537
+ "module": "./events/events.js"
4538
+ }
4539
+ },
4180
4540
  {
4181
4541
  "kind": "js",
4182
4542
  "name": "HelixNavigateEvent",
@@ -4209,6 +4569,14 @@
4209
4569
  "module": "./events/events.js"
4210
4570
  }
4211
4571
  },
4572
+ {
4573
+ "kind": "js",
4574
+ "name": "HelixReorderEvent",
4575
+ "declaration": {
4576
+ "name": "HelixReorderEvent",
4577
+ "module": "./events/events.js"
4578
+ }
4579
+ },
4212
4580
  {
4213
4581
  "kind": "js",
4214
4582
  "name": "HelixResizeEvent",
@@ -5448,6 +5816,16 @@
5448
5816
  ],
5449
5817
  "description": "Binds to a rows slot's `slotchange`, and answers `hasRows` from it."
5450
5818
  },
5819
+ {
5820
+ "kind": "field",
5821
+ "name": "slotted",
5822
+ "type": {
5823
+ "text": "Element[]"
5824
+ },
5825
+ "privacy": "protected",
5826
+ "description": "What the rows slot is given, read from the host so a first render has it.",
5827
+ "readonly": true
5828
+ },
5451
5829
  {
5452
5830
  "kind": "field",
5453
5831
  "name": "hasRows",
@@ -5515,7 +5893,7 @@
5515
5893
  },
5516
5894
  "privacy": "private",
5517
5895
  "default": "0",
5518
- "description": "How many there were, the last time anyone looked.\n\n**Never name this `rows`.** TypeScript's `private` is a compile-time\nclaim and the runtime field is whatever it is named, so a subclass\ndeclaring `@property() rows` for the data a consumer hands it would share\none field with this counter: the slot count would overwrite the\nconsumer's array on every `updated`, and `hasRows` would read the array's\nlength as a slot count. Nothing would throw. `helix-data-table` takes\nexactly that property."
5896
+ "description": "How many there were, the last time anyone looked.\n\n**Never name this `rows`.** TypeScript's `private` is a compile-time\nclaim and the runtime field is whatever it is named, so a subclass\ndeclaring `@property() rows` for the data a consumer hands it would share\none field with this counter: the slot count would overwrite the\nconsumer's array on every update, and `hasRows` would read the array's\nlength as a slot count. Nothing would throw. `helix-data-table` takes\nexactly that property."
5519
5897
  },
5520
5898
  {
5521
5899
  "kind": "method",
@@ -5545,6 +5923,16 @@
5545
5923
  "privacy": "protected",
5546
5924
  "readonly": true
5547
5925
  },
5926
+ {
5927
+ "kind": "field",
5928
+ "name": "slotted",
5929
+ "type": {
5930
+ "text": "Element[]"
5931
+ },
5932
+ "privacy": "protected",
5933
+ "description": "What the rows slot is given: every child without a `slot`.\n\nThe default slot, because that is where every collection in this library\nputs its rows: Listbox its options, Tree its items, Table its rows, Menu\nand Drilldown their pages. A named slot is a header or a footer, and\nneither is content.\n\n**The host's children rather than the slot's assigned elements**, which\nare the same elements, because a slot exists only once a render has made\nit and the count is wanted by the first render. A collection that assigns\nits slots by hand says what it assigns.",
5934
+ "readonly": true
5935
+ },
5548
5936
  {
5549
5937
  "kind": "method",
5550
5938
  "name": "renderStatus",
@@ -52,6 +52,15 @@ export declare class Heights {
52
52
  * thousand.
53
53
  */
54
54
  indexAt(offset: number, count: number): number;
55
+ /**
56
+ * Moves every height along by `by` places, for a set that grew at its start.
57
+ *
58
+ * A transcript loading its history prepends, and every item already measured
59
+ * is now `by` further down the set. Moved rather than forgotten, because the
60
+ * measurements are still true of the same items: forgetting would put every
61
+ * one of them back at the estimate at the moment the reader is looking at them.
62
+ */
63
+ shift(by: number): void;
55
64
  /**
56
65
  * Drops everything, for a set that is no longer the same set.
57
66
  *
@@ -51,6 +51,19 @@ export class Heights {
51
51
  }
52
52
  return Math.min(low, count - 1);
53
53
  }
54
+ shift(by) {
55
+ if (by === 0) {
56
+ return;
57
+ }
58
+ const moved = [...this.seen].map(([index, size]) => [index + by, size]);
59
+ this.seen.clear();
60
+ for (const [index, size] of moved) {
61
+ if (index >= 0) {
62
+ this.seen.set(index, size);
63
+ }
64
+ }
65
+ this.dirty = 0;
66
+ }
54
67
  forget() {
55
68
  this.seen.clear();
56
69
  this.dirty = 0;
@@ -138,6 +138,8 @@ export declare class VirtualizeController implements ReactiveController {
138
138
  window: Window;
139
139
  /** Where each item starts, once any of them has been seen. */
140
140
  private readonly heights;
141
+ /** Room `shift` added above the window, owed to the scroll offset once drawn. */
142
+ private owed;
141
143
  constructor(host: ReactiveControllerHost, options: VirtualizeOptions);
142
144
  hostConnected(): void;
143
145
  hostDisconnected(): void;
@@ -183,6 +185,19 @@ export declare class VirtualizeController implements ReactiveController {
183
185
  * Skipped at the very top, where there is nothing above to have moved.
184
186
  */
185
187
  hostUpdated(): void;
188
+ /**
189
+ * Tells the controller the set grew by `by` items at its start.
190
+ *
191
+ * For a host that prepends, which is what a transcript loading its history
192
+ * does. Every index the controller knew now names the item `by` places later,
193
+ * so the heights and the window move with them, and the scroll offset is moved
194
+ * by the room the new items take once they have been drawn. The reader stays
195
+ * on the turn they were reading rather than on whatever now sits at their old
196
+ * offset, which is what loading history upward has to look like.
197
+ *
198
+ * Call it from `willUpdate`, before the render that draws the grown set.
199
+ */
200
+ shift(by: number): void;
186
201
  /**
187
202
  * Drops every measurement, for a host that swapped its data.
188
203
  *
@@ -6,6 +6,7 @@ export class VirtualizeController {
6
6
  this.settling = 0;
7
7
  this.warned = false;
8
8
  this.window = { after: 0, before: 0, first: 0, last: 0 };
9
+ this.owed = 0;
9
10
  this.handleScroll = () => {
10
11
  this.settling = 0;
11
12
  this.measure();
@@ -95,6 +96,13 @@ export class VirtualizeController {
95
96
  };
96
97
  }
97
98
  hostUpdated() {
99
+ if (this.owed !== 0) {
100
+ const box = this.options.container();
101
+ if (box) {
102
+ box.scrollTop += this.owed;
103
+ }
104
+ this.owed = 0;
105
+ }
98
106
  if (!this.measuring()) {
99
107
  return;
100
108
  }
@@ -121,6 +129,21 @@ export class VirtualizeController {
121
129
  }
122
130
  this.measure();
123
131
  }
132
+ shift(by) {
133
+ if (by <= 0) {
134
+ return;
135
+ }
136
+ const count = Math.max(0, this.options.count());
137
+ this.heights.shift(by);
138
+ const room = this.heights.before(by, count);
139
+ this.window = {
140
+ ...this.window,
141
+ before: this.window.before + room,
142
+ first: this.window.first + by,
143
+ last: this.window.last + by,
144
+ };
145
+ this.owed += room;
146
+ }
124
147
  forget() {
125
148
  this.heights.forget();
126
149
  }
@@ -80,6 +80,43 @@ export interface FilterDetail {
80
80
  /** What the list is being narrowed to. */
81
81
  value: string;
82
82
  }
83
+ /**
84
+ * Detail carried by `helix-facet`.
85
+ *
86
+ * **Everything needed to draw the chip, so nothing has to be looked up again.**
87
+ * A consumer hearing this renders a `FilterChip` from it and stops: the labels
88
+ * are the reader's words and the names are their own model's, and both are here
89
+ * because only the thing that read the declarations knows both.
90
+ */
91
+ export interface FacetDetail {
92
+ /**
93
+ * How many values the comparison in force takes.
94
+ *
95
+ * **What tells an empty filter from no filter.** `values` empty usually means
96
+ * the field is not being filtered any more, and a consumer drops the chip; a
97
+ * comparison of arity `none`, `is empty` or `is set`, is a filter that has no
98
+ * values to carry and never will. Without this the two arrive identical, and
99
+ * the thing that read the declaration is the only thing that could say which.
100
+ */
101
+ arity: 'many' | 'none' | 'one';
102
+ /** What the consumer's model calls the field. */
103
+ field: string;
104
+ /** What a reader is shown for the field. */
105
+ label: string;
106
+ /** What the consumer's model calls the comparison in force. */
107
+ operator: string;
108
+ /**
109
+ * The comparison as a reader sees it, and **empty for the field's usual one**.
110
+ *
111
+ * Passed straight to a chip's `operator-label`, which draws the segment only
112
+ * where there is one. Deciding it here is the point: the thing that read the
113
+ * field knows which of its operators is the default, and the chip never has
114
+ * to.
115
+ */
116
+ operatorLabel: string;
117
+ /** What the field is compared to, as the consumer's own values. */
118
+ values: readonly string[];
119
+ }
83
120
  /** Detail carried by `helix-read`. */
84
121
  export interface ReadDetail {
85
122
  /**
@@ -589,6 +626,22 @@ export declare class HelixSortEvent extends CustomEvent<SortDetail> {
589
626
  static readonly eventName = "helix-sort";
590
627
  constructor(detail: SortDetail);
591
628
  }
629
+ /**
630
+ * Fired when the reader changes which values a field is filtered by.
631
+ *
632
+ * **Not `helix-filter`, which is a different thing already taken.** That one
633
+ * says a list narrowed itself to a string, and `Data Table` and `Listbox` both
634
+ * dispatch it with exactly that meaning. This one carries a structured filter:
635
+ * a field, a comparison and a set of values, which is a shape neither of those
636
+ * has and neither should grow.
637
+ *
638
+ * Cancelable, and refusing it is how a consumer takes the decision for one
639
+ * interaction, the way `requestChange` documents for every stateful change here.
640
+ */
641
+ export declare class HelixFacetEvent extends CustomEvent<FacetDetail> {
642
+ static readonly eventName = "helix-facet";
643
+ constructor(detail: FacetDetail);
644
+ }
592
645
  /**
593
646
  * Fired when a component narrows what it is showing.
594
647
  *
@@ -633,6 +686,39 @@ export declare class HelixSelectEvent extends CustomEvent<SelectDetail> {
633
686
  static readonly eventName = "helix-select";
634
687
  constructor(detail: SelectDetail);
635
688
  }
689
+ /**
690
+ * Fired when the reader asks for the columns in a different order.
691
+ *
692
+ * Cancelable, and refusing it leaves the columns where they were, so a consumer
693
+ * who stores the order, in a URL or a preference, can take the decision the way
694
+ * a server-side sort takes `helix-sort`. The detail is the whole order as
695
+ * space-separated fields rather than one move, because the order is the fact a
696
+ * consumer stores, and a move is only meaningful against an order they would
697
+ * then have to keep in step.
698
+ *
699
+ * Not `helix-sort`: that reorders the rows, this reorders the columns, and a
700
+ * consumer listening for one should not hear the other.
701
+ */
702
+ export declare class HelixReorderEvent extends CustomEvent<ChangeDetail> {
703
+ static readonly eventName = "helix-reorder";
704
+ constructor(detail: ChangeDetail);
705
+ }
706
+ /**
707
+ * Fired when a reader reaches the oldest turn a transcript holds, while there are
708
+ * older ones to fetch.
709
+ *
710
+ * **A report of where the reader is, not a request to be granted**, so it is not
711
+ * cancelable: there is nothing for the transcript to do if nobody refuses, and
712
+ * nothing to stop if somebody does. The consumer answers by fetching, and says
713
+ * so through the transcript's `history` while the older turns are on their way.
714
+ *
715
+ * No detail. The consumer holds the transcript and knows its oldest turn, which
716
+ * is the one thing a fetch needs.
717
+ */
718
+ export declare class HelixHistoryEvent extends CustomEvent<Record<string, never>> {
719
+ static readonly eventName = "helix-history";
720
+ constructor();
721
+ }
636
722
  /**
637
723
  * Every event this file publishes, and the one place a new one is added.
638
724
  *
@@ -663,11 +749,14 @@ declare const registry: {
663
749
  readonly HelixCopyEvent: typeof HelixCopyEvent;
664
750
  readonly HelixCreateEvent: typeof HelixCreateEvent;
665
751
  readonly HelixDismissEvent: typeof HelixDismissEvent;
752
+ readonly HelixFacetEvent: typeof HelixFacetEvent;
666
753
  readonly HelixFilterEvent: typeof HelixFilterEvent;
754
+ readonly HelixHistoryEvent: typeof HelixHistoryEvent;
667
755
  readonly HelixNavigateEvent: typeof HelixNavigateEvent;
668
756
  readonly HelixNextEvent: typeof HelixNextEvent;
669
757
  readonly HelixPreviousEvent: typeof HelixPreviousEvent;
670
758
  readonly HelixReadEvent: typeof HelixReadEvent;
759
+ readonly HelixReorderEvent: typeof HelixReorderEvent;
671
760
  readonly HelixResizeEvent: typeof HelixResizeEvent;
672
761
  readonly HelixSampleEndEvent: typeof HelixSampleEndEvent;
673
762
  readonly HelixSampleStartEvent: typeof HelixSampleStartEvent;
@@ -306,6 +306,17 @@ export class HelixSortEvent extends CustomEvent {
306
306
  });
307
307
  }
308
308
  }
309
+ export class HelixFacetEvent extends CustomEvent {
310
+ static { this.eventName = 'helix-facet'; }
311
+ constructor(detail) {
312
+ super(HelixFacetEvent.eventName, {
313
+ bubbles: true,
314
+ cancelable: true,
315
+ composed: true,
316
+ detail,
317
+ });
318
+ }
319
+ }
309
320
  export class HelixFilterEvent extends CustomEvent {
310
321
  static { this.eventName = 'helix-filter'; }
311
322
  constructor(detail) {
@@ -339,6 +350,28 @@ export class HelixSelectEvent extends CustomEvent {
339
350
  });
340
351
  }
341
352
  }
353
+ export class HelixReorderEvent extends CustomEvent {
354
+ static { this.eventName = 'helix-reorder'; }
355
+ constructor(detail) {
356
+ super(HelixReorderEvent.eventName, {
357
+ bubbles: true,
358
+ cancelable: true,
359
+ composed: true,
360
+ detail,
361
+ });
362
+ }
363
+ }
364
+ export class HelixHistoryEvent extends CustomEvent {
365
+ static { this.eventName = 'helix-history'; }
366
+ constructor() {
367
+ super(HelixHistoryEvent.eventName, {
368
+ bubbles: true,
369
+ cancelable: false,
370
+ composed: true,
371
+ detail: {},
372
+ });
373
+ }
374
+ }
342
375
  const registry = {
343
376
  HelixActivateEvent,
344
377
  HelixBeforeActivateEvent,
@@ -358,11 +391,14 @@ const registry = {
358
391
  HelixCopyEvent,
359
392
  HelixCreateEvent,
360
393
  HelixDismissEvent,
394
+ HelixFacetEvent,
361
395
  HelixFilterEvent,
396
+ HelixHistoryEvent,
362
397
  HelixNavigateEvent,
363
398
  HelixNextEvent,
364
399
  HelixPreviousEvent,
365
400
  HelixReadEvent,
401
+ HelixReorderEvent,
366
402
  HelixResizeEvent,
367
403
  HelixSampleEndEvent,
368
404
  HelixSampleStartEvent,
@@ -119,6 +119,64 @@ export declare class HelixElement extends LitElement {
119
119
  /** Resolves `whenSettled`, once, from the frame that records the state. */
120
120
  private resolveSettled;
121
121
  constructor();
122
+ /** Whether the shadow root was a server's, parsed from declarative shadow DOM. */
123
+ private fromServer;
124
+ protected createRenderRoot(): HTMLElement | DocumentFragment;
125
+ /**
126
+ * Whether this update is hydrating a server's markup, and so has to render
127
+ * exactly what the server rendered.
128
+ *
129
+ * **Lit hydrates by assuming the markup already shows the first render.** A
130
+ * value that differs is recorded and never written, so the page keeps the
131
+ * server's, and a template that differs throws. The server has no children,
132
+ * so anything a component reads from its children waits for the update after
133
+ * hydration, which `finishHydration` asks for. Without a server's markup the
134
+ * first render reads them and there is one render.
135
+ */
136
+ protected get hydrating(): boolean;
137
+ /**
138
+ * Asks for the update a hydration put off, so the reads it skipped happen.
139
+ *
140
+ * Called from `firstUpdated`, the end of the hydrating update. The render it
141
+ * schedules can only follow that one, which is the exception Lit's
142
+ * change-in-update warning names, and it happens only on a page a server
143
+ * rendered.
144
+ */
145
+ protected finishHydration(): void;
146
+ /**
147
+ * Whether this render can know the children.
148
+ *
149
+ * A server cannot: its element shim carries attributes and no tree, so
150
+ * `children` is undefined there. A render hydrating what a server sent must
151
+ * not, because it has to be the server's render. Anywhere else it can. A
152
+ * component whose render depends on its children reads them through
153
+ * `knownChildren`, and treats "not known" as its own answer rather than as
154
+ * "none": a list that renders its empty state because nobody has looked yet is
155
+ * the one thing an empty state must never do.
156
+ */
157
+ protected get childrenKnown(): boolean;
158
+ /** The children a render may read: all of them where they are known, and none otherwise. */
159
+ protected get knownChildren(): Element[];
160
+ /**
161
+ * Whether this element's slots route by name although it asked to assign them
162
+ * by hand.
163
+ *
164
+ * **A declarative shadow root is always the by-name kind.** HTML has no way to
165
+ * ask for manual assignment, so a component that assigns by hand and hydrates
166
+ * a server's root gets one where `slot.assign()` does nothing, and its
167
+ * children land wherever their `slot` attribute sends them. The way through is
168
+ * to write that attribute, which is what `route` does. Only there: on a root
169
+ * this element made, assignment stays manual and the consumer's markup is
170
+ * never touched, which is what a React consumer's hydration requires.
171
+ */
172
+ protected get assignsByName(): boolean;
173
+ /**
174
+ * Sends a child to a slot by name, on a root that routes by name.
175
+ *
176
+ * `undefined` is the default slot. Written only when it differs, so routing
177
+ * the same child again on every update writes nothing.
178
+ */
179
+ protected route(child: Element, name: string | undefined): void;
122
180
  /**
123
181
  * Announces an event, having first bound whatever React is holding for it.
124
182
  *
@@ -130,6 +130,7 @@ export class HelixElement extends LitElement {
130
130
  constructor() {
131
131
  super();
132
132
  this.resolveSettled = () => { };
133
+ this.fromServer = false;
133
134
  this.stopInner = (event) => {
134
135
  if (event.target !== this) {
135
136
  event.stopPropagation();
@@ -140,6 +141,39 @@ export class HelixElement extends LitElement {
140
141
  this.resolveSettled = resolve;
141
142
  });
142
143
  }
144
+ createRenderRoot() {
145
+ this.fromServer = this.shadowRoot !== null;
146
+ return super.createRenderRoot();
147
+ }
148
+ get hydrating() {
149
+ return this.fromServer && !this.hasUpdated;
150
+ }
151
+ finishHydration() {
152
+ if (this.fromServer) {
153
+ this.requestUpdate();
154
+ }
155
+ }
156
+ get childrenKnown() {
157
+ return this.children !== undefined && !this.hydrating;
158
+ }
159
+ get knownChildren() {
160
+ return this.childrenKnown ? [...this.children] : [];
161
+ }
162
+ get assignsByName() {
163
+ const asked = this.constructor.shadowRootOptions.slotAssignment;
164
+ return (asked === 'manual' &&
165
+ typeof ShadowRoot !== 'undefined' &&
166
+ this.renderRoot instanceof ShadowRoot &&
167
+ this.renderRoot.slotAssignment !== 'manual');
168
+ }
169
+ route(child, name) {
170
+ if (name === undefined) {
171
+ child.removeAttribute('slot');
172
+ }
173
+ else if (child.getAttribute('slot') !== name) {
174
+ child.setAttribute('slot', name);
175
+ }
176
+ }
143
177
  dispatchEvent(event) {
144
178
  bindReactHandler(this, event);
145
179
  return super.dispatchEvent(event);
package/dist/index.d.ts CHANGED
@@ -14,7 +14,7 @@ export { SourceController, type SourceHost, } from './controllers/source-control
14
14
  export { type TokenColor, TokenColorController, } from './controllers/token-color-controller/token-color-controller.js';
15
15
  export { type Rendered, VirtualizeController, type VirtualizeOptions, type Window as VirtualizeWindow, } from './controllers/virtualize-controller/virtualize-controller.js';
16
16
  export { eventsForTag, registerElementEvents } from './events/element-events.js';
17
- export { type CaretDetail, type ChangeDetail, type CompleteDetail, type ConsoleDetail, type CopyDetail, type CreateDetail, type DismissDetail, type FilterDetail, HelixActivateEvent, HelixBeforeActivateEvent, HelixBeforeChangeEvent, HelixBeforeCloseEvent, HelixBeforeCollapseEvent, HelixBeforeEditEvent, HelixBeforeOpenEvent, HelixBeforePanelResizeEvent, HelixBeforePinEvent, HelixBeforeResizeEvent, HelixBeforeSplitEvent, HelixBeforeToggleEvent, HelixCaretEvent, HelixCompleteEvent, HelixConsoleEvent, HelixCopyEvent, HelixCreateEvent, HelixDismissEvent, HelixFilterEvent, HelixNavigateEvent, HelixNextEvent, HelixPreviousEvent, HelixReadEvent, HelixResizeEvent, HelixSampleEndEvent, HelixSampleStartEvent, HelixScrolledEvent, HelixSelectEvent, HelixSendEvent, HelixShortcutEvent, HelixSortEvent, HelixTourAskEvent, type NavigateDetail, type OpenDetail, type ReadDetail, type ResizeDetail, type ScrolledDetail, type SelectDetail, type SendDetail, type ShortcutDetail, type SkipDetail, type SortDetail, type ToggleDetail, } from './events/events.js';
17
+ export { type CaretDetail, type ChangeDetail, type CompleteDetail, type ConsoleDetail, type CopyDetail, type CreateDetail, type DismissDetail, type FacetDetail, type FilterDetail, HelixActivateEvent, HelixBeforeActivateEvent, HelixBeforeChangeEvent, HelixBeforeCloseEvent, HelixBeforeCollapseEvent, HelixBeforeEditEvent, HelixBeforeOpenEvent, HelixBeforePanelResizeEvent, HelixBeforePinEvent, HelixBeforeResizeEvent, HelixBeforeSplitEvent, HelixBeforeToggleEvent, HelixCaretEvent, HelixCompleteEvent, HelixConsoleEvent, HelixCopyEvent, HelixCreateEvent, HelixDismissEvent, HelixFacetEvent, HelixFilterEvent, HelixHistoryEvent, HelixNavigateEvent, HelixNextEvent, HelixPreviousEvent, HelixReadEvent, HelixReorderEvent, HelixResizeEvent, HelixSampleEndEvent, HelixSampleStartEvent, HelixScrolledEvent, HelixSelectEvent, HelixSendEvent, HelixShortcutEvent, HelixSortEvent, HelixTourAskEvent, type NavigateDetail, type OpenDetail, type ReadDetail, type ResizeDetail, type ScrolledDetail, type SelectDetail, type SendDetail, type ShortcutDetail, type SkipDetail, type SortDetail, type ToggleDetail, } from './events/events.js';
18
18
  export { HelixElement } from './helix-element/helix-element.js';
19
19
  export { HelixFormElement } from './helix-form-element/helix-form-element.js';
20
20
  export { MixinChartSource, type MixinChartSourceInterface, PALETTE, seriesIndex, } from './mixins/mixin-chart-source/mixin-chart-source.js';
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ export { SourceController, } from './controllers/source-controller/source-contro
14
14
  export { TokenColorController, } from './controllers/token-color-controller/token-color-controller.js';
15
15
  export { VirtualizeController, } from './controllers/virtualize-controller/virtualize-controller.js';
16
16
  export { eventsForTag, registerElementEvents } from './events/element-events.js';
17
- export { HelixActivateEvent, HelixBeforeActivateEvent, HelixBeforeChangeEvent, HelixBeforeCloseEvent, HelixBeforeCollapseEvent, HelixBeforeEditEvent, HelixBeforeOpenEvent, HelixBeforePanelResizeEvent, HelixBeforePinEvent, HelixBeforeResizeEvent, HelixBeforeSplitEvent, HelixBeforeToggleEvent, HelixCaretEvent, HelixCompleteEvent, HelixConsoleEvent, HelixCopyEvent, HelixCreateEvent, HelixDismissEvent, HelixFilterEvent, HelixNavigateEvent, HelixNextEvent, HelixPreviousEvent, HelixReadEvent, HelixResizeEvent, HelixSampleEndEvent, HelixSampleStartEvent, HelixScrolledEvent, HelixSelectEvent, HelixSendEvent, HelixShortcutEvent, HelixSortEvent, HelixTourAskEvent, } from './events/events.js';
17
+ export { HelixActivateEvent, HelixBeforeActivateEvent, HelixBeforeChangeEvent, HelixBeforeCloseEvent, HelixBeforeCollapseEvent, HelixBeforeEditEvent, HelixBeforeOpenEvent, HelixBeforePanelResizeEvent, HelixBeforePinEvent, HelixBeforeResizeEvent, HelixBeforeSplitEvent, HelixBeforeToggleEvent, HelixCaretEvent, HelixCompleteEvent, HelixConsoleEvent, HelixCopyEvent, HelixCreateEvent, HelixDismissEvent, HelixFacetEvent, HelixFilterEvent, HelixHistoryEvent, HelixNavigateEvent, HelixNextEvent, HelixPreviousEvent, HelixReadEvent, HelixReorderEvent, HelixResizeEvent, HelixSampleEndEvent, HelixSampleStartEvent, HelixScrolledEvent, HelixSelectEvent, HelixSendEvent, HelixShortcutEvent, HelixSortEvent, HelixTourAskEvent, } from './events/events.js';
18
18
  export { HelixElement } from './helix-element/helix-element.js';
19
19
  export { HelixFormElement } from './helix-form-element/helix-form-element.js';
20
20
  export { MixinChartSource, PALETTE, seriesIndex, } from './mixins/mixin-chart-source/mixin-chart-source.js';
@@ -1,4 +1,5 @@
1
- import { type LitElement, nothing, type TemplateResult } from 'lit';
1
+ import { nothing, type TemplateResult } from 'lit';
2
+ import type { HelixElement } from '../../helix-element/helix-element.js';
2
3
  type Constructor<T> = new (...args: any[]) => T;
3
4
  /**
4
5
  * The API a collection has for the two states that are not its content.
@@ -18,6 +19,8 @@ export declare class CollectionElement {
18
19
  protected renderStatus(hasContent: boolean): TemplateResult | typeof nothing;
19
20
  /** Binds to a rows slot's `slotchange`, and answers `hasRows` from it. */
20
21
  protected countRows(event: Event): void;
22
+ /** What the rows slot is given, read from the host so a first render has it. */
23
+ protected get slotted(): Element[];
21
24
  /**
22
25
  * Whether there is anything to show, as far as anything can tell.
23
26
  *
@@ -48,5 +51,5 @@ export declare class CollectionElement {
48
51
  * apply this, rather than here.** See `MixinField` for why a class-level tag on
49
52
  * a mixin reaches no manifest.
50
53
  */
51
- export declare const MixinCollection: <T extends Constructor<LitElement>>(Base: T) => Constructor<CollectionElement> & T;
54
+ export declare const MixinCollection: <T extends Constructor<HelixElement>>(Base: T) => Constructor<CollectionElement> & T;
52
55
  export {};
@@ -23,18 +23,24 @@ export const MixinCollection = (Base) => {
23
23
  get hasRows() {
24
24
  return this.slotCounted ? this.slotRows > 0 : true;
25
25
  }
26
- updated(changed) {
27
- super.updated?.(changed);
28
- const slot = this.renderRoot?.querySelector?.('slot:not([name])');
29
- if (!slot) {
26
+ get slotted() {
27
+ return [...(this.children ?? [])].filter((child) => child.slot === '');
28
+ }
29
+ willUpdate(changed) {
30
+ super.willUpdate(changed);
31
+ if (this.children === undefined || this.hydrating) {
30
32
  return;
31
33
  }
32
- const rows = slot.assignedElements().length;
34
+ const rows = this.slotted.length;
33
35
  this.slotCounted = true;
34
36
  if (rows !== this.slotRows) {
35
37
  this.slotRows = rows;
36
38
  }
37
39
  }
40
+ firstUpdated(changed) {
41
+ super.firstUpdated(changed);
42
+ this.finishHydration();
43
+ }
38
44
  renderStatus(hasContent) {
39
45
  if (this.loading) {
40
46
  return html `<p part="loading" role="status">${this.loadingText}</p>`;
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.23.0",
4
+ "version": "0.24.0",
5
5
  "license": "MIT",
6
6
  "author": "Ross Sheppard <ross.sheppard@fusion.dev>",
7
7
  "repository": {
@@ -34,12 +34,12 @@
34
34
  "bugs": "https://github.com/project-fusion/fusion/issues",
35
35
  "dependencies": {
36
36
  "@floating-ui/dom": "^1.8.0",
37
- "@fusion.dev/helix-icons": "0.23.0"
37
+ "@fusion.dev/helix-icons": "0.24.0"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@custom-elements-manifest/analyzer": "^0.11.0",
41
- "@fusion.dev/helix-cem": "0.23.0",
42
- "@fusion.dev/helix-testing": "0.23.0",
41
+ "@fusion.dev/helix-cem": "0.24.0",
42
+ "@fusion.dev/helix-testing": "0.24.0",
43
43
  "lit": "^3.2.1"
44
44
  },
45
45
  "homepage": "https://fusion.dev",