diff --git a/packages/element-web-module-api/element-web-module-api.api.md b/packages/element-web-module-api/element-web-module-api.api.md index aff9156165..d64e8bffda 100644 --- a/packages/element-web-module-api/element-web-module-api.api.md +++ b/packages/element-web-module-api/element-web-module-api.api.md @@ -8,6 +8,7 @@ import { ComponentType } from 'react'; import { IWidget } from 'matrix-widget-api'; import { JSX } from 'react'; import { ModuleApi } from '@matrix-org/react-sdk-module-api'; +import { ReactNode } from 'react'; import { Root } from 'react-dom/client'; import { RuntimeModule } from '@matrix-org/react-sdk-module-api'; @@ -51,6 +52,8 @@ export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiEx // @alpha readonly customComponents: CustomComponentsApi; // @alpha + readonly customisations: CustomisationsApi; + // @alpha readonly extras: ExtrasApi; readonly i18n: I18nApi; readonly navigation: NavigationApi; @@ -115,10 +118,34 @@ export type Container = "top" | "right" | "center"; // @alpha export interface CustomComponentsApi { + registerLoginComponent(renderer: CustomLoginRenderFunction): void; registerMessageRenderer(eventTypeOrFilter: string | ((mxEvent: MatrixEvent) => boolean), renderer: CustomMessageRenderFunction, hints?: CustomMessageRenderHints): void; registerRoomPreviewBar(renderer: CustomRoomPreviewBarRenderFunction): void; } +// @alpha +export interface CustomisationsApi { + registerShouldShowComponent(fn: (this: void, component: UIComponent) => boolean | void): void; +} + +// @alpha +export type CustomLoginComponentProps = { + serverConfig: CustomLoginComponentPropsServerConfig; + fragmentAfterLogin?: string; + children?: ReactNode; + onLoggedIn(data: AccountAuthInfo): void; + onServerConfigChange(config: CustomLoginComponentPropsServerConfig): void; +}; + +// @alpha +export interface CustomLoginComponentPropsServerConfig { + hsName: string; + hsUrl: string; +} + +// @alpha +export type CustomLoginRenderFunction = ExtendablePropsRenderFunction; + // @alpha export type CustomMessageComponentProps = { mxEvent: MatrixEvent; @@ -177,10 +204,15 @@ export interface DirectoryCustomisations { requireCanonicalAliasAccessToPublish?(): boolean; } +// @alpha +export type ExtendablePropsRenderFunction =

( +props: P, +originalComponent: (props: P) => JSX.Element) => JSX.Element; + // @alpha export interface ExtrasApi { + addRoomHeaderButtonCallback(cb: RoomHeaderButtonsCallback): void; getVisibleRoomBySpaceKey(spaceKey: string, cb: () => string[]): void; - setRoomHeaderButtonCallback(cb: RoomHeaderButtonsCallback): void; setSpacePanelItem(spaceKey: string, props: SpacePanelItemProps): void; } @@ -412,6 +444,17 @@ export type Translations = Record; +// @alpha +export const enum UIComponent { + AddIntegrations = "UIComponent.addIntegrations", + CreateRooms = "UIComponent.roomCreation", + CreateSpaces = "UIComponent.spaceCreation", + ExploreRooms = "UIComponent.exploreRooms", + FilterContainer = "UIComponent.filterContainer", + InviteUsers = "UIComponent.sendInvites", + RoomOptionsMenu = "UIComponent.roomOptionsMenu" +} + // @alpha @deprecated (undocumented) export interface UserIdentifierCustomisations { getDisplayUserIdentifier(userId: string, opts: { diff --git a/packages/element-web-module-api/package.json b/packages/element-web-module-api/package.json index c00b38855e..a1b1ffac3f 100644 --- a/packages/element-web-module-api/package.json +++ b/packages/element-web-module-api/package.json @@ -1,7 +1,7 @@ { "name": "@element-hq/element-web-module-api", "type": "module", - "version": "1.10.0", + "version": "1.11.0", "description": "Module API surface for element-web", "repository": { "type": "git", diff --git a/packages/element-web-module-api/src/api/custom-components.ts b/packages/element-web-module-api/src/api/custom-components.ts index faba9c02cb..0265c9c63d 100644 --- a/packages/element-web-module-api/src/api/custom-components.ts +++ b/packages/element-web-module-api/src/api/custom-components.ts @@ -5,8 +5,9 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. */ -import type { JSX } from "react"; +import type { JSX, ReactNode } from "react"; import type { MatrixEvent } from "../models/event"; +import type { AccountAuthInfo } from "./auth.ts"; /** * Properties for all message components. @@ -91,6 +92,71 @@ export type CustomRoomPreviewBarRenderFunction = ( originalComponent: (props: CustomRoomPreviewBarComponentProps) => JSX.Element, ) => JSX.Element; +/** + * Authentication server config object. + * @alpha Subject to change. + */ +export interface CustomLoginComponentPropsServerConfig { + /** + * The URL of the homeserver's client-server API + */ + hsUrl: string; + /** + * The name of the homeserver to present to the user + */ + hsName: string; +} + +/** + * Properties for login component. + * @alpha Subject to change. + */ +export type CustomLoginComponentProps = { + /** + * The details of the currently chosen Matrix homeserver + */ + serverConfig: CustomLoginComponentPropsServerConfig; + /** + * The URL fragment to send the user to after authentication is complete + */ + fragmentAfterLogin?: string; + /** + * Additional components to render as children + */ + children?: ReactNode; + /** + * Function to complete login + * @param data - the data to authenticate the user with + */ + onLoggedIn(data: AccountAuthInfo): void; + /** + * Function to change the selected server + * @param config - new server configuration details + */ + onServerConfigChange(config: CustomLoginComponentPropsServerConfig): void; +}; + +/** + * Function used to render a component with a superset of the known props. + * @alpha Unlikely to change + */ +export type ExtendablePropsRenderFunction =

( + /** + * Properties for the component to be rendered. + */ + props: P, + /** + * Render function for the original component. + */ + originalComponent: (props: P) => JSX.Element, +) => JSX.Element; + +/** + * Function used to render a login component. + * @alpha Unlikely to change + */ +export type CustomLoginRenderFunction = ExtendablePropsRenderFunction; + /** * API for inserting custom components into Element. * @alpha Subject to change. @@ -143,4 +209,19 @@ export interface CustomComponentsApi { * ``` */ registerRoomPreviewBar(renderer: CustomRoomPreviewBarRenderFunction): void; + + /** + * Register a renderer for the login component. + * + * The render function should return a rendered component. + * + * @param renderer - The render function for the login component. + * @example + * ``` + * customComponents.registerLoginComponent((props, OriginalComponent) => { + * return ; + * }); + * ``` + */ + registerLoginComponent(renderer: CustomLoginRenderFunction): void; } diff --git a/packages/element-web-module-api/src/api/customisations.ts b/packages/element-web-module-api/src/api/customisations.ts new file mode 100644 index 0000000000..1a02c68ed0 --- /dev/null +++ b/packages/element-web-module-api/src/api/customisations.ts @@ -0,0 +1,65 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +/** + * Enum of UI components which can have their behaviour tweaked + * @alpha + */ +export const enum UIComponent { + /** + * Components that lead to a user being invited. + */ + InviteUsers = "UIComponent.sendInvites", + + /** + * Components that lead to a room being created that aren't already + * guarded by some other condition (ie: "only if you can edit this + * space" is *not* guarded by this component, but "start DM" is). + */ + CreateRooms = "UIComponent.roomCreation", + + /** + * Components that lead to a Space being created that aren't already + * guarded by some other condition (ie: "only if you can add subspaces" + * is *not* guarded by this component, but "create new space" is). + */ + CreateSpaces = "UIComponent.spaceCreation", + + /** + * Components that lead to the public room directory. + */ + ExploreRooms = "UIComponent.exploreRooms", + + /** + * Components that lead to the user being able to easily add widgets + * and integrations to the room, such as from the room information card. + */ + AddIntegrations = "UIComponent.addIntegrations", + + /** + * Component that lead to the user being able to search, dial, explore rooms + */ + FilterContainer = "UIComponent.filterContainer", + + /** + * Components that lead the user to room options menu. + */ + RoomOptionsMenu = "UIComponent.roomOptionsMenu", +} + +/** + * API for customising Element Web's components + * @alpha Subject to change. + */ +export interface CustomisationsApi { + /** + * Method to register a callback which can affect whether a given component is drawn or not. + * @param fn - the callback, if it returns true the component will be rendered, if false it will not be. + * If undefined will defer to next callback, ultimately falling through to `true` if none return false. + */ + registerShouldShowComponent(fn: (this: void, component: UIComponent) => boolean | void): void; +} diff --git a/packages/element-web-module-api/src/api/index.ts b/packages/element-web-module-api/src/api/index.ts index 840eea6eb2..3d60fc2a43 100644 --- a/packages/element-web-module-api/src/api/index.ts +++ b/packages/element-web-module-api/src/api/index.ts @@ -22,6 +22,7 @@ import { type StoresApi } from "./stores.ts"; import { type ClientApi } from "./client.ts"; import { type WidgetLifecycleApi } from "./widget-lifecycle.ts"; import { type WidgetApi } from "./widget.ts"; +import { type CustomisationsApi } from "./customisations.ts"; /** * Module interface for modules to implement. @@ -152,6 +153,12 @@ export interface Api */ readonly widget: WidgetApi; + /** + * Allows modules to customise behaviour of app's components. + * @alpha Subject to change. + */ + readonly customisations: CustomisationsApi; + /** * Create a ReactDOM root for rendering React components. * Exposed to allow modules to avoid needing to bundle their own ReactDOM. diff --git a/packages/element-web-module-api/src/index.ts b/packages/element-web-module-api/src/index.ts index 33b8b92c58..1f34106cfc 100644 --- a/packages/element-web-module-api/src/index.ts +++ b/packages/element-web-module-api/src/index.ts @@ -24,5 +24,7 @@ export type * from "./api/stores"; export type * from "./api/client"; export type * from "./api/widget-lifecycle"; export type * from "./api/widget"; +export type * from "./api/customisations"; +export { UIComponent } from "./api/customisations"; export * from "./api/watchable"; export type * from "./utils";