Add module APIs to enable widget toggle buttons

This commit is contained in:
David Baker 2026-03-06 10:58:00 +00:00
parent 7206db8243
commit bf69ddf2b4
6 changed files with 115 additions and 1 deletions

View File

@ -5,6 +5,7 @@
```ts
import { ComponentType } from 'react';
import { IWidget } from 'matrix-widget-api';
import { JSX } from 'react';
import { ModuleApi } from '@matrix-org/react-sdk-module-api';
import { Root } from 'react-dom/client';
@ -56,6 +57,8 @@ export interface Api extends LegacyModuleApiExtension, LegacyCustomisationsApiEx
readonly rootNode: HTMLElement;
readonly stores: StoresApi;
// @alpha
readonly widget: WidgetApi;
// @alpha
readonly widgetLifecycle: WidgetLifecycleApi;
}
@ -107,6 +110,9 @@ export interface ConfigApi {
get<K extends keyof Config = never>(key?: K): Config | Config[K];
}
// @alpha
export type Container = "top" | "right" | "center";
// @alpha
export interface CustomComponentsApi {
registerMessageRenderer(eventTypeOrFilter: string | ((mxEvent: MatrixEvent) => boolean), renderer: CustomMessageRenderFunction, hints?: CustomMessageRenderHints): void;
@ -174,6 +180,7 @@ export interface DirectoryCustomisations {
// @alpha
export interface ExtrasApi {
getVisibleRoomBySpaceKey(spaceKey: string, cb: () => string[]): void;
setRoomHeaderButtonCallback(cb: RoomHeaderButtonsCallback): void;
setSpacePanelItem(spaceKey: string, props: SpacePanelItemProps): void;
}
@ -358,6 +365,9 @@ export interface Room {
name: Watchable<string>;
}
// @alpha
export type RoomHeaderButtonsCallback = (roomId: string) => JSX.Element | undefined;
// @alpha @deprecated (undocumented)
export interface RoomListCustomisations<Room> {
isRoomVisible?(room: Room): boolean;
@ -436,6 +446,14 @@ export class Watchable<T> {
watch(listener: (value: T) => void): void;
}
// @alpha
export interface WidgetApi {
getAppAvatarUrl(app: IWidget, width?: number, height?: number, resizeMethod?: string): string | null;
getWidgetsInRoom(roomId: string): IWidget[];
isAppInContainer(app: IWidget, container: Container, roomId: string): boolean;
moveAppToContainer(app: IWidget, container: Container, roomId: string): void;
}
// @alpha
export type WidgetDescriptor = {
id: string;
@ -476,4 +494,3 @@ export interface WidgetVariablesCustomisations {
// (No @packageDocumentation comment for this package)
```

View File

@ -61,5 +61,8 @@
"matrix-web-i18n": {
"optional": true
}
},
"dependencies": {
"matrix-widget-api": "^1.17.0"
}
}

View File

@ -43,6 +43,15 @@ export interface SpacePanelItemProps {
onSelected: () => void;
}
/**
* A callback that returns a JSX element representing the buttons.
*
* @alpha
* @param roomId - The ID of the room for which the header is being rendered.
* @returns A JSX element representing the buttons to be rendered in the room header, or undefined if no buttons should be rendered.
*/
export type RoomHeaderButtonsCallback = (roomId: string) => JSX.Element | undefined;
/**
* API for inserting extra UI into Element Web.
* @alpha Subject to change.
@ -67,4 +76,11 @@ export interface ExtrasApi {
* @param cb - A callback that returns the list of visible room IDs.
*/
getVisibleRoomBySpaceKey(spaceKey: string, cb: () => string[]): void;
/**
* Sets the callback to get extra buttons in the room header (which can vary depending on the room being displayed).
*
* @param cb - A callback that returns a JSX element representing the buttons (see {@link RoomHeaderButtonsCallback}).
*/
setRoomHeaderButtonCallback(cb: RoomHeaderButtonsCallback): void;
}

View File

@ -1,5 +1,6 @@
/*
Copyright 2025 New Vector Ltd.
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.
@ -20,6 +21,7 @@ import { type BuiltinsApi } from "./builtins.ts";
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";
/**
* Module interface for modules to implement.
@ -143,6 +145,13 @@ export interface Api
*/
readonly widgetLifecycle: WidgetLifecycleApi;
/**
* API for modules to interact with widgets in Element Web, including getting what widgets
* are active in a given room.
* @alpha Subject to change.
*/
readonly widget: WidgetApi;
/**
* Create a ReactDOM root for rendering React components.
* Exposed to allow modules to avoid needing to bundle their own ReactDOM.

View File

@ -0,0 +1,68 @@
/*
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.
*/
import { type IWidget } from "matrix-widget-api";
/**
* Containers that control where a widget is displayed on the screen.
*
* "top" is the app drawer, and currently the only sensible value.
*
* "right" is the right panel, and the default for widgets. Setting
* this as a container on a widget is essentially like saying "no
* changes needed", though this may change in the future.
*
* "center" was uncodumented at time of porting this from an enum.
* Possibly when a widget replaces the main chat view like element call.
*
* @alpha Subject to change.
*/
export type Container = "top" | "right" | "center";
/**
* An API for interfacing with widgets in Element Web, including getting what widgets
* are active in a given room.
* @alpha Subject to change.
*/
export interface WidgetApi {
/**
* Gets the widgets active in a given room.
*
* @param roomId - The room to get the widgets for.
*/
getWidgetsInRoom(roomId: string): IWidget[];
/**
* Gets the URL of a widget's avatar, if it has one.
*
* @param app - The widget to get the avatar URL for.
* @param width - Optional width to resize the avatar to.
* @param height - Optional height to resize the avatar to.
* @param resizeMethod - Optional method to use when resizing the avatar.
* @returns The URL of the widget's avatar, or null if it doesn't have one.
*/
getAppAvatarUrl(app: IWidget, width?: number, height?: number, resizeMethod?: string): string | null;
/**
* Checks if a widget is in a specific container in a given room.
*
* @param app - The widget to check.
* @param container - The container to check.
* @param roomId - The room to check in.
* @returns True if the widget is in the specified container, false otherwise.
*/
isAppInContainer(app: IWidget, container: Container, roomId: string): boolean;
/**
* Moves a widget to a specific container in a given room.
*
* @param app - The widget to move.
* @param container - The container to move the widget to.
* @param roomId - The room to move the widget in.
*/
moveAppToContainer(app: IWidget, container: Container, roomId: string): void;
}

View File

@ -23,5 +23,6 @@ export type * from "./api/builtins";
export type * from "./api/stores";
export type * from "./api/client";
export type * from "./api/widget-lifecycle";
export type * from "./api/widget";
export * from "./api/watchable";
export type * from "./utils";