Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
400 changes: 400 additions & 0 deletions src/editor/app.js

Large diffs are not rendered by default.

27 changes: 19 additions & 8 deletions src/editor/components/add-interaction-popover/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ import {
setBlockAnchorIfPossible,
openInteractionsSidebar,
} from '~interact/editor/util'
import {
getCurrentSelectedTarget,
isElementorEditor,
} from '~interact/editor/editors'
import { cloneDeep, first } from 'lodash'

import {
Expand Down Expand Up @@ -56,21 +60,24 @@ const AddInteractionPopover = props => {
const [ selected, setSelected ] = useState( initialSelected )
const [ showDescription, setShowDescription ] = useState( null )
const [ hidden, setHidden ] = useState( false )
const isElementor = isElementorEditor()

const {
getBlockNamesByClientId,
getSelectedBlockClientId,
} = useSelect( select => {
const blockEditorStore = select( 'core/block-editor' )
return {
getBlockNamesByClientId: select( 'core/block-editor' ).getBlockNamesByClientId,
getSelectedBlockClientId: select( 'core/block-editor' ).getSelectedBlockClientId,
getBlockNamesByClientId: blockEditorStore?.getBlockNamesByClientId || ( () => [] ),
getSelectedBlockClientId: blockEditorStore?.getSelectedBlockClientId || ( () => null ),
}
} )

const [ target, setTarget ] = useState( {
type: 'block',
value: getOrGenerateBlockAnchor( getSelectedBlockClientId(), false ) || '',
blockName: first( getBlockNamesByClientId( getSelectedBlockClientId() ) ) || '',
type: getCurrentSelectedTarget()?.type || ( isElementor ? 'selector' : 'block' ),
value: getCurrentSelectedTarget()?.value || getOrGenerateBlockAnchor( getSelectedBlockClientId(), false ) || '',
blockName: getCurrentSelectedTarget()?.blockName || first( getBlockNamesByClientId( getSelectedBlockClientId() ) ) || '',
options: getCurrentSelectedTarget()?.options || '',
} )
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

const libraryTitle = ! showElementOption && showPageOption ? __( 'My Page Interactions', 'interactions' )
Expand All @@ -94,7 +101,7 @@ const AddInteractionPopover = props => {
return acc
}, { elementInteractions: [], pageInteractions: [] } )

if ( hidden ) {
if ( hidden && ! isElementor ) {
return (
<BlockPickerPopover
offset={ offset }
Expand Down Expand Up @@ -226,8 +233,12 @@ const AddInteractionPopover = props => {
<TargetSelector
value={ target }
onChange={ setTarget }
hasPickerPopover={ false }
onBlockSelectClick={ () => setHidden( true ) }
hasPickerPopover={ ! isElementor }
onBlockSelectClick={ () => {
if ( ! isElementor ) {
setHidden( true )
}
} }
/>
) }

Expand Down
13 changes: 10 additions & 3 deletions src/editor/components/import-export-modal/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* External deprendencies
*/
import { getOrGenerateBlockAnchor } from '~interact/editor/util'
import { getCurrentSelectedTarget } from '~interact/editor/editors'
import { first } from 'lodash'

/**
Expand Down Expand Up @@ -52,9 +53,10 @@ const ImportExportModal = props => {
getBlockNamesByClientId,
getSelectedBlockClientId,
} = useSelect( select => {
const blockEditorStore = select( 'core/block-editor' )
return {
getBlockNamesByClientId: select( 'core/block-editor' ).getBlockNamesByClientId,
getSelectedBlockClientId: select( 'core/block-editor' ).getSelectedBlockClientId,
getBlockNamesByClientId: blockEditorStore?.getBlockNamesByClientId || ( () => [] ),
getSelectedBlockClientId: blockEditorStore?.getSelectedBlockClientId || ( () => null ),
}
} )

Expand Down Expand Up @@ -83,9 +85,14 @@ const ImportExportModal = props => {
target = null,
} = data

const selectedTarget = getCurrentSelectedTarget()
if ( selectedTarget ) {
target = selectedTarget
}

// If the currently selected block is valid, overwrite the interaction trigger.
const clientId = getSelectedBlockClientId()
if ( clientId ) {
if ( clientId && ! selectedTarget ) {
target = {
type: 'block',
value: getOrGenerateBlockAnchor( clientId, true ) || '',
Expand Down
13 changes: 8 additions & 5 deletions src/editor/components/location-rules/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@ import {
import {
Fragment, useEffect, useState,
} from '@wordpress/element'
import { select } from '@wordpress/data'
import { __, sprintf } from '@wordpress/i18n'
import apiFetch from '@wordpress/api-fetch'
import { getCurrentEditorPostContext } from '~interact/editor/editors'

const NOOP = () => {}

const getCurrentPostId = () => getCurrentEditorPostContext().postId
const getCurrentPostType = () => getCurrentEditorPostContext().postType

const updateLocation = ( locations, index1, index2, newLocation ) => {
const newLocations = cloneDeep( locations )
newLocations[ index1 ][ index2 ] = newLocation
Expand Down Expand Up @@ -94,7 +97,7 @@ const LocationRules = props => {
onChange( removeLocation( locations, i, k ) )
} }
onClickAnd={ () => {
const value = locations.length === 0 ? select( 'core/editor' ).getCurrentPostId() : ''
const value = locations.length === 0 ? getCurrentPostId() : ''
onChange( addLocation( locations, i, k + 1, {
param: 'post',
operator: '==',
Expand All @@ -111,7 +114,7 @@ const LocationRules = props => {
label={ __( 'Add rule group', 'interactions' ) }
variant="secondary"
onClick={ () => {
const value = locations.length === 0 ? select( 'core/editor' ).getCurrentPostId() : ''
const value = locations.length === 0 ? getCurrentPostId() : ''
onChange( addLocation( locations, locations.length, 0, {
param: 'post',
operator: '==',
Expand Down Expand Up @@ -151,10 +154,10 @@ const LocationRule = props => {

// If param is a post/page, then the post_id doesn't exist yet, then we need to add it near the top as "Current Post" or "Current Page"
if ( param === 'post' || param === 'page' ) {
const postType = select( 'core/editor' ).getCurrentPostType()
const postType = getCurrentPostType()
options.some( ( { post_type, options } ) => {
if ( post_type === postType ) {
const currentPostId = select( 'core/editor' ).getCurrentPostId()
const currentPostId = getCurrentPostId()
// Check if the current post is already in the list
const exists = options.some( ( { value } ) => {
return value === currentPostId
Expand Down
38 changes: 34 additions & 4 deletions src/editor/components/target-selector/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import TargetSVG from '~interact/editor/assets/target.svg'

import { GridLayout, FlexLayout } from '~interact/editor/components'
import {
getSelectedBlockAnchor,
isElementorEditor,
startElementorElementPicker,
} from '~interact/editor/editors'
import { getOrGenerateBlockAnchor, getOrGenerateBlockClass } from '~interact/editor/util'
import {
SelectControl,
Expand Down Expand Up @@ -40,6 +45,9 @@ const TargetSelector = props => {
const [ isPopoverOpen, setIsPopoverOpen ] = useState( false )
const [ buttonRef, setButtonRef ] = useState( null )
const prevValueRef = useRef( {} )
const elementPickerStopRef = useRef( null )
const isElementor = isElementorEditor()
const hasBlockEditor = !! select( 'core/block-editor' )?.getSelectedBlockClientId

const targetButton = (
<>
Expand All @@ -50,14 +58,24 @@ const TargetSelector = props => {
ref={ setButtonRef }
onClick={ () => {
onBlockSelectClick()
if ( hasPickerPopover && ! isPopoverOpen ) {
if ( isElementor ) {
elementPickerStopRef.current?.()
elementPickerStopRef.current = startElementorElementPicker( {
targetType: value.type === 'class' ? 'class' : 'selector',
onPick: target => {
onChange( target )
onBlockSelectDone()
},
onCancel: onBlockSelectDone,
} )
} else if ( hasPickerPopover && ! isPopoverOpen ) {
setIsPopoverOpen( true )
} else if ( hasPickerPopover && isPopoverOpen ) {
setIsPopoverOpen( false )
}
} }
/>
{ hasPickerPopover && isPopoverOpen && (
{ hasPickerPopover && isPopoverOpen && ! isElementor && (
<BlockPickerPopover
anchor={ anchor || buttonRef }
placement="left"
Expand Down Expand Up @@ -148,6 +166,18 @@ const TargetSelector = props => {
targetOptions = targetOptions.filter( target => target.value !== 'trigger' )
}

if ( isElementor ) {
targetOptions = targetOptions.filter( target =>
[ 'trigger', 'class', 'selector' ].includes( target.value )
)
}
Comment on lines +169 to +173
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Saved value.type === 'block' breaks the SelectControl display in Elementor mode.

When Elementor is active, 'block' and 'block-name' are removed from targetOptions (lines 169–173), but there's no corresponding reset if the current value.type is already 'block' (e.g., an interaction saved in Gutenberg and then opened in Elementor). The SelectControl receives value="block" with no matching <option>, so the browser renders the first option visually while the underlying data stays 'block'. The block input section (line 233) still renders, causing a confusing UI.

A useEffect that normalises value.type when the Elementor filter doesn't include the current type would fix this:

🐛 Proposed fix
 if ( isElementor ) {
     targetOptions = targetOptions.filter( target =>
         [ 'trigger', 'class', 'selector' ].includes( target.value )
     )
 }

+useEffect( () => {
+    if ( isElementor && ! [ 'trigger', 'class', 'selector' ].includes( value.type ) ) {
+        onChange( { ...value, type: 'selector', value: '' } )
+    }
+}, [ isElementor ] ) // eslint-disable-line react-hooks/exhaustive-deps
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if ( isElementor ) {
targetOptions = targetOptions.filter( target =>
[ 'trigger', 'class', 'selector' ].includes( target.value )
)
}
if ( isElementor ) {
targetOptions = targetOptions.filter( target =>
[ 'trigger', 'class', 'selector' ].includes( target.value )
)
}
useEffect( () => {
if ( isElementor && ! [ 'trigger', 'class', 'selector' ].includes( value.type ) ) {
onChange( { ...value, type: 'selector', value: '' } )
}
}, [ isElementor ] ) // eslint-disable-line react-hooks/exhaustive-deps
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/editor/components/target-selector/index.js` around lines 169 - 173, When
Elementor mode removes certain options from targetOptions (via isElementor),
ensure the currently selected value.type is normalized if it no longer exists:
add a useEffect that runs when isElementor or targetOptions change, checks if
value.type (from props/state) is included in targetOptions.map(t=>t.value), and
if not calls the setter that updates value.type to a safe default (e.g., the
first allowed option such as 'trigger' or ''), so the SelectControl and the
block input rendering (the block-related UI) stay in sync; reference
targetOptions, isElementor, value.type, SelectControl and the block input render
logic when implementing.


useEffect( () => {
return () => {
elementPickerStopRef.current?.()
}
}, [] )

// Watch for warnings, we need to throttle this because we are subscribed to
// the editor and changes can be fast.
const [ targetWarning, setTargetWarning ] = useState( getTargetSelectorWarning( value.type, value.value ) )
Expand Down Expand Up @@ -176,7 +206,7 @@ const TargetSelector = props => {
const newTarget = { ...value, type }

// Use any previous values we may have already entered.
if ( type === 'block-name' ) {
if ( type === 'block-name' && hasBlockEditor ) {
if ( prevValueRef.current[ type ] ) {
newTarget.value = prevValueRef.current[ type ]
} else {
Expand Down Expand Up @@ -207,7 +237,7 @@ const TargetSelector = props => {
className="interact-target-block-input"
id="interact-target-block-input"
label={ __( 'Block Anchor / ID', 'interactions' ) }
value={ value.value }
value={ value.value || ( isElementor ? '' : getSelectedBlockAnchor() || '' ) }
// When typing, the previous blockName should be invalid
onChange={ targetValue => onChange( {
...value, blockName: '', value: targetValue,
Expand Down
8 changes: 2 additions & 6 deletions src/editor/components/timeline/class-runner.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import InteractRunner from '../../../frontend/scripts/class-runner'
import { actions as actionsConfig, interactions as interactionsConfig } from 'interactions'
import { getBlockClientId } from './with-tracked-anchors'
import { getEditorCanvasElement } from '~interact/editor/editors'

const NOOP = () => {}

Expand Down Expand Up @@ -48,12 +49,7 @@ class InteractEditorRunner extends InteractRunner {
* @return {DOMElement} The document where the interactions are being previewed.
*/
getDocument() {
const iframe = document.querySelector( 'iframe[name="editor-canvas"]' )
let editorEl = document.querySelector( '.editor-styles-wrapper' )
if ( iframe ) {
editorEl = iframe.contentDocument.querySelector( '.editor-styles-wrapper' )
}
return editorEl
return getEditorCanvasElement()
}

getTimelineType( interaction ) {
Expand Down
3 changes: 2 additions & 1 deletion src/editor/components/timeline/runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { cloneDeep } from 'lodash'
import { getBlockClientId } from './with-tracked-anchors'
import { doAction } from '@wordpress/hooks'
import { select } from '@wordpress/data'
import { getEditorMode } from '~interact/editor/editors'

// Create the runner.
const runner = new InteractEditorRunner()
Expand Down Expand Up @@ -41,7 +42,7 @@ export const useTimelineRunnerRef = ( interaction, actions, timelineIndex ) => {
const runnerRef = useRef( null )
const [ initialStyles, setInitialStyles ] = useState( '' )

const renderingMode = select( 'core/editor' ).getRenderingMode()
const renderingMode = select( 'core/editor' )?.getRenderingMode?.() || getEditorMode()
const prevRenderingMode = useRef( renderingMode )

// Initialize the runner
Expand Down
11 changes: 2 additions & 9 deletions src/editor/components/timeline/use-initial-style-tag.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,9 @@

import { useEffect, useRef } from '@wordpress/element'
import { debounce } from 'lodash'
import { getEditorCanvasElement } from '~interact/editor/editors'

// Gets the main editor element, either the iframe or the main document.
const getEditorEl = () => {
const iframe = document.querySelector( 'iframe[name="editor-canvas"]' )
let editorEl = document.querySelector( '.editor-styles-wrapper' )
if ( iframe ) {
editorEl = iframe.contentDocument.querySelector( '.editor-styles-wrapper' )
}
return editorEl
}
const getEditorEl = () => getEditorCanvasElement()

// Creates a style tag inside the editor to hold the initial interaction styles
export const useInitialStyleTag = style => {
Expand Down
Loading
Loading