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,139 @@
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.createDirectEntry = exports.LinkPicker = void 0;
var _react = require("react");
var _reactNative = require("react-native");
var _clipboard = _interopRequireDefault(require("@react-native-clipboard/clipboard"));
var _element = require("@wordpress/element");
var _i18n = require("@wordpress/i18n");
var _components = require("@wordpress/components");
var _url = require("@wordpress/url");
var _icons = require("@wordpress/icons");
var _compose = require("@wordpress/compose");
var _linkPickerResults = _interopRequireDefault(require("./link-picker-results"));
var _navBar = _interopRequireDefault(require("../bottom-sheet/nav-bar"));
var _styles = _interopRequireDefault(require("./styles.scss"));
/**
* External dependencies
*/
/**
* WordPress dependencies
*/
/**
* Internal dependencies
*/
// This creates a search suggestion for adding a url directly.
const createDirectEntry = value => {
let type = 'URL';
const protocol = (0, _url.getProtocol)(value)?.toLowerCase() || '';
if (protocol.includes('mailto')) {
type = 'mailto';
}
if (protocol.includes('tel')) {
type = 'tel';
}
if (value?.startsWith('#')) {
type = 'internal';
}
return {
isDirectEntry: true,
title: value,
url: type === 'URL' ? (0, _url.prependHTTP)(value) : value,
type
};
};
exports.createDirectEntry = createDirectEntry;
const getURLFromClipboard = async () => {
const text = await _clipboard.default.getString();
return !!text && (0, _url.isURL)(text) ? text : '';
};
const LinkPicker = ({
value: initialValue,
onLinkPicked,
onCancel: cancel
}) => {
const [value, setValue] = (0, _element.useState)(initialValue);
const [clipboardUrl, setClipboardUrl] = (0, _element.useState)('');
const directEntry = createDirectEntry(value);
// The title of a direct entry is displayed as the raw input value, but if we
// are replacing empty text, we want to use the generated url.
const pickLink = ({
title,
url,
isDirectEntry
}) => {
onLinkPicked({
title: isDirectEntry ? url : title,
url
});
};
const onSubmit = () => {
pickLink(directEntry);
};
const clear = () => {
setValue('');
setClipboardUrl('');
};
const omniCellStyle = (0, _compose.usePreferredColorSchemeStyle)(_styles.default.omniCell, _styles.default.omniCellDark);
const iconStyle = (0, _compose.usePreferredColorSchemeStyle)(_styles.default.icon, _styles.default.iconDark);
(0, _element.useEffect)(() => {
getURLFromClipboard().then(setClipboardUrl).catch(() => setClipboardUrl(''));
}, []);
// TODO: Localize the accessibility label.
// TODO: Decide on if `LinkSuggestionItemCell` with `isDirectEntry` makes sense.
return (0, _react.createElement)(_reactNative.SafeAreaView, {
style: _styles.default.safeArea
}, (0, _react.createElement)(_navBar.default, null, (0, _react.createElement)(_navBar.default.DismissButton, {
onPress: cancel
}), (0, _react.createElement)(_navBar.default.Heading, null, (0, _i18n.__)('Link to')), (0, _react.createElement)(_navBar.default.ApplyButton, {
onPress: onSubmit
})), (0, _react.createElement)(_reactNative.View, {
style: _styles.default.contentContainer
}, (0, _react.createElement)(_components.BottomSheet.Cell, {
icon: _icons.link,
style: omniCellStyle,
valueStyle: _styles.default.omniInput,
value: value,
placeholder: (0, _i18n.__)('Search or type URL'),
autoCapitalize: "none",
autoCorrect: false,
keyboardType: "url",
onChangeValue: setValue,
onSubmit: onSubmit
/* eslint-disable-next-line jsx-a11y/no-autofocus */,
autoFocus: true,
separatorType: "none"
}, value !== '' && (0, _react.createElement)(_reactNative.TouchableOpacity, {
onPress: clear,
style: _styles.default.clearIcon
}, (0, _react.createElement)(_components.Icon, {
icon: _icons.cancelCircleFilled,
fill: iconStyle.color,
size: 24
}))), !!clipboardUrl && clipboardUrl !== value && (0, _react.createElement)(_components.BottomSheet.LinkSuggestionItemCell, {
accessible: true,
accessibilityLabel: (0, _i18n.sprintf)( /* translators: Copy URL from the clipboard, https://sample.url */
(0, _i18n.__)('Copy URL from the clipboard, %s'), clipboardUrl),
suggestion: {
type: 'clipboard',
url: clipboardUrl,
isDirectEntry: true
},
onLinkPicked: pickLink
}), !!value && (0, _react.createElement)(_linkPickerResults.default, {
query: value,
onLinkPicked: pickLink,
directEntry: directEntry
})));
};
exports.LinkPicker = LinkPicker;
//# sourceMappingURL=index.native.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,147 @@
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = LinkPickerResults;
var _react = require("react");
var _reactNative = require("react-native");
var _components = require("@wordpress/components");
var _compose = require("@wordpress/compose");
var _element = require("@wordpress/element");
var _data = require("@wordpress/data");
var _styles = _interopRequireDefault(require("./styles.scss"));
/**
* External dependencies
*/
/**
* WordPress dependencies
*/
/**
* Internal dependencies
*/
const PER_PAGE = 20;
const REQUEST_DEBOUNCE_DELAY = 400;
const MINIMUM_QUERY_SIZE = 2;
const meetsThreshold = query => MINIMUM_QUERY_SIZE <= query.length;
function LinkPickerResults({
query,
onLinkPicked,
directEntry
}) {
const [links, setLinks] = (0, _element.useState)([directEntry]);
const [hasAllSuggestions, setHasAllSuggestions] = (0, _element.useState)(false);
const nextPage = (0, _element.useRef)(1);
const pendingRequest = (0, _element.useRef)();
const clearRequest = () => {
pendingRequest.current = null;
};
// A stable debounced function to fetch suggestions and append.
const {
fetchMoreSuggestions
} = (0, _data.useSelect)(select => {
const {
getSettings
} = select('core/block-editor');
const fetchLinkSuggestions = async ({
search
}) => {
if (meetsThreshold(search)) {
return await getSettings().__experimentalFetchLinkSuggestions(search, {
page: nextPage.current,
type: 'post',
perPage: PER_PAGE
});
}
};
const fetchMore = async ({
query: search,
links: currentSuggestions
}) => {
// Return early if we've already detected the end of data or we are
// already awaiting a response.
if (hasAllSuggestions || pendingRequest.current) {
return;
}
const request = fetchLinkSuggestions({
search
});
pendingRequest.current = request;
const suggestions = await request;
// Only update links for the most recent request.
if (suggestions && request === pendingRequest.current) {
// Since we don't have the response header, we check if the results
// are truncated to determine we've reached the end.
if (suggestions.length < PER_PAGE) {
setHasAllSuggestions(true);
}
setLinks([...currentSuggestions, ...suggestions]);
nextPage.current++;
}
clearRequest();
};
return {
fetchMoreSuggestions: (0, _compose.debounce)(fetchMore, REQUEST_DEBOUNCE_DELAY)
};
// Disable eslint rule for now, to avoid introducing a regression
// (see https://github.com/WordPress/gutenberg/pull/23922#discussion_r1170634879).
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
// Prevent setting state when unmounted.
(0, _element.useEffect)(() => clearRequest, []);
// Any time the query changes, we reset pagination.
(0, _element.useEffect)(() => {
clearRequest();
nextPage.current = 1;
setHasAllSuggestions(false);
setLinks([directEntry]);
fetchMoreSuggestions({
query,
links: [directEntry]
});
// Disable reason: deferring this refactor to the native team.
// see https://github.com/WordPress/gutenberg/pull/41166
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [query]);
const onEndReached = () => fetchMoreSuggestions({
query,
links
});
const spinner = !hasAllSuggestions && meetsThreshold(query) && (0, _react.createElement)(_reactNative.View, {
style: _styles.default.spinner,
testID: "link-picker-loading"
}, (0, _react.createElement)(_reactNative.ActivityIndicator, {
animating: true
}));
return (0, _react.createElement)(_components.BottomSheetConsumer, null, ({
listProps
}) => (0, _react.createElement)(_reactNative.FlatList, {
data: links,
keyboardShouldPersistTaps: "always",
renderItem: ({
item
}) => (0, _react.createElement)(_components.BottomSheet.LinkSuggestionItemCell, {
suggestion: item,
onLinkPicked: onLinkPicked
}),
keyExtractor: ({
url,
type
}) => `${url}-${type}`,
onEndReached: onEndReached,
onEndReachedThreshold: 0.1,
initialNumToRender: PER_PAGE,
ListFooterComponent: spinner,
...listProps,
contentContainerStyle: [...listProps.contentContainerStyle, _styles.default.list]
}));
}
//# sourceMappingURL=link-picker-results.native.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,71 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _react = require("react");
var _reactNative = require("react-native");
var _native = require("@react-navigation/native");
var _element = require("@wordpress/element");
var _ = require("./");
/**
* External dependencies
*/
/**
* WordPress dependencies
*/
/**
* Internal dependencies
*/
const LinkPickerScreen = ({
returnScreenName
}) => {
const navigation = (0, _native.useNavigation)();
const route = (0, _native.useRoute)();
const navigateToLinkTimeoutRef = (0, _element.useRef)(null);
const navigateBackTimeoutRef = (0, _element.useRef)(null);
const onLinkPicked = ({
url,
title
}) => {
_reactNative.Keyboard.dismiss();
navigateToLinkTimeoutRef.current = setTimeout(() => {
navigation.navigate(returnScreenName, {
inputValue: url,
text: title
});
}, 100);
};
const onCancel = () => {
_reactNative.Keyboard.dismiss();
navigateBackTimeoutRef.current = setTimeout(() => {
navigation.goBack();
}, 100);
};
(0, _element.useEffect)(() => {
return () => {
clearTimeout(navigateToLinkTimeoutRef.current);
clearTimeout(navigateBackTimeoutRef.current);
};
}, []);
const {
inputValue
} = route.params;
return (0, _element.useMemo)(() => {
return (0, _react.createElement)(_.LinkPicker, {
value: inputValue,
onLinkPicked: onLinkPicked,
onCancel: onCancel
});
// Disable reason: deferring this refactor to the native team.
// see https://github.com/WordPress/gutenberg/pull/41166
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [inputValue]);
};
var _default = LinkPickerScreen;
exports.default = _default;
//# sourceMappingURL=link-picker-screen.native.js.map

View File

@@ -0,0 +1 @@
{"version":3,"names":["_reactNative","require","_native","_element","_","LinkPickerScreen","returnScreenName","navigation","useNavigation","route","useRoute","navigateToLinkTimeoutRef","useRef","navigateBackTimeoutRef","onLinkPicked","url","title","Keyboard","dismiss","current","setTimeout","navigate","inputValue","text","onCancel","goBack","useEffect","clearTimeout","params","useMemo","_react","createElement","LinkPicker","value","_default","exports","default"],"sources":["@wordpress/components/src/mobile/link-picker/link-picker-screen.native.js"],"sourcesContent":["/**\n * External dependencies\n */\nimport { Keyboard } from 'react-native';\nimport { useNavigation, useRoute } from '@react-navigation/native';\n\n/**\n * WordPress dependencies\n */\nimport { useEffect, useMemo, useRef } from '@wordpress/element';\n\n/**\n * Internal dependencies\n */\nimport { LinkPicker } from './';\n\nconst LinkPickerScreen = ( { returnScreenName } ) => {\n\tconst navigation = useNavigation();\n\tconst route = useRoute();\n\tconst navigateToLinkTimeoutRef = useRef( null );\n\tconst navigateBackTimeoutRef = useRef( null );\n\n\tconst onLinkPicked = ( { url, title } ) => {\n\t\tKeyboard.dismiss();\n\t\tnavigateToLinkTimeoutRef.current = setTimeout( () => {\n\t\t\tnavigation.navigate( returnScreenName, {\n\t\t\t\tinputValue: url,\n\t\t\t\ttext: title,\n\t\t\t} );\n\t\t}, 100 );\n\t};\n\n\tconst onCancel = () => {\n\t\tKeyboard.dismiss();\n\t\tnavigateBackTimeoutRef.current = setTimeout( () => {\n\t\t\tnavigation.goBack();\n\t\t}, 100 );\n\t};\n\n\tuseEffect( () => {\n\t\treturn () => {\n\t\t\tclearTimeout( navigateToLinkTimeoutRef.current );\n\t\t\tclearTimeout( navigateBackTimeoutRef.current );\n\t\t};\n\t}, [] );\n\n\tconst { inputValue } = route.params;\n\treturn useMemo( () => {\n\t\treturn (\n\t\t\t<LinkPicker\n\t\t\t\tvalue={ inputValue }\n\t\t\t\tonLinkPicked={ onLinkPicked }\n\t\t\t\tonCancel={ onCancel }\n\t\t\t/>\n\t\t);\n\t\t// Disable reason: deferring this refactor to the native team.\n\t\t// see https://github.com/WordPress/gutenberg/pull/41166\n\t\t// eslint-disable-next-line react-hooks/exhaustive-deps\n\t}, [ inputValue ] );\n};\n\nexport default LinkPickerScreen;\n"],"mappings":";;;;;;;AAGA,IAAAA,YAAA,GAAAC,OAAA;AACA,IAAAC,OAAA,GAAAD,OAAA;AAKA,IAAAE,QAAA,GAAAF,OAAA;AAKA,IAAAG,CAAA,GAAAH,OAAA;AAdA;AACA;AACA;;AAIA;AACA;AACA;;AAGA;AACA;AACA;;AAGA,MAAMI,gBAAgB,GAAGA,CAAE;EAAEC;AAAiB,CAAC,KAAM;EACpD,MAAMC,UAAU,GAAG,IAAAC,qBAAa,EAAC,CAAC;EAClC,MAAMC,KAAK,GAAG,IAAAC,gBAAQ,EAAC,CAAC;EACxB,MAAMC,wBAAwB,GAAG,IAAAC,eAAM,EAAE,IAAK,CAAC;EAC/C,MAAMC,sBAAsB,GAAG,IAAAD,eAAM,EAAE,IAAK,CAAC;EAE7C,MAAME,YAAY,GAAGA,CAAE;IAAEC,GAAG;IAAEC;EAAM,CAAC,KAAM;IAC1CC,qBAAQ,CAACC,OAAO,CAAC,CAAC;IAClBP,wBAAwB,CAACQ,OAAO,GAAGC,UAAU,CAAE,MAAM;MACpDb,UAAU,CAACc,QAAQ,CAAEf,gBAAgB,EAAE;QACtCgB,UAAU,EAAEP,GAAG;QACfQ,IAAI,EAAEP;MACP,CAAE,CAAC;IACJ,CAAC,EAAE,GAAI,CAAC;EACT,CAAC;EAED,MAAMQ,QAAQ,GAAGA,CAAA,KAAM;IACtBP,qBAAQ,CAACC,OAAO,CAAC,CAAC;IAClBL,sBAAsB,CAACM,OAAO,GAAGC,UAAU,CAAE,MAAM;MAClDb,UAAU,CAACkB,MAAM,CAAC,CAAC;IACpB,CAAC,EAAE,GAAI,CAAC;EACT,CAAC;EAED,IAAAC,kBAAS,EAAE,MAAM;IAChB,OAAO,MAAM;MACZC,YAAY,CAAEhB,wBAAwB,CAACQ,OAAQ,CAAC;MAChDQ,YAAY,CAAEd,sBAAsB,CAACM,OAAQ,CAAC;IAC/C,CAAC;EACF,CAAC,EAAE,EAAG,CAAC;EAEP,MAAM;IAAEG;EAAW,CAAC,GAAGb,KAAK,CAACmB,MAAM;EACnC,OAAO,IAAAC,gBAAO,EAAE,MAAM;IACrB,OACC,IAAAC,MAAA,CAAAC,aAAA,EAAC3B,CAAA,CAAA4B,UAAU;MACVC,KAAK,EAAGX,UAAY;MACpBR,YAAY,EAAGA,YAAc;MAC7BU,QAAQ,EAAGA;IAAU,CACrB,CAAC;IAEH;IACA;IACA;EACD,CAAC,EAAE,CAAEF,UAAU,CAAG,CAAC;AACpB,CAAC;AAAC,IAAAY,QAAA,GAEa7B,gBAAgB;AAAA8B,OAAA,CAAAC,OAAA,GAAAF,QAAA"}