@tanstack/angular-table 9.0.0-alpha.8 → 9.0.0-beta.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 (54) hide show
  1. package/README.md +127 -0
  2. package/dist/README.md +127 -0
  3. package/dist/fesm2022/tanstack-angular-table-static-functions.mjs +6 -0
  4. package/dist/fesm2022/tanstack-angular-table-static-functions.mjs.map +1 -0
  5. package/dist/fesm2022/tanstack-angular-table.mjs +1337 -242
  6. package/dist/fesm2022/tanstack-angular-table.mjs.map +1 -1
  7. package/dist/types/tanstack-angular-table-static-functions.d.ts +1 -0
  8. package/dist/types/tanstack-angular-table.d.ts +793 -0
  9. package/package.json +39 -19
  10. package/skills/angular/angular-rendering-directives/SKILL.md +415 -0
  11. package/skills/angular/angular-rendering-directives/references/content-shapes.md +142 -0
  12. package/skills/angular/angular-rendering-directives/references/create-table-hook-registries.md +89 -0
  13. package/skills/angular/angular-rendering-directives/references/di-tokens.md +171 -0
  14. package/skills/angular/angular-rendering-directives/references/flex-render-component-options.md +64 -0
  15. package/skills/angular/client-to-server/SKILL.md +467 -0
  16. package/skills/angular/compose-with-tanstack-query/SKILL.md +482 -0
  17. package/skills/angular/compose-with-tanstack-store/SKILL.md +397 -0
  18. package/skills/angular/compose-with-tanstack-virtual/SKILL.md +400 -0
  19. package/skills/angular/getting-started/SKILL.md +496 -0
  20. package/skills/angular/getting-started/references/feature-row-model-mapping.md +48 -0
  21. package/skills/angular/migrate-v8-to-v9/SKILL.md +419 -0
  22. package/skills/angular/migrate-v8-to-v9/references/v8-to-v9-mapping.md +261 -0
  23. package/skills/angular/production-readiness/SKILL.md +469 -0
  24. package/skills/angular/table-state/SKILL.md +429 -0
  25. package/skills/angular/table-state/references/external-atoms-and-app-hook.md +152 -0
  26. package/src/flex-render/context.ts +14 -0
  27. package/src/flex-render/flags.ts +34 -0
  28. package/src/flex-render/flexRenderComponent.ts +288 -0
  29. package/src/flex-render/flexRenderComponentFactory.ts +251 -0
  30. package/src/flex-render/renderer.ts +393 -0
  31. package/src/flex-render/view.ts +226 -0
  32. package/src/flexRender.ts +124 -0
  33. package/src/helpers/cell.ts +104 -0
  34. package/src/helpers/createTableHook.ts +499 -0
  35. package/src/helpers/flexRenderCell.ts +136 -0
  36. package/src/helpers/header.ts +99 -0
  37. package/src/helpers/table.ts +85 -0
  38. package/src/index.ts +23 -70
  39. package/src/injectTable.ts +212 -0
  40. package/src/{lazy-signal-initializer.ts → lazySignalInitializer.ts} +2 -2
  41. package/src/reactivity.ts +105 -0
  42. package/static-functions/index.ts +1 -0
  43. package/static-functions/ng-package.json +6 -0
  44. package/dist/esm2022/flex-render.mjs +0 -150
  45. package/dist/esm2022/index.mjs +0 -48
  46. package/dist/esm2022/lazy-signal-initializer.mjs +0 -43
  47. package/dist/esm2022/proxy.mjs +0 -83
  48. package/dist/esm2022/tanstack-angular-table.mjs +0 -5
  49. package/dist/flex-render.d.ts +0 -31
  50. package/dist/index.d.ts +0 -5
  51. package/dist/lazy-signal-initializer.d.ts +0 -5
  52. package/dist/proxy.d.ts +0 -3
  53. package/src/flex-render.ts +0 -187
  54. package/src/proxy.ts +0 -97
@@ -0,0 +1,393 @@
1
+ import {
2
+ Injector,
3
+ computed,
4
+ effect,
5
+ runInInjectionContext,
6
+ untracked,
7
+ } from '@angular/core'
8
+ import { TanStackTableCellToken } from '../helpers/cell'
9
+ import { TanStackTableHeaderToken } from '../helpers/header'
10
+ import { TanStackTableToken } from '../helpers/table'
11
+ import { FlexRenderComponentProps } from './context'
12
+ import { FlexRenderFlags } from './flags'
13
+ import { flexRenderComponent } from './flexRenderComponent'
14
+ import { FlexRenderComponentFactory } from './flexRenderComponentFactory'
15
+ import {
16
+ FlexRenderComponentView,
17
+ FlexRenderTemplateView,
18
+ mapToFlexRenderTypedContent,
19
+ } from './view'
20
+ import type {
21
+ FlexRenderTypedContent,
22
+ FlexRenderView,
23
+ FlexRenderViewAllowedType,
24
+ } from './view'
25
+ import type { FlexRenderComponent } from './flexRenderComponent'
26
+ import type {
27
+ CellContext,
28
+ CellData,
29
+ HeaderContext,
30
+ RowData,
31
+ TableFeatures,
32
+ } from '@tanstack/table-core'
33
+ import type {
34
+ EffectRef,
35
+ TemplateRef,
36
+ Type,
37
+ ViewContainerRef,
38
+ } from '@angular/core'
39
+
40
+ /**
41
+ * Content supported by the `flexRender` directive when declaring
42
+ * a table column header/cell.
43
+ */
44
+ export type FlexRenderContent<TProps extends NonNullable<unknown>> =
45
+ | string
46
+ | number
47
+ | Type<TProps>
48
+ | FlexRenderComponent<TProps>
49
+ | TemplateRef<{ $implicit: TProps }>
50
+ | null
51
+ | Record<any, any>
52
+ | undefined
53
+
54
+ /**
55
+ * Input content supported by the `flexRender` directives.
56
+ */
57
+ export type FlexRenderInputContent<TProps extends NonNullable<unknown>> =
58
+ | number
59
+ | string
60
+ | ((props: TProps) => FlexRenderContent<TProps>)
61
+ | null
62
+ | undefined
63
+
64
+ /**
65
+ * Options used to create a {@link FlexViewRenderer}.
66
+ *
67
+ * This renderer is designed to be embedded inside a directive/component that owns the
68
+ * `ViewContainerRef` and possibly a fallback `TemplateRef`.
69
+ */
70
+ interface RendererViewOptions<TProps extends NonNullable<unknown>> {
71
+ /**
72
+ * Signal-like getter that returns the latest renderable content.
73
+ */
74
+ content: () => FlexRenderInputContent<TProps>
75
+ /**
76
+ * Signal-like getter returning the current props/context object.
77
+ */
78
+ props: () => NoInfer<TProps>
79
+ /**
80
+ * Getter returning the base injector to evaluate render functions in.
81
+ *
82
+ * If `content` is a function, it will be executed inside this injection context
83
+ * via `runInInjectionContext` so Angular DI works as expected.
84
+ */
85
+ injector: () => Injector
86
+ /**
87
+ * Container that will host the dynamically created view/component.
88
+ */
89
+ viewContainerRef: ViewContainerRef
90
+ /**
91
+ * Fallback template used for primitive rendering.
92
+ *
93
+ * The template is instantiated with `$implicit` set to the primitive string/number.
94
+ */
95
+ templateRef: TemplateRef<unknown>
96
+ }
97
+
98
+ /**
99
+ * Internal view renderer used by Angular TanStack Table to implement `flexRender` directives.
100
+ *
101
+ * @internal Use FlexRender directives instead.
102
+ */
103
+ export class FlexViewRenderer<
104
+ TFeatures extends TableFeatures,
105
+ TRowData extends RowData,
106
+ TValue extends CellData,
107
+ TProps extends
108
+ | NonNullable<unknown>
109
+ | CellContext<TFeatures, TRowData, TValue>
110
+ | HeaderContext<TFeatures, TRowData, TValue>,
111
+ > {
112
+ #renderFlags = FlexRenderFlags.ViewFirstRender
113
+ #renderView: FlexRenderView<
114
+ FlexRenderViewAllowedType,
115
+ FlexRenderTypedContent
116
+ > | null = null
117
+ #currentRenderEffectRef: EffectRef | null = null
118
+ #content: () => FlexRenderInputContent<TProps>
119
+ #props: () => TProps
120
+ #injector: () => Injector
121
+ #viewContainerRef: ViewContainerRef
122
+ #templateRef: TemplateRef<unknown>
123
+ #flexRenderComponentFactory: FlexRenderComponentFactory
124
+
125
+ readonly #getLatestContentValue = () => {
126
+ const content = this.#content()
127
+ const props = this.#props()
128
+ return typeof content !== 'function'
129
+ ? content
130
+ : runInInjectionContext(this.#injector(), () => content(props))
131
+ }
132
+
133
+ readonly #latestContent = computed(() => this.#getLatestContentValue())
134
+
135
+ #getContentValue = computed(() => {
136
+ const latestContent = this.#latestContent()
137
+ return mapToFlexRenderTypedContent(latestContent)
138
+ })
139
+
140
+ constructor(options: RendererViewOptions<TProps>) {
141
+ this.#content = options.content
142
+ this.#props = options.props
143
+ this.#injector = options.injector
144
+ this.#templateRef = options.templateRef
145
+ this.#viewContainerRef = options.viewContainerRef
146
+ this.#flexRenderComponentFactory = new FlexRenderComponentFactory(
147
+ this.#viewContainerRef,
148
+ )
149
+ }
150
+
151
+ mount(): EffectRef {
152
+ let previousContent: FlexRenderInputContent<TProps>
153
+ let previousProps: TProps
154
+
155
+ return effect(() => {
156
+ const props = this.#props()
157
+ const content = this.#content()
158
+
159
+ if (!(this.#renderFlags & FlexRenderFlags.ViewFirstRender)) {
160
+ if (previousContent !== content) {
161
+ this.#renderFlags |= FlexRenderFlags.ContentChanged
162
+ }
163
+ if (previousProps !== props) {
164
+ this.#renderFlags |= FlexRenderFlags.PropsReferenceChanged
165
+ }
166
+ }
167
+
168
+ untracked(() => this.#update())
169
+
170
+ if (FlexRenderFlags.ViewFirstRender & this.#renderFlags) {
171
+ this.#renderFlags &= ~FlexRenderFlags.ViewFirstRender
172
+ }
173
+
174
+ previousContent = content
175
+ previousProps = props
176
+ })
177
+ }
178
+
179
+ destroy(): void {
180
+ if (this.#currentRenderEffectRef) {
181
+ this.#currentRenderEffectRef.destroy()
182
+ this.#currentRenderEffectRef = null
183
+ }
184
+ if (this.#renderView) {
185
+ this.#renderView.unmount()
186
+ this.#renderView = null
187
+ }
188
+ }
189
+
190
+ #update() {
191
+ if (
192
+ this.#renderFlags &
193
+ (FlexRenderFlags.ContentChanged | FlexRenderFlags.ViewFirstRender)
194
+ ) {
195
+ this.#render()
196
+ return
197
+ }
198
+
199
+ if (this.#renderFlags & FlexRenderFlags.PropsReferenceChanged) {
200
+ if (this.#renderView) this.#renderView.updateProps(this.#props())
201
+ this.#renderFlags &= ~FlexRenderFlags.PropsReferenceChanged
202
+ }
203
+
204
+ if (this.#renderFlags & FlexRenderFlags.Dirty) {
205
+ if (this.#renderView) this.#renderView.dirtyCheck()
206
+ this.#renderFlags &= ~FlexRenderFlags.Dirty
207
+ }
208
+ }
209
+
210
+ #render() {
211
+ // When the view is recreated from scratch (content change or first render),
212
+ // we have to destroy the current effect listener since it will be recreated
213
+ // skipping the first call (FlexRenderFlags.RenderEffectChecked)
214
+ if (this.#shouldRecreateEntireView() && this.#currentRenderEffectRef) {
215
+ this.#currentRenderEffectRef.destroy()
216
+ this.#currentRenderEffectRef = null
217
+ this.#renderFlags &= ~FlexRenderFlags.RenderEffectChecked
218
+ }
219
+
220
+ this.#viewContainerRef.clear()
221
+ if (this.#renderView) {
222
+ this.#renderView.unmount()
223
+ this.#renderView = null
224
+ }
225
+
226
+ this.#renderFlags =
227
+ (this.#renderFlags & FlexRenderFlags.ViewFirstRender) |
228
+ (this.#renderFlags & FlexRenderFlags.RenderEffectChecked)
229
+
230
+ const resolvedContent = this.#getContentValue()
231
+ this.#renderView = this.#renderViewByContent(resolvedContent)
232
+ // If the content is a function `content(props)`, we initialize an effect
233
+ // to react to changes. If the current fn uses signals, we will set the DirtySignal flag
234
+ // to re-schedule the component updates
235
+ if (
236
+ !this.#currentRenderEffectRef &&
237
+ typeof untracked(this.#content) === 'function'
238
+ ) {
239
+ this.#currentRenderEffectRef = effect(
240
+ () => {
241
+ this.#latestContent()
242
+ if (!(this.#renderFlags & FlexRenderFlags.RenderEffectChecked)) {
243
+ this.#renderFlags |= FlexRenderFlags.RenderEffectChecked
244
+ return
245
+ }
246
+ this.#renderFlags |= FlexRenderFlags.Dirty
247
+ this.#doCheck()
248
+ },
249
+ { injector: this.#viewContainerRef.injector },
250
+ )
251
+ }
252
+ }
253
+
254
+ #shouldRecreateEntireView() {
255
+ return (
256
+ this.#renderFlags &
257
+ FlexRenderFlags.ContentChanged &
258
+ FlexRenderFlags.ViewFirstRender
259
+ )
260
+ }
261
+
262
+ #doCheck() {
263
+ const latestContent = this.#getContentValue()
264
+ if (latestContent.kind === 'null' || !this.#renderView) {
265
+ this.#renderFlags |= FlexRenderFlags.ContentChanged
266
+ } else {
267
+ const { kind: currentKind } = this.#renderView.content
268
+ if (
269
+ latestContent.kind !== currentKind ||
270
+ !this.#renderView.eq(latestContent)
271
+ ) {
272
+ this.#renderFlags |= FlexRenderFlags.ContentChanged
273
+ }
274
+ this.#renderView.content = latestContent
275
+ }
276
+ this.#update()
277
+ }
278
+
279
+ #renderViewByContent(
280
+ content: FlexRenderTypedContent,
281
+ ): FlexRenderView<FlexRenderViewAllowedType, FlexRenderTypedContent> | null {
282
+ if (content.kind === 'primitive') {
283
+ return this.#renderStringContent(content)
284
+ } else if (content.kind === 'templateRef') {
285
+ return this.#renderTemplateRefContent(content)
286
+ } else if (content.kind === 'flexRenderComponent') {
287
+ return this.#renderComponent(content)
288
+ } else if (content.kind === 'component') {
289
+ return this.#renderCustomComponent(content)
290
+ } else {
291
+ return null
292
+ }
293
+ }
294
+
295
+ #renderStringContent(
296
+ template: Extract<FlexRenderTypedContent, { kind: 'primitive' }>,
297
+ ): FlexRenderTemplateView {
298
+ const context = () => {
299
+ const content = this.#content()
300
+ return typeof content === 'string' || typeof content === 'number'
301
+ ? content
302
+ : runInInjectionContext(this.#injector(), () =>
303
+ content?.(this.#props()),
304
+ )
305
+ }
306
+ const ref = this.#viewContainerRef.createEmbeddedView(this.#templateRef, {
307
+ get $implicit() {
308
+ return context()
309
+ },
310
+ })
311
+ return new FlexRenderTemplateView(template, ref)
312
+ }
313
+
314
+ #renderTemplateRefContent(
315
+ template: Extract<FlexRenderTypedContent, { kind: 'templateRef' }>,
316
+ ): FlexRenderTemplateView {
317
+ const latestContext = () => this.#props()
318
+ const view = this.#viewContainerRef.createEmbeddedView(
319
+ template.content,
320
+ {
321
+ get $implicit() {
322
+ return latestContext()
323
+ },
324
+ },
325
+ { injector: this.#getInjector() },
326
+ )
327
+ return new FlexRenderTemplateView(template, view)
328
+ }
329
+
330
+ #renderComponent(
331
+ flexRenderComponent: Extract<
332
+ FlexRenderTypedContent,
333
+ { kind: 'flexRenderComponent' }
334
+ >,
335
+ ): FlexRenderComponentView {
336
+ const { injector } = flexRenderComponent.content
337
+ const componentInjector = this.#getInjector(injector)
338
+ const view = this.#flexRenderComponentFactory.createComponent(
339
+ flexRenderComponent.content,
340
+ componentInjector,
341
+ )
342
+ return new FlexRenderComponentView(flexRenderComponent, view)
343
+ }
344
+
345
+ #renderCustomComponent(
346
+ component: Extract<FlexRenderTypedContent, { kind: 'component' }>,
347
+ ): FlexRenderComponentView {
348
+ const instance = flexRenderComponent(component.content, {
349
+ inputs: this.#props(),
350
+ })
351
+ const injector = this.#getInjector(instance.injector)
352
+ const view = this.#flexRenderComponentFactory.createComponent(
353
+ instance,
354
+ injector,
355
+ )
356
+ return new FlexRenderComponentView(component, view)
357
+ }
358
+
359
+ #getInjector(parentInjector?: Injector) {
360
+ const getContext = () => this.#props()
361
+ const proxy = new Proxy(this.#props(), {
362
+ get: (_, key) => getContext()[key as keyof typeof _],
363
+ })
364
+
365
+ const staticProviders = []
366
+ if ('table' in proxy) {
367
+ staticProviders.push({
368
+ provide: TanStackTableToken,
369
+ useValue: () => proxy.table,
370
+ })
371
+ }
372
+ if ('cell' in proxy) {
373
+ staticProviders.push({
374
+ provide: TanStackTableCellToken,
375
+ useValue: () => proxy.cell,
376
+ })
377
+ }
378
+ if ('header' in proxy) {
379
+ staticProviders.push({
380
+ provide: TanStackTableHeaderToken,
381
+ useValue: () => proxy.header,
382
+ })
383
+ }
384
+
385
+ return Injector.create({
386
+ parent: parentInjector ?? this.#injector(),
387
+ providers: [
388
+ ...staticProviders,
389
+ { provide: FlexRenderComponentProps, useValue: proxy },
390
+ ],
391
+ })
392
+ }
393
+ }
@@ -0,0 +1,226 @@
1
+ import { TemplateRef, Type } from '@angular/core'
2
+ import { FlexRenderComponentInstance } from './flexRenderComponent'
3
+ import type { FlexRenderComponent } from './flexRenderComponent'
4
+ import type { FlexRenderContent } from './renderer'
5
+ import type { EmbeddedViewRef } from '@angular/core'
6
+ import type { FlexRenderComponentRef } from './flexRenderComponentFactory'
7
+
8
+ export type FlexRenderTypedContent =
9
+ | { kind: 'null' }
10
+ | {
11
+ kind: 'primitive'
12
+ content: string | number | Record<string, any>
13
+ }
14
+ | { kind: 'flexRenderComponent'; content: FlexRenderComponent<unknown> }
15
+ | { kind: 'templateRef'; content: TemplateRef<unknown> }
16
+ | { kind: 'component'; content: Type<unknown> }
17
+
18
+ /**
19
+ * Normalizes arbitrary Angular flex-render content into the renderer's internal
20
+ * tagged representation.
21
+ *
22
+ * This lets the directive decide whether to reuse, update, or recreate an
23
+ * embedded view or component view.
24
+ */
25
+ export function mapToFlexRenderTypedContent(
26
+ content: FlexRenderContent<any>,
27
+ ): FlexRenderTypedContent {
28
+ if (content === null || content === undefined) {
29
+ return { kind: 'null' }
30
+ }
31
+ if (typeof content === 'string' || typeof content === 'number') {
32
+ return { kind: 'primitive', content }
33
+ }
34
+ if (content instanceof FlexRenderComponentInstance) {
35
+ return { kind: 'flexRenderComponent', content }
36
+ } else if (content instanceof TemplateRef) {
37
+ return { kind: 'templateRef', content }
38
+ } else if (content instanceof Type) {
39
+ return { kind: 'component', content }
40
+ } else {
41
+ return { kind: 'primitive', content }
42
+ }
43
+ }
44
+
45
+ export type FlexRenderViewAllowedType =
46
+ | FlexRenderComponentRef<any>
47
+ | EmbeddedViewRef<unknown>
48
+ | null
49
+
50
+ export abstract class FlexRenderView<
51
+ TView extends FlexRenderViewAllowedType,
52
+ TContent extends FlexRenderTypedContent,
53
+ > {
54
+ readonly view: TView
55
+ #previousContent: FlexRenderTypedContent | undefined
56
+ #content: FlexRenderTypedContent
57
+
58
+ protected constructor(
59
+ initialContent: Exclude<FlexRenderTypedContent, { kind: 'null' }>,
60
+ view: TView,
61
+ ) {
62
+ this.#content = initialContent
63
+ this.view = view
64
+ }
65
+
66
+ get previousContent(): FlexRenderTypedContent {
67
+ return this.#previousContent ?? { kind: 'null' }
68
+ }
69
+
70
+ get content() {
71
+ return this.#content
72
+ }
73
+
74
+ set content(content: FlexRenderTypedContent) {
75
+ this.#previousContent = this.#content
76
+ this.#content = content
77
+ }
78
+
79
+ abstract updateProps(props: Record<string, any>): void
80
+
81
+ abstract dirtyCheck(): void
82
+
83
+ abstract onDestroy(callback: Function): void
84
+
85
+ abstract eq(view: TContent): boolean
86
+
87
+ abstract unmount(): void
88
+ }
89
+
90
+ /**
91
+ * Tracks an Angular embedded template view rendered by `FlexRenderDirective`.
92
+ *
93
+ * Template views receive updated props through their proxied context and can be
94
+ * reused while the rendered content kind stays compatible.
95
+ */
96
+ export class FlexRenderTemplateView extends FlexRenderView<
97
+ EmbeddedViewRef<unknown>,
98
+ Extract<FlexRenderTypedContent, { kind: 'primitive' | 'templateRef' }>
99
+ > {
100
+ constructor(
101
+ initialContent: Extract<
102
+ FlexRenderTypedContent,
103
+ { kind: 'primitive' | 'templateRef' }
104
+ >,
105
+ view: EmbeddedViewRef<unknown>,
106
+ ) {
107
+ super(initialContent, view)
108
+ }
109
+
110
+ override updateProps(props: Record<string, any>) {
111
+ this.view.markForCheck()
112
+ }
113
+
114
+ override dirtyCheck() {
115
+ // Basically a no-op. When the view is created via EmbeddedViewRef, we don't need to do any manual update
116
+ // since this type of content has a proxy as a context, then every time the root component is checked for changes,
117
+ // the property getter will be re-evaluated.
118
+ //
119
+ // If in a future we need to manually mark the view as dirty, just uncomment next line
120
+ // this.view.markForCheck()
121
+ }
122
+
123
+ override unmount() {
124
+ this.view.destroy()
125
+ }
126
+
127
+ override onDestroy(callback: Function) {
128
+ this.view.onDestroy(callback)
129
+ }
130
+
131
+ override eq(
132
+ compare: Extract<
133
+ FlexRenderTypedContent,
134
+ { kind: 'primitive' | 'templateRef' }
135
+ >,
136
+ ): boolean {
137
+ return (
138
+ (this.content.kind === 'primitive' &&
139
+ compare.kind === 'primitive' &&
140
+ this.content.content === compare.content) ||
141
+ (this.content.kind === 'templateRef' &&
142
+ compare.kind === 'templateRef' &&
143
+ this.content.content === compare.content)
144
+ )
145
+ }
146
+ }
147
+
148
+ /**
149
+ * Tracks an Angular component view rendered by `FlexRenderDirective`.
150
+ *
151
+ * Component views own input/output updates for `flexRenderComponent(...)`
152
+ * results and component classes rendered directly from column definitions.
153
+ */
154
+ export class FlexRenderComponentView extends FlexRenderView<
155
+ FlexRenderComponentRef<unknown>,
156
+ Extract<FlexRenderTypedContent, { kind: 'component' | 'flexRenderComponent' }>
157
+ > {
158
+ constructor(
159
+ initialContent: Extract<
160
+ FlexRenderTypedContent,
161
+ { kind: 'component' | 'flexRenderComponent' }
162
+ >,
163
+ view: FlexRenderComponentRef<unknown>,
164
+ ) {
165
+ super(initialContent, view)
166
+ }
167
+
168
+ override updateProps(props: Record<string, any>) {
169
+ switch (this.content.kind) {
170
+ case 'component': {
171
+ this.view.setInputs(props)
172
+ break
173
+ }
174
+ case 'flexRenderComponent': {
175
+ // No-op. When FlexRenderFlags.PropsReferenceChanged is set,
176
+ // FlexRenderComponent will be updated into `dirtyCheck`.
177
+ break
178
+ }
179
+ }
180
+ }
181
+
182
+ override dirtyCheck() {
183
+ switch (this.content.kind) {
184
+ case 'component': {
185
+ // Component context is currently valuated with the cell context. Since it's reference
186
+ // shouldn't change, we force mark the component as dirty in order to re-evaluate function invocation in view.
187
+ // NOTE: this should behave like having a component with ChangeDetectionStrategy.Default
188
+ this.view.markAsDirty()
189
+ break
190
+ }
191
+ case 'flexRenderComponent': {
192
+ // Given context instance will always have a different reference than the previous one,
193
+ // so instead of recreating the entire view, we will only update the current view
194
+ if (this.view.eqType(this.content.content)) {
195
+ this.view.update(this.content.content)
196
+ }
197
+ this.view.markAsDirty()
198
+ break
199
+ }
200
+ }
201
+ }
202
+
203
+ override unmount() {
204
+ this.view.componentRef.destroy()
205
+ }
206
+
207
+ override onDestroy(callback: Function) {
208
+ this.view.componentRef.onDestroy(callback)
209
+ }
210
+
211
+ override eq(
212
+ compare: Extract<
213
+ FlexRenderTypedContent,
214
+ { kind: 'component' | 'flexRenderComponent' }
215
+ >,
216
+ ): boolean {
217
+ return (
218
+ (this.content.kind === 'component' &&
219
+ compare.kind === 'component' &&
220
+ this.content.content === compare.content) ||
221
+ (this.content.kind === 'flexRenderComponent' &&
222
+ compare.kind === 'flexRenderComponent' &&
223
+ this.content.content.component === compare.content.component)
224
+ )
225
+ }
226
+ }