Skip to content

Commit 7808a0f

Browse files
committed
Create WidgetContextMenu component in shared-components
1 parent a88a357 commit 7808a0f

File tree

4 files changed

+406
-0
lines changed

4 files changed

+406
-0
lines changed

packages/shared-components/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export * from "./rich-list/RichItem";
1919
export * from "./rich-list/RichList";
2020
export * from "./utils/Box";
2121
export * from "./utils/Flex";
22+
export * from "./right-panel/WidgetContextMenu";
2223

2324
// Utils
2425
export * from "./utils/i18n";
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/*
2+
* Copyright 2025 New Vector Ltd.
3+
*
4+
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
5+
* Please see LICENSE files in the repository root for full details.
6+
*/
7+
8+
import React, { type JSX } from "react";
9+
import { type ClientWidgetApi } from "matrix-widget-api";
10+
import { IconButton, Menu, MenuItem } from "@vector-im/compound-web";
11+
12+
import { _t } from "../../utils/i18n.tsx";
13+
import { type ViewModel } from "../../viewmodel/ViewModel.ts";
14+
import { useViewModel } from "../../useViewModel.ts";
15+
16+
export interface WidgetContextMenuSnapshot {
17+
showStreamAudioStreamButton: boolean;
18+
showEditButton: boolean;
19+
showRevokeButton: boolean;
20+
showDeleteButton: boolean;
21+
showSnapshotButton: boolean;
22+
showMoveButtons: [boolean, boolean];
23+
canModify: boolean;
24+
widgetMessaging: ClientWidgetApi | undefined;
25+
isMenuOpened: boolean;
26+
}
27+
28+
export interface WidgetContextMenuAction {
29+
onStreamAudioClick: () => Promise<void>;
30+
onEditClick: () => void;
31+
onSnapshotClick: () => void;
32+
onDeleteClick: () => void;
33+
onRevokeClick: () => void;
34+
onFinished: () => void;
35+
onMoveButton: (direction: number) => void;
36+
}
37+
38+
export type WidgetContextMenuViewModel = ViewModel<WidgetContextMenuSnapshot> & WidgetContextMenuAction;
39+
40+
interface WidgetContextMenuViewProps {
41+
vm: WidgetContextMenuViewModel;
42+
}
43+
44+
export const WidgetContextMenuView: React.FC<WidgetContextMenuViewProps> = ({
45+
vm
46+
}) => {
47+
48+
const {
49+
showStreamAudioStreamButton,
50+
showEditButton,
51+
showSnapshotButton,
52+
showDeleteButton,
53+
showRevokeButton,
54+
showMoveButtons,
55+
isMenuOpened
56+
}= useViewModel(vm);
57+
58+
let streamAudioStreamButton: JSX.Element | undefined;
59+
if (showStreamAudioStreamButton) {
60+
streamAudioStreamButton = (
61+
<MenuItem
62+
onSelect={vm.onStreamAudioClick}
63+
label={_t("widget|context_menu|start_audio_stream")}
64+
/>
65+
);
66+
}
67+
68+
let editButton: JSX.Element | undefined;
69+
if (showEditButton) {
70+
editButton = <MenuItem onSelect={vm.onEditClick} label={_t("action|edit")} />;
71+
}
72+
73+
let snapshotButton: JSX.Element | undefined;
74+
if (showSnapshotButton) {
75+
snapshotButton = (
76+
<MenuItem onSelect={vm.onSnapshotClick} label={_t("widget|context_menu|screenshot")} />
77+
);
78+
}
79+
80+
let deleteButton: JSX.Element | undefined;
81+
if (showDeleteButton) {
82+
deleteButton = (
83+
<MenuItem
84+
onSelect={vm.onDeleteClick}
85+
// TODO label={userWidget ? _t("action|remove") : _t("widget|context_menu|remove")}
86+
label={_t("widget|context_menu|remove")}
87+
/>
88+
);
89+
}
90+
91+
let revokeButton: JSX.Element | undefined;
92+
if (showRevokeButton) {
93+
revokeButton = (
94+
<MenuItem onSelect={vm.onRevokeClick} label={_t("widget|context_menu|revoke")} />
95+
);
96+
}
97+
98+
const [showMoveLeftButton, showMoveRightButton] = showMoveButtons;
99+
let moveLeftButton: JSX.Element | undefined;
100+
if (showMoveLeftButton) {
101+
moveLeftButton = <MenuItem onSelect={() => vm.onMoveButton(-1)} label={_t("widget|context_menu|move_left")} />;
102+
}
103+
104+
let moveRightButton: JSX.Element | undefined;
105+
if (showMoveRightButton) {
106+
moveRightButton = <MenuItem onSelect={() => vm.onMoveButton(1)} label={_t("widget|context_menu|move_right")} />;
107+
}
108+
109+
return (
110+
<Menu
111+
title="Widget context menu"
112+
open={isMenuOpened}
113+
showTitle={false}
114+
trigger={
115+
<IconButton aria-label={_t("action|add")} />
116+
}
117+
onOpenChange={vm.onFinished}
118+
>
119+
{streamAudioStreamButton}
120+
{editButton}
121+
{revokeButton}
122+
{deleteButton}
123+
{snapshotButton}
124+
{moveLeftButton}
125+
{moveRightButton}
126+
</Menu>
127+
);
128+
};
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/*
2+
* Copyright 2025 New Vector Ltd.
3+
*
4+
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
5+
* Please see LICENSE files in the repository root for full details.
6+
*/
7+
8+
export type { WidgetContextMenuSnapshot, WidgetContextMenuViewModel } from "./WidgetContextMenuView";
9+
export { WidgetContextMenuView } from "./WidgetContextMenuView";

0 commit comments

Comments
 (0)