@tanstack/ember-table 9.0.0-beta.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.
Files changed (46) hide show
  1. package/README.md +128 -0
  2. package/addon-main.cjs +4 -0
  3. package/declarations/FlexRender.d.ts +63 -0
  4. package/declarations/FlexRender.d.ts.map +1 -0
  5. package/declarations/create-table-hook.d.ts +33 -0
  6. package/declarations/create-table-hook.d.ts.map +1 -0
  7. package/declarations/experimental-worker-plugin.d.ts +2 -0
  8. package/declarations/experimental-worker-plugin.d.ts.map +1 -0
  9. package/declarations/flex-render-helpers.d.ts +25 -0
  10. package/declarations/flex-render-helpers.d.ts.map +1 -0
  11. package/declarations/flex-render.d.ts +3 -0
  12. package/declarations/flex-render.d.ts.map +1 -0
  13. package/declarations/index.d.ts +10 -0
  14. package/declarations/index.d.ts.map +1 -0
  15. package/declarations/reactivity.d.ts +3 -0
  16. package/declarations/reactivity.d.ts.map +1 -0
  17. package/declarations/signal.d.ts +38 -0
  18. package/declarations/signal.d.ts.map +1 -0
  19. package/declarations/static-functions.d.ts +2 -0
  20. package/declarations/static-functions.d.ts.map +1 -0
  21. package/declarations/use-table.d.ts +3 -0
  22. package/declarations/use-table.d.ts.map +1 -0
  23. package/dist/FlexRender-fkWv7pUy.js +128 -0
  24. package/dist/FlexRender-fkWv7pUy.js.map +1 -0
  25. package/dist/experimental-worker-plugin.js +2 -0
  26. package/dist/experimental-worker-plugin.js.map +1 -0
  27. package/dist/flex-render.js +3 -0
  28. package/dist/flex-render.js.map +1 -0
  29. package/dist/index.js +286 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/static-functions.js +2 -0
  32. package/dist/static-functions.js.map +1 -0
  33. package/package.json +147 -0
  34. package/skills/create-table-hook/SKILL.md +117 -0
  35. package/skills/getting-started/SKILL.md +160 -0
  36. package/skills/table-state/SKILL.md +187 -0
  37. package/src/FlexRender.gts +267 -0
  38. package/src/create-table-hook.ts +83 -0
  39. package/src/experimental-worker-plugin.ts +1 -0
  40. package/src/flex-render-helpers.ts +103 -0
  41. package/src/flex-render.ts +6 -0
  42. package/src/index.ts +31 -0
  43. package/src/reactivity.ts +39 -0
  44. package/src/signal.ts +126 -0
  45. package/src/static-functions.ts +1 -0
  46. package/src/use-table.ts +154 -0
package/dist/index.js ADDED
@@ -0,0 +1,286 @@
1
+ import { constructTable, createColumnHelper } from '@tanstack/table-core';
2
+ export * from '@tanstack/table-core';
3
+ import { untrack } from '@glimmer/validator';
4
+ import { tracked, cached } from '@glimmer/tracking';
5
+ import { g, i, n } from 'decorator-transforms/runtime-esm';
6
+ export { F as FlexRenderCell, a as FlexRenderComponentConfig, b as FlexRenderFooter, c as FlexRenderHeader, f as flexRenderComponent } from './FlexRender-fkWv7pUy.js';
7
+ export { flexRender } from '@tanstack/table-core/flex-render';
8
+
9
+ function subscribeNoEffect() {
10
+ return null;
11
+ }
12
+
13
+ /**
14
+ * Ember-native signal implementation.
15
+ * In future ember >7.3 `tracked` will be available by itself
16
+ * so the class wrapper will not be needed for those versions
17
+ */
18
+ class Signal {
19
+ static {
20
+ g(this.prototype, "_value", [tracked]);
21
+ }
22
+ #_value = (i(this, "_value"), void 0);
23
+ #options;
24
+
25
+ // Ember reads are tag-tracked; this observer list exists for the plain-JS
26
+ // subscribers core wires up (external-atom sync in constructTable, the
27
+ // inspector), which have no access to framework tracking.
28
+ #listeners = new Set();
29
+ constructor(value, options) {
30
+ this._value = value;
31
+ this.#options = options;
32
+ }
33
+ subscribe(listenerOrObserver) {
34
+ const listener = typeof listenerOrObserver === 'function' ? listenerOrObserver : listenerOrObserver.next;
35
+ if (!listener) {
36
+ return {
37
+ unsubscribe: () => {}
38
+ };
39
+ }
40
+ this.#listeners.add(listener);
41
+ return {
42
+ unsubscribe: () => this.#listeners.delete(listener)
43
+ };
44
+ }
45
+ get() {
46
+ return this.value;
47
+ }
48
+ set(value) {
49
+ if (typeof value === 'function') {
50
+ return this.update(value);
51
+ }
52
+ this.value = value;
53
+ }
54
+ get value() {
55
+ return this._value;
56
+ }
57
+ set value(next) {
58
+ const prev = untrack(() => this._value);
59
+ const isEqual = this.#options?.compare ? this.#options.compare(prev, next) : prev === next;
60
+ if (isEqual) {
61
+ return;
62
+ }
63
+ this._value = next;
64
+ for (const listener of this.#listeners) {
65
+ listener(next);
66
+ }
67
+ }
68
+ update(fn) {
69
+ const original = untrack(() => this._value);
70
+ this.value = fn(original);
71
+ }
72
+ }
73
+ class ComputedSignal {
74
+ #compute;
75
+ constructor(compute) {
76
+ this.#compute = compute;
77
+ }
78
+ get() {
79
+ return this.value;
80
+ }
81
+ subscribe = subscribeNoEffect;
82
+ get value() {
83
+ return this.#compute();
84
+ }
85
+ static {
86
+ n(this.prototype, "value", [cached]);
87
+ }
88
+ }
89
+ function signal(value, options) {
90
+ return new Signal(value, options);
91
+ }
92
+
93
+ /**
94
+ * Creates an Ember-native writable atom, satisfying the `@tanstack/store`
95
+ * `Atom` contract so it can be passed to `options.atoms`. Because it is backed
96
+ * by a `@tracked` Signal, reading `atom.get()` directly in a template or
97
+ * getter is reactive — unlike a foreign `@tanstack/store` atom, whose reads
98
+ * create no Glimmer tag dependency.
99
+ *
100
+ * Takes a plain initial value only; there is no derived/function overload.
101
+ */
102
+ function createAtom(initialValue, options) {
103
+ return signal(initialValue, options);
104
+ }
105
+ function computed(fn) {
106
+ return new ComputedSignal(fn);
107
+ }
108
+
109
+ function emberReactivity() {
110
+ const subscriptions = new Set();
111
+ return {
112
+ createOptionsStore: true,
113
+ wrapExternalAtoms: true,
114
+ // timing is not important, but the main thing is that the work does *not*
115
+ // happen during the render phase.
116
+ schedule: fn => queueMicrotask(() => fn()),
117
+ batch: fn => fn(),
118
+ untrack,
119
+ // @cached
120
+ createReadonlyAtom: fn => {
121
+ return computed(fn);
122
+ },
123
+ // @tracked
124
+ createWritableAtom: (value, options) => {
125
+ return signal(value, options);
126
+ },
127
+ // Not for the ember integration, but for the tanstack inspector
128
+ addSubscription: subscription => {
129
+ subscriptions.add(subscription);
130
+ },
131
+ unmount: () => {
132
+ subscriptions.forEach(s => s.unsubscribe());
133
+ subscriptions.clear();
134
+ }
135
+ };
136
+ }
137
+
138
+ // Internal table slots used by the pull-based options/state wiring below.
139
+ // `Table_Internal` is not exported from the table-core build, so the shape is
140
+ // declared structurally here.
141
+
142
+ function useTable(getOptions) {
143
+ const reactivity = emberReactivity();
144
+
145
+ // Creates reactive read only signal for options
146
+ const userOptions = computed(getOptions);
147
+
148
+ // Untracked to prevent possible "set on same computation as read" errors in Ember.
149
+ const initialOptions = untrack(() => userOptions.get());
150
+ const table = constructTable({
151
+ ...initialOptions,
152
+ features: {
153
+ coreReactivityFeature: reactivity,
154
+ ...initialOptions.features
155
+ },
156
+ mergeOptions: (defaultOptions, newOptions) => ({
157
+ ...defaultOptions,
158
+ ...newOptions
159
+ })
160
+ });
161
+ const optionsStore = table.optionsStore;
162
+ const liveOptions = computed(() => {
163
+ const stored = optionsStore.get();
164
+ return {
165
+ ...stored,
166
+ ...userOptions.get(),
167
+ // stored options carry construct-time normalization (the reactivity
168
+ // feature, wrapped external atoms) that must win over the raw user
169
+ // options.
170
+ features: stored.features,
171
+ atoms: stored.atoms
172
+ };
173
+ });
174
+
175
+ /**
176
+ * This is to get around core table not using lazy access so we need to re-wrap
177
+ *
178
+ * Similar to other reactive signal frameworks (solid, angular, svelte)
179
+ */
180
+ Object.defineProperty(table, 'options', {
181
+ configurable: true,
182
+ enumerable: true,
183
+ get: () => liveOptions.get(),
184
+ set: value => {
185
+ optionsStore.set(() => value);
186
+ }
187
+ });
188
+ const atoms = table.atoms;
189
+ const stateKeys = Object.keys(table.baseAtoms);
190
+ for (const key of stateKeys) {
191
+ const baseAtom = table.baseAtoms[key];
192
+
193
+ /**
194
+ * Original atoms could cause effects for top level properties
195
+ *
196
+ * Core table should migrate to pure derived data which would boost render
197
+ * performance for both signal and non-signal frameworks.
198
+ */
199
+ atoms[key] = reactivity.createReadonlyAtom(() => {
200
+ const externalAtom = table.options.atoms?.[key];
201
+ if (externalAtom) {
202
+ return externalAtom.get();
203
+ }
204
+ const stateSlice = table.options.state?.[key];
205
+ if (stateSlice !== undefined) {
206
+ return stateSlice;
207
+ }
208
+ return baseAtom.get();
209
+ }, {
210
+ debugName: `table/atoms/${key}`
211
+ });
212
+ }
213
+ const stateProxy = new Proxy({}, {
214
+ get: (_target, key) => typeof key === 'string' ? atoms[key]?.get() : undefined,
215
+ has: (_target, key) => typeof key === 'string' && stateKeys.includes(key),
216
+ ownKeys: () => stateKeys,
217
+ getOwnPropertyDescriptor: (_target, key) => typeof key === 'string' && stateKeys.includes(key) ? {
218
+ enumerable: true,
219
+ configurable: true,
220
+ value: atoms[key].get()
221
+ } : undefined
222
+ })
223
+
224
+ /**
225
+ * Store is reasigned to point to proxy object to allow individual state slices to be independently reactive.
226
+ *
227
+ * Type cast is needed because we are setting during construction
228
+ * Table store is readonly after first initialization.
229
+ */;
230
+ table.store = {
231
+ get: () => stateProxy,
232
+ get state() {
233
+ return stateProxy;
234
+ },
235
+ subscribe: subscribeNoEffect
236
+ };
237
+ return table;
238
+ }
239
+
240
+ /**
241
+ * Bundles a feature set and shared default options once so every table in your
242
+ * app can be created without repeating them. Returns a typed column helper
243
+ * factory and a `createAppTable` that wraps {@link useTable}.
244
+ *
245
+ * Unlike the React adapter's hook, this does not pre-bind cell/header
246
+ * components onto the table; render with the `FlexRenderCell`,
247
+ * `FlexRenderHeader`, and `FlexRenderFooter` components as usual.
248
+ *
249
+ * @example
250
+ * ```ts
251
+ * const { createAppTable, createAppColumnHelper } = createTableHook({
252
+ * features: tableFeatures({ rowSortingFeature, sortedRowModel: createSortedRowModel(), sortFns }),
253
+ * })
254
+ *
255
+ * const columnHelper = createAppColumnHelper<Person>()
256
+ * const columns = columnHelper.columns([...])
257
+ *
258
+ * // inside a Glimmer component; options stay a thunk so tracked reads are reactive
259
+ * table = createAppTable(() => ({ columns, data: this.data }))
260
+ * ```
261
+ */
262
+ function createTableHook({
263
+ ...defaultTableOptions
264
+ }) {
265
+ function createAppColumnHelper() {
266
+ return createColumnHelper();
267
+ }
268
+ function createAppTable(getTableOptions) {
269
+ // Keep options a thunk: the merge runs inside `useTable`'s options thunk,
270
+ // so tracked properties read in `getTableOptions` stay reactive. Per-table
271
+ // options take precedence over the shared defaults (except `features`,
272
+ // which only the hook provides).
273
+ return useTable(() => ({
274
+ ...defaultTableOptions,
275
+ ...getTableOptions()
276
+ }));
277
+ }
278
+ return {
279
+ appFeatures: defaultTableOptions.features,
280
+ createAppColumnHelper,
281
+ createAppTable
282
+ };
283
+ }
284
+
285
+ export { ComputedSignal, Signal, computed, createAtom, createTableHook, emberReactivity, signal, useTable };
286
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/signal.ts","../src/reactivity.ts","../src/use-table.ts","../src/create-table-hook.ts"],"sourcesContent":["import type { Atom, AtomOptions, Observer, Subscription } from '@tanstack/store'\n\nimport { tracked, cached } from '@glimmer/tracking'\nimport { untrack } from '@glimmer/validator'\n\nexport function subscribeNoEffect(): Subscription {\n return null as unknown as Subscription\n}\n\n/**\n * Ember-native signal implementation.\n * In future ember >7.3 `tracked` will be available by itself\n * so the class wrapper will not be needed for those versions\n */\nexport class Signal<T> {\n @tracked _value\n\n #options: AtomOptions<T> | undefined\n\n // Ember reads are tag-tracked; this observer list exists for the plain-JS\n // subscribers core wires up (external-atom sync in constructTable, the\n // inspector), which have no access to framework tracking.\n #listeners = new Set<(value: T) => void>()\n\n constructor(value: T, options: AtomOptions<T> | undefined) {\n this._value = value\n this.#options = options\n }\n\n subscribe(\n listenerOrObserver: Observer<T> | ((value: T) => void),\n ): Subscription {\n const listener =\n typeof listenerOrObserver === 'function'\n ? listenerOrObserver\n : listenerOrObserver.next\n\n if (!listener) {\n return { unsubscribe: () => {} }\n }\n\n this.#listeners.add(listener)\n return { unsubscribe: () => this.#listeners.delete(listener) }\n }\n\n get() {\n return this.value\n }\n set(value: T | ((prev: T) => T)) {\n if (typeof value === 'function') {\n return this.update(value as unknown as (prev: T) => T)\n }\n\n this.value = value\n }\n\n get value() {\n return this._value\n }\n\n set value(next: T) {\n const prev = untrack(() => this._value)\n\n const isEqual = this.#options?.compare\n ? this.#options.compare(prev, next)\n : prev === next\n if (isEqual) {\n return\n }\n\n this._value = next\n\n for (const listener of this.#listeners) {\n listener(next)\n }\n }\n\n update(fn: (value: T) => T) {\n const original = untrack(() => this._value)\n\n this.value = fn(original)\n }\n}\n\nexport class ComputedSignal<T> {\n #compute\n\n constructor(compute: () => T) {\n this.#compute = compute\n }\n\n get() {\n return this.value\n }\n\n subscribe = subscribeNoEffect\n\n @cached\n get value() {\n return this.#compute()\n }\n}\n\nexport function signal<T>(value: T, options: AtomOptions<T> | undefined) {\n return new Signal(value, options)\n}\n\n/**\n * Creates an Ember-native writable atom, satisfying the `@tanstack/store`\n * `Atom` contract so it can be passed to `options.atoms`. Because it is backed\n * by a `@tracked` Signal, reading `atom.get()` directly in a template or\n * getter is reactive — unlike a foreign `@tanstack/store` atom, whose reads\n * create no Glimmer tag dependency.\n *\n * Takes a plain initial value only; there is no derived/function overload.\n */\nexport function createAtom<T>(\n initialValue: T,\n options?: AtomOptions<T>,\n): Atom<T> {\n return signal(initialValue, options)\n}\n\nexport function computed<T>(fn: () => T) {\n return new ComputedSignal(fn)\n}\n","import type { Subscription } from '@tanstack/store'\nimport type {\n TableAtomOptions,\n TableReactivityBindings,\n} from '@tanstack/table-core/reactivity'\n\nimport { untrack } from '@glimmer/validator'\nimport { computed, signal } from './signal.ts'\n\nexport function emberReactivity(): TableReactivityBindings {\n const subscriptions = new Set<Subscription>()\n\n return {\n createOptionsStore: true,\n wrapExternalAtoms: true,\n\n // timing is not important, but the main thing is that the work does *not*\n // happen during the render phase.\n schedule: (fn) => queueMicrotask(() => fn()),\n batch: (fn) => fn(),\n untrack,\n // @cached\n createReadonlyAtom: <T>(fn: () => T) => {\n return computed(fn)\n },\n // @tracked\n createWritableAtom: <T>(value: T, options?: TableAtomOptions<T>) => {\n return signal(value, options)\n },\n // Not for the ember integration, but for the tanstack inspector\n addSubscription: (subscription) => {\n subscriptions.add(subscription)\n },\n unmount: () => {\n subscriptions.forEach((s) => s.unsubscribe())\n subscriptions.clear()\n },\n }\n}\n","import { constructTable } from '@tanstack/table-core'\nimport { untrack } from '@glimmer/validator'\nimport { emberReactivity } from './reactivity.ts'\nimport { computed, subscribeNoEffect } from './signal.ts'\nimport type {\n RowData,\n Table,\n TableFeatures,\n TableOptions,\n TableState,\n} from '@tanstack/table-core'\nimport type { Atom, ReadonlyAtom, ReadonlyStore } from '@tanstack/store'\n\n// Internal table slots used by the pull-based options/state wiring below.\n// `Table_Internal` is not exported from the table-core build, so the shape is\n// declared structurally here.\ninterface TableInternals<\n TFeatures extends TableFeatures,\n TData extends RowData,\n> {\n optionsStore?: {\n get(): TableOptions<TFeatures, TData>\n set(value: () => TableOptions<TFeatures, TData>): void\n }\n baseAtoms: Record<string, { get(): unknown }>\n atoms: Record<string, unknown>\n}\n\nexport function useTable<\n TFeatures extends TableFeatures,\n TData extends RowData,\n>(getOptions: () => TableOptions<TFeatures, TData>): Table<TFeatures, TData> {\n const reactivity = emberReactivity()\n\n // Creates reactive read only signal for options\n const userOptions = computed(getOptions)\n\n // Untracked to prevent possible \"set on same computation as read\" errors in Ember.\n const initialOptions = untrack(() => userOptions.get())\n\n const table = constructTable<TFeatures, TData>({\n ...initialOptions,\n features: {\n coreReactivityFeature: reactivity,\n ...initialOptions.features,\n },\n mergeOptions: (\n defaultOptions: TableOptions<TFeatures, TData>,\n newOptions: Partial<TableOptions<TFeatures, TData>>,\n ) => ({\n ...defaultOptions,\n ...newOptions,\n }),\n }) as Table<TFeatures, TData> & TableInternals<TFeatures, TData>\n\n const optionsStore = table.optionsStore!\n\n const liveOptions = computed(() => {\n const stored = optionsStore.get()\n return {\n ...stored,\n ...userOptions.get(),\n // stored options carry construct-time normalization (the reactivity\n // feature, wrapped external atoms) that must win over the raw user\n // options.\n features: stored.features,\n atoms: stored.atoms,\n }\n })\n\n /**\n * This is to get around core table not using lazy access so we need to re-wrap\n *\n * Similar to other reactive signal frameworks (solid, angular, svelte)\n */\n Object.defineProperty(table, 'options', {\n configurable: true,\n enumerable: true,\n get: () => liveOptions.get(),\n set: (value: TableOptions<TFeatures, TData>) => {\n optionsStore.set(() => value)\n },\n })\n\n const atoms: Record<string, ReadonlyAtom<unknown>> = table.atoms\n const stateKeys = Object.keys(table.baseAtoms)\n\n for (const key of stateKeys) {\n const baseAtom = (table.baseAtoms as Record<string, ReadonlyAtom<unknown>>)[\n key\n ]!\n\n /**\n * Original atoms could cause effects for top level properties\n *\n * Core table should migrate to pure derived data which would boost render\n * performance for both signal and non-signal frameworks.\n */\n atoms[key] = reactivity.createReadonlyAtom(\n () => {\n const externalAtom = (\n table.options.atoms as Record<string, Atom<unknown>> | undefined\n )?.[key]\n if (externalAtom) {\n return externalAtom.get()\n }\n const stateSlice = (\n table.options.state as Record<string, unknown> | undefined\n )?.[key]\n if (stateSlice !== undefined) {\n return stateSlice\n }\n return baseAtom.get()\n },\n { debugName: `table/atoms/${key}` },\n )\n }\n\n const stateProxy: TableState<TFeatures> = new Proxy(\n {},\n {\n get: (_target, key) =>\n typeof key === 'string' ? atoms[key]?.get() : undefined,\n has: (_target, key) => typeof key === 'string' && stateKeys.includes(key),\n ownKeys: () => stateKeys,\n getOwnPropertyDescriptor: (_target, key) =>\n typeof key === 'string' && stateKeys.includes(key)\n ? {\n enumerable: true,\n configurable: true,\n value: atoms[key]!.get(),\n }\n : undefined,\n },\n ) as TableState<TFeatures>\n\n /**\n * Store is reasigned to point to proxy object to allow individual state slices to be independently reactive.\n *\n * Type cast is needed because we are setting during construction\n * Table store is readonly after first initialization.\n */\n ;(\n table as { store: Omit<ReadonlyStore<TableState<TFeatures>>, 'atom'> }\n ).store = {\n get: () => stateProxy,\n get state() {\n return stateProxy\n },\n subscribe: subscribeNoEffect,\n }\n\n return table\n}\n","import { createColumnHelper as coreCreateColumnHelper } from '@tanstack/table-core'\nimport { useTable } from './use-table.ts'\nimport type {\n RowData,\n Table,\n TableFeatures,\n TableOptions,\n} from '@tanstack/table-core'\n\nexport type CreateTableHookOptions<TFeatures extends TableFeatures> = Omit<\n // `any` (not `RowData`) is intentional and matches the other adapters'\n // createTableHook: shared defaults are declared before any specific TData is\n // known, and TData-dependent options like `getRowId` and `meta` are\n // contravariant in TData, so a narrower type would reject them here.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n TableOptions<TFeatures, any>,\n 'columns' | 'data' | 'state'\n>\n\nexport type AppEmberTable<\n TFeatures extends TableFeatures,\n TData extends RowData,\n> = Table<TFeatures, TData>\n\nexport type AppColumnHelper<\n TFeatures extends TableFeatures,\n TData extends RowData,\n> = ReturnType<typeof coreCreateColumnHelper<TFeatures, TData>>\n\n/**\n * Bundles a feature set and shared default options once so every table in your\n * app can be created without repeating them. Returns a typed column helper\n * factory and a `createAppTable` that wraps {@link useTable}.\n *\n * Unlike the React adapter's hook, this does not pre-bind cell/header\n * components onto the table; render with the `FlexRenderCell`,\n * `FlexRenderHeader`, and `FlexRenderFooter` components as usual.\n *\n * @example\n * ```ts\n * const { createAppTable, createAppColumnHelper } = createTableHook({\n * features: tableFeatures({ rowSortingFeature, sortedRowModel: createSortedRowModel(), sortFns }),\n * })\n *\n * const columnHelper = createAppColumnHelper<Person>()\n * const columns = columnHelper.columns([...])\n *\n * // inside a Glimmer component; options stay a thunk so tracked reads are reactive\n * table = createAppTable(() => ({ columns, data: this.data }))\n * ```\n */\nexport function createTableHook<TFeatures extends TableFeatures>({\n ...defaultTableOptions\n}: CreateTableHookOptions<TFeatures>) {\n function createAppColumnHelper<TData extends RowData>(): AppColumnHelper<\n TFeatures,\n TData\n > {\n return coreCreateColumnHelper<TFeatures, TData>()\n }\n\n function createAppTable<TData extends RowData>(\n getTableOptions: () => Omit<TableOptions<TFeatures, TData>, 'features'>,\n ): AppEmberTable<TFeatures, TData> {\n // Keep options a thunk: the merge runs inside `useTable`'s options thunk,\n // so tracked properties read in `getTableOptions` stay reactive. Per-table\n // options take precedence over the shared defaults (except `features`,\n // which only the hook provides).\n return useTable<TFeatures, TData>(\n () =>\n ({\n ...defaultTableOptions,\n ...getTableOptions(),\n }) as TableOptions<TFeatures, TData>,\n )\n }\n\n return {\n appFeatures: defaultTableOptions.features as TFeatures,\n createAppColumnHelper,\n createAppTable,\n }\n}\n"],"names":["subscribeNoEffect","Signal","g","prototype","tracked","i","Set","constructor","value","options","_value","subscribe","listenerOrObserver","listener","next","unsubscribe","add","delete","get","set","update","prev","untrack","isEqual","compare","fn","original","ComputedSignal","compute","n","cached","signal","createAtom","initialValue","computed","emberReactivity","subscriptions","createOptionsStore","wrapExternalAtoms","schedule","queueMicrotask","batch","createReadonlyAtom","createWritableAtom","addSubscription","subscription","unmount","forEach","s","clear","useTable","getOptions","reactivity","userOptions","initialOptions","table","constructTable","features","coreReactivityFeature","mergeOptions","defaultOptions","newOptions","optionsStore","liveOptions","stored","atoms","Object","defineProperty","configurable","enumerable","stateKeys","keys","baseAtoms","key","baseAtom","externalAtom","stateSlice","state","undefined","debugName","stateProxy","Proxy","_target","has","includes","ownKeys","getOwnPropertyDescriptor","store","createTableHook","defaultTableOptions","createAppColumnHelper","coreCreateColumnHelper","createAppTable","getTableOptions","appFeatures"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export * from '@tanstack/table-core/static-functions';
2
+ //# sourceMappingURL=static-functions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"static-functions.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,147 @@
1
+ {
2
+ "name": "@tanstack/ember-table",
3
+ "version": "9.0.0-beta.0",
4
+ "description": "Headless UI for building powerful tables & datagrids for Ember.",
5
+ "author": "Tanner Linsley",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/TanStack/table.git",
10
+ "directory": "packages/ember-table"
11
+ },
12
+ "homepage": "https://tanstack.com/table",
13
+ "funding": {
14
+ "type": "github",
15
+ "url": "https://github.com/sponsors/tannerlinsley"
16
+ },
17
+ "keywords": [
18
+ "ember",
19
+ "ember-addon",
20
+ "table",
21
+ "ember-table",
22
+ "datagrid",
23
+ "tanstack-intent"
24
+ ],
25
+ "type": "module",
26
+ "imports": {
27
+ "#src/*": "./src/*"
28
+ },
29
+ "exports": {
30
+ ".": {
31
+ "types": "./declarations/index.d.ts",
32
+ "default": "./dist/index.js"
33
+ },
34
+ "./static-functions": {
35
+ "types": "./declarations/static-functions.d.ts",
36
+ "default": "./dist/static-functions.js"
37
+ },
38
+ "./flex-render": {
39
+ "types": "./declarations/flex-render.d.ts",
40
+ "default": "./dist/flex-render.js"
41
+ },
42
+ "./experimental-worker-plugin": {
43
+ "types": "./declarations/experimental-worker-plugin.d.ts",
44
+ "default": "./dist/experimental-worker-plugin.js"
45
+ },
46
+ "./addon-main.js": "./addon-main.cjs",
47
+ "./package.json": "./package.json"
48
+ },
49
+ "engines": {
50
+ "node": ">=20"
51
+ },
52
+ "files": [
53
+ "addon-main.cjs",
54
+ "declarations",
55
+ "dist",
56
+ "src",
57
+ "skills"
58
+ ],
59
+ "scripts": {
60
+ "build": "rollup --config",
61
+ "clean": "rimraf ./dist ./declarations ./dist-tests",
62
+ "format": "prettier . --cache --write",
63
+ "lint": "concurrently \"pnpm:lint:*(!fix)\" --names \"lint:\" --prefixColors auto",
64
+ "lint:fix": "concurrently \"pnpm:lint:*:fix\" --names \"fix:\" --prefixColors auto && pnpm run format",
65
+ "lint:format": "prettier . --cache --check",
66
+ "lint:hbs": "ember-template-lint . --no-error-on-unmatched-pattern",
67
+ "lint:hbs:fix": "ember-template-lint . --fix --no-error-on-unmatched-pattern",
68
+ "lint:js": "eslint . --cache",
69
+ "lint:js:fix": "eslint . --fix",
70
+ "lint:types": "ember-tsc --noEmit",
71
+ "prepack": "rollup --config",
72
+ "start": "vite dev",
73
+ "test:build": "publint --strict",
74
+ "test:eslint": "eslint . --cache",
75
+ "test:lib": "vite build --mode=development --out-dir dist-tests && testem --file testem.cjs ci --port 0",
76
+ "test:types": "ember-tsc --noEmit"
77
+ },
78
+ "dependencies": {
79
+ "@embroider/addon-shim": "^1.8.9",
80
+ "@tanstack/store": "^0.11.0",
81
+ "@tanstack/table-core": "workspace:*",
82
+ "decorator-transforms": "2.4.0"
83
+ },
84
+ "devDependencies": {
85
+ "@babel/core": "7.29.7",
86
+ "@babel/eslint-parser": "^7.25.1",
87
+ "@babel/plugin-transform-typescript": "7.29.7",
88
+ "@babel/runtime": "7.29.7",
89
+ "@ember/app-tsconfig": "2.0.0",
90
+ "@ember/library-tsconfig": "^1.0.0",
91
+ "@ember/test-helpers": "^5.2.1",
92
+ "@ember/test-waiters": "^4.1.1",
93
+ "@embroider/addon-dev": "^8.1.0",
94
+ "@embroider/compat": "^4.1.0",
95
+ "@embroider/core": "4.6.2",
96
+ "@embroider/macros": "1.20.5",
97
+ "@embroider/vite": "^1.1.5",
98
+ "@eslint/js": "^9.17.0",
99
+ "@glimmer/component": "2.1.1",
100
+ "@glint/ember-tsc": "1.8.11",
101
+ "@glint/template": "1.7.8",
102
+ "@glint/tsserver-plugin": "2.5.17",
103
+ "@playwright/test": "^1.61.1",
104
+ "@rollup/plugin-babel": "7.1.0",
105
+ "@types/qunit": "^2.19.12",
106
+ "babel-plugin-ember-template-compilation": "4.0.0",
107
+ "concurrently": "^9.0.1",
108
+ "ember-page-title": "^9.0.3",
109
+ "ember-qunit": "^9.0.2",
110
+ "ember-source": "7.0.0",
111
+ "ember-strict-application-resolver": "0.1.1",
112
+ "ember-template-lint": "^7.9.0",
113
+ "eslint": "^9.17.0",
114
+ "eslint-config-prettier": "^10.1.5",
115
+ "eslint-plugin-ember": "^12.3.3",
116
+ "eslint-plugin-import": "^2.31.0",
117
+ "eslint-plugin-n": "^17.15.1",
118
+ "globals": "^16.1.0",
119
+ "prettier": "^3.8.4",
120
+ "publint": "^0.3.21",
121
+ "qunit": "^2.24.1",
122
+ "qunit-dom": "^3.4.0",
123
+ "rollup": "^4.22.5",
124
+ "testem": "^3.15.1",
125
+ "typescript": "6.0.3",
126
+ "typescript-eslint": "^8.19.1",
127
+ "vite": "^8.1.0"
128
+ },
129
+ "ember": {
130
+ "edition": "octane"
131
+ },
132
+ "ember-addon": {
133
+ "version": 2,
134
+ "type": "addon",
135
+ "main": "addon-main.cjs"
136
+ },
137
+ "nx": {
138
+ "targets": {
139
+ "build": {
140
+ "outputs": [
141
+ "{projectRoot}/dist",
142
+ "{projectRoot}/declarations"
143
+ ]
144
+ }
145
+ }
146
+ }
147
+ }
@@ -0,0 +1,117 @@
1
+ ---
2
+ name: create-table-hook
3
+ description: >
4
+ Share TanStack Ember Table v9 features, row-model slots, defaults, and inferred column helpers with createTableHook, createAppTable, createAppColumnHelper, and appFeatures. Load for recurring Ember table conventions, per-table overrides, or confusion with component/context registries from other adapters.
5
+ metadata:
6
+ type: framework
7
+ library: '@tanstack/ember-table'
8
+ framework: ember
9
+ library_version: '9.0.0-beta.38'
10
+ requires:
11
+ - '@tanstack/table-core#core'
12
+ - getting-started
13
+ - table-state
14
+ sources:
15
+ - 'TanStack/table:docs/framework/ember/guide/composable-tables.md'
16
+ - 'TanStack/table:examples/ember/basic-app-table'
17
+ - 'TanStack/table:packages/ember-table/src/create-table-hook.ts'
18
+ ---
19
+
20
+ This skill builds on `@tanstack/table-core#core`, `getting-started`, and `table-state`. Use an app factory when several tables share real features or defaults; keep a one-off table on `useTable`.
21
+
22
+ ## Setup
23
+
24
+ ```gts
25
+ import Component from '@glimmer/component'
26
+ import { tracked } from '@glimmer/tracking'
27
+ import {
28
+ createSortedRowModel,
29
+ createTableHook,
30
+ rowSortingFeature,
31
+ sortFns,
32
+ tableFeatures,
33
+ } from '@tanstack/ember-table'
34
+
35
+ const features = tableFeatures({
36
+ rowSortingFeature,
37
+ sortedRowModel: createSortedRowModel(),
38
+ sortFns,
39
+ })
40
+
41
+ export const { appFeatures, createAppColumnHelper, createAppTable } =
42
+ createTableHook({
43
+ features,
44
+ enableSortingRemoval: false,
45
+ })
46
+
47
+ type Person = { id: string; name: string }
48
+ const columnHelper = createAppColumnHelper<Person>()
49
+ const columns = columnHelper.columns([
50
+ columnHelper.accessor('name', { header: 'Name' }),
51
+ ])
52
+
53
+ export default class PeopleTable extends Component {
54
+ @tracked data: Person[] = []
55
+
56
+ table = createAppTable(() => ({
57
+ columns,
58
+ data: this.data,
59
+ }))
60
+ }
61
+ ```
62
+
63
+ Create the hook and column helpers at stable module scope. `createAppTable` preserves the `useTable` options thunk, so tracked reads such as `this.data` remain reactive. Do not pass `features` again at the table call site; the factory owns the feature set and binds it into column inference.
64
+
65
+ ## Composition Contract
66
+
67
+ - Hook options supply stable shared `features` and default table options.
68
+ - `createAppColumnHelper<TData>()` binds the feature type while inferring each row type.
69
+ - `createAppTable(() => options)` merges shared defaults with table-specific options.
70
+ - Table-specific options win over shared defaults, except that the factory remains the feature owner.
71
+ - `appFeatures` exposes the exact registered feature set for helper types or reusable utilities.
72
+
73
+ Shared options intentionally exclude `columns`, `data`, and `state`. Keep model inputs and controlled state at each table call site; share conventions, not one mutable state object across unrelated tables.
74
+
75
+ Sharing `rowSortingFeature`, `sortedRowModel`, and sorting defaults gives every table sorting capability; it does not make their internal sorting state shared. The factory technically accepts a stable external atom in shared `atoms`, but that deliberately couples tables and their column IDs. Prefer independent state unless synchronized tables are the actual product behavior.
76
+
77
+ ## Render Normally
78
+
79
+ Ember's factory does not register `AppTable`, `AppCell`, `AppHeader`, context hooks, or reusable component registries. Continue rendering with `FlexRenderCell`, `FlexRenderHeader`, and `FlexRenderFooter`. Continue wrapping receiver-dependent v9 methods in Ember template helpers as described by `getting-started`.
80
+
81
+ ## Common Mistakes
82
+
83
+ ### HIGH Copying another adapter's component registry
84
+
85
+ Wrong: expect `table.AppTable`, `useTableContext`, or a `tableComponents` option from React, Solid, Svelte, or Lit examples.
86
+
87
+ Correct: use the normal Ember FlexRender components and pass the table through ordinary Ember composition when another component truly needs it.
88
+
89
+ The Ember hook shares features/defaults and type inference only.
90
+
91
+ ### HIGH Passing features per table
92
+
93
+ Wrong:
94
+
95
+ ```gts
96
+ createAppTable(() => ({ features: otherFeatures, columns, data: this.data }))
97
+ ```
98
+
99
+ Correct: define a separate app hook when a table needs a genuinely different feature set. `createAppTable` omits `features` from its call-site type.
100
+
101
+ ### HIGH Recreating the hook or columns with tracked updates
102
+
103
+ Wrong: call `createTableHook`, `createAppColumnHelper`, or `columns(...)` inside a component getter or the `createAppTable` thunk.
104
+
105
+ Correct: create them once at module scope and let the table thunk read only changing inputs.
106
+
107
+ ### MEDIUM Sharing controlled state as a default
108
+
109
+ The hook options omit `state`. Put `state` and matching `on[State]Change` callbacks in each `createAppTable` thunk so every table has one clear owner per slice. Use a shared external atom only when tables intentionally coordinate the same compatible slice.
110
+
111
+ ### MEDIUM Repeating manual feature generics
112
+
113
+ Use `createAppColumnHelper<Person>()`; do not thread `typeof features` through every column helper after the factory has already captured it.
114
+
115
+ ## API Discovery
116
+
117
+ Inspect `node_modules/@tanstack/ember-table/src/create-table-hook.ts` for the installed factory return shape, omitted options, and merge precedence. Inspect `use-table.ts` for thunk reactivity and `node_modules/@tanstack/table-core/src/features/<feature>/` for the shared feature APIs. Do not infer Ember component/context behavior from another adapter's createTableHook.