@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.
- package/CHANGELOG.md +16 -0
- package/dist/index.d.mts +78 -196
- package/dist/index.d.ts +78 -196
- package/dist/index.js +774 -201
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +734 -162
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -8
- package/src/components/__tests__/app-bar.test.tsx +23 -0
- package/src/components/actions/action.tsx +33 -0
- package/src/components/actions/actions-group.tsx +69 -0
- package/src/components/actions/link.tsx +33 -0
- package/src/components/actions/toggle-action.tsx +35 -0
- package/src/components/app-bar.tsx +42 -0
- package/src/components/locations/__tests__/locations-components.test.tsx +37 -0
- package/src/components/locations/__tests__/menus.test.tsx +158 -0
- package/src/components/locations/integrations-menu-location.tsx +44 -0
- package/src/components/locations/main-menu-location.tsx +57 -0
- package/src/components/locations/page-indication-location.tsx +8 -0
- package/src/components/locations/primary-action-location.tsx +8 -0
- package/src/components/locations/responsive-location.tsx +8 -0
- package/src/components/locations/tools-menu-location.tsx +28 -0
- package/src/components/locations/utilities-menu-location.tsx +35 -0
- package/src/components/ui/popover-menu-item.tsx +47 -0
- package/src/components/ui/popover-menu.tsx +26 -0
- package/src/components/ui/popover-sub-menu.tsx +29 -0
- package/src/components/ui/toolbar-logo.tsx +84 -0
- package/src/components/ui/toolbar-menu-item.tsx +45 -0
- package/src/components/ui/toolbar-menu-more.tsx +29 -0
- package/src/components/ui/toolbar-menu-toggle-item.tsx +34 -0
- package/src/components/ui/toolbar-menu.tsx +15 -0
- package/src/contexts/menu-context.tsx +24 -0
- package/src/extensions/documents-indicator/index.ts +1 -1
- package/src/extensions/documents-preview/index.ts +1 -1
- package/src/extensions/documents-save/components/primary-action-menu.tsx +1 -1
- package/src/extensions/documents-save/hooks/use-document-copy-and-share-props.ts +1 -1
- package/src/extensions/documents-save/hooks/use-document-save-draft-props.ts +1 -1
- package/src/extensions/documents-save/hooks/use-document-save-template-props.ts +1 -1
- package/src/extensions/documents-save/index.ts +1 -1
- package/src/extensions/documents-save/locations.ts +1 -1
- package/src/extensions/elements/index.ts +1 -1
- package/src/extensions/finder/index.ts +1 -1
- package/src/extensions/help/index.ts +1 -1
- package/src/extensions/history/index.ts +1 -1
- package/src/extensions/keyboard-shortcuts/hooks/use-action-props.ts +1 -2
- package/src/extensions/keyboard-shortcuts/index.ts +1 -1
- package/src/extensions/site-settings/hooks/use-action-props.ts +1 -2
- package/src/extensions/site-settings/index.ts +1 -1
- package/src/extensions/structure/hooks/use-action-props.ts +1 -2
- package/src/extensions/structure/index.ts +1 -1
- package/src/extensions/theme-builder/hooks/use-action-props.ts +1 -2
- package/src/extensions/theme-builder/index.ts +1 -1
- package/src/extensions/user-preferences/hooks/use-action-props.ts +1 -2
- package/src/extensions/user-preferences/index.ts +1 -1
- package/src/extensions/wordpress/index.ts +1 -1
- package/src/index.ts +14 -10
- package/src/init.ts +1 -1
- package/src/locations/__tests__/menus.test.tsx +212 -0
- package/src/locations/index.ts +27 -0
- package/src/locations/menus.tsx +172 -0
- 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,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
|
+
}
|