Skip to content
This repository has been archived by the owner on Feb 23, 2024. It is now read-only.

Experiment: Add a simple price filter block with directives #8221

Closed
wants to merge 68 commits into from
Closed
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
5807789
Add Interactivity API scripts
SantosGuillamot Dec 21, 2022
53dc29d
Enqueue scripts if Products exists for testing
SantosGuillamot Dec 21, 2022
f316faa
Test client-side transitions
SantosGuillamot Dec 22, 2022
640df94
Remove script enqueue
SantosGuillamot Dec 23, 2022
915270d
Remove hpq dependency
SantosGuillamot Dec 23, 2022
f9f8c6e
Update Interactivity scripts to latest version
SantosGuillamot Dec 23, 2022
a69fc12
Merge branch 'trunk' into experiment/interactivity-api
luisherranz Jan 3, 2023
8c467ec
Remove interactivity scripts from core entries
DAreRodz Jan 12, 2023
6c9f92b
Create webpack config for interactivity api
DAreRodz Jan 12, 2023
419aff6
Enqueue the directives runtime
DAreRodz Jan 12, 2023
2a215b0
Updated wp directives code
DAreRodz Jan 12, 2023
e6bafe5
Use a filter to enque the directives runtime
DAreRodz Jan 12, 2023
7c6cbee
Remove base-interactivity alias for now
DAreRodz Jan 13, 2023
655b125
Add path for modules inside base-interactivity
DAreRodz Jan 13, 2023
20bae4c
Create Simple Price Filter block
DAreRodz Jan 16, 2023
3717d0f
Add ssr for min and max price
DAreRodz Jan 17, 2023
6b69132
Add logic to reset button
DAreRodz Jan 17, 2023
748a1e9
Enable directives and client transitions
DAreRodz Jan 17, 2023
b9519c6
Add a functional range bar
DAreRodz Jan 18, 2023
1d135d4
Revert "Remove base-interactivity alias for now"
DAreRodz Jan 18, 2023
2688b49
Fix issues with range values
DAreRodz Jan 18, 2023
e95b46a
Slightly improve appearance
DAreRodz Jan 18, 2023
7f52c28
Replace Infinity with maxRange
DAreRodz Jan 18, 2023
18c7436
Remove unnecessary filter and enqueue
DAreRodz Jan 18, 2023
cfc1718
Merge branch 'add/interactivity-api' into try/add-simple-price-filter…
luisherranz Jan 18, 2023
e397551
Update router code
DAreRodz Jan 20, 2023
5823192
Update Interactivity location and alias
DAreRodz Jan 20, 2023
c54d1dc
Merge branch 'add/interactivity-api' into try/add-simple-price-filter…
DAreRodz Jan 20, 2023
035ba3c
Update imports in simple price filter
DAreRodz Jan 20, 2023
5067e7a
Update Interactivity API
DAreRodz Jan 27, 2023
92d0d9d
Change `wp` prefixes to `woo`
DAreRodz Jan 27, 2023
0199ade
Use `woo` prefix for the directives runtime bundle
DAreRodz Jan 27, 2023
321152d
Merge branch 'trunk' into try/add-simple-price-filter-block
DAreRodz Jan 27, 2023
7485dba
Merge branch 'update/interactivity-api-prefixes' into try/add-simple-…
DAreRodz Jan 27, 2023
08e715f
Update cst meta tag
DAreRodz Jan 27, 2023
70bc51b
Use `data-woo-` prefixed directives
DAreRodz Jan 27, 2023
ceedc72
Merge branch 'trunk' into update/interactivity-api-prefixes
DAreRodz Feb 10, 2023
6277a3c
Update Interactivity API runtime
DAreRodz Feb 10, 2023
423c858
Hardcode php from interactivity API
DAreRodz Feb 14, 2023
9af6478
Temporarily add gutenberg plugin as dependency
DAreRodz Feb 14, 2023
36bd2c5
Merge branch 'trunk' into update/interactivity-api-store-ssr
DAreRodz Feb 20, 2023
78b7db2
Exclude Interactivity API files from phpcs checks
DAreRodz Feb 20, 2023
92f5a85
Update Interactivity API js files
DAreRodz Feb 20, 2023
0c11440
Update Interactivity API php files
DAreRodz Feb 21, 2023
97f39c1
Remove gutenberg from wp-env plugins
DAreRodz Feb 21, 2023
06e21f3
Merge branch 'update/interactivity-api-store-ssr' into try/add-simple…
DAreRodz Feb 21, 2023
fb4a9df
Enable client side navigation
DAreRodz Feb 21, 2023
25693ea
Fix store dependency in simple-price-filter
DAreRodz Feb 14, 2023
cd95d99
Remove SSRed state
DAreRodz Feb 14, 2023
a6dc2e2
Fix registered runtime paths
DAreRodz Feb 14, 2023
28dd2ad
Fix registered runtime paths
DAreRodz Feb 14, 2023
042100c
Init SimplePriceFilter store in php
DAreRodz Feb 14, 2023
5453d6b
Fix prefixes when getting attributes in directives
DAreRodz Feb 14, 2023
00cae9f
Merge branch 'update/interactivity-api-store-ssr' into try/add-simple…
DAreRodz Feb 21, 2023
72bd2d9
Remove unnecessary state
DAreRodz Feb 14, 2023
9d77a52
Add missing initial state in Simple Price Filter
DAreRodz Feb 14, 2023
78b0882
Remove bound attributes
DAreRodz Feb 14, 2023
81cf10e
Add gutenberg plugin to wp-env
DAreRodz Feb 21, 2023
1874a4c
Fix directive prefix in constants
DAreRodz Feb 21, 2023
02f25fd
Merge branch 'update/interactivity-api-store-ssr' into try/add-simple…
DAreRodz Feb 21, 2023
e3df31b
Merge branch 'trunk' into update/interactivity-api-store-ssr
DAreRodz Feb 21, 2023
0cd9a23
Avoid a Fatal error when importing `wp-html`
DAreRodz Feb 21, 2023
64c8aa7
Merge branch 'update/interactivity-api-store-ssr' into try/add-simple…
DAreRodz Feb 21, 2023
68b0a66
Remove TODO comments from Interactivity API files
DAreRodz Feb 21, 2023
d3fc1cb
Merge branch 'update/interactivity-api-store-ssr' into try/add-simple…
DAreRodz Feb 21, 2023
97c47a3
Merge branch 'trunk' into try/add-simple-price-filter-block
DAreRodz Feb 28, 2023
c1b8397
Use `woo_directives_store`
DAreRodz Feb 28, 2023
6653daa
Remove unnecessary alias from webpack helpers
DAreRodz Feb 28, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions assets/js/base/interactivity/components.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* External dependencies
*/
import { useMemo, useContext } from 'preact/hooks';
import { deepSignal } from 'deepsignal';

/**
* Internal dependencies
*/
import { component } from './hooks';

export default () => {
const WpContext = ( { children, data, context: { Provider } } ) => {
const signals = useMemo(
() => deepSignal( JSON.parse( data ) ),
[ data ]
);
return <Provider value={ signals }>{ children }</Provider>;
};
component( 'wp-context', WpContext );

const WpShow = ( { children, when, evaluate, context } ) => {
const contextValue = useContext( context );
if ( evaluate( when, { context: contextValue } ) ) {
return children;
}
return <template>{ children }</template>;
};
component( 'wp-show', WpShow );
};
158 changes: 158 additions & 0 deletions assets/js/base/interactivity/directives.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/**
* External dependencies
*/

import { useContext, useMemo, useEffect } from 'preact/hooks';
import { useSignalEffect } from '@preact/signals';
import { deepSignal } from 'deepsignal';

/**
* Internal dependencies
*/
import { directive } from './hooks';
import { prefetch, navigate, hasClientSideTransitions } from './router';

// Until useSignalEffects is fixed:
// https://github.com/preactjs/signals/issues/228
const raf = window.requestAnimationFrame;
const tick = () => new Promise( ( r ) => raf( () => raf( r ) ) );

// Check if current page has client-side transitions enabled.
const clientSideTransitions = hasClientSideTransitions( document.head );

export default () => {
// wp-context
directive(
'context',
( {
directives: {
context: { default: context },
},
props: { children },
context: { Provider },
} ) => {
const signals = useMemo( () => deepSignal( context ), [ context ] );
return <Provider value={ signals }>{ children }</Provider>;
}
);

// wp-effect:[name]
directive( 'effect', ( { directives: { effect }, context, evaluate } ) => {
const contextValue = useContext( context );
Object.values( effect ).forEach( ( path ) => {
useSignalEffect( () => {
evaluate( path, { context: contextValue } );
} );
} );
} );

// wp-on:[event]
directive( 'on', ( { directives: { on }, element, evaluate, context } ) => {
const contextValue = useContext( context );
Object.entries( on ).forEach( ( [ name, path ] ) => {
element.props[ `on${ name }` ] = ( event ) => {
evaluate( path, { event, context: contextValue } );
};
} );
} );

// wp-class:[classname]
directive(
'class',
( {
directives: { class: className },
element,
evaluate,
context,
} ) => {
const contextValue = useContext( context );
Object.keys( className )
.filter( ( n ) => n !== 'default' )
.forEach( ( name ) => {
const result = evaluate( className[ name ], {
className: name,
context: contextValue,
} );
if ( ! result )
element.props.class = element.props.class
.replace(
new RegExp( `(^|\\s)${ name }(\\s|$)`, 'g' ),
' '
)
.trim();
else if (
! new RegExp( `(^|\\s)${ name }(\\s|$)` ).test(
element.props.class
)
)
element.props.class += ` ${ name }`;

useEffect( () => {
// This seems necessary because Preact doesn't change the class names
// on the hydration, so we have to do it manually. It doesn't need
// deps because it only needs to do it the first time.
if ( ! result ) {
element.ref.current.classList.remove( name );
} else {
element.ref.current.classList.add( name );
}
}, [] );
} );
}
);

// wp-bind:[attribute]
directive(
'bind',
( { directives: { bind }, element, context, evaluate } ) => {
const contextValue = useContext( context );
Object.entries( bind )
.filter( ( n ) => n !== 'default' )
.forEach( ( [ attribute, path ] ) => {
element.props[ attribute ] = evaluate( path, {
context: contextValue,
} );
} );
}
);

// wp-link
directive(
'link',
( {
directives: {
link: { default: link },
},
props: { href },
element,
} ) => {
useEffect( () => {
// Prefetch the page if it is in the directive options.
if ( clientSideTransitions && link?.prefetch ) {
prefetch( href );
}
} );

// Don't do anything if it's falsy.
if ( clientSideTransitions && link !== false ) {
element.props.onclick = async ( event ) => {
event.preventDefault();

// Fetch the page (or return it from cache).
await navigate( href );

// Update the scroll, depending on the option. True by default.
if ( link?.scroll === 'smooth' ) {
window.scrollTo( {
top: 0,
left: 0,
behavior: 'smooth',
} );
} else if ( link?.scroll !== false ) {
window.scrollTo( 0, 0 );
}
};
}
}
);
};
88 changes: 88 additions & 0 deletions assets/js/base/interactivity/hooks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/**
* External dependencies
*/
import { h, options, createContext } from 'preact';
import { useRef } from 'preact/hooks';

/**
* Internal dependencies
*/
import { store } from './wpx';

// Main context.
const context = createContext( {} );

// WordPress Directives.
const directives = {};
export const directive = ( name, cb ) => {
directives[ name ] = cb;
};

// WordPress Components.
const components = {};
export const component = ( name, Comp ) => {
components[ name ] = Comp;
};

// Resolve the path to some property of the wpx object.
const resolve = ( path, context ) => {
let current = { ...store, context };
path.split( '.' ).forEach( ( p ) => ( current = current[ p ] ) );
return current;
};

// Generate the evaluate function.
const getEvaluate =
( { ref } = {} ) =>
( path, extraArgs = {} ) => {
const value = resolve( path, extraArgs.context );
return typeof value === 'function'
? value( {
state: store.state,
...( ref !== undefined ? { ref } : {} ),
...extraArgs,
} )
: value;
};

// Directive wrapper.
const WpDirective = ( { type, wp, props: originalProps } ) => {
const ref = useRef( null );
const element = h( type, { ...originalProps, ref, _wrapped: true } );
const props = { ...originalProps, children: element };
const evaluate = getEvaluate( { ref: ref.current } );
const directiveArgs = { directives: wp, props, element, context, evaluate };

for ( const d in wp ) {
const wrapper = directives[ d ]?.( directiveArgs );
if ( wrapper !== undefined ) props.children = wrapper;
}

return props.children;
};

// Preact Options Hook called each time a vnode is created.
const old = options.vnode;
options.vnode = ( vnode ) => {
const type = vnode.type;
const wp = vnode.props.wp;

if ( typeof type === 'string' && type.startsWith( 'wp-' ) ) {
vnode.props.children = h(
components[ type ],
{ ...vnode.props, context, evaluate: getEvaluate() },
vnode.props.children
);
} else if ( wp ) {
const props = vnode.props;
delete props.wp;
if ( ! props._wrapped ) {
vnode.props = { type: vnode.type, wp, props };
vnode.type = WpDirective;
} else {
delete props._wrapped;
}
}

if ( old ) old( vnode );
};
16 changes: 16 additions & 0 deletions assets/js/base/interactivity/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* Internal dependencies
*/
import registerDirectives from './directives';
import registerComponents from './components';
import { init } from './router';

/**
* Initialize the initial vDOM.
*/
document.addEventListener( 'DOMContentLoaded', async () => {
registerDirectives();
registerComponents();
await init();
console.log( 'hydrated!' );
} );
Loading