@universal-ember/table 3.0.0 → 3.0.1

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 (168) hide show
  1. package/dist/-private/-type-tests/plugin-properties.test.js +27 -0
  2. package/dist/-private/-type-tests/plugin-properties.test.js.map +1 -0
  3. package/dist/-private/-type-tests/plugin-with.test.js +20 -0
  4. package/dist/-private/-type-tests/plugin-with.test.js.map +1 -0
  5. package/dist/-private/-type-tests/plugins-accessors.test.js +36 -0
  6. package/dist/-private/-type-tests/plugins-accessors.test.js.map +1 -0
  7. package/dist/-private/-type-tests/plugins-signature-from.test.js +15 -0
  8. package/dist/-private/-type-tests/plugins-signature-from.test.js.map +1 -0
  9. package/dist/-private/-type-tests/plugins-signature-utils.test.js +36 -0
  10. package/dist/-private/-type-tests/plugins-signature-utils.test.js.map +1 -0
  11. package/dist/-private/-type-tests/table-api.test.js +17 -0
  12. package/dist/-private/-type-tests/table-api.test.js.map +1 -0
  13. package/dist/-private/-type-tests/table-config.test.js +55 -0
  14. package/dist/-private/-type-tests/table-config.test.js.map +1 -0
  15. package/dist/-private/column.js +62 -0
  16. package/dist/-private/column.js.map +1 -0
  17. package/dist/-private/ember-compat.js +17 -0
  18. package/dist/-private/ember-compat.js.map +1 -0
  19. package/dist/-private/interfaces/column.js +2 -0
  20. package/dist/-private/interfaces/column.js.map +1 -0
  21. package/dist/-private/interfaces/index.js +2 -0
  22. package/dist/-private/interfaces/index.js.map +1 -0
  23. package/dist/-private/interfaces/modifier.js +2 -0
  24. package/dist/-private/interfaces/modifier.js.map +1 -0
  25. package/dist/-private/interfaces/pagination.js +2 -0
  26. package/dist/-private/interfaces/pagination.js.map +1 -0
  27. package/dist/-private/interfaces/plugins.js +2 -0
  28. package/dist/-private/interfaces/plugins.js.map +1 -0
  29. package/dist/-private/interfaces/preferences.js +2 -0
  30. package/dist/-private/interfaces/preferences.js.map +1 -0
  31. package/dist/-private/interfaces/selection.js +2 -0
  32. package/dist/-private/interfaces/selection.js.map +1 -0
  33. package/dist/-private/interfaces/table.js +2 -0
  34. package/dist/-private/interfaces/table.js.map +1 -0
  35. package/dist/-private/js-helper.js +55 -0
  36. package/dist/-private/js-helper.js.map +1 -0
  37. package/dist/-private/preferences.js +143 -0
  38. package/dist/-private/preferences.js.map +1 -0
  39. package/dist/-private/private-types.js +2 -0
  40. package/dist/-private/private-types.js.map +1 -0
  41. package/dist/-private/row.js +51 -0
  42. package/dist/-private/row.js.map +1 -0
  43. package/dist/-private/table.js +273 -0
  44. package/dist/-private/table.js.map +1 -0
  45. package/dist/-private/utils.js +15 -0
  46. package/dist/-private/utils.js.map +1 -0
  47. package/dist/_rollupPluginBabelHelpers-BpiaYhlf.js +63 -0
  48. package/dist/_rollupPluginBabelHelpers-BpiaYhlf.js.map +1 -0
  49. package/dist/index.js +4 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/plugins/-private/base.js +524 -0
  52. package/dist/plugins/-private/base.js.map +1 -0
  53. package/dist/plugins/-private/utils.js +103 -0
  54. package/dist/plugins/-private/utils.js.map +1 -0
  55. package/dist/plugins/column-reordering/helpers.js +44 -0
  56. package/dist/plugins/column-reordering/helpers.js.map +1 -0
  57. package/dist/plugins/column-reordering/index.js +3 -0
  58. package/dist/plugins/column-reordering/index.js.map +1 -0
  59. package/dist/plugins/column-reordering/plugin.js +359 -0
  60. package/dist/plugins/column-reordering/plugin.js.map +1 -0
  61. package/dist/plugins/column-reordering/utils.js +34 -0
  62. package/dist/plugins/column-reordering/utils.js.map +1 -0
  63. package/dist/plugins/column-resizing/handle.js +241 -0
  64. package/dist/plugins/column-resizing/handle.js.map +1 -0
  65. package/dist/plugins/column-resizing/helpers.js +71 -0
  66. package/dist/plugins/column-resizing/helpers.js.map +1 -0
  67. package/dist/plugins/column-resizing/index.js +4 -0
  68. package/dist/plugins/column-resizing/index.js.map +1 -0
  69. package/dist/plugins/column-resizing/plugin.js +328 -0
  70. package/dist/plugins/column-resizing/plugin.js.map +1 -0
  71. package/dist/plugins/column-resizing/resize-observer.js +44 -0
  72. package/dist/plugins/column-resizing/resize-observer.js.map +1 -0
  73. package/dist/plugins/column-resizing/utils.js +44 -0
  74. package/dist/plugins/column-resizing/utils.js.map +1 -0
  75. package/dist/plugins/column-visibility/helpers.js +25 -0
  76. package/dist/plugins/column-visibility/helpers.js.map +1 -0
  77. package/dist/plugins/column-visibility/index.js +3 -0
  78. package/dist/plugins/column-visibility/index.js.map +1 -0
  79. package/dist/plugins/column-visibility/plugin.js +92 -0
  80. package/dist/plugins/column-visibility/plugin.js.map +1 -0
  81. package/dist/plugins/data-sorting/helpers.js +49 -0
  82. package/dist/plugins/data-sorting/helpers.js.map +1 -0
  83. package/dist/plugins/data-sorting/index.js +4 -0
  84. package/dist/plugins/data-sorting/index.js.map +1 -0
  85. package/dist/plugins/data-sorting/plugin.js +132 -0
  86. package/dist/plugins/data-sorting/plugin.js.map +1 -0
  87. package/dist/plugins/data-sorting/types.js +14 -0
  88. package/dist/plugins/data-sorting/types.js.map +1 -0
  89. package/dist/plugins/index.js +3 -0
  90. package/dist/plugins/index.js.map +1 -0
  91. package/dist/plugins/metadata/helpers.js +12 -0
  92. package/dist/plugins/metadata/helpers.js.map +1 -0
  93. package/dist/plugins/metadata/index.js +3 -0
  94. package/dist/plugins/metadata/index.js.map +1 -0
  95. package/dist/plugins/metadata/plugin.js +25 -0
  96. package/dist/plugins/metadata/plugin.js.map +1 -0
  97. package/dist/plugins/row-selection/helpers.js +10 -0
  98. package/dist/plugins/row-selection/helpers.js.map +1 -0
  99. package/dist/plugins/row-selection/index.js +3 -0
  100. package/dist/plugins/row-selection/index.js.map +1 -0
  101. package/dist/plugins/row-selection/plugin.js +118 -0
  102. package/dist/plugins/row-selection/plugin.js.map +1 -0
  103. package/dist/plugins/sticky-columns/helpers.js +49 -0
  104. package/dist/plugins/sticky-columns/helpers.js.map +1 -0
  105. package/dist/plugins/sticky-columns/index.js +3 -0
  106. package/dist/plugins/sticky-columns/index.js.map +1 -0
  107. package/dist/plugins/sticky-columns/plugin.js +139 -0
  108. package/dist/plugins/sticky-columns/plugin.js.map +1 -0
  109. package/dist/test-support/index.js +62 -0
  110. package/dist/test-support/index.js.map +1 -0
  111. package/dist/utils.js +77 -0
  112. package/dist/utils.js.map +1 -0
  113. package/package.json +3 -2
  114. package/src/-private/-type-tests/plugin-properties.test.ts +38 -0
  115. package/src/-private/-type-tests/plugin-with.test.ts +23 -0
  116. package/src/-private/-type-tests/plugins-accessors.test.ts +86 -0
  117. package/src/-private/-type-tests/plugins-signature-from.test.ts +66 -0
  118. package/src/-private/-type-tests/plugins-signature-utils.test.ts +154 -0
  119. package/src/-private/-type-tests/table-api.test.ts +20 -0
  120. package/src/-private/-type-tests/table-config.test.ts +70 -0
  121. package/src/-private/column.ts +67 -0
  122. package/src/-private/ember-compat.ts +26 -0
  123. package/src/-private/interfaces/column.ts +73 -0
  124. package/src/-private/interfaces/index.ts +7 -0
  125. package/src/-private/interfaces/modifier.ts +7 -0
  126. package/src/-private/interfaces/pagination.ts +13 -0
  127. package/src/-private/interfaces/plugins.ts +349 -0
  128. package/src/-private/interfaces/preferences.ts +82 -0
  129. package/src/-private/interfaces/selection.ts +38 -0
  130. package/src/-private/interfaces/table.ts +121 -0
  131. package/src/-private/js-helper.ts +65 -0
  132. package/src/-private/preferences.ts +176 -0
  133. package/src/-private/private-types.ts +8 -0
  134. package/src/-private/row.ts +66 -0
  135. package/src/-private/table.ts +310 -0
  136. package/src/-private/utils.ts +21 -0
  137. package/src/index.ts +25 -0
  138. package/src/plugins/-private/base.ts +836 -0
  139. package/src/plugins/-private/utils.ts +166 -0
  140. package/src/plugins/column-reordering/helpers.ts +50 -0
  141. package/src/plugins/column-reordering/index.ts +6 -0
  142. package/src/plugins/column-reordering/plugin.ts +489 -0
  143. package/src/plugins/column-reordering/utils.ts +48 -0
  144. package/src/plugins/column-resizing/handle.ts +280 -0
  145. package/src/plugins/column-resizing/helpers.ts +79 -0
  146. package/src/plugins/column-resizing/index.ts +7 -0
  147. package/src/plugins/column-resizing/plugin.ts +490 -0
  148. package/src/plugins/column-resizing/resize-observer.ts +48 -0
  149. package/src/plugins/column-resizing/utils.ts +54 -0
  150. package/src/plugins/column-visibility/helpers.ts +28 -0
  151. package/src/plugins/column-visibility/index.ts +6 -0
  152. package/src/plugins/column-visibility/plugin.ts +155 -0
  153. package/src/plugins/data-sorting/helpers.ts +56 -0
  154. package/src/plugins/data-sorting/index.ts +8 -0
  155. package/src/plugins/data-sorting/plugin.ts +222 -0
  156. package/src/plugins/data-sorting/types.ts +26 -0
  157. package/src/plugins/index.ts +20 -0
  158. package/src/plugins/metadata/helpers.ts +12 -0
  159. package/src/plugins/metadata/index.ts +7 -0
  160. package/src/plugins/metadata/plugin.ts +26 -0
  161. package/src/plugins/row-selection/helpers.ts +13 -0
  162. package/src/plugins/row-selection/index.ts +7 -0
  163. package/src/plugins/row-selection/plugin.ts +218 -0
  164. package/src/plugins/sticky-columns/helpers.ts +59 -0
  165. package/src/plugins/sticky-columns/index.ts +7 -0
  166. package/src/plugins/sticky-columns/plugin.ts +201 -0
  167. package/src/test-support/index.ts +76 -0
  168. package/src/utils.ts +85 -0
@@ -0,0 +1,349 @@
1
+ /**
2
+ * NOTE:
3
+ * Empty, EmptyObject, and GetOrElse are copied from @glimmer/component
4
+ */
5
+
6
+ import type { Constructor } from '../../-private/private-types.ts';
7
+ import type { Column, Row, Table } from '../../index.ts';
8
+ import type { Destructor } from '../../-private/interfaces';
9
+
10
+ type DataTypeOf<T> = T extends Table<infer DataType> ? DataType : T;
11
+
12
+ /**
13
+ * @private utility class
14
+ *
15
+ * This class exists because there isn't a way to, in TS,
16
+ * get access to static properties from an instance type
17
+ */
18
+ export type PluginClass<PluginType> = PluginType & {
19
+ new: (...args: unknown[]) => PluginType;
20
+ features?: string[];
21
+ requires?: string[];
22
+ };
23
+
24
+ export type PluginSubclassInstance<PluginType> = PluginType & {
25
+ constructor: PluginClass<PluginType>;
26
+ };
27
+
28
+ /**
29
+ * @public
30
+ *
31
+ * The data passed to a plugin's column APIs
32
+ */
33
+ export interface ColumnApi<T extends Table = Table> {
34
+ column: Column<DataTypeOf<T>>;
35
+ table: T;
36
+ }
37
+
38
+ /**
39
+ * @public
40
+ *
41
+ * The data passed to a plugin's row APIs
42
+ */
43
+ export interface RowApi<T extends Table = Table> {
44
+ row: Row<DataTypeOf<T>>;
45
+ table: T;
46
+ }
47
+
48
+ /**
49
+ * @private utility type
50
+ *
51
+ * Note that this exists here, and the Plugin interface exists in general
52
+ * because we need to derive types in a static context on BasePlugin,
53
+ * and the source of types need to exist somewhere other than BasePlugin,
54
+ * so that:
55
+ * - inference will work
56
+ * - we avoid infinite recursive type definitions
57
+ */
58
+ export type SignatureFrom<Klass extends Plugin<any>> =
59
+ Klass extends Plugin<infer Signature> ? Signature : never;
60
+
61
+ /**
62
+ * @public
63
+ *
64
+ * Table plugins are stateless objects that optionally provide hooks based on what
65
+ * the plugin wishes to modify.
66
+ *
67
+ * If state is desired, Metadata classes may be provided to manage that state.
68
+ * As a convenience, when the meta classes are instantiated, they'll be given the same
69
+ * `owner` as everything else in the application, so service injection will be available
70
+ * within the meta class instances.
71
+ *
72
+ * A plugin can provide components that the consuming Table can opt in to rendering.
73
+ * (though, often these components will be required to be rendered for the plugin to work)
74
+ *
75
+ * a `Plugin` has one type argument:
76
+ * - Signature - which can provide optional information about the Meta/State and Options the plugin can take
77
+ *
78
+ * Any particular plugin instantiation will have at most 1 instance of their TableMeta
79
+ * and `n` instances of their ColumnMeta, where `n` is at most the number of columns.
80
+ */
81
+ export interface Plugin<Signature = unknown> {
82
+ /**
83
+ * Unique name for the plugin.
84
+ * - only one plugin of the same name is allowed
85
+ * - the name is used for storing preferences / serializable information
86
+ */
87
+ name: string;
88
+
89
+ /**
90
+ * Some plugins may require that other plugins be present.
91
+ * and because plugins can be interchangeable, the features implemented
92
+ * by those plugins must be declared via strings so that we can have
93
+ * a semi-stable reference that isn't tied to object equality or anything like that.
94
+ *
95
+ * This enables, for example, the StickyColumns plugin to work with different implementations of the ColumnResizing plugin (such as one
96
+ * might have between an aria-grid and a data table)
97
+ */
98
+ features?: string[];
99
+
100
+ /**
101
+ * List of features to lookup "somewhere" in the list of plugins
102
+ * order does not matter.
103
+ */
104
+ requires?: string[];
105
+
106
+ /**
107
+ * Optional state that this plugin may or may not choose to use
108
+ *
109
+ * columns will each have an instance of meta.column.
110
+ * the table will have only one instance of meta.table.
111
+ */
112
+ meta?: {
113
+ /**
114
+ * @public
115
+ *
116
+ * Specifies the class definition to use for storing column-related state / behavior for this plugin
117
+ */
118
+ column?: Constructor<ColumnMetaFor<Signature>>;
119
+
120
+ /**
121
+ * @public
122
+ *
123
+ * Specifies the class definition to use for storing table-related state / behavior for this plugin
124
+ */
125
+ table?: Constructor<TableMetaFor<Signature>>;
126
+
127
+ /**
128
+ * @public
129
+ *
130
+ * Specifies the class definition to use for storing the row-related state / behavior for this plugin
131
+ */
132
+ row?: Constructor<RowMetaFor<Signature>>;
133
+ };
134
+
135
+ /**
136
+ * @public
137
+ * @kind Column property
138
+ *
139
+ * Specify a modifier setup/teardown function to attach to each of the header cells
140
+ *
141
+ * Can be used to add / remove attributes, event listeners, etc
142
+ */
143
+ headerCellModifier?: (
144
+ element: HTMLElement,
145
+ ...args: [ColumnApi<Table<any>>]
146
+ ) => void | Destructor;
147
+
148
+ /**
149
+ * @public
150
+ * @kind Row property
151
+ *
152
+ * Specify a modifier setup/teardown function to attach to each of the rows
153
+ *
154
+ * Can be used to add / remove attributes, event listeners, etc
155
+ */
156
+ rowModifier?: (
157
+ element: HTMLElement,
158
+ ...args: [RowApi<Table<any>>]
159
+ ) => void | Destructor;
160
+
161
+ /**
162
+ * @public
163
+ * @kind Table hook
164
+ *
165
+ * Specify a modifier setup/teardown function to attach to the table's containing element
166
+ */
167
+ containerModifier?: (
168
+ element: HTMLElement,
169
+ ...args: [Table<any>]
170
+ ) => void | Destructor;
171
+
172
+ /**
173
+ * @public
174
+ * @kind Table Hook
175
+ *
176
+ * If the plugin has state, this should be used to reset that state
177
+ */
178
+ reset?: () => void;
179
+
180
+ /**
181
+ * @public
182
+ * @kind Table Hook
183
+ *
184
+ * A plugin may change the columns order, visibility, etc.
185
+ * By implementing this getter, this plugin's
186
+ * `columns` property will be used by other plugins via
187
+ * the `columns.for(table, RequestingPlugin)` api.
188
+ *
189
+ * For the end-consumer, they may choose to do
190
+ * `columns.for(table)`, which will aggregate all column modifications
191
+ * from all plugins.
192
+ *
193
+ * As always, `table.columns` is the way to get the unmodified list of columns.
194
+ */
195
+ columns?: Column<any>[];
196
+ }
197
+
198
+ /**
199
+ * @private utility type
200
+ */
201
+ type GetOrElse<Obj, K, Fallback> = K extends keyof Obj ? Obj[K] : Fallback;
202
+
203
+ /**
204
+ * @public
205
+ *
206
+ * utility class to help with autocompletion / documentation
207
+ * in the editor while while defining the signature of custom plugins.
208
+ */
209
+ export interface PluginSignature {
210
+ /**
211
+ * Meta is how plugins can manage per-{table,columns,rows}
212
+ * state, event listeners, and general public API
213
+ */
214
+ Meta?: {
215
+ /**
216
+ * If a plugin has Table meta/state,
217
+ * the shape of that state can be described here
218
+ */
219
+ Table?: unknown;
220
+ /**
221
+ * If a plugin has Column meta/state,
222
+ * the shape of that state can be described here
223
+ */
224
+ Column?: unknown;
225
+ /**
226
+ * If a plugin has Row meta/state,
227
+ * the shape of that state can be described here
228
+ */
229
+ Row?: unknown;
230
+ };
231
+ Options?: {
232
+ /**
233
+ * If a plugin has options configurable for the whole table,
234
+ * those can be specified here.
235
+ *
236
+ * These are passed via the the `withOptions` API
237
+ *
238
+ * ```js
239
+ * headlessTable(this?, {
240
+ * // ...
241
+ * plugins: [
242
+ * MyPlugin.withOptions(() => {
243
+ * // the return value here is this is Signature['Options']['Plugin']
244
+ * return {};
245
+ * })
246
+ * ]
247
+ * })
248
+ * ```
249
+ */
250
+ Plugin?: unknown;
251
+ /**
252
+ * If a plugin has options configurable per column,
253
+ * those can be specified here
254
+ *
255
+ * These are passed via the the `forColumn` API
256
+ *
257
+ * ```js
258
+ * headlessTable(this?, {
259
+ * // ...
260
+ * columns: () => [
261
+ * MyPlugin.forColumn(() => {
262
+ * // the return value here is this is Signature['Options']['Column']
263
+ * return {};
264
+ * })
265
+ * ]
266
+ * })
267
+ * ```
268
+ */
269
+ Column?: unknown;
270
+ };
271
+ }
272
+
273
+ /**
274
+ * @private default type
275
+ *
276
+ * Describes the shape of all the dynamic parts of a Plugin.
277
+ *
278
+ * There are no row options, because rows are not statically configurable.
279
+ */
280
+ export interface DefaultPluginSignature {
281
+ Meta: {
282
+ Row: unknown;
283
+ Column: unknown;
284
+ Table: unknown;
285
+ };
286
+ Options: {
287
+ Plugin: unknown;
288
+ Column: unknown;
289
+ };
290
+ }
291
+
292
+ /**
293
+ * @private utility type
294
+ */
295
+ export type TableMetaFor<Signature> = Signature extends {
296
+ Meta: { Table: unknown };
297
+ }
298
+ ? GetOrElse<Signature['Meta'], 'Table', never>
299
+ : never;
300
+
301
+ /**
302
+ * @private utility type
303
+ */
304
+ export type ColumnMetaFor<Signature> = Signature extends {
305
+ Meta: { Column: unknown };
306
+ }
307
+ ? GetOrElse<Signature['Meta'], 'Column', never>
308
+ : never;
309
+
310
+ /**
311
+ * @private utility type
312
+ */
313
+ export type RowMetaFor<Signature> = Signature extends { Meta: { Row: unknown } }
314
+ ? GetOrElse<Signature['Meta'], 'Row', never>
315
+ : never;
316
+
317
+ /**
318
+ * @private utility type
319
+ */
320
+ export type OptionsFor<Signature> = Signature extends { Options: object }
321
+ ? GetOrElse<Signature['Options'], 'Plugin', EmptyObject>
322
+ : EmptyObject;
323
+
324
+ /**
325
+ * @private utility type
326
+ */
327
+ export type ColumnOptionsFor<Signature> = Signature extends { Options: object }
328
+ ? GetOrElse<Signature['Options'], 'Column', EmptyObject>
329
+ : EmptyObject;
330
+
331
+ // Type-only "symbol" to use with `EmptyObject` below, so that it is *not*
332
+ // equivalent to an empty interface.
333
+ declare const Empty: unique symbol;
334
+
335
+ /**
336
+ * This provides us a way to have a "fallback" which represents an empty object,
337
+ * without the downsides of how TS treats `{}`. Specifically: this will
338
+ * correctly leverage "excess property checking" so that, given a component
339
+ * which has no named args, if someone invokes it with any named args, they will
340
+ * get a type error.
341
+ *
342
+ * @internal This is exported so declaration emit works (if it were not emitted,
343
+ * declarations which fall back to it would not work). It is *not* intended for
344
+ * public usage, and the specific mechanics it uses may change at any time.
345
+ * The location of this export *is* part of the public API, because moving it
346
+ * will break existing declarations, but is not legal for end users to import
347
+ * themselves, so ***DO NOT RELY ON IT***.
348
+ */
349
+ export type EmptyObject = { [Empty]?: true };
@@ -0,0 +1,82 @@
1
+ export interface PreferencesAdapter {
2
+ persist?(key: string, data?: TablePreferencesData): void;
3
+ restore?(key: string): TablePreferencesData | undefined;
4
+ }
5
+
6
+ /**
7
+ * The root preferences object
8
+ *
9
+ * This object is serialized to JSON for your `PreferencesAdapter` to consume.
10
+ * This could allow for saving the data off to an API or local storage.
11
+ */
12
+ export interface TablePreferencesData {
13
+ /**
14
+ * Every plugin has its own namespace for preferences storage.
15
+ *
16
+ * This is so that plugins can not worry about colliding with other plugins'
17
+ * keys within the preferences. For example: multiple plugins may use "enabled"
18
+ */
19
+ plugins?: Registry;
20
+ }
21
+
22
+ /**
23
+ * A type registry for @universal-ember/table Plugin's Preferences.
24
+ * Meant to be declaration-merged so string lookups resolve to the correct type.
25
+ *
26
+ * And so that accessing the full "preferences" object from "persist"
27
+ * and within "restore" can be fully typed.
28
+ * This also helps out with Glint, as `unknown` types are not allowed to be rendered
29
+ *
30
+ * As a plugin author, to help define what your preferences shape is, you may
31
+ * ```ts
32
+ * import { type PluginPreferences } from '@universal-ember/table/plugins';
33
+ *
34
+ * interface SortingPreferences extends PluginPreferences {
35
+ *
36
+ * }
37
+ *
38
+ * declare module '@universal-ember/table/plugins' {
39
+ * interface Registry {
40
+ * // The key *must* match the same of the class
41
+ * Sorting: SortingPreferences;
42
+ * }
43
+ * }
44
+ * ```
45
+ */
46
+ export interface Registry {}
47
+
48
+ export type PluginPreferenceFor<PluginName> = PluginName extends keyof Registry
49
+ ? Registry[PluginName] & PluginPreferences
50
+ : PluginPreferences;
51
+
52
+ export type PreferencesTableKey<PluginName> =
53
+ keyof PluginPreferenceFor<PluginName>['table'];
54
+
55
+ export type PreferencesTableValues<PluginName> =
56
+ PluginPreferenceFor<PluginName>['table'][PreferencesTableKey<PluginName>];
57
+ export type PreferencesColumnValues<PluginName> =
58
+ PluginPreferenceFor<PluginName>['columns'][keyof PluginPreferenceFor<PluginName>['columns']];
59
+ /**
60
+ * Preferences for a column may store a map of key-value pairs
61
+ * for each of
62
+ * - the table
63
+ * - each column
64
+ */
65
+ export interface PluginPreferences {
66
+ /**
67
+ * A plugin's preferences for the table can be any
68
+ * string -> stringifyable mapping
69
+ */
70
+ table: Record<string, unknown>;
71
+ /**
72
+ * preferences for a plugin's columns-of-interest are mapped out by
73
+ * the column's key
74
+ */
75
+ columns: {
76
+ /**
77
+ * For any particular column that a plugin may desire to store preferences on,
78
+ * the data can be any string -> stringifyable mapping
79
+ */
80
+ [columnKey: string]: Record<string, unknown>;
81
+ };
82
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Types minimally replicated here so we don't need to depend on the package that
3
+ * actually defines these types
4
+ *
5
+ * This also enables a much broader variety of implementations as these interfaces
6
+ * describe only what's needed by the headless table, and not the concrete implementation
7
+ */
8
+ type CurrentState<T> =
9
+ | {
10
+ state: 'ALL' | 'NONE';
11
+ }
12
+ | {
13
+ state: 'SOME';
14
+ includeItems: T[];
15
+ }
16
+ | {
17
+ state: 'ALL_EXCEPT';
18
+ excludeItems: T[];
19
+ };
20
+
21
+ /**
22
+ * A table can provide a `Selection` object that matches this API
23
+ */
24
+ export interface Selection<Item = object> {
25
+ get selected(): Item[];
26
+ get currentState(): CurrentState<Item>;
27
+ get isIndeterminate(): boolean;
28
+ get isAllSelected(): boolean;
29
+ get numSelected(): number;
30
+ get numTotal(): number;
31
+ isSelected(item: Item): boolean;
32
+ selectItem(item: Item): void;
33
+ toggleItem(item: Item): void;
34
+ toggleAll(): void;
35
+ selectAll(): void;
36
+ deselectAll(): void;
37
+ deselectItem(item: Item): void;
38
+ }
@@ -0,0 +1,121 @@
1
+ import type { Plugins } from '../../plugins/-private/utils';
2
+ import type { ColumnConfig } from './column';
3
+ import type { Pagination } from './pagination';
4
+ import type { PreferencesAdapter } from './preferences';
5
+ import type { Selection } from './selection';
6
+
7
+ export interface TableMeta {
8
+ totalRowCount?: number;
9
+ totalRowsSelectedCount?: number;
10
+ }
11
+
12
+ export interface TableConfig<DataType> {
13
+ /**
14
+ * Configuration describing how the table will crawl through `data`
15
+ * and render it. Within this `columns` config, there will also be opportunities
16
+ * to set the behavior of columns when rendered
17
+ */
18
+ columns: () => ColumnConfig<DataType>[];
19
+ /**
20
+ * The data to render, as described via the `columns` option.
21
+ *
22
+ * This data may or may not match the shape requested by the columns configuration.
23
+ * When a key-value pair matches what a column config requests, the data will be rendered.
24
+ * When a key-value pair is misisng, a fallback or empty representation of a value will be
25
+ * shown instead.
26
+ */
27
+ data: () => DataType[];
28
+
29
+ /**
30
+ * A collection of plugins for use in extending table behavior.
31
+ * plugins have a collection of hooks and properties to use, but for anything
32
+ * requiring user interaction there will be manual connecting.
33
+ *
34
+ * The instance for each plugin can be accessed via HeadlessTable's `pluginOf(<Plugin>)`
35
+ * method, where it takes the plugin constructor/class/object for lookup purposes.
36
+ *
37
+ * Some plugins may require setting options for hooking into behavior
38
+ * provided by the plugin (for example sorting).
39
+ *
40
+ * Example:
41
+ * ```js
42
+ * import { DataSorting } from '@universal-ember/table/plugins/data-sorting';
43
+ * import { ColumnResizing } from '@universal-ember/table/plugins/column-resizing';
44
+ *
45
+ * ...
46
+ *
47
+ * plugins: [
48
+ * DataSorting.with(() => {
49
+ * return {
50
+ * sorts: [array of sorts],
51
+ * onSort: this.doThingWhenSortsChange,
52
+ * };
53
+ * }),
54
+ * ColumnResizing.with(() => {
55
+ * return {
56
+ * enabled: true,
57
+ * }
58
+ * }),
59
+ * ]
60
+ * ```
61
+ *
62
+ * However, for plugins with no needed options, the list can be simplified:
63
+ * ```js
64
+ * import { ColumnResizing } from '@universal-ember/table/plugins/column-resizing';
65
+ * import { StickyColumns } from '@universal-ember/table/plugins/sticky-columns';
66
+ *
67
+ * ...
68
+ *
69
+ * plugins: [
70
+ * ColumnResizing,
71
+ * StickyColumns,
72
+ * ]
73
+ * ```
74
+ */
75
+ plugins?: Plugins;
76
+
77
+ // Bulk selection plugin?
78
+ // - maybe for providing each row with a checkbox, and some hooks for interacting with
79
+ // a potential pagination plugin
80
+ bulkSelection?: Selection;
81
+ isCheckboxSelectable?: boolean;
82
+
83
+ // Row selection plugin?
84
+ // - maybe for clicking on a row to open a side panel?
85
+ isRowSelectable?: boolean;
86
+ rowSelection?: () => DataType;
87
+ onRowSelectionChange?: (selection: DataType | undefined) => void;
88
+
89
+ // Uncategorized
90
+ meta?: TableMeta;
91
+ pagination?: Pagination;
92
+
93
+ /**
94
+ * Foundational to tables is how to store settings within them.
95
+ * The `key` is meant to identify a particular kind of table. For example, if
96
+ * you have a table representing "blog posts", your table key may be "blog-posts".
97
+ *
98
+ * And most importantly, the `adapter` is how you load and save the preferences.
99
+ * This may bo to local storage, or some API.
100
+ */
101
+ preferences?: {
102
+ /**
103
+ * What to name the table in the preferences storage of your choice.
104
+ * Any string is valid provided that the storage adapter of your choice supports
105
+ * the format.
106
+ *
107
+ * For example, if you have a table of "blog posts", the preferences key might be
108
+ * `"all-blog-posts"`
109
+ */
110
+ key: string;
111
+ /**
112
+ * Configuration for how you wish to `persist` and `restore` the configuration for your table.
113
+ *
114
+ * `persist` may be async as it is a fire-and-forget type of action.
115
+ *
116
+ * However, `restore` must be synchronous, as this is a blocking operation for rendering the table.
117
+ * So it's best to load up the table preferences before rendering a table.
118
+ */
119
+ adapter?: PreferencesAdapter;
120
+ };
121
+ }
@@ -0,0 +1,65 @@
1
+ import { Table } from './table.ts';
2
+
3
+ import type { TableConfig } from './interfaces';
4
+
5
+ type Args<T> =
6
+ | [destroyable: object, options: TableConfig<T>]
7
+ | [options: TableConfig<T>];
8
+
9
+ /**
10
+ * Represents a UI-less version of a table
11
+ *
12
+ * _For use for building tables in ui frameworks_.
13
+ *
14
+ * @example
15
+ * ```js
16
+ * import { use } from 'ember-resources';
17
+ * import { headlessTable } '@universal-ember/table';
18
+ *
19
+ * class MyImplementation {
20
+ * @use table = headlessTable({
21
+ * // your config here
22
+ * })
23
+ * }
24
+ * ```
25
+ */
26
+ export function headlessTable<T = unknown>(options: TableConfig<T>): Table<T>;
27
+
28
+ /**
29
+ * Represents a UI-less version of a table
30
+ *
31
+ * _For use for building tables in ui frameworks_.
32
+ *
33
+ * @example
34
+ * ```js
35
+ * import { headlessTable } '@universal-ember/table';
36
+ *
37
+ * class MyImplementation {
38
+ * table = headlessTable(this, {
39
+ * // your config here
40
+ * })
41
+ * }
42
+ * ```
43
+ *
44
+ */
45
+ export function headlessTable<T = unknown>(
46
+ destroyable: object,
47
+ options: TableConfig<T>,
48
+ ): Table<T>;
49
+
50
+ export function headlessTable<T = unknown>(...args: Args<T>): Table<T> {
51
+ if (args.length === 2) {
52
+ const [destroyable, options] = args;
53
+
54
+ /**
55
+ * If any "root level" config changes, we need to throw-away everything.
56
+ * otherwise individual-property reactivity can be managed on a per-property
57
+ * "thunk"-basis
58
+ */
59
+ return Table.from<Table<T>>(destroyable, () => options);
60
+ }
61
+
62
+ const [options] = args;
63
+
64
+ return Table.from<Table<T>>(() => options);
65
+ }