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,237 @@
import { createElement, Fragment } from "react";
/**
* External dependencies
*/
// eslint-disable-next-line no-restricted-imports
import * as Ariakit from '@ariakit/react';
/**
* WordPress dependencies
*/
import { forwardRef, createContext, useContext, useMemo, cloneElement, isValidElement, useCallback } from '@wordpress/element';
import { isRTL } from '@wordpress/i18n';
import { check, chevronRightSmall } from '@wordpress/icons';
import { SVG, Circle } from '@wordpress/primitives';
/**
* Internal dependencies
*/
import { useContextSystem, contextConnect } from '../context';
import Icon from '../icon';
import * as Styled from './styles';
export const DropdownMenuContext = createContext(undefined);
export const DropdownMenuItem = forwardRef(function DropdownMenuItem({
prefix,
suffix,
children,
hideOnClick = true,
...props
}, ref) {
const dropdownMenuContext = useContext(DropdownMenuContext);
return createElement(Styled.DropdownMenuItem, {
ref: ref,
...props,
accessibleWhenDisabled: true,
hideOnClick: hideOnClick,
store: dropdownMenuContext?.store
}, createElement(Styled.ItemPrefixWrapper, null, prefix), createElement(Styled.DropdownMenuItemContentWrapper, null, createElement(Styled.DropdownMenuItemChildrenWrapper, null, children), suffix && createElement(Styled.ItemSuffixWrapper, null, suffix)));
});
export const DropdownMenuCheckboxItem = forwardRef(function DropdownMenuCheckboxItem({
suffix,
children,
hideOnClick = false,
...props
}, ref) {
const dropdownMenuContext = useContext(DropdownMenuContext);
return createElement(Styled.DropdownMenuCheckboxItem, {
ref: ref,
...props,
accessibleWhenDisabled: true,
hideOnClick: hideOnClick,
store: dropdownMenuContext?.store
}, createElement(Ariakit.MenuItemCheck, {
store: dropdownMenuContext?.store,
render: createElement(Styled.ItemPrefixWrapper, null)
// Override some ariakit inline styles
,
style: {
width: 'auto',
height: 'auto'
}
}, createElement(Icon, {
icon: check,
size: 24
})), createElement(Styled.DropdownMenuItemContentWrapper, null, createElement(Styled.DropdownMenuItemChildrenWrapper, null, children), suffix && createElement(Styled.ItemSuffixWrapper, null, suffix)));
});
const radioCheck = createElement(SVG, {
xmlns: "http://www.w3.org/2000/svg",
viewBox: "0 0 24 24"
}, createElement(Circle, {
cx: 12,
cy: 12,
r: 3
}));
export const DropdownMenuRadioItem = forwardRef(function DropdownMenuRadioItem({
suffix,
children,
hideOnClick = false,
...props
}, ref) {
const dropdownMenuContext = useContext(DropdownMenuContext);
return createElement(Styled.DropdownMenuRadioItem, {
ref: ref,
...props,
accessibleWhenDisabled: true,
hideOnClick: hideOnClick,
store: dropdownMenuContext?.store
}, createElement(Ariakit.MenuItemCheck, {
store: dropdownMenuContext?.store,
render: createElement(Styled.ItemPrefixWrapper, null)
// Override some ariakit inline styles
,
style: {
width: 'auto',
height: 'auto'
}
}, createElement(Icon, {
icon: radioCheck,
size: 24
})), createElement(Styled.DropdownMenuItemContentWrapper, null, createElement(Styled.DropdownMenuItemChildrenWrapper, null, children), suffix && createElement(Styled.ItemSuffixWrapper, null, suffix)));
});
export const DropdownMenuGroup = forwardRef(function DropdownMenuGroup(props, ref) {
const dropdownMenuContext = useContext(DropdownMenuContext);
return createElement(Styled.DropdownMenuGroup, {
ref: ref,
...props,
store: dropdownMenuContext?.store
});
});
const UnconnectedDropdownMenu = (props, ref) => {
var _props$placement;
const {
// Store props
open,
defaultOpen = false,
onOpenChange,
placement,
// Menu trigger props
trigger,
// Menu props
gutter,
children,
shift,
modal = true,
// From internal components context
variant,
// Rest
...otherProps
} = useContextSystem(props, 'DropdownMenu');
const parentContext = useContext(DropdownMenuContext);
const computedDirection = isRTL() ? 'rtl' : 'ltr';
// If an explicit value for the `placement` prop is not passed,
// apply a default placement of `bottom-start` for the root dropdown,
// and of `right-start` for nested dropdowns.
let computedPlacement = (_props$placement = props.placement) !== null && _props$placement !== void 0 ? _props$placement : parentContext?.store ? 'right-start' : 'bottom-start';
// Swap left/right in case of RTL direction
if (computedDirection === 'rtl') {
if (/right/.test(computedPlacement)) {
computedPlacement = computedPlacement.replace('right', 'left');
} else if (/left/.test(computedPlacement)) {
computedPlacement = computedPlacement.replace('left', 'right');
}
}
const dropdownMenuStore = Ariakit.useMenuStore({
parent: parentContext?.store,
open,
defaultOpen,
placement: computedPlacement,
focusLoop: true,
setOpen(willBeOpen) {
onOpenChange?.(willBeOpen);
},
rtl: computedDirection === 'rtl'
});
const contextValue = useMemo(() => ({
store: dropdownMenuStore,
variant
}), [dropdownMenuStore, variant]);
// Extract the side from the applied placement — useful for animations.
const appliedPlacementSide = dropdownMenuStore.useState('placement').split('-')[0];
if (dropdownMenuStore.parent && !(isValidElement(trigger) && DropdownMenuItem === trigger.type)) {
// eslint-disable-next-line no-console
console.warn('For nested DropdownMenus, the `trigger` should always be a `DropdownMenuItem`.');
}
const hideOnEscape = useCallback(event => {
// Pressing Escape can cause unexpected consequences (ie. exiting
// full screen mode on MacOs, close parent modals...).
event.preventDefault();
// Returning `true` causes the menu to hide.
return true;
}, []);
const wrapperProps = useMemo(() => ({
dir: computedDirection,
style: {
direction: computedDirection
}
}), [computedDirection]);
return createElement(Fragment, null, createElement(Ariakit.MenuButton, {
ref: ref,
store: dropdownMenuStore,
render: dropdownMenuStore.parent ? cloneElement(trigger, {
// Add submenu arrow, unless a `suffix` is explicitly specified
suffix: createElement(Fragment, null, trigger.props.suffix, createElement(Styled.SubmenuChevronIcon, {
"aria-hidden": "true",
icon: chevronRightSmall,
size: 24,
preserveAspectRatio: "xMidYMid slice"
}))
}) : trigger
}), createElement(Styled.DropdownMenu, {
...otherProps,
modal: modal,
store: dropdownMenuStore
// Root menu has an 8px distance from its trigger,
// otherwise 0 (which causes the submenu to slightly overlap)
,
gutter: gutter !== null && gutter !== void 0 ? gutter : dropdownMenuStore.parent ? 0 : 8
// Align nested menu by the same (but opposite) amount
// as the menu container's padding.
,
shift: shift !== null && shift !== void 0 ? shift : dropdownMenuStore.parent ? -4 : 0,
hideOnHoverOutside: false,
"data-side": appliedPlacementSide,
variant: variant,
wrapperProps: wrapperProps,
hideOnEscape: hideOnEscape,
unmountOnHide: true
}, createElement(DropdownMenuContext.Provider, {
value: contextValue
}, children)));
};
export const DropdownMenu = contextConnect(UnconnectedDropdownMenu, 'DropdownMenu');
export const DropdownMenuSeparator = forwardRef(function DropdownMenuSeparator(props, ref) {
const dropdownMenuContext = useContext(DropdownMenuContext);
return createElement(Styled.DropdownMenuSeparator, {
ref: ref,
...props,
store: dropdownMenuContext?.store,
variant: dropdownMenuContext?.variant
});
});
export const DropdownMenuItemLabel = forwardRef(function DropdownMenuItemLabel(props, ref) {
return createElement(Styled.DropdownMenuItemLabel, {
numberOfLines: 1,
ref: ref,
...props
});
});
export const DropdownMenuItemHelpText = forwardRef(function DropdownMenuItemHelpText(props, ref) {
return createElement(Styled.DropdownMenuItemHelpText, {
numberOfLines: 2,
ref: ref,
...props
});
});
//# sourceMappingURL=index.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=types.js.map

View File

@@ -0,0 +1 @@
{"version":3,"names":[],"sources":["@wordpress/components/src/dropdown-menu-v2/types.ts"],"sourcesContent":["/**\n * External dependencies\n */\n// eslint-disable-next-line no-restricted-imports\nimport type * as Ariakit from '@ariakit/react';\nimport type { Placement } from '@floating-ui/react-dom';\n\nexport interface DropdownMenuContext {\n\t/**\n\t * The ariakit store shared across all DropdownMenu subcomponents.\n\t */\n\tstore: Ariakit.MenuStore;\n\t/**\n\t * The variant used by the underlying menu popover.\n\t */\n\tvariant?: 'toolbar';\n}\n\nexport interface DropdownMenuProps {\n\t/**\n\t * The trigger button.\n\t */\n\ttrigger: React.ReactElement;\n\t/**\n\t * The contents of the dropdown.\n\t */\n\tchildren?: React.ReactNode;\n\t/**\n\t * The open state of the dropdown menu when it is initially rendered. Use when\n\t * not wanting to control its open state.\n\t *\n\t * @default false\n\t */\n\tdefaultOpen?: boolean;\n\t/**\n\t * The controlled open state of the dropdown menu. Must be used in conjunction\n\t * with `onOpenChange`.\n\t */\n\topen?: boolean;\n\t/**\n\t * Event handler called when the open state of the dropdown menu changes.\n\t */\n\tonOpenChange?: ( open: boolean ) => void;\n\t/**\n\t * The modality of the dropdown menu. When set to true, interaction with\n\t * outside elements will be disabled and only menu content will be visible to\n\t * screen readers.\n\t *\n\t * @default true\n\t */\n\tmodal?: boolean;\n\t/**\n\t * The placement of the dropdown menu popover.\n\t *\n\t * @default 'bottom-start' for root-level menus, 'right-start' for nested menus\n\t */\n\tplacement?: Placement;\n\t/**\n\t * The distance between the popover and the anchor element.\n\t *\n\t * @default 8 for root-level menus, 16 for nested menus\n\t */\n\tgutter?: number;\n\t/**\n\t * The skidding of the popover along the anchor element. Can be set to\n\t * negative values to make the popover shift to the opposite side.\n\t *\n\t * @default 0 for root-level menus, -8 for nested menus\n\t */\n\tshift?: number;\n\t/**\n\t * Determines whether the menu popover will be hidden when the user presses\n\t * the Escape key.\n\t *\n\t * @default `( event ) => { event.preventDefault(); return true; }`\n\t */\n\thideOnEscape?:\n\t\t| boolean\n\t\t| ( (\n\t\t\t\tevent: KeyboardEvent | React.KeyboardEvent< Element >\n\t\t ) => boolean );\n}\n\nexport interface DropdownMenuGroupProps {\n\t/**\n\t * The contents of the dropdown menu group.\n\t */\n\tchildren: React.ReactNode;\n}\n\nexport interface DropdownMenuItemProps {\n\t/**\n\t * The contents of the menu item.\n\t */\n\tchildren: React.ReactNode;\n\t/**\n\t * The contents of the menu item's prefix.\n\t */\n\tprefix?: React.ReactNode;\n\t/**\n\t * The contents of the menu item's suffix.\n\t */\n\tsuffix?: React.ReactNode;\n\t/**\n\t * Whether to hide the parent menu when the item is clicked.\n\t *\n\t * @default true\n\t */\n\thideOnClick?: boolean;\n\t/**\n\t * Determines if the element is disabled.\n\t */\n\tdisabled?: boolean;\n}\n\nexport interface DropdownMenuCheckboxItemProps\n\textends Omit< DropdownMenuItemProps, 'prefix' | 'hideOnClick' > {\n\t/**\n\t * Whether to hide the dropdown menu when the item is clicked.\n\t *\n\t * @default false\n\t */\n\thideOnClick?: boolean;\n\t/**\n\t * The checkbox menu item's name.\n\t */\n\tname: string;\n\t/**\n\t * The checkbox item's value, useful when using multiple checkbox menu items\n\t * associated to the same `name`.\n\t */\n\tvalue?: string;\n\t/**\n\t * The controlled checked state of the checkbox menu item.\n\t */\n\tchecked?: boolean;\n\t/**\n\t * The checked state of the checkbox menu item when it is initially rendered.\n\t * Use when not wanting to control its checked state.\n\t */\n\tdefaultChecked?: boolean;\n\t/**\n\t * Event handler called when the checked state of the checkbox menu item changes.\n\t */\n\tonChange?: ( event: React.ChangeEvent< HTMLInputElement > ) => void;\n}\n\nexport interface DropdownMenuRadioItemProps\n\textends Omit< DropdownMenuItemProps, 'prefix' | 'hideOnClick' > {\n\t/**\n\t * Whether to hide the dropdown menu when the item is clicked.\n\t *\n\t * @default false\n\t */\n\thideOnClick?: boolean;\n\t/**\n\t * The radio item's name.\n\t */\n\tname: string;\n\t/**\n\t * The radio item's value.\n\t */\n\tvalue: string | number;\n\t/**\n\t * The controlled checked state of the radio menu item.\n\t */\n\tchecked?: boolean;\n\t/**\n\t * The checked state of the radio menu item when it is initially rendered.\n\t * Use when not wanting to control its checked state.\n\t */\n\tdefaultChecked?: boolean;\n\t/**\n\t * Event handler called when the checked radio menu item changes.\n\t */\n\tonChange?: ( event: React.ChangeEvent< HTMLInputElement > ) => void;\n}\n\nexport interface DropdownMenuSeparatorProps {}\n"],"mappings":""}