go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/milo/ui/src/generic_libs/components/routed_tabs/reducer.tsx (about) 1 // Copyright 2023 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 export interface RoutedTabsState { 16 readonly activeTab: { 17 readonly id: string; 18 readonly hookRef: unknown; 19 } | null; 20 } 21 22 export type Action = { 23 readonly type: 'activateTab' | 'deactivateTab'; 24 /** 25 * The ID of the tab. Used to inform `<RoutedTabs />` which tab should be 26 * highlighted. 27 */ 28 readonly id: string; 29 /** 30 * A reference that uniquely identifies a `useTabId` call in a component. 31 * 32 * For a given `<RoutedTabs />`, only one `useTabId` call can exist in its 33 * descendants at any time. 34 */ 35 readonly hookRef: unknown; 36 }; 37 38 export function reducer( 39 state: RoutedTabsState, 40 action: Action, 41 ): RoutedTabsState { 42 switch (action.type) { 43 case 'activateTab': { 44 if ( 45 state.activeTab !== null && 46 state.activeTab.hookRef !== action.hookRef 47 ) { 48 throw new Error( 49 `cannot activate a tab when there's already an active tab; ` + 50 `active: ${state.activeTab.id}}; ` + 51 `activating: ${action.id}`, 52 ); 53 } 54 return { activeTab: { id: action.id, hookRef: action.hookRef } }; 55 } 56 case 'deactivateTab': { 57 if (state.activeTab?.hookRef !== action.hookRef) { 58 throw new Error( 59 `cannot deactivate an inactive tab; ` + 60 `active: ${state.activeTab?.id ?? null}}; ` + 61 `deactivating: ${action.id}`, 62 ); 63 } 64 return { activeTab: null }; 65 } 66 } 67 }