diff --git a/packages/admin-ui/src/lib/core/src/shared/components/data-table-2/data-table2.component.html b/packages/admin-ui/src/lib/core/src/shared/components/data-table-2/data-table2.component.html
index 4355b17e85..3d6cf323f0 100644
--- a/packages/admin-ui/src/lib/core/src/shared/components/data-table-2/data-table2.component.html
+++ b/packages/admin-ui/src/lib/core/src/shared/components/data-table-2/data-table2.component.html
@@ -111,13 +111,14 @@
diff --git a/packages/admin-ui/src/lib/core/src/shared/components/data-table-2/data-table2.component.ts b/packages/admin-ui/src/lib/core/src/shared/components/data-table-2/data-table2.component.ts
index d29ee72849..36f67ff4e3 100644
--- a/packages/admin-ui/src/lib/core/src/shared/components/data-table-2/data-table2.component.ts
+++ b/packages/admin-ui/src/lib/core/src/shared/components/data-table-2/data-table2.component.ts
@@ -7,6 +7,7 @@ import {
ContentChildren,
EventEmitter,
inject,
+ Injector,
Input,
OnChanges,
OnDestroy,
@@ -124,10 +125,11 @@ export class DataTable2Component implements AfterContentInit, OnChanges, OnDe
@ContentChild('vdrDt2CustomSearch') customSearchTemplate: TemplateRef;
@ContentChildren(TemplateRef) templateRefs: QueryList>;
+ injector = inject(Injector);
route = inject(ActivatedRoute);
filterPresetService = inject(FilterPresetService);
dataTableCustomComponentService = inject(DataTableCustomComponentService);
- protected customComponents = new Map();
+ protected customComponents = new Map();
rowTemplate: TemplateRef;
currentStart: number;
@@ -226,12 +228,13 @@ export class DataTable2Component implements AfterContentInit, OnChanges, OnDe
column.setVisibility(column.hiddenByDefault);
}
column.onColumnChange(updateColumnVisibility);
- const customComponent = this.dataTableCustomComponentService.getCustomComponentsFor(
- this.id,
- column.id,
- );
- if (customComponent) {
- this.customComponents.set(column.id, customComponent);
+ const config = this.dataTableCustomComponentService.getCustomComponentsFor(this.id, column.id);
+ if (config) {
+ const injector = Injector.create({
+ parent: this.injector,
+ providers: config.providers ?? [],
+ });
+ this.customComponents.set(column.id, { config, injector });
}
});
diff --git a/packages/admin-ui/src/lib/react/src/components/react-custom-column.component.ts b/packages/admin-ui/src/lib/react/src/components/react-custom-column.component.ts
new file mode 100644
index 0000000000..5de904d729
--- /dev/null
+++ b/packages/admin-ui/src/lib/react/src/components/react-custom-column.component.ts
@@ -0,0 +1,32 @@
+import { Component, inject, InjectionToken, Input, OnInit, ViewEncapsulation } from '@angular/core';
+import { CustomColumnComponent } from '@vendure/admin-ui/core';
+import { ElementType } from 'react';
+import { ReactComponentHostDirective } from '../directives/react-component-host.directive';
+
+export const REACT_CUSTOM_COLUMN_COMPONENT_OPTIONS = new InjectionToken<{
+ component: ElementType;
+ props?: Record;
+}>('REACT_CUSTOM_COLUMN_COMPONENT_OPTIONS');
+
+@Component({
+ selector: 'vdr-react-custom-column-component',
+ template: ` `,
+ styleUrls: ['./react-global-styles.scss'],
+ encapsulation: ViewEncapsulation.None,
+ standalone: true,
+ imports: [ReactComponentHostDirective],
+})
+export class ReactCustomColumnComponent implements CustomColumnComponent, OnInit {
+ @Input() rowItem: any;
+
+ protected reactComponent = inject(REACT_CUSTOM_COLUMN_COMPONENT_OPTIONS).component;
+ private options = inject(REACT_CUSTOM_COLUMN_COMPONENT_OPTIONS);
+ protected props: Record;
+
+ ngOnInit() {
+ this.props = {
+ rowItem: this.rowItem,
+ ...(this.options.props ?? {}),
+ };
+ }
+}
diff --git a/packages/admin-ui/src/lib/react/src/public_api.ts b/packages/admin-ui/src/lib/react/src/public_api.ts
index 2d090030c7..1196a577d4 100644
--- a/packages/admin-ui/src/lib/react/src/public_api.ts
+++ b/packages/admin-ui/src/lib/react/src/public_api.ts
@@ -1,4 +1,5 @@
// This file was generated by the build-public-api.ts script
+export * from './components/react-custom-column.component';
export * from './components/react-custom-detail.component';
export * from './components/react-form-input.component';
export * from './components/react-route.component';
@@ -11,6 +12,7 @@ export * from './react-hooks/use-injector';
export * from './react-hooks/use-page-metadata';
export * from './react-hooks/use-query';
export * from './register-react-custom-detail-component';
+export * from './register-react-data-table-component';
export * from './register-react-form-input-component';
export * from './register-react-route-component';
export * from './types';
diff --git a/packages/admin-ui/src/lib/react/src/register-react-data-table-component.ts b/packages/admin-ui/src/lib/react/src/register-react-data-table-component.ts
new file mode 100644
index 0000000000..8084c8b25f
--- /dev/null
+++ b/packages/admin-ui/src/lib/react/src/register-react-data-table-component.ts
@@ -0,0 +1,109 @@
+import { APP_INITIALIZER } from '@angular/core';
+import {
+ DataTableColumnId,
+ DataTableCustomComponentService,
+ DataTableLocationId,
+} from '@vendure/admin-ui/core';
+import { ElementType } from 'react';
+import {
+ REACT_CUSTOM_COLUMN_COMPONENT_OPTIONS,
+ ReactCustomColumnComponent,
+} from './components/react-custom-column.component';
+
+/**
+ * @description
+ * Configures a {@link CustomDetailComponent} to be placed in the given location.
+ *
+ * @docsCategory react-extensions
+ */
+export interface ReactDataTableComponentConfig {
+ /**
+ * @description
+ * The location in the UI where the custom component should be placed.
+ */
+ tableId: DataTableLocationId;
+ /**
+ * @description
+ * The column in the table where the custom component should be placed.
+ */
+ columnId: DataTableColumnId;
+ /**
+ * @description
+ * The component to render in the table cell. This component will receive the `rowItem` prop
+ * which is the data object for the row, e.g. the `Product` object if used in the `product-list` table.
+ */
+ component: ElementType;
+ /**
+ * @description
+ * Optional props to pass to the React component.
+ */
+ props?: Record;
+}
+
+/**
+ * @description
+ * The props that will be passed to the React component registered via {@link registerReactDataTableComponent}.
+ */
+export interface ReactDataTableComponentProps {
+ rowItem: any;
+ [prop: string]: any;
+}
+
+/**
+ * @description
+ * Registers a React component to be rendered in a data table in the given location.
+ * The component will receive the `rowItem` prop which is the data object for the row,
+ * e.g. the `Product` object if used in the `product-list` table.
+ *
+ * @example
+ * ```ts title="components/SlugWithLink.tsx"
+ * import { ReactDataTableComponentProps } from '\@vendure/admin-ui/react';
+ * import React from 'react';
+ *
+ * export function SlugWithLink({ rowItem }: ReactDataTableComponentProps) {
+ * return (
+ *
+ * {rowItem.slug}
+ *
+ * );
+ * }
+ * ```
+ *
+ * ```ts title="providers.ts"
+ * import { registerReactDataTableComponent } from '\@vendure/admin-ui/react';
+ * import { SlugWithLink } from './components/SlugWithLink';
+ *
+ * export default [
+ * registerReactDataTableComponent({
+ * component: SlugWithLink,
+ * tableId: 'product-list',
+ * columnId: 'slug',
+ * props: {
+ * foo: 'bar',
+ * },
+ * }),
+ * ];
+ * ```
+ */
+export function registerReactDataTableComponent(config: ReactDataTableComponentConfig) {
+ return {
+ provide: APP_INITIALIZER,
+ multi: true,
+ useFactory: (dataTableCustomComponentService: DataTableCustomComponentService) => () => {
+ dataTableCustomComponentService.registerCustomComponent({
+ ...config,
+ component: ReactCustomColumnComponent,
+ providers: [
+ {
+ provide: REACT_CUSTOM_COLUMN_COMPONENT_OPTIONS,
+ useValue: {
+ component: config.component,
+ props: config.props,
+ },
+ },
+ ],
+ });
+ },
+ deps: [DataTableCustomComponentService],
+ };
+}
diff --git a/packages/dev-server/test-plugins/experimental-ui/components/CustomColumnComponent.tsx b/packages/dev-server/test-plugins/experimental-ui/components/CustomColumnComponent.tsx
new file mode 100644
index 0000000000..eeadb9e917
--- /dev/null
+++ b/packages/dev-server/test-plugins/experimental-ui/components/CustomColumnComponent.tsx
@@ -0,0 +1,10 @@
+import { ReactDataTableComponentProps } from '@vendure/admin-ui/react';
+import React from 'react';
+
+export function SlugWithLink({ rowItem }: ReactDataTableComponentProps) {
+ return (
+
+ {rowItem.slug}
+
+ );
+}
diff --git a/packages/dev-server/test-plugins/experimental-ui/providers.ts b/packages/dev-server/test-plugins/experimental-ui/providers.ts
index 2f085d0595..4c386bfc3a 100644
--- a/packages/dev-server/test-plugins/experimental-ui/providers.ts
+++ b/packages/dev-server/test-plugins/experimental-ui/providers.ts
@@ -1,7 +1,12 @@
import { addNavMenuSection, registerDataTableComponent } from '@vendure/admin-ui/core';
-import { registerReactFormInputComponent, registerReactCustomDetailComponent } from '@vendure/admin-ui/react';
+import {
+ registerReactFormInputComponent,
+ registerReactCustomDetailComponent,
+ registerReactDataTableComponent,
+} from '@vendure/admin-ui/react';
import { CustomTableComponent } from './components/custom-table.component';
+import { CustomColumnComponent } from './components/CustomColumnComponent';
import { CustomDetailComponent } from './components/CustomDetailComponent';
import { ReactNumberInput } from './components/ReactNumberInput';
@@ -40,4 +45,12 @@ export default [
tableId: 'product-list',
columnId: 'slug',
}),
+ registerReactDataTableComponent({
+ component: CustomColumnComponent,
+ tableId: 'product-list',
+ columnId: 'id',
+ props: {
+ foo: 'bar',
+ },
+ }),
];
|