fix: prevent asset conflicts between React and Grid.js versions

Add coexistence checks to all enqueue methods to prevent loading
both React and Grid.js assets simultaneously.

Changes:
- ReactAdmin.php: Only enqueue React assets when ?react=1
- Init.php: Skip Grid.js when React active on admin pages
- Form.php, Coupon.php, Access.php: Restore classic assets when ?react=0
- Customer.php, Product.php, License.php: Add coexistence checks

Now the toggle between Classic and React versions works correctly.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
dwindown
2026-04-18 17:02:14 +07:00
parent bd9cdac02e
commit e8fbfb14c1
74973 changed files with 6658406 additions and 71 deletions

View File

@@ -0,0 +1,166 @@
import { createElement, Fragment } from "react";
/**
* External dependencies
*/
/**
* WordPress dependencies
*/
import { speak } from '@wordpress/a11y';
import { check, moreVertical, plus } from '@wordpress/icons';
import { __, _x, sprintf } from '@wordpress/i18n';
/**
* Internal dependencies
*/
import DropdownMenu from '../../dropdown-menu';
import MenuGroup from '../../menu-group';
import MenuItem from '../../menu-item';
import { HStack } from '../../h-stack';
import { Heading } from '../../heading';
import { useToolsPanelHeader } from './hook';
import { contextConnect } from '../../context';
import { ResetLabel } from '../styles';
const DefaultControlsGroup = ({
itemClassName,
items,
toggleItem
}) => {
if (!items.length) {
return null;
}
const resetSuffix = createElement(ResetLabel, {
"aria-hidden": true
}, __('Reset'));
return createElement(Fragment, null, items.map(([label, hasValue]) => {
if (hasValue) {
return createElement(MenuItem, {
key: label,
className: itemClassName,
role: "menuitem",
label: sprintf(
// translators: %s: The name of the control being reset e.g. "Padding".
__('Reset %s'), label),
onClick: () => {
toggleItem(label);
speak(sprintf(
// translators: %s: The name of the control being reset e.g. "Padding".
__('%s reset to default'), label), 'assertive');
},
suffix: resetSuffix
}, label);
}
return createElement(MenuItem, {
key: label,
icon: check,
className: itemClassName,
role: "menuitemcheckbox",
isSelected: true,
"aria-disabled": true
}, label);
}));
};
const OptionalControlsGroup = ({
items,
toggleItem
}) => {
if (!items.length) {
return null;
}
return createElement(Fragment, null, items.map(([label, isSelected]) => {
const itemLabel = isSelected ? sprintf(
// translators: %s: The name of the control being hidden and reset e.g. "Padding".
__('Hide and reset %s'), label) : sprintf(
// translators: %s: The name of the control to display e.g. "Padding".
__('Show %s'), label);
return createElement(MenuItem, {
key: label,
icon: isSelected ? check : null,
isSelected: isSelected,
label: itemLabel,
onClick: () => {
if (isSelected) {
speak(sprintf(
// translators: %s: The name of the control being reset e.g. "Padding".
__('%s hidden and reset to default'), label), 'assertive');
} else {
speak(sprintf(
// translators: %s: The name of the control being reset e.g. "Padding".
__('%s is now visible'), label), 'assertive');
}
toggleItem(label);
},
role: "menuitemcheckbox"
}, label);
}));
};
const ToolsPanelHeader = (props, forwardedRef) => {
const {
areAllOptionalControlsHidden,
defaultControlsItemClassName,
dropdownMenuClassName,
hasMenuItems,
headingClassName,
headingLevel = 2,
label: labelText,
menuItems,
resetAll,
toggleItem,
dropdownMenuProps,
...headerProps
} = useToolsPanelHeader(props);
if (!labelText) {
return null;
}
const defaultItems = Object.entries(menuItems?.default || {});
const optionalItems = Object.entries(menuItems?.optional || {});
const dropDownMenuIcon = areAllOptionalControlsHidden ? plus : moreVertical;
const dropDownMenuLabelText = sprintf(
// translators: %s: The name of the tool e.g. "Color" or "Typography".
_x('%s options', 'Button label to reveal tool panel options'), labelText);
const dropdownMenuDescriptionText = areAllOptionalControlsHidden ? __('All options are currently hidden') : undefined;
const canResetAll = [...defaultItems, ...optionalItems].some(([, isSelected]) => isSelected);
return createElement(HStack, {
...headerProps,
ref: forwardedRef
}, createElement(Heading, {
level: headingLevel,
className: headingClassName
}, labelText), hasMenuItems && createElement(DropdownMenu, {
...dropdownMenuProps,
icon: dropDownMenuIcon,
label: dropDownMenuLabelText,
menuProps: {
className: dropdownMenuClassName
},
toggleProps: {
isSmall: true,
describedBy: dropdownMenuDescriptionText
}
}, () => createElement(Fragment, null, createElement(MenuGroup, {
label: labelText
}, createElement(DefaultControlsGroup, {
items: defaultItems,
toggleItem: toggleItem,
itemClassName: defaultControlsItemClassName
}), createElement(OptionalControlsGroup, {
items: optionalItems,
toggleItem: toggleItem
})), createElement(MenuGroup, null, createElement(MenuItem, {
"aria-disabled": !canResetAll
// @ts-expect-error - TODO: If this "tertiary" style is something we really want to allow on MenuItem,
// we should rename it and explicitly allow it as an official API. All the other Button variants
// don't make sense in a MenuItem context, and should be disallowed.
,
variant: 'tertiary',
onClick: () => {
if (canResetAll) {
resetAll();
speak(__('All options reset'), 'assertive');
}
}
}, __('Reset all'))))));
};
const ConnectedToolsPanelHeader = contextConnect(ToolsPanelHeader, 'ToolsPanelHeader');
export default ConnectedToolsPanelHeader;
//# sourceMappingURL=component.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,49 @@
/**
* WordPress dependencies
*/
import { useMemo } from '@wordpress/element';
/**
* Internal dependencies
*/
import * as styles from '../styles';
import { useToolsPanelContext } from '../context';
import { useContextSystem } from '../../context';
import { useCx } from '../../utils/hooks/use-cx';
export function useToolsPanelHeader(props) {
const {
className,
headingLevel = 2,
...otherProps
} = useContextSystem(props, 'ToolsPanelHeader');
const cx = useCx();
const classes = useMemo(() => {
return cx(styles.ToolsPanelHeader, className);
}, [className, cx]);
const dropdownMenuClassName = useMemo(() => {
return cx(styles.DropdownMenu);
}, [cx]);
const headingClassName = useMemo(() => {
return cx(styles.ToolsPanelHeading);
}, [cx]);
const defaultControlsItemClassName = useMemo(() => {
return cx(styles.DefaultControlsItem);
}, [cx]);
const {
menuItems,
hasMenuItems,
areAllOptionalControlsHidden
} = useToolsPanelContext();
return {
...otherProps,
areAllOptionalControlsHidden,
defaultControlsItemClassName,
dropdownMenuClassName,
hasMenuItems,
headingClassName,
headingLevel,
menuItems,
className: classes
};
}
//# sourceMappingURL=hook.js.map

View File

@@ -0,0 +1 @@
{"version":3,"names":["useMemo","styles","useToolsPanelContext","useContextSystem","useCx","useToolsPanelHeader","props","className","headingLevel","otherProps","cx","classes","ToolsPanelHeader","dropdownMenuClassName","DropdownMenu","headingClassName","ToolsPanelHeading","defaultControlsItemClassName","DefaultControlsItem","menuItems","hasMenuItems","areAllOptionalControlsHidden"],"sources":["@wordpress/components/src/tools-panel/tools-panel-header/hook.ts"],"sourcesContent":["/**\n * WordPress dependencies\n */\nimport { useMemo } from '@wordpress/element';\n\n/**\n * Internal dependencies\n */\nimport * as styles from '../styles';\nimport { useToolsPanelContext } from '../context';\nimport type { WordPressComponentProps } from '../../context';\nimport { useContextSystem } from '../../context';\nimport { useCx } from '../../utils/hooks/use-cx';\nimport type { ToolsPanelHeaderProps } from '../types';\n\nexport function useToolsPanelHeader(\n\tprops: WordPressComponentProps< ToolsPanelHeaderProps, 'h2' >\n) {\n\tconst {\n\t\tclassName,\n\t\theadingLevel = 2,\n\t\t...otherProps\n\t} = useContextSystem( props, 'ToolsPanelHeader' );\n\n\tconst cx = useCx();\n\tconst classes = useMemo( () => {\n\t\treturn cx( styles.ToolsPanelHeader, className );\n\t}, [ className, cx ] );\n\n\tconst dropdownMenuClassName = useMemo( () => {\n\t\treturn cx( styles.DropdownMenu );\n\t}, [ cx ] );\n\n\tconst headingClassName = useMemo( () => {\n\t\treturn cx( styles.ToolsPanelHeading );\n\t}, [ cx ] );\n\n\tconst defaultControlsItemClassName = useMemo( () => {\n\t\treturn cx( styles.DefaultControlsItem );\n\t}, [ cx ] );\n\n\tconst { menuItems, hasMenuItems, areAllOptionalControlsHidden } =\n\t\tuseToolsPanelContext();\n\n\treturn {\n\t\t...otherProps,\n\t\tareAllOptionalControlsHidden,\n\t\tdefaultControlsItemClassName,\n\t\tdropdownMenuClassName,\n\t\thasMenuItems,\n\t\theadingClassName,\n\t\theadingLevel,\n\t\tmenuItems,\n\t\tclassName: classes,\n\t};\n}\n"],"mappings":"AAAA;AACA;AACA;AACA,SAASA,OAAO,QAAQ,oBAAoB;;AAE5C;AACA;AACA;AACA,OAAO,KAAKC,MAAM,MAAM,WAAW;AACnC,SAASC,oBAAoB,QAAQ,YAAY;AAEjD,SAASC,gBAAgB,QAAQ,eAAe;AAChD,SAASC,KAAK,QAAQ,0BAA0B;AAGhD,OAAO,SAASC,mBAAmBA,CAClCC,KAA6D,EAC5D;EACD,MAAM;IACLC,SAAS;IACTC,YAAY,GAAG,CAAC;IAChB,GAAGC;EACJ,CAAC,GAAGN,gBAAgB,CAAEG,KAAK,EAAE,kBAAmB,CAAC;EAEjD,MAAMI,EAAE,GAAGN,KAAK,CAAC,CAAC;EAClB,MAAMO,OAAO,GAAGX,OAAO,CAAE,MAAM;IAC9B,OAAOU,EAAE,CAAET,MAAM,CAACW,gBAAgB,EAAEL,SAAU,CAAC;EAChD,CAAC,EAAE,CAAEA,SAAS,EAAEG,EAAE,CAAG,CAAC;EAEtB,MAAMG,qBAAqB,GAAGb,OAAO,CAAE,MAAM;IAC5C,OAAOU,EAAE,CAAET,MAAM,CAACa,YAAa,CAAC;EACjC,CAAC,EAAE,CAAEJ,EAAE,CAAG,CAAC;EAEX,MAAMK,gBAAgB,GAAGf,OAAO,CAAE,MAAM;IACvC,OAAOU,EAAE,CAAET,MAAM,CAACe,iBAAkB,CAAC;EACtC,CAAC,EAAE,CAAEN,EAAE,CAAG,CAAC;EAEX,MAAMO,4BAA4B,GAAGjB,OAAO,CAAE,MAAM;IACnD,OAAOU,EAAE,CAAET,MAAM,CAACiB,mBAAoB,CAAC;EACxC,CAAC,EAAE,CAAER,EAAE,CAAG,CAAC;EAEX,MAAM;IAAES,SAAS;IAAEC,YAAY;IAAEC;EAA6B,CAAC,GAC9DnB,oBAAoB,CAAC,CAAC;EAEvB,OAAO;IACN,GAAGO,UAAU;IACbY,4BAA4B;IAC5BJ,4BAA4B;IAC5BJ,qBAAqB;IACrBO,YAAY;IACZL,gBAAgB;IAChBP,YAAY;IACZW,SAAS;IACTZ,SAAS,EAAEI;EACZ,CAAC;AACF"}

View File

@@ -0,0 +1,2 @@
export { default } from './component';
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"names":["default"],"sources":["@wordpress/components/src/tools-panel/tools-panel-header/index.ts"],"sourcesContent":["export { default } from './component';\n"],"mappings":"AAAA,SAASA,OAAO,QAAQ,aAAa"}