ember-primitives 0.57.0 → 0.58.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.
@@ -0,0 +1,91 @@
1
+ import Component from "@glimmer/component";
2
+ import type Owner from "@ember/owner";
3
+ export interface Signature<T = unknown> {
4
+ Args: {
5
+ /**
6
+ * The collection of items to render.
7
+ *
8
+ * Replacing the array (new identity) restarts rendering from the
9
+ * first batch.
10
+ *
11
+ * ```gjs
12
+ * import { IncrementalEach } from 'ember-primitives';
13
+ *
14
+ * <template>
15
+ * <IncrementalEach @items={{this.rows}} as |row|>
16
+ * <my-row @row={{row}} />
17
+ * </IncrementalEach>
18
+ * </template>
19
+ * ```
20
+ */
21
+ items: readonly T[];
22
+ /**
23
+ * How many items to add per idle callback.
24
+ *
25
+ * Larger batches add more items per chunk; smaller batches yield to
26
+ * the browser more often.
27
+ *
28
+ * Default: 50. Must be positive; `0` or less asserts in development.
29
+ *
30
+ * ```gjs
31
+ * import { IncrementalEach } from 'ember-primitives';
32
+ *
33
+ * <template>
34
+ * <IncrementalEach @items={{this.rows}} @batchSize={{100}} as |row|>
35
+ * <my-row @row={{row}} />
36
+ * </IncrementalEach>
37
+ * </template>
38
+ * ```
39
+ */
40
+ batchSize?: number;
41
+ };
42
+ Blocks: {
43
+ /**
44
+ * Yielded for each rendered item, with the index in the original
45
+ * `@items` array.
46
+ *
47
+ * ```gjs
48
+ * import { IncrementalEach } from 'ember-primitives';
49
+ *
50
+ * <template>
51
+ * <IncrementalEach @items={{this.rows}} as |row index|>
52
+ * {{index}}: {{row.label}}
53
+ * </IncrementalEach>
54
+ * </template>
55
+ * ```
56
+ */
57
+ default: [item: T, index: number];
58
+ };
59
+ }
60
+ /**
61
+ * A drop-in replacement for `{{#each}}` that renders a large collection a
62
+ * batch at a time during the browser's idle periods, instead of all at once.
63
+ *
64
+ * Every item ends up in the DOM, so browser find (Ctrl+F / Cmd+F), anchor
65
+ * links, screen readers, print, and SEO all work against the full list.
66
+ * Yielding the main thread between batches keeps the page responsive while
67
+ * the rest of the list is filling in.
68
+ *
69
+ * Intended for non-scrollable containers, or anywhere a virtual/windowed
70
+ * list does not apply (variable item heights, lists that grow the page,
71
+ * surfaces that need every row indexable).
72
+ *
73
+ * @example
74
+ * ```gjs
75
+ * import { IncrementalEach } from 'ember-primitives';
76
+ *
77
+ * <template>
78
+ * <ul>
79
+ * <IncrementalEach @items={{this.rows}} @batchSize={{100}} as |row index|>
80
+ * <li>{{index}}: {{row.label}}</li>
81
+ * </IncrementalEach>
82
+ * </ul>
83
+ * </template>
84
+ * ```
85
+ */
86
+ export declare class IncrementalEach<T = unknown> extends Component<Signature<T>> {
87
+ #private;
88
+ constructor(owner: Owner, args: Signature<T>["Args"]);
89
+ get visible(): readonly T[];
90
+ }
91
+ //# sourceMappingURL=incremental-each.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"incremental-each.d.ts","sourceRoot":"","sources":["../../src/components/incremental-each.gts"],"names":[],"mappings":"AA6MA,OAAO,SAAS,MAAM,oBAAoB,CAAC;AAO3C,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAMtC,MAAM,WAAW,SAAS,CAAC,CAAC,GAAG,OAAO;IACpC,IAAI,EAAE;QACJ;;;;;;;;;;;;;;;WAeG;QACH,KAAK,EAAE,SAAS,CAAC,EAAE,CAAC;QAEpB;;;;;;;;;;;;;;;;;WAiBG;QACH,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,MAAM,EAAE;QACN;;;;;;;;;;;;;WAaG;QACH,OAAO,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;KACnC,CAAC;CACH;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,qBAAa,eAAe,CAAC,CAAC,GAAG,OAAO,CAAE,SAAQ,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;;gBAe3D,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IA0BpD,IAAI,OAAO,IAAI,SAAS,CAAC,EAAE,CAc1B;CAyDF"}
@@ -1,4 +1,4 @@
1
- import type { Signature as FloatingUiComponentSignature } from "../floating-ui/component.ts";
1
+ import type { Signature as FloatingUiComponentSignature } from '../floating-ui/component';
2
2
  import type { Signature as HookSignature } from "../floating-ui/modifier.ts";
3
3
  import type { TOC } from "@ember/component/template-only";
4
4
  import type { ModifierLike, WithBoundArgs } from "@glint/template";
@@ -1 +1 @@
1
- {"version":3,"file":"popover.d.ts","sourceRoot":"","sources":["../../src/components/popover.gts"],"names":[],"mappings":"AAyQA,OAAO,KAAK,EAAE,SAAS,IAAI,4BAA4B,EAAE,MAAM,6BAA6B,CAAC;AAC7F,OAAO,KAAK,EAAE,SAAS,IAAI,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC7E,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,gCAAgC,CAAC;AAE1D,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEnE,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE;QACJ;;;;WAIG;QACH,WAAW,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,aAAa,CAAC,CAAC;QAC5D;;;;WAIG;QACH,UAAU,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC;QAC1D;;;;WAIG;QACH,aAAa,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,eAAe,CAAC,CAAC;QAChE;;;;;;;;;;;;WAYG;QACH,SAAS,CAAC,EAAE,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,EAAE,GAAG,QAAQ,GAAG,MAAM,EAAE,CAAC;QAC9E;;;;WAIG;QACH,YAAY,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,cAAc,CAAC,CAAC;QAC9D;;;;;;WAMG;QACH,QAAQ,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC,CAAC;KACvD,CAAC;IACF,MAAM,EAAE;QACN,OAAO,EAAE;YACP;gBACE,SAAS,EAAE,4BAA4B,CAAC,QAAQ,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;gBAChE,YAAY,EAAE,4BAA4B,CAAC,QAAQ,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC;gBACnF,OAAO,EAAE,aAAa,CAAC,OAAO,OAAO,EAAE,UAAU,CAAC,CAAC;gBACnD,IAAI,EAAE,4BAA4B,CAAC,QAAQ,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;gBACnE,KAAK,EAAE,YAAY,CAAC;oBAAE,OAAO,EAAE,WAAW,CAAA;iBAAE,CAAC,CAAC;aAC/C;SACF,CAAC;KACH,CAAC;CACH;AAoCD;;;;;GAKG;AACH,QAAA,MAAM,OAAO,EAAE,GAAG,CAAC;IACjB,OAAO,EAAE,cAAc,CAAC;IACxB,IAAI,EAAE;QACJ,QAAQ,EAAE,YAAY,CAAC;YAAE,OAAO,EAAE,WAAW,CAAA;SAAE,CAAC,CAAC;QACjD;;;;;;;;;;;;WAYG;QACH,EAAE,CAAC,EAAE,MAAM,CAAC;KACb,CAAC;IACF,MAAM,EAAE;QAAE,OAAO,EAAE,EAAE,CAAA;KAAE,CAAC;CACzB,CA0BC,CAAC;AAkFH,eAAO,MAAM,OAAO,EAAE,GAAG,CAAC,SAAS,CAmCjC,CAAC;AAEH,eAAe,OAAO,CAAC"}
1
+ {"version":3,"file":"popover.d.ts","sourceRoot":"","sources":["../../src/components/popover.gts"],"names":[],"mappings":"AAyQA,OAAO,KAAK,EAAE,SAAS,IAAI,4BAA4B,EAAE,MAAM,8BAA8B,CAAC;AAC9F,OAAO,KAAK,EAAE,SAAS,IAAI,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC7E,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,gCAAgC,CAAC;AAE1D,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEnE,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE;QACJ;;;;WAIG;QACH,WAAW,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,aAAa,CAAC,CAAC;QAC5D;;;;WAIG;QACH,UAAU,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC;QAC1D;;;;WAIG;QACH,aAAa,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,eAAe,CAAC,CAAC;QAChE;;;;;;;;;;;;WAYG;QACH,SAAS,CAAC,EAAE,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,EAAE,GAAG,QAAQ,GAAG,MAAM,EAAE,CAAC;QAC9E;;;;WAIG;QACH,YAAY,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,cAAc,CAAC,CAAC;QAC9D;;;;;;WAMG;QACH,QAAQ,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC,CAAC;KACvD,CAAC;IACF,MAAM,EAAE;QACN,OAAO,EAAE;YACP;gBACE,SAAS,EAAE,4BAA4B,CAAC,QAAQ,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;gBAChE,YAAY,EAAE,4BAA4B,CAAC,QAAQ,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC;gBACnF,OAAO,EAAE,aAAa,CAAC,OAAO,OAAO,EAAE,UAAU,CAAC,CAAC;gBACnD,IAAI,EAAE,4BAA4B,CAAC,QAAQ,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;gBACnE,KAAK,EAAE,YAAY,CAAC;oBAAE,OAAO,EAAE,WAAW,CAAA;iBAAE,CAAC,CAAC;aAC/C;SACF,CAAC;KACH,CAAC;CACH;AAoCD;;;;;GAKG;AACH,QAAA,MAAM,OAAO,EAAE,GAAG,CAAC;IACjB,OAAO,EAAE,cAAc,CAAC;IACxB,IAAI,EAAE;QACJ,QAAQ,EAAE,YAAY,CAAC;YAAE,OAAO,EAAE,WAAW,CAAA;SAAE,CAAC,CAAC;QACjD;;;;;;;;;;;;WAYG;QACH,EAAE,CAAC,EAAE,MAAM,CAAC;KACb,CAAC;IACF,MAAM,EAAE;QAAE,OAAO,EAAE,EAAE,CAAA;KAAE,CAAC;CACzB,CA0BC,CAAC;AAkFH,eAAO,MAAM,OAAO,EAAE,GAAG,CAAC,SAAS,CAmCjC,CAAC;AAEH,eAAe,OAAO,CAAC"}
@@ -6,6 +6,7 @@ export { Dialog, Dialog as Modal } from './components/dialog';
6
6
  export { Drawer } from './components/drawer';
7
7
  export { ExternalLink } from './components/external-link';
8
8
  export { Form } from './components/form';
9
+ export { IncrementalEach } from './components/incremental-each';
9
10
  export { Key, KeyCombo } from './components/keys';
10
11
  export { StickyFooter } from './components/layout/sticky-footer';
11
12
  export { Link } from './components/link';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AACvD,YAAY,EACV,iCAAiC,EACjC,gCAAgC,EAChC,8BAA8B,EAC9B,iCAAiC,GAClC,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACzD,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,KAAK,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAC9D,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAC7C,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AACrE,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAC7C,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAC7C,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,oCAAoC,CAAC;AACnE,OAAO,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AACnD,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAChE,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAClE,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACpD,cAAc,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AACvD,YAAY,EACV,iCAAiC,EACjC,gCAAgC,EAChC,8BAA8B,EAC9B,iCAAiC,GAClC,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACzD,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,KAAK,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAC9D,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACpE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AACrE,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAC7C,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAC7C,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,oCAAoC,CAAC;AACnE,OAAO,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AACnD,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAChE,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAClE,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACpD,cAAc,cAAc,CAAC"}
@@ -12,7 +12,7 @@ import type { PortalTargets } from './components/portal-targets';
12
12
  import type { Shadowed } from './components/shadowed';
13
13
  import type { Switch } from './components/switch';
14
14
  import type { Toggle } from './components/toggle';
15
- import type { service } from './helpers/service';
15
+ import type { service } from './helpers/service.ts';
16
16
  export default interface Registry {
17
17
  Accordion: typeof Accordion;
18
18
  AccordionItem: typeof AccordionItem;
@@ -1 +1 @@
1
- {"version":3,"file":"template-registry.d.ts","sourceRoot":"","sources":["../src/template-registry.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAMjD,MAAM,CAAC,OAAO,WAAW,QAAQ;IAE/B,SAAS,EAAE,OAAO,SAAS,CAAC;IAC5B,aAAa,EAAE,OAAO,aAAa,CAAC;IACpC,eAAe,EAAE,OAAO,eAAe,CAAC;IACxC,gBAAgB,EAAE,OAAO,gBAAgB,CAAC;IAC1C,gBAAgB,EAAE,OAAO,gBAAgB,CAAC;IAC1C,MAAM,EAAE,OAAO,MAAM,CAAC;IACtB,YAAY,EAAE,OAAO,YAAY,CAAC;IAClC,IAAI,EAAE,OAAO,IAAI,CAAC;IAClB,OAAO,EAAE,OAAO,OAAO,CAAC;IACxB,aAAa,EAAE,OAAO,aAAa,CAAC;IACpC,MAAM,EAAE,OAAO,MAAM,CAAC;IACtB,QAAQ,EAAE,OAAO,QAAQ,CAAC;IAC1B,MAAM,EAAE,OAAO,MAAM,CAAC;IACtB,MAAM,EAAE,OAAO,MAAM,CAAC;IAGtB,OAAO,EAAE,OAAO,OAAO,CAAC;CACzB"}
1
+ {"version":3,"file":"template-registry.d.ts","sourceRoot":"","sources":["../src/template-registry.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAC3E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAC3E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAMpD,MAAM,CAAC,OAAO,WAAW,QAAQ;IAE/B,SAAS,EAAE,OAAO,SAAS,CAAC;IAC5B,aAAa,EAAE,OAAO,aAAa,CAAC;IACpC,eAAe,EAAE,OAAO,eAAe,CAAC;IACxC,gBAAgB,EAAE,OAAO,gBAAgB,CAAC;IAC1C,gBAAgB,EAAE,OAAO,gBAAgB,CAAC;IAC1C,MAAM,EAAE,OAAO,MAAM,CAAC;IACtB,YAAY,EAAE,OAAO,YAAY,CAAC;IAClC,IAAI,EAAE,OAAO,IAAI,CAAC;IAClB,OAAO,EAAE,OAAO,OAAO,CAAC;IACxB,aAAa,EAAE,OAAO,aAAa,CAAC;IACpC,MAAM,EAAE,OAAO,MAAM,CAAC;IACtB,QAAQ,EAAE,OAAO,QAAQ,CAAC;IAC1B,MAAM,EAAE,OAAO,MAAM,CAAC;IACtB,MAAM,EAAE,OAAO,MAAM,CAAC;IAGtB,OAAO,EAAE,OAAO,OAAO,CAAC;CACzB"}
@@ -0,0 +1,120 @@
1
+ import Component from '@glimmer/component';
2
+ import { assert } from '@ember/debug';
3
+ import { registerDestructor, isDestroyed, isDestroying } from '@ember/destroyable';
4
+ import { buildWaiter } from '@ember/test-waiters';
5
+ import { cell } from 'ember-resources';
6
+ import { precompileTemplate } from '@ember/template-compilation';
7
+ import { setComponentTemplate } from '@ember/component';
8
+
9
+ const DEFAULT_BATCH_SIZE = 50;
10
+ const waiter = buildWaiter("ember-primitives:incremental-each");
11
+ /**
12
+ * A drop-in replacement for `{{#each}}` that renders a large collection a
13
+ * batch at a time during the browser's idle periods, instead of all at once.
14
+ *
15
+ * Every item ends up in the DOM, so browser find (Ctrl+F / Cmd+F), anchor
16
+ * links, screen readers, print, and SEO all work against the full list.
17
+ * Yielding the main thread between batches keeps the page responsive while
18
+ * the rest of the list is filling in.
19
+ *
20
+ * Intended for non-scrollable containers, or anywhere a virtual/windowed
21
+ * list does not apply (variable item heights, lists that grow the page,
22
+ * surfaces that need every row indexable).
23
+ *
24
+ * @example
25
+ * ```gjs
26
+ * import { IncrementalEach } from 'ember-primitives';
27
+ *
28
+ * <template>
29
+ * <ul>
30
+ * <IncrementalEach @items={{this.rows}} @batchSize={{100}} as |row index|>
31
+ * <li>{{index}}: {{row.label}}</li>
32
+ * </IncrementalEach>
33
+ * </ul>
34
+ * </template>
35
+ * ```
36
+ */
37
+ class IncrementalEach extends Component {
38
+ // How many items have been committed to the DOM so far. Bumped one
39
+ // batch at a time by the idle callback. Wrapped in a `cell` because
40
+ // `@tracked` doesn't compose with `#`-private fields under this
41
+ // codebase's decorator transform.
42
+ #count = cell(0);
43
+ // Plain field so identity checks don't add a render-time dependency
44
+ // on top of `args.items`.
45
+ #itemsRef = null;
46
+ #idleHandle = null;
47
+ #waiterToken = null;
48
+ constructor(owner, args) {
49
+ super(owner, args);
50
+ registerDestructor(this, () => this.#cancel());
51
+ }
52
+ get #batchSize() {
53
+ const requested = this.args.batchSize ?? DEFAULT_BATCH_SIZE;
54
+ assert(`<IncrementalEach> @batchSize must be a positive number, got ${requested}`, requested > 0);
55
+ return requested;
56
+ }
57
+ // The items that should currently be rendered. `@cached` keeps the
58
+ // returned slice stable across renders that don't change `#count` or
59
+ // `args.items`, so Glimmer's `{{#each}}` doesn't see a fresh array on
60
+ // every render and we only slice once per batch landing. This is
61
+ // also where scheduling is driven from: `@items` identity changes
62
+ // reset `#count` to zero, and missing items queue the next idle
63
+ // callback. Autotrack stays consistent because the only synchronous
64
+ // write here (`#count = 0`) happens before `#count` is read.
65
+ /* eslint-disable ember/no-side-effects */
66
+ get visible() {
67
+ const items = this.args.items ?? [];
68
+ if (items !== this.#itemsRef) {
69
+ this.#itemsRef = items;
70
+ this.#cancel();
71
+ this.#count.current = 0;
72
+ }
73
+ if (this.#count.current < items.length && this.#idleHandle === null) {
74
+ this.#scheduleNextBatch();
75
+ }
76
+ return items.slice(0, this.#count.current);
77
+ }
78
+ /* eslint-enable ember/no-side-effects */
79
+ #scheduleNextBatch() {
80
+ // Defensive: if a batch is already pending, drop it before
81
+ // queueing a new one. The `visible` getter already guards on
82
+ // `#idleHandle === null`, but a future caller might not.
83
+ this.#cancel();
84
+ this.#waiterToken = waiter.beginAsync();
85
+ // The `timeout` cap ensures forward progress even when the host
86
+ // is CPU-bound and the browser never reports a free idle slot.
87
+ // In normal use this is a no-op because real idle time arrives
88
+ // far sooner.
89
+ this.#idleHandle = requestIdleCallback(() => {
90
+ this.#idleHandle = null;
91
+ if (isDestroyed(this) || isDestroying(this)) return;
92
+ const items = this.args.items ?? [];
93
+ this.#count.current = Math.min(this.#count.current + this.#batchSize, items.length);
94
+ this.#endWaiter();
95
+ }, {
96
+ timeout: 100
97
+ });
98
+ }
99
+ #cancel() {
100
+ if (this.#idleHandle !== null) {
101
+ cancelIdleCallback(this.#idleHandle);
102
+ this.#idleHandle = null;
103
+ }
104
+ this.#endWaiter();
105
+ }
106
+ #endWaiter() {
107
+ if (this.#waiterToken !== null) {
108
+ waiter.endAsync(this.#waiterToken);
109
+ this.#waiterToken = null;
110
+ }
111
+ }
112
+ static {
113
+ setComponentTemplate(precompileTemplate("{{#each this.visible as |item index|}}\n {{yield item index}}\n{{/each}}", {
114
+ strictMode: true
115
+ }), this);
116
+ }
117
+ }
118
+
119
+ export { IncrementalEach };
120
+ //# sourceMappingURL=incremental-each.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"incremental-each.js","sources":["../../src/components/incremental-each.gts"],"sourcesContent":["import Component from \"@glimmer/component\";\nimport { assert } from \"@ember/debug\";\nimport { isDestroyed, isDestroying, registerDestructor } from \"@ember/destroyable\";\nimport { buildWaiter } from \"@ember/test-waiters\";\n\nimport { cell } from \"ember-resources\";\n\nimport type Owner from \"@ember/owner\";\n\nconst DEFAULT_BATCH_SIZE = 50;\n\nconst waiter = buildWaiter(\"ember-primitives:incremental-each\");\n\nexport interface Signature<T = unknown> {\n Args: {\n /**\n * The collection of items to render.\n *\n * Replacing the array (new identity) restarts rendering from the\n * first batch.\n *\n * ```gjs\n * import { IncrementalEach } from 'ember-primitives';\n *\n * <template>\n * <IncrementalEach @items={{this.rows}} as |row|>\n * <my-row @row={{row}} />\n * </IncrementalEach>\n * </template>\n * ```\n */\n items: readonly T[];\n\n /**\n * How many items to add per idle callback.\n *\n * Larger batches add more items per chunk; smaller batches yield to\n * the browser more often.\n *\n * Default: 50. Must be positive; `0` or less asserts in development.\n *\n * ```gjs\n * import { IncrementalEach } from 'ember-primitives';\n *\n * <template>\n * <IncrementalEach @items={{this.rows}} @batchSize={{100}} as |row|>\n * <my-row @row={{row}} />\n * </IncrementalEach>\n * </template>\n * ```\n */\n batchSize?: number;\n };\n Blocks: {\n /**\n * Yielded for each rendered item, with the index in the original\n * `@items` array.\n *\n * ```gjs\n * import { IncrementalEach } from 'ember-primitives';\n *\n * <template>\n * <IncrementalEach @items={{this.rows}} as |row index|>\n * {{index}}: {{row.label}}\n * </IncrementalEach>\n * </template>\n * ```\n */\n default: [item: T, index: number];\n };\n}\n\n/**\n * A drop-in replacement for `{{#each}}` that renders a large collection a\n * batch at a time during the browser's idle periods, instead of all at once.\n *\n * Every item ends up in the DOM, so browser find (Ctrl+F / Cmd+F), anchor\n * links, screen readers, print, and SEO all work against the full list.\n * Yielding the main thread between batches keeps the page responsive while\n * the rest of the list is filling in.\n *\n * Intended for non-scrollable containers, or anywhere a virtual/windowed\n * list does not apply (variable item heights, lists that grow the page,\n * surfaces that need every row indexable).\n *\n * @example\n * ```gjs\n * import { IncrementalEach } from 'ember-primitives';\n *\n * <template>\n * <ul>\n * <IncrementalEach @items={{this.rows}} @batchSize={{100}} as |row index|>\n * <li>{{index}}: {{row.label}}</li>\n * </IncrementalEach>\n * </ul>\n * </template>\n * ```\n */\nexport class IncrementalEach<T = unknown> extends Component<Signature<T>> {\n // How many items have been committed to the DOM so far. Bumped one\n // batch at a time by the idle callback. Wrapped in a `cell` because\n // `@tracked` doesn't compose with `#`-private fields under this\n // codebase's decorator transform.\n #count = cell(0);\n\n // Plain field so identity checks don't add a render-time dependency\n // on top of `args.items`.\n #itemsRef: readonly T[] | null = null;\n\n #idleHandle: number | null = null;\n\n #waiterToken: unknown = null;\n\n constructor(owner: Owner, args: Signature<T>[\"Args\"]) {\n super(owner, args);\n\n registerDestructor(this, () => this.#cancel());\n }\n\n get #batchSize(): number {\n const requested = this.args.batchSize ?? DEFAULT_BATCH_SIZE;\n\n assert(\n `<IncrementalEach> @batchSize must be a positive number, got ${requested}`,\n requested > 0,\n );\n\n return requested;\n }\n\n // The items that should currently be rendered. `@cached` keeps the\n // returned slice stable across renders that don't change `#count` or\n // `args.items`, so Glimmer's `{{#each}}` doesn't see a fresh array on\n // every render and we only slice once per batch landing. This is\n // also where scheduling is driven from: `@items` identity changes\n // reset `#count` to zero, and missing items queue the next idle\n // callback. Autotrack stays consistent because the only synchronous\n // write here (`#count = 0`) happens before `#count` is read.\n /* eslint-disable ember/no-side-effects */\n get visible(): readonly T[] {\n const items = this.args.items ?? [];\n\n if (items !== this.#itemsRef) {\n this.#itemsRef = items;\n this.#cancel();\n this.#count.current = 0;\n }\n\n if (this.#count.current < items.length && this.#idleHandle === null) {\n this.#scheduleNextBatch();\n }\n\n return items.slice(0, this.#count.current);\n }\n /* eslint-enable ember/no-side-effects */\n\n #scheduleNextBatch() {\n // Defensive: if a batch is already pending, drop it before\n // queueing a new one. The `visible` getter already guards on\n // `#idleHandle === null`, but a future caller might not.\n this.#cancel();\n\n this.#waiterToken = waiter.beginAsync();\n\n // The `timeout` cap ensures forward progress even when the host\n // is CPU-bound and the browser never reports a free idle slot.\n // In normal use this is a no-op because real idle time arrives\n // far sooner.\n this.#idleHandle = requestIdleCallback(\n () => {\n this.#idleHandle = null;\n\n if (isDestroyed(this) || isDestroying(this)) return;\n\n const items = this.args.items ?? [];\n\n this.#count.current = Math.min(this.#count.current + this.#batchSize, items.length);\n this.#endWaiter();\n },\n { timeout: 100 },\n );\n }\n\n #cancel() {\n if (this.#idleHandle !== null) {\n cancelIdleCallback(this.#idleHandle);\n this.#idleHandle = null;\n }\n\n this.#endWaiter();\n }\n\n #endWaiter() {\n if (this.#waiterToken !== null) {\n waiter.endAsync(this.#waiterToken);\n this.#waiterToken = null;\n }\n }\n\n <template>\n {{#each this.visible as |item index|}}\n {{yield item index}}\n {{/each}}\n </template>\n}\n"],"names":["DEFAULT_BATCH_SIZE","waiter","buildWaiter","IncrementalEach","Component","cell","constructor","owner","args","registerDestructor","#batchSize","requested","batchSize","assert","visible","items","current","length","slice","#scheduleNextBatch","beginAsync","requestIdleCallback","isDestroyed","isDestroying","Math","min","timeout","#cancel","cancelIdleCallback","#endWaiter","endAsync","setComponentTemplate","precompileTemplate","strictMode"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"popover.js","sources":["../../src/components/popover.gts"],"sourcesContent":["import { hash } from \"@ember/helper\";\n\nimport { arrow } from \"@floating-ui/dom\";\nimport { element } from \"ember-element-helper\";\nimport { modifier as eModifier } from \"ember-modifier\";\nimport { cell } from \"ember-resources\";\n\nimport { FloatingUI } from \"../floating-ui.ts\";\n\nimport type { Signature as FloatingUiComponentSignature } from \"../floating-ui/component.ts\";\nimport type { Signature as HookSignature } from \"../floating-ui/modifier.ts\";\nimport type { TOC } from \"@ember/component/template-only\";\nimport type { ElementContext, Middleware } from \"@floating-ui/dom\";\nimport type { ModifierLike, WithBoundArgs } from \"@glint/template\";\n\nexport interface Signature {\n Args: {\n /**\n * See the Floating UI's [flip docs](https://floating-ui.com/docs/flip) for possible values.\n *\n * This argument is forwarded to the `<FloatingUI>` component.\n */\n flipOptions?: HookSignature[\"Args\"][\"Named\"][\"flipOptions\"];\n /**\n * Array of one or more objects to add to Floating UI's list of [middleware](https://floating-ui.com/docs/middleware)\n *\n * This argument is forwarded to the `<FloatingUI>` component.\n */\n middleware?: HookSignature[\"Args\"][\"Named\"][\"middleware\"];\n /**\n * See the Floating UI's [offset docs](https://floating-ui.com/docs/offset) for possible values.\n *\n * This argument is forwarded to the `<FloatingUI>` component.\n */\n offsetOptions?: HookSignature[\"Args\"][\"Named\"][\"offsetOptions\"];\n /**\n * One of the possible [`placements`](https://floating-ui.com/docs/computeposition#placement). The default is 'bottom'.\n *\n * Possible values are\n * - top\n * - bottom\n * - right\n * - left\n *\n * And may optionally have `-start` or `-end` added to adjust position along the side.\n *\n * This argument is forwarded to the `<FloatingUI>` component.\n */\n placement?: `${\"top\" | \"bottom\" | \"left\" | \"right\"}${\"\" | \"-start\" | \"-end\"}`;\n /**\n * See the Floating UI's [shift docs](https://floating-ui.com/docs/shift) for possible values.\n *\n * This argument is forwarded to the `<FloatingUI>` component.\n */\n shiftOptions?: HookSignature[\"Args\"][\"Named\"][\"shiftOptions\"];\n /**\n * CSS position property, either `fixed` or `absolute`.\n *\n * Pros and cons of each strategy are explained on [Floating UI's Docs](https://floating-ui.com/docs/computePosition#strategy)\n *\n * This argument is forwarded to the `<FloatingUI>` component.\n */\n strategy?: HookSignature[\"Args\"][\"Named\"][\"strategy\"];\n };\n Blocks: {\n default: [\n {\n reference: FloatingUiComponentSignature[\"Blocks\"][\"default\"][0];\n setReference: FloatingUiComponentSignature[\"Blocks\"][\"default\"][2][\"setReference\"];\n Content: WithBoundArgs<typeof Content, \"floating\">;\n data: FloatingUiComponentSignature[\"Blocks\"][\"default\"][2][\"data\"];\n arrow: ModifierLike<{ Element: HTMLElement }>;\n },\n ];\n };\n}\n\nconst showPopover = eModifier<{ Element: Element }>((element) => {\n const el = element as HTMLElement;\n\n // Reset [popover] UA overflow default that clips arrows positioned outside\n el.style.setProperty(\"overflow\", \"visible\");\n\n // Don't promote to top layer if already inside a popover — the parent\n // popover already handles layering. Adding both to the top layer causes\n // stacking issues where the parent renders on top of the child.\n if (el.parentElement?.closest(\"[popover]\")) {\n el.removeAttribute(\"popover\");\n\n // <dialog> elements are hidden by default — ensure they're visible\n // when opting out of the top layer.\n if (el instanceof HTMLDialogElement) {\n el.setAttribute(\"open\", \"\");\n }\n } else {\n el.showPopover();\n }\n\n return () => {\n try {\n el.hidePopover();\n } catch {\n /* already hidden */\n }\n };\n});\n\nfunction getElementTag(tagName: undefined | string) {\n return tagName || \"div\";\n}\n\n/**\n * Content uses `popover=\"manual\"` + `showPopover()` to promote\n * the element to the browser's top layer. This escapes all ancestor\n * overflow clipping and stacking contexts — the same guarantee that\n * portalling provided, but using the browser's native mechanism.\n */\nconst Content: TOC<{\n Element: HTMLDivElement;\n Args: {\n floating: ModifierLike<{ Element: HTMLElement }>;\n /**\n * By default the popover content is wrapped in a div.\n * You may change this by supplying the name of an element here.\n *\n * For example:\n * ```gjs\n * <Popover as |p|>\n * <p.Content @as=\"dialog\">\n * this is now focus trapped\n * </p.Content>\n * </Popover>\n * ```\n */\n as?: string;\n };\n Blocks: { default: [] };\n}> = <template>\n {{#let (element (getElementTag @as)) as |El|}}\n {{! @glint-ignore\n https://github.com/tildeio/ember-element-helper/issues/91\n https://github.com/typed-ember/glint/issues/610\n }}\n <El popover=\"manual\" {{showPopover}} {{@floating}} ...attributes>\n {{yield}}\n </El>\n {{/let}}\n</template>;\n\ninterface AttachArrowSignature {\n Element: HTMLElement;\n Args: {\n Named: {\n arrowElement: ReturnType<typeof ArrowElement>;\n data:\n | undefined\n | {\n placement: string;\n middlewareData?: {\n arrow?: { x?: number; y?: number };\n };\n };\n };\n };\n}\n\nconst arrowSides = {\n top: \"bottom\",\n right: \"left\",\n bottom: \"top\",\n left: \"right\",\n};\n\ntype Direction = \"top\" | \"bottom\" | \"left\" | \"right\";\ntype Placement = `${Direction}${\"\" | \"-start\" | \"-end\"}`;\n\nconst attachArrow: ModifierLike<AttachArrowSignature> = eModifier<AttachArrowSignature>(\n (element, _: [], named) => {\n if (element === named.arrowElement.current) {\n if (!named.data) return;\n if (!named.data.middlewareData) return;\n\n const { arrow } = named.data.middlewareData;\n const { placement } = named.data;\n\n if (!arrow) return;\n if (!placement) return;\n\n const { x: arrowX, y: arrowY } = arrow;\n const otherSide = (placement as Placement).split(\"-\")[0] as Direction;\n const staticSide = arrowSides[otherSide];\n\n Object.assign(named.arrowElement.current.style, {\n left: arrowX != null ? `${arrowX}px` : \"\",\n top: arrowY != null ? `${arrowY}px` : \"\",\n right: \"\",\n bottom: \"\",\n [staticSide]: \"-4px\",\n });\n\n return;\n }\n\n void (async () => {\n await Promise.resolve();\n named.arrowElement.set(element);\n })();\n },\n);\n\nconst ArrowElement: () => ReturnType<typeof cell<HTMLElement>> = () => cell<HTMLElement>();\n\nfunction maybeAddArrow(middleware: Middleware[] | undefined, element: Element | undefined) {\n const result = [...(middleware || [])];\n\n if (element) {\n result.push(arrow({ element }));\n }\n\n return result;\n}\n\nfunction flipOptions(options: HookSignature[\"Args\"][\"Named\"][\"flipOptions\"]) {\n return {\n elementContext: \"reference\" as ElementContext,\n ...options,\n };\n}\n\nexport const Popover: TOC<Signature> = <template>\n {{#let (ArrowElement) as |arrowElement|}}\n <FloatingUI\n @placement={{@placement}}\n @strategy={{@strategy}}\n @middleware={{maybeAddArrow @middleware arrowElement.current}}\n @flipOptions={{flipOptions @flipOptions}}\n @shiftOptions={{@shiftOptions}}\n @offsetOptions={{@offsetOptions}}\n as |reference floating extra|\n >\n {{#let (modifier attachArrow arrowElement=arrowElement data=extra.data) as |arrow|}}\n {{yield\n (hash\n reference=reference\n setReference=extra.setReference\n Content=(component Content floating=floating)\n data=extra.data\n arrow=arrow\n )\n }}\n {{/let}}\n </FloatingUI>\n {{/let}}\n</template>;\n\nexport default Popover;\n"],"names":["showPopover","eModifier","element","el","style","setProperty","parentElement","closest","removeAttribute","HTMLDialogElement","setAttribute","hidePopover","getElementTag","tagName","Content","setComponentTemplate","precompileTemplate","strictMode","scope","templateOnly","arrowSides","top","right","bottom","left","attachArrow","_","named","arrowElement","current","data","middlewareData","arrow","placement","x","arrowX","y","arrowY","otherSide","split","staticSide","Object","assign","Promise","resolve","set","ArrowElement","cell","maybeAddArrow","middleware","result","push","flipOptions","options","elementContext","Popover","FloatingUI","hash"],"mappings":"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"}
1
+ {"version":3,"file":"popover.js","sources":["../../src/components/popover.gts"],"sourcesContent":["import { hash } from \"@ember/helper\";\n\nimport { arrow } from \"@floating-ui/dom\";\nimport { element } from \"ember-element-helper\";\nimport { modifier as eModifier } from \"ember-modifier\";\nimport { cell } from \"ember-resources\";\n\nimport { FloatingUI } from \"../floating-ui.ts\";\n\nimport type { Signature as FloatingUiComponentSignature } from \"../floating-ui/component.gts\";\nimport type { Signature as HookSignature } from \"../floating-ui/modifier.ts\";\nimport type { TOC } from \"@ember/component/template-only\";\nimport type { ElementContext, Middleware } from \"@floating-ui/dom\";\nimport type { ModifierLike, WithBoundArgs } from \"@glint/template\";\n\nexport interface Signature {\n Args: {\n /**\n * See the Floating UI's [flip docs](https://floating-ui.com/docs/flip) for possible values.\n *\n * This argument is forwarded to the `<FloatingUI>` component.\n */\n flipOptions?: HookSignature[\"Args\"][\"Named\"][\"flipOptions\"];\n /**\n * Array of one or more objects to add to Floating UI's list of [middleware](https://floating-ui.com/docs/middleware)\n *\n * This argument is forwarded to the `<FloatingUI>` component.\n */\n middleware?: HookSignature[\"Args\"][\"Named\"][\"middleware\"];\n /**\n * See the Floating UI's [offset docs](https://floating-ui.com/docs/offset) for possible values.\n *\n * This argument is forwarded to the `<FloatingUI>` component.\n */\n offsetOptions?: HookSignature[\"Args\"][\"Named\"][\"offsetOptions\"];\n /**\n * One of the possible [`placements`](https://floating-ui.com/docs/computeposition#placement). The default is 'bottom'.\n *\n * Possible values are\n * - top\n * - bottom\n * - right\n * - left\n *\n * And may optionally have `-start` or `-end` added to adjust position along the side.\n *\n * This argument is forwarded to the `<FloatingUI>` component.\n */\n placement?: `${\"top\" | \"bottom\" | \"left\" | \"right\"}${\"\" | \"-start\" | \"-end\"}`;\n /**\n * See the Floating UI's [shift docs](https://floating-ui.com/docs/shift) for possible values.\n *\n * This argument is forwarded to the `<FloatingUI>` component.\n */\n shiftOptions?: HookSignature[\"Args\"][\"Named\"][\"shiftOptions\"];\n /**\n * CSS position property, either `fixed` or `absolute`.\n *\n * Pros and cons of each strategy are explained on [Floating UI's Docs](https://floating-ui.com/docs/computePosition#strategy)\n *\n * This argument is forwarded to the `<FloatingUI>` component.\n */\n strategy?: HookSignature[\"Args\"][\"Named\"][\"strategy\"];\n };\n Blocks: {\n default: [\n {\n reference: FloatingUiComponentSignature[\"Blocks\"][\"default\"][0];\n setReference: FloatingUiComponentSignature[\"Blocks\"][\"default\"][2][\"setReference\"];\n Content: WithBoundArgs<typeof Content, \"floating\">;\n data: FloatingUiComponentSignature[\"Blocks\"][\"default\"][2][\"data\"];\n arrow: ModifierLike<{ Element: HTMLElement }>;\n },\n ];\n };\n}\n\nconst showPopover = eModifier<{ Element: Element }>((element) => {\n const el = element as HTMLElement;\n\n // Reset [popover] UA overflow default that clips arrows positioned outside\n el.style.setProperty(\"overflow\", \"visible\");\n\n // Don't promote to top layer if already inside a popover — the parent\n // popover already handles layering. Adding both to the top layer causes\n // stacking issues where the parent renders on top of the child.\n if (el.parentElement?.closest(\"[popover]\")) {\n el.removeAttribute(\"popover\");\n\n // <dialog> elements are hidden by default — ensure they're visible\n // when opting out of the top layer.\n if (el instanceof HTMLDialogElement) {\n el.setAttribute(\"open\", \"\");\n }\n } else {\n el.showPopover();\n }\n\n return () => {\n try {\n el.hidePopover();\n } catch {\n /* already hidden */\n }\n };\n});\n\nfunction getElementTag(tagName: undefined | string) {\n return tagName || \"div\";\n}\n\n/**\n * Content uses `popover=\"manual\"` + `showPopover()` to promote\n * the element to the browser's top layer. This escapes all ancestor\n * overflow clipping and stacking contexts — the same guarantee that\n * portalling provided, but using the browser's native mechanism.\n */\nconst Content: TOC<{\n Element: HTMLDivElement;\n Args: {\n floating: ModifierLike<{ Element: HTMLElement }>;\n /**\n * By default the popover content is wrapped in a div.\n * You may change this by supplying the name of an element here.\n *\n * For example:\n * ```gjs\n * <Popover as |p|>\n * <p.Content @as=\"dialog\">\n * this is now focus trapped\n * </p.Content>\n * </Popover>\n * ```\n */\n as?: string;\n };\n Blocks: { default: [] };\n}> = <template>\n {{#let (element (getElementTag @as)) as |El|}}\n {{! @glint-ignore\n https://github.com/tildeio/ember-element-helper/issues/91\n https://github.com/typed-ember/glint/issues/610\n }}\n <El popover=\"manual\" {{showPopover}} {{@floating}} ...attributes>\n {{yield}}\n </El>\n {{/let}}\n</template>;\n\ninterface AttachArrowSignature {\n Element: HTMLElement;\n Args: {\n Named: {\n arrowElement: ReturnType<typeof ArrowElement>;\n data:\n | undefined\n | {\n placement: string;\n middlewareData?: {\n arrow?: { x?: number; y?: number };\n };\n };\n };\n };\n}\n\nconst arrowSides = {\n top: \"bottom\",\n right: \"left\",\n bottom: \"top\",\n left: \"right\",\n};\n\ntype Direction = \"top\" | \"bottom\" | \"left\" | \"right\";\ntype Placement = `${Direction}${\"\" | \"-start\" | \"-end\"}`;\n\nconst attachArrow: ModifierLike<AttachArrowSignature> = eModifier<AttachArrowSignature>(\n (element, _: [], named) => {\n if (element === named.arrowElement.current) {\n if (!named.data) return;\n if (!named.data.middlewareData) return;\n\n const { arrow } = named.data.middlewareData;\n const { placement } = named.data;\n\n if (!arrow) return;\n if (!placement) return;\n\n const { x: arrowX, y: arrowY } = arrow;\n const otherSide = (placement as Placement).split(\"-\")[0] as Direction;\n const staticSide = arrowSides[otherSide];\n\n Object.assign(named.arrowElement.current.style, {\n left: arrowX != null ? `${arrowX}px` : \"\",\n top: arrowY != null ? `${arrowY}px` : \"\",\n right: \"\",\n bottom: \"\",\n [staticSide]: \"-4px\",\n });\n\n return;\n }\n\n void (async () => {\n await Promise.resolve();\n named.arrowElement.set(element);\n })();\n },\n);\n\nconst ArrowElement: () => ReturnType<typeof cell<HTMLElement>> = () => cell<HTMLElement>();\n\nfunction maybeAddArrow(middleware: Middleware[] | undefined, element: Element | undefined) {\n const result = [...(middleware || [])];\n\n if (element) {\n result.push(arrow({ element }));\n }\n\n return result;\n}\n\nfunction flipOptions(options: HookSignature[\"Args\"][\"Named\"][\"flipOptions\"]) {\n return {\n elementContext: \"reference\" as ElementContext,\n ...options,\n };\n}\n\nexport const Popover: TOC<Signature> = <template>\n {{#let (ArrowElement) as |arrowElement|}}\n <FloatingUI\n @placement={{@placement}}\n @strategy={{@strategy}}\n @middleware={{maybeAddArrow @middleware arrowElement.current}}\n @flipOptions={{flipOptions @flipOptions}}\n @shiftOptions={{@shiftOptions}}\n @offsetOptions={{@offsetOptions}}\n as |reference floating extra|\n >\n {{#let (modifier attachArrow arrowElement=arrowElement data=extra.data) as |arrow|}}\n {{yield\n (hash\n reference=reference\n setReference=extra.setReference\n Content=(component Content floating=floating)\n data=extra.data\n arrow=arrow\n )\n }}\n {{/let}}\n </FloatingUI>\n {{/let}}\n</template>;\n\nexport default Popover;\n"],"names":["showPopover","eModifier","element","el","style","setProperty","parentElement","closest","removeAttribute","HTMLDialogElement","setAttribute","hidePopover","getElementTag","tagName","Content","setComponentTemplate","precompileTemplate","strictMode","scope","templateOnly","arrowSides","top","right","bottom","left","attachArrow","_","named","arrowElement","current","data","middlewareData","arrow","placement","x","arrowX","y","arrowY","otherSide","split","staticSide","Object","assign","Promise","resolve","set","ArrowElement","cell","maybeAddArrow","middleware","result","push","flipOptions","options","elementContext","Popover","FloatingUI","hash"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -6,6 +6,7 @@ export { Dialog, Dialog as Modal } from './components/dialog.js';
6
6
  export { Drawer } from './components/drawer.js';
7
7
  export { ExternalLink } from './components/external-link.js';
8
8
  export { Form } from './components/form.js';
9
+ export { IncrementalEach } from './components/incremental-each.js';
9
10
  export { Key, KeyCombo } from './components/keys.js';
10
11
  export { StickyFooter } from './components/layout/sticky-footer.js';
11
12
  export { Link } from './components/link.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["/**\n * DANGER: this is a *barrel file*\n *\n * It forces the whole library to be loaded and all dependencies.\n *\n * If you have a small app, you probably don't want to import from here -- instead import from each sub-path.\n */\nimport { importSync, isDevelopingApp, macroCondition } from '@embroider/macros';\n\nif (macroCondition(isDevelopingApp())) {\n importSync('./components/violations.css');\n}\n\nexport { Accordion } from './components/accordion.gts';\nexport type {\n AccordionContentExternalSignature,\n AccordionHeaderExternalSignature,\n AccordionItemExternalSignature,\n AccordionTriggerExternalSignature,\n} from './components/accordion/public.ts';\nexport { Avatar } from './components/avatar.gts';\nexport { Breadcrumb } from './components/breadcrumb.gts';\nexport { Dialog, Dialog as Modal } from './components/dialog.gts';\nexport { Drawer } from './components/drawer.gts';\nexport { ExternalLink } from './components/external-link.gts';\nexport { Form } from './components/form.gts';\nexport { Key, KeyCombo } from './components/keys.gts';\nexport { StickyFooter } from './components/layout/sticky-footer.gts';\nexport { Link } from './components/link.gts';\nexport { Menu } from './components/menu.gts';\nexport { OTP, OTPInput } from './components/one-time-password.gts';\nexport { Popover } from './components/popover.gts';\nexport { Portal } from './components/portal.gts';\nexport { PortalTargets } from './components/portal-targets.gts';\nexport { TARGETS as PORTALS } from './components/portal-targets.gts';\nexport { Progress } from './components/progress.gts';\nexport { Rating } from './components/rating.gts';\nexport { Scroller } from './components/scroller.gts';\nexport { Separator } from './components/separator.gts';\nexport { Shadowed } from './components/shadowed.gts';\nexport { Switch } from './components/switch.gts';\nexport { Toggle } from './components/toggle.gts';\nexport { ToggleGroup } from './components/toggle-group.gts';\nexport { VisuallyHidden } from './components/visually-hidden.gts';\nexport { Zoetrope } from './components/zoetrope.ts';\nexport * from './helpers.ts';\n"],"names":["macroCondition","isDevelopingApp","importSync"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA,IAAIA,cAAc,CAACC,eAAe,EAAE,CAAC,EAAE;EACrCC,UAAU,CAAC,6BAA6B,CAAC;AAC3C"}
1
+ {"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["/**\n * DANGER: this is a *barrel file*\n *\n * It forces the whole library to be loaded and all dependencies.\n *\n * If you have a small app, you probably don't want to import from here -- instead import from each sub-path.\n */\nimport { importSync, isDevelopingApp, macroCondition } from '@embroider/macros';\n\nif (macroCondition(isDevelopingApp())) {\n importSync('./components/violations.css');\n}\n\nexport { Accordion } from './components/accordion.gts';\nexport type {\n AccordionContentExternalSignature,\n AccordionHeaderExternalSignature,\n AccordionItemExternalSignature,\n AccordionTriggerExternalSignature,\n} from './components/accordion/public.ts';\nexport { Avatar } from './components/avatar.gts';\nexport { Breadcrumb } from './components/breadcrumb.gts';\nexport { Dialog, Dialog as Modal } from './components/dialog.gts';\nexport { Drawer } from './components/drawer.gts';\nexport { ExternalLink } from './components/external-link.gts';\nexport { Form } from './components/form.gts';\nexport { IncrementalEach } from './components/incremental-each.gts';\nexport { Key, KeyCombo } from './components/keys.gts';\nexport { StickyFooter } from './components/layout/sticky-footer.gts';\nexport { Link } from './components/link.gts';\nexport { Menu } from './components/menu.gts';\nexport { OTP, OTPInput } from './components/one-time-password.gts';\nexport { Popover } from './components/popover.gts';\nexport { Portal } from './components/portal.gts';\nexport { PortalTargets } from './components/portal-targets.gts';\nexport { TARGETS as PORTALS } from './components/portal-targets.gts';\nexport { Progress } from './components/progress.gts';\nexport { Rating } from './components/rating.gts';\nexport { Scroller } from './components/scroller.gts';\nexport { Separator } from './components/separator.gts';\nexport { Shadowed } from './components/shadowed.gts';\nexport { Switch } from './components/switch.gts';\nexport { Toggle } from './components/toggle.gts';\nexport { ToggleGroup } from './components/toggle-group.gts';\nexport { VisuallyHidden } from './components/visually-hidden.gts';\nexport { Zoetrope } from './components/zoetrope.ts';\nexport * from './helpers.ts';\n"],"names":["macroCondition","isDevelopingApp","importSync"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA,IAAIA,cAAc,CAACC,eAAe,EAAE,CAAC,EAAE;EACrCC,UAAU,CAAC,6BAA6B,CAAC;AAC3C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ember-primitives",
3
- "version": "0.57.0",
3
+ "version": "0.58.0",
4
4
  "description": "Making apps easier to build",
5
5
  "keywords": [
6
6
  "ember-addon"
@@ -0,0 +1,205 @@
1
+ import Component from "@glimmer/component";
2
+ import { assert } from "@ember/debug";
3
+ import { isDestroyed, isDestroying, registerDestructor } from "@ember/destroyable";
4
+ import { buildWaiter } from "@ember/test-waiters";
5
+
6
+ import { cell } from "ember-resources";
7
+
8
+ import type Owner from "@ember/owner";
9
+
10
+ const DEFAULT_BATCH_SIZE = 50;
11
+
12
+ const waiter = buildWaiter("ember-primitives:incremental-each");
13
+
14
+ export interface Signature<T = unknown> {
15
+ Args: {
16
+ /**
17
+ * The collection of items to render.
18
+ *
19
+ * Replacing the array (new identity) restarts rendering from the
20
+ * first batch.
21
+ *
22
+ * ```gjs
23
+ * import { IncrementalEach } from 'ember-primitives';
24
+ *
25
+ * <template>
26
+ * <IncrementalEach @items={{this.rows}} as |row|>
27
+ * <my-row @row={{row}} />
28
+ * </IncrementalEach>
29
+ * </template>
30
+ * ```
31
+ */
32
+ items: readonly T[];
33
+
34
+ /**
35
+ * How many items to add per idle callback.
36
+ *
37
+ * Larger batches add more items per chunk; smaller batches yield to
38
+ * the browser more often.
39
+ *
40
+ * Default: 50. Must be positive; `0` or less asserts in development.
41
+ *
42
+ * ```gjs
43
+ * import { IncrementalEach } from 'ember-primitives';
44
+ *
45
+ * <template>
46
+ * <IncrementalEach @items={{this.rows}} @batchSize={{100}} as |row|>
47
+ * <my-row @row={{row}} />
48
+ * </IncrementalEach>
49
+ * </template>
50
+ * ```
51
+ */
52
+ batchSize?: number;
53
+ };
54
+ Blocks: {
55
+ /**
56
+ * Yielded for each rendered item, with the index in the original
57
+ * `@items` array.
58
+ *
59
+ * ```gjs
60
+ * import { IncrementalEach } from 'ember-primitives';
61
+ *
62
+ * <template>
63
+ * <IncrementalEach @items={{this.rows}} as |row index|>
64
+ * {{index}}: {{row.label}}
65
+ * </IncrementalEach>
66
+ * </template>
67
+ * ```
68
+ */
69
+ default: [item: T, index: number];
70
+ };
71
+ }
72
+
73
+ /**
74
+ * A drop-in replacement for `{{#each}}` that renders a large collection a
75
+ * batch at a time during the browser's idle periods, instead of all at once.
76
+ *
77
+ * Every item ends up in the DOM, so browser find (Ctrl+F / Cmd+F), anchor
78
+ * links, screen readers, print, and SEO all work against the full list.
79
+ * Yielding the main thread between batches keeps the page responsive while
80
+ * the rest of the list is filling in.
81
+ *
82
+ * Intended for non-scrollable containers, or anywhere a virtual/windowed
83
+ * list does not apply (variable item heights, lists that grow the page,
84
+ * surfaces that need every row indexable).
85
+ *
86
+ * @example
87
+ * ```gjs
88
+ * import { IncrementalEach } from 'ember-primitives';
89
+ *
90
+ * <template>
91
+ * <ul>
92
+ * <IncrementalEach @items={{this.rows}} @batchSize={{100}} as |row index|>
93
+ * <li>{{index}}: {{row.label}}</li>
94
+ * </IncrementalEach>
95
+ * </ul>
96
+ * </template>
97
+ * ```
98
+ */
99
+ export class IncrementalEach<T = unknown> extends Component<Signature<T>> {
100
+ // How many items have been committed to the DOM so far. Bumped one
101
+ // batch at a time by the idle callback. Wrapped in a `cell` because
102
+ // `@tracked` doesn't compose with `#`-private fields under this
103
+ // codebase's decorator transform.
104
+ #count = cell(0);
105
+
106
+ // Plain field so identity checks don't add a render-time dependency
107
+ // on top of `args.items`.
108
+ #itemsRef: readonly T[] | null = null;
109
+
110
+ #idleHandle: number | null = null;
111
+
112
+ #waiterToken: unknown = null;
113
+
114
+ constructor(owner: Owner, args: Signature<T>["Args"]) {
115
+ super(owner, args);
116
+
117
+ registerDestructor(this, () => this.#cancel());
118
+ }
119
+
120
+ get #batchSize(): number {
121
+ const requested = this.args.batchSize ?? DEFAULT_BATCH_SIZE;
122
+
123
+ assert(
124
+ `<IncrementalEach> @batchSize must be a positive number, got ${requested}`,
125
+ requested > 0,
126
+ );
127
+
128
+ return requested;
129
+ }
130
+
131
+ // The items that should currently be rendered. `@cached` keeps the
132
+ // returned slice stable across renders that don't change `#count` or
133
+ // `args.items`, so Glimmer's `{{#each}}` doesn't see a fresh array on
134
+ // every render and we only slice once per batch landing. This is
135
+ // also where scheduling is driven from: `@items` identity changes
136
+ // reset `#count` to zero, and missing items queue the next idle
137
+ // callback. Autotrack stays consistent because the only synchronous
138
+ // write here (`#count = 0`) happens before `#count` is read.
139
+ /* eslint-disable ember/no-side-effects */
140
+ get visible(): readonly T[] {
141
+ const items = this.args.items ?? [];
142
+
143
+ if (items !== this.#itemsRef) {
144
+ this.#itemsRef = items;
145
+ this.#cancel();
146
+ this.#count.current = 0;
147
+ }
148
+
149
+ if (this.#count.current < items.length && this.#idleHandle === null) {
150
+ this.#scheduleNextBatch();
151
+ }
152
+
153
+ return items.slice(0, this.#count.current);
154
+ }
155
+ /* eslint-enable ember/no-side-effects */
156
+
157
+ #scheduleNextBatch() {
158
+ // Defensive: if a batch is already pending, drop it before
159
+ // queueing a new one. The `visible` getter already guards on
160
+ // `#idleHandle === null`, but a future caller might not.
161
+ this.#cancel();
162
+
163
+ this.#waiterToken = waiter.beginAsync();
164
+
165
+ // The `timeout` cap ensures forward progress even when the host
166
+ // is CPU-bound and the browser never reports a free idle slot.
167
+ // In normal use this is a no-op because real idle time arrives
168
+ // far sooner.
169
+ this.#idleHandle = requestIdleCallback(
170
+ () => {
171
+ this.#idleHandle = null;
172
+
173
+ if (isDestroyed(this) || isDestroying(this)) return;
174
+
175
+ const items = this.args.items ?? [];
176
+
177
+ this.#count.current = Math.min(this.#count.current + this.#batchSize, items.length);
178
+ this.#endWaiter();
179
+ },
180
+ { timeout: 100 },
181
+ );
182
+ }
183
+
184
+ #cancel() {
185
+ if (this.#idleHandle !== null) {
186
+ cancelIdleCallback(this.#idleHandle);
187
+ this.#idleHandle = null;
188
+ }
189
+
190
+ this.#endWaiter();
191
+ }
192
+
193
+ #endWaiter() {
194
+ if (this.#waiterToken !== null) {
195
+ waiter.endAsync(this.#waiterToken);
196
+ this.#waiterToken = null;
197
+ }
198
+ }
199
+
200
+ <template>
201
+ {{#each this.visible as |item index|}}
202
+ {{yield item index}}
203
+ {{/each}}
204
+ </template>
205
+ }
@@ -7,7 +7,7 @@ import { cell } from "ember-resources";
7
7
 
8
8
  import { FloatingUI } from "../floating-ui.ts";
9
9
 
10
- import type { Signature as FloatingUiComponentSignature } from "../floating-ui/component.ts";
10
+ import type { Signature as FloatingUiComponentSignature } from "../floating-ui/component.gts";
11
11
  import type { Signature as HookSignature } from "../floating-ui/modifier.ts";
12
12
  import type { TOC } from "@ember/component/template-only";
13
13
  import type { ElementContext, Middleware } from "@floating-ui/dom";
package/src/index.ts CHANGED
@@ -24,6 +24,7 @@ export { Dialog, Dialog as Modal } from './components/dialog.gts';
24
24
  export { Drawer } from './components/drawer.gts';
25
25
  export { ExternalLink } from './components/external-link.gts';
26
26
  export { Form } from './components/form.gts';
27
+ export { IncrementalEach } from './components/incremental-each.gts';
27
28
  export { Key, KeyCombo } from './components/keys.gts';
28
29
  export { StickyFooter } from './components/layout/sticky-footer.gts';
29
30
  export { Link } from './components/link.gts';
@@ -2,21 +2,21 @@
2
2
  // Add all your components, helpers and modifiers to the template registry here, so apps don't have to do this.
3
3
  // See https://typed-ember.gitbook.io/glint/using-glint/ember/authoring-addons
4
4
 
5
- import type { Accordion } from './components/accordion';
6
- import type { AccordionContent } from './components/accordion/content';
7
- import type { AccordionHeader } from './components/accordion/header';
8
- import type { AccordionItem } from './components/accordion/item';
9
- import type { AccordionTrigger } from './components/accordion/trigger';
10
- import type { Dialog } from './components/dialog';
11
- import type { ExternalLink } from './components/external-link';
12
- import type { Link } from './components/link';
13
- import type { Popover } from './components/popover';
14
- import type { Portal } from './components/portal';
15
- import type { PortalTargets } from './components/portal-targets';
16
- import type { Shadowed } from './components/shadowed';
17
- import type { Switch } from './components/switch';
18
- import type { Toggle } from './components/toggle';
19
- import type { service } from './helpers/service';
5
+ import type { Accordion } from './components/accordion.gts';
6
+ import type { AccordionContent } from './components/accordion/content.gts';
7
+ import type { AccordionHeader } from './components/accordion/header.gts';
8
+ import type { AccordionItem } from './components/accordion/item.gts';
9
+ import type { AccordionTrigger } from './components/accordion/trigger.gts';
10
+ import type { Dialog } from './components/dialog.gts';
11
+ import type { ExternalLink } from './components/external-link.gts';
12
+ import type { Link } from './components/link.gts';
13
+ import type { Popover } from './components/popover.gts';
14
+ import type { Portal } from './components/portal.gts';
15
+ import type { PortalTargets } from './components/portal-targets.gts';
16
+ import type { Shadowed } from './components/shadowed.gts';
17
+ import type { Switch } from './components/switch.gts';
18
+ import type { Toggle } from './components/toggle.gts';
19
+ import type { service } from './helpers/service.ts';
20
20
 
21
21
  // import type MyComponent from './components/my-component';
22
22