@linagora/twake-mui 18.2.0 → 18.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ # @linagora/twake-mui [18.3.0](https://github.com/linagora/twake-ui/compare/@linagora/twake-mui@18.2.0...@linagora/twake-mui@18.3.0) (2026-10-22)
2
+
3
+
4
+ ### Features
5
+
6
+ * **twake-mui:** Name the close and icon action buttons of ContactPopover ([4ece4c5](https://github.com/linagora/twake-ui/commit/4ece4c5509ae5c0f0b1a09c3492d834ffba40f80))
7
+
1
8
  # @linagora/twake-mui [18.2.0](https://github.com/linagora/twake-ui/compare/@linagora/twake-mui@18.1.0...@linagora/twake-mui@18.2.0) (2026-10-22)
2
9
 
3
10
 
@@ -1,8 +1,12 @@
1
1
  import React from 'react';
2
2
  export interface ContactPopoverCalendarActionProps {
3
3
  /** URL for calendar action (e.g., view attendee's calendar) */
4
- url: string;
4
+ url?: string;
5
+ /** Callback when the action is clicked, used when no url is given */
6
+ onClick?: () => void;
5
7
  /** Disable the action button */
6
8
  disabled?: boolean;
9
+ /** Accessible name and tooltip of the action, defaults to "Plan a meeting" */
10
+ label?: string;
7
11
  }
8
- export declare const ContactPopoverCalendarAction: ({ url, disabled }: ContactPopoverCalendarActionProps) => React.ReactElement;
12
+ export declare const ContactPopoverCalendarAction: ({ url, onClick, disabled, label }: ContactPopoverCalendarActionProps) => React.ReactElement;
@@ -1,12 +1,8 @@
1
- import { Icon } from '@linagora/twake-icons';
2
1
  import { CalendarToday } from '@linagora/twake-icons';
3
- import { IconButton, useTheme } from '@mui/material';
4
2
  import React from 'react';
5
- export const ContactPopoverCalendarAction = ({ url, disabled = false }) => {
6
- const theme = useTheme();
7
- return (React.createElement(IconButton, { href: url, target: "_blank", rel: "noopener noreferrer", size: "small", disabled: disabled, sx: {
8
- border: `1px solid ${theme.vars.palette.divider}`,
9
- color: theme.vars.palette.text.icon
10
- } },
11
- React.createElement(Icon, { icon: CalendarToday, size: 20 })));
3
+ import { useI18n } from 'twake-i18n';
4
+ import { ContactPopoverIconAction } from './ContactPopoverIconAction';
5
+ export const ContactPopoverCalendarAction = ({ url, onClick, disabled = false, label }) => {
6
+ const { t } = useI18n();
7
+ return (React.createElement(ContactPopoverIconAction, { icon: CalendarToday, label: label ?? t('ContactPopover.calendarButton'), url: url, onClick: onClick, disabled: disabled }));
12
8
  };
@@ -1,8 +1,12 @@
1
1
  import React from 'react';
2
2
  export interface ContactPopoverChatActionProps {
3
3
  /** URL for chat action (e.g., Twake Chat link) */
4
- url: string;
4
+ url?: string;
5
+ /** Callback when the action is clicked, used when no url is given */
6
+ onClick?: () => void;
5
7
  /** Disable the action button */
6
8
  disabled?: boolean;
9
+ /** Accessible name and tooltip of the action, defaults to "Open a chat" */
10
+ label?: string;
7
11
  }
8
- export declare const ContactPopoverChatAction: ({ url, disabled }: ContactPopoverChatActionProps) => React.ReactElement;
12
+ export declare const ContactPopoverChatAction: ({ url, onClick, disabled, label }: ContactPopoverChatActionProps) => React.ReactElement;
@@ -1,12 +1,8 @@
1
- import { Icon } from '@linagora/twake-icons';
2
1
  import { Discuss } from '@linagora/twake-icons';
3
- import { IconButton, useTheme } from '@mui/material';
4
2
  import React from 'react';
5
- export const ContactPopoverChatAction = ({ url, disabled = false }) => {
6
- const theme = useTheme();
7
- return (React.createElement(IconButton, { href: url, target: "_blank", rel: "noopener noreferrer", size: "small", disabled: disabled, sx: {
8
- border: `1px solid ${theme.vars.palette.divider}`,
9
- color: theme.vars.palette.text.icon
10
- } },
11
- React.createElement(Icon, { icon: Discuss, size: 20 })));
3
+ import { useI18n } from 'twake-i18n';
4
+ import { ContactPopoverIconAction } from './ContactPopoverIconAction';
5
+ export const ContactPopoverChatAction = ({ url, onClick, disabled = false, label }) => {
6
+ const { t } = useI18n();
7
+ return (React.createElement(ContactPopoverIconAction, { icon: Discuss, label: label ?? t('ContactPopover.chatButton'), url: url, onClick: onClick, disabled: disabled }));
12
8
  };
@@ -1,8 +1,10 @@
1
1
  import React from 'react';
2
2
  export interface ContactPopoverEmailActionProps {
3
3
  /** URL for email action */
4
- url: string;
4
+ url?: string;
5
+ /** Callback when the action is clicked, used when no url is given */
6
+ onClick?: () => void;
5
7
  /** Disable the action button */
6
8
  disabled?: boolean;
7
9
  }
8
- export declare const ContactPopoverEmailAction: ({ url, disabled }: ContactPopoverEmailActionProps) => React.ReactElement;
10
+ export declare const ContactPopoverEmailAction: ({ url, onClick, disabled }: ContactPopoverEmailActionProps) => React.ReactElement;
@@ -1,10 +1,13 @@
1
1
  import { Button, useTheme } from '@mui/material';
2
2
  import React from 'react';
3
3
  import { useI18n } from 'twake-i18n';
4
- export const ContactPopoverEmailAction = ({ url, disabled = false }) => {
4
+ export const ContactPopoverEmailAction = ({ url, onClick, disabled = false }) => {
5
5
  const theme = useTheme();
6
6
  const { t } = useI18n();
7
- return (React.createElement(Button, { href: url, target: "_blank", rel: "noopener noreferrer", variant: "outlined", size: "small", disabled: disabled, sx: {
7
+ const linkProps = url
8
+ ? { href: url, target: '_blank', rel: 'noopener noreferrer' }
9
+ : { onClick };
10
+ return (React.createElement(Button, { ...linkProps, variant: "outlined", size: "small", disabled: disabled, sx: {
8
11
  justifyContent: 'center',
9
12
  color: theme.vars.palette.text.primary,
10
13
  borderColor: theme.vars.palette.divider
@@ -0,0 +1,15 @@
1
+ import { Icon } from '@linagora/twake-icons';
2
+ import React from 'react';
3
+ export interface ContactPopoverIconActionProps {
4
+ /** Icon shown in the button */
5
+ icon: React.ComponentProps<typeof Icon>['icon'];
6
+ /** Accessible name of the button, also shown as its tooltip */
7
+ label: string;
8
+ /** URL opened in a new tab when the button is clicked */
9
+ url?: string;
10
+ /** Callback when the button is clicked, used when no url is given */
11
+ onClick?: () => void;
12
+ /** Disable the action button */
13
+ disabled?: boolean;
14
+ }
15
+ export declare const ContactPopoverIconAction: ({ icon, label, url, onClick, disabled }: ContactPopoverIconActionProps) => React.ReactElement;
@@ -0,0 +1,19 @@
1
+ import { Icon } from '@linagora/twake-icons';
2
+ import { Box, IconButton, Tooltip, useTheme } from '@mui/material';
3
+ import React from 'react';
4
+ export const ContactPopoverIconAction = ({ icon, label, url, onClick, disabled = false }) => {
5
+ const theme = useTheme();
6
+ const linkProps = url
7
+ ? { href: url, target: '_blank', rel: 'noopener noreferrer' }
8
+ : { onClick };
9
+ return (
10
+ // the span lets the tooltip show on a disabled button, and takes its
11
+ // aria-label: the button is named too
12
+ React.createElement(Tooltip, { title: label },
13
+ React.createElement(Box, { component: "span", sx: { display: 'inline-flex' } },
14
+ React.createElement(IconButton, { ...linkProps, "aria-label": label, size: "small", disabled: disabled, sx: {
15
+ border: `1px solid ${theme.vars.palette.divider}`,
16
+ color: theme.vars.palette.text.icon
17
+ } },
18
+ React.createElement(Icon, { icon: icon, size: 20 })))));
19
+ };
@@ -1,8 +1,12 @@
1
1
  import React from 'react';
2
2
  export interface ContactPopoverVideoActionProps {
3
3
  /** URL for video call action (e.g., Twake Meet link) */
4
- url: string;
4
+ url?: string;
5
+ /** Callback when the action is clicked, used when no url is given */
6
+ onClick?: () => void;
5
7
  /** Disable the action button */
6
8
  disabled?: boolean;
9
+ /** Accessible name and tooltip of the action, defaults to "Start a video call" */
10
+ label?: string;
7
11
  }
8
- export declare const ContactPopoverVideoAction: ({ url, disabled }: ContactPopoverVideoActionProps) => React.ReactElement;
12
+ export declare const ContactPopoverVideoAction: ({ url, onClick, disabled, label }: ContactPopoverVideoActionProps) => React.ReactElement;
@@ -1,12 +1,8 @@
1
- import { Icon } from '@linagora/twake-icons';
2
1
  import { Camera } from '@linagora/twake-icons';
3
- import { IconButton, useTheme } from '@mui/material';
4
2
  import React from 'react';
5
- export const ContactPopoverVideoAction = ({ url, disabled = false }) => {
6
- const theme = useTheme();
7
- return (React.createElement(IconButton, { href: url, target: "_blank", rel: "noopener noreferrer", size: "small", disabled: disabled, sx: {
8
- border: `1px solid ${theme.vars.palette.divider}`,
9
- color: theme.vars.palette.text.icon
10
- } },
11
- React.createElement(Icon, { icon: Camera, size: 20 })));
3
+ import { useI18n } from 'twake-i18n';
4
+ import { ContactPopoverIconAction } from './ContactPopoverIconAction';
5
+ export const ContactPopoverVideoAction = ({ url, onClick, disabled = false, label }) => {
6
+ const { t } = useI18n();
7
+ return (React.createElement(ContactPopoverIconAction, { icon: Camera, label: label ?? t('ContactPopover.videoButton'), url: url, onClick: onClick, disabled: disabled }));
12
8
  };
@@ -17,6 +17,8 @@ export interface ContactPopoverProps {
17
17
  onClose?: () => void;
18
18
  /** Disable the popover (render children only) */
19
19
  disabled?: boolean;
20
+ /** Open the popover when the trigger is hovered, not only when clicked */
21
+ openOnHover?: boolean;
20
22
  }
21
23
  interface ContactPopoverComponent extends React.FC<ContactPopoverProps> {
22
24
  Actions: typeof ContactPopoverActions;
@@ -2,7 +2,7 @@ import { Icon } from '@linagora/twake-icons';
2
2
  import { Cross } from '@linagora/twake-icons';
3
3
  import { Box, IconButton, Popover, styled } from '@mui/material';
4
4
  import React, { useEffect, useRef, useState } from 'react';
5
- import { useExtendI18n } from 'twake-i18n';
5
+ import { useExtendI18n, useI18n } from 'twake-i18n';
6
6
  import { AttendeeInfo } from './AttendeeInfo';
7
7
  import { ContactPopoverActions } from './ContactPopoverActions';
8
8
  import { ContactPopoverCalendarAction } from './ContactPopoverCalendarAction';
@@ -30,8 +30,9 @@ const CloseButton = styled(IconButton)(() => ({
30
30
  top: 8,
31
31
  right: 8
32
32
  }));
33
- const ContactPopover = ({ name, email, children, onEmailCopy, onClose, disabled = false }) => {
33
+ const ContactPopover = ({ name, email, children, onEmailCopy, onClose, disabled = false, openOnHover = true }) => {
34
34
  useExtendI18n(locales);
35
+ const { t } = useI18n();
35
36
  const [anchorEl, setAnchorEl] = useState(null);
36
37
  const openTimeoutRef = useRef(null);
37
38
  const closeTimeoutRef = useRef(null);
@@ -103,7 +104,7 @@ const ContactPopover = ({ name, email, children, onEmailCopy, onClose, disabled
103
104
  return React.createElement(React.Fragment, null, triggerChildren);
104
105
  }
105
106
  return (React.createElement("div", null,
106
- React.createElement("div", { onClick: handleClick, onMouseEnter: handleTriggerMouseEnter, onMouseLeave: handleTriggerMouseLeave, onKeyDown: event => {
107
+ React.createElement("div", { onClick: handleClick, onMouseEnter: openOnHover ? handleTriggerMouseEnter : undefined, onMouseLeave: openOnHover ? handleTriggerMouseLeave : undefined, onKeyDown: event => {
107
108
  if (event.key === 'Enter' || event.key === ' ') {
108
109
  handleClick(event);
109
110
  }
@@ -119,12 +120,12 @@ const ContactPopover = ({ name, email, children, onEmailCopy, onClose, disabled
119
120
  borderRadius: 2,
120
121
  maxWidth: 400
121
122
  },
122
- onMouseEnter: handlePopoverMouseEnter,
123
- onMouseLeave: handleMouseLeave
123
+ onMouseEnter: openOnHover ? handlePopoverMouseEnter : undefined,
124
+ onMouseLeave: openOnHover ? handleMouseLeave : undefined
124
125
  }
125
126
  } },
126
127
  React.createElement(StyledPopoverContent, { onClick: event => event.stopPropagation() },
127
- React.createElement(CloseButton, { size: "small", onClick: handleClose },
128
+ React.createElement(CloseButton, { size: "small", onClick: handleClose, "aria-label": t('ContactPopover.closeButton') },
128
129
  React.createElement(Icon, { icon: Cross, size: 16 })),
129
130
  React.createElement(AttendeeInfo, { name: name, email: email, onEmailCopy: onEmailCopy }),
130
131
  actionsChild))));
@@ -2,6 +2,10 @@
2
2
  "ContactPopover": {
3
3
  "emailButton": "E-Mail senden",
4
4
  "copyTooltip": "E-Mail kopieren",
5
- "copiedTooltip": "Kopiert!"
5
+ "copiedTooltip": "Kopiert!",
6
+ "closeButton": "Schließen",
7
+ "chatButton": "Chat öffnen",
8
+ "videoButton": "Videoanruf starten",
9
+ "calendarButton": "Besprechung planen"
6
10
  }
7
11
  }
@@ -2,6 +2,10 @@
2
2
  "ContactPopover": {
3
3
  "emailButton": "Send an email",
4
4
  "copyTooltip": "Copy email",
5
- "copiedTooltip": "Copied!"
5
+ "copiedTooltip": "Copied!",
6
+ "closeButton": "Close",
7
+ "chatButton": "Open a chat",
8
+ "videoButton": "Start a video call",
9
+ "calendarButton": "Plan a meeting"
6
10
  }
7
11
  }
@@ -2,6 +2,10 @@
2
2
  "ContactPopover": {
3
3
  "emailButton": "Enviar un correo",
4
4
  "copyTooltip": "Copiar correo",
5
- "copiedTooltip": "¡Copiado!"
5
+ "copiedTooltip": "¡Copiado!",
6
+ "closeButton": "Cerrar",
7
+ "chatButton": "Abrir un chat",
8
+ "videoButton": "Iniciar una videollamada",
9
+ "calendarButton": "Planificar una reunión"
6
10
  }
7
11
  }
@@ -2,6 +2,10 @@
2
2
  "ContactPopover": {
3
3
  "emailButton": "Envoyer un email",
4
4
  "copyTooltip": "Copier l'email",
5
- "copiedTooltip": "Copié !"
5
+ "copiedTooltip": "Copié !",
6
+ "closeButton": "Fermer",
7
+ "chatButton": "Ouvrir une discussion",
8
+ "videoButton": "Lancer un appel vidéo",
9
+ "calendarButton": "Planifier une réunion"
6
10
  }
7
11
  }
@@ -2,6 +2,10 @@
2
2
  "ContactPopover": {
3
3
  "emailButton": "Invia un'email",
4
4
  "copyTooltip": "Copia email",
5
- "copiedTooltip": "Copiato!"
5
+ "copiedTooltip": "Copiato!",
6
+ "closeButton": "Chiudi",
7
+ "chatButton": "Apri una chat",
8
+ "videoButton": "Avvia una videochiamata",
9
+ "calendarButton": "Pianifica una riunione"
6
10
  }
7
11
  }
@@ -2,6 +2,10 @@
2
2
  "ContactPopover": {
3
3
  "emailButton": "Отправить email",
4
4
  "copyTooltip": "Копировать email",
5
- "copiedTooltip": "Скопировано!"
5
+ "copiedTooltip": "Скопировано!",
6
+ "closeButton": "Закрыть",
7
+ "chatButton": "Открыть чат",
8
+ "videoButton": "Начать видеозвонок",
9
+ "calendarButton": "Запланировать встречу"
6
10
  }
7
11
  }
@@ -2,6 +2,10 @@
2
2
  "ContactPopover": {
3
3
  "emailButton": "Gửi email",
4
4
  "copyTooltip": "Sao chép email",
5
- "copiedTooltip": "Đã sao chép!"
5
+ "copiedTooltip": "Đã sao chép!",
6
+ "closeButton": "Đóng",
7
+ "chatButton": "Mở trò chuyện",
8
+ "videoButton": "Bắt đầu cuộc gọi video",
9
+ "calendarButton": "Lên lịch cuộc họp"
6
10
  }
7
11
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@linagora/twake-mui",
3
3
  "type": "module",
4
- "version": "18.2.0",
4
+ "version": "18.3.0",
5
5
  "description": "MUI v9 theme system for Twake applications",
6
6
  "author": "Twake",
7
7
  "license": "MIT",