From 61eb5e8d0f709759b117f8f96d710425e328cdfb Mon Sep 17 00:00:00 2001 From: Matthias Mair Date: Sat, 9 May 2026 15:14:36 +0200 Subject: [PATCH] frontend: more exposing for plugins (#11897) * expose EditApiForm in context * expose renderRemoteInstance in context * expose useInstance in context * export DetailDrawerComponent, useLocalLibState * fix import/type * add changelog entry --- src/frontend/CHANGELOG.md | 11 ++++++ .../lib/components/nav/DetailDrawer.tsx | 2 +- src/frontend/lib/index.ts | 4 +- src/frontend/lib/types/Plugins.tsx | 7 +++- src/frontend/lib/types/Rendering.tsx | 37 +++++++++++++++++++ .../src/components/plugins/PluginContext.tsx | 7 +++- .../src/components/render/Instance.tsx | 8 +--- src/frontend/src/hooks/UseInstance.tsx | 34 ++--------------- .../src/pages/part/bom/BomActions.tsx | 2 +- 9 files changed, 71 insertions(+), 41 deletions(-) diff --git a/src/frontend/CHANGELOG.md b/src/frontend/CHANGELOG.md index de7a54ed88..7068a287b0 100644 --- a/src/frontend/CHANGELOG.md +++ b/src/frontend/CHANGELOG.md @@ -2,6 +2,17 @@ This file contains historical changelog information for the InvenTree UI components library. +### 0.11.4 - Unreleased + +Adds additional functions in the plugin context related to form rendering and API invocation: +- `useInstance` +- `renderRemoteInstance` +- `EditApiForm` + +Exposes sub-components related to DetailDrawer rendering: +- `DetailDrawerComponent` +- `useLocalLibState` + ### 0.11.3 - April 2026 Exposes additional type definitions related to rendering drawers from tables: diff --git a/src/frontend/lib/components/nav/DetailDrawer.tsx b/src/frontend/lib/components/nav/DetailDrawer.tsx index d2d2b0d2d1..f8edbe2aec 100644 --- a/src/frontend/lib/components/nav/DetailDrawer.tsx +++ b/src/frontend/lib/components/nav/DetailDrawer.tsx @@ -26,7 +26,7 @@ export interface DrawerProps { closeOnEscape?: boolean; } -function DetailDrawerComponent({ +export function DetailDrawerComponent({ title, position = 'right', size, diff --git a/src/frontend/lib/index.ts b/src/frontend/lib/index.ts index e6a6327f7d..bb325a25f7 100644 --- a/src/frontend/lib/index.ts +++ b/src/frontend/lib/index.ts @@ -135,7 +135,8 @@ export { export { type DrawerProps, DetailDrawer, - DetailDrawerLink + DetailDrawerLink, + DetailDrawerComponent } from './components/nav/DetailDrawer'; export { StylishText } from './components/StylishText'; @@ -144,3 +145,4 @@ export { type StoredTableStateProps, useStoredTableState } from './states/StoredTableState'; +export { useLocalLibState } from './states/LocalLibState'; diff --git a/src/frontend/lib/types/Plugins.tsx b/src/frontend/lib/types/Plugins.tsx index 575cf9f3c5..25066cb354 100644 --- a/src/frontend/lib/types/Plugins.tsx +++ b/src/frontend/lib/types/Plugins.tsx @@ -7,11 +7,12 @@ import type { ModelDict } from '../enums/ModelInformation'; import type { ModelType } from '../enums/ModelType'; import type { ApiFormModalProps, + ApiFormProps, BulkEditApiFormModalProps, StockOperationProps } from './Forms'; import type { UseModalReturn } from './Modals'; -import type { RenderInstanceProps } from './Rendering'; +import type { RemoteInstanceProps, RenderInstanceProps } from './Rendering'; import type { SettingsStateProps } from './Settings'; import type { InvenTreeTableRenderProps } from './Tables'; import type { UserStateProps } from './User'; @@ -46,6 +47,7 @@ export type InvenTreeFormsContext = { create: (props: ApiFormModalProps) => UseModalReturn; delete: (props: ApiFormModalProps) => UseModalReturn; edit: (props: ApiFormModalProps) => UseModalReturn; + editApiForm: (props: { id?: string; props: ApiFormProps }) => React.ReactNode; stockActions: StockAdjustmentFormsContext; }; @@ -97,6 +99,9 @@ export type InvenTreePluginContext = { globalSettings: SettingsStateProps; modelInformation: ModelDict; renderInstance: (props: Readonly) => React.ReactNode; + renderRemoteInstance: ( + props: Readonly + ) => React.ReactNode; host: string; i18n: I18n; locale: string; diff --git a/src/frontend/lib/types/Rendering.tsx b/src/frontend/lib/types/Rendering.tsx index c2f4b1e676..0fa3b5abb1 100644 --- a/src/frontend/lib/types/Rendering.tsx +++ b/src/frontend/lib/types/Rendering.tsx @@ -1,4 +1,11 @@ +import type { + QueryObserverResult, + UseQueryResult +} from '@tanstack/react-query'; +import type { ReactNode } from 'react'; +import type { ApiEndpoints } from '..'; import type { ModelType } from '../enums/ModelType'; +import type { PathParams } from './Core'; /** * Interface for rendering a model instance. @@ -22,4 +29,34 @@ export type ModelRendererDict = EnumDictionary< export type RenderInstanceProps = { model: ModelType | undefined; + custom_model?: string; } & InstanceRenderInterface; + +export interface UseInstanceResult { + instance: any; + setInstance: (instance: any) => void; + refreshInstance: () => void; + refreshInstancePromise: () => Promise>; + instanceQuery: UseQueryResult; + isLoaded: boolean; +} + +export interface useInstanceProps { + endpoint: ApiEndpoints; + pk?: string | number | undefined; + hasPrimaryKey?: boolean; + params?: any; + pathParams?: PathParams; + disabled?: boolean; + defaultValue?: any; + refetchOnMount?: boolean; + refetchOnWindowFocus?: boolean; + updateInterval?: number; +} + +export interface RemoteInstanceProps { + model: ModelType; + modelUrl?: string; + modelRenderer?: (instance: any) => ReactNode; + pk: number; +} diff --git a/src/frontend/src/components/plugins/PluginContext.tsx b/src/frontend/src/components/plugins/PluginContext.tsx index 0c527fb507..0fbb0b7763 100644 --- a/src/frontend/src/components/plugins/PluginContext.tsx +++ b/src/frontend/src/components/plugins/PluginContext.tsx @@ -38,6 +38,7 @@ import { useDeleteApiFormModal, useEditApiFormModal } from '../../hooks/UseForm'; +import { useInstance } from '../../hooks/UseInstance'; import { type ImporterOpenOptions, closeGlobalImporter, @@ -46,7 +47,8 @@ import { } from '../../states/ImporterState'; import { useServerApiState } from '../../states/ServerApiState'; import { InvenTreeTableInternal } from '../../tables/InvenTreeTable'; -import { RenderInstance } from '../render/Instance'; +import { EditApiForm } from '../forms/ApiForm'; +import { RenderInstance, RenderRemoteInstance } from '../render/Instance'; export const useInvenTreeContext = () => { const [locale, host] = useLocalState(useShallow((s) => [s.language, s.host])); @@ -77,7 +79,9 @@ export const useInvenTreeContext = () => { globalSettings: globalSettings, userSettings: userSettings, modelInformation: ModelInformationDict, + useInstance: useInstance, renderInstance: RenderInstance, + renderRemoteInstance: RenderRemoteInstance, theme: theme, colorScheme: colorScheme, importer: { @@ -100,6 +104,7 @@ export const useInvenTreeContext = () => { create: useCreateApiFormModal, delete: useDeleteApiFormModal, edit: useEditApiFormModal, + editApiForm: EditApiForm, stockActions: { addStock: useAddStockItem, assignStock: useAssignStockItem, diff --git a/src/frontend/src/components/render/Instance.tsx b/src/frontend/src/components/render/Instance.tsx index b21c820fd5..e81803aaa5 100644 --- a/src/frontend/src/components/render/Instance.tsx +++ b/src/frontend/src/components/render/Instance.tsx @@ -18,6 +18,7 @@ import { apiUrl } from '@lib/functions/Api'; import { getBaseUrl, navigateToLink } from '@lib/functions/Navigation'; import type { ModelRendererDict, + RemoteInstanceProps, RenderInstanceProps } from '@lib/types/Rendering'; export type { InstanceRenderInterface } from '@lib/types/Rendering'; @@ -124,12 +125,7 @@ export function RenderRemoteInstance({ modelUrl, modelRenderer, pk -}: Readonly<{ - model: ModelType; - modelUrl?: string; - modelRenderer?: (instance: any) => ReactNode; - pk: number; -}>): ReactNode { +}: Readonly): ReactNode { const api = useApi(); const { data, isLoading, isFetching } = useQuery({ diff --git a/src/frontend/src/hooks/UseInstance.tsx b/src/frontend/src/hooks/UseInstance.tsx index da3777449e..68381e9479 100644 --- a/src/frontend/src/hooks/UseInstance.tsx +++ b/src/frontend/src/hooks/UseInstance.tsx @@ -1,24 +1,9 @@ -import { - type QueryObserverResult, - type UseQueryResult, - useQuery -} from '@tanstack/react-query'; -import { useCallback, useMemo, useState } from 'react'; - -import type { ApiEndpoints } from '@lib/enums/ApiEndpoints'; import { apiUrl } from '@lib/functions/Api'; -import type { PathParams } from '@lib/types/Core'; +import type { UseInstanceResult, useInstanceProps } from '@lib/types/Rendering'; +import { useQuery } from '@tanstack/react-query'; +import { useCallback, useMemo, useState } from 'react'; import { useApi } from '../contexts/ApiContext'; -export interface UseInstanceResult { - instance: any; - setInstance: (instance: any) => void; - refreshInstance: () => void; - refreshInstancePromise: () => Promise>; - instanceQuery: UseQueryResult; - isLoaded: boolean; -} - /** * Custom hook for loading a single instance of an instance from the API * @@ -40,18 +25,7 @@ export function useInstance({ refetchOnMount = true, refetchOnWindowFocus = false, updateInterval -}: { - endpoint: ApiEndpoints; - pk?: string | number | undefined; - hasPrimaryKey?: boolean; - params?: any; - pathParams?: PathParams; - disabled?: boolean; - defaultValue?: any; - refetchOnMount?: boolean; - refetchOnWindowFocus?: boolean; - updateInterval?: number; -}): UseInstanceResult { +}: useInstanceProps): UseInstanceResult { const api = useApi(); const [instance, setInstance] = useState(defaultValue); diff --git a/src/frontend/src/pages/part/bom/BomActions.tsx b/src/frontend/src/pages/part/bom/BomActions.tsx index 764db9f1b8..9331db979a 100644 --- a/src/frontend/src/pages/part/bom/BomActions.tsx +++ b/src/frontend/src/pages/part/bom/BomActions.tsx @@ -1,6 +1,7 @@ import { ActionButton } from '@lib/components/ActionButton'; import { ApiEndpoints } from '@lib/enums/ApiEndpoints'; import { UserRoles } from '@lib/enums/Roles'; +import type { UseInstanceResult } from '@lib/types/Rendering'; import { t } from '@lingui/core/macro'; import { ActionIcon, @@ -23,7 +24,6 @@ import { useSearchParams } from 'react-router-dom'; import { RenderUser } from '../../../components/render/User'; import useBackgroundTask from '../../../hooks/UseBackgroundTask'; import { useApiFormModal } from '../../../hooks/UseForm'; -import type { UseInstanceResult } from '../../../hooks/UseInstance'; import { useUserState } from '../../../states/UserState'; import { BomCompareDrawer } from './BomCompare';