@tanstack/angular-table 9.0.0-alpha.10 → 9.0.0-alpha.11
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/dist/fesm2022/tanstack-angular-table.mjs +1363 -247
- package/dist/fesm2022/tanstack-angular-table.mjs.map +1 -1
- package/dist/types/tanstack-angular-table.d.ts +767 -0
- package/package.json +27 -17
- package/src/angularReactivityFeature.ts +210 -0
- package/src/flex-render/context.ts +14 -0
- package/src/flex-render/flags.ts +34 -0
- package/src/flex-render/flexRenderComponent.ts +288 -0
- package/src/flex-render/flexRenderComponentFactory.ts +241 -0
- package/src/flex-render/renderer.ts +376 -0
- package/src/flex-render/view.ts +165 -0
- package/src/flexRender.ts +124 -0
- package/src/helpers/cell.ts +104 -0
- package/src/helpers/createTableHook.ts +480 -0
- package/src/helpers/flexRenderCell.ts +136 -0
- package/src/helpers/header.ts +99 -0
- package/src/helpers/table.ts +87 -0
- package/src/index.ts +17 -70
- package/src/injectTable.ts +123 -0
- package/src/{lazy-signal-initializer.ts → lazySignalInitializer.ts} +2 -2
- package/src/reactivityUtils.ts +232 -0
- package/dist/esm2022/flex-render.mjs +0 -148
- package/dist/esm2022/index.mjs +0 -48
- package/dist/esm2022/lazy-signal-initializer.mjs +0 -43
- package/dist/esm2022/proxy.mjs +0 -83
- package/dist/esm2022/tanstack-angular-table.mjs +0 -5
- package/dist/flex-render.d.ts +0 -30
- package/dist/index.d.ts +0 -5
- package/dist/lazy-signal-initializer.d.ts +0 -5
- package/dist/proxy.d.ts +0 -3
- package/src/flex-render.ts +0 -184
- package/src/proxy.ts +0 -97
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/angular-table",
|
|
3
|
-
"version": "9.0.0-alpha.
|
|
3
|
+
"version": "9.0.0-alpha.11",
|
|
4
4
|
"description": "Headless UI for building powerful tables & datagrids for Angular.",
|
|
5
5
|
"author": "Tanner Linsley",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
|
-
"url": "https://github.com/TanStack/table.git",
|
|
9
|
+
"url": "git+https://github.com/TanStack/table.git",
|
|
10
10
|
"directory": "packages/angular-table"
|
|
11
11
|
},
|
|
12
12
|
"homepage": "https://tanstack.com/table",
|
|
@@ -21,13 +21,11 @@
|
|
|
21
21
|
"datagrid"
|
|
22
22
|
],
|
|
23
23
|
"type": "module",
|
|
24
|
-
"module": "dist/
|
|
25
|
-
"types": "dist/
|
|
24
|
+
"module": "dist/fesm2022/tanstack-angular-table.mjs",
|
|
25
|
+
"types": "dist/types/tanstack-angular-table.d.ts",
|
|
26
26
|
"exports": {
|
|
27
27
|
".": {
|
|
28
|
-
"types": "./dist/
|
|
29
|
-
"esm": "./dist/esm2022/index.mjs",
|
|
30
|
-
"esm2022": "./dist/esm2022/index.mjs",
|
|
28
|
+
"types": "./dist/types/tanstack-angular-table.d.ts",
|
|
31
29
|
"default": "./dist/fesm2022/tanstack-angular-table.mjs"
|
|
32
30
|
},
|
|
33
31
|
"./package.json": {
|
|
@@ -35,26 +33,38 @@
|
|
|
35
33
|
}
|
|
36
34
|
},
|
|
37
35
|
"engines": {
|
|
38
|
-
"node": ">=
|
|
36
|
+
"node": ">=18"
|
|
39
37
|
},
|
|
40
38
|
"files": [
|
|
41
39
|
"dist",
|
|
42
40
|
"src"
|
|
43
41
|
],
|
|
44
42
|
"dependencies": {
|
|
45
|
-
"
|
|
46
|
-
"
|
|
43
|
+
"@tanstack/angular-store": "^0.8.0",
|
|
44
|
+
"tslib": "^2.8.1",
|
|
45
|
+
"@tanstack/table-core": "9.0.0-alpha.11"
|
|
47
46
|
},
|
|
48
47
|
"devDependencies": {
|
|
49
|
-
"@analogjs/vite-plugin-angular": "^
|
|
50
|
-
"@angular
|
|
51
|
-
"@angular/
|
|
52
|
-
"@angular/platform-browser
|
|
53
|
-
"ng-packagr": "^
|
|
48
|
+
"@analogjs/vite-plugin-angular": "^2.2.2",
|
|
49
|
+
"@analogjs/vitest-angular": "^2.2.2",
|
|
50
|
+
"@angular/core": "^21.1.1",
|
|
51
|
+
"@angular/platform-browser": "^21.1.1",
|
|
52
|
+
"ng-packagr": "^21.1.0",
|
|
53
|
+
"typescript": "5.9.3"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
|
-
"@angular/core": ">=
|
|
56
|
+
"@angular/core": ">=19"
|
|
57
57
|
},
|
|
58
58
|
"sideEffects": false,
|
|
59
|
-
"scripts": {
|
|
59
|
+
"scripts": {
|
|
60
|
+
"build": "ng-packagr -p ng-package.json -c tsconfig.build.json && rimraf ./dist/package.json",
|
|
61
|
+
"build:types": "tsc --emitDeclarationOnly",
|
|
62
|
+
"clean": "rimraf ./build && rimraf ./dist",
|
|
63
|
+
"test:build": "publint --strict",
|
|
64
|
+
"test:eslint": "eslint ./src",
|
|
65
|
+
"test:lib": "vitest",
|
|
66
|
+
"test:benchmark": "vitest bench",
|
|
67
|
+
"test:lib:dev": "vitest --watch",
|
|
68
|
+
"test:types": "tsc && vitest --typecheck"
|
|
69
|
+
}
|
|
60
70
|
}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { computed, signal } from '@angular/core'
|
|
2
|
+
import { setReactivePropertiesOnObject } from './reactivityUtils'
|
|
3
|
+
import type { Signal } from '@angular/core'
|
|
4
|
+
import type {
|
|
5
|
+
RowData,
|
|
6
|
+
Table,
|
|
7
|
+
TableFeature,
|
|
8
|
+
TableFeatures,
|
|
9
|
+
} from '@tanstack/table-core'
|
|
10
|
+
|
|
11
|
+
declare module '@tanstack/table-core' {
|
|
12
|
+
interface TableOptions_Plugins<
|
|
13
|
+
TFeatures extends TableFeatures,
|
|
14
|
+
TData extends RowData,
|
|
15
|
+
> extends TableOptions_AngularReactivity {}
|
|
16
|
+
|
|
17
|
+
interface Table_Plugins<
|
|
18
|
+
TFeatures extends TableFeatures,
|
|
19
|
+
TData extends RowData,
|
|
20
|
+
> extends Table_AngularReactivity<TFeatures, TData> {}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Predicate used to skip/ignore a property name when applying Angular reactivity.
|
|
25
|
+
*
|
|
26
|
+
* Returning `true` means the property should NOT be wrapped/made reactive.
|
|
27
|
+
*/
|
|
28
|
+
type SkipPropertyFn = (property: string) => boolean
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Fine-grained configuration for Angular reactivity.
|
|
32
|
+
*
|
|
33
|
+
* Each key controls whether prototype methods/getters on the corresponding TanStack Table
|
|
34
|
+
* objects are wrapped with signal-aware access.
|
|
35
|
+
*
|
|
36
|
+
* - `true` enables wrapping using the default skip rules.
|
|
37
|
+
* - `false` disables wrapping entirely for that object type.
|
|
38
|
+
* - a function allows customizing the skip rules (see {@link SkipPropertyFn}).
|
|
39
|
+
*/
|
|
40
|
+
export interface AngularReactivityFlags {
|
|
41
|
+
/** Controls reactive wrapping for `Header` instances. */
|
|
42
|
+
header: boolean | SkipPropertyFn
|
|
43
|
+
/** Controls reactive wrapping for `Column` instances. */
|
|
44
|
+
column: boolean | SkipPropertyFn
|
|
45
|
+
/** Controls reactive wrapping for `Row` instances. */
|
|
46
|
+
row: boolean | SkipPropertyFn
|
|
47
|
+
/** Controls reactive wrapping for `Cell` instances. */
|
|
48
|
+
cell: boolean | SkipPropertyFn
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Table option extension for Angular reactivity.
|
|
53
|
+
*
|
|
54
|
+
* Available on `createTable` options via module augmentation in this file.
|
|
55
|
+
*/
|
|
56
|
+
interface TableOptions_AngularReactivity {
|
|
57
|
+
/**
|
|
58
|
+
* Enables/disables and configures Angular reactivity on table-related prototypes.
|
|
59
|
+
*
|
|
60
|
+
* If omitted, defaults are provided by the feature.
|
|
61
|
+
*/
|
|
62
|
+
reactivity?: Partial<AngularReactivityFlags>
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Table API extension for Angular reactivity.
|
|
67
|
+
*
|
|
68
|
+
* Added to the table instance via module augmentation.
|
|
69
|
+
*/
|
|
70
|
+
interface Table_AngularReactivity<
|
|
71
|
+
TFeatures extends TableFeatures,
|
|
72
|
+
TData extends RowData,
|
|
73
|
+
> {
|
|
74
|
+
/**
|
|
75
|
+
* Returns a table signal that updates whenever the table state or options changes.
|
|
76
|
+
*/
|
|
77
|
+
get: Signal<Table<TFeatures, TData>>
|
|
78
|
+
/**
|
|
79
|
+
* Sets the reactive notifier that powers {@link get}.
|
|
80
|
+
*
|
|
81
|
+
* @internal Used by the Angular table adapter to connect its notifier to the core table.
|
|
82
|
+
*/
|
|
83
|
+
setTableNotifier: (signal: Signal<Table<TFeatures, TData>>) => void
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Type map describing what this feature adds to TanStack Table constructors.
|
|
88
|
+
*/
|
|
89
|
+
interface AngularReactivityFeatureConstructors<
|
|
90
|
+
TFeatures extends TableFeatures,
|
|
91
|
+
TData extends RowData,
|
|
92
|
+
> {
|
|
93
|
+
TableOptions: TableOptions_AngularReactivity
|
|
94
|
+
Table: Table_AngularReactivity<TFeatures, TData>
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Resolves the user-provided `reactivity.*` config to a skip predicate.
|
|
99
|
+
*
|
|
100
|
+
* - `false` is handled by callers (feature method returns early)
|
|
101
|
+
* - `true` selects the default predicate
|
|
102
|
+
* - a function overrides the default predicate
|
|
103
|
+
*/
|
|
104
|
+
const getUserSkipPropertyFn = (
|
|
105
|
+
value: undefined | null | boolean | SkipPropertyFn,
|
|
106
|
+
defaultPropertyFn: SkipPropertyFn,
|
|
107
|
+
) => {
|
|
108
|
+
if (typeof value === 'boolean') {
|
|
109
|
+
return defaultPropertyFn
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return value ?? defaultPropertyFn
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function constructAngularReactivityFeature<
|
|
116
|
+
TFeatures extends TableFeatures,
|
|
117
|
+
TData extends RowData,
|
|
118
|
+
>(): TableFeature<AngularReactivityFeatureConstructors<TFeatures, TData>> {
|
|
119
|
+
return {
|
|
120
|
+
getDefaultTableOptions(table) {
|
|
121
|
+
return {
|
|
122
|
+
reactivity: {
|
|
123
|
+
header: true,
|
|
124
|
+
column: true,
|
|
125
|
+
row: true,
|
|
126
|
+
cell: true,
|
|
127
|
+
},
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
constructTableAPIs: (table) => {
|
|
131
|
+
const rootNotifier = signal<Signal<any> | null>(null)
|
|
132
|
+
table.setTableNotifier = (notifier) => rootNotifier.set(notifier)
|
|
133
|
+
table.get = computed(() => rootNotifier()!(), { equal: () => false })
|
|
134
|
+
setReactivePropertiesOnObject(table.get, table, {
|
|
135
|
+
overridePrototype: false,
|
|
136
|
+
skipProperty: skipBaseProperties,
|
|
137
|
+
})
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
assignCellPrototype: (prototype, table) => {
|
|
141
|
+
if (table.options.reactivity?.cell === false) {
|
|
142
|
+
return
|
|
143
|
+
}
|
|
144
|
+
setReactivePropertiesOnObject(table.get, prototype, {
|
|
145
|
+
skipProperty: getUserSkipPropertyFn(
|
|
146
|
+
table.options.reactivity?.cell,
|
|
147
|
+
skipBaseProperties,
|
|
148
|
+
),
|
|
149
|
+
overridePrototype: true,
|
|
150
|
+
})
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
assignColumnPrototype: (prototype, table) => {
|
|
154
|
+
if (table.options.reactivity?.column === false) {
|
|
155
|
+
return
|
|
156
|
+
}
|
|
157
|
+
setReactivePropertiesOnObject(table.get, prototype, {
|
|
158
|
+
skipProperty: getUserSkipPropertyFn(
|
|
159
|
+
table.options.reactivity?.cell,
|
|
160
|
+
skipBaseProperties,
|
|
161
|
+
),
|
|
162
|
+
overridePrototype: true,
|
|
163
|
+
})
|
|
164
|
+
},
|
|
165
|
+
|
|
166
|
+
assignHeaderPrototype: (prototype, table) => {
|
|
167
|
+
if (table.options.reactivity?.header === false) {
|
|
168
|
+
return
|
|
169
|
+
}
|
|
170
|
+
setReactivePropertiesOnObject(table.get, prototype, {
|
|
171
|
+
skipProperty: getUserSkipPropertyFn(
|
|
172
|
+
table.options.reactivity?.cell,
|
|
173
|
+
skipBaseProperties,
|
|
174
|
+
),
|
|
175
|
+
overridePrototype: true,
|
|
176
|
+
})
|
|
177
|
+
},
|
|
178
|
+
|
|
179
|
+
assignRowPrototype: (prototype, table) => {
|
|
180
|
+
if (table.options.reactivity?.row === false) {
|
|
181
|
+
return
|
|
182
|
+
}
|
|
183
|
+
setReactivePropertiesOnObject(table.get, prototype, {
|
|
184
|
+
skipProperty: getUserSkipPropertyFn(
|
|
185
|
+
table.options.reactivity?.cell,
|
|
186
|
+
skipBaseProperties,
|
|
187
|
+
),
|
|
188
|
+
overridePrototype: true,
|
|
189
|
+
})
|
|
190
|
+
},
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export const angularReactivityFeature = constructAngularReactivityFeature()
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Default predicate used to skip base/non-reactive properties.
|
|
198
|
+
*/
|
|
199
|
+
function skipBaseProperties(property: string): boolean {
|
|
200
|
+
return (
|
|
201
|
+
// equals `getContext`
|
|
202
|
+
property === 'getContext' ||
|
|
203
|
+
// start with `_`
|
|
204
|
+
property[0] === '_' ||
|
|
205
|
+
// doesn't start with `get`, but faster
|
|
206
|
+
!(property[0] === 'g' && property[1] === 'e' && property[2] === 't') ||
|
|
207
|
+
// ends with `Handler`
|
|
208
|
+
property.endsWith('Handler')
|
|
209
|
+
)
|
|
210
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { InjectionToken, inject } from '@angular/core'
|
|
2
|
+
|
|
3
|
+
export const FlexRenderComponentProps = new InjectionToken<
|
|
4
|
+
NonNullable<unknown>
|
|
5
|
+
>('[@tanstack/angular-table] Flex render component context props')
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Inject the flex render context props.
|
|
9
|
+
*
|
|
10
|
+
* Can be used in components rendered via FlexRender directives.
|
|
11
|
+
*/
|
|
12
|
+
export function injectFlexRenderContext<T extends NonNullable<unknown>>(): T {
|
|
13
|
+
return inject<T>(FlexRenderComponentProps)
|
|
14
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flags used to manage and optimize the rendering lifecycle of the content of the cell
|
|
3
|
+
* while using {@link FlexViewRenderer}.
|
|
4
|
+
*/
|
|
5
|
+
export const FlexRenderFlags = {
|
|
6
|
+
/**
|
|
7
|
+
* Indicates that the view is being created for the first time or will be cleared during the next update phase.
|
|
8
|
+
* This is the initial state and will transition after the first ngDoCheck.
|
|
9
|
+
*/
|
|
10
|
+
ViewFirstRender: 1 << 0,
|
|
11
|
+
/**
|
|
12
|
+
* Indicates the `content` property has been modified or the view requires a complete re-render.
|
|
13
|
+
* When this flag is enabled, the view will be cleared and recreated from scratch.
|
|
14
|
+
*/
|
|
15
|
+
ContentChanged: 1 << 1,
|
|
16
|
+
/**
|
|
17
|
+
* Indicates that the `props` property reference has changed.
|
|
18
|
+
* When this flag is enabled, the view context is updated based on the type of the content.
|
|
19
|
+
*
|
|
20
|
+
* For Component view, inputs will be updated and view will be marked as dirty.
|
|
21
|
+
* For TemplateRef and primitive values, view will be marked as dirty
|
|
22
|
+
*/
|
|
23
|
+
PropsReferenceChanged: 1 << 2,
|
|
24
|
+
/**
|
|
25
|
+
* Indicates that the current rendered view needs to be checked for changes.
|
|
26
|
+
* This will be set to true when `content(props)` result has changed or during
|
|
27
|
+
* forced update
|
|
28
|
+
*/
|
|
29
|
+
Dirty: 1 << 3,
|
|
30
|
+
/**
|
|
31
|
+
* Indicates that the first render effect has been checked at least one time.
|
|
32
|
+
*/
|
|
33
|
+
RenderEffectChecked: 1 << 4,
|
|
34
|
+
} as const
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import { reflectComponentType } from '@angular/core'
|
|
2
|
+
import type {
|
|
3
|
+
Binding,
|
|
4
|
+
ComponentMirror,
|
|
5
|
+
Injector,
|
|
6
|
+
InputSignal,
|
|
7
|
+
OutputEmitterRef,
|
|
8
|
+
Type,
|
|
9
|
+
createComponent,
|
|
10
|
+
} from '@angular/core'
|
|
11
|
+
|
|
12
|
+
type CreateComponentOptions = Parameters<typeof createComponent>[1]
|
|
13
|
+
type CreateComponentBindings = CreateComponentOptions['bindings']
|
|
14
|
+
type CreateComponentDirectives = CreateComponentOptions['directives']
|
|
15
|
+
|
|
16
|
+
interface FlexRenderOptions<
|
|
17
|
+
TInputs extends Record<string, any>,
|
|
18
|
+
TOutputs extends Record<string, any>,
|
|
19
|
+
> {
|
|
20
|
+
/**
|
|
21
|
+
* Native Angular bindings applied at component creation time via `createComponent`.
|
|
22
|
+
* Use this option to set inputs, outputs, or two-way bindings at creation time.
|
|
23
|
+
* Shouldn't be used together with {@link FlexRenderOptions#inputs} or {@link FlexRenderOptions#outputs} option.
|
|
24
|
+
*
|
|
25
|
+
* Binding input/outputs at creation time: {@link https://angular.dev/guide/components/programmatic-rendering#binding-inputs-outputs-and-setting-host-directives-at-creation}
|
|
26
|
+
*
|
|
27
|
+
* Two-way binding: {@link https://angular.dev/api/core/twoWayBinding}
|
|
28
|
+
*
|
|
29
|
+
* Output binding: {@link https://angular.dev/api/core/outputBinding}
|
|
30
|
+
*
|
|
31
|
+
* Input binding: {@link https://angular.dev/api/core/inputBinding}
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```ts
|
|
35
|
+
* import {flexRenderComponent} from '@tanstack/angular-table';
|
|
36
|
+
* flexRenderComponent(MyComponent, {
|
|
37
|
+
* bindings: [
|
|
38
|
+
* // Will update `value` input every time `mySignalValue` changes
|
|
39
|
+
* inputBinding('value', mySignalValue),
|
|
40
|
+
* // Set myProperty to 1 when the component is created
|
|
41
|
+
* inputBinding('myProperty', () => 1),
|
|
42
|
+
* // Callback called every time `valueChange` output emit
|
|
43
|
+
* outputBinding('valueChange', value => {
|
|
44
|
+
* console.log("my value changed to", value)
|
|
45
|
+
* }),
|
|
46
|
+
* // Two-way binding between `value` input and `valueChange` output
|
|
47
|
+
* // Useful while using `model` inputs.
|
|
48
|
+
* twoWayBinding('value', mySignal)
|
|
49
|
+
* ]
|
|
50
|
+
* })
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
readonly bindings?: Array<Binding>
|
|
54
|
+
/**
|
|
55
|
+
* Directives to apply to the component at creation time.
|
|
56
|
+
*
|
|
57
|
+
* Binding directives at creation time: {@link https://angular.dev/guide/components/programmatic-rendering#binding-inputs-outputs-and-setting-host-directives-at-creation}
|
|
58
|
+
*
|
|
59
|
+
* Two-way binding: {@link https://angular.dev/api/core/twoWayBinding}
|
|
60
|
+
*
|
|
61
|
+
* Output binding: {@link https://angular.dev/api/core/outputBinding}
|
|
62
|
+
*
|
|
63
|
+
* Input binding: {@link https://angular.dev/api/core/inputBinding}
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```ts
|
|
67
|
+
* import {flexRenderComponent} from '@tanstack/angular-table';
|
|
68
|
+
* flexRenderComponent(MyComponent, {
|
|
69
|
+
* bindings: [
|
|
70
|
+
* // ...
|
|
71
|
+
* ],
|
|
72
|
+
* directives: [
|
|
73
|
+
* DirectiveA,
|
|
74
|
+
* {
|
|
75
|
+
* type: DirectiveB,
|
|
76
|
+
* bindings: [
|
|
77
|
+
* inputBinding('value', mySignalValue),
|
|
78
|
+
* // ...
|
|
79
|
+
* ]
|
|
80
|
+
* }
|
|
81
|
+
* ]
|
|
82
|
+
* })
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
readonly directives?: CreateComponentDirectives
|
|
86
|
+
/**
|
|
87
|
+
* Component instance inputs.
|
|
88
|
+
*
|
|
89
|
+
* These values are assigned after the component has been created using
|
|
90
|
+
* [componentRef.setInput API](https://angular.dev/api/core/ComponentRef#setInput).
|
|
91
|
+
*
|
|
92
|
+
* Shouldn't be used together with {@link FlexRenderOptions#bindings} option
|
|
93
|
+
*/
|
|
94
|
+
readonly inputs?: TInputs
|
|
95
|
+
/**
|
|
96
|
+
* Component instance outputs.
|
|
97
|
+
*
|
|
98
|
+
* Outputs are wired imperatively after component creation using {@link OutputEmitterRef#subscribe}.
|
|
99
|
+
*
|
|
100
|
+
* Shouldn't be used together with {@link FlexRenderOptions#bindings} option
|
|
101
|
+
*/
|
|
102
|
+
readonly outputs?: TOutputs
|
|
103
|
+
/**
|
|
104
|
+
* Optional {@link Injector} that will be used when rendering the component
|
|
105
|
+
*/
|
|
106
|
+
readonly injector?: Injector
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
type Inputs<T> = {
|
|
110
|
+
[K in keyof T as T[K] extends InputSignal<infer R>
|
|
111
|
+
? K
|
|
112
|
+
: never]?: T[K] extends InputSignal<infer R> ? R : never
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
type Outputs<T> = {
|
|
116
|
+
[K in keyof T as T[K] extends OutputEmitterRef<infer R>
|
|
117
|
+
? K
|
|
118
|
+
: never]?: T[K] extends OutputEmitterRef<infer R>
|
|
119
|
+
? OutputEmitterRef<R>['emit']
|
|
120
|
+
: never
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Helper function to create a {@link FlexRenderComponent} instance, with better type-safety.
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* ```ts
|
|
128
|
+
* import {flexRenderComponent} from '@tanstack/angular-table'
|
|
129
|
+
* import {inputBinding, outputBinding} from '@angular/core';
|
|
130
|
+
*
|
|
131
|
+
* const columns = [
|
|
132
|
+
* {
|
|
133
|
+
* cell: ({ row }) => {
|
|
134
|
+
* return flexRenderComponent(MyComponent, {
|
|
135
|
+
* inputs: { value: mySignalValue() },
|
|
136
|
+
* outputs: { valueChange: (val) => {} }
|
|
137
|
+
* // or using angular native createComponent#binding api
|
|
138
|
+
* bindings: [
|
|
139
|
+
* inputBinding('value', mySignalValue),
|
|
140
|
+
* outputBinding('valueChange', value => {
|
|
141
|
+
* console.log("my value changed to", value)
|
|
142
|
+
* })
|
|
143
|
+
* ]
|
|
144
|
+
* })
|
|
145
|
+
* },
|
|
146
|
+
* },
|
|
147
|
+
* ]
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
export function flexRenderComponent<TComponent = any>(
|
|
151
|
+
component: Type<TComponent>,
|
|
152
|
+
options?: FlexRenderOptions<Inputs<TComponent>, Outputs<TComponent>>,
|
|
153
|
+
): FlexRenderComponent<TComponent> {
|
|
154
|
+
const { inputs, injector, outputs, directives, bindings } = options ?? {}
|
|
155
|
+
return new FlexRenderComponentInstance(
|
|
156
|
+
component,
|
|
157
|
+
inputs,
|
|
158
|
+
injector,
|
|
159
|
+
outputs,
|
|
160
|
+
directives,
|
|
161
|
+
bindings,
|
|
162
|
+
)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Wrapper interface for a component that will be used as content for {@link FlexRenderDirective}.
|
|
167
|
+
* Can be created using {@link flexRenderComponent} helper.
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
*
|
|
171
|
+
* ```ts
|
|
172
|
+
* import {flexRenderComponent} from '@tanstack/angular-table'
|
|
173
|
+
*
|
|
174
|
+
* // Usage in cell/header/footer definition
|
|
175
|
+
* const columns = [
|
|
176
|
+
* {
|
|
177
|
+
* cell: ({ row }) => {
|
|
178
|
+
* return flexRenderComponent(MyComponent, {
|
|
179
|
+
* inputs: { value: mySignalValue() },
|
|
180
|
+
* outputs: { valueChange: (val) => {} }
|
|
181
|
+
* // or using angular createComponent#bindings api
|
|
182
|
+
* bindings: [
|
|
183
|
+
* inputBinding('value', mySignalValue),
|
|
184
|
+
* outputBinding('valueChange', value => {
|
|
185
|
+
* console.log("my value changed to", value)
|
|
186
|
+
* })
|
|
187
|
+
* ]
|
|
188
|
+
* })
|
|
189
|
+
* },
|
|
190
|
+
* },
|
|
191
|
+
* ]
|
|
192
|
+
*
|
|
193
|
+
* import {input, output} from '@angular/core';
|
|
194
|
+
*
|
|
195
|
+
* @Component({
|
|
196
|
+
* selector: 'my-component',
|
|
197
|
+
* })
|
|
198
|
+
* class MyComponent {
|
|
199
|
+
* readonly value = input(0);
|
|
200
|
+
* readonly valueChange = output<number>();
|
|
201
|
+
* }
|
|
202
|
+
*
|
|
203
|
+
* ```
|
|
204
|
+
*/
|
|
205
|
+
export interface FlexRenderComponent<TComponent = any> {
|
|
206
|
+
/**
|
|
207
|
+
* The component type
|
|
208
|
+
*/
|
|
209
|
+
readonly component: Type<TComponent>
|
|
210
|
+
/**
|
|
211
|
+
* Reflected metadata about the component.
|
|
212
|
+
*/
|
|
213
|
+
readonly mirror: ComponentMirror<TComponent>
|
|
214
|
+
/**
|
|
215
|
+
* List of allowed input names.
|
|
216
|
+
*/
|
|
217
|
+
readonly allowedInputNames: Array<string>
|
|
218
|
+
/**
|
|
219
|
+
* List of allowed output names.
|
|
220
|
+
*/
|
|
221
|
+
readonly allowedOutputNames: Array<string>
|
|
222
|
+
/**
|
|
223
|
+
* Component instance outputs. Subscribed via {@link OutputEmitterRef#subscribe}
|
|
224
|
+
*
|
|
225
|
+
* @see {@link FlexRenderOptions#outputs}
|
|
226
|
+
*/
|
|
227
|
+
readonly outputs?: Outputs<TComponent>
|
|
228
|
+
/**
|
|
229
|
+
* Component instance inputs. Set via [componentRef.setInput API](https://angular.dev/api/core/ComponentRef#setInput))
|
|
230
|
+
*
|
|
231
|
+
* @see {@link FlexRenderOptions#inputs}
|
|
232
|
+
*/
|
|
233
|
+
readonly inputs?: Inputs<TComponent>
|
|
234
|
+
/**
|
|
235
|
+
* Optional {@link Injector} that will be used when rendering the component.
|
|
236
|
+
*
|
|
237
|
+
* @see {@link FlexRenderOptions#injector}
|
|
238
|
+
*/
|
|
239
|
+
readonly injector?: Injector
|
|
240
|
+
/**
|
|
241
|
+
* Bindings to apply to the root component
|
|
242
|
+
*
|
|
243
|
+
* @see {@link FlexRenderOptions#bindings}
|
|
244
|
+
*/
|
|
245
|
+
bindings?: CreateComponentBindings
|
|
246
|
+
/**
|
|
247
|
+
* Directives that should be applied to the component.
|
|
248
|
+
*
|
|
249
|
+
* @see {FlexRenderOptions#directives}
|
|
250
|
+
*/
|
|
251
|
+
directives?: CreateComponentDirectives
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Wrapper class for a component that will be used as content for {@link FlexRenderDirective}
|
|
256
|
+
*
|
|
257
|
+
* Prefer {@link flexRenderComponent} helper for better type-safety
|
|
258
|
+
*/
|
|
259
|
+
export class FlexRenderComponentInstance<
|
|
260
|
+
TComponent = any,
|
|
261
|
+
> implements FlexRenderComponent<TComponent> {
|
|
262
|
+
readonly mirror: ComponentMirror<TComponent>
|
|
263
|
+
readonly allowedInputNames: Array<string> = []
|
|
264
|
+
readonly allowedOutputNames: Array<string> = []
|
|
265
|
+
|
|
266
|
+
constructor(
|
|
267
|
+
readonly component: Type<TComponent>,
|
|
268
|
+
readonly inputs?: Inputs<TComponent>,
|
|
269
|
+
readonly injector?: Injector,
|
|
270
|
+
readonly outputs?: Outputs<TComponent>,
|
|
271
|
+
readonly directives?: CreateComponentDirectives,
|
|
272
|
+
readonly bindings?: CreateComponentBindings,
|
|
273
|
+
) {
|
|
274
|
+
const mirror = reflectComponentType(component)
|
|
275
|
+
if (!mirror) {
|
|
276
|
+
throw new Error(
|
|
277
|
+
`[@tanstack-table/angular] The provided symbol is not a component`,
|
|
278
|
+
)
|
|
279
|
+
}
|
|
280
|
+
this.mirror = mirror
|
|
281
|
+
for (const input of this.mirror.inputs) {
|
|
282
|
+
this.allowedInputNames.push(input.propName)
|
|
283
|
+
}
|
|
284
|
+
for (const output of this.mirror.outputs) {
|
|
285
|
+
this.allowedOutputNames.push(output.propName)
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|