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>
161 lines
5.0 KiB
JavaScript
161 lines
5.0 KiB
JavaScript
import { createElement } from "react";
|
|
/**
|
|
* External dependencies
|
|
*/
|
|
// eslint-disable-next-line no-restricted-imports
|
|
import * as Ariakit from '@ariakit/react';
|
|
|
|
/**
|
|
* WordPress dependencies
|
|
*/
|
|
import { useInstanceId } from '@wordpress/compose';
|
|
import { useLayoutEffect, useMemo, useRef } from '@wordpress/element';
|
|
|
|
/**
|
|
* Internal dependencies
|
|
*/
|
|
|
|
import { TabsContext } from './context';
|
|
import { Tab } from './tab';
|
|
import { TabList } from './tablist';
|
|
import { TabPanel } from './tabpanel';
|
|
function Tabs({
|
|
selectOnMove = true,
|
|
initialTabId,
|
|
orientation = 'horizontal',
|
|
onSelect,
|
|
children,
|
|
selectedTabId
|
|
}) {
|
|
const instanceId = useInstanceId(Tabs, 'tabs');
|
|
const store = Ariakit.useTabStore({
|
|
selectOnMove,
|
|
orientation,
|
|
defaultSelectedId: initialTabId && `${instanceId}-${initialTabId}`,
|
|
setSelectedId: selectedId => {
|
|
const strippedDownId = typeof selectedId === 'string' ? selectedId.replace(`${instanceId}-`, '') : selectedId;
|
|
onSelect?.(strippedDownId);
|
|
},
|
|
selectedId: selectedTabId && `${instanceId}-${selectedTabId}`
|
|
});
|
|
const isControlled = selectedTabId !== undefined;
|
|
const {
|
|
items,
|
|
selectedId
|
|
} = store.useState();
|
|
const {
|
|
setSelectedId,
|
|
move
|
|
} = store;
|
|
|
|
// Keep track of whether tabs have been populated. This is used to prevent
|
|
// certain effects from firing too early while tab data and relevant
|
|
// variables are undefined during the initial render.
|
|
const tabsHavePopulated = useRef(false);
|
|
if (items.length > 0) {
|
|
tabsHavePopulated.current = true;
|
|
}
|
|
const selectedTab = items.find(item => item.id === selectedId);
|
|
const firstEnabledTab = items.find(item => {
|
|
// Ariakit internally refers to disabled tabs as `dimmed`.
|
|
return !item.dimmed;
|
|
});
|
|
const initialTab = items.find(item => item.id === `${instanceId}-${initialTabId}`);
|
|
|
|
// Handle selecting the initial tab.
|
|
useLayoutEffect(() => {
|
|
if (isControlled) {
|
|
return;
|
|
}
|
|
|
|
// Wait for the denoted initial tab to be declared before making a
|
|
// selection. This ensures that if a tab is declared lazily it can
|
|
// still receive initial selection, as well as ensuring no tab is
|
|
// selected if an invalid `initialTabId` is provided.
|
|
if (initialTabId && !initialTab) {
|
|
return;
|
|
}
|
|
|
|
// If the currently selected tab is missing (i.e. removed from the DOM),
|
|
// fall back to the initial tab or the first enabled tab if there is
|
|
// one. Otherwise, no tab should be selected.
|
|
if (!items.find(item => item.id === selectedId)) {
|
|
if (initialTab && !initialTab.dimmed) {
|
|
setSelectedId(initialTab?.id);
|
|
return;
|
|
}
|
|
if (firstEnabledTab) {
|
|
setSelectedId(firstEnabledTab.id);
|
|
} else if (tabsHavePopulated.current) {
|
|
setSelectedId(null);
|
|
}
|
|
}
|
|
}, [firstEnabledTab, initialTab, initialTabId, isControlled, items, selectedId, setSelectedId]);
|
|
|
|
// Handle the currently selected tab becoming disabled.
|
|
useLayoutEffect(() => {
|
|
if (!selectedTab?.dimmed) {
|
|
return;
|
|
}
|
|
|
|
// In controlled mode, we trust that disabling tabs is done
|
|
// intentionally, and don't select a new tab automatically.
|
|
if (isControlled) {
|
|
setSelectedId(null);
|
|
return;
|
|
}
|
|
|
|
// If the currently selected tab becomes disabled, fall back to the
|
|
// `initialTabId` if possible. Otherwise select the first
|
|
// enabled tab (if there is one).
|
|
if (initialTab && !initialTab.dimmed) {
|
|
setSelectedId(initialTab.id);
|
|
return;
|
|
}
|
|
if (firstEnabledTab) {
|
|
setSelectedId(firstEnabledTab.id);
|
|
}
|
|
}, [firstEnabledTab, initialTab, isControlled, selectedTab?.dimmed, setSelectedId]);
|
|
|
|
// Clear `selectedId` if the active tab is removed from the DOM in controlled mode.
|
|
useLayoutEffect(() => {
|
|
if (!isControlled) {
|
|
return;
|
|
}
|
|
|
|
// Once the tabs have populated, if the `selectedTabId` still can't be
|
|
// found, clear the selection.
|
|
if (tabsHavePopulated.current && !!selectedTabId && !selectedTab) {
|
|
setSelectedId(null);
|
|
}
|
|
}, [isControlled, selectedId, selectedTab, selectedTabId, setSelectedId]);
|
|
|
|
// In controlled mode, make sure browser focus follows the selected tab if
|
|
// the selection is changed while a tab is already being focused.
|
|
useLayoutEffect(() => {
|
|
if (!isControlled || !selectOnMove) {
|
|
return;
|
|
}
|
|
const currentItem = items.find(item => item.id === selectedId);
|
|
const activeElement = currentItem?.element?.ownerDocument.activeElement;
|
|
const tabsHasFocus = items.some(item => {
|
|
return activeElement && activeElement === item.element;
|
|
});
|
|
if (activeElement && tabsHasFocus && selectedId !== activeElement.id) {
|
|
move(selectedId);
|
|
}
|
|
}, [isControlled, items, move, selectOnMove, selectedId]);
|
|
const contextValue = useMemo(() => ({
|
|
store,
|
|
instanceId
|
|
}), [store, instanceId]);
|
|
return createElement(TabsContext.Provider, {
|
|
value: contextValue
|
|
}, children);
|
|
}
|
|
Tabs.TabList = TabList;
|
|
Tabs.Tab = Tab;
|
|
Tabs.TabPanel = TabPanel;
|
|
Tabs.Context = TabsContext;
|
|
export default Tabs;
|
|
//# sourceMappingURL=index.js.map
|