@wordpress/interactivity
Advanced tools
Comparing version 6.9.0 to 6.10.0
@@ -0,1 +1,2 @@ | ||
/* wp:polyfill */ | ||
// eslint-disable-next-line eslint-comments/disable-enable-pair | ||
@@ -14,3 +15,3 @@ /* eslint-disable react-hooks/exhaustive-deps */ | ||
import { useWatch, useInit, kebabToCamelCase, warn, splitTask, isPlainObject } from './utils'; | ||
import { directive, getEvaluate } from './hooks'; | ||
import { directive, getEvaluate, isDefaultDirectiveSuffix, isNonDefaultDirectiveSuffix } from './hooks'; | ||
import { getScope } from './scopes'; | ||
@@ -75,5 +76,3 @@ import { proxifyState, proxifyContext, deepMerge } from './proxies'; | ||
}) => { | ||
directives[`on-${type}`].filter(({ | ||
suffix | ||
}) => suffix !== 'default').forEach(entry => { | ||
directives[`on-${type}`].filter(isNonDefaultDirectiveSuffix).forEach(entry => { | ||
const eventName = entry.suffix.split('--', 1)[0]; | ||
@@ -101,5 +100,3 @@ useInit(() => { | ||
}) => { | ||
directives[`on-async-${type}`].filter(({ | ||
suffix | ||
}) => suffix !== 'default').forEach(entry => { | ||
directives[`on-async-${type}`].filter(isNonDefaultDirectiveSuffix).forEach(entry => { | ||
const eventName = entry.suffix.split('--', 1)[0]; | ||
@@ -120,3 +117,3 @@ useInit(() => { | ||
}; | ||
export default (() => { | ||
export default () => { | ||
// data-wp-context | ||
@@ -135,5 +132,3 @@ directive('context', ({ | ||
} = inheritedContext; | ||
const defaultEntry = context.find(({ | ||
suffix | ||
}) => suffix === 'default'); | ||
const defaultEntry = context.find(isDefaultDirectiveSuffix); | ||
const { | ||
@@ -262,5 +257,3 @@ client: inheritedClient, | ||
const events = new Map(); | ||
on.filter(({ | ||
suffix | ||
}) => suffix !== 'default').forEach(entry => { | ||
on.filter(isNonDefaultDirectiveSuffix).forEach(entry => { | ||
const event = entry.suffix.split('--')[0]; | ||
@@ -314,5 +307,3 @@ if (!events.has(event)) { | ||
const events = new Map(); | ||
onAsync.filter(({ | ||
suffix | ||
}) => suffix !== 'default').forEach(entry => { | ||
onAsync.filter(isNonDefaultDirectiveSuffix).forEach(entry => { | ||
const event = entry.suffix.split('--')[0]; | ||
@@ -356,5 +347,3 @@ if (!events.has(event)) { | ||
}) => { | ||
classNames.filter(({ | ||
suffix | ||
}) => suffix !== 'default').forEach(entry => { | ||
classNames.filter(isNonDefaultDirectiveSuffix).forEach(entry => { | ||
const className = entry.suffix; | ||
@@ -392,5 +381,3 @@ const result = evaluate(entry); | ||
}) => { | ||
style.filter(({ | ||
suffix | ||
}) => suffix !== 'default').forEach(entry => { | ||
style.filter(isNonDefaultDirectiveSuffix).forEach(entry => { | ||
const styleProp = entry.suffix; | ||
@@ -430,5 +417,3 @@ const result = evaluate(entry); | ||
}) => { | ||
bind.filter(({ | ||
suffix | ||
}) => suffix !== 'default').forEach(entry => { | ||
bind.filter(isNonDefaultDirectiveSuffix).forEach(entry => { | ||
const attribute = entry.suffix; | ||
@@ -517,5 +502,3 @@ const result = evaluate(entry); | ||
}) => { | ||
const entry = text.find(({ | ||
suffix | ||
}) => suffix === 'default'); | ||
const entry = text.find(isDefaultDirectiveSuffix); | ||
if (!entry) { | ||
@@ -562,8 +545,7 @@ element.props.children = null; | ||
const { | ||
namespace, | ||
suffix | ||
namespace | ||
} = entry; | ||
const list = evaluate(entry); | ||
const itemProp = isNonDefaultDirectiveSuffix(entry) ? kebabToCamelCase(entry.suffix) : 'item'; | ||
return list.map(item => { | ||
const itemProp = suffix === 'default' ? 'item' : kebabToCamelCase(suffix); | ||
const itemContext = proxifyContext(proxifyState(namespace, {}), inheritedValue.client[namespace]); | ||
@@ -601,3 +583,3 @@ const mergedContext = { | ||
}); | ||
}); | ||
}; | ||
//# sourceMappingURL=directives.js.map |
@@ -15,2 +15,8 @@ // eslint-disable-next-line eslint-comments/disable-enable-pair | ||
import { getScope, setScope, resetScope } from './scopes'; | ||
export function isNonDefaultDirectiveSuffix(entry) { | ||
return entry.suffix !== null; | ||
} | ||
export function isDefaultDirectiveSuffix(entry) { | ||
return entry.suffix === null; | ||
} | ||
// Main context. | ||
@@ -34,3 +40,3 @@ const context = createContext({ | ||
* ( { directives: { alert }, element, evaluate } ) => { | ||
* const defaultEntry = alert.find( entry => entry.suffix === 'default' ); | ||
* const defaultEntry = alert.find( isDefaultDirectiveSuffix ); | ||
* element.props.onclick = () => { alert( evaluate( defaultEntry ) ); } | ||
@@ -54,3 +60,3 @@ * } | ||
* Note that, in the previous example, the directive callback gets the path | ||
* value (`state.alert`) from the directive entry with suffix `default`. A | ||
* value (`state.alert`) from the directive entry with suffix `null`. A | ||
* custom suffix can also be specified by appending `--` to the directive | ||
@@ -231,5 +237,3 @@ * attribute, followed by the suffix, like in the following HTML snippet: | ||
if (directives.key) { | ||
vnode.key = directives.key.find(({ | ||
suffix | ||
}) => suffix === 'default').value; | ||
vnode.key = directives.key.find(isDefaultDirectiveSuffix).value; | ||
} | ||
@@ -236,0 +240,0 @@ delete props.__directives; |
@@ -0,1 +1,2 @@ | ||
/* wp:polyfill */ | ||
const contextObjectToProxy = new WeakMap(); | ||
@@ -2,0 +3,0 @@ const contextObjectToFallback = new WeakMap(); |
@@ -0,1 +1,2 @@ | ||
/* wp:polyfill */ | ||
/** | ||
@@ -2,0 +3,0 @@ * Proxies for each object. |
@@ -0,1 +1,2 @@ | ||
/* wp:polyfill */ | ||
/** | ||
@@ -246,39 +247,43 @@ * External dependencies | ||
const deepMergeRecursive = (target, source, override = true) => { | ||
if (isPlainObject(target) && isPlainObject(source)) { | ||
let hasNewKeys = false; | ||
for (const key in source) { | ||
const isNew = !(key in target); | ||
hasNewKeys = hasNewKeys || isNew; | ||
const desc = Object.getOwnPropertyDescriptor(source, key); | ||
if (typeof desc?.get === 'function' || typeof desc?.set === 'function') { | ||
if (override || isNew) { | ||
Object.defineProperty(target, key, { | ||
...desc, | ||
configurable: true, | ||
enumerable: true | ||
}); | ||
const proxy = getProxyFromObject(target); | ||
if (desc?.get && proxy && hasPropSignal(proxy, key)) { | ||
const propSignal = getPropSignal(proxy, key); | ||
propSignal.setGetter(desc.get); | ||
} | ||
if (!(isPlainObject(target) && isPlainObject(source))) { | ||
return; | ||
} | ||
let hasNewKeys = false; | ||
for (const key in source) { | ||
const isNew = !(key in target); | ||
hasNewKeys = hasNewKeys || isNew; | ||
const desc = Object.getOwnPropertyDescriptor(source, key); | ||
const proxy = getProxyFromObject(target); | ||
const propSignal = !!proxy && hasPropSignal(proxy, key) && getPropSignal(proxy, key); | ||
if (typeof desc.get === 'function' || typeof desc.set === 'function') { | ||
if (override || isNew) { | ||
Object.defineProperty(target, key, { | ||
...desc, | ||
configurable: true, | ||
enumerable: true | ||
}); | ||
if (desc.get && propSignal) { | ||
propSignal.setGetter(desc.get); | ||
} | ||
} else if (isPlainObject(source[key])) { | ||
if (isNew) { | ||
target[key] = {}; | ||
} | ||
} else if (isPlainObject(source[key])) { | ||
if (isNew || override && !isPlainObject(target[key])) { | ||
target[key] = {}; | ||
if (propSignal) { | ||
propSignal.setValue(target[key]); | ||
} | ||
} | ||
if (isPlainObject(target[key])) { | ||
deepMergeRecursive(target[key], source[key], override); | ||
} else if (override || isNew) { | ||
Object.defineProperty(target, key, desc); | ||
const proxy = getProxyFromObject(target); | ||
if (desc?.value && proxy && hasPropSignal(proxy, key)) { | ||
const propSignal = getPropSignal(proxy, key); | ||
propSignal.setValue(desc.value); | ||
} | ||
} | ||
} else if (override || isNew) { | ||
Object.defineProperty(target, key, desc); | ||
if (propSignal) { | ||
propSignal.setValue(desc.value); | ||
} | ||
} | ||
if (hasNewKeys && objToIterable.has(target)) { | ||
objToIterable.get(target).value++; | ||
} | ||
} | ||
if (hasNewKeys && objToIterable.has(target)) { | ||
objToIterable.get(target).value++; | ||
} | ||
}; | ||
@@ -285,0 +290,0 @@ |
@@ -0,1 +1,2 @@ | ||
/* wp:polyfill */ | ||
/** | ||
@@ -38,5 +39,4 @@ * External dependencies | ||
*/ | ||
export const splitTask = () => { | ||
export const splitTask = typeof window.scheduler?.yield === 'function' ? window.scheduler.yield.bind(window.scheduler) : () => { | ||
return new Promise(resolve => { | ||
// TODO: Use scheduler.yield() when available. | ||
setTimeout(resolve, 0); | ||
@@ -43,0 +43,0 @@ }); |
@@ -133,7 +133,7 @@ /** | ||
const prefix = directiveMatch[1] || ''; | ||
const suffix = directiveMatch[2] || 'default'; | ||
const suffix = directiveMatch[2] || null; | ||
obj[prefix] = obj[prefix] || []; | ||
obj[prefix].push({ | ||
namespace: ns !== null && ns !== void 0 ? ns : currentNamespace(), | ||
value, | ||
value: value, | ||
suffix | ||
@@ -144,4 +144,2 @@ }); | ||
} | ||
// @ts-expect-error Fixed in upcoming preact release https://github.com/preactjs/preact/pull/4334 | ||
if (localName === 'template') { | ||
@@ -148,0 +146,0 @@ props.content = [...elementNode.content.childNodes].map(childNode => toVdom(childNode)); |
@@ -10,4 +10,12 @@ /** | ||
namespace: string; | ||
suffix: string | null; | ||
} | ||
export interface NonDefaultSuffixDirectiveEntry extends DirectiveEntry { | ||
suffix: string; | ||
} | ||
export interface DefaultSuffixDirectiveEntry extends DirectiveEntry { | ||
suffix: null; | ||
} | ||
export declare function isNonDefaultDirectiveSuffix(entry: DirectiveEntry): entry is NonDefaultSuffixDirectiveEntry; | ||
export declare function isDefaultDirectiveSuffix(entry: DirectiveEntry): entry is DefaultSuffixDirectiveEntry; | ||
type DirectiveEntries = Record<string, DirectiveEntry[]>; | ||
@@ -43,3 +51,3 @@ interface DirectiveArgs { | ||
} | ||
interface DirectiveCallback { | ||
export interface DirectiveCallback { | ||
(args: DirectiveArgs): VNode<any> | null | void; | ||
@@ -72,3 +80,3 @@ } | ||
* ( { directives: { alert }, element, evaluate } ) => { | ||
* const defaultEntry = alert.find( entry => entry.suffix === 'default' ); | ||
* const defaultEntry = alert.find( isDefaultDirectiveSuffix ); | ||
* element.props.onclick = () => { alert( evaluate( defaultEntry ) ); } | ||
@@ -92,3 +100,3 @@ * } | ||
* Note that, in the previous example, the directive callback gets the path | ||
* value (`state.alert`) from the directive entry with suffix `default`. A | ||
* value (`state.alert`) from the directive entry with suffix `null`. A | ||
* custom suffix can also be specified by appending `--` to the directive | ||
@@ -95,0 +103,0 @@ * attribute, followed by the suffix, like in the following HTML snippet: |
@@ -5,2 +5,9 @@ /** | ||
import { type EffectCallback, type Inputs } from 'preact/hooks'; | ||
declare global { | ||
interface Window { | ||
scheduler?: { | ||
readonly yield?: () => Promise<void>; | ||
}; | ||
} | ||
} | ||
/** | ||
@@ -7,0 +14,0 @@ * Returns a promise that resolves after yielding to main. |
@@ -0,1 +1,2 @@ | ||
/* wp:polyfill */ | ||
"use strict"; | ||
@@ -80,5 +81,3 @@ | ||
}) => { | ||
directives[`on-${type}`].filter(({ | ||
suffix | ||
}) => suffix !== 'default').forEach(entry => { | ||
directives[`on-${type}`].filter(_hooks2.isNonDefaultDirectiveSuffix).forEach(entry => { | ||
const eventName = entry.suffix.split('--', 1)[0]; | ||
@@ -106,5 +105,3 @@ (0, _utils.useInit)(() => { | ||
}) => { | ||
directives[`on-async-${type}`].filter(({ | ||
suffix | ||
}) => suffix !== 'default').forEach(entry => { | ||
directives[`on-async-${type}`].filter(_hooks2.isNonDefaultDirectiveSuffix).forEach(entry => { | ||
const eventName = entry.suffix.split('--', 1)[0]; | ||
@@ -139,5 +136,3 @@ (0, _utils.useInit)(() => { | ||
} = inheritedContext; | ||
const defaultEntry = context.find(({ | ||
suffix | ||
}) => suffix === 'default'); | ||
const defaultEntry = context.find(_hooks2.isDefaultDirectiveSuffix); | ||
const { | ||
@@ -266,5 +261,3 @@ client: inheritedClient, | ||
const events = new Map(); | ||
on.filter(({ | ||
suffix | ||
}) => suffix !== 'default').forEach(entry => { | ||
on.filter(_hooks2.isNonDefaultDirectiveSuffix).forEach(entry => { | ||
const event = entry.suffix.split('--')[0]; | ||
@@ -318,5 +311,3 @@ if (!events.has(event)) { | ||
const events = new Map(); | ||
onAsync.filter(({ | ||
suffix | ||
}) => suffix !== 'default').forEach(entry => { | ||
onAsync.filter(_hooks2.isNonDefaultDirectiveSuffix).forEach(entry => { | ||
const event = entry.suffix.split('--')[0]; | ||
@@ -360,5 +351,3 @@ if (!events.has(event)) { | ||
}) => { | ||
classNames.filter(({ | ||
suffix | ||
}) => suffix !== 'default').forEach(entry => { | ||
classNames.filter(_hooks2.isNonDefaultDirectiveSuffix).forEach(entry => { | ||
const className = entry.suffix; | ||
@@ -396,5 +385,3 @@ const result = evaluate(entry); | ||
}) => { | ||
style.filter(({ | ||
suffix | ||
}) => suffix !== 'default').forEach(entry => { | ||
style.filter(_hooks2.isNonDefaultDirectiveSuffix).forEach(entry => { | ||
const styleProp = entry.suffix; | ||
@@ -434,5 +421,3 @@ const result = evaluate(entry); | ||
}) => { | ||
bind.filter(({ | ||
suffix | ||
}) => suffix !== 'default').forEach(entry => { | ||
bind.filter(_hooks2.isNonDefaultDirectiveSuffix).forEach(entry => { | ||
const attribute = entry.suffix; | ||
@@ -521,5 +506,3 @@ const result = evaluate(entry); | ||
}) => { | ||
const entry = text.find(({ | ||
suffix | ||
}) => suffix === 'default'); | ||
const entry = text.find(_hooks2.isDefaultDirectiveSuffix); | ||
if (!entry) { | ||
@@ -566,8 +549,7 @@ element.props.children = null; | ||
const { | ||
namespace, | ||
suffix | ||
namespace | ||
} = entry; | ||
const list = evaluate(entry); | ||
const itemProp = (0, _hooks2.isNonDefaultDirectiveSuffix)(entry) ? (0, _utils.kebabToCamelCase)(entry.suffix) : 'item'; | ||
return list.map(item => { | ||
const itemProp = suffix === 'default' ? 'item' : (0, _utils.kebabToCamelCase)(suffix); | ||
const itemContext = (0, _proxies.proxifyContext)((0, _proxies.proxifyState)(namespace, {}), inheritedValue.client[namespace]); | ||
@@ -574,0 +556,0 @@ const mergedContext = { |
@@ -7,2 +7,4 @@ "use strict"; | ||
exports.getEvaluate = exports.directive = void 0; | ||
exports.isDefaultDirectiveSuffix = isDefaultDirectiveSuffix; | ||
exports.isNonDefaultDirectiveSuffix = isNonDefaultDirectiveSuffix; | ||
var _preact = require("preact"); | ||
@@ -24,2 +26,8 @@ var _hooks = require("preact/hooks"); | ||
function isNonDefaultDirectiveSuffix(entry) { | ||
return entry.suffix !== null; | ||
} | ||
function isDefaultDirectiveSuffix(entry) { | ||
return entry.suffix === null; | ||
} | ||
// Main context. | ||
@@ -43,3 +51,3 @@ const context = (0, _preact.createContext)({ | ||
* ( { directives: { alert }, element, evaluate } ) => { | ||
* const defaultEntry = alert.find( entry => entry.suffix === 'default' ); | ||
* const defaultEntry = alert.find( isDefaultDirectiveSuffix ); | ||
* element.props.onclick = () => { alert( evaluate( defaultEntry ) ); } | ||
@@ -63,3 +71,3 @@ * } | ||
* Note that, in the previous example, the directive callback gets the path | ||
* value (`state.alert`) from the directive entry with suffix `default`. A | ||
* value (`state.alert`) from the directive entry with suffix `null`. A | ||
* custom suffix can also be specified by appending `--` to the directive | ||
@@ -242,5 +250,3 @@ * attribute, followed by the suffix, like in the following HTML snippet: | ||
if (directives.key) { | ||
vnode.key = directives.key.find(({ | ||
suffix | ||
}) => suffix === 'default').value; | ||
vnode.key = directives.key.find(isDefaultDirectiveSuffix).value; | ||
} | ||
@@ -247,0 +253,0 @@ delete props.__directives; |
@@ -0,1 +1,2 @@ | ||
/* wp:polyfill */ | ||
"use strict"; | ||
@@ -2,0 +3,0 @@ |
@@ -0,1 +1,2 @@ | ||
/* wp:polyfill */ | ||
"use strict"; | ||
@@ -2,0 +3,0 @@ |
@@ -0,1 +1,2 @@ | ||
/* wp:polyfill */ | ||
"use strict"; | ||
@@ -255,39 +256,43 @@ | ||
const deepMergeRecursive = (target, source, override = true) => { | ||
if ((0, _utils.isPlainObject)(target) && (0, _utils.isPlainObject)(source)) { | ||
let hasNewKeys = false; | ||
for (const key in source) { | ||
const isNew = !(key in target); | ||
hasNewKeys = hasNewKeys || isNew; | ||
const desc = Object.getOwnPropertyDescriptor(source, key); | ||
if (typeof desc?.get === 'function' || typeof desc?.set === 'function') { | ||
if (override || isNew) { | ||
Object.defineProperty(target, key, { | ||
...desc, | ||
configurable: true, | ||
enumerable: true | ||
}); | ||
const proxy = (0, _registry.getProxyFromObject)(target); | ||
if (desc?.get && proxy && hasPropSignal(proxy, key)) { | ||
const propSignal = getPropSignal(proxy, key); | ||
propSignal.setGetter(desc.get); | ||
} | ||
if (!((0, _utils.isPlainObject)(target) && (0, _utils.isPlainObject)(source))) { | ||
return; | ||
} | ||
let hasNewKeys = false; | ||
for (const key in source) { | ||
const isNew = !(key in target); | ||
hasNewKeys = hasNewKeys || isNew; | ||
const desc = Object.getOwnPropertyDescriptor(source, key); | ||
const proxy = (0, _registry.getProxyFromObject)(target); | ||
const propSignal = !!proxy && hasPropSignal(proxy, key) && getPropSignal(proxy, key); | ||
if (typeof desc.get === 'function' || typeof desc.set === 'function') { | ||
if (override || isNew) { | ||
Object.defineProperty(target, key, { | ||
...desc, | ||
configurable: true, | ||
enumerable: true | ||
}); | ||
if (desc.get && propSignal) { | ||
propSignal.setGetter(desc.get); | ||
} | ||
} else if ((0, _utils.isPlainObject)(source[key])) { | ||
if (isNew) { | ||
target[key] = {}; | ||
} | ||
} else if ((0, _utils.isPlainObject)(source[key])) { | ||
if (isNew || override && !(0, _utils.isPlainObject)(target[key])) { | ||
target[key] = {}; | ||
if (propSignal) { | ||
propSignal.setValue(target[key]); | ||
} | ||
} | ||
if ((0, _utils.isPlainObject)(target[key])) { | ||
deepMergeRecursive(target[key], source[key], override); | ||
} else if (override || isNew) { | ||
Object.defineProperty(target, key, desc); | ||
const proxy = (0, _registry.getProxyFromObject)(target); | ||
if (desc?.value && proxy && hasPropSignal(proxy, key)) { | ||
const propSignal = getPropSignal(proxy, key); | ||
propSignal.setValue(desc.value); | ||
} | ||
} | ||
} else if (override || isNew) { | ||
Object.defineProperty(target, key, desc); | ||
if (propSignal) { | ||
propSignal.setValue(desc.value); | ||
} | ||
} | ||
if (hasNewKeys && objToIterable.has(target)) { | ||
objToIterable.get(target).value++; | ||
} | ||
} | ||
if (hasNewKeys && objToIterable.has(target)) { | ||
objToIterable.get(target).value++; | ||
} | ||
}; | ||
@@ -294,0 +299,0 @@ |
@@ -0,1 +1,2 @@ | ||
/* wp:polyfill */ | ||
"use strict"; | ||
@@ -56,5 +57,4 @@ | ||
*/ | ||
const splitTask = () => { | ||
const splitTask = exports.splitTask = typeof window.scheduler?.yield === 'function' ? window.scheduler.yield.bind(window.scheduler) : () => { | ||
return new Promise(resolve => { | ||
// TODO: Use scheduler.yield() when available. | ||
setTimeout(resolve, 0); | ||
@@ -76,3 +76,2 @@ }); | ||
*/ | ||
exports.splitTask = splitTask; | ||
function createFlusher(compute, notify) { | ||
@@ -79,0 +78,0 @@ let flush = () => undefined; |
@@ -142,7 +142,7 @@ "use strict"; | ||
const prefix = directiveMatch[1] || ''; | ||
const suffix = directiveMatch[2] || 'default'; | ||
const suffix = directiveMatch[2] || null; | ||
obj[prefix] = obj[prefix] || []; | ||
obj[prefix].push({ | ||
namespace: ns !== null && ns !== void 0 ? ns : currentNamespace(), | ||
value, | ||
value: value, | ||
suffix | ||
@@ -153,4 +153,2 @@ }); | ||
} | ||
// @ts-expect-error Fixed in upcoming preact release https://github.com/preactjs/preact/pull/4334 | ||
if (localName === 'template') { | ||
@@ -157,0 +155,0 @@ props.content = [...elementNode.content.childNodes].map(childNode => toVdom(childNode)); |
@@ -5,2 +5,13 @@ <!-- Learn how to maintain this file at https://github.com/WordPress/gutenberg/tree/HEAD/packages#maintaining-changelogs. --> | ||
## 6.10.0 (2024-10-16) | ||
### Internal | ||
- Upgrade preact libraries [#66008](https://github.com/WordPress/gutenberg/pull/66008). | ||
### Bug Fixes | ||
- Fix an issue where "default" could not be used as a directive suffix ([#65815](https://github.com/WordPress/gutenberg/pull/65815)). | ||
- Correctly handle lazily added, deeply nested properties with `deepMerge()` ([#65465](https://github.com/WordPress/gutenberg/pull/65465)). | ||
## 6.9.0 (2024-10-03) | ||
@@ -7,0 +18,0 @@ |
{ | ||
"name": "@wordpress/interactivity", | ||
"version": "6.9.0", | ||
"version": "6.10.0", | ||
"description": "Package that provides a standard and simple way to handle the frontend interactivity of Gutenberg blocks.", | ||
@@ -34,4 +34,4 @@ "author": "The WordPress Contributors", | ||
"dependencies": { | ||
"@preact/signals": "^1.2.2", | ||
"preact": "^10.19.3" | ||
"@preact/signals": "^1.3.0", | ||
"preact": "^10.24.2" | ||
}, | ||
@@ -41,3 +41,3 @@ "publishConfig": { | ||
}, | ||
"gitHead": "2e5495c635910cb34bfaca3c6258d2e989f66214" | ||
"gitHead": "ab34a7ac935fd1478eac63b596242d83270897ee" | ||
} |
@@ -303,46 +303,53 @@ /** | ||
) => { | ||
if ( isPlainObject( target ) && isPlainObject( source ) ) { | ||
let hasNewKeys = false; | ||
for ( const key in source ) { | ||
const isNew = ! ( key in target ); | ||
hasNewKeys = hasNewKeys || isNew; | ||
if ( ! ( isPlainObject( target ) && isPlainObject( source ) ) ) { | ||
return; | ||
} | ||
const desc = Object.getOwnPropertyDescriptor( source, key ); | ||
if ( | ||
typeof desc?.get === 'function' || | ||
typeof desc?.set === 'function' | ||
) { | ||
if ( override || isNew ) { | ||
Object.defineProperty( target, key, { | ||
...desc, | ||
configurable: true, | ||
enumerable: true, | ||
} ); | ||
let hasNewKeys = false; | ||
const proxy = getProxyFromObject( target ); | ||
if ( desc?.get && proxy && hasPropSignal( proxy, key ) ) { | ||
const propSignal = getPropSignal( proxy, key ); | ||
propSignal.setGetter( desc.get ); | ||
} | ||
} | ||
} else if ( isPlainObject( source[ key ] ) ) { | ||
if ( isNew ) { | ||
target[ key ] = {}; | ||
} | ||
for ( const key in source ) { | ||
const isNew = ! ( key in target ); | ||
hasNewKeys = hasNewKeys || isNew; | ||
deepMergeRecursive( target[ key ], source[ key ], override ); | ||
} else if ( override || isNew ) { | ||
Object.defineProperty( target, key, desc! ); | ||
const desc = Object.getOwnPropertyDescriptor( source, key )!; | ||
const proxy = getProxyFromObject( target ); | ||
const propSignal = | ||
!! proxy && | ||
hasPropSignal( proxy, key ) && | ||
getPropSignal( proxy, key ); | ||
const proxy = getProxyFromObject( target ); | ||
if ( desc?.value && proxy && hasPropSignal( proxy, key ) ) { | ||
const propSignal = getPropSignal( proxy, key ); | ||
propSignal.setValue( desc.value ); | ||
if ( | ||
typeof desc.get === 'function' || | ||
typeof desc.set === 'function' | ||
) { | ||
if ( override || isNew ) { | ||
Object.defineProperty( target, key, { | ||
...desc, | ||
configurable: true, | ||
enumerable: true, | ||
} ); | ||
if ( desc.get && propSignal ) { | ||
propSignal.setGetter( desc.get ); | ||
} | ||
} | ||
} else if ( isPlainObject( source[ key ] ) ) { | ||
if ( isNew || ( override && ! isPlainObject( target[ key ] ) ) ) { | ||
target[ key ] = {}; | ||
if ( propSignal ) { | ||
propSignal.setValue( target[ key ] ); | ||
} | ||
} | ||
if ( isPlainObject( target[ key ] ) ) { | ||
deepMergeRecursive( target[ key ], source[ key ], override ); | ||
} | ||
} else if ( override || isNew ) { | ||
Object.defineProperty( target, key, desc ); | ||
if ( propSignal ) { | ||
propSignal.setValue( desc.value ); | ||
} | ||
} | ||
} | ||
if ( hasNewKeys && objToIterable.has( target ) ) { | ||
objToIterable.get( target )!.value++; | ||
} | ||
if ( hasNewKeys && objToIterable.has( target ) ) { | ||
objToIterable.get( target )!.value++; | ||
} | ||
@@ -349,0 +356,0 @@ }; |
@@ -9,3 +9,3 @@ /** | ||
*/ | ||
import { proxifyContext, proxifyState } from '../'; | ||
import { proxifyContext, proxifyState, deepMerge } from '../'; | ||
@@ -281,2 +281,62 @@ describe( 'Interactivity API', () => { | ||
} ); | ||
it( 'should handle deeply nested properties that are initially undefined', () => { | ||
const fallback: any = proxifyContext( | ||
proxifyState( 'test', {} ), | ||
{} | ||
); | ||
const context: any = proxifyContext( | ||
proxifyState( 'test', {} ), | ||
fallback | ||
); | ||
let deepValue: any; | ||
const spy = jest.fn( () => { | ||
deepValue = context.a?.b?.c?.d; | ||
} ); | ||
effect( spy ); | ||
// Initial call, the deep value is undefined | ||
expect( spy ).toHaveBeenCalledTimes( 1 ); | ||
expect( deepValue ).toBeUndefined(); | ||
// Add a deeply nested object to the context | ||
context.a = { b: { c: { d: 'test value' } } }; | ||
// The effect should be called again | ||
expect( spy ).toHaveBeenCalledTimes( 2 ); | ||
expect( deepValue ).toBe( 'test value' ); | ||
// Reading the value directly should also work | ||
expect( context.a.b.c.d ).toBe( 'test value' ); | ||
} ); | ||
it( 'should handle deeply nested properties that are initially undefined and merged with deepMerge', () => { | ||
const fallbackState = proxifyState( 'test', {} ); | ||
const fallback: any = proxifyContext( fallbackState, {} ); | ||
const contextState = proxifyState( 'test', {} ); | ||
const context: any = proxifyContext( contextState, fallback ); | ||
let deepValue: any; | ||
const spy = jest.fn( () => { | ||
deepValue = context.a?.b?.c?.d; | ||
} ); | ||
effect( spy ); | ||
// Initial call, the deep value is undefined | ||
expect( spy ).toHaveBeenCalledTimes( 1 ); | ||
expect( deepValue ).toBeUndefined(); | ||
// Use deepMerge to add a deeply nested object to the context | ||
deepMerge( contextState, { | ||
a: { b: { c: { d: 'test value' } } }, | ||
} ); | ||
// The effect should be called again | ||
expect( spy ).toHaveBeenCalledTimes( 2 ); | ||
expect( deepValue ).toBe( 'test value' ); | ||
// Reading the value directly should also work | ||
expect( context.a.b.c.d ).toBe( 'test value' ); | ||
} ); | ||
} ); | ||
@@ -283,0 +343,0 @@ |
@@ -392,3 +392,76 @@ /* eslint-disable eslint-comments/disable-enable-pair */ | ||
} ); | ||
it( 'should handle deeply nested properties that are initially undefined', () => { | ||
const target: any = proxifyState( 'test', {} ); | ||
let deepValue: any; | ||
const spy = jest.fn( () => { | ||
deepValue = target.a?.b?.c?.d; | ||
} ); | ||
effect( spy ); | ||
// Initial call, the deep value is undefined | ||
expect( spy ).toHaveBeenCalledTimes( 1 ); | ||
expect( deepValue ).toBeUndefined(); | ||
// Use deepMerge to add a deeply nested object to the target | ||
deepMerge( target, { a: { b: { c: { d: 'test value' } } } } ); | ||
// The effect should be called again | ||
expect( spy ).toHaveBeenCalledTimes( 2 ); | ||
expect( deepValue ).toBe( 'test value' ); | ||
// Reading the value directly should also work | ||
expect( target.a.b.c.d ).toBe( 'test value' ); | ||
} ); | ||
it( 'should overwrite values that become objects', () => { | ||
const target: any = proxifyState( 'test', { message: 'hello' } ); | ||
let message: any; | ||
const spy = jest.fn( () => ( message = target.message ) ); | ||
effect( spy ); | ||
expect( spy ).toHaveBeenCalledTimes( 1 ); | ||
expect( message ).toBe( 'hello' ); | ||
deepMerge( target, { | ||
message: { content: 'hello', fontStyle: 'italic' }, | ||
} ); | ||
expect( spy ).toHaveBeenCalledTimes( 2 ); | ||
expect( message ).toEqual( { | ||
content: 'hello', | ||
fontStyle: 'italic', | ||
} ); | ||
expect( target.message ).toEqual( { | ||
content: 'hello', | ||
fontStyle: 'italic', | ||
} ); | ||
} ); | ||
it( 'should not overwrite values that become objects if `override` is false', () => { | ||
const target: any = proxifyState( 'test', { message: 'hello' } ); | ||
let message: any; | ||
const spy = jest.fn( () => ( message = target.message ) ); | ||
effect( spy ); | ||
expect( spy ).toHaveBeenCalledTimes( 1 ); | ||
expect( message ).toBe( 'hello' ); | ||
deepMerge( | ||
target, | ||
{ message: { content: 'hello', fontStyle: 'italic' } }, | ||
false | ||
); | ||
expect( spy ).toHaveBeenCalledTimes( 1 ); | ||
expect( message ).toBe( 'hello' ); | ||
expect( target.message ).toBe( 'hello' ); | ||
expect( target.message.content ).toBeUndefined(); | ||
expect( target.message.fontStyle ).toBeUndefined(); | ||
} ); | ||
} ); | ||
} ); |
@@ -25,2 +25,10 @@ /** | ||
declare global { | ||
interface Window { | ||
scheduler?: { | ||
readonly yield?: () => Promise< void >; | ||
}; | ||
} | ||
} | ||
/** | ||
@@ -52,8 +60,10 @@ * Executes a callback function after the next frame is rendered. | ||
*/ | ||
export const splitTask = () => { | ||
return new Promise( ( resolve ) => { | ||
// TODO: Use scheduler.yield() when available. | ||
setTimeout( resolve, 0 ); | ||
} ); | ||
}; | ||
export const splitTask = | ||
typeof window.scheduler?.yield === 'function' | ||
? window.scheduler.yield.bind( window.scheduler ) | ||
: () => { | ||
return new Promise( ( resolve ) => { | ||
setTimeout( resolve, 0 ); | ||
} ); | ||
}; | ||
@@ -72,5 +82,5 @@ /** | ||
*/ | ||
function createFlusher( compute: () => unknown, notify: () => void ): Flusher { | ||
function createFlusher( compute: () => void, notify: () => void ): Flusher { | ||
let flush: () => void = () => undefined; | ||
const dispose = effect( function ( this: any ) { | ||
const dispose = effect( function ( this: any ): void { | ||
flush = this.c.bind( this ); | ||
@@ -77,0 +87,0 @@ this.x = compute; |
@@ -10,2 +10,3 @@ /** | ||
import { warn } from './utils'; | ||
import { type DirectiveEntry } from './hooks'; | ||
@@ -143,25 +144,23 @@ const ignoreAttr = `data-${ p }-ignore`; | ||
if ( directives.length ) { | ||
props.__directives = directives.reduce( | ||
( obj, [ name, ns, value ] ) => { | ||
const directiveMatch = directiveParser.exec( name ); | ||
if ( directiveMatch === null ) { | ||
warn( `Found malformed directive name: ${ name }.` ); | ||
return obj; | ||
} | ||
const prefix = directiveMatch[ 1 ] || ''; | ||
const suffix = directiveMatch[ 2 ] || 'default'; | ||
props.__directives = directives.reduce< | ||
Record< string, Array< DirectiveEntry > > | ||
>( ( obj, [ name, ns, value ] ) => { | ||
const directiveMatch = directiveParser.exec( name ); | ||
if ( directiveMatch === null ) { | ||
warn( `Found malformed directive name: ${ name }.` ); | ||
return obj; | ||
} | ||
const prefix = directiveMatch[ 1 ] || ''; | ||
const suffix = directiveMatch[ 2 ] || null; | ||
obj[ prefix ] = obj[ prefix ] || []; | ||
obj[ prefix ].push( { | ||
namespace: ns ?? currentNamespace(), | ||
value, | ||
suffix, | ||
} ); | ||
return obj; | ||
}, | ||
{} | ||
); | ||
obj[ prefix ] = obj[ prefix ] || []; | ||
obj[ prefix ].push( { | ||
namespace: ns ?? currentNamespace()!, | ||
value: value as DirectiveEntry[ 'value' ], | ||
suffix, | ||
} ); | ||
return obj; | ||
}, {} ); | ||
} | ||
// @ts-expect-error Fixed in upcoming preact release https://github.com/preactjs/preact/pull/4334 | ||
if ( localName === 'template' ) { | ||
@@ -168,0 +167,0 @@ props.content = [ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
747892
10466
Updated@preact/signals@^1.3.0
Updatedpreact@^10.24.2