@tanstack/angular-table 8.20.5 → 8.21.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.
- package/build/lib/esm2022/flex-render/context.mjs +6 -0
- package/build/lib/esm2022/flex-render/flags.mjs +42 -0
- package/build/lib/esm2022/flex-render/flex-render-component-ref.mjs +173 -0
- package/build/lib/esm2022/flex-render/flex-render-component.mjs +44 -0
- package/build/lib/esm2022/flex-render/view.mjs +102 -0
- package/build/lib/esm2022/flex-render.mjs +133 -73
- package/build/lib/esm2022/index.mjs +3 -2
- package/build/lib/esm2022/proxy.mjs +7 -3
- package/build/lib/fesm2022/tanstack-angular-table.mjs +494 -76
- package/build/lib/fesm2022/tanstack-angular-table.mjs.map +1 -1
- package/build/lib/flex-render/context.d.ts +3 -0
- package/build/lib/flex-render/flags.d.ts +40 -0
- package/build/lib/flex-render/flex-render-component-ref.d.ts +39 -0
- package/build/lib/flex-render/flex-render-component.d.ts +60 -0
- package/build/lib/flex-render/view.d.ts +49 -0
- package/build/lib/flex-render.d.ts +13 -17
- package/build/lib/index.d.ts +2 -1
- package/package.json +6 -3
- package/src/flex-render/context.ts +9 -0
- package/src/flex-render/flags.ts +40 -0
- package/src/flex-render/flex-render-component-ref.ts +236 -0
- package/src/flex-render/flex-render-component.ts +119 -0
- package/src/flex-render/view.ts +153 -0
- package/src/flex-render.ts +196 -94
- package/src/index.ts +7 -1
- package/src/proxy.ts +5 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { untracked, computed, inject,
|
|
3
|
-
import { createTable } from '@tanstack/table-core';
|
|
2
|
+
import { untracked, computed, InjectionToken, inject, reflectComponentType, ViewContainerRef, Injectable, KeyValueDiffers, ChangeDetectorRef, OutputEmitterRef, TemplateRef, Type, Injector, runInInjectionContext, effect, Directive, Inject, Input, signal } from '@angular/core';
|
|
3
|
+
import { memo, createTable } from '@tanstack/table-core';
|
|
4
4
|
export * from '@tanstack/table-core';
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -61,8 +61,12 @@ function proxifyTable(tableSignal) {
|
|
|
61
61
|
* excluding handlers as they do not retain any reactive value
|
|
62
62
|
*/
|
|
63
63
|
if (property.startsWith('get') &&
|
|
64
|
-
!property.endsWith('Handler')
|
|
65
|
-
|
|
64
|
+
!property.endsWith('Handler')
|
|
65
|
+
// e.g. getCoreRowModel, getSelectedRowModel etc.
|
|
66
|
+
// We need that after a signal change even `rowModel` may mark the view as dirty.
|
|
67
|
+
// This allows to always get the latest `getContext` value while using flexRender
|
|
68
|
+
// && !property.endsWith('Model')
|
|
69
|
+
) {
|
|
66
70
|
const maybeFn = table[property];
|
|
67
71
|
if (typeof maybeFn === 'function') {
|
|
68
72
|
Object.defineProperty(target, property, {
|
|
@@ -127,120 +131,548 @@ function serializeArgs(...args) {
|
|
|
127
131
|
return JSON.stringify(args);
|
|
128
132
|
}
|
|
129
133
|
|
|
134
|
+
const FlexRenderComponentProps = new InjectionToken('[@tanstack/angular-table] Flex render component context props');
|
|
135
|
+
function injectFlexRenderContext() {
|
|
136
|
+
return inject(FlexRenderComponentProps);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Flags used to manage and optimize the rendering lifecycle of content of the cell,
|
|
141
|
+
* while using FlexRenderDirective.
|
|
142
|
+
*/
|
|
143
|
+
var FlexRenderFlags;
|
|
144
|
+
(function (FlexRenderFlags) {
|
|
145
|
+
/**
|
|
146
|
+
* Indicates that the view is being created for the first time or will be cleared during the next update phase.
|
|
147
|
+
* This is the initial state and will transition after the first ngDoCheck.
|
|
148
|
+
*/
|
|
149
|
+
FlexRenderFlags[FlexRenderFlags["ViewFirstRender"] = 1] = "ViewFirstRender";
|
|
150
|
+
/**
|
|
151
|
+
* Represents a state where the view is not dirty, meaning no changes require rendering updates.
|
|
152
|
+
*/
|
|
153
|
+
FlexRenderFlags[FlexRenderFlags["Pristine"] = 2] = "Pristine";
|
|
154
|
+
/**
|
|
155
|
+
* Indicates the `content` property has been modified or the view requires a complete re-render.
|
|
156
|
+
* When this flag is enabled, the view will be cleared and recreated from scratch.
|
|
157
|
+
*/
|
|
158
|
+
FlexRenderFlags[FlexRenderFlags["ContentChanged"] = 4] = "ContentChanged";
|
|
159
|
+
/**
|
|
160
|
+
* Indicates that the `props` property reference has changed.
|
|
161
|
+
* When this flag is enabled, the view context is updated based on the type of the content.
|
|
162
|
+
*
|
|
163
|
+
* For Component view, inputs will be updated and view will be marked as dirty.
|
|
164
|
+
* For TemplateRef and primitive values, view will be marked as dirty
|
|
165
|
+
*/
|
|
166
|
+
FlexRenderFlags[FlexRenderFlags["PropsReferenceChanged"] = 8] = "PropsReferenceChanged";
|
|
167
|
+
/**
|
|
168
|
+
* Indicates that the current rendered view needs to be checked for changes.
|
|
169
|
+
*/
|
|
170
|
+
FlexRenderFlags[FlexRenderFlags["DirtyCheck"] = 16] = "DirtyCheck";
|
|
171
|
+
/**
|
|
172
|
+
* Indicates that a signal within the `content(props)` result has changed
|
|
173
|
+
*/
|
|
174
|
+
FlexRenderFlags[FlexRenderFlags["DirtySignal"] = 32] = "DirtySignal";
|
|
175
|
+
/**
|
|
176
|
+
* Indicates that the first render effect has been checked at least one time.
|
|
177
|
+
*/
|
|
178
|
+
FlexRenderFlags[FlexRenderFlags["RenderEffectChecked"] = 64] = "RenderEffectChecked";
|
|
179
|
+
})(FlexRenderFlags || (FlexRenderFlags = {}));
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Helper function to create a [@link FlexRenderComponent] instance, with better type-safety.
|
|
183
|
+
*
|
|
184
|
+
* - options object must be passed when the given component instance contains at least one required signal input.
|
|
185
|
+
* - options/inputs is typed with the given component inputs
|
|
186
|
+
* - options/outputs is typed with the given component outputs
|
|
187
|
+
*/
|
|
188
|
+
function flexRenderComponent(component, ...options) {
|
|
189
|
+
const { inputs, injector, outputs } = options?.[0] ?? {};
|
|
190
|
+
return new FlexRenderComponent(component, inputs, injector, outputs);
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Wrapper class for a component that will be used as content for {@link FlexRenderDirective}
|
|
194
|
+
*
|
|
195
|
+
* Prefer {@link flexRenderComponent} helper for better type-safety
|
|
196
|
+
*/
|
|
197
|
+
class FlexRenderComponent {
|
|
198
|
+
component;
|
|
199
|
+
inputs;
|
|
200
|
+
injector;
|
|
201
|
+
outputs;
|
|
202
|
+
mirror;
|
|
203
|
+
allowedInputNames = [];
|
|
204
|
+
allowedOutputNames = [];
|
|
205
|
+
constructor(component, inputs, injector, outputs) {
|
|
206
|
+
this.component = component;
|
|
207
|
+
this.inputs = inputs;
|
|
208
|
+
this.injector = injector;
|
|
209
|
+
this.outputs = outputs;
|
|
210
|
+
const mirror = reflectComponentType(component);
|
|
211
|
+
if (!mirror) {
|
|
212
|
+
throw new Error(`[@tanstack-table/angular] The provided symbol is not a component`);
|
|
213
|
+
}
|
|
214
|
+
this.mirror = mirror;
|
|
215
|
+
for (const input of this.mirror.inputs) {
|
|
216
|
+
this.allowedInputNames.push(input.propName);
|
|
217
|
+
}
|
|
218
|
+
for (const output of this.mirror.outputs) {
|
|
219
|
+
this.allowedOutputNames.push(output.propName);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
class FlexRenderComponentFactory {
|
|
225
|
+
#viewContainerRef = inject(ViewContainerRef);
|
|
226
|
+
createComponent(flexRenderComponent, componentInjector) {
|
|
227
|
+
const componentRef = this.#viewContainerRef.createComponent(flexRenderComponent.component, {
|
|
228
|
+
injector: componentInjector,
|
|
229
|
+
});
|
|
230
|
+
const view = new FlexRenderComponentRef(componentRef, flexRenderComponent, componentInjector);
|
|
231
|
+
const { inputs, outputs } = flexRenderComponent;
|
|
232
|
+
if (inputs)
|
|
233
|
+
view.setInputs(inputs);
|
|
234
|
+
if (outputs)
|
|
235
|
+
view.setOutputs(outputs);
|
|
236
|
+
return view;
|
|
237
|
+
}
|
|
238
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.11", ngImport: i0, type: FlexRenderComponentFactory, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
239
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.11", ngImport: i0, type: FlexRenderComponentFactory });
|
|
240
|
+
}
|
|
241
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.11", ngImport: i0, type: FlexRenderComponentFactory, decorators: [{
|
|
242
|
+
type: Injectable
|
|
243
|
+
}] });
|
|
244
|
+
class FlexRenderComponentRef {
|
|
245
|
+
componentRef;
|
|
246
|
+
componentInjector;
|
|
247
|
+
#keyValueDiffersFactory;
|
|
248
|
+
#componentData;
|
|
249
|
+
#inputValueDiffer;
|
|
250
|
+
#outputRegistry;
|
|
251
|
+
constructor(componentRef, componentData, componentInjector) {
|
|
252
|
+
this.componentRef = componentRef;
|
|
253
|
+
this.componentInjector = componentInjector;
|
|
254
|
+
this.#componentData = componentData;
|
|
255
|
+
this.#keyValueDiffersFactory = componentInjector.get(KeyValueDiffers);
|
|
256
|
+
this.#outputRegistry = new FlexRenderComponentOutputManager(this.#keyValueDiffersFactory, this.outputs);
|
|
257
|
+
this.#inputValueDiffer = this.#keyValueDiffersFactory
|
|
258
|
+
.find(this.inputs)
|
|
259
|
+
.create();
|
|
260
|
+
this.#inputValueDiffer.diff(this.inputs);
|
|
261
|
+
this.componentRef.onDestroy(() => this.#outputRegistry.unsubscribeAll());
|
|
262
|
+
}
|
|
263
|
+
get component() {
|
|
264
|
+
return this.#componentData.component;
|
|
265
|
+
}
|
|
266
|
+
get inputs() {
|
|
267
|
+
return this.#componentData.inputs ?? {};
|
|
268
|
+
}
|
|
269
|
+
get outputs() {
|
|
270
|
+
return this.#componentData.outputs ?? {};
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Get component input and output diff by the given item
|
|
274
|
+
*/
|
|
275
|
+
diff(item) {
|
|
276
|
+
return {
|
|
277
|
+
inputDiff: this.#inputValueDiffer.diff(item.inputs ?? {}),
|
|
278
|
+
outputDiff: this.#outputRegistry.diff(item.outputs ?? {}),
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
*
|
|
283
|
+
* @param compare Whether the current ref component instance is the same as the given one
|
|
284
|
+
*/
|
|
285
|
+
eqType(compare) {
|
|
286
|
+
return compare.component === this.component;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Tries to update current component refs input by the new given content component.
|
|
290
|
+
*/
|
|
291
|
+
update(content) {
|
|
292
|
+
const eq = this.eqType(content);
|
|
293
|
+
if (!eq)
|
|
294
|
+
return;
|
|
295
|
+
const { inputDiff, outputDiff } = this.diff(content);
|
|
296
|
+
if (inputDiff) {
|
|
297
|
+
inputDiff.forEachAddedItem(item => this.setInput(item.key, item.currentValue));
|
|
298
|
+
inputDiff.forEachChangedItem(item => this.setInput(item.key, item.currentValue));
|
|
299
|
+
inputDiff.forEachRemovedItem(item => this.setInput(item.key, undefined));
|
|
300
|
+
}
|
|
301
|
+
if (outputDiff) {
|
|
302
|
+
outputDiff.forEachAddedItem(item => {
|
|
303
|
+
this.setOutput(item.key, item.currentValue);
|
|
304
|
+
});
|
|
305
|
+
outputDiff.forEachChangedItem(item => {
|
|
306
|
+
if (item.currentValue) {
|
|
307
|
+
this.#outputRegistry.setListener(item.key, item.currentValue);
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
this.#outputRegistry.unsubscribe(item.key);
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
outputDiff.forEachRemovedItem(item => {
|
|
314
|
+
this.#outputRegistry.unsubscribe(item.key);
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
this.#componentData = content;
|
|
318
|
+
}
|
|
319
|
+
markAsDirty() {
|
|
320
|
+
this.componentRef.injector.get(ChangeDetectorRef).markForCheck();
|
|
321
|
+
}
|
|
322
|
+
setInputs(inputs) {
|
|
323
|
+
for (const prop in inputs) {
|
|
324
|
+
this.setInput(prop, inputs[prop]);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
setInput(key, value) {
|
|
328
|
+
if (this.#componentData.allowedInputNames.includes(key)) {
|
|
329
|
+
this.componentRef.setInput(key, value);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
setOutputs(outputs) {
|
|
333
|
+
this.#outputRegistry.unsubscribeAll();
|
|
334
|
+
for (const prop in outputs) {
|
|
335
|
+
this.setOutput(prop, outputs[prop]);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
setOutput(outputName, emit) {
|
|
339
|
+
if (!this.#componentData.allowedOutputNames.includes(outputName))
|
|
340
|
+
return;
|
|
341
|
+
if (!emit) {
|
|
342
|
+
this.#outputRegistry.unsubscribe(outputName);
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
const hasListener = this.#outputRegistry.hasListener(outputName);
|
|
346
|
+
this.#outputRegistry.setListener(outputName, emit);
|
|
347
|
+
if (hasListener) {
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
const instance = this.componentRef.instance;
|
|
351
|
+
const output = instance[outputName];
|
|
352
|
+
if (output && output instanceof OutputEmitterRef) {
|
|
353
|
+
output.subscribe(value => {
|
|
354
|
+
this.#outputRegistry.getListener(outputName)?.(value);
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
class FlexRenderComponentOutputManager {
|
|
360
|
+
#outputSubscribers = {};
|
|
361
|
+
#outputListeners = {};
|
|
362
|
+
#valueDiffer;
|
|
363
|
+
constructor(keyValueDiffers, initialOutputs) {
|
|
364
|
+
this.#valueDiffer = keyValueDiffers.find(initialOutputs).create();
|
|
365
|
+
if (initialOutputs) {
|
|
366
|
+
this.#valueDiffer.diff(initialOutputs);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
hasListener(outputName) {
|
|
370
|
+
return outputName in this.#outputListeners;
|
|
371
|
+
}
|
|
372
|
+
setListener(outputName, callback) {
|
|
373
|
+
this.#outputListeners[outputName] = callback;
|
|
374
|
+
}
|
|
375
|
+
getListener(outputName) {
|
|
376
|
+
return this.#outputListeners[outputName];
|
|
377
|
+
}
|
|
378
|
+
unsubscribeAll() {
|
|
379
|
+
for (const prop in this.#outputSubscribers) {
|
|
380
|
+
this.unsubscribe(prop);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
unsubscribe(outputName) {
|
|
384
|
+
if (outputName in this.#outputSubscribers) {
|
|
385
|
+
this.#outputSubscribers[outputName]?.unsubscribe();
|
|
386
|
+
delete this.#outputSubscribers[outputName];
|
|
387
|
+
delete this.#outputListeners[outputName];
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
diff(outputs) {
|
|
391
|
+
return this.#valueDiffer.diff(outputs ?? {});
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function mapToFlexRenderTypedContent(content) {
|
|
396
|
+
if (content === null || content === undefined) {
|
|
397
|
+
return { kind: 'null' };
|
|
398
|
+
}
|
|
399
|
+
if (typeof content === 'string' || typeof content === 'number') {
|
|
400
|
+
return { kind: 'primitive', content };
|
|
401
|
+
}
|
|
402
|
+
if (content instanceof FlexRenderComponent) {
|
|
403
|
+
return { kind: 'flexRenderComponent', content };
|
|
404
|
+
}
|
|
405
|
+
else if (content instanceof TemplateRef) {
|
|
406
|
+
return { kind: 'templateRef', content };
|
|
407
|
+
}
|
|
408
|
+
else if (content instanceof Type) {
|
|
409
|
+
return { kind: 'component', content };
|
|
410
|
+
}
|
|
411
|
+
else {
|
|
412
|
+
return { kind: 'primitive', content };
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
class FlexRenderView {
|
|
416
|
+
view;
|
|
417
|
+
#previousContent;
|
|
418
|
+
#content;
|
|
419
|
+
constructor(initialContent, view) {
|
|
420
|
+
this.#content = initialContent;
|
|
421
|
+
this.view = view;
|
|
422
|
+
}
|
|
423
|
+
get previousContent() {
|
|
424
|
+
return this.#previousContent ?? { kind: 'null' };
|
|
425
|
+
}
|
|
426
|
+
get content() {
|
|
427
|
+
return this.#content;
|
|
428
|
+
}
|
|
429
|
+
set content(content) {
|
|
430
|
+
this.#previousContent = this.#content;
|
|
431
|
+
this.#content = content;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
class FlexRenderTemplateView extends FlexRenderView {
|
|
435
|
+
constructor(initialContent, view) {
|
|
436
|
+
super(initialContent, view);
|
|
437
|
+
}
|
|
438
|
+
updateProps(props) {
|
|
439
|
+
this.view.markForCheck();
|
|
440
|
+
}
|
|
441
|
+
dirtyCheck() {
|
|
442
|
+
// Basically a no-op. When the view is created via EmbeddedViewRef, we don't need to do any manual update
|
|
443
|
+
// since this type of content has a proxy as a context, then every time the root component is checked for changes,
|
|
444
|
+
// the property getter will be re-evaluated.
|
|
445
|
+
//
|
|
446
|
+
// If in a future we need to manually mark the view as dirty, just uncomment next line
|
|
447
|
+
// this.view.markForCheck()
|
|
448
|
+
}
|
|
449
|
+
onDestroy(callback) {
|
|
450
|
+
this.view.onDestroy(callback);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
class FlexRenderComponentView extends FlexRenderView {
|
|
454
|
+
constructor(initialContent, view) {
|
|
455
|
+
super(initialContent, view);
|
|
456
|
+
}
|
|
457
|
+
updateProps(props) {
|
|
458
|
+
switch (this.content.kind) {
|
|
459
|
+
case 'component': {
|
|
460
|
+
this.view.setInputs(props);
|
|
461
|
+
break;
|
|
462
|
+
}
|
|
463
|
+
case 'flexRenderComponent': {
|
|
464
|
+
// No-op. When FlexRenderFlags.PropsReferenceChanged is set,
|
|
465
|
+
// FlexRenderComponent will be updated into `dirtyCheck`.
|
|
466
|
+
break;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
dirtyCheck() {
|
|
471
|
+
switch (this.content.kind) {
|
|
472
|
+
case 'component': {
|
|
473
|
+
// Component context is currently valuated with the cell context. Since it's reference
|
|
474
|
+
// shouldn't change, we force mark the component as dirty in order to re-evaluate function invocation in view.
|
|
475
|
+
// NOTE: this should behave like having a component with ChangeDetectionStrategy.Default
|
|
476
|
+
this.view.markAsDirty();
|
|
477
|
+
break;
|
|
478
|
+
}
|
|
479
|
+
case 'flexRenderComponent': {
|
|
480
|
+
// Given context instance will always have a different reference than the previous one,
|
|
481
|
+
// so instead of recreating the entire view, we will only update the current view
|
|
482
|
+
if (this.view.eqType(this.content.content)) {
|
|
483
|
+
this.view.update(this.content.content);
|
|
484
|
+
}
|
|
485
|
+
this.view.markAsDirty();
|
|
486
|
+
break;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
onDestroy(callback) {
|
|
491
|
+
this.view.componentRef.onDestroy(callback);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
130
495
|
class FlexRenderDirective {
|
|
131
496
|
viewContainerRef;
|
|
132
497
|
templateRef;
|
|
498
|
+
#flexRenderComponentFactory = inject(FlexRenderComponentFactory);
|
|
499
|
+
#changeDetectorRef = inject(ChangeDetectorRef);
|
|
133
500
|
content = undefined;
|
|
134
501
|
props = {};
|
|
135
502
|
injector = inject(Injector);
|
|
503
|
+
renderFlags = FlexRenderFlags.ViewFirstRender;
|
|
504
|
+
renderView = null;
|
|
505
|
+
#latestContent = () => {
|
|
506
|
+
const { content, props } = this;
|
|
507
|
+
return typeof content !== 'function'
|
|
508
|
+
? content
|
|
509
|
+
: runInInjectionContext(this.injector, () => content(props));
|
|
510
|
+
};
|
|
511
|
+
#getContentValue = memo(() => [this.#latestContent(), this.props, this.content], latestContent => {
|
|
512
|
+
return mapToFlexRenderTypedContent(latestContent);
|
|
513
|
+
}, { key: 'flexRenderContentValue', debug: () => false });
|
|
136
514
|
constructor(viewContainerRef, templateRef) {
|
|
137
515
|
this.viewContainerRef = viewContainerRef;
|
|
138
516
|
this.templateRef = templateRef;
|
|
139
517
|
}
|
|
140
|
-
ref = null;
|
|
141
518
|
ngOnChanges(changes) {
|
|
142
|
-
if (
|
|
143
|
-
this.
|
|
519
|
+
if (changes['props']) {
|
|
520
|
+
this.renderFlags |= FlexRenderFlags.PropsReferenceChanged;
|
|
144
521
|
}
|
|
145
|
-
if (
|
|
522
|
+
if (changes['content']) {
|
|
523
|
+
this.renderFlags |=
|
|
524
|
+
FlexRenderFlags.ContentChanged | FlexRenderFlags.ViewFirstRender;
|
|
525
|
+
this.update();
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
ngDoCheck() {
|
|
529
|
+
if (this.renderFlags & FlexRenderFlags.ViewFirstRender) {
|
|
530
|
+
// On the initial render, the view is created during the `ngOnChanges` hook.
|
|
531
|
+
// Since `ngDoCheck` is called immediately afterward, there's no need to check for changes in this phase.
|
|
532
|
+
this.renderFlags &= ~FlexRenderFlags.ViewFirstRender;
|
|
146
533
|
return;
|
|
147
534
|
}
|
|
148
|
-
this.
|
|
535
|
+
this.renderFlags |= FlexRenderFlags.DirtyCheck;
|
|
536
|
+
const latestContent = this.#getContentValue();
|
|
537
|
+
if (latestContent.kind === 'null' || !this.renderView) {
|
|
538
|
+
this.renderFlags |= FlexRenderFlags.ContentChanged;
|
|
539
|
+
}
|
|
540
|
+
else {
|
|
541
|
+
this.renderView.content = latestContent;
|
|
542
|
+
const { kind: previousKind } = this.renderView.previousContent;
|
|
543
|
+
if (latestContent.kind !== previousKind) {
|
|
544
|
+
this.renderFlags |= FlexRenderFlags.ContentChanged;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
this.update();
|
|
149
548
|
}
|
|
150
|
-
|
|
151
|
-
this.
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
this.ref = null;
|
|
549
|
+
update() {
|
|
550
|
+
if (this.renderFlags &
|
|
551
|
+
(FlexRenderFlags.ContentChanged | FlexRenderFlags.ViewFirstRender)) {
|
|
552
|
+
this.render();
|
|
155
553
|
return;
|
|
156
554
|
}
|
|
157
|
-
if (
|
|
158
|
-
|
|
555
|
+
if (this.renderFlags & FlexRenderFlags.PropsReferenceChanged) {
|
|
556
|
+
if (this.renderView)
|
|
557
|
+
this.renderView.updateProps(this.props);
|
|
558
|
+
this.renderFlags &= ~FlexRenderFlags.PropsReferenceChanged;
|
|
559
|
+
}
|
|
560
|
+
if (this.renderFlags &
|
|
561
|
+
(FlexRenderFlags.DirtyCheck | FlexRenderFlags.DirtySignal)) {
|
|
562
|
+
if (this.renderView)
|
|
563
|
+
this.renderView.dirtyCheck();
|
|
564
|
+
this.renderFlags &= ~(FlexRenderFlags.DirtyCheck | FlexRenderFlags.DirtySignal);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
#currentEffectRef = null;
|
|
568
|
+
render() {
|
|
569
|
+
if (this.#shouldRecreateEntireView() && this.#currentEffectRef) {
|
|
570
|
+
this.#currentEffectRef.destroy();
|
|
571
|
+
this.#currentEffectRef = null;
|
|
572
|
+
this.renderFlags &= ~FlexRenderFlags.RenderEffectChecked;
|
|
573
|
+
}
|
|
574
|
+
this.viewContainerRef.clear();
|
|
575
|
+
this.renderFlags =
|
|
576
|
+
FlexRenderFlags.Pristine |
|
|
577
|
+
(this.renderFlags & FlexRenderFlags.ViewFirstRender) |
|
|
578
|
+
(this.renderFlags & FlexRenderFlags.RenderEffectChecked);
|
|
579
|
+
const resolvedContent = this.#getContentValue();
|
|
580
|
+
if (resolvedContent.kind === 'null') {
|
|
581
|
+
this.renderView = null;
|
|
159
582
|
}
|
|
160
583
|
else {
|
|
161
|
-
|
|
584
|
+
this.renderView = this.#renderViewByContent(resolvedContent);
|
|
585
|
+
}
|
|
586
|
+
// If the content is a function `content(props)`, we initialize an effect
|
|
587
|
+
// in order to react to changes if the given definition use signals.
|
|
588
|
+
if (!this.#currentEffectRef && typeof this.content === 'function') {
|
|
589
|
+
this.#currentEffectRef = effect(() => {
|
|
590
|
+
this.#latestContent();
|
|
591
|
+
if (!(this.renderFlags & FlexRenderFlags.RenderEffectChecked)) {
|
|
592
|
+
this.renderFlags |= FlexRenderFlags.RenderEffectChecked;
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
this.renderFlags |= FlexRenderFlags.DirtySignal;
|
|
596
|
+
// This will mark the view as changed,
|
|
597
|
+
// so we'll try to check for updates into ngDoCheck
|
|
598
|
+
this.#changeDetectorRef.markForCheck();
|
|
599
|
+
}, { injector: this.viewContainerRef.injector });
|
|
162
600
|
}
|
|
163
601
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
602
|
+
#shouldRecreateEntireView() {
|
|
603
|
+
return (this.renderFlags &
|
|
604
|
+
FlexRenderFlags.ContentChanged &
|
|
605
|
+
FlexRenderFlags.ViewFirstRender);
|
|
606
|
+
}
|
|
607
|
+
#renderViewByContent(content) {
|
|
608
|
+
if (content.kind === 'primitive') {
|
|
609
|
+
return this.#renderStringContent(content);
|
|
167
610
|
}
|
|
168
|
-
if (content
|
|
169
|
-
return this
|
|
611
|
+
else if (content.kind === 'templateRef') {
|
|
612
|
+
return this.#renderTemplateRefContent(content);
|
|
170
613
|
}
|
|
171
|
-
else if (content
|
|
172
|
-
return this
|
|
614
|
+
else if (content.kind === 'flexRenderComponent') {
|
|
615
|
+
return this.#renderComponent(content);
|
|
173
616
|
}
|
|
174
|
-
else if (content
|
|
175
|
-
return this
|
|
617
|
+
else if (content.kind === 'component') {
|
|
618
|
+
return this.#renderCustomComponent(content);
|
|
176
619
|
}
|
|
177
620
|
else {
|
|
178
621
|
return null;
|
|
179
622
|
}
|
|
180
623
|
}
|
|
181
|
-
renderStringContent() {
|
|
624
|
+
#renderStringContent(template) {
|
|
182
625
|
const context = () => {
|
|
183
626
|
return typeof this.content === 'string' ||
|
|
184
627
|
typeof this.content === 'number'
|
|
185
628
|
? this.content
|
|
186
629
|
: this.content?.(this.props);
|
|
187
630
|
};
|
|
188
|
-
|
|
631
|
+
const ref = this.viewContainerRef.createEmbeddedView(this.templateRef, {
|
|
189
632
|
get $implicit() {
|
|
190
633
|
return context();
|
|
191
634
|
},
|
|
192
635
|
});
|
|
636
|
+
return new FlexRenderTemplateView(template, ref);
|
|
193
637
|
}
|
|
194
|
-
|
|
195
|
-
const
|
|
638
|
+
#renderTemplateRefContent(template) {
|
|
639
|
+
const latestContext = () => this.props;
|
|
640
|
+
const view = this.viewContainerRef.createEmbeddedView(template.content, {
|
|
641
|
+
get $implicit() {
|
|
642
|
+
return latestContext();
|
|
643
|
+
},
|
|
644
|
+
});
|
|
645
|
+
return new FlexRenderTemplateView(template, view);
|
|
646
|
+
}
|
|
647
|
+
#renderComponent(flexRenderComponent) {
|
|
648
|
+
const { inputs, outputs, injector } = flexRenderComponent.content;
|
|
196
649
|
const getContext = () => this.props;
|
|
197
650
|
const proxy = new Proxy(this.props, {
|
|
198
|
-
get: (_, key) => getContext()
|
|
651
|
+
get: (_, key) => getContext()[key],
|
|
199
652
|
});
|
|
200
653
|
const componentInjector = Injector.create({
|
|
201
654
|
parent: injector ?? this.injector,
|
|
202
655
|
providers: [{ provide: FlexRenderComponentProps, useValue: proxy }],
|
|
203
656
|
});
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
});
|
|
207
|
-
for (const prop in inputs) {
|
|
208
|
-
if (componentRef.instance?.hasOwnProperty(prop)) {
|
|
209
|
-
componentRef.setInput(prop, inputs[prop]);
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
return componentRef;
|
|
657
|
+
const view = this.#flexRenderComponentFactory.createComponent(flexRenderComponent.content, componentInjector);
|
|
658
|
+
return new FlexRenderComponentView(flexRenderComponent, view);
|
|
213
659
|
}
|
|
214
|
-
renderCustomComponent(component) {
|
|
215
|
-
const
|
|
660
|
+
#renderCustomComponent(component) {
|
|
661
|
+
const view = this.#flexRenderComponentFactory.createComponent(flexRenderComponent(component.content, {
|
|
662
|
+
inputs: this.props,
|
|
216
663
|
injector: this.injector,
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
// Only signal based input can be added here
|
|
220
|
-
if (componentRef.instance?.hasOwnProperty(prop) &&
|
|
221
|
-
// @ts-ignore
|
|
222
|
-
isSignal(componentRef.instance[prop])) {
|
|
223
|
-
componentRef.setInput(prop, this.props[prop]);
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
return componentRef;
|
|
227
|
-
}
|
|
228
|
-
getTemplateRefContext() {
|
|
229
|
-
const getContext = () => this.props;
|
|
230
|
-
return {
|
|
231
|
-
get $implicit() {
|
|
232
|
-
return getContext();
|
|
233
|
-
},
|
|
234
|
-
};
|
|
664
|
+
}), this.injector);
|
|
665
|
+
return new FlexRenderComponentView(component, view);
|
|
235
666
|
}
|
|
236
667
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.11", ngImport: i0, type: FlexRenderDirective, deps: [{ token: ViewContainerRef }, { token: TemplateRef }], target: i0.ɵɵFactoryTarget.Directive });
|
|
237
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.11", type: FlexRenderDirective, isStandalone: true, selector: "[flexRender]", inputs: { content: ["flexRender", "content"], props: ["flexRenderProps", "props"], injector: ["flexRenderInjector", "injector"] }, usesOnChanges: true, ngImport: i0 });
|
|
668
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.11", type: FlexRenderDirective, isStandalone: true, selector: "[flexRender]", inputs: { content: ["flexRender", "content"], props: ["flexRenderProps", "props"], injector: ["flexRenderInjector", "injector"] }, providers: [FlexRenderComponentFactory], usesOnChanges: true, ngImport: i0 });
|
|
238
669
|
}
|
|
239
670
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.11", ngImport: i0, type: FlexRenderDirective, decorators: [{
|
|
240
671
|
type: Directive,
|
|
241
672
|
args: [{
|
|
242
673
|
selector: '[flexRender]',
|
|
243
674
|
standalone: true,
|
|
675
|
+
providers: [FlexRenderComponentFactory],
|
|
244
676
|
}]
|
|
245
677
|
}], ctorParameters: () => [{ type: i0.ViewContainerRef, decorators: [{
|
|
246
678
|
type: Inject,
|
|
@@ -258,20 +690,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.11", ngImpo
|
|
|
258
690
|
type: Input,
|
|
259
691
|
args: [{ required: false, alias: 'flexRenderInjector' }]
|
|
260
692
|
}] } });
|
|
261
|
-
class FlexRenderComponent {
|
|
262
|
-
component;
|
|
263
|
-
inputs;
|
|
264
|
-
injector;
|
|
265
|
-
constructor(component, inputs = {}, injector) {
|
|
266
|
-
this.component = component;
|
|
267
|
-
this.inputs = inputs;
|
|
268
|
-
this.injector = injector;
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
const FlexRenderComponentProps = new InjectionToken('[@tanstack/angular-table] Flex render component context props');
|
|
272
|
-
function injectFlexRenderContext() {
|
|
273
|
-
return inject(FlexRenderComponentProps);
|
|
274
|
-
}
|
|
275
693
|
|
|
276
694
|
function createAngularTable(options) {
|
|
277
695
|
return lazyInit(() => {
|
|
@@ -319,5 +737,5 @@ function createAngularTable(options) {
|
|
|
319
737
|
* Generated bundle index. Do not edit.
|
|
320
738
|
*/
|
|
321
739
|
|
|
322
|
-
export { FlexRenderComponent, FlexRenderDirective, createAngularTable, injectFlexRenderContext };
|
|
740
|
+
export { FlexRenderDirective as FlexRender, FlexRenderComponent, FlexRenderDirective, createAngularTable, flexRenderComponent, injectFlexRenderContext };
|
|
323
741
|
//# sourceMappingURL=tanstack-angular-table.mjs.map
|