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,300 @@
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _react = require("react");
var _classnames = _interopRequireDefault(require("classnames"));
var _i18n = require("@wordpress/i18n");
var _element = require("@wordpress/element");
var _compose = require("@wordpress/compose");
var _a11y = require("@wordpress/a11y");
var _icons = require("@wordpress/icons");
var _styles = require("./styles");
var _tokenInput = _interopRequireDefault(require("../form-token-field/token-input"));
var _suggestionsList = _interopRequireDefault(require("../form-token-field/suggestions-list"));
var _baseControl = _interopRequireDefault(require("../base-control"));
var _button = _interopRequireDefault(require("../button"));
var _flex = require("../flex");
var _withFocusOutside = _interopRequireDefault(require("../higher-order/with-focus-outside"));
var _hooks = require("../utils/hooks");
var _strings = require("../utils/strings");
var _useDeprecatedProps = require("../utils/use-deprecated-props");
/**
* External dependencies
*/
/**
* WordPress dependencies
*/
/**
* Internal dependencies
*/
const noop = () => {};
const DetectOutside = (0, _withFocusOutside.default)(class extends _element.Component {
handleFocusOutside(event) {
this.props.onFocusOutside(event);
}
render() {
return this.props.children;
}
});
const getIndexOfMatchingSuggestion = (selectedSuggestion, matchingSuggestions) => selectedSuggestion === null ? -1 : matchingSuggestions.indexOf(selectedSuggestion);
/**
* `ComboboxControl` is an enhanced version of a [`SelectControl`](../select-control/README.md) with the addition of
* being able to search for options using a search input.
*
* ```jsx
* import { ComboboxControl } from '@wordpress/components';
* import { useState } from '@wordpress/element';
*
* const options = [
* {
* value: 'small',
* label: 'Small',
* },
* {
* value: 'normal',
* label: 'Normal',
* },
* {
* value: 'large',
* label: 'Large',
* },
* ];
*
* function MyComboboxControl() {
* const [ fontSize, setFontSize ] = useState();
* const [ filteredOptions, setFilteredOptions ] = useState( options );
* return (
* <ComboboxControl
* label="Font Size"
* value={ fontSize }
* onChange={ setFontSize }
* options={ filteredOptions }
* onFilterValueChange={ ( inputValue ) =>
* setFilteredOptions(
* options.filter( ( option ) =>
* option.label
* .toLowerCase()
* .startsWith( inputValue.toLowerCase() )
* )
* )
* }
* />
* );
* }
* ```
*/
function ComboboxControl(props) {
var _currentOption$label;
const {
__nextHasNoMarginBottom = false,
__next40pxDefaultSize = false,
value: valueProp,
label,
options,
onChange: onChangeProp,
onFilterValueChange = noop,
hideLabelFromVision,
help,
allowReset = true,
className,
messages = {
selected: (0, _i18n.__)('Item selected.')
},
__experimentalRenderItem
} = (0, _useDeprecatedProps.useDeprecated36pxDefaultSizeProp)(props, 'wp.components.ComboboxControl');
const [value, setValue] = (0, _hooks.useControlledValue)({
value: valueProp,
onChange: onChangeProp
});
const currentOption = options.find(option => option.value === value);
const currentLabel = (_currentOption$label = currentOption?.label) !== null && _currentOption$label !== void 0 ? _currentOption$label : '';
// Use a custom prefix when generating the `instanceId` to avoid having
// duplicate input IDs when rendering this component and `FormTokenField`
// in the same page (see https://github.com/WordPress/gutenberg/issues/42112).
const instanceId = (0, _compose.useInstanceId)(ComboboxControl, 'combobox-control');
const [selectedSuggestion, setSelectedSuggestion] = (0, _element.useState)(currentOption || null);
const [isExpanded, setIsExpanded] = (0, _element.useState)(false);
const [inputHasFocus, setInputHasFocus] = (0, _element.useState)(false);
const [inputValue, setInputValue] = (0, _element.useState)('');
const inputContainer = (0, _element.useRef)(null);
const matchingSuggestions = (0, _element.useMemo)(() => {
const startsWithMatch = [];
const containsMatch = [];
const match = (0, _strings.normalizeTextString)(inputValue);
options.forEach(option => {
const index = (0, _strings.normalizeTextString)(option.label).indexOf(match);
if (index === 0) {
startsWithMatch.push(option);
} else if (index > 0) {
containsMatch.push(option);
}
});
return startsWithMatch.concat(containsMatch);
}, [inputValue, options]);
const onSuggestionSelected = newSelectedSuggestion => {
setValue(newSelectedSuggestion.value);
(0, _a11y.speak)(messages.selected, 'assertive');
setSelectedSuggestion(newSelectedSuggestion);
setInputValue('');
setIsExpanded(false);
};
const handleArrowNavigation = (offset = 1) => {
const index = getIndexOfMatchingSuggestion(selectedSuggestion, matchingSuggestions);
let nextIndex = index + offset;
if (nextIndex < 0) {
nextIndex = matchingSuggestions.length - 1;
} else if (nextIndex >= matchingSuggestions.length) {
nextIndex = 0;
}
setSelectedSuggestion(matchingSuggestions[nextIndex]);
setIsExpanded(true);
};
const onKeyDown = event => {
let preventDefault = false;
if (event.defaultPrevented ||
// Ignore keydowns from IMEs
event.nativeEvent.isComposing ||
// Workaround for Mac Safari where the final Enter/Backspace of an IME composition
// is `isComposing=false`, even though it's technically still part of the composition.
// These can only be detected by keyCode.
event.keyCode === 229) {
return;
}
switch (event.code) {
case 'Enter':
if (selectedSuggestion) {
onSuggestionSelected(selectedSuggestion);
preventDefault = true;
}
break;
case 'ArrowUp':
handleArrowNavigation(-1);
preventDefault = true;
break;
case 'ArrowDown':
handleArrowNavigation(1);
preventDefault = true;
break;
case 'Escape':
setIsExpanded(false);
setSelectedSuggestion(null);
preventDefault = true;
break;
default:
break;
}
if (preventDefault) {
event.preventDefault();
}
};
const onBlur = () => {
setInputHasFocus(false);
};
const onFocus = () => {
setInputHasFocus(true);
setIsExpanded(true);
onFilterValueChange('');
setInputValue('');
};
const onFocusOutside = () => {
setIsExpanded(false);
};
const onInputChange = event => {
const text = event.value;
setInputValue(text);
onFilterValueChange(text);
if (inputHasFocus) {
setIsExpanded(true);
}
};
const handleOnReset = () => {
setValue(null);
inputContainer.current?.focus();
};
// Update current selections when the filter input changes.
(0, _element.useEffect)(() => {
const hasMatchingSuggestions = matchingSuggestions.length > 0;
const hasSelectedMatchingSuggestions = getIndexOfMatchingSuggestion(selectedSuggestion, matchingSuggestions) > 0;
if (hasMatchingSuggestions && !hasSelectedMatchingSuggestions) {
// If the current selection isn't present in the list of suggestions, then automatically select the first item from the list of suggestions.
setSelectedSuggestion(matchingSuggestions[0]);
}
}, [matchingSuggestions, selectedSuggestion]);
// Announcements.
(0, _element.useEffect)(() => {
const hasMatchingSuggestions = matchingSuggestions.length > 0;
if (isExpanded) {
const message = hasMatchingSuggestions ? (0, _i18n.sprintf)( /* translators: %d: number of results. */
(0, _i18n._n)('%d result found, use up and down arrow keys to navigate.', '%d results found, use up and down arrow keys to navigate.', matchingSuggestions.length), matchingSuggestions.length) : (0, _i18n.__)('No results.');
(0, _a11y.speak)(message, 'polite');
}
}, [matchingSuggestions, isExpanded]);
// Disable reason: There is no appropriate role which describes the
// input container intended accessible usability.
// TODO: Refactor click detection to use blur to stop propagation.
/* eslint-disable jsx-a11y/no-static-element-interactions */
return (0, _react.createElement)(DetectOutside, {
onFocusOutside: onFocusOutside
}, (0, _react.createElement)(_baseControl.default, {
__nextHasNoMarginBottom: __nextHasNoMarginBottom,
className: (0, _classnames.default)(className, 'components-combobox-control'),
label: label,
id: `components-form-token-input-${instanceId}`,
hideLabelFromVision: hideLabelFromVision,
help: help
}, (0, _react.createElement)("div", {
className: "components-combobox-control__suggestions-container",
tabIndex: -1,
onKeyDown: onKeyDown
}, (0, _react.createElement)(_styles.InputWrapperFlex, {
__next40pxDefaultSize: __next40pxDefaultSize
}, (0, _react.createElement)(_flex.FlexBlock, null, (0, _react.createElement)(_tokenInput.default, {
className: "components-combobox-control__input",
instanceId: instanceId,
ref: inputContainer,
value: isExpanded ? inputValue : currentLabel,
onFocus: onFocus,
onBlur: onBlur,
isExpanded: isExpanded,
selectedSuggestionIndex: getIndexOfMatchingSuggestion(selectedSuggestion, matchingSuggestions),
onChange: onInputChange
})), allowReset && (0, _react.createElement)(_flex.FlexItem, null, (0, _react.createElement)(_button.default, {
className: "components-combobox-control__reset",
icon: _icons.closeSmall,
disabled: !value,
onClick: handleOnReset,
label: (0, _i18n.__)('Reset')
}))), isExpanded && (0, _react.createElement)(_suggestionsList.default, {
instanceId: instanceId
// The empty string for `value` here is not actually used, but is
// just a quick way to satisfy the TypeScript requirements of SuggestionsList.
// See: https://github.com/WordPress/gutenberg/pull/47581/files#r1091089330
,
match: {
label: inputValue,
value: ''
},
displayTransform: suggestion => suggestion.label,
suggestions: matchingSuggestions,
selectedIndex: getIndexOfMatchingSuggestion(selectedSuggestion, matchingSuggestions),
onHover: setSelectedSuggestion,
onSelect: onSuggestionSelected,
scrollIntoView: true,
__experimentalRenderItem: __experimentalRenderItem
}))));
/* eslint-enable jsx-a11y/no-static-element-interactions */
}
var _default = ComboboxControl;
exports.default = _default;
//# sourceMappingURL=index.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,30 @@
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.InputWrapperFlex = void 0;
var _base = _interopRequireDefault(require("@emotion/styled/base"));
var _react = require("@emotion/react");
var _flex = require("../flex");
var _space = require("../utils/space");
/**
* External dependencies
*/
/**
* Internal dependencies
*/
const deprecatedDefaultSize = ({
__next40pxDefaultSize
}) => !__next40pxDefaultSize && /*#__PURE__*/(0, _react.css)("height:28px;padding-left:", (0, _space.space)(1), ";padding-right:", (0, _space.space)(1), ";" + (process.env.NODE_ENV === "production" ? "" : ";label:deprecatedDefaultSize;"), process.env.NODE_ENV === "production" ? "" : "/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIkB3b3JkcHJlc3MvY29tcG9uZW50cy9zcmMvY29tYm9ib3gtY29udHJvbC9zdHlsZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBaUJJIiwiZmlsZSI6IkB3b3JkcHJlc3MvY29tcG9uZW50cy9zcmMvY29tYm9ib3gtY29udHJvbC9zdHlsZXMudHMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEV4dGVybmFsIGRlcGVuZGVuY2llc1xuICovXG5pbXBvcnQgc3R5bGVkIGZyb20gJ0BlbW90aW9uL3N0eWxlZCc7XG5pbXBvcnQgeyBjc3MgfSBmcm9tICdAZW1vdGlvbi9yZWFjdCc7XG5cbi8qKlxuICogSW50ZXJuYWwgZGVwZW5kZW5jaWVzXG4gKi9cbmltcG9ydCB7IEZsZXggfSBmcm9tICcuLi9mbGV4JztcbmltcG9ydCB7IHNwYWNlIH0gZnJvbSAnLi4vdXRpbHMvc3BhY2UnO1xuaW1wb3J0IHR5cGUgeyBDb21ib2JveENvbnRyb2xQcm9wcyB9IGZyb20gJy4vdHlwZXMnO1xuXG5jb25zdCBkZXByZWNhdGVkRGVmYXVsdFNpemUgPSAoIHtcblx0X19uZXh0NDBweERlZmF1bHRTaXplLFxufTogUGljazwgQ29tYm9ib3hDb250cm9sUHJvcHMsICdfX25leHQ0MHB4RGVmYXVsdFNpemUnID4gKSA9PlxuXHQhIF9fbmV4dDQwcHhEZWZhdWx0U2l6ZSAmJlxuXHRjc3NgXG5cdFx0aGVpZ2h0OiAyOHB4OyAvLyAzMHB4IC0gMnB4IHZlcnRpY2FsIGJvcmRlcnMgb24gcGFyZW50IGNvbnRhaW5lclxuXHRcdHBhZGRpbmctbGVmdDogJHsgc3BhY2UoIDEgKSB9O1xuXHRcdHBhZGRpbmctcmlnaHQ6ICR7IHNwYWNlKCAxICkgfTtcblx0YDtcblxuZXhwb3J0IGNvbnN0IElucHV0V3JhcHBlckZsZXggPSBzdHlsZWQoIEZsZXggKWBcblx0aGVpZ2h0OiAzOHB4OyAvLyA0MHB4IC0gMnB4IHZlcnRpY2FsIGJvcmRlcnMgb24gcGFyZW50IGNvbnRhaW5lclxuXHRwYWRkaW5nLWxlZnQ6ICR7IHNwYWNlKCAyICkgfTtcblx0cGFkZGluZy1yaWdodDogJHsgc3BhY2UoIDIgKSB9O1xuXG5cdCR7IGRlcHJlY2F0ZWREZWZhdWx0U2l6ZSB9XG5gO1xuIl19 */");
const InputWrapperFlex = ( /*#__PURE__*/0, _base.default)(_flex.Flex, process.env.NODE_ENV === "production" ? {
target: "evuatpg0"
} : {
target: "evuatpg0",
label: "InputWrapperFlex"
})("height:38px;padding-left:", (0, _space.space)(2), ";padding-right:", (0, _space.space)(2), ";", deprecatedDefaultSize, ";" + (process.env.NODE_ENV === "production" ? "" : "/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIkB3b3JkcHJlc3MvY29tcG9uZW50cy9zcmMvY29tYm9ib3gtY29udHJvbC9zdHlsZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBdUI4QyIsImZpbGUiOiJAd29yZHByZXNzL2NvbXBvbmVudHMvc3JjL2NvbWJvYm94LWNvbnRyb2wvc3R5bGVzLnRzIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBFeHRlcm5hbCBkZXBlbmRlbmNpZXNcbiAqL1xuaW1wb3J0IHN0eWxlZCBmcm9tICdAZW1vdGlvbi9zdHlsZWQnO1xuaW1wb3J0IHsgY3NzIH0gZnJvbSAnQGVtb3Rpb24vcmVhY3QnO1xuXG4vKipcbiAqIEludGVybmFsIGRlcGVuZGVuY2llc1xuICovXG5pbXBvcnQgeyBGbGV4IH0gZnJvbSAnLi4vZmxleCc7XG5pbXBvcnQgeyBzcGFjZSB9IGZyb20gJy4uL3V0aWxzL3NwYWNlJztcbmltcG9ydCB0eXBlIHsgQ29tYm9ib3hDb250cm9sUHJvcHMgfSBmcm9tICcuL3R5cGVzJztcblxuY29uc3QgZGVwcmVjYXRlZERlZmF1bHRTaXplID0gKCB7XG5cdF9fbmV4dDQwcHhEZWZhdWx0U2l6ZSxcbn06IFBpY2s8IENvbWJvYm94Q29udHJvbFByb3BzLCAnX19uZXh0NDBweERlZmF1bHRTaXplJyA+ICkgPT5cblx0ISBfX25leHQ0MHB4RGVmYXVsdFNpemUgJiZcblx0Y3NzYFxuXHRcdGhlaWdodDogMjhweDsgLy8gMzBweCAtIDJweCB2ZXJ0aWNhbCBib3JkZXJzIG9uIHBhcmVudCBjb250YWluZXJcblx0XHRwYWRkaW5nLWxlZnQ6ICR7IHNwYWNlKCAxICkgfTtcblx0XHRwYWRkaW5nLXJpZ2h0OiAkeyBzcGFjZSggMSApIH07XG5cdGA7XG5cbmV4cG9ydCBjb25zdCBJbnB1dFdyYXBwZXJGbGV4ID0gc3R5bGVkKCBGbGV4IClgXG5cdGhlaWdodDogMzhweDsgLy8gNDBweCAtIDJweCB2ZXJ0aWNhbCBib3JkZXJzIG9uIHBhcmVudCBjb250YWluZXJcblx0cGFkZGluZy1sZWZ0OiAkeyBzcGFjZSggMiApIH07XG5cdHBhZGRpbmctcmlnaHQ6ICR7IHNwYWNlKCAyICkgfTtcblxuXHQkeyBkZXByZWNhdGVkRGVmYXVsdFNpemUgfVxuYDtcbiJdfQ== */"));
exports.InputWrapperFlex = InputWrapperFlex;
//# sourceMappingURL=styles.js.map

View File

@@ -0,0 +1 @@
{"version":3,"names":["_react","require","_flex","_space","deprecatedDefaultSize","__next40pxDefaultSize","css","space","process","env","NODE_ENV","InputWrapperFlex","_base","default","Flex","target","label","exports"],"sources":["@wordpress/components/src/combobox-control/styles.ts"],"sourcesContent":["/**\n * External dependencies\n */\nimport styled from '@emotion/styled';\nimport { css } from '@emotion/react';\n\n/**\n * Internal dependencies\n */\nimport { Flex } from '../flex';\nimport { space } from '../utils/space';\nimport type { ComboboxControlProps } from './types';\n\nconst deprecatedDefaultSize = ( {\n\t__next40pxDefaultSize,\n}: Pick< ComboboxControlProps, '__next40pxDefaultSize' > ) =>\n\t! __next40pxDefaultSize &&\n\tcss`\n\t\theight: 28px; // 30px - 2px vertical borders on parent container\n\t\tpadding-left: ${ space( 1 ) };\n\t\tpadding-right: ${ space( 1 ) };\n\t`;\n\nexport const InputWrapperFlex = styled( Flex )`\n\theight: 38px; // 40px - 2px vertical borders on parent container\n\tpadding-left: ${ space( 2 ) };\n\tpadding-right: ${ space( 2 ) };\n\n\t${ deprecatedDefaultSize }\n`;\n"],"mappings":";;;;;;;;AAIA,IAAAA,MAAA,GAAAC,OAAA;AAKA,IAAAC,KAAA,GAAAD,OAAA;AACA,IAAAE,MAAA,GAAAF,OAAA;AAVA;AACA;AACA;;AAIA;AACA;AACA;;AAKA,MAAMG,qBAAqB,GAAGA,CAAE;EAC/BC;AACsD,CAAC,KACvD,CAAEA,qBAAqB,qBACvBC,UAAG,+BAEe,IAAAC,YAAK,EAAE,CAAE,CAAC,qBACT,IAAAA,YAAK,EAAE,CAAE,CAAC,SAAAC,OAAA,CAAAC,GAAA,CAAAC,QAAA,2DAAAF,OAAA,CAAAC,GAAA,CAAAC,QAAA,m6CAC5B;AAEK,MAAMC,gBAAgB,GAAG,kBAAAC,KAAA,CAAAC,OAAA,EAAQC,UAAI,EAAAN,OAAA,CAAAC,GAAA,CAAAC,QAAA;EAAAK,MAAA;AAAA;EAAAA,MAAA;EAAAC,KAAA;AAAA,CAAC,CAAC,8BAE5B,IAAAT,YAAK,EAAE,CAAE,CAAC,qBACT,IAAAA,YAAK,EAAE,CAAE,CAAC,OAEzBH,qBAAqB,SAAAI,OAAA,CAAAC,GAAA,CAAAC,QAAA,w6CACxB;AAACO,OAAA,CAAAN,gBAAA,GAAAA,gBAAA"}

View File

@@ -0,0 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
//# sourceMappingURL=types.js.map

View File

@@ -0,0 +1 @@
{"version":3,"names":[],"sources":["@wordpress/components/src/combobox-control/types.ts"],"sourcesContent":["/**\n * Internal dependencies\n */\nimport type { BaseControlProps } from '../base-control/types';\n\nexport type ComboboxControlOption = {\n\tlabel: string;\n\tvalue: string;\n\t[ key: string ]: any;\n};\n\nexport type ComboboxControlProps = Pick<\n\tBaseControlProps,\n\t| '__nextHasNoMarginBottom'\n\t| 'className'\n\t| 'label'\n\t| 'hideLabelFromVision'\n\t| 'help'\n> & {\n\t/**\n\t * Custom renderer invoked for each option in the suggestion list.\n\t * The render prop receives as its argument an object containing, under the `item` key,\n\t * the single option's data (directly from the array of data passed to the `options` prop).\n\t */\n\t__experimentalRenderItem?: ( args: {\n\t\titem: ComboboxControlOption;\n\t} ) => React.ReactNode;\n\t/**\n\t * Deprecated. Use `__next40pxDefaultSize` instead.\n\t *\n\t * @default false\n\t * @deprecated\n\t */\n\t__next36pxDefaultSize?: boolean;\n\t/**\n\t * Start opting into the larger default height that will become the default size in a future version.\n\t *\n\t * @default false\n\t */\n\t__next40pxDefaultSize?: boolean;\n\t/**\n\t * Show a reset button to clear the input.\n\t *\n\t * @default true\n\t */\n\tallowReset?: boolean;\n\t/**\n\t * Customizable UI messages.\n\t */\n\tmessages?: {\n\t\t/**\n\t\t * The message to announce to screen readers when a suggestion is selected.\n\t\t *\n\t\t * @default `__( 'Item selected.' )`\n\t\t */\n\t\tselected: string;\n\t};\n\t/**\n\t * Function called with the selected value changes.\n\t */\n\tonChange?: ( value: ComboboxControlProps[ 'value' ] ) => void;\n\t/**\n\t * Function called when the control's search input value changes. The argument contains the next input value.\n\t *\n\t * @default noop\n\t */\n\tonFilterValueChange?: ( value: string ) => void;\n\t/**\n\t * The options that can be chosen from.\n\t */\n\toptions: ComboboxControlOption[];\n\t/**\n\t * The current value of the control.\n\t */\n\tvalue?: string | null;\n};\n"],"mappings":""}