@fusion.dev/helix-core 0.182.2 → 0.184.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.
@@ -5787,6 +5787,16 @@
5787
5787
  "default": "0",
5788
5788
  "description": "The estimate the sums were built against, so a change to it rebuilds."
5789
5789
  },
5790
+ {
5791
+ "kind": "field",
5792
+ "name": "changes",
5793
+ "type": {
5794
+ "text": "number"
5795
+ },
5796
+ "privacy": "private",
5797
+ "default": "0",
5798
+ "description": "Counts each change to a height, for a placement built from them."
5799
+ },
5790
5800
  {
5791
5801
  "kind": "method",
5792
5802
  "name": "at",
@@ -5853,6 +5863,16 @@
5853
5863
  ],
5854
5864
  "description": "The room items `[0, index)` take."
5855
5865
  },
5866
+ {
5867
+ "kind": "method",
5868
+ "name": "stamp",
5869
+ "return": {
5870
+ "type": {
5871
+ "text": "string"
5872
+ }
5873
+ },
5874
+ "description": "Says which heights these are: a placement built from a stamp it was\ngiven again holds, and one built from another stamp is out of date."
5875
+ },
5856
5876
  {
5857
5877
  "kind": "method",
5858
5878
  "name": "total",
@@ -6476,6 +6496,75 @@
6476
6496
  },
6477
6497
  "description": "Whether this pass places by measurement: offered, and wanted or needed."
6478
6498
  },
6499
+ {
6500
+ "kind": "method",
6501
+ "name": "laned",
6502
+ "privacy": "private",
6503
+ "return": {
6504
+ "type": {
6505
+ "text": "Waterfall | undefined"
6506
+ }
6507
+ },
6508
+ "parameters": [
6509
+ {
6510
+ "name": "total",
6511
+ "type": {
6512
+ "text": "number"
6513
+ }
6514
+ }
6515
+ ],
6516
+ "description": "The waterfall the host lays its items out in, placed by the heights known now."
6517
+ },
6518
+ {
6519
+ "kind": "method",
6520
+ "name": "startOf",
6521
+ "privacy": "private",
6522
+ "return": {
6523
+ "type": {
6524
+ "text": "number"
6525
+ }
6526
+ },
6527
+ "parameters": [
6528
+ {
6529
+ "name": "index",
6530
+ "type": {
6531
+ "text": "number"
6532
+ }
6533
+ },
6534
+ {
6535
+ "name": "total",
6536
+ "type": {
6537
+ "text": "number"
6538
+ }
6539
+ }
6540
+ ],
6541
+ "description": "Where item `index` starts down the set, before any drift: after the items\nbefore it, or in its column of a waterfall, where the starts never go back\nup either."
6542
+ },
6543
+ {
6544
+ "kind": "method",
6545
+ "name": "indexAt",
6546
+ "privacy": "private",
6547
+ "return": {
6548
+ "type": {
6549
+ "text": "number"
6550
+ }
6551
+ },
6552
+ "parameters": [
6553
+ {
6554
+ "name": "offset",
6555
+ "type": {
6556
+ "text": "number"
6557
+ }
6558
+ },
6559
+ {
6560
+ "name": "total",
6561
+ "type": {
6562
+ "text": "number"
6563
+ }
6564
+ }
6565
+ ],
6566
+ "description": "The item at `offset`: the last to start at or before it, or in a\nwaterfall the first to reach past it."
6567
+ },
6479
6568
  {
6480
6569
  "kind": "method",
6481
6570
  "name": "placed",
@@ -6701,6 +6790,291 @@
6701
6790
  }
6702
6791
  ]
6703
6792
  },
6793
+ {
6794
+ "kind": "javascript-module",
6795
+ "path": "src/controllers/virtualize-controller/waterfall.ts",
6796
+ "declarations": [
6797
+ {
6798
+ "kind": "class",
6799
+ "description": "Where each item of a waterfall stands: a set laid out down several columns\nof one width, each item under the shortest column so far, as a masonry wall\nof photos is.\n\n**Its own module, which a host hands to `VirtualizeController`**, so the\nhosts that window a single column carry none of it. The controller asks it\nwhich items reach the box and where each one starts, and the host draws each\nitem at its column and its start.\n\n**Placed in the order of the set, each item under the column that ends\nhighest**, the leftmost of a tie, as React Aria's `WaterfallLayout` and\nTanStack Virtual's lanes place them. So an item's start is never above the\nstart of the item before it, and the first item to reach any offset is found\nby a binary search, as one column finds it.\n\nSizes are the controller's: what was measured, or the estimate, each with\nthe gap below it.",
6800
+ "name": "Waterfall",
6801
+ "members": [
6802
+ {
6803
+ "kind": "field",
6804
+ "name": "lanes",
6805
+ "type": {
6806
+ "text": "number"
6807
+ },
6808
+ "default": "1",
6809
+ "description": "How many columns the items are laid out in, at least one."
6810
+ },
6811
+ {
6812
+ "kind": "field",
6813
+ "name": "starts",
6814
+ "type": {
6815
+ "text": "number[]"
6816
+ },
6817
+ "privacy": "private",
6818
+ "default": "[]",
6819
+ "description": "Where each item starts, in pixels from the top of the set."
6820
+ },
6821
+ {
6822
+ "kind": "field",
6823
+ "name": "columns",
6824
+ "type": {
6825
+ "text": "number[]"
6826
+ },
6827
+ "privacy": "private",
6828
+ "default": "[]",
6829
+ "description": "The column each item stands in."
6830
+ },
6831
+ {
6832
+ "kind": "field",
6833
+ "name": "sizes",
6834
+ "type": {
6835
+ "text": "number[]"
6836
+ },
6837
+ "privacy": "private",
6838
+ "default": "[]",
6839
+ "description": "How much room each item takes, with the gap below it."
6840
+ },
6841
+ {
6842
+ "kind": "field",
6843
+ "name": "reach",
6844
+ "type": {
6845
+ "text": "number[]"
6846
+ },
6847
+ "privacy": "private",
6848
+ "default": "[]",
6849
+ "description": "How far down the set the items `[0, i]` reach, the furthest of their ends."
6850
+ },
6851
+ {
6852
+ "kind": "field",
6853
+ "name": "extent",
6854
+ "type": {
6855
+ "text": "number"
6856
+ },
6857
+ "privacy": "private",
6858
+ "default": "0",
6859
+ "description": "How long the set is: where its longest column ends."
6860
+ },
6861
+ {
6862
+ "kind": "field",
6863
+ "name": "built",
6864
+ "type": {
6865
+ "text": "string"
6866
+ },
6867
+ "privacy": "private",
6868
+ "default": "''",
6869
+ "description": "What the placement was built from, so it is built again only when that changes."
6870
+ },
6871
+ {
6872
+ "kind": "method",
6873
+ "name": "place",
6874
+ "return": {
6875
+ "type": {
6876
+ "text": "void"
6877
+ }
6878
+ },
6879
+ "parameters": [
6880
+ {
6881
+ "name": "count",
6882
+ "type": {
6883
+ "text": "number"
6884
+ }
6885
+ },
6886
+ {
6887
+ "name": "size",
6888
+ "type": {
6889
+ "text": "(index: number) => number"
6890
+ }
6891
+ },
6892
+ {
6893
+ "name": "stamp",
6894
+ "type": {
6895
+ "text": "string"
6896
+ }
6897
+ }
6898
+ ],
6899
+ "description": "Places `count` items whose sizes `size` gives, unless `stamp` says the\nsizes, the count and the columns are the ones already placed."
6900
+ },
6901
+ {
6902
+ "kind": "method",
6903
+ "name": "start",
6904
+ "return": {
6905
+ "type": {
6906
+ "text": "number"
6907
+ }
6908
+ },
6909
+ "parameters": [
6910
+ {
6911
+ "name": "index",
6912
+ "type": {
6913
+ "text": "number"
6914
+ }
6915
+ }
6916
+ ],
6917
+ "description": "Where item `index` starts, and where the set ends for an index past it."
6918
+ },
6919
+ {
6920
+ "kind": "method",
6921
+ "name": "lane",
6922
+ "return": {
6923
+ "type": {
6924
+ "text": "number"
6925
+ }
6926
+ },
6927
+ "parameters": [
6928
+ {
6929
+ "name": "index",
6930
+ "type": {
6931
+ "text": "number"
6932
+ }
6933
+ }
6934
+ ],
6935
+ "description": "The column item `index` stands in."
6936
+ },
6937
+ {
6938
+ "kind": "method",
6939
+ "name": "length",
6940
+ "return": {
6941
+ "type": {
6942
+ "text": "number"
6943
+ }
6944
+ },
6945
+ "description": "How long the set is, where its longest column ends."
6946
+ },
6947
+ {
6948
+ "kind": "method",
6949
+ "name": "from",
6950
+ "return": {
6951
+ "type": {
6952
+ "text": "number"
6953
+ }
6954
+ },
6955
+ "parameters": [
6956
+ {
6957
+ "name": "offset",
6958
+ "type": {
6959
+ "text": "number"
6960
+ }
6961
+ }
6962
+ ],
6963
+ "description": "The first item that reaches past `offset`, or the count where none does."
6964
+ },
6965
+ {
6966
+ "kind": "method",
6967
+ "name": "to",
6968
+ "return": {
6969
+ "type": {
6970
+ "text": "number"
6971
+ }
6972
+ },
6973
+ "parameters": [
6974
+ {
6975
+ "name": "offset",
6976
+ "type": {
6977
+ "text": "number"
6978
+ }
6979
+ }
6980
+ ],
6981
+ "description": "The first item that starts at or past `offset`, so every item before it starts above."
6982
+ },
6983
+ {
6984
+ "kind": "method",
6985
+ "name": "search",
6986
+ "privacy": "private",
6987
+ "return": {
6988
+ "type": {
6989
+ "text": "number"
6990
+ }
6991
+ },
6992
+ "parameters": [
6993
+ {
6994
+ "name": "sorted",
6995
+ "type": {
6996
+ "text": "readonly number[]"
6997
+ }
6998
+ },
6999
+ {
7000
+ "name": "offset",
7001
+ "type": {
7002
+ "text": "number"
7003
+ }
7004
+ }
7005
+ ],
7006
+ "description": "The first index whose number in `sorted` is past `offset`."
7007
+ },
7008
+ {
7009
+ "kind": "method",
7010
+ "name": "step",
7011
+ "return": {
7012
+ "type": {
7013
+ "text": "number | undefined"
7014
+ }
7015
+ },
7016
+ "parameters": [
7017
+ {
7018
+ "name": "key",
7019
+ "type": {
7020
+ "text": "string"
7021
+ }
7022
+ },
7023
+ {
7024
+ "name": "at",
7025
+ "type": {
7026
+ "text": "number"
7027
+ }
7028
+ },
7029
+ {
7030
+ "name": "count",
7031
+ "type": {
7032
+ "text": "number"
7033
+ }
7034
+ },
7035
+ {
7036
+ "name": "page",
7037
+ "type": {
7038
+ "text": "number"
7039
+ }
7040
+ }
7041
+ ],
7042
+ "description": "Which item a key moves to from `at`, in a set of `count` items, or\n`undefined` for a key it does not take or where there is no item that way:\n\n- Down and Up move to the next or the previous item in the same column.\n- `next` and `previous` move to the column beside, to its item that sits\n beside this one the most, or the nearest above where none does. Which\n of Left and Right is `next` is the caller's, for a right-to-left page.\n- Page Down and Page Up move down or up the same column by `page` pixels,\n to the item there, or the last item that way."
7043
+ },
7044
+ {
7045
+ "kind": "method",
7046
+ "name": "size",
7047
+ "privacy": "private",
7048
+ "return": {
7049
+ "type": {
7050
+ "text": "number"
7051
+ }
7052
+ },
7053
+ "parameters": [
7054
+ {
7055
+ "name": "index",
7056
+ "type": {
7057
+ "text": "number"
7058
+ }
7059
+ }
7060
+ ],
7061
+ "description": "How much room item `index` takes down its column, with the gap below it."
7062
+ }
7063
+ ],
7064
+ "helix": {}
7065
+ }
7066
+ ],
7067
+ "exports": [
7068
+ {
7069
+ "kind": "js",
7070
+ "name": "Waterfall",
7071
+ "declaration": {
7072
+ "name": "Waterfall",
7073
+ "module": "src/controllers/virtualize-controller/waterfall.ts"
7074
+ }
7075
+ }
7076
+ ]
7077
+ },
6704
7078
  {
6705
7079
  "kind": "javascript-module",
6706
7080
  "path": "src/events/action.ts",
@@ -13785,6 +14159,14 @@
13785
14159
  "package": "#audit-children"
13786
14160
  }
13787
14161
  },
14162
+ {
14163
+ "kind": "js",
14164
+ "name": "auditEntry",
14165
+ "declaration": {
14166
+ "name": "auditEntry",
14167
+ "package": "#audit-children"
14168
+ }
14169
+ },
13788
14170
  {
13789
14171
  "kind": "js",
13790
14172
  "name": "auditName",
@@ -14521,6 +14903,14 @@
14521
14903
  "module": "./controllers/virtualize-controller/virtualize-controller.js"
14522
14904
  }
14523
14905
  },
14906
+ {
14907
+ "kind": "js",
14908
+ "name": "Waterfall",
14909
+ "declaration": {
14910
+ "name": "Waterfall",
14911
+ "module": "./controllers/virtualize-controller/waterfall.js"
14912
+ }
14913
+ },
14524
14914
  {
14525
14915
  "kind": "js",
14526
14916
  "name": "eventsForTag",
@@ -25571,6 +25961,14 @@
25571
25961
  "type": {
25572
25962
  "text": "typeof audit"
25573
25963
  }
25964
+ },
25965
+ {
25966
+ "kind": "function",
25967
+ "name": "auditEntry",
25968
+ "description": "The missing-entry warning in a production build: nothing. See `auditEntry`.",
25969
+ "type": {
25970
+ "text": "typeof entry"
25971
+ }
25574
25972
  }
25575
25973
  ],
25576
25974
  "exports": [
@@ -25581,6 +25979,14 @@
25581
25979
  "name": "auditChildren",
25582
25980
  "module": "src/utils/audit-children/audit-children-off.ts"
25583
25981
  }
25982
+ },
25983
+ {
25984
+ "kind": "js",
25985
+ "name": "auditEntry",
25986
+ "declaration": {
25987
+ "name": "auditEntry",
25988
+ "module": "src/utils/audit-children/audit-children-off.ts"
25989
+ }
25584
25990
  }
25585
25991
  ]
25586
25992
  },
@@ -25627,6 +26033,48 @@
25627
26033
  }
25628
26034
  ],
25629
26035
  "description": "Reports a child from another family that a container holds and nobody\nregistered, naming the entry to import.\n\n**A container does not register another family's children.** An option\nbelongs to the Option family, and a page writing one inside a Select imports\n`@fusion.dev/helix/option` itself, as it imports the Select. Left out, the\noption is an unknown element: the list reads no choices and nothing throws.\nThis says so, once for each container tag and child tag on the page, with\nthe line to add.\n\nAsked once the page has loaded and painted, so an entry a later module\nimports is not taken for a missing one.\n\n**In a development build only**, as `missing-name` is: every container would\npay for a sentence meant for the person writing the page. Core imports it as\n`#audit-children`, which `package.json` resolves here under the\n`development` condition and to `audit-children-off.ts`, which does nothing,\neverywhere else.\n\n`part` is the child's tag after the tier, such as `option`, and `entry` its\nfamily's entry, such as `option`; the host's `tier` gives the rest of both.\n`consequence` says what the missing child costs, after \"so\". `drawn` is for\nchildren the host draws from data, such as the options of Listbox's `items`:\nthe page wrote none, so an undefined tag is reported without one in the\nhost's light DOM."
26036
+ },
26037
+ {
26038
+ "kind": "function",
26039
+ "name": "auditEntry",
26040
+ "return": {
26041
+ "type": {
26042
+ "text": "void"
26043
+ }
26044
+ },
26045
+ "parameters": [
26046
+ {
26047
+ "name": "host",
26048
+ "type": {
26049
+ "text": "HTMLElement"
26050
+ }
26051
+ },
26052
+ {
26053
+ "name": "attribute",
26054
+ "type": {
26055
+ "text": "string"
26056
+ }
26057
+ },
26058
+ {
26059
+ "name": "entry",
26060
+ "type": {
26061
+ "text": "string"
26062
+ }
26063
+ },
26064
+ {
26065
+ "name": "consequence",
26066
+ "type": {
26067
+ "text": "string"
26068
+ }
26069
+ },
26070
+ {
26071
+ "name": "missing",
26072
+ "type": {
26073
+ "text": "() => boolean"
26074
+ }
26075
+ }
26076
+ ],
26077
+ "description": "Reports a property set on a host whose behavior is in an entry of its own\nthat nobody imported, naming the entry.\n\n**The second shape of the same mistake.** Menu's filter is\n`menu/filter`, so a menu that never filters carries none of its code, and a\npage that sets `filterable` and imports only the menu gets a menu with no\nfield and nothing thrown. This says so, once for each host tag and entry on\nthe page, with the line to add.\n\n`attribute` is the one the page set, `entry` the entry after the tier's\npackage, such as `menu/filter`, and `consequence` what the missing entry\ncosts, after \"so\". `missing` is asked once the page has loaded and painted,\nso an entry a later module imports is not taken for a missing one."
25630
26078
  }
25631
26079
  ],
25632
26080
  "exports": [
@@ -25637,6 +26085,14 @@
25637
26085
  "name": "auditChildren",
25638
26086
  "module": "src/utils/audit-children/audit-children.ts"
25639
26087
  }
26088
+ },
26089
+ {
26090
+ "kind": "js",
26091
+ "name": "auditEntry",
26092
+ "declaration": {
26093
+ "name": "auditEntry",
26094
+ "module": "src/utils/audit-children/audit-children.ts"
26095
+ }
25640
26096
  }
25641
26097
  ]
25642
26098
  },
@@ -35040,7 +35496,7 @@
35040
35496
  "type": {
35041
35497
  "text": "string"
35042
35498
  },
35043
- "default": "'0.182.2'",
35499
+ "default": "'0.184.0'",
35044
35500
  "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."
35045
35501
  }
35046
35502
  ],
@@ -35676,6 +36132,6 @@
35676
36132
  ],
35677
36133
  "helix": {
35678
36134
  "package": "@fusion.dev/helix-core",
35679
- "version": "0.182.2"
36135
+ "version": "0.184.0"
35680
36136
  }
35681
36137
  }
@@ -29,6 +29,8 @@ export declare class Heights {
29
29
  private dirty;
30
30
  /** The estimate the sums were built against, so a change to it rebuilds. */
31
31
  private built;
32
+ /** Counts each change to a height, for a placement built from them. */
33
+ private changes;
32
34
  constructor(estimate: () => number);
33
35
  /** How tall one item is: what was measured, or what is assumed. */
34
36
  at(index: number): number;
@@ -42,6 +44,11 @@ export declare class Heights {
42
44
  record(index: number, size: number): boolean;
43
45
  /** The room items `[0, index)` take. */
44
46
  before(index: number, count: number): number;
47
+ /**
48
+ * Says which heights these are: a placement built from a stamp it was
49
+ * given again holds, and one built from another stamp is out of date.
50
+ */
51
+ stamp(): string;
45
52
  /** The room the whole set takes. */
46
53
  total(count: number): number;
47
54
  /**
@@ -4,6 +4,7 @@ export class Heights {
4
4
  this.sums = [0];
5
5
  this.dirty = 0;
6
6
  this.built = 0;
7
+ this.changes = 0;
7
8
  this.estimate = estimate;
8
9
  }
9
10
  at(index) {
@@ -23,12 +24,16 @@ export class Heights {
23
24
  return false;
24
25
  }
25
26
  this.dirty = Math.min(this.dirty, index);
27
+ this.changes += 1;
26
28
  return true;
27
29
  }
28
30
  before(index, count) {
29
31
  this.build(count);
30
32
  return this.sums[Math.max(0, Math.min(index, count))] ?? 0;
31
33
  }
34
+ stamp() {
35
+ return `${this.changes}:${this.estimate()}`;
36
+ }
32
37
  total(count) {
33
38
  return this.before(count, count);
34
39
  }
@@ -63,11 +68,13 @@ export class Heights {
63
68
  }
64
69
  }
65
70
  this.dirty = 0;
71
+ this.changes += 1;
66
72
  }
67
73
  forget() {
68
74
  this.seen.clear();
69
75
  this.dirty = 0;
70
76
  this.sums = [0];
77
+ this.changes += 1;
71
78
  }
72
79
  build(count) {
73
80
  const size = Math.max(1, this.estimate());
@@ -1,4 +1,5 @@
1
1
  import type { ReactiveController, ReactiveControllerHost } from 'lit';
2
+ import type { Waterfall } from './waterfall.js';
2
3
  /** What the controller needs of a set and the box it is shown in. */
3
4
  export interface VirtualizeOptions {
4
5
  /** The scrolling box, or null before it renders. */
@@ -75,6 +76,16 @@ export interface VirtualizeOptions {
75
76
  * scroll offset during a fling.
76
77
  */
77
78
  anchor?: () => boolean;
79
+ /**
80
+ * Lays the items out down several columns, each item under the column that
81
+ * ends highest, as a wall of photos is: a `Waterfall`, whose `lanes` says how
82
+ * many columns. The window is then every item that reaches into the box, in
83
+ * the order of the set, and a host draws each item at its column,
84
+ * `waterfall.lane(index)`, and at `offsetOf(index)` down the set, which
85
+ * `offsetOf(count)` is the length of. Items are placed by measurement, so a
86
+ * host supplies `rows`. None by default.
87
+ */
88
+ waterfall?: () => Waterfall | undefined;
78
89
  }
79
90
  /** A row that has been drawn, and which item of the set it drew. */
80
91
  export interface Rendered {
@@ -320,6 +331,19 @@ export declare class VirtualizeController implements ReactiveController {
320
331
  private sampleRows;
321
332
  /** Whether this pass places by measurement: offered, and wanted or needed. */
322
333
  private measuring;
334
+ /** The waterfall the host lays its items out in, placed by the heights known now. */
335
+ private laned;
336
+ /**
337
+ * Where item `index` starts down the set, before any drift: after the items
338
+ * before it, or in its column of a waterfall, where the starts never go back
339
+ * up either.
340
+ */
341
+ private startOf;
342
+ /**
343
+ * The item at `offset`: the last to start at or before it, or in a
344
+ * waterfall the first to reach past it.
345
+ */
346
+ private indexAt;
323
347
  /**
324
348
  * The measured version, which asks where each item starts rather than dividing.
325
349
  *
@@ -159,14 +159,13 @@ export class VirtualizeController {
159
159
  return;
160
160
  }
161
161
  const overscan = this.options.overscan ?? 4;
162
- let next = this.measuring()
162
+ const choose = () => this.measuring()
163
163
  ? this.placed(box, total, overscan)
164
164
  : this.uniform(box, total, size, overscan);
165
+ let next = choose();
165
166
  if (this.drift !== 0 && (next.before < 0 || next.first === 0)) {
166
167
  this.square();
167
- next = this.measuring()
168
- ? this.placed(box, total, overscan)
169
- : this.uniform(box, total, size, overscan);
168
+ next = choose();
170
169
  }
171
170
  if (this.last?.count !== total || this.last.estimate !== size) {
172
171
  this.last = { count: total, estimate: size };
@@ -235,22 +234,40 @@ export class VirtualizeController {
235
234
  }
236
235
  }
237
236
  measuring() {
238
- return this.options.rows !== undefined && (this.uneven || (this.options.measured?.() ?? true));
237
+ return (this.options.rows !== undefined &&
238
+ (this.uneven ||
239
+ this.options.waterfall?.() !== undefined ||
240
+ (this.options.measured?.() ?? true)));
241
+ }
242
+ laned(total) {
243
+ const lanes = this.options.rows ? this.options.waterfall?.() : undefined;
244
+ lanes?.place(total, (index) => this.heights.at(index), this.heights.stamp());
245
+ return lanes;
246
+ }
247
+ startOf(index, total) {
248
+ return this.laned(total)?.start(index) ?? this.heights.before(index, total);
249
+ }
250
+ indexAt(offset, total) {
251
+ const lanes = this.laned(total);
252
+ return lanes
253
+ ? Math.min(lanes.from(offset), Math.max(0, total - 1))
254
+ : this.heights.indexAt(offset, total);
239
255
  }
240
256
  placed(box, total, overscan) {
241
257
  const top = this.offset(box);
242
258
  const lead = this.lead(box);
243
259
  const from = this.heading < 0 ? top - lead : top;
244
- const first = Math.max(0, this.heights.indexAt(from, total) - overscan);
260
+ const more = overscan * (this.laned(total)?.lanes ?? 1);
261
+ const first = Math.max(0, this.indexAt(from, total) - more);
245
262
  const bottom = top + this.span(box) + (this.heading > 0 ? lead : 0);
246
- let last = this.heights.indexAt(top, total);
247
- while (last < total && this.heights.before(last, total) < bottom) {
263
+ let last = this.indexAt(top, total);
264
+ while (last < total && this.startOf(last, total) < bottom) {
248
265
  last += 1;
249
266
  }
250
- last = Math.min(total, last + overscan);
267
+ last = Math.min(total, last + more);
251
268
  return {
252
- after: Math.max(0, this.heights.total(total) - this.heights.before(last, total)),
253
- before: this.heights.before(first, total) + this.drift,
269
+ after: Math.max(0, this.startOf(total, total) - this.startOf(last, total)),
270
+ before: this.startOf(first, total) + this.drift,
254
271
  first,
255
272
  last,
256
273
  };
@@ -268,20 +285,20 @@ export class VirtualizeController {
268
285
  const first = Math.min(Math.floor(top / size), total);
269
286
  return { first, last: Math.max(first, Math.min(total, Math.ceil(bottom / size))) };
270
287
  }
271
- const first = this.heights.indexAt(top, total);
288
+ const first = this.indexAt(top, total);
272
289
  let last = first;
273
- while (last < total && this.heights.before(last, total) < bottom) {
290
+ while (last < total && this.startOf(last, total) < bottom) {
274
291
  last += 1;
275
292
  }
276
293
  return { first, last };
277
294
  }
278
295
  remaining() {
279
296
  const box = this.options.container();
280
- const total = Math.max(0, this.options.count());
281
297
  if (!box) {
282
298
  return 0;
283
299
  }
284
- const length = this.measuring() ? this.heights.total(total) : total * this.line();
300
+ const total = Math.max(0, this.options.count());
301
+ const length = this.measuring() ? this.startOf(total, total) : total * this.line();
285
302
  return Math.max(0, length - Math.max(0, this.offset(box)) - this.span(box));
286
303
  }
287
304
  endInSight(length = Math.max(0, this.options.count())) {
@@ -332,8 +349,8 @@ export class VirtualizeController {
332
349
  const box = this.options.container();
333
350
  const from = box ? this.noteClamp(box) : this.lastTop;
334
351
  const top = box ? this.offset(box) : 0;
335
- const anchor = this.target ?? this.heights.indexAt(top, total);
336
- const was = this.heights.before(anchor, total);
352
+ const anchor = this.target ?? this.indexAt(top, total);
353
+ const was = this.startOf(anchor, total);
337
354
  let news = false;
338
355
  for (const { element, index } of rows) {
339
356
  if (this.heights.record(index, this.length(element) + this.gap())) {
@@ -344,7 +361,7 @@ export class VirtualizeController {
344
361
  return;
345
362
  }
346
363
  this.settling = 0;
347
- const moved = this.heights.before(anchor, total) - was;
364
+ const moved = this.startOf(anchor, total) - was;
348
365
  if (box && top > 0 && moved !== 0 && (this.options.anchor?.() ?? true)) {
349
366
  if (this.scrolling()) {
350
367
  this.drift -= moved;
@@ -353,6 +370,9 @@ export class VirtualizeController {
353
370
  this.scrollBox(box, from + moved);
354
371
  }
355
372
  }
373
+ if (this.laned(total)) {
374
+ this.host.requestUpdate();
375
+ }
356
376
  this.measure();
357
377
  }
358
378
  shift(by) {
@@ -361,7 +381,7 @@ export class VirtualizeController {
361
381
  }
362
382
  const count = Math.max(0, this.options.count());
363
383
  this.heights.shift(by);
364
- const room = this.heights.before(by, count);
384
+ const room = this.startOf(by, count);
365
385
  this.window = {
366
386
  ...this.window,
367
387
  before: this.window.before + room,
@@ -416,7 +436,7 @@ export class VirtualizeController {
416
436
  offsetOf(index) {
417
437
  const total = Math.max(0, this.options.count());
418
438
  return this.measuring()
419
- ? this.heights.before(index, total) + this.drift
439
+ ? this.startOf(index, total) + this.drift
420
440
  : Math.max(0, Math.min(index, total)) * this.line();
421
441
  }
422
442
  forget() {
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Where each item of a waterfall stands: a set laid out down several columns
3
+ * of one width, each item under the shortest column so far, as a masonry wall
4
+ * of photos is.
5
+ *
6
+ * **Its own module, which a host hands to `VirtualizeController`**, so the
7
+ * hosts that window a single column carry none of it. The controller asks it
8
+ * which items reach the box and where each one starts, and the host draws each
9
+ * item at its column and its start.
10
+ *
11
+ * **Placed in the order of the set, each item under the column that ends
12
+ * highest**, the leftmost of a tie, as React Aria's `WaterfallLayout` and
13
+ * TanStack Virtual's lanes place them. So an item's start is never above the
14
+ * start of the item before it, and the first item to reach any offset is found
15
+ * by a binary search, as one column finds it.
16
+ *
17
+ * Sizes are the controller's: what was measured, or the estimate, each with
18
+ * the gap below it.
19
+ */
20
+ export declare class Waterfall {
21
+ /** How many columns the items are laid out in, at least one. */
22
+ lanes: number;
23
+ /** Where each item starts, in pixels from the top of the set. */
24
+ private starts;
25
+ /** The column each item stands in. */
26
+ private columns;
27
+ /** How much room each item takes, with the gap below it. */
28
+ private sizes;
29
+ /** How far down the set the items `[0, i]` reach, the furthest of their ends. */
30
+ private reach;
31
+ /** How long the set is: where its longest column ends. */
32
+ private extent;
33
+ /** What the placement was built from, so it is built again only when that changes. */
34
+ private built;
35
+ /**
36
+ * Places `count` items whose sizes `size` gives, unless `stamp` says the
37
+ * sizes, the count and the columns are the ones already placed.
38
+ */
39
+ place(count: number, size: (index: number) => number, stamp: string): void;
40
+ /** Where item `index` starts, and where the set ends for an index past it. */
41
+ start(index: number): number;
42
+ /** The column item `index` stands in. */
43
+ lane(index: number): number;
44
+ /** How long the set is, where its longest column ends. */
45
+ length(): number;
46
+ /** The first item that reaches past `offset`, or the count where none does. */
47
+ from(offset: number): number;
48
+ /** The first item that starts at or past `offset`, so every item before it starts above. */
49
+ to(offset: number): number;
50
+ /** The first index whose number in `sorted` is past `offset`. */
51
+ private search;
52
+ /**
53
+ * Which item a key moves to from `at`, in a set of `count` items, or
54
+ * `undefined` for a key it does not take or where there is no item that way:
55
+ *
56
+ * - Down and Up move to the next or the previous item in the same column.
57
+ * - `next` and `previous` move to the column beside, to its item that sits
58
+ * beside this one the most, or the nearest above where none does. Which
59
+ * of Left and Right is `next` is the caller's, for a right-to-left page.
60
+ * - Page Down and Page Up move down or up the same column by `page` pixels,
61
+ * to the item there, or the last item that way.
62
+ */
63
+ step(key: string, at: number, count: number, page: number): number | undefined;
64
+ /** How much room item `index` takes down its column, with the gap below it. */
65
+ private size;
66
+ }
@@ -0,0 +1,137 @@
1
+ export class Waterfall {
2
+ constructor() {
3
+ this.lanes = 1;
4
+ this.starts = [];
5
+ this.columns = [];
6
+ this.sizes = [];
7
+ this.reach = [];
8
+ this.extent = 0;
9
+ this.built = '';
10
+ }
11
+ place(count, size, stamp) {
12
+ const lanes = Math.max(1, Math.trunc(this.lanes) || 1);
13
+ const key = `${count}:${lanes}:${stamp}`;
14
+ if (key === this.built) {
15
+ return;
16
+ }
17
+ this.built = key;
18
+ const ends = new Array(lanes).fill(0);
19
+ this.starts = new Array(count);
20
+ this.columns = new Array(count);
21
+ this.sizes = new Array(count);
22
+ this.reach = new Array(count);
23
+ let furthest = 0;
24
+ for (let index = 0; index < count; index++) {
25
+ let lane = 0;
26
+ for (let other = 1; other < lanes; other++) {
27
+ if ((ends[other] ?? 0) < (ends[lane] ?? 0)) {
28
+ lane = other;
29
+ }
30
+ }
31
+ const start = ends[lane] ?? 0;
32
+ const room = size(index);
33
+ const end = start + room;
34
+ ends[lane] = end;
35
+ furthest = Math.max(furthest, end);
36
+ this.starts[index] = start;
37
+ this.columns[index] = lane;
38
+ this.sizes[index] = room;
39
+ this.reach[index] = furthest;
40
+ }
41
+ this.extent = furthest;
42
+ }
43
+ start(index) {
44
+ return this.starts[index] ?? this.extent;
45
+ }
46
+ lane(index) {
47
+ return this.columns[index] ?? 0;
48
+ }
49
+ length() {
50
+ return this.extent;
51
+ }
52
+ from(offset) {
53
+ return this.search(this.reach, offset);
54
+ }
55
+ to(offset) {
56
+ return this.search(this.starts, offset - 1e-6);
57
+ }
58
+ search(sorted, offset) {
59
+ let low = 0;
60
+ let high = sorted.length;
61
+ while (low < high) {
62
+ const mid = (low + high) >> 1;
63
+ if ((sorted[mid] ?? 0) <= offset) {
64
+ low = mid + 1;
65
+ }
66
+ else {
67
+ high = mid;
68
+ }
69
+ }
70
+ return low;
71
+ }
72
+ step(key, at, count, page) {
73
+ const lane = this.lane(at);
74
+ const along = (way, from = at) => {
75
+ for (let other = from + way; other >= 0 && other < count; other += way) {
76
+ if (this.lane(other) === lane) {
77
+ return other;
78
+ }
79
+ }
80
+ return undefined;
81
+ };
82
+ if (key === 'ArrowDown' || key === 'ArrowUp') {
83
+ return along(key === 'ArrowDown' ? 1 : -1);
84
+ }
85
+ if (key === 'PageDown' || key === 'PageUp') {
86
+ const way = key === 'PageDown' ? 1 : -1;
87
+ const goal = this.start(at) + way * Math.max(1, page);
88
+ let found = at;
89
+ for (let next = along(way); next !== undefined; next = along(way, next)) {
90
+ found = next;
91
+ if (way > 0 ? this.start(next) + this.size(next) > goal : this.start(next) <= goal) {
92
+ break;
93
+ }
94
+ }
95
+ return found === at ? undefined : found;
96
+ }
97
+ if (key !== 'next' && key !== 'previous') {
98
+ return undefined;
99
+ }
100
+ const beside = lane + (key === 'next' ? 1 : -1);
101
+ if (beside < 0 || beside >= Math.max(1, Math.trunc(this.lanes) || 1)) {
102
+ return undefined;
103
+ }
104
+ const top = this.start(at);
105
+ const bottom = top + this.size(at);
106
+ let best;
107
+ let most = 0;
108
+ let above;
109
+ for (let other = Math.max(0, this.from(top) - this.lanes); other < count; other++) {
110
+ const start = this.start(other);
111
+ if (start >= bottom) {
112
+ break;
113
+ }
114
+ if (this.lane(other) !== beside) {
115
+ continue;
116
+ }
117
+ const overlap = Math.min(bottom, start + this.size(other)) - Math.max(top, start);
118
+ if (overlap > most) {
119
+ best = other;
120
+ most = overlap;
121
+ }
122
+ above = other;
123
+ }
124
+ if (best !== undefined || above !== undefined) {
125
+ return best ?? above;
126
+ }
127
+ for (let other = at - 1; other >= 0; other--) {
128
+ if (this.lane(other) === beside) {
129
+ return other;
130
+ }
131
+ }
132
+ return undefined;
133
+ }
134
+ size(index) {
135
+ return this.sizes[index] ?? 0;
136
+ }
137
+ }
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { auditChildren } from '@fusion.dev/helix-core/audits/children';
1
+ export { auditChildren, auditEntry } from '@fusion.dev/helix-core/audits/children';
2
2
  export { auditName } from '@fusion.dev/helix-core/audits/name';
3
3
  export { auditNesting } from '@fusion.dev/helix-core/audits/nesting';
4
4
  export { auditServed } from '@fusion.dev/helix-core/audits/served';
@@ -36,6 +36,7 @@ export { SwipeController, type SwipeOptions, } from './controllers/swipe-control
36
36
  export { type TokenColor, TokenColorController, } from './controllers/token-color-controller/token-color-controller.js';
37
37
  export { UpgradeController, type UpgradeOptions, } from './controllers/upgrade-controller/upgrade-controller.js';
38
38
  export { type Rendered, VirtualizeController, type VirtualizeOptions, type Window as VirtualizeWindow, } from './controllers/virtualize-controller/virtualize-controller.js';
39
+ export { Waterfall } from './controllers/virtualize-controller/waterfall.js';
39
40
  export { eventsForTag, registerElementEvents } from './events/element-events.js';
40
41
  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';
41
42
  export { HelixElement, whenHydrated } from './helix-element/helix-element.js';
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { auditChildren } from '@fusion.dev/helix-core/audits/children';
1
+ export { auditChildren, auditEntry } from '@fusion.dev/helix-core/audits/children';
2
2
  export { auditName } from '@fusion.dev/helix-core/audits/name';
3
3
  export { auditNesting } from '@fusion.dev/helix-core/audits/nesting';
4
4
  export { auditServed } from '@fusion.dev/helix-core/audits/served';
@@ -36,6 +36,7 @@ export { SwipeController, } from './controllers/swipe-controller/swipe-controlle
36
36
  export { TokenColorController, } from './controllers/token-color-controller/token-color-controller.js';
37
37
  export { UpgradeController, } from './controllers/upgrade-controller/upgrade-controller.js';
38
38
  export { VirtualizeController, } from './controllers/virtualize-controller/virtualize-controller.js';
39
+ export { Waterfall } from './controllers/virtualize-controller/waterfall.js';
39
40
  export { eventsForTag, registerElementEvents } from './events/element-events.js';
40
41
  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';
41
42
  export { HelixElement, whenHydrated } from './helix-element/helix-element.js';
@@ -1,4 +1,4 @@
1
- import type { auditChildren as audit } from './audit-children.js';
1
+ import type { auditChildren as audit, auditEntry as entry } from './audit-children.js';
2
2
  /**
3
3
  * The unregistered-child warning in a production build: nothing.
4
4
  *
@@ -8,3 +8,5 @@ import type { auditChildren as audit } from './audit-children.js';
8
8
  * builds. See `audit-children.ts`.
9
9
  */
10
10
  export declare const auditChildren: typeof audit;
11
+ /** The missing-entry warning in a production build: nothing. See `auditEntry`. */
12
+ export declare const auditEntry: typeof entry;
@@ -1 +1,2 @@
1
1
  export const auditChildren = () => { };
2
+ export const auditEntry = () => { };
@@ -26,3 +26,19 @@
26
26
  * host's light DOM.
27
27
  */
28
28
  export declare const auditChildren: (host: HTMLElement, part: string, entry: string, consequence: string, drawn?: boolean) => void;
29
+ /**
30
+ * Reports a property set on a host whose behavior is in an entry of its own
31
+ * that nobody imported, naming the entry.
32
+ *
33
+ * **The second shape of the same mistake.** Menu's filter is
34
+ * `menu/filter`, so a menu that never filters carries none of its code, and a
35
+ * page that sets `filterable` and imports only the menu gets a menu with no
36
+ * field and nothing thrown. This says so, once for each host tag and entry on
37
+ * the page, with the line to add.
38
+ *
39
+ * `attribute` is the one the page set, `entry` the entry after the tier's
40
+ * package, such as `menu/filter`, and `consequence` what the missing entry
41
+ * costs, after "so". `missing` is asked once the page has loaded and painted,
42
+ * so an entry a later module imports is not taken for a missing one.
43
+ */
44
+ export declare const auditEntry: (host: HTMLElement, attribute: string, entry: string, consequence: string, missing: () => boolean) => void;
@@ -1,5 +1,16 @@
1
1
  import { reportRuntime } from '../report-runtime/report-runtime.js';
2
2
  const reported = new Set();
3
+ const afterPaint = (check) => {
4
+ const frame = () => {
5
+ requestAnimationFrame(check);
6
+ };
7
+ if (document.readyState === 'complete') {
8
+ frame();
9
+ }
10
+ else {
11
+ window.addEventListener('load', frame, { once: true });
12
+ }
13
+ };
3
14
  export const auditChildren = (host, part, entry, consequence, drawn = false) => {
4
15
  if (typeof window === 'undefined') {
5
16
  return;
@@ -25,13 +36,27 @@ export const auditChildren = (host, part, entry, consequence, drawn = false) =>
25
36
  tag: host.localName,
26
37
  });
27
38
  };
28
- const frame = () => {
29
- requestAnimationFrame(check);
30
- };
31
- if (document.readyState === 'complete') {
32
- frame();
39
+ afterPaint(check);
40
+ };
41
+ export const auditEntry = (host, attribute, entry, consequence, missing) => {
42
+ if (typeof window === 'undefined') {
43
+ return;
33
44
  }
34
- else {
35
- window.addEventListener('load', frame, { once: true });
45
+ const key = `${host.localName}>${entry}`;
46
+ if (reported.has(key)) {
47
+ return;
36
48
  }
49
+ afterPaint(() => {
50
+ if (reported.has(key) || !host.isConnected || !missing()) {
51
+ return;
52
+ }
53
+ reported.add(key);
54
+ const tier = host.constructor.tier ?? 'helix-headless-';
55
+ const pkg = tier === 'helix-' ? '@fusion.dev/helix' : '@fusion.dev/helix-headless';
56
+ reportRuntime({
57
+ kind: 'unregistered-part',
58
+ message: `<${host.localName}> has \`${attribute}\` and its entry is not imported, so ${consequence}. Import '${pkg}/${entry}'.`,
59
+ tag: host.localName,
60
+ });
61
+ });
37
62
  };
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.182.2";
9
+ export declare const VERSION = "0.184.0";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = '0.182.2';
1
+ export const VERSION = '0.184.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.182.2",
4
+ "version": "0.184.0",
5
5
  "license": "MIT",
6
6
  "author": "Ross Sheppard <ross.sheppard@fusion.dev>",
7
7
  "repository": {
@@ -74,12 +74,12 @@
74
74
  "bugs": "https://github.com/project-fusion/fusion/issues",
75
75
  "dependencies": {
76
76
  "@floating-ui/dom": "^1.8.0",
77
- "@fusion.dev/helix-icons": "0.182.2",
77
+ "@fusion.dev/helix-icons": "0.184.0",
78
78
  "tslib": "^2.8.1"
79
79
  },
80
80
  "devDependencies": {
81
- "@fusion.dev/helix-cem": "0.182.2",
82
- "@fusion.dev/helix-testing": "0.182.2",
81
+ "@fusion.dev/helix-cem": "0.184.0",
82
+ "@fusion.dev/helix-testing": "0.184.0",
83
83
  "@lit-labs/ssr": "^4.1.0",
84
84
  "lit": "^3.2.1"
85
85
  },