@nocobase/client-v2 2.2.0-beta.9 → 3.0.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/es/APIClient.d.ts +1 -3
- package/es/BaseApplication.d.ts +1 -0
- package/es/acl/ACLProvider.d.ts +10 -0
- package/es/authRedirect.d.ts +5 -0
- package/es/collection-field-interface/CollectionFieldInterface.d.ts +1 -0
- package/es/collection-field-interface/CollectionFieldInterfaceManager.d.ts +1 -0
- package/es/components/AttachmentUpload.d.ts +51 -0
- package/es/components/category-tabs/SortableCategoryTabs.d.ts +55 -0
- package/es/components/category-tabs/index.d.ts +9 -0
- package/es/components/form/ScanInput/useCodeScanner.d.ts +3 -2
- package/es/components/form/TypedVariableInput.d.ts +31 -5
- package/es/components/form/filter/CollectionFilter.d.ts +2 -0
- package/es/components/form/filter/CollectionFilterItem.d.ts +11 -1
- package/es/components/form/filter/CollectionFilterPanel.d.ts +2 -0
- package/es/components/form/filter/index.d.ts +2 -0
- package/es/components/form/filter/useFilterActionProps.d.ts +2 -0
- package/es/components/index.d.ts +2 -0
- package/es/flow/actions/index.d.ts +1 -1
- package/es/flow/actions/linkageRules.d.ts +3 -0
- package/es/flow/admin-shell/admin-layout/AdminLayoutMenuModels.d.ts +1 -0
- package/es/flow/admin-shell/admin-layout/NocoBaseLogo.d.ts +10 -0
- package/es/flow/components/FieldAssignExactDatePicker.d.ts +1 -0
- package/es/flow/components/FieldAssignValueInput.d.ts +9 -0
- package/es/flow/components/FlowRoute.d.ts +2 -2
- package/es/flow/components/RunJSValueEditor.d.ts +1 -0
- package/es/flow/components/filter/FilterGroup.d.ts +1 -0
- package/es/flow/components/filter/VariableFilterItem.d.ts +6 -1
- package/es/flow/index.d.ts +1 -0
- package/es/flow/internal/registerDeviceTypeContext.d.ts +10 -0
- package/es/flow/models/base/ActionModelCore.d.ts +4 -2
- package/es/flow/models/base/BlockGridModel.d.ts +3 -0
- package/es/flow/models/base/PageModel/PageModel.d.ts +12 -0
- package/es/flow/models/base/PageModel/PageModelTabBar.d.ts +22 -0
- package/es/flow/models/base/PageModel/PageTabModel.d.ts +12 -0
- package/es/flow/models/blocks/form/QuickEditFormModel.d.ts +17 -2
- package/es/flow/models/blocks/table/TableColumnModel.d.ts +1 -0
- package/es/flow/models/fields/DateTimeFieldModel/dateLimit.d.ts +15 -7
- package/es/flow-compat/fieldValidationConstants.d.ts +1 -1
- package/es/flow-compat/routeTypes.d.ts +1 -0
- package/es/index.d.ts +8 -1
- package/es/index.mjs +190 -141
- package/es/nocobase-buildin-plugin/currentUserAuthStatus.d.ts +11 -0
- package/es/nocobase-buildin-plugin/index.d.ts +9 -0
- package/es/settings-app/SettingsApplication.d.ts +18 -0
- package/es/settings-app/SettingsBrand.d.ts +16 -0
- package/es/settings-app/SettingsBuildInPlugin.d.ts +9 -0
- package/es/settings-app/SettingsDocumentRedirect.d.ts +10 -0
- package/es/settings-app/SettingsGroupNav.d.ts +17 -0
- package/es/settings-app/SettingsPluginSettingsManager.d.ts +14 -0
- package/es/settings-app/SettingsRouterManager.d.ts +15 -0
- package/es/settings-app/SettingsSearch.d.ts +17 -0
- package/es/settings-app/SettingsShell.d.ts +10 -0
- package/es/settings-app/isSettingsApp.d.ts +18 -0
- package/es/settings-app/runtimePaths.d.ts +18 -0
- package/es/settings-app/settingsDocumentPath.d.ts +14 -0
- package/es/settings-app/settingsTheme.d.ts +87 -0
- package/es/settings-app/useSettingsThemeConfig.d.ts +22 -0
- package/es/settings-center/AdminSettingsLayout.d.ts +2 -2
- package/es/settings-center/groups.d.ts +66 -0
- package/es/settings-center/useSettingsGroups.d.ts +36 -0
- package/es/settings-center/useSettingsSearch.d.ts +35 -0
- package/es/settings-center/utils.d.ts +24 -0
- package/es/theme/index.d.ts +1 -0
- package/es/theme/itemActive.d.ts +24 -0
- package/es/theme/type.d.ts +9 -0
- package/es/utils/getRouteRuntimeVersion.d.ts +23 -0
- package/es/utils/index.d.ts +1 -0
- package/lib/index.js +195 -146
- package/lib/locale/languageCodes.js +3 -1
- package/package.json +9 -7
- package/src/APIClient.ts +1 -10
- package/src/Application.tsx +4 -0
- package/src/BaseApplication.tsx +11 -6
- package/src/__tests__/app.test.tsx +73 -0
- package/src/__tests__/authRedirect.test.ts +137 -0
- package/src/__tests__/browserChecker.test.ts +185 -6
- package/src/__tests__/getRouteRuntimeVersion.test.ts +68 -0
- package/src/__tests__/mockSettingsApplication.ts +29 -0
- package/src/__tests__/nocobase-buildin-plugin-auth.test.tsx +79 -10
- package/src/__tests__/plugin-manager.test.tsx +7 -6
- package/src/__tests__/settings-application.test.ts +164 -0
- package/src/__tests__/settings-auth-routes.test.ts +71 -0
- package/src/__tests__/settings-center.test.tsx +38 -37
- package/src/__tests__/settings-layout-root.test.tsx +260 -0
- package/src/__tests__/settings-runtime-paths.test.ts +99 -0
- package/src/__tests__/settings-shell.test.tsx +200 -0
- package/src/acl/ACLProvider.tsx +21 -0
- package/src/authRedirect.ts +72 -3
- package/src/collection-field-interface/CollectionFieldInterface.ts +1 -0
- package/src/collection-field-interface/CollectionFieldInterfaceManager.ts +1 -0
- package/src/collection-manager/field-validation.ts +1 -1
- package/src/components/AttachmentUpload.tsx +275 -0
- package/src/components/README.md +7 -1
- package/src/components/README.zh-CN.md +6 -1
- package/src/components/category-tabs/SortableCategoryTabs.tsx +210 -0
- package/src/components/category-tabs/index.ts +10 -0
- package/src/components/form/JsonTextArea.tsx +4 -0
- package/src/components/form/ScanInput/CodeScanner.tsx +79 -35
- package/src/components/form/ScanInput/__tests__/CodeScanner.test.tsx +101 -0
- package/src/components/form/ScanInput/__tests__/useCodeScanner.test.tsx +284 -11
- package/src/components/form/ScanInput/useCodeScanner.ts +332 -24
- package/src/components/form/TypedVariableInput.tsx +452 -101
- package/src/components/form/__tests__/TypedVariableInput.test.tsx +364 -12
- package/src/components/form/filter/CollectionFilter.tsx +4 -0
- package/src/components/form/filter/CollectionFilterItem.tsx +32 -7
- package/src/components/form/filter/CollectionFilterPanel.tsx +4 -0
- package/src/components/form/filter/__tests__/CollectionFilterItem.test.tsx +38 -0
- package/src/components/form/filter/__tests__/useFilterActionProps.test.tsx +95 -0
- package/src/components/form/filter/index.ts +2 -0
- package/src/components/form/filter/useFilterActionProps.ts +37 -6
- package/src/components/index.ts +2 -0
- package/src/flow/FlowPage.tsx +9 -1
- package/src/flow/__tests__/FlowPage.test.tsx +50 -3
- package/src/flow/__tests__/FlowRoute.test.tsx +128 -10
- package/src/flow/__tests__/PluginFlowEngine.test.ts +58 -0
- package/src/flow/actions/__tests__/actionLinkageRules.forkProps.test.ts +314 -0
- package/src/flow/actions/__tests__/dataScopeFormValueClear.test.ts +102 -0
- package/src/flow/actions/__tests__/linkageRules.tab.test.ts +171 -0
- package/src/flow/actions/__tests__/linkageRulesRefresh.test.ts +7 -3
- package/src/flow/actions/index.ts +2 -0
- package/src/flow/actions/linkageRules.tsx +111 -16
- package/src/flow/actions/linkageRulesRefresh.tsx +2 -6
- package/src/flow/admin-shell/admin-layout/AdminLayoutComponent.tsx +1 -57
- package/src/flow/admin-shell/admin-layout/NocoBaseLogo.tsx +77 -0
- package/src/flow/admin-shell/admin-layout/TopbarActionsBar.tsx +37 -14
- package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutComponent.test.tsx +253 -6
- package/src/flow/admin-shell/admin-layout/__tests__/TopbarActionsBar.test.tsx +77 -13
- package/src/flow/common/Markdown/style.ts +4 -0
- package/src/flow/components/BlockItemCard.tsx +2 -2
- package/src/flow/components/FieldAssignExactDatePicker.tsx +25 -11
- package/src/flow/components/FieldAssignValueInput.tsx +91 -20
- package/src/flow/components/FlowRoute.tsx +78 -32
- package/src/flow/components/RunJSValueEditor.tsx +9 -1
- package/src/flow/components/__tests__/FieldAssignValueInput.context.test.tsx +216 -0
- package/src/flow/components/filter/FilterGroup.tsx +33 -5
- package/src/flow/components/filter/VariableFilterItem.tsx +158 -10
- package/src/flow/components/filter/__tests__/FilterGroup.test.tsx +45 -0
- package/src/flow/components/filter/__tests__/VariableFilterItem.leftMetaTree.test.tsx +9 -0
- package/src/flow/components/filter/__tests__/VariableFilterItem.test.tsx +70 -1
- package/src/flow/index.ts +3 -0
- package/src/flow/internal/registerDeviceTypeContext.ts +39 -0
- package/src/flow/models/base/ActionModelCore.tsx +11 -2
- package/src/flow/models/base/BlockGridModel.tsx +26 -0
- package/src/flow/models/base/CollectionBlockModel.tsx +6 -2
- package/src/flow/models/base/PageModel/PageModel.tsx +387 -53
- package/src/flow/models/base/PageModel/PageModelTabBar.tsx +114 -0
- package/src/flow/models/base/PageModel/PageTabModel.tsx +249 -18
- package/src/flow/models/base/PageModel/__tests__/PageModel.test.ts +790 -10
- package/src/flow/models/base/PageModel/__tests__/PageModelTabBar.module-isolation.test.tsx +130 -0
- package/src/flow/models/base/PageModel/__tests__/PageModelTabBar.test.tsx +118 -0
- package/src/flow/models/base/PageModel/__tests__/PageTabModel.test.ts +1075 -7
- package/src/flow/models/base/__tests__/ActionModelCore.render.test.tsx +49 -0
- package/src/flow/models/base/__tests__/BlockGridModel.selectSceneActivation.test.ts +124 -0
- package/src/flow/models/base/__tests__/CollectionBlockModel.addAppends.test.ts +41 -0
- package/src/flow/models/blocks/filter-form/FilterFormBlockModel.tsx +28 -3
- package/src/flow/models/blocks/filter-form/FilterFormItemModel.tsx +27 -12
- package/src/flow/models/blocks/filter-form/__tests__/FilterFormItemModel.defineChildren.test.ts +36 -0
- package/src/flow/models/blocks/filter-form/__tests__/defaultValues.wiring.test.ts +66 -4
- package/src/flow/models/blocks/form/QuickEditFormModel.tsx +189 -36
- package/src/flow/models/blocks/form/__tests__/QuickEditFormModel.quickEdit.test.ts +350 -2
- package/src/flow/models/blocks/js-block/JSBlock.tsx +1 -1
- package/src/flow/models/blocks/table/TableActionsColumnModel.tsx +2 -1
- package/src/flow/models/blocks/table/TableBlockModel.tsx +4 -2
- package/src/flow/models/blocks/table/TableColumnModel.tsx +30 -1
- package/src/flow/models/blocks/table/__tests__/TableBlockModel.quickEditRefresh.test.ts +12 -0
- package/src/flow/models/blocks/table/__tests__/TableColumnModel.test.tsx +33 -0
- package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/PopupSubTableFieldModel.tsx +2 -1
- package/src/flow/models/fields/DateTimeFieldModel/__tests__/DateTimeNoTzFieldModel.dateLimit.test.tsx +149 -0
- package/src/flow/models/fields/DateTimeFieldModel/dateLimit.ts +149 -113
- package/src/flow/models/fields/JsonFieldModel.tsx +31 -8
- package/src/flow/models/fields/VariableFieldFormModel.tsx +3 -3
- package/src/flow/models/fields/__tests__/JsonFieldModel.test.ts +82 -0
- package/src/flow/models/fields/__tests__/VariableFieldFormModel.test.tsx +51 -0
- package/src/flow/models/fields/mobile-components/MobileLazySelect.tsx +20 -10
- package/src/flow/models/fields/mobile-components/MobileSelect.tsx +24 -12
- package/src/flow/models/topbar/TopbarActionModel.tsx +52 -180
- package/src/flow/utils/dataScopeFormValueClear.ts +4 -3
- package/src/flow-compat/fieldValidationConstants.ts +1 -1
- package/src/flow-compat/routeTypes.ts +1 -0
- package/src/index.ts +14 -1
- package/src/locale/languageCodes.ts +3 -1
- package/src/nocobase-buildin-plugin/currentUserAuthStatus.ts +58 -0
- package/src/nocobase-buildin-plugin/index.tsx +110 -70
- package/src/settings-app/SettingsApplication.ts +31 -0
- package/src/settings-app/SettingsBrand.tsx +108 -0
- package/src/settings-app/SettingsBuildInPlugin.ts +10 -0
- package/src/settings-app/SettingsDocumentRedirect.tsx +26 -0
- package/src/settings-app/SettingsGroupNav.tsx +124 -0
- package/src/settings-app/SettingsPluginSettingsManager.ts +38 -0
- package/src/settings-app/SettingsRouterManager.ts +92 -0
- package/src/settings-app/SettingsSearch.tsx +238 -0
- package/src/settings-app/SettingsShell.tsx +167 -0
- package/src/settings-app/isSettingsApp.ts +21 -0
- package/src/settings-app/runtimePaths.ts +104 -0
- package/src/settings-app/settingsDocumentPath.ts +66 -0
- package/src/settings-app/settingsTheme.ts +301 -0
- package/src/settings-app/useSettingsThemeConfig.ts +100 -0
- package/src/settings-center/AdminSettingsLayout.tsx +122 -141
- package/src/settings-center/SystemSettingsPage.tsx +4 -170
- package/src/settings-center/groups.ts +108 -0
- package/src/settings-center/useSettingsGroups.ts +105 -0
- package/src/settings-center/useSettingsSearch.ts +173 -0
- package/src/settings-center/utils.tsx +50 -1
- package/src/theme/index.tsx +1 -0
- package/src/theme/itemActive.ts +31 -0
- package/src/theme/type.ts +10 -0
- package/src/utils/getRouteRuntimeVersion.ts +185 -0
- package/src/utils/index.tsx +1 -0
|
@@ -33,6 +33,7 @@ import { commonConditionHandler, ConditionBuilder } from '../../../components/Co
|
|
|
33
33
|
import { TextAreaWithContextSelector } from '../../../components/TextAreaWithContextSelector';
|
|
34
34
|
import { confirmUnsavedChangesHandler } from './closeGuard';
|
|
35
35
|
import { BasePageTabModel } from './PageTabModel';
|
|
36
|
+
import { FilteredPageTabBar, NO_ACTIVE_PAGE_TAB_KEY } from './PageModelTabBar';
|
|
36
37
|
|
|
37
38
|
type PageModelStructure = {
|
|
38
39
|
subModels: {
|
|
@@ -49,6 +50,54 @@ type PageModelContextWithRoute = {
|
|
|
49
50
|
currentRoute?: CurrentRouteWithTabs | null;
|
|
50
51
|
};
|
|
51
52
|
|
|
53
|
+
type RequestedTabKey = {
|
|
54
|
+
key?: string;
|
|
55
|
+
source: 'url' | 'implicit' | 'props' | 'none';
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
type TabActiveResolution = RequestedTabKey & {
|
|
59
|
+
status: 'unspecified' | 'visible' | 'hidden' | 'unknown';
|
|
60
|
+
effectiveKey?: string;
|
|
61
|
+
allTabs: BasePageTabModel[];
|
|
62
|
+
unhiddenTabs: BasePageTabModel[];
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
type TabActiveKeySyncState = {
|
|
66
|
+
previousEffectiveActiveKey?: string;
|
|
67
|
+
effectiveActiveKey?: string;
|
|
68
|
+
previousAllHidden: boolean;
|
|
69
|
+
lastCorrectionSignature?: string;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
type TabActiveKeySyncProps = {
|
|
73
|
+
dependencyKey: string;
|
|
74
|
+
onSync: (state: TabActiveKeySyncState) => TabActiveKeySyncState;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
type LocallyCommittedTabTransition = {
|
|
78
|
+
activeKey: string;
|
|
79
|
+
previousActiveKey?: string;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
function TabActiveKeySync({ dependencyKey, onSync }: TabActiveKeySyncProps) {
|
|
83
|
+
const previousEffectiveActiveKey = React.useRef<string>();
|
|
84
|
+
const previousAllHidden = React.useRef(false);
|
|
85
|
+
const lastCorrectionSignature = React.useRef<string>();
|
|
86
|
+
|
|
87
|
+
React.useEffect(() => {
|
|
88
|
+
const nextState = onSync({
|
|
89
|
+
previousEffectiveActiveKey: previousEffectiveActiveKey.current,
|
|
90
|
+
previousAllHidden: previousAllHidden.current,
|
|
91
|
+
lastCorrectionSignature: lastCorrectionSignature.current,
|
|
92
|
+
});
|
|
93
|
+
previousEffectiveActiveKey.current = nextState.effectiveActiveKey;
|
|
94
|
+
previousAllHidden.current = nextState.previousAllHidden;
|
|
95
|
+
lastCorrectionSignature.current = nextState.lastCorrectionSignature;
|
|
96
|
+
}, [dependencyKey, onSync]);
|
|
97
|
+
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
|
|
52
101
|
export class PageModel extends FlowModel<PageModelStructure> {
|
|
53
102
|
tabBarExtraContent: { left?: ReactNode; right?: ReactNode } = {};
|
|
54
103
|
private viewActivatedListener?: (_payload?: unknown) => void;
|
|
@@ -57,6 +106,8 @@ export class PageModel extends FlowModel<PageModelStructure> {
|
|
|
57
106
|
private dirtyRefreshScheduled = false;
|
|
58
107
|
private unmounted = false;
|
|
59
108
|
private documentTitleUpdateVersion = 0;
|
|
109
|
+
private implicitActiveKey?: string;
|
|
110
|
+
private locallyCommittedTabTransition?: LocallyCommittedTabTransition;
|
|
60
111
|
|
|
61
112
|
/**
|
|
62
113
|
* 根页面标签页开关以路由表为准,避免 flow model 里的旧配置覆盖路由管理设置。
|
|
@@ -75,14 +126,277 @@ export class PageModel extends FlowModel<PageModelStructure> {
|
|
|
75
126
|
return !!this.props.enableTabs;
|
|
76
127
|
}
|
|
77
128
|
|
|
78
|
-
private
|
|
129
|
+
private getAllTabs(): BasePageTabModel[] {
|
|
130
|
+
return this.subModels?.tabs || [];
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
private getUnhiddenTabs(): BasePageTabModel[] {
|
|
134
|
+
return this.getAllTabs().filter((tab) => !tab.hidden);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
private getRequestedTabKey(): RequestedTabKey {
|
|
79
138
|
const viewParams = this.context.view?.navigation?.viewParams;
|
|
80
139
|
if (viewParams) {
|
|
81
|
-
|
|
140
|
+
const urlKey = typeof viewParams.tabUid === 'string' && viewParams.tabUid ? viewParams.tabUid : undefined;
|
|
141
|
+
if (urlKey) {
|
|
142
|
+
this.implicitActiveKey = undefined;
|
|
143
|
+
return { key: urlKey, source: 'url' };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const activeKey =
|
|
147
|
+
typeof this.props.tabActiveKey === 'string' && this.props.tabActiveKey ? this.props.tabActiveKey : undefined;
|
|
148
|
+
if (activeKey && activeKey === this.implicitActiveKey) {
|
|
149
|
+
return { key: activeKey, source: 'implicit' };
|
|
150
|
+
}
|
|
151
|
+
return { source: 'none' };
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const activeKey =
|
|
155
|
+
typeof this.props.tabActiveKey === 'string' && this.props.tabActiveKey ? this.props.tabActiveKey : undefined;
|
|
156
|
+
if (!activeKey) {
|
|
157
|
+
return { source: 'none' };
|
|
158
|
+
}
|
|
159
|
+
return { key: activeKey, source: activeKey === this.implicitActiveKey ? 'implicit' : 'props' };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
private resolveTabActiveState(): TabActiveResolution {
|
|
163
|
+
const allTabs = this.getAllTabs();
|
|
164
|
+
const unhiddenTabs = this.getUnhiddenTabs();
|
|
165
|
+
const requested = this.getRequestedTabKey();
|
|
166
|
+
|
|
167
|
+
if (!requested.key) {
|
|
168
|
+
return {
|
|
169
|
+
...requested,
|
|
170
|
+
status: 'unspecified',
|
|
171
|
+
effectiveKey: unhiddenTabs[0]?.uid,
|
|
172
|
+
allTabs,
|
|
173
|
+
unhiddenTabs,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const requestedTab = allTabs.find((tab) => tab.uid === requested.key);
|
|
178
|
+
if (requested.source === 'implicit' && (!requestedTab || requestedTab.hidden)) {
|
|
179
|
+
return {
|
|
180
|
+
...requested,
|
|
181
|
+
status: 'unspecified',
|
|
182
|
+
effectiveKey: unhiddenTabs[0]?.uid,
|
|
183
|
+
allTabs,
|
|
184
|
+
unhiddenTabs,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (!requestedTab) {
|
|
189
|
+
return {
|
|
190
|
+
...requested,
|
|
191
|
+
status: 'unknown',
|
|
192
|
+
effectiveKey: requested.key,
|
|
193
|
+
allTabs,
|
|
194
|
+
unhiddenTabs,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (unhiddenTabs.some((tab) => tab.uid === requested.key)) {
|
|
199
|
+
return {
|
|
200
|
+
...requested,
|
|
201
|
+
status: 'visible',
|
|
202
|
+
effectiveKey: requested.key,
|
|
203
|
+
allTabs,
|
|
204
|
+
unhiddenTabs,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return {
|
|
209
|
+
...requested,
|
|
210
|
+
status: 'hidden',
|
|
211
|
+
effectiveKey: requested.key,
|
|
212
|
+
allTabs,
|
|
213
|
+
unhiddenTabs,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
private getActiveTabKey(): string | undefined {
|
|
218
|
+
if (!this.getEnableTabs()) {
|
|
219
|
+
const firstTabKey = this.getFirstTab()?.uid;
|
|
220
|
+
if (firstTabKey) {
|
|
221
|
+
return firstTabKey;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return this.resolveTabActiveState().effectiveKey;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
private getTabActiveKeySyncDependency() {
|
|
228
|
+
const allTabs = this.getAllTabs();
|
|
229
|
+
const unhiddenTabs = this.getUnhiddenTabs();
|
|
230
|
+
const viewParams = this.context.view?.navigation?.viewParams;
|
|
231
|
+
return JSON.stringify([
|
|
232
|
+
!!this.context.flowSettingsEnabled,
|
|
233
|
+
viewParams ? viewParams.tabUid || null : null,
|
|
234
|
+
this.props.tabActiveKey || null,
|
|
235
|
+
allTabs.map((tab) => tab.uid),
|
|
236
|
+
unhiddenTabs.map((tab) => tab.uid),
|
|
237
|
+
this.implicitActiveKey || null,
|
|
238
|
+
]);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
private requestDocumentTitleUpdate(preferredActiveTabKey?: string, retryCount = 0) {
|
|
242
|
+
this.updateDocumentTitle(preferredActiveTabKey, retryCount).catch((error) => {
|
|
243
|
+
console.warn('[PageModel] Failed to update document title', error);
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
private rememberImplicitActiveKey(activeKey: string) {
|
|
248
|
+
this.implicitActiveKey = activeKey;
|
|
249
|
+
if (this.props.tabActiveKey !== activeKey) {
|
|
250
|
+
this.setProps('tabActiveKey', activeKey);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
private commitTabActiveKey(
|
|
255
|
+
activeKey: string | undefined,
|
|
256
|
+
options: {
|
|
257
|
+
previousActiveKey?: string;
|
|
258
|
+
navigate: boolean;
|
|
259
|
+
preserveImplicitKey?: boolean;
|
|
260
|
+
rememberLocalTransition?: boolean;
|
|
261
|
+
},
|
|
262
|
+
) {
|
|
263
|
+
const { previousActiveKey, navigate, preserveImplicitKey = false, rememberLocalTransition = false } = options;
|
|
264
|
+
if (rememberLocalTransition && activeKey) {
|
|
265
|
+
this.locallyCommittedTabTransition = { activeKey, previousActiveKey };
|
|
266
|
+
}
|
|
267
|
+
this.implicitActiveKey = activeKey && preserveImplicitKey ? activeKey : undefined;
|
|
268
|
+
|
|
269
|
+
if (navigate) {
|
|
270
|
+
this.context.view?.navigation?.changeTo?.({ tabUid: activeKey });
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (activeKey && activeKey !== previousActiveKey) {
|
|
274
|
+
this.invokeTabModelLifecycleMethod(activeKey, 'onActive');
|
|
275
|
+
}
|
|
276
|
+
if (previousActiveKey && previousActiveKey !== activeKey) {
|
|
277
|
+
this.invokeTabModelLifecycleMethod(previousActiveKey, 'onInactive');
|
|
278
|
+
}
|
|
279
|
+
this.setProps('tabActiveKey', activeKey);
|
|
280
|
+
|
|
281
|
+
if (!activeKey) {
|
|
282
|
+
this.requestDocumentTitleUpdate();
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
private consumeLocallyCommittedTabTransition(previousActiveKey: string | undefined, activeKey: string | undefined) {
|
|
287
|
+
const transition = this.locallyCommittedTabTransition;
|
|
288
|
+
if (!transition || !activeKey) {
|
|
289
|
+
return false;
|
|
290
|
+
}
|
|
291
|
+
if (transition.activeKey === activeKey && transition.previousActiveKey === previousActiveKey) {
|
|
292
|
+
this.locallyCommittedTabTransition = undefined;
|
|
293
|
+
return true;
|
|
82
294
|
}
|
|
83
|
-
|
|
295
|
+
if (activeKey !== transition.previousActiveKey) {
|
|
296
|
+
this.locallyCommittedTabTransition = undefined;
|
|
297
|
+
}
|
|
298
|
+
return false;
|
|
84
299
|
}
|
|
85
300
|
|
|
301
|
+
private synchronizeTabActiveKey = (state: TabActiveKeySyncState): TabActiveKeySyncState => {
|
|
302
|
+
const resolution = this.resolveTabActiveState();
|
|
303
|
+
const allHidden = resolution.allTabs.length > 0 && resolution.unhiddenTabs.length === 0;
|
|
304
|
+
const createNextState = (effectiveActiveKey: string | undefined, lastCorrectionSignature?: string) => ({
|
|
305
|
+
previousEffectiveActiveKey: effectiveActiveKey,
|
|
306
|
+
effectiveActiveKey,
|
|
307
|
+
previousAllHidden: allHidden,
|
|
308
|
+
lastCorrectionSignature,
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
if (resolution.status === 'unknown') {
|
|
312
|
+
return createNextState(resolution.effectiveKey);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (resolution.status === 'visible') {
|
|
316
|
+
const activeKey = resolution.effectiveKey;
|
|
317
|
+
if (this.consumeLocallyCommittedTabTransition(state.previousEffectiveActiveKey, activeKey)) {
|
|
318
|
+
return createNextState(activeKey);
|
|
319
|
+
}
|
|
320
|
+
const shouldActivateAfterRestore = state.previousAllHidden && resolution.source !== 'url';
|
|
321
|
+
const shouldSyncChangedVisibleKey =
|
|
322
|
+
!!state.previousEffectiveActiveKey && state.previousEffectiveActiveKey !== activeKey;
|
|
323
|
+
if ((shouldActivateAfterRestore || shouldSyncChangedVisibleKey) && activeKey) {
|
|
324
|
+
const signature = `visible:${resolution.source}:${state.previousEffectiveActiveKey || ''}->${activeKey}`;
|
|
325
|
+
if (signature !== state.lastCorrectionSignature) {
|
|
326
|
+
this.commitTabActiveKey(activeKey, {
|
|
327
|
+
previousActiveKey: state.previousEffectiveActiveKey,
|
|
328
|
+
navigate: false,
|
|
329
|
+
preserveImplicitKey: resolution.source === 'implicit',
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
return createNextState(activeKey, signature);
|
|
333
|
+
}
|
|
334
|
+
return createNextState(activeKey);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (resolution.status === 'hidden') {
|
|
338
|
+
const activeKey = resolution.effectiveKey;
|
|
339
|
+
if (this.consumeLocallyCommittedTabTransition(state.previousEffectiveActiveKey, activeKey)) {
|
|
340
|
+
return createNextState(activeKey);
|
|
341
|
+
}
|
|
342
|
+
if (activeKey && state.previousEffectiveActiveKey !== activeKey) {
|
|
343
|
+
const signature = `hidden:${resolution.source}:${state.previousEffectiveActiveKey || ''}->${activeKey}`;
|
|
344
|
+
if (signature !== state.lastCorrectionSignature) {
|
|
345
|
+
this.commitTabActiveKey(activeKey, {
|
|
346
|
+
previousActiveKey: state.previousEffectiveActiveKey,
|
|
347
|
+
navigate: false,
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
return createNextState(activeKey, signature);
|
|
351
|
+
}
|
|
352
|
+
return createNextState(activeKey);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const activeKey = resolution.effectiveKey;
|
|
356
|
+
if (!activeKey) {
|
|
357
|
+
if (!allHidden) {
|
|
358
|
+
if (resolution.source !== 'implicit' || !resolution.key) {
|
|
359
|
+
return createNextState(undefined);
|
|
360
|
+
}
|
|
361
|
+
const signature = `implicit-missing:${state.previousEffectiveActiveKey || ''}`;
|
|
362
|
+
if (signature !== state.lastCorrectionSignature) {
|
|
363
|
+
this.commitTabActiveKey(undefined, {
|
|
364
|
+
previousActiveKey: state.previousEffectiveActiveKey,
|
|
365
|
+
navigate: false,
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
return createNextState(undefined, signature);
|
|
369
|
+
}
|
|
370
|
+
const signature = `all-hidden:${state.previousEffectiveActiveKey || ''}`;
|
|
371
|
+
if (!state.previousAllHidden && signature !== state.lastCorrectionSignature) {
|
|
372
|
+
this.implicitActiveKey = undefined;
|
|
373
|
+
if (this.props.tabActiveKey || state.previousEffectiveActiveKey) {
|
|
374
|
+
this.commitTabActiveKey(undefined, {
|
|
375
|
+
previousActiveKey: state.previousEffectiveActiveKey,
|
|
376
|
+
navigate: false,
|
|
377
|
+
});
|
|
378
|
+
} else {
|
|
379
|
+
this.requestDocumentTitleUpdate();
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
return createNextState(undefined, signature);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const shouldSwitchImplicitActiveKey =
|
|
386
|
+
state.previousAllHidden || (!!state.previousEffectiveActiveKey && state.previousEffectiveActiveKey !== activeKey);
|
|
387
|
+
const signature = `implicit:${state.previousEffectiveActiveKey || ''}->${activeKey}`;
|
|
388
|
+
if (shouldSwitchImplicitActiveKey && signature !== state.lastCorrectionSignature) {
|
|
389
|
+
this.commitTabActiveKey(activeKey, {
|
|
390
|
+
previousActiveKey: state.previousEffectiveActiveKey,
|
|
391
|
+
navigate: false,
|
|
392
|
+
preserveImplicitKey: true,
|
|
393
|
+
});
|
|
394
|
+
} else {
|
|
395
|
+
this.rememberImplicitActiveKey(activeKey);
|
|
396
|
+
}
|
|
397
|
+
return createNextState(activeKey, shouldSwitchImplicitActiveKey ? signature : undefined);
|
|
398
|
+
};
|
|
399
|
+
|
|
86
400
|
private scheduleActiveLifecycleRefresh(forceRefresh = false): void {
|
|
87
401
|
if (this.dirtyRefreshScheduled) return;
|
|
88
402
|
this.dirtyRefreshScheduled = true;
|
|
@@ -107,7 +421,7 @@ export class PageModel extends FlowModel<PageModelStructure> {
|
|
|
107
421
|
}
|
|
108
422
|
|
|
109
423
|
deactivateCurrentTab() {
|
|
110
|
-
const activeKey = this.
|
|
424
|
+
const activeKey = this.getActiveTabKey();
|
|
111
425
|
if (activeKey) {
|
|
112
426
|
this.invokeTabModelLifecycleMethod(activeKey, 'onInactive');
|
|
113
427
|
}
|
|
@@ -116,9 +430,10 @@ export class PageModel extends FlowModel<PageModelStructure> {
|
|
|
116
430
|
onMount(): void {
|
|
117
431
|
super.onMount();
|
|
118
432
|
this.unmounted = false;
|
|
433
|
+
this.implicitActiveKey = undefined;
|
|
119
434
|
this.setProps('tabActiveKey', this.context.view.inputArgs?.tabUid);
|
|
120
435
|
if (this.context?.pageInfo) this.context.pageInfo.version = 'v2';
|
|
121
|
-
|
|
436
|
+
this.requestDocumentTitleUpdate();
|
|
122
437
|
|
|
123
438
|
// When a nested view (popup/page) is closed, the opener view becomes active again.
|
|
124
439
|
// We align this with the existing tab lifecycle by invoking `onActive` for the current tab blocks.
|
|
@@ -189,7 +504,7 @@ export class PageModel extends FlowModel<PageModelStructure> {
|
|
|
189
504
|
}
|
|
190
505
|
|
|
191
506
|
if (method === 'onActive') {
|
|
192
|
-
|
|
507
|
+
this.requestDocumentTitleUpdate(tabActiveKey);
|
|
193
508
|
}
|
|
194
509
|
}
|
|
195
510
|
|
|
@@ -236,11 +551,15 @@ export class PageModel extends FlowModel<PageModelStructure> {
|
|
|
236
551
|
}
|
|
237
552
|
};
|
|
238
553
|
|
|
554
|
+
const unhiddenTabs = this.getUnhiddenTabs();
|
|
555
|
+
const activeTabKey = preferredActiveTabKey || this.getActiveTabKey();
|
|
556
|
+
const shouldUsePageTitle = !this.getEnableTabs() || (!activeTabKey && unhiddenTabs.length === 0);
|
|
557
|
+
|
|
239
558
|
let nextTitle = '';
|
|
240
|
-
if (
|
|
241
|
-
const activeTabKey = preferredActiveTabKey || this.getActiveTabKey();
|
|
559
|
+
if (!shouldUsePageTitle) {
|
|
242
560
|
const activeTabModel = activeTabKey
|
|
243
|
-
?
|
|
561
|
+
? this.findSubModel('tabs', (model) => model.uid === activeTabKey) ||
|
|
562
|
+
(this.flowEngine.getModel(activeTabKey) as BasePageTabModel | undefined)
|
|
244
563
|
: this.getFirstTab();
|
|
245
564
|
if (!activeTabModel && retryCount < 5) {
|
|
246
565
|
window.setTimeout(() => {
|
|
@@ -251,7 +570,7 @@ export class PageModel extends FlowModel<PageModelStructure> {
|
|
|
251
570
|
if (updateVersion !== this.documentTitleUpdateVersion) {
|
|
252
571
|
return;
|
|
253
572
|
}
|
|
254
|
-
|
|
573
|
+
this.requestDocumentTitleUpdate(activeTabKey, retryCount + 1);
|
|
255
574
|
}, 0);
|
|
256
575
|
return;
|
|
257
576
|
}
|
|
@@ -293,33 +612,31 @@ export class PageModel extends FlowModel<PageModelStructure> {
|
|
|
293
612
|
|
|
294
613
|
mapTabs() {
|
|
295
614
|
return this.mapSubModels('tabs', (model) => {
|
|
296
|
-
return
|
|
297
|
-
|
|
298
|
-
:
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
};
|
|
322
|
-
}).filter(Boolean);
|
|
615
|
+
return {
|
|
616
|
+
key: model.uid,
|
|
617
|
+
label: (
|
|
618
|
+
<Droppable model={model}>
|
|
619
|
+
<FlowModelRenderer
|
|
620
|
+
model={model}
|
|
621
|
+
showFlowSettings={{
|
|
622
|
+
showBackground: true,
|
|
623
|
+
showBorder: false,
|
|
624
|
+
toolbarPosition: 'above',
|
|
625
|
+
style: { transform: 'translateY(8px)' },
|
|
626
|
+
}}
|
|
627
|
+
extraToolbarItems={[
|
|
628
|
+
{
|
|
629
|
+
key: 'drag-handler',
|
|
630
|
+
component: DragHandler,
|
|
631
|
+
sort: 1,
|
|
632
|
+
},
|
|
633
|
+
]}
|
|
634
|
+
/>
|
|
635
|
+
</Droppable>
|
|
636
|
+
),
|
|
637
|
+
children: model.renderChildren(),
|
|
638
|
+
};
|
|
639
|
+
});
|
|
323
640
|
}
|
|
324
641
|
|
|
325
642
|
getFirstTab() {
|
|
@@ -336,6 +653,17 @@ export class PageModel extends FlowModel<PageModelStructure> {
|
|
|
336
653
|
}
|
|
337
654
|
|
|
338
655
|
renderTabs() {
|
|
656
|
+
const activeState = this.resolveTabActiveState();
|
|
657
|
+
const tabItems = this.mapTabs();
|
|
658
|
+
const hiddenTabKeys = new Set(
|
|
659
|
+
this.context.flowSettingsEnabled ? [] : activeState.allTabs.filter((tab) => tab.hidden).map((tab) => tab.uid),
|
|
660
|
+
);
|
|
661
|
+
const hiddenActiveTabLabel =
|
|
662
|
+
activeState.status === 'hidden'
|
|
663
|
+
? activeState.allTabs.find((tab) => tab.uid === activeState.effectiveKey)?.getTabTitle?.()
|
|
664
|
+
: undefined;
|
|
665
|
+
const tabsActiveKey =
|
|
666
|
+
activeState.effectiveKey || (activeState.allTabs.length > 0 ? NO_ACTIVE_PAGE_TAB_KEY : undefined);
|
|
339
667
|
const tabNavPaddingInlineStart = this.context.themeToken?.paddingLG ?? 16;
|
|
340
668
|
const leftExtraContent =
|
|
341
669
|
this.tabBarExtraContent.left !== undefined ? (
|
|
@@ -366,25 +694,31 @@ export class PageModel extends FlowModel<PageModelStructure> {
|
|
|
366
694
|
|
|
367
695
|
return (
|
|
368
696
|
<DndProvider onDragEnd={this.handleDragEnd.bind(this)}>
|
|
697
|
+
<TabActiveKeySync dependencyKey={this.getTabActiveKeySyncDependency()} onSync={this.synchronizeTabActiveKey} />
|
|
369
698
|
<Tabs
|
|
370
|
-
activeKey={
|
|
371
|
-
this.context.view?.navigation?.viewParams
|
|
372
|
-
? this.context.view.navigation.viewParams.tabUid || this.getFirstTab()?.uid
|
|
373
|
-
: this.props.tabActiveKey
|
|
374
|
-
}
|
|
699
|
+
activeKey={tabsActiveKey}
|
|
375
700
|
tabBarStyle={this.props.tabBarStyle}
|
|
376
|
-
items={
|
|
701
|
+
items={tabItems}
|
|
702
|
+
renderTabBar={
|
|
703
|
+
hiddenTabKeys.size > 0
|
|
704
|
+
? (tabBarProps) => (
|
|
705
|
+
<FilteredPageTabBar
|
|
706
|
+
hiddenTabKeys={hiddenTabKeys}
|
|
707
|
+
hiddenActiveTabLabel={hiddenActiveTabLabel}
|
|
708
|
+
items={tabItems}
|
|
709
|
+
tabBarProps={tabBarProps}
|
|
710
|
+
/>
|
|
711
|
+
)
|
|
712
|
+
: undefined
|
|
713
|
+
}
|
|
377
714
|
onChange={(activeKey) => {
|
|
378
|
-
const previousActiveKey = this.
|
|
379
|
-
this.
|
|
380
|
-
|
|
715
|
+
const previousActiveKey = this.getActiveTabKey();
|
|
716
|
+
this.implicitActiveKey = undefined;
|
|
717
|
+
this.commitTabActiveKey(activeKey, {
|
|
718
|
+
previousActiveKey,
|
|
719
|
+
navigate: !!this.context.view?.navigation,
|
|
720
|
+
rememberLocalTransition: true,
|
|
381
721
|
});
|
|
382
|
-
|
|
383
|
-
this.invokeTabModelLifecycleMethod(activeKey, 'onActive');
|
|
384
|
-
if (previousActiveKey && previousActiveKey !== activeKey) {
|
|
385
|
-
this.invokeTabModelLifecycleMethod(previousActiveKey, 'onInactive');
|
|
386
|
-
}
|
|
387
|
-
this.setProps('tabActiveKey', activeKey);
|
|
388
722
|
}}
|
|
389
723
|
// destroyInactiveTabPane
|
|
390
724
|
tabBarExtraContent={{
|
|
@@ -527,7 +861,7 @@ PageModel.registerFlow({
|
|
|
527
861
|
marginBottom: 0,
|
|
528
862
|
});
|
|
529
863
|
}
|
|
530
|
-
|
|
864
|
+
await (ctx.model as PageModel).updateDocumentTitle();
|
|
531
865
|
},
|
|
532
866
|
},
|
|
533
867
|
},
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { Tabs } from 'antd';
|
|
11
|
+
import type { TabsProps } from 'antd';
|
|
12
|
+
import React from 'react';
|
|
13
|
+
|
|
14
|
+
export const NO_ACTIVE_PAGE_TAB_KEY = '__no_active_page_tab__';
|
|
15
|
+
|
|
16
|
+
type RenderTabBar = NonNullable<React.ComponentProps<typeof Tabs>['renderTabBar']>;
|
|
17
|
+
type PageTabItem = NonNullable<TabsProps['items']>[number];
|
|
18
|
+
|
|
19
|
+
type FilteredPageTabBarProps = {
|
|
20
|
+
hiddenTabKeys: Set<string>;
|
|
21
|
+
hiddenActiveTabLabel?: React.ReactNode;
|
|
22
|
+
items: PageTabItem[];
|
|
23
|
+
tabBarProps: Parameters<RenderTabBar>[0];
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export function FilteredPageTabBar({
|
|
27
|
+
hiddenTabKeys,
|
|
28
|
+
hiddenActiveTabLabel,
|
|
29
|
+
items,
|
|
30
|
+
tabBarProps,
|
|
31
|
+
}: FilteredPageTabBarProps) {
|
|
32
|
+
// Keep the outer Tabs instance responsible for every pane, while a navigation-only Tabs instance
|
|
33
|
+
// receives only visible items. This avoids relying on the rc-tabs Context instance bundled by antd.
|
|
34
|
+
const visibleItems = items
|
|
35
|
+
.filter((item) => !hiddenTabKeys.has(String(item.key)))
|
|
36
|
+
.map(
|
|
37
|
+
({
|
|
38
|
+
children: _children,
|
|
39
|
+
className: _className,
|
|
40
|
+
destroyInactiveTabPane: _destroyInactiveTabPane,
|
|
41
|
+
forceRender: _forceRender,
|
|
42
|
+
style: _style,
|
|
43
|
+
...item
|
|
44
|
+
}) => ({
|
|
45
|
+
...item,
|
|
46
|
+
children: null,
|
|
47
|
+
}),
|
|
48
|
+
);
|
|
49
|
+
const activeKey = visibleItems.some((item) => item.key === tabBarProps.activeKey)
|
|
50
|
+
? tabBarProps.activeKey
|
|
51
|
+
: NO_ACTIVE_PAGE_TAB_KEY;
|
|
52
|
+
const firstFocusableTabKey = visibleItems.find((item) => !item.disabled)?.key;
|
|
53
|
+
const hiddenActiveTabKey = hiddenTabKeys.has(String(tabBarProps.activeKey))
|
|
54
|
+
? String(tabBarProps.activeKey)
|
|
55
|
+
: undefined;
|
|
56
|
+
const hiddenActiveTabLabelId =
|
|
57
|
+
hiddenActiveTabKey && tabBarProps.id ? `${tabBarProps.id}-tab-${hiddenActiveTabKey}` : undefined;
|
|
58
|
+
const renderTabNode = (node: React.ReactElement) => {
|
|
59
|
+
const tabKey = String(node.key);
|
|
60
|
+
if (activeKey !== NO_ACTIVE_PAGE_TAB_KEY || tabKey !== String(firstFocusableTabKey)) {
|
|
61
|
+
return node;
|
|
62
|
+
}
|
|
63
|
+
let tabButtonFound = false;
|
|
64
|
+
const children = React.Children.map(
|
|
65
|
+
(node.props as { children?: React.ReactNode }).children,
|
|
66
|
+
(child: React.ReactNode) => {
|
|
67
|
+
if (tabButtonFound || !React.isValidElement(child)) {
|
|
68
|
+
return child;
|
|
69
|
+
}
|
|
70
|
+
const childProps = child.props as {
|
|
71
|
+
role?: string;
|
|
72
|
+
tabIndex?: number | null;
|
|
73
|
+
};
|
|
74
|
+
if (childProps.role !== 'tab') {
|
|
75
|
+
return child;
|
|
76
|
+
}
|
|
77
|
+
tabButtonFound = true;
|
|
78
|
+
return React.cloneElement(child as React.ReactElement<typeof childProps>, {
|
|
79
|
+
tabIndex: 0,
|
|
80
|
+
});
|
|
81
|
+
},
|
|
82
|
+
);
|
|
83
|
+
return React.cloneElement(node, undefined, children);
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<>
|
|
88
|
+
{hiddenActiveTabLabelId ? (
|
|
89
|
+
<span id={hiddenActiveTabLabelId} hidden>
|
|
90
|
+
{hiddenActiveTabLabel || hiddenActiveTabKey}
|
|
91
|
+
</span>
|
|
92
|
+
) : null}
|
|
93
|
+
<Tabs
|
|
94
|
+
activeKey={NO_ACTIVE_PAGE_TAB_KEY}
|
|
95
|
+
animated={{ inkBar: tabBarProps.animated.inkBar, tabPane: false }}
|
|
96
|
+
destroyInactiveTabPane
|
|
97
|
+
id={tabBarProps.id}
|
|
98
|
+
items={visibleItems}
|
|
99
|
+
more={tabBarProps.more}
|
|
100
|
+
onTabClick={(key, event) => tabBarProps.onTabClick(key, event)}
|
|
101
|
+
onTabScroll={tabBarProps.onTabScroll}
|
|
102
|
+
renderTabBar={(navigationTabBarProps, NavigationDefaultTabBar) => (
|
|
103
|
+
<NavigationDefaultTabBar {...navigationTabBarProps} activeKey={activeKey}>
|
|
104
|
+
{renderTabNode}
|
|
105
|
+
</NavigationDefaultTabBar>
|
|
106
|
+
)}
|
|
107
|
+
tabBarExtraContent={tabBarProps.extra}
|
|
108
|
+
tabBarGutter={tabBarProps.tabBarGutter}
|
|
109
|
+
tabBarStyle={tabBarProps.style}
|
|
110
|
+
tabPosition={tabBarProps.tabPosition}
|
|
111
|
+
/>
|
|
112
|
+
</>
|
|
113
|
+
);
|
|
114
|
+
}
|