From 4c2fca87c8a94445226aedf44cbb01473ef99ac1 Mon Sep 17 00:00:00 2001
From: Alquen Sarmiento
Date: Thu, 7 May 2026 13:25:10 +0800
Subject: [PATCH 01/14] feat: add elementor integration
---
src/editor/app.js | 400 +++++++++++++++
.../add-interaction-popover/index.js | 27 +-
.../components/import-export-modal/index.js | 13 +-
src/editor/components/location-rules/index.js | 13 +-
.../components/target-selector/index.js | 38 +-
.../components/timeline/class-runner.js | 8 +-
src/editor/components/timeline/runner.js | 3 +-
.../timeline/use-initial-style-tag.js | 11 +-
src/editor/editor.js | 454 +-----------------
src/editor/editor.php | 34 +-
src/editor/editor.scss | 62 ++-
src/editor/editors/abstract.js | 87 ++++
src/editor/editors/elementor.js | 282 +++++++++++
src/editor/editors/gutenberg.js | 129 +++++
src/editor/editors/index.js | 42 ++
src/editor/hooks/use-interactions.js | 29 +-
src/editor/plugins/index.js | 12 +-
src/editor/util/index.js | 25 +-
18 files changed, 1147 insertions(+), 522 deletions(-)
create mode 100644 src/editor/app.js
create mode 100644 src/editor/editors/abstract.js
create mode 100644 src/editor/editors/elementor.js
create mode 100644 src/editor/editors/gutenberg.js
create mode 100644 src/editor/editors/index.js
diff --git a/src/editor/app.js b/src/editor/app.js
new file mode 100644
index 0000000..e0cba9e
--- /dev/null
+++ b/src/editor/app.js
@@ -0,0 +1,400 @@
+import ElementSVG from './assets/element.svg'
+import PageSVG from './assets/page.svg'
+import {
+ AddInteractionButton,
+ InteractionButton,
+ InteractionPanel,
+ ImportExportModal,
+} from './components'
+import { createNewInteraction, createNewAction } from './util'
+import { useInteractions } from './hooks'
+import { interactions as interactionsConfig, manageInteractionsUrl } from 'interactions'
+import { InteractionLibrary } from './interaction-library'
+import { isGutenbergEditor } from '~interact/editor/editors'
+
+import { __ } from '@wordpress/i18n'
+import { upload } from '@wordpress/icons'
+import {
+ PanelBody,
+ Button,
+ BaseControl,
+ Notice,
+} from '@wordpress/components'
+import {
+ useState,
+ useCallback,
+ useRef,
+ useEffect,
+ createInterpolateElement,
+} from '@wordpress/element'
+import { useSelect, useDispatch } from '@wordpress/data'
+
+import useOnPostPreview from './use-on-post-save'
+
+const InteractionsApp = ( {
+ selectedBlockAnchor = null,
+ enablePostPreviewGuard = true,
+} ) => {
+ const isGutenberg = isGutenbergEditor()
+ const interactionLibraryMode = useSelect( select =>
+ select( 'interact/interaction-library-modal' ).getMode(),
+ [] )
+ // Interaction library open modal and set target function.
+ const {
+ setMode: setInteractionLibraryMode,
+ } = useDispatch( 'interact/interaction-library-modal' )
+
+ const [ selectedInteraction, setSelectedInteraction ] = useState( null )
+ const [ editPropsPassed, setEditPropsPassed ] = useState( {} )
+ const [ editMode, setEditMode ] = useState( 'edit' )
+ const [ isShowingError, setIsShowingError ] = useState( true )
+ const [ importExportModalProps, setImportExportModalProps ] = useState( null )
+
+ const sidebarRef = useRef()
+ // This gets updated when the current interaction being edited is dirty.
+ const isDirtyRef = useRef( false )
+
+ const {
+ interactions: allInteractions,
+ interactionsFiltered: interactions,
+ loadingError,
+ updateInteraction,
+ deleteInteraction,
+ } = useInteractions()
+
+ // This listens to the post preview button and publish button and asks the
+ // user to save the interaction if it's dirty.
+ const onPostSaveCallback = proceedSaveCallback => {
+ if ( isDirtyRef.current ) {
+ // eslint-disable-next-line no-alert
+ if ( confirm( __( 'You have unsaved changes in your interaction. Do you want to save it before continuing?', 'interactions' ) ) ) {
+ window?.dispatchEvent( new CustomEvent( 'interact/save-interaction', {
+ detail: {
+ // This callback will be called after the interaction is saved.
+ callback: proceedSaveCallback,
+ },
+ } ) )
+ return true
+ }
+ }
+ }
+
+ useOnPostPreview( enablePostPreviewGuard ? onPostSaveCallback : () => {} )
+
+ const getInteractionFromKey = useCallback( key => {
+ return allInteractions.find( interaction => interaction.key === key )
+ }, [ allInteractions ] )
+
+ const onAddInteractionHandler = useCallback( ( interactionType, target = null, props = {} ) => {
+ if ( selectedInteraction && isDirtyRef.current ) {
+ alert( __( 'You are currently editing an interaction, please save or discard your changes first.', 'interactions' ) )// eslint-disable-line no-alert
+ return
+ }
+ setEditMode( 'new' )
+ const newInteraction = createNewInteraction( interactionType, target, props )
+ setSelectedInteraction( newInteraction )
+ }, [ selectedInteraction ] )
+
+ const onEditInteractionHandler = useCallback( ( keyOrInteraction, editProps ) => {
+ if ( selectedInteraction && isDirtyRef.current ) {
+ alert( __( 'You are currently editing an interaction, please save or discard your changes first.', 'interactions' ) )// eslint-disable-line no-alert
+ return
+ }
+ // If editMode is provided (e.g. when duplicating), set the editMode state accordingly.
+ if ( typeof editProps.editMode !== 'undefined' ) {
+ setEditMode( editProps.editMode )
+ }
+ setEditPropsPassed( editProps )
+ setSelectedInteraction( typeof keyOrInteraction === 'string' ? getInteractionFromKey( keyOrInteraction ) : keyOrInteraction )
+ }, [ getInteractionFromKey, selectedInteraction ] )
+
+ // Listen to external adds of interactions from the main toolbar button.
+ useEffect( () => {
+ const onAddInteractionEventHandler = event => {
+ onAddInteractionHandler( event.detail.type, event.detail.target, event.detail.props )
+ }
+ const onEditInteractionEventHandler = event => {
+ const {
+ key, interaction, ...editProps
+ } = event.detail
+ onEditInteractionHandler( key || interaction, editProps )
+ }
+
+ window?.addEventListener( 'interact/add-interaction', onAddInteractionEventHandler )
+ window?.addEventListener( 'interact/edit-interaction', onEditInteractionEventHandler )
+
+ return () => {
+ window?.removeEventListener( 'interact/add-interaction', onAddInteractionEventHandler )
+ window?.removeEventListener( 'interact/edit-interaction', onEditInteractionEventHandler )
+ }
+ }, [ onAddInteractionHandler, onEditInteractionHandler ] )
+
+ useEffect( () => {
+ if ( ! selectedInteraction ) {
+ setEditPropsPassed( {} )
+ }
+ }, [ selectedInteraction ] )
+
+ useEffect( () => {
+ const dismissedErrors = JSON.parse( localStorage.getItem( 'interact-dismissed-errors' ) || '[]' )
+ const errorKey = loadingError?.interactionKey
+
+ if ( ! loadingError?.interactionKey ) {
+ return
+ }
+
+ if ( dismissedErrors.includes( errorKey ) ) {
+ setIsShowingError( false )
+ } else {
+ setIsShowingError( true )
+ }
+ }, [ loadingError ] )
+
+ // Interaction library can only be opened if the current interaction is not dirty.
+ useEffect( () => {
+ if ( ! isGutenberg ) {
+ return
+ }
+
+ if ( selectedInteraction && isDirtyRef.current && interactionLibraryMode ) {
+ setInteractionLibraryMode( null )
+ alert( __( 'You are currently editing an interaction, please save or discard your changes first.', 'interactions' ) )// eslint-disable-line no-alert
+ }
+ }, [ isGutenberg, selectedInteraction, isDirtyRef, interactionLibraryMode, setInteractionLibraryMode ] )
+
+ const { elementInteractions, pageInteractions } = interactions.reduce( ( acc, interaction ) => {
+ const interactionConfig = interactionsConfig[ interaction.type ]
+ if ( interactionConfig?.type === 'element' ) {
+ acc.elementInteractions.push( interaction )
+ } else if ( interactionConfig?.type === 'page' ) {
+ acc.pageInteractions.push( interaction )
+ }
+ return acc
+ }, { elementInteractions: [], pageInteractions: [] } )
+
+ const onOpenImportExportModal = props => {
+ setImportExportModalProps( props )
+ }
+
+ const onCloseImportExportModal = () => {
+ setImportExportModalProps( null )
+ }
+
+ return <>
+ { selectedInteraction === null && loadingError && isShowingError &&
+
+ setIsShowingError( false ) }
+ isDismissible={ false }
+ >
+ { loadingError.message }
+ { __( 'Check the browser console for more details.', 'interactions' ) }
+
+
+
+
+
+ }
+ { allInteractions.length > 0 && selectedInteraction === null &&
+
+ { interactions.length > 0 && { __( 'These interactions are on this page because of their location rules.', 'interactions' ) }
}
+ { interactions.length === 0 && { __( 'There are no interactions on this page because no matches were found in the location rules.', 'interactions' ) }
}
+
+
+ }
+ { selectedInteraction === null &&
+
+
+
+ { __( 'Animate or trigger actions on any button, image, text or widget.', 'interactions' ) }
+
+
+ { __( 'Learn more', 'interactions' ) }
+
+
+
+
+
+
+ { elementInteractions.map( interaction => {
+ return (
+
{
+ setSelectedInteraction( interaction )
+ } }
+ onDelete={ () => {
+ deleteInteraction( interaction.key )
+ } }
+ />
+ )
+ } ) }
+ { ! elementInteractions.length && (
+
+
+
{ __( 'Define actions that occur when user interacts with elements on your page', 'interactions' ) }
+
+ ) }
+
+
+
+
+
+ { __( 'Launch page-wide transitions, backgrounds or state-based effects.', 'interactions' ) }
+
+
+ { __( 'Learn more', 'interactions' ) }
+
+
+
+
+
+
+ { pageInteractions.map( interaction => {
+ return (
+
{
+ setSelectedInteraction( interaction )
+ } }
+ onDelete={ () => {
+ deleteInteraction( interaction.key )
+ } }
+ />
+ )
+ } ) }
+ { ! pageInteractions.length && (
+
+
+
{ __( 'Define actions that occur when there\'s a change in your page\'s state', 'interactions' ) }
+
+ ) }
+
+
+
+
+ }
+ { selectedInteraction !== null &&
+ {
+ return updateInteraction( newInteraction ).then( () => {
+ setEditMode( 'edit' )
+ } )
+ } }
+ onClose={ ( focusOnInteractionButton = false ) => {
+ if ( focusOnInteractionButton ) {
+ setTimeout( () => {
+ sidebarRef.current?.querySelector( `.interact-list__item-button--${ selectedInteraction.key }` )?.focus()
+ } )
+ }
+ setSelectedInteraction( null )
+ setEditMode( 'edit' )
+ } }
+ onDelete={ () => deleteInteraction( selectedInteraction.key ) }
+ onDirtyChange={ isDirty => isDirtyRef.current = isDirty }
+ onOpenImportExportModal={ onOpenImportExportModal }
+ />
+ }
+ { importExportModalProps &&
+
+ }
+ { isGutenberg && interactionLibraryMode && }
+ >
+}
+
+export default InteractionsApp
diff --git a/src/editor/components/add-interaction-popover/index.js b/src/editor/components/add-interaction-popover/index.js
index 0301258..38e1210 100644
--- a/src/editor/components/add-interaction-popover/index.js
+++ b/src/editor/components/add-interaction-popover/index.js
@@ -16,6 +16,10 @@ import {
setBlockAnchorIfPossible,
openInteractionsSidebar,
} from '~interact/editor/util'
+import {
+ getCurrentSelectedTarget,
+ isElementorEditor,
+} from '~interact/editor/editors'
import { cloneDeep, first } from 'lodash'
import {
@@ -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 || '',
} )
const libraryTitle = ! showElementOption && showPageOption ? __( 'My Page Interactions', 'interactions' )
@@ -94,7 +101,7 @@ const AddInteractionPopover = props => {
return acc
}, { elementInteractions: [], pageInteractions: [] } )
- if ( hidden ) {
+ if ( hidden && ! isElementor ) {
return (
{
setHidden( true ) }
+ hasPickerPopover={ ! isElementor }
+ onBlockSelectClick={ () => {
+ if ( ! isElementor ) {
+ setHidden( true )
+ }
+ } }
/>
) }
diff --git a/src/editor/components/import-export-modal/index.js b/src/editor/components/import-export-modal/index.js
index e092f85..44727f7 100644
--- a/src/editor/components/import-export-modal/index.js
+++ b/src/editor/components/import-export-modal/index.js
@@ -6,6 +6,7 @@
* External deprendencies
*/
import { getOrGenerateBlockAnchor } from '~interact/editor/util'
+import { getCurrentSelectedTarget } from '~interact/editor/editors'
import { first } from 'lodash'
/**
@@ -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 ),
}
} )
@@ -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 ) || '',
diff --git a/src/editor/components/location-rules/index.js b/src/editor/components/location-rules/index.js
index f9bb975..3bf05ba 100644
--- a/src/editor/components/location-rules/index.js
+++ b/src/editor/components/location-rules/index.js
@@ -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
@@ -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: '==',
@@ -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: '==',
@@ -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
diff --git a/src/editor/components/target-selector/index.js b/src/editor/components/target-selector/index.js
index ca37d6b..7f4ba10 100644
--- a/src/editor/components/target-selector/index.js
+++ b/src/editor/components/target-selector/index.js
@@ -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,
@@ -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 = (
<>
@@ -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 && (
{
targetOptions = targetOptions.filter( target => target.value !== 'trigger' )
}
+ if ( isElementor ) {
+ targetOptions = targetOptions.filter( target =>
+ [ 'trigger', 'class', 'selector' ].includes( target.value )
+ )
+ }
+
+ 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 ) )
@@ -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 {
@@ -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,
diff --git a/src/editor/components/timeline/class-runner.js b/src/editor/components/timeline/class-runner.js
index a06d812..d197467 100644
--- a/src/editor/components/timeline/class-runner.js
+++ b/src/editor/components/timeline/class-runner.js
@@ -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 = () => {}
@@ -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 ) {
diff --git a/src/editor/components/timeline/runner.js b/src/editor/components/timeline/runner.js
index b2a23ac..39f4523 100644
--- a/src/editor/components/timeline/runner.js
+++ b/src/editor/components/timeline/runner.js
@@ -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()
@@ -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
diff --git a/src/editor/components/timeline/use-initial-style-tag.js b/src/editor/components/timeline/use-initial-style-tag.js
index d5ce48f..19e6aa1 100644
--- a/src/editor/components/timeline/use-initial-style-tag.js
+++ b/src/editor/components/timeline/use-initial-style-tag.js
@@ -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 => {
diff --git a/src/editor/editor.js b/src/editor/editor.js
index ede9876..8b9741c 100644
--- a/src/editor/editor.js
+++ b/src/editor/editor.js
@@ -1,453 +1,7 @@
-import IconSVG from './assets/icon.svg'
-import ElementSVG from './assets/element.svg'
-import PageSVG from './assets/page.svg'
-import {
- AddInteractionButton,
- InteractionButton,
- InteractionPanel,
- ImportExportModal,
-} from './components'
-import { createNewInteraction, createNewAction } from './util'
-import { useInteractions } from './hooks'
-import { interactions as interactionsConfig, manageInteractionsUrl } from 'interactions'
-import { InteractionLibrary } from './interaction-library'
-
-import { registerPlugin } from '@wordpress/plugins'
-import { __ } from '@wordpress/i18n'
-import { upload } from '@wordpress/icons'
-import {
- PanelBody,
- Button,
- BaseControl,
- Notice,
-} from '@wordpress/components'
-import {
- useState,
- useCallback,
- useRef,
- useEffect,
- createInterpolateElement,
-} from '@wordpress/element'
-import { useSelect, useDispatch } from '@wordpress/data'
-
import './plugins'
-import useOnPostPreview from './use-on-post-save'
-
-const InteractionsEditor = () => {
- // We need to to this for both, because one might be disabled. E.g. in
- // WooCommerce, editSite is loaded and stops the sidebar from showing up.
- const SideEditorPluginSidebar = window.wp.editSite?.PluginSidebar
- const PostEditorPluginSidebar = window.wp.editPost?.PluginSidebar
-
- const SideBar = SideEditorPluginSidebar ? SideEditorPluginSidebar
- : PostEditorPluginSidebar ? PostEditorPluginSidebar : null
-
- const selectedBlockAnchor = useSelect( select => {
- const clientId = select( 'core/block-editor' ).getSelectedBlockClientId()
- return clientId ? select( 'core/block-editor' ).getBlockAttributes( clientId )?.anchor : null
- } )
-
- const interactionLibraryMode = useSelect( select =>
- select( 'interact/interaction-library-modal' ).getMode(),
- [] )
- // Interaction library open modal and set target function
- const {
- setMode: setInteractionLibraryMode,
- } = useDispatch( 'interact/interaction-library-modal' )
-
- const [ selectedInteraction, setSelectedInteraction ] = useState( null )
- const [ editPropsPassed, setEditPropsPassed ] = useState( {} )
- const [ editMode, setEditMode ] = useState( 'edit' )
- const [ isShowingError, setIsShowingError ] = useState( true )
- const [ importExportModalProps, setImportExportModalProps ] = useState( null )
-
- const sidebarRef = useRef()
- const isDirtyRef = useRef( false ) // This gets updated when the current interaction being edited is dirty.
-
- const {
- interactions: allInteractions,
- interactionsFiltered: interactions,
- loadingError,
- updateInteraction,
- deleteInteraction,
- } = useInteractions()
-
- // This listens to the post preview button and publish button and asks the
- // user to save the interaction if it's dirty.
- const onPostSaveCallback = proceedSaveCallback => {
- if ( isDirtyRef.current ) {
- // eslint-disable-next-line no-alert
- if ( confirm( __( 'You have unsaved changes in your interaction. Do you want to save it before continuing?', 'interactions' ) ) ) {
- // Save the interaction, give the callback as the detail.
- window?.dispatchEvent( new CustomEvent( 'interact/save-interaction', {
- detail: {
- // This callback will be called after the interaction is saved.
- callback: proceedSaveCallback,
- },
- } ) )
- // Return true to stop the preview.
- return true
- }
- }
- }
- useOnPostPreview( onPostSaveCallback )
-
- const getInteractionFromKey = useCallback( key => {
- return allInteractions.find( interaction => interaction.key === key )
- }, [ allInteractions ] )
-
- const onAddInteractionHandler = useCallback( ( interactionType, target = null, props = {} ) => {
- if ( selectedInteraction && isDirtyRef.current ) {
- alert( __( 'You are currently editing an interaction, please save or discard your changes first.', 'interactions' ) )// eslint-disable-line no-alert
- return
- }
- setEditMode( 'new' )
- const newInteraction = createNewInteraction( interactionType, target, props )
- setSelectedInteraction( newInteraction )
- }, [ selectedInteraction ] )
-
- const onEditInteractionHandler = useCallback( ( keyOrInteraction, editProps ) => {
- if ( selectedInteraction && isDirtyRef.current ) {
- alert( __( 'You are currently editing an interaction, please save or discard your changes first.', 'interactions' ) )// eslint-disable-line no-alert
- return
- }
- // If editMode is provided (e.g. when duplicating), set the editMode state accordingly
- if ( typeof editProps.editMode !== 'undefined' ) {
- setEditMode( editProps.editMode )
- }
- setEditPropsPassed( editProps )
- setSelectedInteraction( typeof keyOrInteraction === 'string' ? getInteractionFromKey( keyOrInteraction ) : keyOrInteraction )
- }, [ getInteractionFromKey, selectedInteraction ] )
-
- // Listen to external adds of interactions from the main toolbar button.
- useEffect( () => {
- const onAddInteractionEventHandler = event => {
- onAddInteractionHandler( event.detail.type, event.detail.target, event.detail.props )
- }
- const onEditInteractionEventHandler = event => {
- const {
- key, interaction, ...editProps
- } = event.detail
- onEditInteractionHandler( key || interaction, editProps )
- }
-
- window?.addEventListener( 'interact/add-interaction', onAddInteractionEventHandler )
- window?.addEventListener( 'interact/edit-interaction', onEditInteractionEventHandler )
-
- return () => {
- window?.removeEventListener( 'interact/add-interaction', onAddInteractionEventHandler )
- window?.removeEventListener( 'interact/edit-interaction', onEditInteractionEventHandler )
- }
- }, [ onAddInteractionHandler, onEditInteractionHandler ] )
-
- useEffect( () => {
- if ( ! selectedInteraction ) {
- setEditPropsPassed( {} )
- }
- }, [ selectedInteraction ] )
-
- useEffect( () => {
- const dismissedErrors = JSON.parse( localStorage.getItem( 'interact-dismissed-errors' ) || '[]' )
- const errorKey = loadingError?.interactionKey
-
- if ( ! loadingError?.interactionKey ) {
- return
- }
-
- if ( dismissedErrors.includes( errorKey ) ) {
- setIsShowingError( false )
- } else {
- setIsShowingError( true )
- }
- }, [ loadingError ] )
-
- // Interaction library can only be opened if the current interaction is not dirty.
- useEffect( () => {
- if ( selectedInteraction && isDirtyRef.current && interactionLibraryMode ) {
- setInteractionLibraryMode( null )
- alert( __( 'You are currently editing an interaction, please save or discard your changes first.', 'interactions' ) )// eslint-disable-line no-alert
- }
- }, [ selectedInteraction, isDirtyRef, interactionLibraryMode, setInteractionLibraryMode ] )
-
- const { elementInteractions, pageInteractions } = interactions
- // Sort alphabetically by title
- // .sort( ( a, b ) => {
- // if ( a.title < b.title ) {
- // return -1
- // }
- // if ( a.title > b.title ) {
- // return 1
- // }
- // return 0
- // } )
- .reduce( ( acc, interaction ) => {
- const interactionConfig = interactionsConfig[ interaction.type ]
- if ( interactionConfig?.type === 'element' ) {
- acc.elementInteractions.push( interaction )
- } else if ( interactionConfig?.type === 'page' ) {
- acc.pageInteractions.push( interaction )
- }
- return acc
- }, { elementInteractions: [], pageInteractions: [] } )
-
- const onOpenImportExportModal = props => {
- setImportExportModalProps( props )
- }
-
- const onCloseImportExportModal = () => {
- setImportExportModalProps( null )
- }
-
- // If the sidebar is not available (like in the Widgets editor), then do nothing.
- if ( ! SideBar ) {
- return null
- }
-
- return <>
- }
- >
- { selectedInteraction === null && loadingError && isShowingError &&
-
- setIsShowingError( false ) }
- isDismissible={ false }
- >
- { loadingError.message }
- { __( 'Check the browser console for more details.', 'interactions' ) }
-
-
-
-
-
- }
- { allInteractions.length > 0 && selectedInteraction === null &&
-
- { interactions.length > 0 && { __( 'These interactions are on this page because of their location rules.', 'interactions' ) }
}
- { interactions.length === 0 && { __( 'There are no interactions on this page because no matches were found in the location rules.', 'interactions' ) }
}
-
-
- }
- { selectedInteraction === null &&
-
-
-
- { __( 'Animate or trigger actions on any button, image, text or widget.', 'interactions' ) }
-
-
- { __( 'Learn more', 'interactions' ) }
-
-
-
-
-
-
- { elementInteractions
- .map( interaction => {
- return (
-
{
- setSelectedInteraction( interaction )
- } }
- onDelete={ () => {
- deleteInteraction( interaction.key )
- } }
- />
- )
- } ) }
- { ! elementInteractions.length && (
-
-
-
{ __( 'Define actions that occur when user interacts with elements on your page', 'interactions' ) }
-
- ) }
-
-
-
-
-
-
- { __( 'Launch page‑wide transitions, backgrounds or state‑based effects.', 'interactions' ) }
-
-
- { __( 'Learn more', 'interactions' ) }
-
-
-
-
-
-
- { pageInteractions
- .map( interaction => {
- return (
-
{
- setSelectedInteraction( interaction )
- } }
- onDelete={ () => {
- deleteInteraction( interaction.key )
- } }
- />
- )
- } ) }
- { ! pageInteractions.length && (
-
-
-
{ __( 'Define actions that occur when there\'s a change in your page\'s state', 'interactions' ) }
-
- ) }
-
-
-
- { /*
-
- */ }
-
- }
- { selectedInteraction !== null &&
- {
- return updateInteraction( newInteraction ).then( () => {
- setEditMode( 'edit' )
- } )
- } }
- onClose={ ( focusOnInteractionButton = false ) => {
- if ( focusOnInteractionButton ) {
- // Focus on the previous interaction button so we can go back to it.
- setTimeout( () => {
- sidebarRef.current?.querySelector( `.interact-list__item-button--${ selectedInteraction.key }` )?.focus()
- } )
- }
- setSelectedInteraction( null )
- setEditMode( 'edit' )
- } }
- onDelete={ () => deleteInteraction( selectedInteraction.key ) }
- onDirtyChange={ isDirty => isDirtyRef.current = isDirty }
- onOpenImportExportModal={ onOpenImportExportModal }
- />
- }
- { importExportModalProps &&
-
- }
-
- { /* Render the Interaction Library modal in the root editor component */
- interactionLibraryMode &&
- }
- >
-}
+import { getInteractionsEditor } from './editors'
+import { domReady } from '~interact/shared/dom-ready.js'
-registerPlugin( 'interact-editor', {
- render: InteractionsEditor,
+domReady( () => {
+ getInteractionsEditor().init()
} )
diff --git a/src/editor/editor.php b/src/editor/editor.php
index a964602..3c9df00 100644
--- a/src/editor/editor.php
+++ b/src/editor/editor.php
@@ -18,17 +18,38 @@ class Interact_Editor {
*/
function __construct() {
if ( is_admin() ) {
- add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_editor' ) );
+ add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_gutenberg_editor' ) );
+ add_action( 'elementor/editor/after_enqueue_scripts', array( $this, 'enqueue_elementor_editor' ) );
add_action( 'enqueue_block_assets', array( $this, 'enqueue_assets' ) );
}
}
+ /**
+ * Loads the editor script inside the Gutenberg editor.
+ *
+ * @return void
+ */
+ public function enqueue_gutenberg_editor() {
+ $this->enqueue_editor( 'gutenberg' );
+ }
+
+ /**
+ * Loads the editor script inside the Elementor editor.
+ *
+ * @return void
+ */
+ public function enqueue_elementor_editor() {
+ $this->enqueue_editor( 'elementor' );
+ }
+
/**
* Loads the editor script.
*
+ * @param string $editor_mode Current editor mode.
+ *
* @return void
*/
- public function enqueue_editor() {
+ public function enqueue_editor( $editor_mode = 'gutenberg' ) {
// Load the required interaciton and action types.
interact_require_types();
@@ -63,6 +84,7 @@ public function enqueue_editor() {
[ $actions, $action_categories ] = $this->get_action_types_config();
global $wp_version;
+ $post = get_post();
$args = apply_filters( 'interact/localize_script', array(
'interactions' => $interactions,
'interactionCategories' => $interaction_categories,
@@ -77,6 +99,14 @@ public function enqueue_editor() {
'restNonce' => wp_create_nonce( 'wp_rest' ), // This needs to be 'wp_rest' to use the built-in nonce verification.
'srcUrl' => untrailingslashit( plugins_url( '/', INTERACT_FILE ) ),
'currentUserCanUnfilteredHtml' => current_user_can( 'unfiltered_html' ),
+ // Keep the current editor and document context available to the
+ // shared editor app so integrations can resolve targets and
+ // location rules correctly.
+ 'editorMode' => $editor_mode,
+ 'currentPostId' => $post ? (int) $post->ID : 0,
+ 'currentPostType' => $post ? $post->post_type : '',
+ 'currentPostTemplate' => $post ? get_page_template_slug( $post->ID ) : '',
+ 'currentPostParent' => $post ? (int) $post->post_parent : 0,
) );
wp_localize_script( 'interact-editor', 'interactions', $args );
}
diff --git a/src/editor/editor.scss b/src/editor/editor.scss
index a07a736..e2e3c17 100644
--- a/src/editor/editor.scss
+++ b/src/editor/editor.scss
@@ -139,6 +139,66 @@
--wp-components-color-accent: #05f;
}
+.interact-elementor-launcher {
+ position: fixed !important;
+ right: 32px;
+ bottom: 32px;
+ z-index: 100000;
+ border-radius: 999px !important;
+ box-shadow: 0 16px 40px rgba(0, 0, 0, 0.18);
+}
+
+.interact-elementor-backdrop {
+ position: fixed;
+ inset: 0;
+ background: rgba(17, 24, 39, 0.18);
+ z-index: 99998;
+}
+
+.interact-elementor-panel {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ width: min(420px, calc(100vw - 48px));
+ background: #fff;
+ box-shadow: -24px 0 48px rgba(15, 23, 42, 0.18);
+ transform: translateX(100%);
+ transition: transform 180ms ease;
+ z-index: 99999;
+ display: flex;
+ flex-direction: column;
+}
+
+.interact-elementor-panel.is-open {
+ transform: translateX(0);
+}
+
+.interact-elementor-panel__header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 16px 18px;
+ border-bottom: 1px solid #e5e7eb;
+ background: #f8fafc;
+}
+
+.interact-elementor-panel__title {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ font-weight: 600;
+}
+
+.interact-elementor-panel__body {
+ flex: 1;
+ overflow: auto;
+}
+
+.interact-elementor-sidebar {
+ min-height: 100%;
+}
+
/* Wordpress 7.0 compatibility */
.interact-sidebar, .interact-popover {
@@ -160,4 +220,4 @@
.interact-property-control .components-base-control__field {
margin-bottom: 8px;
}
-}
\ No newline at end of file
+}
diff --git a/src/editor/editors/abstract.js b/src/editor/editors/abstract.js
new file mode 100644
index 0000000..e8e5052
--- /dev/null
+++ b/src/editor/editors/abstract.js
@@ -0,0 +1,87 @@
+import {
+ currentPostId,
+ currentPostParent,
+ currentPostTemplate,
+ currentPostType,
+} from 'interactions'
+import { select } from '@wordpress/data'
+
+const NOOP = () => {}
+
+// Base editor adapter that defines the shared editor contract.
+class InteractionsEditorAbstract {
+ constructor() {
+ this.initialized = false
+ }
+
+ // Boot the current editor integration.
+ init() {
+ this.initialized = true
+ return this
+ }
+
+ // Return the current editor mode.
+ getEditorMode() {
+ throw new Error( 'InteractionsEditorAbstract#getEditorMode must be implemented.' )
+ }
+
+ isElementor() {
+ return this.getEditorMode() === 'elementor'
+ }
+
+ isGutenberg() {
+ return ! this.isElementor()
+ }
+
+ // Return the current document context for location rule matching.
+ getCurrentPostContext() {
+ const editorStore = select( 'core/editor' )
+ return {
+ postId: editorStore?.getCurrentPostId?.() || currentPostId || 0,
+ postType: editorStore?.getCurrentPostType?.() || currentPostType || '',
+ postTemplate: editorStore?.getCurrentPost?.()?.template || currentPostTemplate || '',
+ postParent: editorStore?.getCurrentPost?.()?.parent || currentPostParent || 0,
+ }
+ }
+
+ getCanvasDocument() {
+ return document
+ }
+
+ getCanvasElement() {
+ const canvasDocument = this.getCanvasDocument()
+ return canvasDocument?.querySelector( '.editor-styles-wrapper' ) || canvasDocument?.body || document.body
+ }
+
+ // Open the editor panel when supported by the current integration.
+ openPanel() {
+ return null
+ }
+
+ openInteractionsPanel() {
+ return this.openPanel()
+ }
+
+ getSelectedBlockAnchor() {
+ return null
+ }
+
+ getCurrentSelectedTarget() {
+ return null
+ }
+
+ registerSelectionTracking() {
+ return NOOP
+ }
+
+ // Start an editor-specific target picker.
+ startElementPicker( args = {} ) {
+ const {
+ onCancel = NOOP,
+ } = args
+ onCancel()
+ return NOOP
+ }
+}
+
+export default InteractionsEditorAbstract
diff --git a/src/editor/editors/elementor.js b/src/editor/editors/elementor.js
new file mode 100644
index 0000000..45cbf50
--- /dev/null
+++ b/src/editor/editors/elementor.js
@@ -0,0 +1,282 @@
+import IconSVG from '../assets/icon.svg'
+import InteractionsApp from '../app'
+import InteractionsEditorAbstract from './abstract'
+
+import { __ } from '@wordpress/i18n'
+import { Button } from '@wordpress/components'
+import {
+ useEffect,
+ useState,
+ createRoot,
+} from '@wordpress/element'
+
+const NOOP = () => {}
+
+// Elementor editor adapter.
+class ElementorInteractionsEditor extends InteractionsEditorAbstract {
+ constructor() {
+ super()
+ this.selectedElement = null
+ }
+
+ getEditorMode() {
+ return 'elementor'
+ }
+
+ // Mount the Elementor launcher and side panel shell.
+ init() {
+ if ( this.initialized ) {
+ return this
+ }
+
+ const mountNodeId = 'interact-elementor-root'
+ if ( document.getElementById( mountNodeId ) ) {
+ return super.init()
+ }
+
+ const ElementorInteractionsEditorComponent = () => {
+ const [ isOpen, setIsOpen ] = useState( false )
+
+ useEffect( () => {
+ const openHandler = () => setIsOpen( true )
+ window.addEventListener( 'interact/open-elementor-sidebar', openHandler )
+ return () => window.removeEventListener( 'interact/open-elementor-sidebar', openHandler )
+ }, [] )
+
+ const Wrapper = ( {
+ children,
+ } ) => (
+
+ { children }
+
+ )
+
+ return (
+ <>
+ }
+ onClick={ () => setIsOpen( value => ! value ) }
+ >
+ { __( 'Interactions', 'interactions' ) }
+
+ { /* { isOpen && (
+ setIsOpen( false ) }
+ aria-hidden="true"
+ />
+ ) } */ }
+
+
+
+
+ { __( 'Interactions', 'interactions' ) }
+
+
+
+
+
+
+
+
+ >
+ )
+ }
+
+ const mountNode = document.createElement( 'div' )
+ mountNode.id = mountNodeId
+ document.body.appendChild( mountNode )
+ this.registerSelectionTracking()
+ createRoot( mountNode ).render(
)
+
+ return super.init()
+ }
+
+ // Return the Elementor preview canvas document.
+ getCanvasDocument() {
+ const iframe = document.querySelector( '#elementor-preview-iframe' )
+ return iframe?.contentDocument || document
+ }
+
+ // Open the Interactions sidebar in Elementor.
+ openPanel() {
+ window.dispatchEvent( new CustomEvent( 'interact/open-elementor-sidebar' ) )
+ return null
+ }
+
+ // Build an interaction target from a selected Elementor element.
+ buildTargetFromElement( element, targetType = 'selector' ) {
+ if ( ! element ) {
+ return null
+ }
+
+ const targetElement = element.closest( '.elementor-element[data-id]' )
+ if ( ! targetElement ) {
+ return null
+ }
+
+ const elementId = targetElement.getAttribute( 'data-id' )
+ if ( ! elementId ) {
+ return null
+ }
+
+ const elementType = targetElement.getAttribute( 'data-element_type' ) || ''
+ const widgetType = targetElement.getAttribute( 'data-widget_type' ) || ''
+ const label = widgetType || elementType || 'elementor-element'
+ const wrapperSelector = `.elementor-element.elementor-element-${ elementId }`
+ const classValue = `elementor-element-${ elementId }`
+ const targetValue = targetType === 'class'
+ ? classValue
+ : elementType === 'widget'
+ ? `${ wrapperSelector } > *`
+ : wrapperSelector
+
+ return {
+ type: targetType,
+ value: targetValue,
+ blockName: label,
+ }
+ }
+
+ getHighlightElement( element ) {
+ if ( ! element ) {
+ return null
+ }
+
+ const targetElement = element.closest( '.elementor-element[data-id]' )
+ if ( ! targetElement ) {
+ return null
+ }
+
+ const elementType = targetElement.getAttribute( 'data-element_type' ) || ''
+ if ( elementType !== 'widget' ) {
+ return targetElement
+ }
+
+ return targetElement.firstElementChild || targetElement
+ }
+
+ // Return the currently selected Elementor target.
+ getCurrentSelectedTarget() {
+ return this.buildTargetFromElement( this.selectedElement?.element || null )
+ }
+
+ // Track the current Elementor selection from the editor panel.
+ registerSelectionTracking() {
+ if ( ! window.elementor?.hooks?.addAction ) {
+ return NOOP
+ }
+
+ const register = action => {
+ window.elementor.hooks.addAction( action, ( panel, model, view ) => {
+ this.selectedElement = {
+ model,
+ view,
+ element: view?.$el?.get?.( 0 ) || null,
+ }
+ } )
+ }
+
+ [
+ 'panel/open_editor/section',
+ 'panel/open_editor/column',
+ 'panel/open_editor/container',
+ 'panel/open_editor/widget',
+ ].forEach( register )
+
+ return NOOP
+ }
+
+ // Start an Elementor preview picker for selector or class targets.
+ startElementPicker( {
+ targetType = 'selector',
+ onPick = NOOP,
+ onCancel = NOOP,
+ } = {} ) {
+ const previewDocument = this.getCanvasDocument()
+ if ( ! previewDocument ) {
+ onCancel()
+ return NOOP
+ }
+
+ let highlightedElement = null
+
+ // Restore the previously highlighted element back to its original outline.
+ const clearHighlight = () => {
+ if ( highlightedElement ) {
+ highlightedElement.style.outline = highlightedElement.dataset.interactPrevOutline || ''
+ highlightedElement.style.outlineOffset = highlightedElement.dataset.interactPrevOutlineOffset || ''
+ delete highlightedElement.dataset.interactPrevOutline
+ delete highlightedElement.dataset.interactPrevOutlineOffset
+ }
+ highlightedElement = null
+ }
+
+ // Follow the pointer inside the preview and visually mark the current pick candidate.
+ const mouseMoveHandler = event => {
+ const candidate = event.target.closest( '.elementor-element[data-id]' )
+ const highlightCandidate = this.getHighlightElement( candidate )
+ if ( highlightCandidate === highlightedElement ) {
+ return
+ }
+ clearHighlight()
+ if ( highlightCandidate ) {
+ highlightedElement = highlightCandidate
+ highlightedElement.dataset.interactPrevOutline = highlightedElement.style.outline || ''
+ highlightedElement.dataset.interactPrevOutlineOffset = highlightedElement.style.outlineOffset || ''
+ highlightedElement.style.outline = '2px solid #05f'
+ highlightedElement.style.outlineOffset = '2px'
+ }
+ }
+
+ // Convert the clicked Elementor element into an interaction target and stop pick mode.
+ const clickHandler = event => {
+ const candidate = event.target.closest( '.elementor-element[data-id]' )
+ if ( ! candidate ) {
+ return
+ }
+ event.preventDefault()
+ event.stopPropagation()
+ const target = this.buildTargetFromElement( candidate, targetType )
+ stop()
+ if ( target ) {
+ onPick( target )
+ } else {
+ onCancel()
+ }
+ }
+
+ // Allow canceling the picker with Escape.
+ const keyHandler = event => {
+ if ( event.key === 'Escape' ) {
+ stop()
+ onCancel()
+ }
+ }
+
+ // Remove all temporary picker listeners and preview highlighting.
+ const stop = () => {
+ clearHighlight()
+ previewDocument.removeEventListener( 'mousemove', mouseMoveHandler, true )
+ previewDocument.removeEventListener( 'click', clickHandler, true )
+ document.removeEventListener( 'keydown', keyHandler, true )
+ }
+
+ previewDocument.addEventListener( 'mousemove', mouseMoveHandler, true )
+ previewDocument.addEventListener( 'click', clickHandler, true )
+ document.addEventListener( 'keydown', keyHandler, true )
+
+ return stop
+ }
+}
+
+export default ElementorInteractionsEditor
diff --git a/src/editor/editors/gutenberg.js b/src/editor/editors/gutenberg.js
new file mode 100644
index 0000000..0c42d05
--- /dev/null
+++ b/src/editor/editors/gutenberg.js
@@ -0,0 +1,129 @@
+import IconSVG from '../assets/icon.svg'
+import InteractionsApp from '../app'
+import InteractionsEditorAbstract from './abstract'
+
+import { registerPlugin } from '@wordpress/plugins'
+import { __ } from '@wordpress/i18n'
+import {
+ useSelect,
+ dispatch,
+ select,
+} from '@wordpress/data'
+
+// Stable Gutenberg sidebar shell used to keep the shared app mounted.
+const GutenbergSidebarWrapper = ( {
+ children,
+ SideBar,
+} ) => (
+
}
+ >
+ { children }
+
+)
+
+// Gutenberg editor adapter.
+class GutenbergInteractionsEditor extends InteractionsEditorAbstract {
+ getEditorMode() {
+ return 'gutenberg'
+ }
+
+ // Register the Gutenberg sidebar plugin.
+ init() {
+ if ( this.initialized ) {
+ return this
+ }
+
+ const GutenbergInteractionsEditorComponent = () => {
+ const SideEditorPluginSidebar = window.wp.editSite?.PluginSidebar
+ const PostEditorPluginSidebar = window.wp.editPost?.PluginSidebar
+ const SideBar = SideEditorPluginSidebar ? SideEditorPluginSidebar
+ : PostEditorPluginSidebar ? PostEditorPluginSidebar : null
+
+ const selectedBlockAnchor = useSelect( select => {
+ const clientId = select( 'core/block-editor' )?.getSelectedBlockClientId?.()
+ return clientId ? select( 'core/block-editor' ).getBlockAttributes( clientId )?.anchor : null
+ }, [] )
+
+ if ( ! SideBar ) {
+ return null
+ }
+
+ return (
+
+
+
+ )
+ }
+
+ registerPlugin( 'interact-editor', {
+ render: GutenbergInteractionsEditorComponent,
+ } )
+
+ return super.init()
+ }
+
+ // Return the Gutenberg editor canvas document.
+ getCanvasDocument() {
+ const iframe = document.querySelector( 'iframe[name="editor-canvas"]' )
+ return iframe?.contentDocument || document
+ }
+
+ // Open the Interactions sidebar in Gutenberg.
+ openPanel() {
+ if ( dispatch( 'core/edit-post' ) ) {
+ return dispatch( 'core/edit-post' ).openGeneralSidebar( 'interact-editor/sidebar' )
+ }
+ return dispatch( 'core/edit-site' ).openGeneralSidebar( 'interact-editor/sidebar' )
+ }
+
+ // Return the currently selected block anchor.
+ getSelectedBlockAnchor() {
+ const blockEditorStore = select( 'core/block-editor' )
+ if ( ! blockEditorStore?.getSelectedBlockClientId ) {
+ return null
+ }
+ const clientId = blockEditorStore.getSelectedBlockClientId()
+ return clientId ? blockEditorStore.getBlockAttributes( clientId )?.anchor : null
+ }
+
+ // Return the current Gutenberg selection as an interaction target.
+ getCurrentSelectedTarget() {
+ const blockEditorStore = select( 'core/block-editor' )
+ const clientId = blockEditorStore?.getSelectedBlockClientId?.()
+ if ( ! clientId ) {
+ return null
+ }
+
+ const block = blockEditorStore.getBlock?.( clientId )
+ if ( ! block ) {
+ return null
+ }
+
+ const hasAnchorAttribute = !! select( 'core/blocks' ).getBlockType( block.name )?.attributes?.anchor
+ if ( hasAnchorAttribute ) {
+ return {
+ type: 'block',
+ value: this.getSelectedBlockAnchor() || '',
+ blockName: block.name || '',
+ options: { clientId },
+ }
+ }
+
+ const className = block.attributes?.className?.split( ' ' )?.[ 0 ] || ''
+ return {
+ type: 'class',
+ value: className,
+ blockName: block.name || '',
+ options: { clientId },
+ }
+ }
+}
+
+export default GutenbergInteractionsEditor
diff --git a/src/editor/editors/index.js b/src/editor/editors/index.js
new file mode 100644
index 0000000..259a301
--- /dev/null
+++ b/src/editor/editors/index.js
@@ -0,0 +1,42 @@
+import { editorMode } from 'interactions'
+import GutenbergInteractionsEditor from './gutenberg'
+import ElementorInteractionsEditor from './elementor'
+
+let activeEditor = null
+
+// Create the active editor adapter for the current editor environment.
+const createInteractionsEditor = () => {
+ return editorMode === 'elementor'
+ ? new ElementorInteractionsEditor()
+ : new GutenbergInteractionsEditor()
+}
+
+// Return the memoized editor adapter instance.
+export const getInteractionsEditor = () => {
+ if ( ! activeEditor ) {
+ activeEditor = createInteractionsEditor()
+ }
+ return activeEditor
+}
+
+export const getEditorMode = () => getInteractionsEditor().getEditorMode()
+
+export const isElementorEditor = () => getInteractionsEditor().isElementor()
+
+export const isGutenbergEditor = () => getInteractionsEditor().isGutenberg()
+
+export const getCurrentEditorPostContext = () => getInteractionsEditor().getCurrentPostContext()
+
+export const getSelectedBlockAnchor = () => getInteractionsEditor().getSelectedBlockAnchor()
+
+export const getEditorCanvasDocument = () => getInteractionsEditor().getCanvasDocument()
+
+export const getEditorCanvasElement = () => getInteractionsEditor().getCanvasElement()
+
+export const openInteractionsSidebar = () => getInteractionsEditor().openInteractionsPanel()
+
+export const getCurrentSelectedTarget = () => getInteractionsEditor().getCurrentSelectedTarget()
+
+export const registerElementorSelectionTracking = () => getInteractionsEditor().registerSelectionTracking()
+
+export const startElementorElementPicker = args => getInteractionsEditor().startElementPicker( args )
diff --git a/src/editor/hooks/use-interactions.js b/src/editor/hooks/use-interactions.js
index 10d6c85..553e80e 100644
--- a/src/editor/hooks/use-interactions.js
+++ b/src/editor/hooks/use-interactions.js
@@ -10,6 +10,7 @@ import { domReady } from '~interact/shared/dom-ready.js'
import apiFetch from '@wordpress/api-fetch'
import { __ } from '@wordpress/i18n'
import { ensureInteractionDefaults } from '../util'
+import { getCurrentEditorPostContext, getEditorMode } from '~interact/editor/editors'
const DEFAULT_STATE = {
interactions: [],
@@ -92,16 +93,12 @@ register( createReduxStore( 'interact/interactions', {
* Whether or not the interaction should be shown in the editor based on what's
* currently beign edited in the Block Editor.
*
- * @param {Array} interaction
- * @param {Object} select wp.data.select
+ * @param {Array} interaction
*
* @return {boolean} Whether or not the interaction should be shown in the editor.
*/
-export const isInteractionShown = ( interaction, select ) => {
- // If the editor is not available (e.g. in Widgets editor), don't do anything.
- if ( ! select( 'core/editor' ) ) {
- return false
- }
+export const isInteractionShown = interaction => {
+ const currentContext = getCurrentEditorPostContext()
return interaction.locations.some( locationGroup => {
return locationGroup.every( location => {
const {
@@ -113,15 +110,15 @@ export const isInteractionShown = ( interaction, select ) => {
case 'page': {
// If blank, then it's all posts/pages.
if ( ! value || isNaN( +value ) ) {
- const postType = select( 'core/editor' ).getCurrentPostType()
+ const postType = currentContext.postType
const postTypeParam = value || param
return operator === '==' ? postType === postTypeParam : postType !== postTypeParam
}
- const match = value.toString() === select( 'core/editor' ).getCurrentPostId()?.toString()
+ const match = value.toString() === currentContext.postId?.toString()
return operator === '==' ? match : ! match
}
case 'post_type': {
- const match = value.toString() === select( 'core/editor' ).getCurrentPostType()?.toString()
+ const match = value.toString() === currentContext.postType?.toString()
return operator === '==' ? match : ! match
}
case 'post_status':
@@ -131,20 +128,20 @@ export const isInteractionShown = ( interaction, select ) => {
return true
case 'post_template':
case 'page_template': {
- const match = value.toString() === select( 'core/editor' ).getCurrentPost()?.template.toString()
+ const match = value.toString() === currentContext.postTemplate?.toString()
return operator === '==' ? match : ! match
}
case 'post_parent':
case 'page_parent': {
- const match = value.toString() === select( 'core/editor' ).getCurrentPost()?.parent.toString()
+ const match = value.toString() === currentContext.postParent?.toString()
return operator === '==' ? match : ! match
}
case 'all': // Entire website
return true
case 'wp_template': // Site editor templates: home, 404, etc
- const currentPostType = select( 'core/editor' ).getCurrentPostType()
+ const currentPostType = currentContext.postType
if ( currentPostType === 'wp_template' ) {
- const match = value.toString() === select( 'core/editor' ).getCurrentPostId()?.toString()
+ const match = value.toString() === currentContext.postId?.toString()
return operator === '==' ? match : ! match
}
break
@@ -177,7 +174,7 @@ const useInteractions = () => {
const updateInteraction = newInteraction => {
// Check if we updated any anchors/attributes, if we did, then we need to ask whether to also update the post.
const didModifyPostContent = select( 'interact/interactions' ).didModifyPostContent()
- if ( didModifyPostContent ) {
+ if ( didModifyPostContent && getEditorMode() !== 'elementor' ) {
if ( confirm( __( 'Some block anchors have been updated for your interactions to work correctly. Do you want to save these post changes? (Any modified synced patterns will also be saved)', 'interactions' ) ) ) { // eslint-disable-line no-alert
dispatch( 'interact/interactions' ).setDidModifyPostContent( false )
// Save the post.
@@ -244,7 +241,7 @@ const useInteractions = () => {
}
const interactions = select( 'interact/interactions' ).getInteractions()
- const interactionsFiltered = interactions.filter( interaction => isInteractionShown( interaction, select ) )
+ const interactionsFiltered = interactions.filter( interaction => isInteractionShown( interaction ) )
return {
interactions,
diff --git a/src/editor/plugins/index.js b/src/editor/plugins/index.js
index c448b35..1039a02 100644
--- a/src/editor/plugins/index.js
+++ b/src/editor/plugins/index.js
@@ -1,4 +1,8 @@
-import './block-toolbar-button'
-import './top-toolbar-button'
-import './block-highlight'
-import './block-select'
+import { editorMode } from 'interactions'
+
+if ( editorMode !== 'elementor' ) {
+ require( './block-toolbar-button' )
+ require( './top-toolbar-button' )
+ require( './block-highlight' )
+ require( './block-select' )
+}
diff --git a/src/editor/util/index.js b/src/editor/util/index.js
index d34bf05..c2356e5 100644
--- a/src/editor/util/index.js
+++ b/src/editor/util/index.js
@@ -6,6 +6,10 @@ import {
import { select, dispatch } from '@wordpress/data'
import { sprintf, __ } from '@wordpress/i18n'
import { addClientIdAnchorPair } from '../components/timeline/with-tracked-anchors'
+import {
+ getCurrentEditorPostContext,
+ openInteractionsSidebar,
+} from '~interact/editor/editors'
const getUniqueTitle = title => {
const interactions = select( 'interact/interactions' ).getInteractions()
@@ -52,12 +56,17 @@ const getBlockNameFromAnchor = anchor => {
// Returns the current page
export const getLocationForCurrentPage = () => {
- const currentPostType = select( 'core/editor' ).getCurrentPostType()
+ const {
+ postId,
+ postType,
+ } = getCurrentEditorPostContext()
+
+ const currentPostType = postType
let locationParam = currentPostType === 'page' ? 'page'
: currentPostType === 'wp_template' ? 'wp_template' // Site editor templates
: !! currentPostType ? 'post'
: null
- let locationValue = select( 'core/editor' ).getCurrentPostId()
+ let locationValue = postId
if ( ! locationParam ) {
locationParam = 'all'
@@ -382,14 +391,4 @@ export const getOrGenerateBlockClass = ( clientId, updateAttribute = true ) => {
return className
}
-/**
- * Utility function to open the Interactions sidebar.
- *
- * @return {Object} Dispatch action object
- */
-export const openInteractionsSidebar = () => {
- if ( dispatch( 'core/edit-post' ) ) {
- return dispatch( 'core/edit-post' ).openGeneralSidebar( 'interact-editor/sidebar' )
- }
- return dispatch( 'core/edit-site' ).openGeneralSidebar( 'interact-editor/sidebar' )
-}
+export { openInteractionsSidebar }
From 5a86027c8cdd05101e9374aad8d930f630d2de59 Mon Sep 17 00:00:00 2001
From: Alquen Sarmiento
Date: Fri, 8 May 2026 15:01:42 +0800
Subject: [PATCH 02/14] fix: refactor wrappers
---
src/editor/editors/elementor.js | 12 ++----------
src/editor/editors/gutenberg.js | 24 +++++++-----------------
2 files changed, 9 insertions(+), 27 deletions(-)
diff --git a/src/editor/editors/elementor.js b/src/editor/editors/elementor.js
index 45cbf50..2dfc58d 100644
--- a/src/editor/editors/elementor.js
+++ b/src/editor/editors/elementor.js
@@ -43,14 +43,6 @@ class ElementorInteractionsEditor extends InteractionsEditorAbstract {
return () => window.removeEventListener( 'interact/open-elementor-sidebar', openHandler )
}, [] )
- const Wrapper = ( {
- children,
- } ) => (
-
- { children }
-
- )
-
return (
<>
>
diff --git a/src/editor/editors/gutenberg.js b/src/editor/editors/gutenberg.js
index 0c42d05..4edf85d 100644
--- a/src/editor/editors/gutenberg.js
+++ b/src/editor/editors/gutenberg.js
@@ -10,21 +10,6 @@ import {
select,
} from '@wordpress/data'
-// Stable Gutenberg sidebar shell used to keep the shared app mounted.
-const GutenbergSidebarWrapper = ( {
- children,
- SideBar,
-} ) => (
- }
- >
- { children }
-
-)
-
// Gutenberg editor adapter.
class GutenbergInteractionsEditor extends InteractionsEditorAbstract {
getEditorMode() {
@@ -53,12 +38,17 @@ class GutenbergInteractionsEditor extends InteractionsEditorAbstract {
}
return (
-
+ }
+ >
-
+
)
}
From 15aac8201907ae7d2834847a5fbd5279723c4371 Mon Sep 17 00:00:00 2001
From: Alquen Sarmiento
Date: Fri, 8 May 2026 15:14:03 +0800
Subject: [PATCH 03/14] fix: enqueue the needed style by elementor editor
---
src/editor/editor.php | 5 +++++
src/editor/editor.scss | 13 +++++--------
2 files changed, 10 insertions(+), 8 deletions(-)
diff --git a/src/editor/editor.php b/src/editor/editor.php
index 3c9df00..b4b6c5e 100644
--- a/src/editor/editor.php
+++ b/src/editor/editor.php
@@ -39,6 +39,11 @@ public function enqueue_gutenberg_editor() {
* @return void
*/
public function enqueue_elementor_editor() {
+ // Loads the core WordPress editor styles needed by the elementor editor UI.
+ // if ( wp_style_is( 'wp-components', 'registered' ) ) {
+ // wp_enqueue_style( 'wp-components' );
+ // }
+
$this->enqueue_editor( 'elementor' );
}
diff --git a/src/editor/editor.scss b/src/editor/editor.scss
index e2e3c17..37f82b3 100644
--- a/src/editor/editor.scss
+++ b/src/editor/editor.scss
@@ -139,6 +139,8 @@
--wp-components-color-accent: #05f;
}
+/* Interaction Elementor Editor Panel Styles */
+
.interact-elementor-launcher {
position: fixed !important;
right: 32px;
@@ -148,20 +150,15 @@
box-shadow: 0 16px 40px rgba(0, 0, 0, 0.18);
}
-.interact-elementor-backdrop {
- position: fixed;
- inset: 0;
- background: rgba(17, 24, 39, 0.18);
- z-index: 99998;
-}
-
.interact-elementor-panel {
position: fixed;
top: 0;
right: 0;
bottom: 0;
- width: min(420px, calc(100vw - 48px));
+ width: 280px;
background: #fff;
+ color: #1e1e1e;
+ line-height: 18px;
box-shadow: -24px 0 48px rgba(15, 23, 42, 0.18);
transform: translateX(100%);
transition: transform 180ms ease;
From 527c3a6923f49244912c1cb9bb0f44eca9d7ff17 Mon Sep 17 00:00:00 2001
From: Alquen Sarmiento
Date: Fri, 8 May 2026 15:19:32 +0800
Subject: [PATCH 04/14] feat: laod wp-components style
---
src/editor/editor.php | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/editor/editor.php b/src/editor/editor.php
index b4b6c5e..035d2e9 100644
--- a/src/editor/editor.php
+++ b/src/editor/editor.php
@@ -40,9 +40,9 @@ public function enqueue_gutenberg_editor() {
*/
public function enqueue_elementor_editor() {
// Loads the core WordPress editor styles needed by the elementor editor UI.
- // if ( wp_style_is( 'wp-components', 'registered' ) ) {
- // wp_enqueue_style( 'wp-components' );
- // }
+ if ( wp_style_is( 'wp-components', 'registered' ) ) {
+ wp_enqueue_style( 'wp-components' );
+ }
$this->enqueue_editor( 'elementor' );
}
From 11367f4c26678ab4421be1dbba57d4f68931837a Mon Sep 17 00:00:00 2001
From: Alquen Sarmiento
Date: Fri, 8 May 2026 15:20:21 +0800
Subject: [PATCH 05/14] fix: guard dismissed-error parsing against malformed
localStorage
---
src/editor/app.js | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/src/editor/app.js b/src/editor/app.js
index e0cba9e..5f7dd0b 100644
--- a/src/editor/app.js
+++ b/src/editor/app.js
@@ -31,6 +31,15 @@ import { useSelect, useDispatch } from '@wordpress/data'
import useOnPostPreview from './use-on-post-save'
+const getDismissedErrors = () => {
+ try {
+ const dismissedErrors = JSON.parse( localStorage.getItem( 'interact-dismissed-errors' ) || '[]' )
+ return Array.isArray( dismissedErrors ) ? dismissedErrors : []
+ } catch ( error ) {
+ return []
+ }
+}
+
const InteractionsApp = ( {
selectedBlockAnchor = null,
enablePostPreviewGuard = true,
@@ -136,7 +145,7 @@ const InteractionsApp = ( {
}, [ selectedInteraction ] )
useEffect( () => {
- const dismissedErrors = JSON.parse( localStorage.getItem( 'interact-dismissed-errors' ) || '[]' )
+ const dismissedErrors = getDismissedErrors()
const errorKey = loadingError?.interactionKey
if ( ! loadingError?.interactionKey ) {
@@ -196,7 +205,7 @@ const InteractionsApp = ( {
variant="secondary"
size="small"
onClick={ () => {
- const dismissedErrors = JSON.parse( localStorage.getItem( 'interact-dismissed-errors' ) || '[]' )
+ const dismissedErrors = getDismissedErrors()
const errorKey = loadingError?.interactionKey
localStorage.setItem( 'interact-dismissed-errors', JSON.stringify( [ ...dismissedErrors, errorKey ] ) )
setIsShowingError( false )
From cf97ffbde382742577f37f41e96bfe00178717e7 Mon Sep 17 00:00:00 2001
From: Alquen Sarmiento
Date: Fri, 8 May 2026 15:30:00 +0800
Subject: [PATCH 06/14] fix: normalize imported element actions the same way as
page imports
---
src/editor/app.js | 46 ++++++++++++++++++++++++++++++----------------
1 file changed, 30 insertions(+), 16 deletions(-)
diff --git a/src/editor/app.js b/src/editor/app.js
index 5f7dd0b..f263317 100644
--- a/src/editor/app.js
+++ b/src/editor/app.js
@@ -31,6 +31,7 @@ import { useSelect, useDispatch } from '@wordpress/data'
import useOnPostPreview from './use-on-post-save'
+// Get dismissed errors from localStorage with error handling.
const getDismissedErrors = () => {
try {
const dismissedErrors = JSON.parse( localStorage.getItem( 'interact-dismissed-errors' ) || '[]' )
@@ -40,6 +41,31 @@ const getDismissedErrors = () => {
}
}
+// Normalize imported interaction data to ensure it has the expected structure, even if some fields are missing.
+const normalizeImportedInteraction = data => {
+ const timelines = data.timelines || []
+
+ return {
+ ...data,
+ timelines: timelines.map( timeline => {
+ const actionsToImport = timeline.actions || []
+ const actions = actionsToImport.map( action => (
+ createNewAction( {
+ actionType: action.type ?? '',
+ start: action.timing?.start ?? 0,
+ targetType: action.target?.type ?? '',
+ props: { ...action },
+ } )
+ ) )
+
+ return {
+ ...timeline,
+ actions,
+ }
+ } ),
+ }
+}
+
const InteractionsApp = ( {
selectedBlockAnchor = null,
enablePostPreviewGuard = true,
@@ -262,7 +288,9 @@ const InteractionsApp = ( {
) }
> ),
importLabel: __( 'Import interaction', 'interactions' ),
- onImport: onAddInteractionHandler,
+ onImport: ( type, target, data ) => {
+ onAddInteractionHandler( type, target, normalizeImportedInteraction( data ) )
+ },
} ) }
/>
),
importLabel: __( 'Import interaction', 'interactions' ),
onImport: ( type, target, data ) => {
- const timelines = data.timelines || []
- timelines.forEach( ( timeline, i ) => {
- const actionsToImport = timeline.actions || []
- const newActions = actionsToImport.map( action => (
- createNewAction( {
- actionType: action.type ?? '',
- start: action.timing?.start ?? 0,
- targetType: action.target?.type ?? '',
- props: { ...action },
- } )
- ) )
- data.timelines[ i ] = { ...timeline, actions: newActions }
- } )
-
- onAddInteractionHandler( type, target, data )
+ onAddInteractionHandler( type, target, normalizeImportedInteraction( data ) )
},
} ) }
/>
From ed9014d4dfc62ab449dad2772583bf2e2e68bbfc Mon Sep 17 00:00:00 2001
From: Alquen Sarmiento
Date: Fri, 8 May 2026 15:48:16 +0800
Subject: [PATCH 07/14] fix: run getCurrentSelectedTarget once
---
.../components/add-interaction-popover/index.js | 15 ++++++++++-----
1 file changed, 10 insertions(+), 5 deletions(-)
diff --git a/src/editor/components/add-interaction-popover/index.js b/src/editor/components/add-interaction-popover/index.js
index 38e1210..74f3ed8 100644
--- a/src/editor/components/add-interaction-popover/index.js
+++ b/src/editor/components/add-interaction-popover/index.js
@@ -73,11 +73,16 @@ const AddInteractionPopover = props => {
}
} )
- const [ target, setTarget ] = useState( {
- type: getCurrentSelectedTarget()?.type || ( isElementor ? 'selector' : 'block' ),
- value: getCurrentSelectedTarget()?.value || getOrGenerateBlockAnchor( getSelectedBlockClientId(), false ) || '',
- blockName: getCurrentSelectedTarget()?.blockName || first( getBlockNamesByClientId( getSelectedBlockClientId() ) ) || '',
- options: getCurrentSelectedTarget()?.options || '',
+ const [ target, setTarget ] = useState( () => {
+ const current = getCurrentSelectedTarget()
+ const clientId = getSelectedBlockClientId()
+
+ return {
+ type: current?.type || ( isElementor ? 'selector' : 'block' ),
+ value: current?.value || getOrGenerateBlockAnchor( clientId, false ) || '',
+ blockName: current?.blockName || first( getBlockNamesByClientId( clientId ) ) || '',
+ options: current?.options || '',
+ }
} )
const libraryTitle = ! showElementOption && showPageOption ? __( 'My Page Interactions', 'interactions' )
From ecc4b53d0dc17784a9c91dc58774d293d1ccbb5f Mon Sep 17 00:00:00 2001
From: Alquen Sarmiento
Date: Fri, 8 May 2026 16:01:45 +0800
Subject: [PATCH 08/14] fix: is_admin might prevent elementor enqueue
---
src/editor/editor.php | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/editor/editor.php b/src/editor/editor.php
index 035d2e9..75224fd 100644
--- a/src/editor/editor.php
+++ b/src/editor/editor.php
@@ -19,9 +19,10 @@ class Interact_Editor {
function __construct() {
if ( is_admin() ) {
add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_gutenberg_editor' ) );
- add_action( 'elementor/editor/after_enqueue_scripts', array( $this, 'enqueue_elementor_editor' ) );
add_action( 'enqueue_block_assets', array( $this, 'enqueue_assets' ) );
}
+
++ add_action( 'elementor/editor/after_enqueue_scripts', array( $this, 'enqueue_elementor_editor' ) );
}
/**
From 79ebfcbd06cf9a277ebfefa9316b1a5ab2987ac1 Mon Sep 17 00:00:00 2001
From: Alquen Sarmiento
Date: Mon, 11 May 2026 11:04:32 +0800
Subject: [PATCH 09/14] fix: patch elementor.js
---
src/editor/editors/elementor.js | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/src/editor/editors/elementor.js b/src/editor/editors/elementor.js
index 2dfc58d..f8097c4 100644
--- a/src/editor/editors/elementor.js
+++ b/src/editor/editors/elementor.js
@@ -96,7 +96,7 @@ class ElementorInteractionsEditor extends InteractionsEditorAbstract {
// Return the Elementor preview canvas document.
getCanvasDocument() {
const iframe = document.querySelector( '#elementor-preview-iframe' )
- return iframe?.contentDocument || document
+ return iframe?.contentDocument || null
}
// Open the Interactions sidebar in Elementor.
@@ -178,12 +178,13 @@ class ElementorInteractionsEditor extends InteractionsEditorAbstract {
} )
}
- [
+ const actions = [
'panel/open_editor/section',
'panel/open_editor/column',
'panel/open_editor/container',
'panel/open_editor/widget',
- ].forEach( register )
+ ]
+ actions.forEach( register )
return NOOP
}
@@ -260,11 +261,13 @@ class ElementorInteractionsEditor extends InteractionsEditorAbstract {
clearHighlight()
previewDocument.removeEventListener( 'mousemove', mouseMoveHandler, true )
previewDocument.removeEventListener( 'click', clickHandler, true )
+ previewDocument.removeEventListener( 'keydown', keyHandler, true )
document.removeEventListener( 'keydown', keyHandler, true )
}
previewDocument.addEventListener( 'mousemove', mouseMoveHandler, true )
previewDocument.addEventListener( 'click', clickHandler, true )
+ previewDocument.addEventListener( 'keydown', keyHandler, true )
document.addEventListener( 'keydown', keyHandler, true )
return stop
From 21d65a8a88c9dc7c41ddd78ab6cefed1a67ea9af Mon Sep 17 00:00:00 2001
From: Alquen Sarmiento
Date: Mon, 11 May 2026 11:27:14 +0800
Subject: [PATCH 10/14] fix: generate wp components based on imported
components
---
package-lock.json | 1137 ++++++++++++++++++++++--
package.json | 22 +-
scripts/generate-wp-components-css.mjs | 179 ++++
src/editor/editor.php | 13 +-
4 files changed, 1249 insertions(+), 102 deletions(-)
create mode 100644 scripts/generate-wp-components-css.mjs
diff --git a/package-lock.json b/package-lock.json
index 90050bb..77813be 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,14 +1,15 @@
{
"name": "interactions",
- "version": "1.3.2",
+ "version": "1.3.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "interactions",
- "version": "1.3.2",
+ "version": "1.3.3",
"license": "GPL-2.0-or-later",
"dependencies": {
+ "@wordpress/components": "^33.0.0",
"@wordpress/icons": "^10.31.0",
"canvas-confetti": "^1.9.3",
"classnames": "^2.5.1",
@@ -53,11 +54,48 @@
"node": ">=6.0.0"
}
},
+ "node_modules/@ariakit/core": {
+ "version": "0.4.20",
+ "resolved": "https://registry.npmjs.org/@ariakit/core/-/core-0.4.20.tgz",
+ "integrity": "sha512-DJbUnui0fM+2ZgiWLOMuFOmlWSJDNV3f6tqghIYRTWEm51TN/LoU6uM8og6/g7Nrwl4Uo5l8AoQT9Kkr/i/uRg==",
+ "license": "MIT"
+ },
+ "node_modules/@ariakit/react": {
+ "version": "0.4.26",
+ "resolved": "https://registry.npmjs.org/@ariakit/react/-/react-0.4.26.tgz",
+ "integrity": "sha512-NcoPrYE4vgwyODAhdpNNuA7ldwODDuFqZl6jORPVDY3l+oRjl/OYwtQyyC3ZhC/4mjntYBYuKKrPJEizLmoxpg==",
+ "license": "MIT",
+ "dependencies": {
+ "@ariakit/react-core": "0.4.26"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/ariakit"
+ },
+ "peerDependencies": {
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/@ariakit/react-core": {
+ "version": "0.4.26",
+ "resolved": "https://registry.npmjs.org/@ariakit/react-core/-/react-core-0.4.26.tgz",
+ "integrity": "sha512-/Peh1KiVpjj79nCJIa6lEdzSTT9P9FZoy+CxByIFKL3YKdlXmDIIhS1E/tAqKbDq4ODVdynnqmrIDxE5wCoZYw==",
+ "license": "MIT",
+ "dependencies": {
+ "@ariakit/core": "0.4.20",
+ "@floating-ui/dom": "^1.0.0",
+ "use-sync-external-store": "^1.6.0"
+ },
+ "peerDependencies": {
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
"node_modules/@babel/code-frame": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
"integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
- "dev": true,
"dependencies": {
"@babel/helper-validator-identifier": "^7.27.1",
"js-tokens": "^4.0.0",
@@ -128,7 +166,6 @@
"version": "7.28.3",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz",
"integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==",
- "dev": true,
"dependencies": {
"@babel/parser": "^7.28.3",
"@babel/types": "^7.28.2",
@@ -226,7 +263,6 @@
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
"integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
- "dev": true,
"engines": {
"node": ">=6.9.0"
}
@@ -248,7 +284,6 @@
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
"integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
- "dev": true,
"dependencies": {
"@babel/traverse": "^7.27.1",
"@babel/types": "^7.27.1"
@@ -346,7 +381,6 @@
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
- "dev": true,
"engines": {
"node": ">=6.9.0"
}
@@ -355,7 +389,6 @@
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
"integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
- "dev": true,
"engines": {
"node": ">=6.9.0"
}
@@ -400,7 +433,6 @@
"version": "7.28.4",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz",
"integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==",
- "dev": true,
"dependencies": {
"@babel/types": "^7.28.4"
},
@@ -1874,7 +1906,6 @@
"version": "7.27.2",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
"integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
- "dev": true,
"dependencies": {
"@babel/code-frame": "^7.27.1",
"@babel/parser": "^7.27.2",
@@ -1888,7 +1919,6 @@
"version": "7.28.4",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz",
"integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==",
- "dev": true,
"dependencies": {
"@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.3",
@@ -1906,7 +1936,6 @@
"version": "7.28.4",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz",
"integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==",
- "dev": true,
"dependencies": {
"@babel/helper-string-parser": "^7.27.1",
"@babel/helper-validator-identifier": "^7.27.1"
@@ -2022,6 +2051,18 @@
"@csstools/css-tokenizer": "^3.0.1"
}
},
+ "node_modules/@date-fns/tz": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/@date-fns/tz/-/tz-1.4.1.tgz",
+ "integrity": "sha512-P5LUNhtbj6YfI3iJjw5EL9eUAG6OitD0W3fWQcpQjDRc/QIsL0tRNuO1PcDvPccWL1fSTXXdE1ds+l95DV/OFA==",
+ "license": "MIT"
+ },
+ "node_modules/@date-fns/utc": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/@date-fns/utc/-/utc-2.1.1.tgz",
+ "integrity": "sha512-SlJDfG6RPeEX8wEVv6ZB3kak4MmbtyiI2qX/5zuKdordbrhB/iaJ58GVMZgJ6P1sJaM1gMgENFYYeg1JWrCFrA==",
+ "license": "MIT"
+ },
"node_modules/@discoveryjs/json-ext": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz",
@@ -2041,6 +2082,180 @@
"url": "https://github.com/sponsors/JounQin"
}
},
+ "node_modules/@emotion/babel-plugin": {
+ "version": "11.13.5",
+ "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz",
+ "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.16.7",
+ "@babel/runtime": "^7.18.3",
+ "@emotion/hash": "^0.9.2",
+ "@emotion/memoize": "^0.9.0",
+ "@emotion/serialize": "^1.3.3",
+ "babel-plugin-macros": "^3.1.0",
+ "convert-source-map": "^1.5.0",
+ "escape-string-regexp": "^4.0.0",
+ "find-root": "^1.1.0",
+ "source-map": "^0.5.7",
+ "stylis": "4.2.0"
+ }
+ },
+ "node_modules/@emotion/babel-plugin/node_modules/convert-source-map": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
+ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/babel-plugin/node_modules/source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/@emotion/cache": {
+ "version": "11.14.0",
+ "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz",
+ "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==",
+ "license": "MIT",
+ "dependencies": {
+ "@emotion/memoize": "^0.9.0",
+ "@emotion/sheet": "^1.4.0",
+ "@emotion/utils": "^1.4.2",
+ "@emotion/weak-memoize": "^0.4.0",
+ "stylis": "4.2.0"
+ }
+ },
+ "node_modules/@emotion/css": {
+ "version": "11.13.5",
+ "resolved": "https://registry.npmjs.org/@emotion/css/-/css-11.13.5.tgz",
+ "integrity": "sha512-wQdD0Xhkn3Qy2VNcIzbLP9MR8TafI0MJb7BEAXKp+w4+XqErksWR4OXomuDzPsN4InLdGhVe6EYcn2ZIUCpB8w==",
+ "license": "MIT",
+ "dependencies": {
+ "@emotion/babel-plugin": "^11.13.5",
+ "@emotion/cache": "^11.13.5",
+ "@emotion/serialize": "^1.3.3",
+ "@emotion/sheet": "^1.4.0",
+ "@emotion/utils": "^1.4.2"
+ }
+ },
+ "node_modules/@emotion/hash": {
+ "version": "0.9.2",
+ "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz",
+ "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/is-prop-valid": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.4.0.tgz",
+ "integrity": "sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==",
+ "license": "MIT",
+ "dependencies": {
+ "@emotion/memoize": "^0.9.0"
+ }
+ },
+ "node_modules/@emotion/memoize": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz",
+ "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/react": {
+ "version": "11.14.0",
+ "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz",
+ "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.18.3",
+ "@emotion/babel-plugin": "^11.13.5",
+ "@emotion/cache": "^11.14.0",
+ "@emotion/serialize": "^1.3.3",
+ "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0",
+ "@emotion/utils": "^1.4.2",
+ "@emotion/weak-memoize": "^0.4.0",
+ "hoist-non-react-statics": "^3.3.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@emotion/serialize": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz",
+ "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==",
+ "license": "MIT",
+ "dependencies": {
+ "@emotion/hash": "^0.9.2",
+ "@emotion/memoize": "^0.9.0",
+ "@emotion/unitless": "^0.10.0",
+ "@emotion/utils": "^1.4.2",
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/@emotion/sheet": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz",
+ "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/styled": {
+ "version": "11.14.1",
+ "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.1.tgz",
+ "integrity": "sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.18.3",
+ "@emotion/babel-plugin": "^11.13.5",
+ "@emotion/is-prop-valid": "^1.3.0",
+ "@emotion/serialize": "^1.3.3",
+ "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0",
+ "@emotion/utils": "^1.4.2"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.0.0-rc.0",
+ "react": ">=16.8.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@emotion/unitless": {
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz",
+ "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/use-insertion-effect-with-fallbacks": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz",
+ "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ }
+ },
+ "node_modules/@emotion/utils": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz",
+ "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==",
+ "license": "MIT"
+ },
+ "node_modules/@emotion/weak-memoize": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz",
+ "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==",
+ "license": "MIT"
+ },
"node_modules/@es-joy/jsdoccomment": {
"version": "0.41.0",
"resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.41.0.tgz",
@@ -2166,6 +2381,44 @@
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
+ "node_modules/@floating-ui/core": {
+ "version": "1.7.5",
+ "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.5.tgz",
+ "integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/utils": "^0.2.11"
+ }
+ },
+ "node_modules/@floating-ui/dom": {
+ "version": "1.7.6",
+ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.6.tgz",
+ "integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/core": "^1.7.5",
+ "@floating-ui/utils": "^0.2.11"
+ }
+ },
+ "node_modules/@floating-ui/react-dom": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.8.tgz",
+ "integrity": "sha512-HOdqOt3R3OGeTKidaLvJKcgg75S6tibQ3Tif4eyd91QnIJWr0NLvoXFpJA/j8HqkFSL68GDca9AuyWEHlhyClw==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/dom": "^1.6.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0",
+ "react-dom": ">=16.8.0"
+ }
+ },
+ "node_modules/@floating-ui/utils": {
+ "version": "0.2.11",
+ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.11.tgz",
+ "integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==",
+ "license": "MIT"
+ },
"node_modules/@formatjs/ecma402-abstract": {
"version": "2.3.4",
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.4.tgz",
@@ -2709,7 +2962,6 @@
"version": "0.3.13",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
- "dev": true,
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.0",
"@jridgewell/trace-mapping": "^0.3.24"
@@ -2729,7 +2981,6 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
- "dev": true,
"engines": {
"node": ">=6.0.0"
}
@@ -2747,14 +2998,12 @@
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
- "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
- "dev": true
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.31",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
- "dev": true,
"dependencies": {
"@jridgewell/resolve-uri": "^3.1.0",
"@jridgewell/sourcemap-codec": "^1.4.14"
@@ -4401,6 +4650,52 @@
"url": "https://github.com/sponsors/gregberge"
}
},
+ "node_modules/@tabby_ai/hijri-converter": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/@tabby_ai/hijri-converter/-/hijri-converter-1.0.5.tgz",
+ "integrity": "sha512-r5bClKrcIusDoo049dSL8CawnHR6mRdDwhlQuIgZRNty68q0x8k3Lf1BtPAMxRf/GgnHBnIO4ujd3+GQdLWzxQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/@tannin/compile": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@tannin/compile/-/compile-1.1.0.tgz",
+ "integrity": "sha512-n8m9eNDfoNZoxdvWiTfW/hSPhehzLJ3zW7f8E7oT6mCROoMNWCB4TYtv041+2FMAxweiE0j7i1jubQU4MEC/Gg==",
+ "license": "MIT",
+ "dependencies": {
+ "@tannin/evaluate": "^1.2.0",
+ "@tannin/postfix": "^1.1.0"
+ }
+ },
+ "node_modules/@tannin/evaluate": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@tannin/evaluate/-/evaluate-1.2.0.tgz",
+ "integrity": "sha512-3ioXvNowbO/wSrxsDG5DKIMxC81P0QrQTYai8zFNY+umuoHWRPbQ/TuuDEOju9E+jQDXmj6yI5GyejNuh8I+eg==",
+ "license": "MIT"
+ },
+ "node_modules/@tannin/plural-forms": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@tannin/plural-forms/-/plural-forms-1.1.0.tgz",
+ "integrity": "sha512-xl9R2mDZO/qiHam1AgMnAES6IKIg7OBhcXqy6eDsRCdXuxAFPcjrej9HMjyCLE0DJ/8cHf0i5OQTstuBRhpbHw==",
+ "license": "MIT",
+ "dependencies": {
+ "@tannin/compile": "^1.1.0"
+ }
+ },
+ "node_modules/@tannin/postfix": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@tannin/postfix/-/postfix-1.1.0.tgz",
+ "integrity": "sha512-oocsqY7g0cR+Gur5jRQLSrX2OtpMLMse1I10JQBm8CdGMrDkh1Mg2gjsiquMHRtBs4Qwu5wgEp5GgIYHk4SNPw==",
+ "license": "MIT"
+ },
+ "node_modules/@tannin/sprintf": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@tannin/sprintf/-/sprintf-1.3.3.tgz",
+ "integrity": "sha512-RwARl+hFwhzy0tg9atWcchLFvoQiOh4rrP7uG2N5E4W80BPCUX0ElcUR9St43fxB9EfjsW2df9Qp+UsTbvQDjA==",
+ "license": "MIT"
+ },
"node_modules/@tootallnate/once": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
@@ -4575,6 +4870,18 @@
"@types/node": "*"
}
},
+ "node_modules/@types/gradient-parser": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@types/gradient-parser/-/gradient-parser-1.1.0.tgz",
+ "integrity": "sha512-SaEcbgQscHtGJ1QL+ajgDTmmqU2f6T+00jZRcFlVHUW2Asivc84LNUev/UQFyu117AsdyrtI+qpwLvgjJXJxmw==",
+ "license": "MIT"
+ },
+ "node_modules/@types/highlight-words-core": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@types/highlight-words-core/-/highlight-words-core-1.2.1.tgz",
+ "integrity": "sha512-9VZUA5omXBfn+hDxFjUDu1FOJTBM3LmvqfDey+Z6Aa8B8/JmF5SMj6FBrjfgJ/Q3YXOZd3qyTDfJyMZSs/wCUA==",
+ "license": "MIT"
+ },
"node_modules/@types/http-errors": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz",
@@ -4649,6 +4956,12 @@
"integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==",
"dev": true
},
+ "node_modules/@types/mousetrap": {
+ "version": "1.6.15",
+ "resolved": "https://registry.npmjs.org/@types/mousetrap/-/mousetrap-1.6.15.tgz",
+ "integrity": "sha512-qL0hyIMNPow317QWW/63RvL1x5MVMV+Ru3NaY9f/CuEpCqrmb7WeuK2071ZY5hczOnm38qExWM2i2WtkXLSqFw==",
+ "license": "MIT"
+ },
"node_modules/@types/mysql": {
"version": "2.15.26",
"resolved": "https://registry.npmjs.org/@types/mysql/-/mysql-2.15.26.tgz",
@@ -4685,8 +4998,7 @@
"node_modules/@types/parse-json": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
- "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==",
- "dev": true
+ "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw=="
},
"node_modules/@types/pg": {
"version": "8.6.1",
@@ -4726,12 +5038,13 @@
"dev": true
},
"node_modules/@types/react": {
- "version": "18.3.24",
- "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.24.tgz",
- "integrity": "sha512-0dLEBsA1kI3OezMBF8nSsb7Nk19ZnsyE1LLhB8r27KbgU5H4pvuqZLdtE+aUkJVoXgTVuA+iLIwmZ0TuK4tx6A==",
+ "version": "18.3.28",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.28.tgz",
+ "integrity": "sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==",
+ "license": "MIT",
"dependencies": {
"@types/prop-types": "*",
- "csstype": "^3.0.2"
+ "csstype": "^3.2.2"
}
},
"node_modules/@types/react-dom": {
@@ -5098,6 +5411,24 @@
"integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==",
"dev": true
},
+ "node_modules/@use-gesture/core": {
+ "version": "10.3.1",
+ "resolved": "https://registry.npmjs.org/@use-gesture/core/-/core-10.3.1.tgz",
+ "integrity": "sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw==",
+ "license": "MIT"
+ },
+ "node_modules/@use-gesture/react": {
+ "version": "10.3.1",
+ "resolved": "https://registry.npmjs.org/@use-gesture/react/-/react-10.3.1.tgz",
+ "integrity": "sha512-Yy19y6O2GJq8f7CHf7L0nxL8bf4PZCPaVOCgJrusOeFHY1LvHgYXnmnXg6N5iwAnbgbZCDjo60SiM6IPJi9C5g==",
+ "license": "MIT",
+ "dependencies": {
+ "@use-gesture/core": "10.3.1"
+ },
+ "peerDependencies": {
+ "react": ">= 16.8.0"
+ }
+ },
"node_modules/@webassemblyjs/ast": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz",
@@ -5288,6 +5619,20 @@
}
}
},
+ "node_modules/@wordpress/a11y": {
+ "version": "4.45.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/a11y/-/a11y-4.45.0.tgz",
+ "integrity": "sha512-KOgdBsZP34nAi+UfrhIAZDt2I1ZDb3DXAgIeQk7QxTIc9OlQKMNfrYwPG0jidgfKwmjFxh8vV8HbZcBzTD29Rw==",
+ "license": "GPL-2.0-or-later",
+ "dependencies": {
+ "@wordpress/dom-ready": "^4.45.0",
+ "@wordpress/i18n": "^6.18.0"
+ },
+ "engines": {
+ "node": ">=18.12.0",
+ "npm": ">=8.19.2"
+ }
+ },
"node_modules/@wordpress/babel-preset-default": {
"version": "8.31.0",
"resolved": "https://registry.npmjs.org/@wordpress/babel-preset-default/-/babel-preset-default-8.31.0.tgz",
@@ -5361,6 +5706,199 @@
"npm": ">=8.19.2"
}
},
+ "node_modules/@wordpress/components": {
+ "version": "33.0.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/components/-/components-33.0.0.tgz",
+ "integrity": "sha512-VeLDtfz8612bdRqgQiSMtIIEGDi4ZByj0XUvjT7E6RVLgczQyV9DTpGOPyL6PbTyAluIx6hjt9bzsaC+bM6G+w==",
+ "license": "GPL-2.0-or-later",
+ "dependencies": {
+ "@ariakit/react": "^0.4.22",
+ "@date-fns/utc": "^2.1.1",
+ "@emotion/cache": "^11.14.0",
+ "@emotion/css": "^11.13.5",
+ "@emotion/react": "^11.14.0",
+ "@emotion/serialize": "^1.3.3",
+ "@emotion/styled": "^11.14.1",
+ "@emotion/utils": "^1.4.2",
+ "@floating-ui/react-dom": "2.0.8",
+ "@types/gradient-parser": "1.1.0",
+ "@types/highlight-words-core": "1.2.1",
+ "@types/react": "^18.3.27",
+ "@use-gesture/react": "^10.3.1",
+ "@wordpress/a11y": "^4.45.0",
+ "@wordpress/base-styles": "^7.0.0",
+ "@wordpress/compose": "^7.45.0",
+ "@wordpress/date": "^5.45.0",
+ "@wordpress/deprecated": "^4.45.0",
+ "@wordpress/dom": "^4.45.0",
+ "@wordpress/element": "^6.45.0",
+ "@wordpress/escape-html": "^3.45.0",
+ "@wordpress/hooks": "^4.45.0",
+ "@wordpress/html-entities": "^4.45.0",
+ "@wordpress/i18n": "^6.18.0",
+ "@wordpress/icons": "^13.0.0",
+ "@wordpress/is-shallow-equal": "^5.45.0",
+ "@wordpress/keycodes": "^4.45.0",
+ "@wordpress/primitives": "^4.45.0",
+ "@wordpress/private-apis": "^1.45.0",
+ "@wordpress/rich-text": "^7.45.0",
+ "@wordpress/warning": "^3.45.0",
+ "change-case": "^4.1.2",
+ "clsx": "^2.1.1",
+ "colord": "^2.7.0",
+ "csstype": "^3.2.3",
+ "date-fns": "^3.6.0",
+ "deepmerge": "^4.3.0",
+ "fast-deep-equal": "^3.1.3",
+ "framer-motion": "^11.15.0",
+ "gradient-parser": "1.1.1",
+ "highlight-words-core": "^1.2.2",
+ "is-plain-object": "^5.0.0",
+ "memize": "^2.1.0",
+ "path-to-regexp": "^6.2.1",
+ "re-resizable": "^6.4.0",
+ "react-colorful": "^5.6.1",
+ "react-day-picker": "^9.7.0",
+ "remove-accents": "^0.5.0",
+ "uuid": "^9.0.1"
+ },
+ "engines": {
+ "node": ">=18.12.0",
+ "npm": ">=8.19.2"
+ },
+ "peerDependencies": {
+ "react": "^18.0.0",
+ "react-dom": "^18.0.0"
+ }
+ },
+ "node_modules/@wordpress/components/node_modules/@wordpress/base-styles": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/base-styles/-/base-styles-7.0.0.tgz",
+ "integrity": "sha512-Q0BbZzfeYbQZKHnyNT4RF8RGVugN5jStGtpRKhBYQW7ut7sS61LbbpP7jR0D0sDPYoEEC8jKZQSZwSM23B4jow==",
+ "license": "GPL-2.0-or-later",
+ "engines": {
+ "node": ">=18.12.0",
+ "npm": ">=8.19.2"
+ }
+ },
+ "node_modules/@wordpress/components/node_modules/@wordpress/icons": {
+ "version": "13.0.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/icons/-/icons-13.0.0.tgz",
+ "integrity": "sha512-+CLbvNdzMUHxQK5I6gFdHb3X6EVAH6SOSIj0xtMWm6PZO+Nnf7tXHfNBuxqTnGfxT5grtfb6D3A9ZMBU+Tpv+Q==",
+ "license": "GPL-2.0-or-later",
+ "dependencies": {
+ "@wordpress/element": "^6.45.0",
+ "@wordpress/primitives": "^4.45.0",
+ "change-case": "4.1.2"
+ },
+ "engines": {
+ "node": ">=18.12.0",
+ "npm": ">=8.19.2"
+ },
+ "peerDependencies": {
+ "react": "^18.0.0"
+ }
+ },
+ "node_modules/@wordpress/components/node_modules/date-fns": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz",
+ "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/kossnocorp"
+ }
+ },
+ "node_modules/@wordpress/components/node_modules/path-to-regexp": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz",
+ "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==",
+ "license": "MIT"
+ },
+ "node_modules/@wordpress/components/node_modules/uuid": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
+ "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
+ "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).",
+ "funding": [
+ "https://github.com/sponsors/broofa",
+ "https://github.com/sponsors/ctavan"
+ ],
+ "license": "MIT",
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
+ "node_modules/@wordpress/compose": {
+ "version": "7.45.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/compose/-/compose-7.45.0.tgz",
+ "integrity": "sha512-/keWdRFUe7bnzh2ZtOYLexknpj0K0G56WFw7RLZehl54a9EmzjYjAODBOF9DB3c07pJuNuy7c5QgqMPi0cqLlw==",
+ "license": "GPL-2.0-or-later",
+ "dependencies": {
+ "@types/mousetrap": "^1.6.8",
+ "@wordpress/deprecated": "^4.45.0",
+ "@wordpress/dom": "^4.45.0",
+ "@wordpress/element": "^6.45.0",
+ "@wordpress/is-shallow-equal": "^5.45.0",
+ "@wordpress/keycodes": "^4.45.0",
+ "@wordpress/priority-queue": "^3.45.0",
+ "@wordpress/undo-manager": "^1.45.0",
+ "change-case": "^4.1.2",
+ "mousetrap": "^1.6.5",
+ "use-memo-one": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=18.12.0",
+ "npm": ">=8.19.2"
+ },
+ "peerDependencies": {
+ "react": "^18.0.0"
+ }
+ },
+ "node_modules/@wordpress/data": {
+ "version": "10.45.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/data/-/data-10.45.0.tgz",
+ "integrity": "sha512-OR/uMpcEbCh1aBkbzateXffNrL829M+N92qtuD+Gt08Mey129WIEVR9kBC2Tf02VtXs644OKZD6cz77KlxH8XA==",
+ "license": "GPL-2.0-or-later",
+ "dependencies": {
+ "@wordpress/compose": "^7.45.0",
+ "@wordpress/deprecated": "^4.45.0",
+ "@wordpress/element": "^6.45.0",
+ "@wordpress/is-shallow-equal": "^5.45.0",
+ "@wordpress/priority-queue": "^3.45.0",
+ "@wordpress/private-apis": "^1.45.0",
+ "@wordpress/redux-routine": "^5.45.0",
+ "deepmerge": "^4.3.0",
+ "equivalent-key-map": "^0.2.2",
+ "is-plain-object": "^5.0.0",
+ "is-promise": "^4.0.0",
+ "redux": "^5.0.1",
+ "rememo": "^4.0.2",
+ "use-memo-one": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=18.12.0",
+ "npm": ">=8.19.2"
+ },
+ "peerDependencies": {
+ "react": "^18.0.0"
+ }
+ },
+ "node_modules/@wordpress/date": {
+ "version": "5.45.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/date/-/date-5.45.0.tgz",
+ "integrity": "sha512-34v3hCxn68kYzWs8bhuAt8cfMxdFX9ukKn3a3FB+tAJXpxafnPCcZoWfJHn4I8hepCbreFrf3UiGdA+id2kQ4A==",
+ "license": "GPL-2.0-or-later",
+ "dependencies": {
+ "@wordpress/deprecated": "^4.45.0",
+ "moment": "^2.29.4",
+ "moment-timezone": "^0.5.40"
+ },
+ "engines": {
+ "node": ">=18.12.0",
+ "npm": ">=8.19.2"
+ }
+ },
"node_modules/@wordpress/dependency-extraction-webpack-plugin": {
"version": "6.31.0",
"resolved": "https://registry.npmjs.org/@wordpress/dependency-extraction-webpack-plugin/-/dependency-extraction-webpack-plugin-6.31.0.tgz",
@@ -5383,6 +5921,42 @@
"integrity": "sha512-dnSoUiLAoVaMXxFsVi4CrPVYMKOuDBXTghXSmMINX44RZ8WM9cXlY7UqrQnlAcODCVO7FV3+8t/5nDKAjimLfg==",
"dev": true
},
+ "node_modules/@wordpress/deprecated": {
+ "version": "4.45.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/deprecated/-/deprecated-4.45.0.tgz",
+ "integrity": "sha512-qer/fk/lgmmisb8/hj1xZtsbJbZhCoOblhyxI2k7RRul7rQDdk+fm28LJYV+eIF0ldSVX30f4dmz1pvcVHQEEg==",
+ "license": "GPL-2.0-or-later",
+ "dependencies": {
+ "@wordpress/hooks": "^4.45.0"
+ },
+ "engines": {
+ "node": ">=18.12.0",
+ "npm": ">=8.19.2"
+ }
+ },
+ "node_modules/@wordpress/dom": {
+ "version": "4.45.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/dom/-/dom-4.45.0.tgz",
+ "integrity": "sha512-6RObr/KEZS1FnZwpcDAsKlJ3qw2KLF5+A/LsxlM9fSWDGSO05CEaTp+VmWgx9pwjQWbPEa7N73ijEy8cCNSZWA==",
+ "license": "GPL-2.0-or-later",
+ "dependencies": {
+ "@wordpress/deprecated": "^4.45.0"
+ },
+ "engines": {
+ "node": ">=18.12.0",
+ "npm": ">=8.19.2"
+ }
+ },
+ "node_modules/@wordpress/dom-ready": {
+ "version": "4.45.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/dom-ready/-/dom-ready-4.45.0.tgz",
+ "integrity": "sha512-0lFImpg9DGXcGCDQePdoU8haz7QYsKOFXUMTpRvi/Te38LFXzgZtOUBQbY8fRBlLxrgrj4FsAIc7bzdLn73wNQ==",
+ "license": "GPL-2.0-or-later",
+ "engines": {
+ "node": ">=18.12.0",
+ "npm": ">=8.19.2"
+ }
+ },
"node_modules/@wordpress/e2e-test-utils-playwright": {
"version": "1.31.0",
"resolved": "https://registry.npmjs.org/@wordpress/e2e-test-utils-playwright/-/e2e-test-utils-playwright-1.31.0.tgz",
@@ -5405,14 +5979,14 @@
}
},
"node_modules/@wordpress/element": {
- "version": "6.31.0",
- "resolved": "https://registry.npmjs.org/@wordpress/element/-/element-6.31.0.tgz",
- "integrity": "sha512-KOier6Y4b4Y5yEV1GYen81R9gCEOvJT6eVbsc93w2fFEKi2FK/oI7IKzGv9GeJMkoCWvTSX6C/ZYTWk6fCUfeA==",
+ "version": "6.45.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/element/-/element-6.45.0.tgz",
+ "integrity": "sha512-WFrGNPEnj8uE+XhFW9NVbxvqraYpConaEokLv9IszFYVfyg8juXSQcHOAfEnxjC08HBPfVcayr2igu/XUgGOAw==",
+ "license": "GPL-2.0-or-later",
"dependencies": {
- "@babel/runtime": "7.25.7",
- "@types/react": "^18.2.79",
- "@types/react-dom": "^18.2.25",
- "@wordpress/escape-html": "^3.31.0",
+ "@types/react": "^18.3.27",
+ "@types/react-dom": "^18.3.1",
+ "@wordpress/escape-html": "^3.45.0",
"change-case": "^4.1.2",
"is-plain-object": "^5.0.0",
"react": "^18.3.0",
@@ -5424,12 +5998,10 @@
}
},
"node_modules/@wordpress/escape-html": {
- "version": "3.31.0",
- "resolved": "https://registry.npmjs.org/@wordpress/escape-html/-/escape-html-3.31.0.tgz",
- "integrity": "sha512-9g9qd7Q16PWDeYEa2dU+84d1SvjP4LfS7n7AuXkwl5+F7KfL2nZTmDTHWutw9jVjdDAGmjm1VNIj4ydQk9vaLA==",
- "dependencies": {
- "@babel/runtime": "7.25.7"
- },
+ "version": "3.45.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/escape-html/-/escape-html-3.45.0.tgz",
+ "integrity": "sha512-IW4mnA+65XKhABuBkwrQNAlbq97luC6ZIBfdSq0Tkq+AFPqE1lJTMlLo7iBkTpsHsBLyznViPXultq40fz8L7w==",
+ "license": "GPL-2.0-or-later",
"engines": {
"node": ">=18.12.0",
"npm": ">=8.19.2"
@@ -5478,6 +6050,46 @@
}
}
},
+ "node_modules/@wordpress/hooks": {
+ "version": "4.45.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/hooks/-/hooks-4.45.0.tgz",
+ "integrity": "sha512-+gOlu8TdohqL1INQNxS/7CxhM4T4MuYnKietWV9zWDmNQV2ysM0SdamNk5pWERJ4w0yY9XhtMBcwR/piJtePZg==",
+ "license": "GPL-2.0-or-later",
+ "engines": {
+ "node": ">=18.12.0",
+ "npm": ">=8.19.2"
+ }
+ },
+ "node_modules/@wordpress/html-entities": {
+ "version": "4.45.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/html-entities/-/html-entities-4.45.0.tgz",
+ "integrity": "sha512-7W95xaOv4UgMSWlEmyO7YkBsUae3QlQu3GKENVH7Pt/osbJGSPInAJ1ruO4oeUwGPygWOL7b7IzRsgTNP0M/Wg==",
+ "license": "GPL-2.0-or-later",
+ "engines": {
+ "node": ">=18.12.0",
+ "npm": ">=8.19.2"
+ }
+ },
+ "node_modules/@wordpress/i18n": {
+ "version": "6.18.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/i18n/-/i18n-6.18.0.tgz",
+ "integrity": "sha512-6dYCih4wUwi7Csu4RNfHiAKkgWhpSQdl8YthvQUF59Sfsoia3RCdtd4K2l7W4f18ldFA/RXjShMjvSexWy6OyQ==",
+ "license": "GPL-2.0-or-later",
+ "dependencies": {
+ "@tannin/sprintf": "^1.3.2",
+ "@wordpress/hooks": "^4.45.0",
+ "gettext-parser": "^1.3.1",
+ "memize": "^2.1.0",
+ "tannin": "^1.2.0"
+ },
+ "bin": {
+ "pot-to-php": "tools/pot-to-php.js"
+ },
+ "engines": {
+ "node": ">=18.12.0",
+ "npm": ">=8.19.2"
+ }
+ },
"node_modules/@wordpress/icons": {
"version": "10.31.0",
"resolved": "https://registry.npmjs.org/@wordpress/icons/-/icons-10.31.0.tgz",
@@ -5492,6 +6104,16 @@
"npm": ">=8.19.2"
}
},
+ "node_modules/@wordpress/is-shallow-equal": {
+ "version": "5.45.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/is-shallow-equal/-/is-shallow-equal-5.45.0.tgz",
+ "integrity": "sha512-saamGjAuhZOiFOyznsriPGrO8GRDremImMO4q92qjQqmDqssC+FRDQnwr9D8BaedSnVvUDcriGeYBObEEnIJ2A==",
+ "license": "GPL-2.0-or-later",
+ "engines": {
+ "node": ">=18.12.0",
+ "npm": ">=8.19.2"
+ }
+ },
"node_modules/@wordpress/jest-console": {
"version": "8.31.0",
"resolved": "https://registry.npmjs.org/@wordpress/jest-console/-/jest-console-8.31.0.tgz",
@@ -5527,6 +6149,19 @@
"jest": ">=29"
}
},
+ "node_modules/@wordpress/keycodes": {
+ "version": "4.45.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/keycodes/-/keycodes-4.45.0.tgz",
+ "integrity": "sha512-N+Wp572xZovLM45cYo6HfUNTQNDfEqakAYIOcY8bUqA2iFelN6AUkNfUIkIxmrE0EqkQAQ5odES03g8ym7e1IA==",
+ "license": "GPL-2.0-or-later",
+ "dependencies": {
+ "@wordpress/i18n": "^6.18.0"
+ },
+ "engines": {
+ "node": ">=18.12.0",
+ "npm": ">=8.19.2"
+ }
+ },
"node_modules/@wordpress/npm-package-json-lint-config": {
"version": "5.31.0",
"resolved": "https://registry.npmjs.org/@wordpress/npm-package-json-lint-config/-/npm-package-json-lint-config-5.31.0.tgz",
@@ -5571,12 +6206,12 @@
}
},
"node_modules/@wordpress/primitives": {
- "version": "4.31.0",
- "resolved": "https://registry.npmjs.org/@wordpress/primitives/-/primitives-4.31.0.tgz",
- "integrity": "sha512-cY4EKYQRqHu9NZuoWchxc/KWiofwGskzxz0oCfgbdkRlfTag8yBjWMayz+fRNaenw0l5pzLyIg3rcNDN8xLezw==",
+ "version": "4.45.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/primitives/-/primitives-4.45.0.tgz",
+ "integrity": "sha512-x+i6EKUvz96EkUb2KuBTLNGm8d5+ZS0FYjUEnIhp5dtWxjMe8dJT6LS+n363vg+K28LVvjptiTAaByccnNKc9w==",
+ "license": "GPL-2.0-or-later",
"dependencies": {
- "@babel/runtime": "7.25.7",
- "@wordpress/element": "^6.31.0",
+ "@wordpress/element": "^6.45.0",
"clsx": "^2.1.1"
},
"engines": {
@@ -5587,6 +6222,74 @@
"react": "^18.0.0"
}
},
+ "node_modules/@wordpress/priority-queue": {
+ "version": "3.45.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/priority-queue/-/priority-queue-3.45.0.tgz",
+ "integrity": "sha512-0sIX2PRPzo5nk252f60xpPj3/BUZxEOLcabCC7FuvQDYPGZrRyS6Dy0vDDzozZxHGuUYCT65t8ubBwXx37wXCw==",
+ "license": "GPL-2.0-or-later",
+ "dependencies": {
+ "requestidlecallback": "^0.3.0"
+ },
+ "engines": {
+ "node": ">=18.12.0",
+ "npm": ">=8.19.2"
+ }
+ },
+ "node_modules/@wordpress/private-apis": {
+ "version": "1.45.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/private-apis/-/private-apis-1.45.0.tgz",
+ "integrity": "sha512-UjhIDpoyKKUghPM0tkqd5Whsuk4kqfAfhb5VYGoEYtunDs0rB8IxgFO7hE0PhimHL74QVgaJOlprRZVRCCoQ6w==",
+ "license": "GPL-2.0-or-later",
+ "engines": {
+ "node": ">=18.12.0",
+ "npm": ">=8.19.2"
+ }
+ },
+ "node_modules/@wordpress/redux-routine": {
+ "version": "5.45.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/redux-routine/-/redux-routine-5.45.0.tgz",
+ "integrity": "sha512-6ShpBns4jIBFXrYFBcKA5pnFm/kjr1SqFvLj5DwLgMV61eI3Rr9LyZwIzNR2BGg067ryxu4W172Uqjke/mZjcQ==",
+ "license": "GPL-2.0-or-later",
+ "dependencies": {
+ "is-plain-object": "^5.0.0",
+ "is-promise": "^4.0.0",
+ "rungen": "^0.3.2"
+ },
+ "engines": {
+ "node": ">=18.12.0",
+ "npm": ">=8.19.2"
+ },
+ "peerDependencies": {
+ "redux": ">=4"
+ }
+ },
+ "node_modules/@wordpress/rich-text": {
+ "version": "7.45.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/rich-text/-/rich-text-7.45.0.tgz",
+ "integrity": "sha512-C5+JQqNzA3fiQq0hN9pQPKsjcwO/fczouHqubq3847kAUrClROqqI1GJHE34WLl1Vp+/tWQuBkIjQ/95olKteA==",
+ "license": "GPL-2.0-or-later",
+ "dependencies": {
+ "@wordpress/a11y": "^4.45.0",
+ "@wordpress/compose": "^7.45.0",
+ "@wordpress/data": "^10.45.0",
+ "@wordpress/deprecated": "^4.45.0",
+ "@wordpress/dom": "^4.45.0",
+ "@wordpress/element": "^6.45.0",
+ "@wordpress/escape-html": "^3.45.0",
+ "@wordpress/i18n": "^6.18.0",
+ "@wordpress/keycodes": "^4.45.0",
+ "@wordpress/private-apis": "^1.45.0",
+ "colord": "2.9.3",
+ "memize": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=18.12.0",
+ "npm": ">=8.19.2"
+ },
+ "peerDependencies": {
+ "react": "^18.0.0"
+ }
+ },
"node_modules/@wordpress/scripts": {
"version": "30.24.0",
"resolved": "https://registry.npmjs.org/@wordpress/scripts/-/scripts-30.24.0.tgz",
@@ -5777,11 +6480,24 @@
"stylelint-scss": "^6.4.0"
}
},
+ "node_modules/@wordpress/undo-manager": {
+ "version": "1.45.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/undo-manager/-/undo-manager-1.45.0.tgz",
+ "integrity": "sha512-BqclZIPjzBYIjLqLZFihs+Ce+w+yBQuj44VYSrRDOj56AbMtwmClIUqgIVBZAe2En/2ncixTTWOZG9KluvEXfA==",
+ "license": "GPL-2.0-or-later",
+ "dependencies": {
+ "@wordpress/is-shallow-equal": "^5.45.0"
+ },
+ "engines": {
+ "node": ">=18.12.0",
+ "npm": ">=8.19.2"
+ }
+ },
"node_modules/@wordpress/warning": {
- "version": "3.31.0",
- "resolved": "https://registry.npmjs.org/@wordpress/warning/-/warning-3.31.0.tgz",
- "integrity": "sha512-Npw1Apa6r+K+jtX40ABWAXv7J1bVnOi6h9VPiMY8l/iZoRHBXao8HTgQnIoCm+GzymaQs6NQoH4X8UAClggeXA==",
- "dev": true,
+ "version": "3.45.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/warning/-/warning-3.45.0.tgz",
+ "integrity": "sha512-NQ9tAhPdwhfceVIzWra1rbumvgAFAEDTgZlWsX880zLiq1F8JTwBouwW6wfIhA3XLcY6Yj7cBBYLa8vnNiDZDw==",
+ "license": "GPL-2.0-or-later",
"engines": {
"node": ">=18.12.0",
"npm": ">=8.19.2"
@@ -6547,6 +7263,21 @@
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
+ "node_modules/babel-plugin-macros": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
+ "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.12.5",
+ "cosmiconfig": "^7.0.0",
+ "resolve": "^1.19.0"
+ },
+ "engines": {
+ "node": ">=10",
+ "npm": ">=6"
+ }
+ },
"node_modules/babel-plugin-polyfill-corejs2": {
"version": "0.4.14",
"resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.14.tgz",
@@ -7046,7 +7777,6 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
- "dev": true,
"engines": {
"node": ">=6"
}
@@ -7415,8 +8145,7 @@
"node_modules/colord": {
"version": "2.9.3",
"resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz",
- "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==",
- "dev": true
+ "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw=="
},
"node_modules/colorette": {
"version": "2.0.20",
@@ -7760,7 +8489,6 @@
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
"integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
- "dev": true,
"dependencies": {
"@types/parse-json": "^4.0.0",
"import-fresh": "^3.2.1",
@@ -8093,9 +8821,10 @@
"dev": true
},
"node_modules/csstype": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
- "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+ "license": "MIT"
},
"node_modules/cwd": {
"version": "0.10.0",
@@ -8206,6 +8935,12 @@
"url": "https://opencollective.com/date-fns"
}
},
+ "node_modules/date-fns-jalali": {
+ "version": "4.1.0-0",
+ "resolved": "https://registry.npmjs.org/date-fns-jalali/-/date-fns-jalali-4.1.0-0.tgz",
+ "integrity": "sha512-hTIP/z+t+qKwBDcmmsnmjWTduxCg+5KfdqWQvb2X/8C9+knYY6epN/pfxdDuyVlSVeFz0sM5eEfwIUQ70U4ckg==",
+ "license": "MIT"
+ },
"node_modules/debounce": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz",
@@ -8216,7 +8951,6 @@
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
- "dev": true,
"dependencies": {
"ms": "^2.1.3"
},
@@ -8302,7 +9036,6 @@
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
- "dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -8655,6 +9388,15 @@
"node": ">= 0.8"
}
},
+ "node_modules/encoding": {
+ "version": "0.1.13",
+ "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
+ "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
+ "license": "MIT",
+ "dependencies": {
+ "iconv-lite": "^0.6.2"
+ }
+ },
"node_modules/end-of-stream": {
"version": "1.4.5",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
@@ -8723,11 +9465,16 @@
"node": ">=4"
}
},
+ "node_modules/equivalent-key-map": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/equivalent-key-map/-/equivalent-key-map-0.2.2.tgz",
+ "integrity": "sha512-xvHeyCDbZzkpN4VHQj/n+j2lOwL0VWszG30X4cOrc9Y7Tuo2qCdZK/0AMod23Z5dCtNUbaju6p0rwOhHUk05ew==",
+ "license": "MIT"
+ },
"node_modules/error-ex": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz",
"integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==",
- "dev": true,
"dependencies": {
"is-arrayish": "^0.2.1"
}
@@ -8935,7 +9682,6 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
- "dev": true,
"engines": {
"node": ">=10"
},
@@ -10049,8 +10795,7 @@
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
- "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
- "dev": true
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
},
"node_modules/fast-diff": {
"version": "1.3.0",
@@ -10350,6 +11095,12 @@
"find-process": "bin/find-process.js"
}
},
+ "node_modules/find-root": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
+ "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==",
+ "license": "MIT"
+ },
"node_modules/find-up": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
@@ -10524,6 +11275,33 @@
"url": "https://github.com/sponsors/rawify"
}
},
+ "node_modules/framer-motion": {
+ "version": "11.18.2",
+ "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.18.2.tgz",
+ "integrity": "sha512-5F5Och7wrvtLVElIpclDT0CBzMVg3dL22B64aZwHtsIY8RB4mXICLrkajK4G9R+ieSAGcgrLeae2SeUTg2pr6w==",
+ "license": "MIT",
+ "dependencies": {
+ "motion-dom": "^11.18.1",
+ "motion-utils": "^11.18.1",
+ "tslib": "^2.4.0"
+ },
+ "peerDependencies": {
+ "@emotion/is-prop-valid": "*",
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/is-prop-valid": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ },
+ "react-dom": {
+ "optional": true
+ }
+ }
+ },
"node_modules/fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
@@ -10572,7 +11350,6 @@
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
- "dev": true,
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
@@ -10737,6 +11514,16 @@
"node": ">= 14"
}
},
+ "node_modules/gettext-parser": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/gettext-parser/-/gettext-parser-1.4.0.tgz",
+ "integrity": "sha512-sedZYLHlHeBop/gZ1jdg59hlUEcpcZJofLq2JFwJT1zTqAU3l2wFv6IsuwFHGqbiT9DWzMUW4/em2+hspnmMMA==",
+ "license": "MIT",
+ "dependencies": {
+ "encoding": "^0.1.12",
+ "safe-buffer": "^5.1.1"
+ }
+ },
"node_modules/glob": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
@@ -10902,6 +11689,14 @@
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
"dev": true
},
+ "node_modules/gradient-parser": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/gradient-parser/-/gradient-parser-1.1.1.tgz",
+ "integrity": "sha512-Hu0YfNU+38EsTmnUfLXUKFMXq9yz7htGYpF4x+dlbBhUCvIvzLt0yVLT/gJRmvLKFJdqNFrz4eKkIUjIXSr7Tw==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/graphemer": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
@@ -11017,7 +11812,6 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
- "dev": true,
"dependencies": {
"function-bind": "^1.1.2"
},
@@ -11034,6 +11828,27 @@
"tslib": "^2.0.3"
}
},
+ "node_modules/highlight-words-core": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/highlight-words-core/-/highlight-words-core-1.2.3.tgz",
+ "integrity": "sha512-m1O9HW3/GNHxzSIXWw1wCNXXsgLlxrP0OI6+ycGUhiUHkikqW3OrwVHz+lxeNBe5yqLESdIcj8PowHQ2zLvUvQ==",
+ "license": "MIT"
+ },
+ "node_modules/hoist-non-react-statics": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
+ "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "react-is": "^16.7.0"
+ }
+ },
+ "node_modules/hoist-non-react-statics/node_modules/react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
+ "license": "MIT"
+ },
"node_modules/homedir-polyfill": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz",
@@ -11291,7 +12106,6 @@
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
- "dev": true,
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
},
@@ -11390,7 +12204,6 @@
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
"integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
- "dev": true,
"dependencies": {
"parent-module": "^1.0.0",
"resolve-from": "^4.0.0"
@@ -11406,7 +12219,6 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
- "dev": true,
"engines": {
"node": ">=4"
}
@@ -11577,8 +12389,7 @@
"node_modules/is-arrayish": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
- "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
- "dev": true
+ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="
},
"node_modules/is-async-function": {
"version": "2.1.1",
@@ -11679,7 +12490,6 @@
"version": "2.16.1",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
"integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
- "dev": true,
"dependencies": {
"hasown": "^2.0.2"
},
@@ -11903,6 +12713,12 @@
"integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==",
"dev": true
},
+ "node_modules/is-promise": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
+ "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
+ "license": "MIT"
+ },
"node_modules/is-regex": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz",
@@ -12992,7 +13808,6 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
"integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
- "dev": true,
"bin": {
"jsesc": "bin/jsesc"
},
@@ -13009,8 +13824,7 @@
"node_modules/json-parse-even-better-errors": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
- "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
- "dev": true
+ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
},
"node_modules/json-schema-traverse": {
"version": "0.4.1",
@@ -13416,8 +14230,7 @@
"node_modules/lines-and-columns": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
- "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
- "dev": true
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
},
"node_modules/linkify-it": {
"version": "3.0.3",
@@ -13844,6 +14657,12 @@
"node": ">= 4.0.0"
}
},
+ "node_modules/memize": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/memize/-/memize-2.1.1.tgz",
+ "integrity": "sha512-8Nl+i9S5D6KXnruM03Jgjb+LwSupvR13WBr4hJegaaEyobvowCVupi79y2WSiWvO1mzBWxPwEYE5feCe8vyA5w==",
+ "license": "MIT"
+ },
"node_modules/meow": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz",
@@ -14145,6 +14964,48 @@
"integrity": "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==",
"dev": true
},
+ "node_modules/moment": {
+ "version": "2.30.1",
+ "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
+ "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/moment-timezone": {
+ "version": "0.5.48",
+ "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.48.tgz",
+ "integrity": "sha512-f22b8LV1gbTO2ms2j2z13MuPogNoh5UzxL3nzNAYKGraILnbGc9NEE6dyiiiLv46DGRb8A4kg8UKWLjPthxBHw==",
+ "license": "MIT",
+ "dependencies": {
+ "moment": "^2.29.4"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/motion-dom": {
+ "version": "11.18.1",
+ "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-11.18.1.tgz",
+ "integrity": "sha512-g76KvA001z+atjfxczdRtw/RXOM3OMSdd1f4DL77qCTF/+avrRJiawSG4yDibEQ215sr9kpinSlX2pCTJ9zbhw==",
+ "license": "MIT",
+ "dependencies": {
+ "motion-utils": "^11.18.1"
+ }
+ },
+ "node_modules/motion-utils": {
+ "version": "11.18.1",
+ "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-11.18.1.tgz",
+ "integrity": "sha512-49Kt+HKjtbJKLtgO/LKj9Ld+6vw9BjH5d9sc40R/kVyH8GLAXgT42M2NnuPcJNuA3s9ZfZBUcwIgpmZWGEE+hA==",
+ "license": "MIT"
+ },
+ "node_modules/mousetrap": {
+ "version": "1.6.5",
+ "resolved": "https://registry.npmjs.org/mousetrap/-/mousetrap-1.6.5.tgz",
+ "integrity": "sha512-QNo4kEepaIBwiT8CDhP98umTetp+JNfQYBWvC1pc6/OAibuXtRcxZ58Qz8skvEHYvURne/7R8T5VoOI7rDsEUA==",
+ "license": "Apache-2.0 WITH LLVM-exception"
+ },
"node_modules/mrmime": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
@@ -14157,8 +15018,7 @@
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
- "dev": true
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
},
"node_modules/multicast-dns": {
"version": "7.2.5",
@@ -14899,7 +15759,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
- "dev": true,
"dependencies": {
"callsites": "^3.0.0"
},
@@ -14917,7 +15776,6 @@
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
"integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
- "dev": true,
"dependencies": {
"@babel/code-frame": "^7.0.0",
"error-ex": "^1.3.1",
@@ -15021,8 +15879,7 @@
"node_modules/path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
- "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
- "dev": true
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
},
"node_modules/path-scurry": {
"version": "1.11.1",
@@ -15065,7 +15922,6 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
- "dev": true,
"engines": {
"node": ">=8"
}
@@ -15110,8 +15966,7 @@
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
- "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
- "dev": true
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
},
"node_modules/picomatch": {
"version": "2.3.1",
@@ -16359,6 +17214,16 @@
"node": ">=0.10.0"
}
},
+ "node_modules/re-resizable": {
+ "version": "6.11.2",
+ "resolved": "https://registry.npmjs.org/re-resizable/-/re-resizable-6.11.2.tgz",
+ "integrity": "sha512-2xI2P3OHs5qw7K0Ud1aLILK6MQxW50TcO+DetD9eIV58j84TqYeHoZcL9H4GXFXXIh7afhH8mv5iUCXII7OW7A==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "^16.13.1 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^16.13.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
"node_modules/react": {
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
@@ -16370,6 +17235,48 @@
"node": ">=0.10.0"
}
},
+ "node_modules/react-colorful": {
+ "version": "5.6.1",
+ "resolved": "https://registry.npmjs.org/react-colorful/-/react-colorful-5.6.1.tgz",
+ "integrity": "sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": ">=16.8.0",
+ "react-dom": ">=16.8.0"
+ }
+ },
+ "node_modules/react-day-picker": {
+ "version": "9.14.0",
+ "resolved": "https://registry.npmjs.org/react-day-picker/-/react-day-picker-9.14.0.tgz",
+ "integrity": "sha512-tBaoDWjPwe0M5pGrum4H0SR6Lyk+BO9oHnp9JbKpGKW2mlraNPgP9BMfsg5pWpwrssARmeqk7YBl2oXutZTaHA==",
+ "license": "MIT",
+ "dependencies": {
+ "@date-fns/tz": "^1.4.1",
+ "@tabby_ai/hijri-converter": "1.0.5",
+ "date-fns": "^4.1.0",
+ "date-fns-jalali": "4.1.0-0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "individual",
+ "url": "https://github.com/sponsors/gpbl"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ }
+ },
+ "node_modules/react-day-picker/node_modules/date-fns": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
+ "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/kossnocorp"
+ }
+ },
"node_modules/react-dom": {
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
@@ -16546,6 +17453,12 @@
"node": ">=8"
}
},
+ "node_modules/redux": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
+ "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==",
+ "license": "MIT"
+ },
"node_modules/reflect.getprototypeof": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
@@ -16658,6 +17571,24 @@
"node": ">=6"
}
},
+ "node_modules/rememo": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/rememo/-/rememo-4.0.2.tgz",
+ "integrity": "sha512-NVfSP9NstE3QPNs/TnegQY0vnJnstKQSpcrsI2kBTB3dB2PkdfKdTa+abbjMIDqpc63fE5LfjLgfMst0ULMFxQ==",
+ "license": "MIT"
+ },
+ "node_modules/remove-accents": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.5.0.tgz",
+ "integrity": "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==",
+ "license": "MIT"
+ },
+ "node_modules/requestidlecallback": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/requestidlecallback/-/requestidlecallback-0.3.0.tgz",
+ "integrity": "sha512-TWHFkT7S9p7IxLC5A1hYmAYQx2Eb9w1skrXmQ+dS1URyvR8tenMLl4lHbqEOUnpEYxNKpkVMXUgknVpBZWXXfQ==",
+ "license": "MIT"
+ },
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
@@ -16709,7 +17640,6 @@
"version": "1.22.10",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
"integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
- "dev": true,
"dependencies": {
"is-core-module": "^2.16.0",
"path-parse": "^1.0.7",
@@ -16929,6 +17859,12 @@
"queue-microtask": "^1.2.2"
}
},
+ "node_modules/rungen": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/rungen/-/rungen-0.3.2.tgz",
+ "integrity": "sha512-zWl10xu2D7zoR8zSC2U6bg5bYF6T/Wk7rxwp8IPaJH7f0Ge21G03kNHVgHR7tyVkSSfAOG0Rqf/Cl38JftSmtw==",
+ "license": "MIT"
+ },
"node_modules/rxjs": {
"version": "7.8.2",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz",
@@ -16961,7 +17897,6 @@
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
- "dev": true,
"funding": [
{
"type": "github",
@@ -17013,8 +17948,7 @@
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
- "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
- "dev": true
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"node_modules/sass": {
"version": "1.92.1",
@@ -18581,6 +19515,12 @@
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
}
},
+ "node_modules/stylis": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
+ "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==",
+ "license": "MIT"
+ },
"node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@@ -18613,7 +19553,6 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
- "dev": true,
"engines": {
"node": ">= 0.4"
},
@@ -18745,6 +19684,15 @@
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"dev": true
},
+ "node_modules/tannin": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/tannin/-/tannin-1.2.0.tgz",
+ "integrity": "sha512-U7GgX/RcSeUETbV7gYgoz8PD7Ni4y95pgIP/Z6ayI3CfhSujwKEBlGFTCRN+Aqnuyf4AN2yHL+L8x+TCGjb9uA==",
+ "license": "MIT",
+ "dependencies": {
+ "@tannin/plural-forms": "^1.1.0"
+ }
+ },
"node_modules/tapable": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.3.tgz",
@@ -19555,6 +20503,24 @@
"requires-port": "^1.0.0"
}
},
+ "node_modules/use-memo-one": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.3.tgz",
+ "integrity": "sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
+ "node_modules/use-sync-external-store": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz",
+ "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -20314,7 +21280,6 @@
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
- "dev": true,
"engines": {
"node": ">= 6"
}
diff --git a/package.json b/package.json
index 65b5d93..d652d9f 100644
--- a/package.json
+++ b/package.json
@@ -13,7 +13,8 @@
"build": "node scripts/update-build-type.js free && node scripts/sync-version.js ${npm_config_suffix} && npx wp-scripts build && npm run build:css && npm run build:frontend-php && npm run optimize-videos && BUILD_TYPE=free npm run package ${npm_config_suffix}",
"build:frontend-php": "node scripts/generate-frontend-php-scripts.mjs production free",
"build:frontend-php:premium": "node scripts/generate-frontend-php-scripts.mjs production premium",
- "build:css": "webpack --config webpack.css.config.js && npm run clean:css-js",
+ "build:css": "webpack --config webpack.css.config.js && npm run build:css:wp-components && npm run clean:css-js",
+ "build:css:wp-components": "node scripts/generate-wp-components-css.mjs && npx sass build/wp-components.scss dist/wp-components.css --load-path=node_modules --no-source-map",
"clean:css-js": "rm -f dist/*.css.js",
"start": "node scripts/update-build-type.js free && concurrently \"wp-scripts start\" \"webpack --config webpack.css.config.js --watch\"",
"lint:js": "wp-scripts lint-js",
@@ -34,28 +35,29 @@
"extends @wordpress/browserslist-config"
],
"devDependencies": {
+ "@node-minify/core": "^8.0.6",
+ "@node-minify/uglify-js": "^8.0.6",
+ "@svgr/webpack": "^8.1.0",
+ "@wordpress/babel-preset-default": "^8.19.0",
"@wordpress/eslint-plugin": "^22.15.0",
"@wordpress/prettier-config": "^4.29.0",
"@wordpress/scripts": "^30.0.0",
"@wordpress/stylelint-config": "^23.21.0",
- "@wordpress/babel-preset-default": "^8.19.0",
- "@svgr/webpack": "^8.1.0",
- "@node-minify/core": "^8.0.6",
- "@node-minify/uglify-js": "^8.0.6",
+ "archiver": "^6.0.1",
"babel-loader": "^9.2.1",
"concurrently": "^8.2.2",
+ "eslint": "^8.57.0",
+ "eslint-plugin-compat": "^6.0.2",
"file-loader": "^6.2.0",
"fluent-ffmpeg": "^2.1.3",
"mini-css-extract-plugin": "^2.7.6",
+ "prettier": "^3.3.3",
"sass": "^1.69.0",
"sass-loader": "^14.0.0",
- "style-loader": "^3.3.3",
- "archiver": "^6.0.1",
- "eslint": "^8.57.0",
- "eslint-plugin-compat": "^6.0.2",
- "prettier": "^3.3.3"
+ "style-loader": "^3.3.3"
},
"dependencies": {
+ "@wordpress/components": "^33.0.0",
"@wordpress/icons": "^10.31.0",
"canvas-confetti": "^1.9.3",
"classnames": "^2.5.1",
diff --git a/scripts/generate-wp-components-css.mjs b/scripts/generate-wp-components-css.mjs
new file mode 100644
index 0000000..16ed570
--- /dev/null
+++ b/scripts/generate-wp-components-css.mjs
@@ -0,0 +1,179 @@
+import fs from 'fs'
+import path from 'path'
+import globPackage from 'glob'
+import babelParser from '@babel/parser'
+
+const globSync = globPackage.sync
+const { parse } = babelParser
+
+const ROOT = process.cwd()
+const SOURCE_GLOB = 'src/**/*.js'
+const OUTPUT_SCSS = path.join( ROOT, 'build', 'wp-components.scss' )
+const OUTPUT_REPORT = path.join( ROOT, 'build', 'wp-components-report.json' )
+
+// Shared styles that are not imported directly, but are needed by some of the
+// component styles that we do include.
+const SHARED_STYLE_ENTRIES = [
+ 'button-group',
+ 'form-toggle',
+ 'tip',
+ 'validated-form-controls',
+]
+
+const EXPORT_TO_STYLE_ENTRY = {
+ BaseControl: 'base-control',
+ Button: 'button',
+ ColorIndicator: 'color-indicator',
+ ColorPicker: 'color-picker',
+ ComboboxControl: 'combobox-control',
+ Dashicon: 'dashicon',
+ Dropdown: 'dropdown',
+ FlexItem: 'flex',
+ MenuGroup: 'menu-group',
+ MenuItem: 'menu-item',
+ MenuItemsChoice: 'menu-items-choice',
+ Modal: 'modal',
+ Notice: 'notice',
+ PanelBody: 'panel',
+ Popover: 'popover',
+ RangeControl: 'range-control',
+ SearchControl: 'search-control',
+ SelectControl: 'select-control',
+ Spinner: 'spinner',
+ TextControl: 'text-control',
+ TextareaControl: 'textarea-control',
+ ToggleControl: 'toggle-control',
+ ToolbarButton: 'toolbar/toolbar-button',
+ ToolbarGroup: 'toolbar/toolbar-group',
+ Tooltip: 'tooltip',
+ __experimentalHStack: 'h-stack',
+ __experimentalNumberControl: 'number-control',
+ __experimentalToggleGroupControl: 'toggle-group-control',
+ __experimentalToggleGroupControlOption: 'toggle-group-control',
+}
+
+const toNamespace = entry => {
+ return 'wpComponents' + entry
+ .replace( /[^a-zA-Z0-9]+(.)/g, ( _, char ) => char.toUpperCase() )
+ .replace( /^(.)/, char => char.toUpperCase() )
+}
+
+const findStyleFile = entry => {
+ return path.join(
+ ROOT,
+ 'node_modules',
+ '@wordpress',
+ 'components',
+ 'src',
+ entry,
+ 'style.scss'
+ )
+}
+
+const files = globSync( SOURCE_GLOB, { cwd: ROOT, absolute: true } )
+const usedExports = new Set()
+
+// Scan every JS module in the codebase and collect the named exports imported
+// from @wordpress/components so the generated stylesheet only includes the
+// component styles we actually reference.
+for ( const file of files ) {
+ const source = fs.readFileSync( file, 'utf8' )
+ const ast = parse( source, {
+ sourceType: 'module',
+ plugins: [ 'jsx' ],
+ } )
+
+ for ( const node of ast.program.body ) {
+ if ( node.type !== 'ImportDeclaration' || node.source.value !== '@wordpress/components' ) {
+ continue
+ }
+
+ for ( const specifier of node.specifiers ) {
+ if ( specifier.type === 'ImportSpecifier' ) {
+ usedExports.add( specifier.imported.name )
+ }
+ }
+ }
+}
+
+const styleEntries = new Set()
+const missingExports = []
+
+// Map every discovered component import to its source Sass entry when one is
+// available in the package. Components that do not ship a standalone style.scss
+// are reported so we can keep track of the remaining gaps.
+for ( const componentExport of [ ...usedExports ].sort() ) {
+ const styleEntry = EXPORT_TO_STYLE_ENTRY[ componentExport ]
+ if ( ! styleEntry ) {
+ missingExports.push( {
+ export: componentExport,
+ reason: 'No style entry mapping defined.',
+ } )
+ continue
+ }
+
+ if ( ! fs.existsSync( findStyleFile( styleEntry ) ) ) {
+ missingExports.push( {
+ export: componentExport,
+ styleEntry,
+ reason: 'Mapped style entry has no style.scss file.',
+ } )
+ continue
+ }
+
+ styleEntries.add( styleEntry )
+}
+
+// Some imported components depend on shared styles that are not imported
+// directly, so add them explicitly to keep the reduced bundle stable.
+for ( const sharedEntry of SHARED_STYLE_ENTRIES ) {
+ if ( fs.existsSync( findStyleFile( sharedEntry ) ) ) {
+ styleEntries.add( sharedEntry )
+ }
+}
+
+fs.mkdirSync( path.dirname( OUTPUT_SCSS ), { recursive: true } )
+
+// Generate a thin Sass entrypoint that re-exports the discovered component
+// styles. Sass requires each @use to have a unique namespace, even when we only
+// care about the emitted CSS side effects.
+const scssContents = [
+ '// Auto-generated by scripts/generate-wp-components-css.mjs',
+ '// Reduced bundle for @wordpress/components styles used in this codebase.',
+ '// Do not edit manually.',
+ '',
+ '@use "@wordpress/base-styles/mixins" as *;',
+ '',
+ ...Array.from( styleEntries )
+ .sort()
+ .map( entry => `@use "@wordpress/components/src/${ entry }/style" as ${ toNamespace( entry ) };` ),
+ '',
+].join( '\n' )
+
+fs.writeFileSync( OUTPUT_SCSS, scssContents )
+
+const report = {
+ generatedAt: new Date().toISOString(),
+ scannedPattern: SOURCE_GLOB,
+ sharedStyleEntries: SHARED_STYLE_ENTRIES,
+ usedExports: Array.from( usedExports ).sort(),
+ styleEntries: Array.from( styleEntries ).sort(),
+ missingExports,
+ outputScss: path.relative( ROOT, OUTPUT_SCSS ),
+}
+
+fs.writeFileSync( OUTPUT_REPORT, JSON.stringify( report, null, 2 ) + '\n' )
+
+process.stdout.write(
+ JSON.stringify(
+ {
+ outputScss: path.relative( ROOT, OUTPUT_SCSS ),
+ outputReport: path.relative( ROOT, OUTPUT_REPORT ),
+ usedExports: report.usedExports.length,
+ styleEntries: report.styleEntries.length,
+ missingExports: missingExports.length,
+ },
+ null,
+ 2
+ ) + '\n'
+)
diff --git a/src/editor/editor.php b/src/editor/editor.php
index 75224fd..32ad19c 100644
--- a/src/editor/editor.php
+++ b/src/editor/editor.php
@@ -21,8 +21,7 @@ function __construct() {
add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_gutenberg_editor' ) );
add_action( 'enqueue_block_assets', array( $this, 'enqueue_assets' ) );
}
-
-+ add_action( 'elementor/editor/after_enqueue_scripts', array( $this, 'enqueue_elementor_editor' ) );
+ add_action( 'elementor/editor/after_enqueue_scripts', array( $this, 'enqueue_elementor_editor' ) );
}
/**
@@ -40,10 +39,12 @@ public function enqueue_gutenberg_editor() {
* @return void
*/
public function enqueue_elementor_editor() {
- // Loads the core WordPress editor styles needed by the elementor editor UI.
- if ( wp_style_is( 'wp-components', 'registered' ) ) {
- wp_enqueue_style( 'wp-components' );
- }
+ wp_enqueue_style(
+ 'interact-editor-wp-components',
+ plugins_url( 'dist/wp-components.css', INTERACT_FILE ),
+ array(),
+ INTERACT_VERSION
+ );
$this->enqueue_editor( 'elementor' );
}
From 0441e27af016dab09fc9e4502238ccb7f2da3c1c Mon Sep 17 00:00:00 2001
From: Alquen Sarmiento
Date: Tue, 12 May 2026 10:54:33 +0800
Subject: [PATCH 11/14] fix: enqueue the wp-components for elementor
---
package.json | 3 +-
scripts/generate-wp-components-css.mjs | 179 -------------------------
src/editor/editor.php | 9 +-
3 files changed, 4 insertions(+), 187 deletions(-)
delete mode 100644 scripts/generate-wp-components-css.mjs
diff --git a/package.json b/package.json
index d652d9f..ae30ca6 100644
--- a/package.json
+++ b/package.json
@@ -13,8 +13,7 @@
"build": "node scripts/update-build-type.js free && node scripts/sync-version.js ${npm_config_suffix} && npx wp-scripts build && npm run build:css && npm run build:frontend-php && npm run optimize-videos && BUILD_TYPE=free npm run package ${npm_config_suffix}",
"build:frontend-php": "node scripts/generate-frontend-php-scripts.mjs production free",
"build:frontend-php:premium": "node scripts/generate-frontend-php-scripts.mjs production premium",
- "build:css": "webpack --config webpack.css.config.js && npm run build:css:wp-components && npm run clean:css-js",
- "build:css:wp-components": "node scripts/generate-wp-components-css.mjs && npx sass build/wp-components.scss dist/wp-components.css --load-path=node_modules --no-source-map",
+ "build:css": "webpack --config webpack.css.config.js && npm run clean:css-js",
"clean:css-js": "rm -f dist/*.css.js",
"start": "node scripts/update-build-type.js free && concurrently \"wp-scripts start\" \"webpack --config webpack.css.config.js --watch\"",
"lint:js": "wp-scripts lint-js",
diff --git a/scripts/generate-wp-components-css.mjs b/scripts/generate-wp-components-css.mjs
deleted file mode 100644
index 16ed570..0000000
--- a/scripts/generate-wp-components-css.mjs
+++ /dev/null
@@ -1,179 +0,0 @@
-import fs from 'fs'
-import path from 'path'
-import globPackage from 'glob'
-import babelParser from '@babel/parser'
-
-const globSync = globPackage.sync
-const { parse } = babelParser
-
-const ROOT = process.cwd()
-const SOURCE_GLOB = 'src/**/*.js'
-const OUTPUT_SCSS = path.join( ROOT, 'build', 'wp-components.scss' )
-const OUTPUT_REPORT = path.join( ROOT, 'build', 'wp-components-report.json' )
-
-// Shared styles that are not imported directly, but are needed by some of the
-// component styles that we do include.
-const SHARED_STYLE_ENTRIES = [
- 'button-group',
- 'form-toggle',
- 'tip',
- 'validated-form-controls',
-]
-
-const EXPORT_TO_STYLE_ENTRY = {
- BaseControl: 'base-control',
- Button: 'button',
- ColorIndicator: 'color-indicator',
- ColorPicker: 'color-picker',
- ComboboxControl: 'combobox-control',
- Dashicon: 'dashicon',
- Dropdown: 'dropdown',
- FlexItem: 'flex',
- MenuGroup: 'menu-group',
- MenuItem: 'menu-item',
- MenuItemsChoice: 'menu-items-choice',
- Modal: 'modal',
- Notice: 'notice',
- PanelBody: 'panel',
- Popover: 'popover',
- RangeControl: 'range-control',
- SearchControl: 'search-control',
- SelectControl: 'select-control',
- Spinner: 'spinner',
- TextControl: 'text-control',
- TextareaControl: 'textarea-control',
- ToggleControl: 'toggle-control',
- ToolbarButton: 'toolbar/toolbar-button',
- ToolbarGroup: 'toolbar/toolbar-group',
- Tooltip: 'tooltip',
- __experimentalHStack: 'h-stack',
- __experimentalNumberControl: 'number-control',
- __experimentalToggleGroupControl: 'toggle-group-control',
- __experimentalToggleGroupControlOption: 'toggle-group-control',
-}
-
-const toNamespace = entry => {
- return 'wpComponents' + entry
- .replace( /[^a-zA-Z0-9]+(.)/g, ( _, char ) => char.toUpperCase() )
- .replace( /^(.)/, char => char.toUpperCase() )
-}
-
-const findStyleFile = entry => {
- return path.join(
- ROOT,
- 'node_modules',
- '@wordpress',
- 'components',
- 'src',
- entry,
- 'style.scss'
- )
-}
-
-const files = globSync( SOURCE_GLOB, { cwd: ROOT, absolute: true } )
-const usedExports = new Set()
-
-// Scan every JS module in the codebase and collect the named exports imported
-// from @wordpress/components so the generated stylesheet only includes the
-// component styles we actually reference.
-for ( const file of files ) {
- const source = fs.readFileSync( file, 'utf8' )
- const ast = parse( source, {
- sourceType: 'module',
- plugins: [ 'jsx' ],
- } )
-
- for ( const node of ast.program.body ) {
- if ( node.type !== 'ImportDeclaration' || node.source.value !== '@wordpress/components' ) {
- continue
- }
-
- for ( const specifier of node.specifiers ) {
- if ( specifier.type === 'ImportSpecifier' ) {
- usedExports.add( specifier.imported.name )
- }
- }
- }
-}
-
-const styleEntries = new Set()
-const missingExports = []
-
-// Map every discovered component import to its source Sass entry when one is
-// available in the package. Components that do not ship a standalone style.scss
-// are reported so we can keep track of the remaining gaps.
-for ( const componentExport of [ ...usedExports ].sort() ) {
- const styleEntry = EXPORT_TO_STYLE_ENTRY[ componentExport ]
- if ( ! styleEntry ) {
- missingExports.push( {
- export: componentExport,
- reason: 'No style entry mapping defined.',
- } )
- continue
- }
-
- if ( ! fs.existsSync( findStyleFile( styleEntry ) ) ) {
- missingExports.push( {
- export: componentExport,
- styleEntry,
- reason: 'Mapped style entry has no style.scss file.',
- } )
- continue
- }
-
- styleEntries.add( styleEntry )
-}
-
-// Some imported components depend on shared styles that are not imported
-// directly, so add them explicitly to keep the reduced bundle stable.
-for ( const sharedEntry of SHARED_STYLE_ENTRIES ) {
- if ( fs.existsSync( findStyleFile( sharedEntry ) ) ) {
- styleEntries.add( sharedEntry )
- }
-}
-
-fs.mkdirSync( path.dirname( OUTPUT_SCSS ), { recursive: true } )
-
-// Generate a thin Sass entrypoint that re-exports the discovered component
-// styles. Sass requires each @use to have a unique namespace, even when we only
-// care about the emitted CSS side effects.
-const scssContents = [
- '// Auto-generated by scripts/generate-wp-components-css.mjs',
- '// Reduced bundle for @wordpress/components styles used in this codebase.',
- '// Do not edit manually.',
- '',
- '@use "@wordpress/base-styles/mixins" as *;',
- '',
- ...Array.from( styleEntries )
- .sort()
- .map( entry => `@use "@wordpress/components/src/${ entry }/style" as ${ toNamespace( entry ) };` ),
- '',
-].join( '\n' )
-
-fs.writeFileSync( OUTPUT_SCSS, scssContents )
-
-const report = {
- generatedAt: new Date().toISOString(),
- scannedPattern: SOURCE_GLOB,
- sharedStyleEntries: SHARED_STYLE_ENTRIES,
- usedExports: Array.from( usedExports ).sort(),
- styleEntries: Array.from( styleEntries ).sort(),
- missingExports,
- outputScss: path.relative( ROOT, OUTPUT_SCSS ),
-}
-
-fs.writeFileSync( OUTPUT_REPORT, JSON.stringify( report, null, 2 ) + '\n' )
-
-process.stdout.write(
- JSON.stringify(
- {
- outputScss: path.relative( ROOT, OUTPUT_SCSS ),
- outputReport: path.relative( ROOT, OUTPUT_REPORT ),
- usedExports: report.usedExports.length,
- styleEntries: report.styleEntries.length,
- missingExports: missingExports.length,
- },
- null,
- 2
- ) + '\n'
-)
diff --git a/src/editor/editor.php b/src/editor/editor.php
index 32ad19c..1664f3a 100644
--- a/src/editor/editor.php
+++ b/src/editor/editor.php
@@ -39,12 +39,9 @@ public function enqueue_gutenberg_editor() {
* @return void
*/
public function enqueue_elementor_editor() {
- wp_enqueue_style(
- 'interact-editor-wp-components',
- plugins_url( 'dist/wp-components.css', INTERACT_FILE ),
- array(),
- INTERACT_VERSION
- );
+ if ( wp_style_is( 'wp-components', 'registered' ) ) {
+ wp_enqueue_style( 'wp-components' );
+ }
$this->enqueue_editor( 'elementor' );
}
From 084bebed23c242e303872c227403817a7d9caba7 Mon Sep 17 00:00:00 2001
From: Alquen Sarmiento
Date: Tue, 12 May 2026 11:16:38 +0800
Subject: [PATCH 12/14] fix: fix drag and drop in action timeline
---
src/editor/components/timeline/index.js | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/editor/components/timeline/index.js b/src/editor/components/timeline/index.js
index 0c04d38..ab84b81 100644
--- a/src/editor/components/timeline/index.js
+++ b/src/editor/components/timeline/index.js
@@ -1165,13 +1165,15 @@ const ActionDropGap = props => {
const [ isHighlighted, setIsHighlighted ] = useState( false )
- const onDragOverHandler = () => {
+ const onDragOverHandler = ev => {
+ ev.preventDefault()
setIsHighlighted( true )
}
const onDragLeaveHandler = () => {
setIsHighlighted( false )
}
const onDragDropHandler = ev => {
+ ev.preventDefault()
const actionKeyDrop = ev.dataTransfer.getData( 'text/plain' )
onDrop( actionKeyDrop )
setIsHighlighted( false )
@@ -1270,6 +1272,7 @@ const ActionItem = props => {
}
const onDragOverHandler = ev => {
+ ev.preventDefault()
const rect = dragItemRef.current.getBoundingClientRect()
if ( ev.clientY < rect.top + ( rect.height / 2 ) ) {
setHighlightLocation( 'top' )
@@ -1283,6 +1286,7 @@ const ActionItem = props => {
}
const onDragDropHandler = ev => {
+ ev.preventDefault()
const actionKeyDrop = ev.dataTransfer.getData( 'text/plain' )
onDrop( actionKeyDrop, highlightLocation )
setHighlightLocation( null )
From a84b1b0c5515817b39fc4d06c49d8513e89a63d5 Mon Sep 17 00:00:00 2001
From: Alquen Sarmiento
Date: Tue, 12 May 2026 11:41:58 +0800
Subject: [PATCH 13/14] fix: add default styles to be used in elementor panel
---
src/editor/editor.scss | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/src/editor/editor.scss b/src/editor/editor.scss
index 37f82b3..0d3ad7c 100644
--- a/src/editor/editor.scss
+++ b/src/editor/editor.scss
@@ -165,6 +165,15 @@
z-index: 99999;
display: flex;
flex-direction: column;
+
+ h2 {
+ font-size: 13px;
+ margin: 1.33em;
+ }
+
+ p {
+ margin: 1em 0;
+ }
}
.interact-elementor-panel.is-open {
@@ -196,6 +205,20 @@
min-height: 100%;
}
+.interact-popover {
+ color: #3c434a;
+ line-height: 1.4em;
+
+ h2 {
+ font-size: 13px;
+ margin: 1.33em;
+ }
+
+ p {
+ margin: 1em 0;
+ }
+}
+
/* Wordpress 7.0 compatibility */
.interact-sidebar, .interact-popover {
From e30c49bf6f9c9008d968040cee5fcfa3821a13bd Mon Sep 17 00:00:00 2001
From: Alquen Sarmiento
Date: Wed, 13 May 2026 13:39:57 +0800
Subject: [PATCH 14/14] fix: add script for scoping wp-components
---
package.json | 5 +-
scripts/build-scoped-wp-components-css.mjs | 101 +++++++++++++++++++++
src/editor/editor.php | 9 +-
3 files changed, 110 insertions(+), 5 deletions(-)
create mode 100644 scripts/build-scoped-wp-components-css.mjs
diff --git a/package.json b/package.json
index ae30ca6..c7e050a 100644
--- a/package.json
+++ b/package.json
@@ -13,7 +13,8 @@
"build": "node scripts/update-build-type.js free && node scripts/sync-version.js ${npm_config_suffix} && npx wp-scripts build && npm run build:css && npm run build:frontend-php && npm run optimize-videos && BUILD_TYPE=free npm run package ${npm_config_suffix}",
"build:frontend-php": "node scripts/generate-frontend-php-scripts.mjs production free",
"build:frontend-php:premium": "node scripts/generate-frontend-php-scripts.mjs production premium",
- "build:css": "webpack --config webpack.css.config.js && npm run clean:css-js",
+ "build:css": "webpack --config webpack.css.config.js && npm run build:css:wp-components-scoped && npm run clean:css-js",
+ "build:css:wp-components-scoped": "node scripts/build-scoped-wp-components-css.mjs",
"clean:css-js": "rm -f dist/*.css.js",
"start": "node scripts/update-build-type.js free && concurrently \"wp-scripts start\" \"webpack --config webpack.css.config.js --watch\"",
"lint:js": "wp-scripts lint-js",
@@ -25,7 +26,7 @@
"optimize-videos": "node scripts/optimize-videos.js",
"package": "node scripts/package.js",
"sync-version": "node scripts/sync-version.js",
- "build:premium": "node scripts/update-build-type.js premium && node scripts/sync-version.js ${npm_config_suffix} && npm run clean:css-js && npx wp-scripts build --config pro__premium_only/webpack.config.js && npx webpack --config pro__premium_only/webpack.css.config.js && npm run build:frontend-php:premium && npm run optimize-videos && BUILD_TYPE=premium npm run package ${npm_config_suffix}",
+ "build:premium": "node scripts/update-build-type.js premium && node scripts/sync-version.js ${npm_config_suffix} && npm run clean:css-js && npx wp-scripts build --config pro__premium_only/webpack.config.js && npx webpack --config pro__premium_only/webpack.css.config.js && npm run build:css:wp-components-scoped && npm run build:frontend-php:premium && npm run optimize-videos && BUILD_TYPE=premium npm run package ${npm_config_suffix}",
"start:premium": "cd pro__premium_only && npm run start",
"lint:premium": "cd pro__premium_only && npm run lint",
"lint:premium:fix": "cd pro__premium_only && npm run lint:fix"
diff --git a/scripts/build-scoped-wp-components-css.mjs b/scripts/build-scoped-wp-components-css.mjs
new file mode 100644
index 0000000..a208c6d
--- /dev/null
+++ b/scripts/build-scoped-wp-components-css.mjs
@@ -0,0 +1,101 @@
+import fs from 'fs'
+import path from 'path'
+import postcss from 'postcss'
+
+const ROOT = process.cwd()
+const INPUT_CSS = path.join(
+ ROOT,
+ 'node_modules',
+ '@wordpress',
+ 'components',
+ 'build-style',
+ 'style.css'
+)
+const OUTPUT_CSS = path.join( ROOT, 'dist', 'wp-components-scoped.css' )
+
+const COMPONENT_SCOPES = [
+ '#interact-elementor-root',
+ '.interact-popover',
+]
+const BODY_SCOPE = 'body.interact-elementor-editor'
+const PORTAL_PATTERNS = [
+ '.components-modal',
+ '.components-snackbar',
+ '.components-tooltip',
+ '.components-guide',
+]
+
+// Wrap scope selectors in :where() so scoping does not increase specificity.
+const wrapScope = scope => `:where(${ scope })`
+
+// Some WordPress component UI is rendered in portals outside our sidebar root.
+const isPortalSelector = selector => {
+ return PORTAL_PATTERNS.some( pattern => selector.includes( pattern ) )
+}
+
+// Check if a rule is inside a keyframes block, which should not be scoped.
+const isInsideKeyframes = rule => {
+ let current = rule.parent
+ while ( current ) {
+ if ( current.type === 'atrule' && current.name.includes( 'keyframes' ) ) {
+ return true
+ }
+ current = current.parent
+ }
+ return false
+}
+
+// Scope each selector to the Interactions Elementor UI while keeping popovers
+// and other portal-based components reachable outside the sidebar root.
+const scopeRootSelector = selector => {
+ if ( selector.startsWith( ':root' ) ) {
+ return [ ...COMPONENT_SCOPES.map( wrapScope ), wrapScope( BODY_SCOPE ) ]
+ }
+
+ if ( selector.startsWith( 'body' ) ) {
+ return [ selector.replace( /^body\b/, wrapScope( BODY_SCOPE ) ) ]
+ }
+
+ if ( selector.startsWith( 'html' ) ) {
+ return [ `${ wrapScope( BODY_SCOPE ) } ${ selector }` ]
+ }
+
+ if ( isPortalSelector( selector ) ) {
+ return [ `${ wrapScope( BODY_SCOPE ) } ${ selector }` ]
+ }
+
+ if ( /(^|[\s>+~])\.components-popover(?![a-zA-Z0-9_-])/.test( selector ) ) {
+ return [ selector.replace( /(^|[\s>+~])\.components-popover(?![a-zA-Z0-9_-])/g, `$1${ wrapScope( '.interact-popover.components-popover' ) }` ) ]
+ }
+
+ return COMPONENT_SCOPES.map( scope => `${ wrapScope( scope ) } ${ selector }` )
+}
+
+const css = fs.readFileSync( INPUT_CSS, 'utf8' )
+const root = postcss.parse( css )
+
+root.walkRules( rule => {
+ if ( ! Array.isArray( rule.selectors ) ) {
+ return
+ }
+
+ if ( isInsideKeyframes( rule ) ) {
+ return
+ }
+
+ rule.selectors = rule.selectors.flatMap( selector => scopeRootSelector( selector ) )
+} )
+
+fs.mkdirSync( path.dirname( OUTPUT_CSS ), { recursive: true } )
+fs.writeFileSync( OUTPUT_CSS, root.toString() )
+
+process.stdout.write(
+ JSON.stringify(
+ {
+ input: path.relative( ROOT, INPUT_CSS ),
+ output: path.relative( ROOT, OUTPUT_CSS ),
+ },
+ null,
+ 2
+ ) + '\n'
+)
diff --git a/src/editor/editor.php b/src/editor/editor.php
index 1664f3a..a505bd6 100644
--- a/src/editor/editor.php
+++ b/src/editor/editor.php
@@ -39,9 +39,12 @@ public function enqueue_gutenberg_editor() {
* @return void
*/
public function enqueue_elementor_editor() {
- if ( wp_style_is( 'wp-components', 'registered' ) ) {
- wp_enqueue_style( 'wp-components' );
- }
+ wp_enqueue_style(
+ 'interact-editor-wp-components-scoped',
+ plugins_url( 'dist/wp-components-scoped.css', INTERACT_FILE ),
+ array(),
+ INTERACT_VERSION
+ );
$this->enqueue_editor( 'elementor' );
}