@elementor/editor-editing-panel 4.4.0-1090 → 4.4.0-1092

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elementor/editor-editing-panel",
3
- "version": "4.4.0-1090",
3
+ "version": "4.4.0-1092",
4
4
  "private": false,
5
5
  "author": "Elementor Team",
6
6
  "homepage": "https://elementor.com/",
@@ -39,31 +39,32 @@
39
39
  "dev": "tsup --config=../../tsup.dev.ts"
40
40
  },
41
41
  "dependencies": {
42
- "@elementor/editor": "4.4.0-1090",
43
- "@elementor/editor-canvas": "4.4.0-1090",
44
- "@elementor/editor-controls": "4.4.0-1090",
45
- "@elementor/editor-documents": "4.4.0-1090",
46
- "@elementor/editor-elements": "4.4.0-1090",
47
- "@elementor/editor-interactions": "4.4.0-1090",
48
- "@elementor/editor-notifications": "4.4.0-1090",
49
- "@elementor/editor-panels": "4.4.0-1090",
50
- "@elementor/editor-props": "4.4.0-1090",
51
- "@elementor/editor-responsive": "4.4.0-1090",
52
- "@elementor/editor-styles": "4.4.0-1090",
53
- "@elementor/editor-styles-repository": "4.4.0-1090",
54
- "@elementor/editor-ui": "4.4.0-1090",
55
- "@elementor/editor-v1-adapters": "4.4.0-1090",
56
- "@elementor/http-client": "4.4.0-1090",
42
+ "@elementor/editor": "4.4.0-1092",
43
+ "@elementor/editor-canvas": "4.4.0-1092",
44
+ "@elementor/editor-controls": "4.4.0-1092",
45
+ "@elementor/editor-current-user": "4.4.0-1092",
46
+ "@elementor/editor-documents": "4.4.0-1092",
47
+ "@elementor/editor-elements": "4.4.0-1092",
48
+ "@elementor/editor-interactions": "4.4.0-1092",
49
+ "@elementor/editor-notifications": "4.4.0-1092",
50
+ "@elementor/editor-panels": "4.4.0-1092",
51
+ "@elementor/editor-props": "4.4.0-1092",
52
+ "@elementor/editor-responsive": "4.4.0-1092",
53
+ "@elementor/editor-styles": "4.4.0-1092",
54
+ "@elementor/editor-styles-repository": "4.4.0-1092",
55
+ "@elementor/editor-ui": "4.4.0-1092",
56
+ "@elementor/editor-v1-adapters": "4.4.0-1092",
57
+ "@elementor/http-client": "4.4.0-1092",
57
58
  "@elementor/icons": "~1.75.1",
58
- "@elementor/editor-variables": "4.4.0-1090",
59
- "@elementor/locations": "4.4.0-1090",
60
- "@elementor/menus": "4.4.0-1090",
61
- "@elementor/query": "4.4.0-1090",
62
- "@elementor/schema": "4.4.0-1090",
63
- "@elementor/session": "4.4.0-1090",
59
+ "@elementor/editor-variables": "4.4.0-1092",
60
+ "@elementor/locations": "4.4.0-1092",
61
+ "@elementor/menus": "4.4.0-1092",
62
+ "@elementor/query": "4.4.0-1092",
63
+ "@elementor/schema": "4.4.0-1092",
64
+ "@elementor/session": "4.4.0-1092",
64
65
  "@elementor/ui": "1.37.5",
65
- "@elementor/utils": "4.4.0-1090",
66
- "@elementor/wp-media": "4.4.0-1090",
66
+ "@elementor/utils": "4.4.0-1092",
67
+ "@elementor/wp-media": "4.4.0-1092",
67
68
  "@wordpress/i18n": "^5.13.0"
68
69
  },
69
70
  "peerDependencies": {
@@ -0,0 +1,51 @@
1
+ import * as React from 'react';
2
+ import { type PropsWithChildren, useState } from 'react';
3
+ import { InfoCircleFilledIcon } from '@elementor/icons';
4
+ import { Alert, AlertTitle, Box, Infotip, Link } from '@elementor/ui';
5
+ import { __ } from '@wordpress/i18n';
6
+
7
+ const LEARN_MORE_URL = 'https://go.elementor.com/content-only-access-infotip';
8
+
9
+ const INFOTIP_WIDTH = 300;
10
+
11
+ export const ContentOnlyInfotip = ( { children }: PropsWithChildren ) => {
12
+ const [ isOpen, setIsOpen ] = useState( false );
13
+
14
+ return (
15
+ <Infotip
16
+ open={ isOpen }
17
+ placement="bottom"
18
+ color="secondary"
19
+ content={
20
+ <Alert color="secondary" icon={ <InfoCircleFilledIcon /> } size="small">
21
+ <AlertTitle>{ __( 'Content-only access', 'elementor' ) }</AlertTitle>
22
+ <Box component="span">
23
+ { __( 'Your Site Admin has limited this role to content editing.', 'elementor' ) }{ ' ' }
24
+ <Link href={ LEARN_MORE_URL } target="_blank" rel="noreferrer" color="info.main">
25
+ { __( 'Learn More', 'elementor' ) }
26
+ </Link>
27
+ </Box>
28
+ </Alert>
29
+ }
30
+ slotProps={ {
31
+ popper: {
32
+ sx: { width: INFOTIP_WIDTH },
33
+ // The popper is portaled, so it needs its own hover handlers to stay open while the pointer
34
+ // travels from the tab to the "Learn More" link.
35
+ onMouseEnter: () => setIsOpen( true ),
36
+ onMouseLeave: () => setIsOpen( false ),
37
+ },
38
+ } }
39
+ >
40
+ { /* A disabled MUI tab sets pointer-events: none, so the trigger has to opt back in to receive hover. */ }
41
+ <Box
42
+ component="span"
43
+ sx={ { pointerEvents: 'auto' } }
44
+ onMouseEnter={ () => setIsOpen( true ) }
45
+ onMouseLeave={ () => setIsOpen( false ) }
46
+ >
47
+ { children }
48
+ </Box>
49
+ </Infotip>
50
+ );
51
+ };
@@ -1,5 +1,6 @@
1
1
  import { Fragment } from 'react';
2
2
  import * as React from 'react';
3
+ import { useHasContentOnlyAccess } from '@elementor/editor-current-user';
3
4
  import { getWidgetsCache } from '@elementor/editor-elements';
4
5
  import { Divider, Stack, Tab, TabPanel, Tabs, useTabs } from '@elementor/ui';
5
6
  import { __ } from '@wordpress/i18n';
@@ -8,12 +9,17 @@ import { useElement } from '../contexts/element-context';
8
9
  import { ScrollProvider } from '../contexts/scroll-context';
9
10
  import { useDefaultPanelSettings } from '../hooks/use-default-panel-settings';
10
11
  import { useStateByElement } from '../hooks/use-state-by-element';
12
+ import { ContentOnlyInfotip } from './content-only-infotip';
11
13
  import { InteractionsTab } from './interactions-tab';
12
14
  import { SettingsTab } from './settings-tab';
13
15
  import { stickyHeaderStyles, StyleTab } from './style-tab';
14
16
 
15
17
  type TabValue = 'settings' | 'style' | 'interactions';
16
18
 
19
+ const CONTENT_ONLY_TAB: TabValue = 'settings';
20
+
21
+ const DESIGN_TABS: TabValue[] = [ 'style', 'interactions' ];
22
+
17
23
  export const EditingPanelTabs = () => {
18
24
  const { element } = useElement();
19
25
 
@@ -31,10 +37,24 @@ const PanelTabContent = () => {
31
37
  const editorDefaults = useDefaultPanelSettings();
32
38
  const defaultComponentTab = editorDefaults.defaultTab as TabValue;
33
39
  const isPromotedElement = !! getWidgetsCache()?.[ element.type ]?.meta?.is_pro_promotion;
40
+ const hasContentOnlyAccess = useHasContentOnlyAccess();
41
+
42
+ // A promoted element has no General tab, and its Style tab only renders an upsell, so restricting it would leave the panel empty.
43
+ const areDesignTabsRestricted = hasContentOnlyAccess && ! isPromotedElement;
34
44
 
35
45
  const [ storedTab, setCurrentTab ] = useStateByElement< TabValue >( 'tab', defaultComponentTab );
36
- const currentTab = isPromotedElement && storedTab === 'settings' ? 'style' : storedTab;
46
+ const currentTab = resolveCurrentTab( { storedTab, isPromotedElement, areDesignTabsRestricted } );
37
47
  const { getTabProps, getTabPanelProps, getTabsProps } = useTabs< TabValue >( currentTab );
48
+
49
+ const getDesignTabProps = ( tab: TabValue ) => ( {
50
+ ...getTabProps( tab ),
51
+ disabled: areDesignTabsRestricted,
52
+ sx: areDesignTabsRestricted ? { pointerEvents: 'auto' } : undefined,
53
+ } );
54
+
55
+ const withRestrictionInfotip = ( label: string ) =>
56
+ areDesignTabsRestricted ? <ContentOnlyInfotip>{ label }</ContentOnlyInfotip> : label;
57
+
38
58
  return (
39
59
  <ScrollProvider>
40
60
  <Stack direction="column" sx={ { width: '100%' } }>
@@ -45,6 +65,10 @@ const PanelTabContent = () => {
45
65
  sx={ { mt: 0.5 } }
46
66
  { ...getTabsProps() }
47
67
  onChange={ ( _: unknown, newValue: TabValue ) => {
68
+ if ( areDesignTabsRestricted && DESIGN_TABS.includes( newValue ) ) {
69
+ return;
70
+ }
71
+
48
72
  getTabsProps().onChange( _, newValue );
49
73
  setCurrentTab( newValue );
50
74
  } }
@@ -52,8 +76,14 @@ const PanelTabContent = () => {
52
76
  { ! isPromotedElement && (
53
77
  <Tab label={ __( 'General', 'elementor' ) } { ...getTabProps( 'settings' ) } />
54
78
  ) }
55
- <Tab label={ __( 'Style', 'elementor' ) } { ...getTabProps( 'style' ) } />
56
- <Tab label={ __( 'Interactions', 'elementor' ) } { ...getTabProps( 'interactions' ) } />
79
+ <Tab
80
+ label={ withRestrictionInfotip( __( 'Style', 'elementor' ) ) }
81
+ { ...getDesignTabProps( 'style' ) }
82
+ />
83
+ <Tab
84
+ label={ withRestrictionInfotip( __( 'Interactions', 'elementor' ) ) }
85
+ { ...getDesignTabProps( 'interactions' ) }
86
+ />
57
87
  </Tabs>
58
88
  <Divider />
59
89
  </Stack>
@@ -62,13 +92,37 @@ const PanelTabContent = () => {
62
92
  <SettingsTab />
63
93
  </TabPanel>
64
94
  ) }
65
- <TabPanel { ...getTabPanelProps( 'style' ) } disablePadding>
66
- <StyleTab />
67
- </TabPanel>
68
- <TabPanel { ...getTabPanelProps( 'interactions' ) } disablePadding>
69
- <InteractionsTab />
70
- </TabPanel>
95
+ { ! areDesignTabsRestricted && (
96
+ <>
97
+ <TabPanel { ...getTabPanelProps( 'style' ) } disablePadding>
98
+ <StyleTab />
99
+ </TabPanel>
100
+ <TabPanel { ...getTabPanelProps( 'interactions' ) } disablePadding>
101
+ <InteractionsTab />
102
+ </TabPanel>
103
+ </>
104
+ ) }
71
105
  </Stack>
72
106
  </ScrollProvider>
73
107
  );
74
108
  };
109
+
110
+ const resolveCurrentTab = ( {
111
+ storedTab,
112
+ isPromotedElement,
113
+ areDesignTabsRestricted,
114
+ }: {
115
+ storedTab: TabValue;
116
+ isPromotedElement: boolean;
117
+ areDesignTabsRestricted: boolean;
118
+ } ): TabValue => {
119
+ if ( areDesignTabsRestricted && DESIGN_TABS.includes( storedTab ) ) {
120
+ return CONTENT_ONLY_TAB;
121
+ }
122
+
123
+ if ( isPromotedElement && storedTab === 'settings' ) {
124
+ return 'style';
125
+ }
126
+
127
+ return storedTab;
128
+ };