@elementor/editor-app-bar 0.14.0 → 0.14.2

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.
Files changed (61) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/index.d.mts +78 -196
  3. package/dist/index.d.ts +78 -196
  4. package/dist/index.js +774 -201
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +734 -162
  7. package/dist/index.mjs.map +1 -1
  8. package/package.json +9 -8
  9. package/src/components/__tests__/app-bar.test.tsx +23 -0
  10. package/src/components/actions/action.tsx +33 -0
  11. package/src/components/actions/actions-group.tsx +69 -0
  12. package/src/components/actions/link.tsx +33 -0
  13. package/src/components/actions/toggle-action.tsx +35 -0
  14. package/src/components/app-bar.tsx +42 -0
  15. package/src/components/locations/__tests__/locations-components.test.tsx +37 -0
  16. package/src/components/locations/__tests__/menus.test.tsx +158 -0
  17. package/src/components/locations/integrations-menu-location.tsx +44 -0
  18. package/src/components/locations/main-menu-location.tsx +57 -0
  19. package/src/components/locations/page-indication-location.tsx +8 -0
  20. package/src/components/locations/primary-action-location.tsx +8 -0
  21. package/src/components/locations/responsive-location.tsx +8 -0
  22. package/src/components/locations/tools-menu-location.tsx +28 -0
  23. package/src/components/locations/utilities-menu-location.tsx +35 -0
  24. package/src/components/ui/popover-menu-item.tsx +47 -0
  25. package/src/components/ui/popover-menu.tsx +26 -0
  26. package/src/components/ui/popover-sub-menu.tsx +29 -0
  27. package/src/components/ui/toolbar-logo.tsx +84 -0
  28. package/src/components/ui/toolbar-menu-item.tsx +45 -0
  29. package/src/components/ui/toolbar-menu-more.tsx +29 -0
  30. package/src/components/ui/toolbar-menu-toggle-item.tsx +34 -0
  31. package/src/components/ui/toolbar-menu.tsx +15 -0
  32. package/src/contexts/menu-context.tsx +24 -0
  33. package/src/extensions/documents-indicator/index.ts +1 -1
  34. package/src/extensions/documents-preview/index.ts +1 -1
  35. package/src/extensions/documents-save/components/primary-action-menu.tsx +1 -1
  36. package/src/extensions/documents-save/hooks/use-document-copy-and-share-props.ts +1 -1
  37. package/src/extensions/documents-save/hooks/use-document-save-draft-props.ts +1 -1
  38. package/src/extensions/documents-save/hooks/use-document-save-template-props.ts +1 -1
  39. package/src/extensions/documents-save/index.ts +1 -1
  40. package/src/extensions/documents-save/locations.ts +1 -1
  41. package/src/extensions/elements/index.ts +1 -1
  42. package/src/extensions/finder/index.ts +1 -1
  43. package/src/extensions/help/index.ts +1 -1
  44. package/src/extensions/history/index.ts +1 -1
  45. package/src/extensions/keyboard-shortcuts/hooks/use-action-props.ts +1 -2
  46. package/src/extensions/keyboard-shortcuts/index.ts +1 -1
  47. package/src/extensions/site-settings/hooks/use-action-props.ts +1 -2
  48. package/src/extensions/site-settings/index.ts +1 -1
  49. package/src/extensions/structure/hooks/use-action-props.ts +1 -2
  50. package/src/extensions/structure/index.ts +1 -1
  51. package/src/extensions/theme-builder/hooks/use-action-props.ts +1 -2
  52. package/src/extensions/theme-builder/index.ts +1 -1
  53. package/src/extensions/user-preferences/hooks/use-action-props.ts +1 -2
  54. package/src/extensions/user-preferences/index.ts +1 -1
  55. package/src/extensions/wordpress/index.ts +1 -1
  56. package/src/index.ts +14 -10
  57. package/src/init.ts +1 -1
  58. package/src/locations/__tests__/menus.test.tsx +212 -0
  59. package/src/locations/index.ts +27 -0
  60. package/src/locations/menus.tsx +172 -0
  61. package/src/types.ts +4 -0
@@ -0,0 +1,42 @@
1
+ import * as React from 'react';
2
+ import { AppBar as BaseAppBar, Box, Divider, Grid, ThemeProvider, Toolbar } from '@elementor/ui';
3
+ import MainMenuLocation from './locations/main-menu-location';
4
+ import ToolsMenuLocation from './locations/tools-menu-location';
5
+ import UtilitiesMenuLocation from './locations/utilities-menu-location';
6
+ import PrimaryActionLocation from './locations/primary-action-location';
7
+ import ToolbarMenu from './ui/toolbar-menu';
8
+ import PageIndicationLocation from './locations/page-indication-location';
9
+ import ResponsiveLocation from './locations/responsive-location';
10
+ import { __useActiveDocument as useActiveDocument } from '@elementor/editor-documents';
11
+
12
+ export default function AppBar() {
13
+ const document = useActiveDocument();
14
+ return (
15
+ <ThemeProvider colorScheme="dark">
16
+ <BaseAppBar position="sticky">
17
+ <Toolbar disableGutters variant="dense">
18
+ <Box display="grid" gridTemplateColumns="repeat(3, 1fr)" flexGrow={ 1 }>
19
+ <Grid container>
20
+ <MainMenuLocation />
21
+ { document?.permissions?.allowAddingWidgets &&
22
+ <ToolsMenuLocation /> }
23
+ </Grid>
24
+ <Grid container justifyContent="center">
25
+ <ToolbarMenu spacing={ 1.5 }>
26
+ <Divider orientation="vertical" />
27
+ <PageIndicationLocation />
28
+ <Divider orientation="vertical" />
29
+ <ResponsiveLocation />
30
+ <Divider orientation="vertical" />
31
+ </ToolbarMenu>
32
+ </Grid>
33
+ <Grid container justifyContent="flex-end">
34
+ <UtilitiesMenuLocation />
35
+ <PrimaryActionLocation />
36
+ </Grid>
37
+ </Box>
38
+ </Toolbar>
39
+ </BaseAppBar>
40
+ </ThemeProvider>
41
+ );
42
+ }
@@ -0,0 +1,37 @@
1
+ import * as React from 'react';
2
+ import { render, screen } from '@testing-library/react';
3
+ import ResponsiveLocation from '../responsive-location';
4
+ import PrimaryActionLocation from '../primary-action-location';
5
+ import PageIndicationLocation from '../page-indication-location';
6
+ import { injectIntoPageIndication, injectIntoPrimaryAction, injectIntoResponsive } from '../../../locations';
7
+
8
+ describe( 'Locations components', () => {
9
+ it.each( [
10
+ {
11
+ name: 'page indication',
12
+ component: PageIndicationLocation,
13
+ inject: injectIntoPageIndication,
14
+ },
15
+ {
16
+ name: 'responsive',
17
+ component: ResponsiveLocation,
18
+ inject: injectIntoResponsive,
19
+ },
20
+ {
21
+ name: 'primary action',
22
+ component: PrimaryActionLocation,
23
+ inject: injectIntoPrimaryAction,
24
+ },
25
+ ] )( 'should inject into $name', ( { inject, component: Component } ) => {
26
+ // Act.
27
+ inject( {
28
+ id: 'test',
29
+ component: () => <span>test</span>,
30
+ } );
31
+
32
+ // Assert.
33
+ render( <Component /> );
34
+
35
+ expect( screen.getByText( 'test' ) ).toBeInTheDocument();
36
+ } );
37
+ } );
@@ -0,0 +1,158 @@
1
+ import * as React from 'react';
2
+ import { fireEvent, render, screen } from '@testing-library/react';
3
+ import MainMenuLocation from '../main-menu-location';
4
+ import ToolsMenuLocation from '../tools-menu-location';
5
+ import UtilitiesMenuLocation from '../utilities-menu-location';
6
+ import { integrationsMenu, mainMenu, toolsMenu, utilitiesMenu } from '../../../locations';
7
+ import {
8
+ createMockMenuAction,
9
+ createMockMenuLink,
10
+ createMockMenuToggleAction,
11
+ createMockSubMenu,
12
+ } from 'test-utils';
13
+
14
+ describe( 'Menus components', () => {
15
+ describe( 'Main menu', () => {
16
+ it( 'should render ordered menu items in a popover', () => {
17
+ // Arrange.
18
+ mainMenu.registerAction( {
19
+ ...createMockMenuAction(),
20
+ id: 'test-action',
21
+ } );
22
+
23
+ mainMenu.registerToggleAction( {
24
+ ...createMockMenuToggleAction(),
25
+ id: 'test-toggle-action',
26
+ } );
27
+
28
+ mainMenu.registerLink( {
29
+ ...createMockMenuLink(),
30
+ group: 'exits',
31
+ id: 'test-link',
32
+ } );
33
+
34
+ // Act.
35
+ render( <MainMenuLocation /> );
36
+
37
+ fireEvent.click( screen.getByRole( 'button' ) ); // Opens the popover menu
38
+
39
+ // Assert.
40
+ const menuItems = screen.getAllByRole( 'menuitem' );
41
+
42
+ expect( menuItems ).toHaveLength( 3 );
43
+
44
+ // Ensure the last one is the link, so the order is correct (`default`, then `exits`).
45
+ expect( menuItems[ 2 ] ).toHaveAttribute( 'href', 'https://elementor.com' );
46
+ } );
47
+
48
+ it( 'should render the integrations menu inside the tools menu if there are registered integrations', () => {
49
+ integrationsMenu.registerAction( {
50
+ ...createMockMenuAction(),
51
+ } );
52
+
53
+ render( <ToolsMenuLocation /> );
54
+
55
+ expect( screen.getByLabelText( 'Integrations' ) ).toBeInTheDocument();
56
+
57
+ fireEvent.click( screen.getByLabelText( 'Integrations' ) ); // Opens the integrations menu.
58
+
59
+ expect( screen.getByText( 'Test' ) ).toBeInTheDocument();
60
+ } );
61
+
62
+ it( 'should render groups and nested groups of actions', () => {
63
+ const subMenu = integrationsMenu.registerSubMenu( {
64
+ ...createMockSubMenu(),
65
+ } );
66
+ subMenu.registerAction( {
67
+ ...createMockMenuAction(),
68
+ id: 'sub-group-action',
69
+ } );
70
+ const subMenuSubGroup = subMenu.registerSubMenu( {
71
+ ...createMockSubMenu( { title: 'Sub group' } ),
72
+ } );
73
+ subMenuSubGroup.registerAction( {
74
+ ...createMockMenuAction( { title: 'Sub group action' } ),
75
+ id: 'sub-sub-group-action',
76
+ } );
77
+
78
+ // Act.
79
+ render( <ToolsMenuLocation /> );
80
+
81
+ // Assert.
82
+ expect( screen.getByLabelText( 'Integrations' ) ).toBeInTheDocument();
83
+
84
+ // Act.
85
+ fireEvent.click( screen.getByLabelText( 'Integrations' ) ); // Opens the integrations menu.
86
+
87
+ // Assert.
88
+ expect( screen.getByText( 'Test Group' ) ).toBeInTheDocument();
89
+
90
+ // Act.
91
+ fireEvent.mouseOver( screen.getByText( 'Test Group' ) ); // Opens the Test Group menu.
92
+
93
+ // Assert.
94
+ expect( screen.getByText( 'Sub group' ) ).toBeInTheDocument();
95
+
96
+ // Act.
97
+ fireEvent.mouseOver( screen.getByText( 'Sub group' ) ); // Opens the Sub group menu.
98
+
99
+ expect( screen.getByText( 'Sub group action' ) ).toBeInTheDocument();
100
+ } );
101
+
102
+ it( 'should NOT render the integrations menu inside if there are no registered integrations', () => {
103
+ // Act.
104
+ render( <MainMenuLocation /> );
105
+
106
+ fireEvent.click( screen.getByRole( 'button' ) ); // Opens the popover menu
107
+
108
+ // Assert.
109
+ expect( screen.queryByText( 'Integrations' ) ).not.toBeInTheDocument();
110
+ } );
111
+ } );
112
+
113
+ describe.each( [
114
+ {
115
+ menuName: 'Tools',
116
+ menu: toolsMenu,
117
+ maxItems: 5,
118
+ Component: ToolsMenuLocation,
119
+ },
120
+ {
121
+ menuName: 'Utilities',
122
+ menu: utilitiesMenu,
123
+ maxItems: 4,
124
+ Component: UtilitiesMenuLocation,
125
+ },
126
+ ] )( '$menuName menu', ( { maxItems, menu, Component } ) => {
127
+ it( `should render ${ maxItems } menu items in a toolbar and the rest in a popover`, () => {
128
+ // Arrange.
129
+ const extraAfterMax = 2;
130
+
131
+ for ( let i = 0; i < maxItems + extraAfterMax; i++ ) {
132
+ menu.registerAction( {
133
+ id: `test-${ i }`,
134
+ props: {
135
+ title: `Test ${ i }`,
136
+ icon: () => <span>a</span>,
137
+ },
138
+ } );
139
+ }
140
+
141
+ // Act.
142
+ render( <Component /> );
143
+
144
+ // Assert.
145
+ const toolbarButtons = screen.getAllByRole( 'button' );
146
+ const popoverButton = toolbarButtons[ maxItems ];
147
+
148
+ expect( toolbarButtons ).toHaveLength( maxItems + 1 ); // Including the popover button.
149
+ expect( popoverButton ).toHaveAttribute( 'aria-label', 'More' );
150
+
151
+ // Act.
152
+ fireEvent.click( popoverButton );
153
+
154
+ // Assert.
155
+ expect( screen.getAllByRole( 'menuitem' ) ).toHaveLength( extraAfterMax );
156
+ } );
157
+ } );
158
+ } );
@@ -0,0 +1,44 @@
1
+ import * as React from 'react';
2
+ import { integrationsMenu } from '../../locations';
3
+ import {
4
+ bindMenu,
5
+ bindTrigger,
6
+ usePopupState,
7
+ } from '@elementor/ui';
8
+ import { __ } from '@wordpress/i18n';
9
+ import { PlugIcon } from '@elementor/icons';
10
+ import PopoverMenu from '../ui/popover-menu';
11
+ import ToolbarMenuItem from '../ui/toolbar-menu-item';
12
+
13
+ const { useMenuItems } = integrationsMenu;
14
+
15
+ export default function IntegrationsMenuLocation() {
16
+ const menuItems = useMenuItems();
17
+
18
+ const popupState = usePopupState( {
19
+ variant: 'popover',
20
+ popupId: 'elementor-v2-app-bar-integrations',
21
+ } );
22
+
23
+ if ( menuItems.default.length === 0 ) {
24
+ return null;
25
+ }
26
+
27
+ return (
28
+ <>
29
+ <ToolbarMenuItem { ...bindTrigger( popupState ) } title={ __( 'Integrations', 'elementor' ) }>
30
+ <PlugIcon />
31
+ </ToolbarMenuItem>
32
+ <PopoverMenu
33
+ onClick={ popupState.close }
34
+ { ...bindMenu( popupState ) }
35
+ marginThreshold={ 8 }
36
+ open={ popupState.isOpen }
37
+ >
38
+ { menuItems.default.map(
39
+ ( { MenuItem: IntegrationsMenuItem, id } ) => <IntegrationsMenuItem key={ id } />
40
+ ) }
41
+ </PopoverMenu>
42
+ </>
43
+ );
44
+ }
@@ -0,0 +1,57 @@
1
+ import * as React from 'react';
2
+ import { usePopupState, bindMenu, bindTrigger, Stack, Divider } from '@elementor/ui';
3
+ import { mainMenu } from '../../locations';
4
+ import PopoverMenu from '../ui/popover-menu';
5
+ import ToolbarLogo from '../ui/toolbar-logo';
6
+ import { ExtendedWindow } from '../../types';
7
+
8
+ const { useMenuItems } = mainMenu;
9
+
10
+ export default function MainMenuLocation() {
11
+ const menuItems = useMenuItems();
12
+
13
+ const popupState = usePopupState( {
14
+ variant: 'popover',
15
+ popupId: 'elementor-v2-app-bar-main-menu',
16
+ } );
17
+
18
+ const toolbarLogoProps = bindTrigger( popupState );
19
+
20
+ const onToolbarClick: React.MouseEventHandler = ( e ) => {
21
+ const extendedWindow = window as unknown as ExtendedWindow;
22
+ const config = extendedWindow?.elementor?.editorEvents?.config;
23
+
24
+ if ( config ) {
25
+ extendedWindow.elementor.editorEvents.dispatchEvent(
26
+ config.names.topBar.elementorLogoDropdown,
27
+ {
28
+ location: config.locations.topBar,
29
+ secondaryLocation: config.secondaryLocations.elementorLogo,
30
+ trigger: config.triggers.dropdownClick,
31
+ element: config.elements.buttonIcon,
32
+ },
33
+ );
34
+ }
35
+
36
+ toolbarLogoProps.onClick( e );
37
+ };
38
+
39
+ return (
40
+ <Stack sx={ { paddingInlineStart: 3 } } direction="row" alignItems="center">
41
+ <ToolbarLogo
42
+ { ...toolbarLogoProps }
43
+ onClick={ onToolbarClick }
44
+ selected={ popupState.isOpen }
45
+ />
46
+ <PopoverMenu
47
+ onClick={ popupState.close }
48
+ { ...bindMenu( popupState ) }
49
+ marginThreshold={ 8 }
50
+ >
51
+ { menuItems.default.map( ( { MenuItem, id } ) => <MenuItem key={ id } /> ) }
52
+ { menuItems.exits.length > 0 && <Divider /> }
53
+ { menuItems.exits.map( ( { MenuItem, id } ) => <MenuItem key={ id } /> ) }
54
+ </PopoverMenu>
55
+ </Stack>
56
+ );
57
+ }
@@ -0,0 +1,8 @@
1
+ import * as React from 'react';
2
+ import { PageIndicationSlot } from '../../locations';
3
+
4
+ export default function PageIndicationLocation() {
5
+ return (
6
+ <PageIndicationSlot />
7
+ );
8
+ }
@@ -0,0 +1,8 @@
1
+ import * as React from 'react';
2
+ import { PrimaryActionSlot } from '../../locations';
3
+
4
+ export default function PrimaryActionLocation() {
5
+ return (
6
+ <PrimaryActionSlot />
7
+ );
8
+ }
@@ -0,0 +1,8 @@
1
+ import * as React from 'react';
2
+ import { ResponsiveSlot } from '../../locations';
3
+
4
+ export default function ResponsiveLocation() {
5
+ return (
6
+ <ResponsiveSlot />
7
+ );
8
+ }
@@ -0,0 +1,28 @@
1
+ import * as React from 'react';
2
+ import { toolsMenu } from '../../locations';
3
+ import ToolbarMenu from '../ui/toolbar-menu';
4
+ import ToolbarMenuMore from '../ui/toolbar-menu-more';
5
+ import IntegrationsMenuLocation from './integrations-menu-location';
6
+
7
+ const MAX_TOOLBAR_ACTIONS = 5;
8
+
9
+ const { useMenuItems } = toolsMenu;
10
+
11
+ export default function ToolsMenuLocation() {
12
+ const menuItems = useMenuItems();
13
+
14
+ const toolbarMenuItems = menuItems.default.slice( 0, MAX_TOOLBAR_ACTIONS );
15
+ const popoverMenuItems = menuItems.default.slice( MAX_TOOLBAR_ACTIONS );
16
+
17
+ return (
18
+ <ToolbarMenu>
19
+ { toolbarMenuItems.map( ( { MenuItem, id } ) => <MenuItem key={ id } /> ) }
20
+ <IntegrationsMenuLocation />
21
+ { popoverMenuItems.length > 0 && (
22
+ <ToolbarMenuMore id="elementor-editor-app-bar-tools-more">
23
+ { popoverMenuItems.map( ( { MenuItem, id } ) => <MenuItem key={ id } /> ) }
24
+ </ToolbarMenuMore>
25
+ ) }
26
+ </ToolbarMenu>
27
+ );
28
+ }
@@ -0,0 +1,35 @@
1
+ import * as React from 'react';
2
+ import { Fragment } from 'react';
3
+ import ToolbarMenu from '../ui/toolbar-menu';
4
+ import { utilitiesMenu } from '../../locations';
5
+ import { Divider } from '@elementor/ui';
6
+ import ToolbarMenuMore from '../ui/toolbar-menu-more';
7
+
8
+ const MAX_TOOLBAR_ACTIONS = 4;
9
+
10
+ const { useMenuItems } = utilitiesMenu;
11
+
12
+ export default function UtilitiesMenuLocation() {
13
+ const menuItems = useMenuItems();
14
+
15
+ const toolbarMenuItems = menuItems.default.slice( 0, MAX_TOOLBAR_ACTIONS );
16
+ const popoverMenuItems = menuItems.default.slice( MAX_TOOLBAR_ACTIONS );
17
+
18
+ return (
19
+ <ToolbarMenu>
20
+ { toolbarMenuItems.map(
21
+ ( { MenuItem, id }, index ) => (
22
+ <Fragment key={ id }>
23
+ { index === 0 && <Divider orientation="vertical" /> }
24
+ <MenuItem />
25
+ </Fragment>
26
+ )
27
+ ) }
28
+ { popoverMenuItems.length > 0 && (
29
+ <ToolbarMenuMore id="elementor-editor-app-bar-utilities-more">
30
+ { popoverMenuItems.map( ( { MenuItem, id } ) => <MenuItem key={ id } /> ) }
31
+ </ToolbarMenuMore>
32
+ ) }
33
+ </ToolbarMenu>
34
+ );
35
+ }
@@ -0,0 +1,47 @@
1
+ import * as React from 'react';
2
+ import {
3
+ MenuItem,
4
+ MenuItemProps,
5
+ ListItemText,
6
+ ListItemIcon,
7
+ withDirection,
8
+ } from '@elementor/ui';
9
+ import { ArrowUpRightIcon, ChevronRightIcon } from '@elementor/icons';
10
+
11
+ type ExtraProps = {
12
+ href?: string;
13
+ target?: string;
14
+ text?: string;
15
+ icon?: JSX.Element;
16
+ isGroupParent?: boolean;
17
+ }
18
+
19
+ export type PopoverMenuItemProps = MenuItemProps & ExtraProps;
20
+
21
+ const DirectionalArrowIcon = withDirection( ArrowUpRightIcon );
22
+ const DirectionalChevronIcon = withDirection( ChevronRightIcon );
23
+
24
+ export default function PopoverMenuItem( { text, icon, onClick, href, target, disabled, isGroupParent, ...props }: PopoverMenuItemProps ) {
25
+ const isExternalLink = href && target === '_blank';
26
+
27
+ return (
28
+ <MenuItem
29
+ { ...props }
30
+ disabled={ disabled }
31
+ onClick={ onClick }
32
+ component={ href ? 'a' : 'div' }
33
+ href={ href }
34
+ target={ target }
35
+ sx={ {
36
+ '&:hover': {
37
+ color: 'text.primary', // Overriding global CSS from the editor.
38
+ },
39
+ } }
40
+ >
41
+ <ListItemIcon>{ icon }</ListItemIcon>
42
+ <ListItemText primary={ text } />
43
+ { isExternalLink && <DirectionalArrowIcon /> }
44
+ { isGroupParent && <DirectionalChevronIcon /> }
45
+ </MenuItem>
46
+ );
47
+ }
@@ -0,0 +1,26 @@
1
+ import * as React from 'react';
2
+ import { Menu, MenuProps, PopupState } from '@elementor/ui';
3
+ import { MenuContextProvider } from '../../contexts/menu-context';
4
+
5
+ export type PopoverMenuProps = MenuProps & {
6
+ popupState?: PopupState
7
+ };
8
+
9
+ export default function PopoverMenu( { children, popupState, ...props }: PopoverMenuProps ) {
10
+ return (
11
+ <MenuContextProvider type={ 'popover' } popupState={ popupState }>
12
+ <Menu
13
+ PaperProps={ {
14
+ sx: { mt: 1.5 },
15
+ } }
16
+ { ...props }
17
+ MenuListProps={ {
18
+ component: 'div',
19
+ dense: true,
20
+ } }
21
+ >
22
+ { children }
23
+ </Menu>
24
+ </MenuContextProvider>
25
+ );
26
+ }
@@ -0,0 +1,29 @@
1
+ import * as React from 'react';
2
+ import PopoverMenu, { PopoverMenuProps } from './popover-menu';
3
+ import { useTheme } from '@elementor/ui';
4
+
5
+ export default function PopoverSubMenu( { children, ...props }: PopoverMenuProps ) {
6
+ const theme = useTheme();
7
+ const isRTL = theme.direction === 'rtl';
8
+
9
+ return (
10
+ <PopoverMenu
11
+ sx={ { pointerEvents: 'none' } }
12
+ PaperProps={ {
13
+ sx: {
14
+ // This is a workaround to support RTL in PopoverMenu, since it doesn't support it yet.
15
+ ...( isRTL
16
+ ? { marginInlineEnd: -1 }
17
+ : { marginInlineStart: 1 }
18
+ ),
19
+ pointerEvents: 'auto',
20
+ },
21
+ } }
22
+ anchorOrigin={ { vertical: 'center', horizontal: isRTL ? 'left' : 'right' } }
23
+ transformOrigin={ { vertical: 'center', horizontal: isRTL ? 'right' : 'left' } }
24
+ { ...props }
25
+ >
26
+ { children }
27
+ </PopoverMenu>
28
+ );
29
+ }
@@ -0,0 +1,84 @@
1
+ import * as React from 'react';
2
+ import { useState } from 'react';
3
+ import { __ } from '@wordpress/i18n';
4
+ import { ToggleButton, ToggleButtonProps, SvgIconProps, SvgIcon, styled } from '@elementor/ui';
5
+
6
+ interface StyledElementorLogoProps extends SvgIconProps {
7
+ showMenuIcon?: boolean;
8
+ }
9
+
10
+ type ToolbarLogoProps = Omit<ToggleButtonProps, 'value'>;
11
+
12
+ const ElementorLogo = ( props: SvgIconProps ) => {
13
+ return (
14
+ <SvgIcon viewBox="0 0 32 32" { ...props }>
15
+ <g>
16
+ <circle cx="16" cy="16" r="16" />
17
+ <path d="M11.7 9H9V22.3H11.7V9Z" />
18
+ <path d="M22.4 9H9V11.7H22.4V9Z" />
19
+ <path d="M22.4 14.4004H9V17.1004H22.4V14.4004Z" />
20
+ <path d="M22.4 19.6992H9V22.3992H22.4V19.6992Z" />
21
+ </g>
22
+ </SvgIcon>
23
+ );
24
+ };
25
+
26
+ const StyledToggleButton = styled( ToggleButton )( ( { theme } ) => ( {
27
+ padding: 0,
28
+ border: 0,
29
+ color: theme.palette.text.primary,
30
+ '&.MuiToggleButton-root:hover': {
31
+ backgroundColor: 'initial',
32
+ },
33
+ '&.MuiToggleButton-root.Mui-selected': {
34
+ backgroundColor: 'initial',
35
+ },
36
+ } ) );
37
+
38
+ const StyledElementorLogo = styled( ElementorLogo, {
39
+ shouldForwardProp: ( prop ) => prop !== 'showMenuIcon',
40
+ } )<StyledElementorLogoProps>( ( { theme, showMenuIcon } ) => ( {
41
+ '& path': {
42
+ fill: theme.palette.background.default,
43
+ transition: 'all 0.2s linear',
44
+ transformOrigin: 'bottom left',
45
+ '&:first-of-type': {
46
+ transitionDelay: ! showMenuIcon && '0.2s',
47
+ transform: showMenuIcon && 'translateY(-9px) scaleY(0)',
48
+ },
49
+ '&:not(:first-of-type)': {
50
+ // Emotion automatically change 4 to -4 in RTL moode.
51
+ transform: ! showMenuIcon && `translateX(${ theme.direction === 'rtl' ? '4' : '9' }px) scaleX(0.6)`,
52
+ },
53
+ '&:nth-of-type(2)': {
54
+ transitionDelay: showMenuIcon ? '0' : '0.2s',
55
+ },
56
+ '&:nth-of-type(3)': {
57
+ transitionDelay: '0.1s',
58
+ },
59
+ '&:nth-of-type(4)': {
60
+ transitionDelay: showMenuIcon ? '0.2s' : '0',
61
+ },
62
+ },
63
+ } ) );
64
+
65
+ export default function ToolbarLogo( props: ToolbarLogoProps ) {
66
+ const [ isHoverState, setIsHoverState ] = useState( false );
67
+ const showMenuIcon = props.selected || isHoverState;
68
+
69
+ return (
70
+ <StyledToggleButton
71
+ { ...props }
72
+ value="selected"
73
+ size="large"
74
+ onMouseEnter={ () => setIsHoverState( true ) }
75
+ onMouseLeave={ () => setIsHoverState( false ) }
76
+ >
77
+ <StyledElementorLogo
78
+ fontSize="large"
79
+ showMenuIcon={ showMenuIcon }
80
+ titleAccess={ __( 'Elementor Logo', 'elementor' ) }
81
+ />
82
+ </StyledToggleButton>
83
+ );
84
+ }
@@ -0,0 +1,45 @@
1
+ import * as React from 'react';
2
+ import { Box, IconButton, IconButtonProps, Tooltip as BaseTooltip, TooltipProps } from '@elementor/ui';
3
+
4
+ export type ToolbarMenuItemProps = IconButtonProps & {
5
+ title?: string;
6
+ selected?: boolean;
7
+ }
8
+
9
+ export default function ToolbarMenuItem( { title, ...props }: ToolbarMenuItemProps ) {
10
+ return (
11
+ <Tooltip title={ title }>
12
+ { /* @see https://mui.com/material-ui/react-tooltip/#disabled-elements */ }
13
+ <Box component="span" aria-label={ undefined }>
14
+ <IconButton
15
+ { ...props }
16
+ aria-label={ title }
17
+ size="medium"
18
+ sx={ {
19
+ '& svg': {
20
+ fontSize: '1.25rem',
21
+ height: '1em',
22
+ width: '1em',
23
+ },
24
+ '&:hover': {
25
+ color: 'text.primary',
26
+ },
27
+ } }
28
+ />
29
+ </Box>
30
+ </Tooltip>
31
+ );
32
+ }
33
+
34
+ function Tooltip( props: TooltipProps ) {
35
+ return <BaseTooltip
36
+ PopperProps={ {
37
+ sx: {
38
+ '&.MuiTooltip-popper .MuiTooltip-tooltip.MuiTooltip-tooltipPlacementBottom': {
39
+ mt: 2,
40
+ },
41
+ },
42
+ } }
43
+ { ...props }
44
+ />;
45
+ }