diff --git a/web_ui/packages/ui/index.ts b/web_ui/packages/ui/index.ts index c8e233d2ac..62842613bf 100644 --- a/web_ui/packages/ui/index.ts +++ b/web_ui/packages/ui/index.ts @@ -163,6 +163,7 @@ export { ViewModes, INITIAL_VIEW_MODE, VIEW_MODE_LABEL } from './src/view-modes/ export { useViewMode } from './src/view-modes/use-view-mode.hook'; export { Toast, toast, removeToasts, removeToast, CustomToast } from './src/toast/toast.component'; export { HorizontalLayout, type HorizontalLayoutOptions } from './src/virtualized-horizontal-grid/horizontal-layout'; +export { ManagedTabs } from './src/managed-tabs/managed-tabs.component'; export { ListBox as AriaComponentsListBox, diff --git a/web_ui/packages/ui/src/managed-tabs/managed-tabs.component.tsx b/web_ui/packages/ui/src/managed-tabs/managed-tabs.component.tsx new file mode 100644 index 0000000000..b19c9a9f38 --- /dev/null +++ b/web_ui/packages/ui/src/managed-tabs/managed-tabs.component.tsx @@ -0,0 +1,124 @@ +// Copyright (C) 2022-2025 Intel Corporation +// LIMITED EDGE SOFTWARE DISTRIBUTION LICENSE + +import { Key, ReactNode } from 'react'; + +import { Flex, Item, Picker, TabList, TabPanels, Tabs } from '@adobe/react-spectrum'; + +import classes from './managed-tabs.module.scss'; + +interface TabItem { + id: string; + key: string; + name: ReactNode; + children: ReactNode; +} + +interface OverflowConfig { + maxVisibleTabs: number; + pickerAriaLabel: string; + onCollapsedItemSelect: (key: string) => void; +} + +interface ManagedTabsProps { + id?: string; + items: T[]; + selectedKey: string; + onSelectionChange: (key: Key) => void; + renderTabItem: (item: T) => ReactNode; + children?: ReactNode; + addButton?: ReactNode; + overflow?: OverflowConfig; + orientation?: 'horizontal' | 'vertical'; + wrapperClassName?: string; + tabListClassName?: string; + ariaLabel: string; +} + +export const ManagedTabs = ({ + id, + items, + selectedKey, + onSelectionChange, + renderTabItem, + children, + addButton, + overflow, + orientation = 'vertical', + wrapperClassName, + tabListClassName, + ariaLabel, +}: ManagedTabsProps) => { + const hasOverflow = overflow && items.length > overflow.maxVisibleTabs; + + const visibleItems = hasOverflow ? items.slice(0, overflow.maxVisibleTabs) : items; + const collapsedItems = hasOverflow ? items.slice(overflow.maxVisibleTabs) : []; + const hasSelectedCollapsedItem = collapsedItems.some((item) => item.id === selectedKey); + + const tabItems = visibleItems.map((item) => ({ + id: item.id, + key: item.id, + name: item.name, + originalItem: item, + })); + + const collapsedPickerItems = collapsedItems.map((item) => ({ id: item.id, name: item.name })); + + return ( + + + +
+ + {(item: TabItem & { originalItem: T }) => ( + + {renderTabItem(item.originalItem)} + + )} + + + {overflow && collapsedItems.length > 0 && ( + overflow.onCollapsedItemSelect(String(key))} + placeholder={`${collapsedItems.length} more`} + UNSAFE_className={[ + classes.collapsedItemsPicker, + !hasSelectedCollapsedItem ? classes.selected : '', + ].join(' ')} + > + {(item) => {item.name}} + + )} +
+ + {addButton && addButton} +
+ + {(item: TabItem) => {children}} +
+
+ ); +}; diff --git a/web_ui/packages/ui/src/managed-tabs/managed-tabs.module.scss b/web_ui/packages/ui/src/managed-tabs/managed-tabs.module.scss new file mode 100644 index 0000000000..e5ba1855a5 --- /dev/null +++ b/web_ui/packages/ui/src/managed-tabs/managed-tabs.module.scss @@ -0,0 +1,107 @@ +/* + Overriding vertical tabs is necessary because if the user's monitor display settings + are higher than 100%, then spectrum's tabs collapses to a Picker. This doesn't happen on + vertical tabs. So we decided to use the vertical tabs logic with horizontal tabs layout. + + https://react-spectrum.adobe.com/react-spectrum/Tabs.html#collapse-overflow-behavior + */ +.componentWrapper { + div[class*='spectrum-Tabs--vertical'] { + border-left: 0 !important; + flex-direction: row !important; + padding-right: var(--spectrum-global-dimension-size-100) !important; + + > div[class*='Tabs-item'] { + margin-bottom: 0 !important; + margin-left: 0 !important; + box-sizing: border-box !important; + display: flex; + align-items: center; + + &:first-child { + padding-left: 0 !important; + } + } + } + + div[class*='TabsPanel--vertical'] { + flex-direction: column !important; + padding: 0 !important; + } +} + +.tabList { + span[class*='spectrum-Tabs-itemLabel'] { + font-size: var(--spectrum-global-dimension-font-size-200); + } + + div[class*='is-selected']::after { + transition: background-color 0.15s ease-in-out; + content: ''; + height: var(--spectrum-global-dimension-size-25); + background-color: transparent; + position: absolute; + left: 0; + right: 0; + bottom: 0; + } + + div[class*='is-selected'] { + position: relative; + + &:after { + background-color: var(--energy-blue); + } + } + + div[class*='spectrum-Tabs-selectionIndicator'] { + display: none; + } + + &.emptySelection { + div[class*='is-selected'] { + color: var(--spectrum-tabs-text-color, var(--spectrum-alias-label-text-color)); + } + } + + > div { + border-bottom-width: 0; + } +} + +.tabWrapper { + border-bottom: var(--spectrum-global-dimension-size-10) solid + var(--spectrum-tabs-rule-color, var(--spectrum-global-color-gray-200)) !important; + box-sizing: border-box !important; + display: flex; + align-items: center; +} + +.tabListScrollContainer { + flex: 1 1 auto; + min-width: 0; + overflow: auto hidden; + display: flex; + align-items: center; + gap: 0; +} + +.collapsedItemsPicker { + position: relative; + flex-shrink: 0; + + &.selected::after { + content: ''; + position: absolute; + height: var(--spectrum-global-dimension-size-25); + left: 0; + bottom: -15px; + right: var(--spectrum-global-dimension-size-400); + background-color: var(--energy-blue); + } + + span[class*='spectrum-Dropdown-label'] { + font-size: var(--spectrum-global-dimension-font-size-100) !important; + font-style: normal !important; + } +} diff --git a/web_ui/src/pages/landing-page/landing-page.module.scss b/web_ui/src/pages/landing-page/landing-page.module.scss deleted file mode 100644 index 6bf920d462..0000000000 --- a/web_ui/src/pages/landing-page/landing-page.module.scss +++ /dev/null @@ -1,4 +0,0 @@ -.selectedTab { - width: var(--spectrum-global-dimension-size-1250); - left: 0; -} diff --git a/web_ui/src/pages/landing-page/workspaces-tabs/workspaces-tabs.component.tsx b/web_ui/src/pages/landing-page/workspaces-tabs/workspaces-tabs.component.tsx index 09f82f0b7a..5aabf5e99b 100644 --- a/web_ui/src/pages/landing-page/workspaces-tabs/workspaces-tabs.component.tsx +++ b/web_ui/src/pages/landing-page/workspaces-tabs/workspaces-tabs.component.tsx @@ -7,7 +7,7 @@ import { isOrganizationAdmin } from '@geti/core/src/users/user-role-utils'; import { RESOURCE_TYPE } from '@geti/core/src/users/users.interface'; import { useWorkspacesApi } from '@geti/core/src/workspaces/hooks/use-workspaces.hook'; import { WorkspaceEntity } from '@geti/core/src/workspaces/services/workspaces.interface'; -import { ActionButton, Flex, Item, Loading, TabList, TabPanels, Tabs, Tooltip, TooltipTrigger } from '@geti/ui'; +import { ActionButton, Loading, ManagedTabs, Tooltip, TooltipTrigger } from '@geti/ui'; import { Add } from '@geti/ui/icons'; import { useOverlayTriggerState } from 'react-stately'; @@ -15,121 +15,90 @@ import { useOrganizationIdentifier } from '../../../hooks/use-organization-ident import { CustomTabItem } from '../../../shared/components/custom-tab-item/custom-tab-item.component'; import { HasPermission } from '../../../shared/components/has-permission/has-permission.component'; import { OPERATION } from '../../../shared/components/has-permission/has-permission.interface'; -import { TabItem } from '../../../shared/components/tabs/tabs.interface'; -import { hasEqualId } from '../../../shared/utils'; import { CreateWorkspaceDialog } from '../../user-management/workspaces/create-workspace-dialog/create-workspace-dialog.component'; import { LandingPageWorkspace as Workspace } from '../landing-page-workspace/landing-page-workspace.component'; import { NoPermissionPlaceholder } from './components/no-permission-placeholder.component'; import { CustomTabItemWithMenu } from './custom-tab-item-with-menu.component'; import { useWorkspacesTabs } from './hooks/use-pinned-collapsed-workspace.hook'; -import classes from '../../../shared/components/custom-tab-item/custom-tab-item.module.scss'; +const WorkspaceTabItem = ({ workspace }: { workspace: WorkspaceEntity }) => { + const { organizationId } = useOrganizationIdentifier(); + const { data: activeUser } = useActiveUser(organizationId); + const { workspaces, selectWorkspace, selectedWorkspaceId } = useWorkspacesTabs(); + const { FEATURE_FLAG_WORKSPACE_ACTIONS } = useFeatureFlags(); + + const isSelected = workspace.id === selectedWorkspaceId; + + if (isSelected && FEATURE_FLAG_WORKSPACE_ACTIONS) { + return ( + } + > + + + ); + } + + return ; +}; export const WorkspacesTabs = () => { const { organizationId } = useOrganizationIdentifier(); - const { workspaces, selectWorkspace, selectedWorkspaceId, handleSelectWorkspace } = useWorkspacesTabs(); + const { workspaces, selectedWorkspaceId, handleSelectWorkspace } = useWorkspacesTabs(); const { FEATURE_FLAG_WORKSPACE_ACTIONS } = useFeatureFlags(); - const { data: activeUser } = useActiveUser(organizationId); const createWorkspaceDialogState = useOverlayTriggerState({}); const { useCreateWorkspaceMutation } = useWorkspacesApi(organizationId); const createWorkspace = useCreateWorkspaceMutation(); - const selectedWorkspace = workspaces.find(hasEqualId(selectedWorkspaceId)); - - const items = workspaces.map(({ id, name }) => ({ - name, - id: `${id === selectedWorkspaceId ? 'selected-' : ''}workspace-${id}`, - key: id, - children: , - })); const workspacesNames = workspaces.map((workspace) => workspace.name); return ( - - + + id='page-layout-id' + items={workspaces} selectedKey={selectedWorkspaceId} - items={items} - aria-label={'Workspaces tabs'} - height={'100%'} - width={'100%'} - orientation={'vertical'} onSelectionChange={handleSelectWorkspace} - > - -
- - {(item: TabItem) => ( - - <> - - {selectedWorkspaceId === item.key && FEATURE_FLAG_WORKSPACE_ACTIONS ? ( - - } - > - - - ) : ( - - )} - - - - )} - -
- - {FEATURE_FLAG_WORKSPACE_ACTIONS && ( - - - - {createWorkspace.isPending ? : } - - Create a new workspace - - - )} -
- - {(item: TabItem) => ( - - } + renderTabItem={(workspace) => } + addButton={ + FEATURE_FLAG_WORKSPACE_ACTIONS ? ( + + - {item.children} - - - )} - -
+ {createWorkspace.isPending ? : } + + Create a new workspace + + ) : undefined + } + ariaLabel={'Workspaces tabs'} + > + } + > + + + -
+ ); }; diff --git a/web_ui/src/pages/project-details/components/project-dataset/dataset-tab-list.component.tsx b/web_ui/src/pages/project-details/components/project-dataset/dataset-tab-list.component.tsx deleted file mode 100644 index 818f288da3..0000000000 --- a/web_ui/src/pages/project-details/components/project-dataset/dataset-tab-list.component.tsx +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (C) 2022-2025 Intel Corporation -// LIMITED EDGE SOFTWARE DISTRIBUTION LICENSE - -import { ActionButton, Flex, Item, Loading, TabList, Tooltip, TooltipTrigger } from '@geti/ui'; -import { Add } from '@geti/ui/icons'; - -import { Dataset } from '../../../../core/projects/dataset.interface'; -import { useDataset } from '../../../../providers/dataset-provider/dataset-provider.component'; -import { CollapsedItemsPicker } from '../../../../shared/components/collapsed-items-picker/collapsed-items-picker.component'; -import { TabItem } from '../../../../shared/components/tabs/tabs.interface'; -import { hasEqualId } from '../../../../shared/utils'; -import { useProject } from '../../providers/project-provider/project-provider.component'; -import { ExportDatasetDialog } from './export-dataset/export-dataset-dialog.component'; -import { useExportImportDatasetDialogStates } from './export-dataset/export-import-dataset-dialog-provider.component'; -import { ProjectDatasetTabActions } from './project-dataset-tab-actions.component'; -import { useSelectedDataset } from './use-selected-dataset/use-selected-dataset.hook'; -import { MAX_NUMBER_OF_DISPLAYED_DATASETS } from './utils'; - -import classes from './project-dataset.module.scss'; - -export const DatasetTabList = () => { - const selectedDataset = useSelectedDataset(); - const { project } = useProject(); - - const numberOfDatasets = project.datasets.length; - - const { createDataset, pinnedDatasets, collapsedDatasets, handleCreateDataset, handleSelectDataset } = useDataset(); - - const { exportDialogState } = useExportImportDatasetDialogStates(); - - const hasSelectedPinnedDataset = pinnedDatasets.find(hasEqualId(selectedDataset.id)) !== undefined; - const collapsedPickerItems = collapsedDatasets.map(({ id, name }) => ({ id, name })); - - return ( - - - {(item: TabItem & { dataset: Dataset }) => ( - - - - )} - - - - - {numberOfDatasets > MAX_NUMBER_OF_DISPLAYED_DATASETS ? ( - - ) : null} - - - - {createDataset.isPending ? : } - - Create new testing set - - - ); -}; diff --git a/web_ui/src/pages/project-details/components/project-dataset/project-dataset.component.tsx b/web_ui/src/pages/project-details/components/project-dataset/project-dataset.component.tsx index a2def958d5..734dd358ae 100644 --- a/web_ui/src/pages/project-details/components/project-dataset/project-dataset.component.tsx +++ b/web_ui/src/pages/project-details/components/project-dataset/project-dataset.component.tsx @@ -1,19 +1,20 @@ // Copyright (C) 2022-2025 Intel Corporation // LIMITED EDGE SOFTWARE DISTRIBUTION LICENSE -import { Key, useMemo } from 'react'; - -import { Flex, Item, TabPanels, Tabs } from '@geti/ui'; +import { ActionButton, Flex, Loading, ManagedTabs, Tooltip, TooltipTrigger } from '@geti/ui'; +import { Add } from '@geti/ui/icons'; import { Dataset } from '../../../../core/projects/dataset.interface'; import { isAnomalyDomain } from '../../../../core/projects/domains'; import { TUTORIAL_CARD_KEYS } from '../../../../core/user-settings/dtos/user-settings.interface'; import { useDataset } from '../../../../providers/dataset-provider/dataset-provider.component'; import { TutorialCardBuilder } from '../../../../shared/components/tutorial-card/tutorial-card-builder.component'; -import { hasEqualId } from '../../../../shared/utils'; import { useProject } from '../../providers/project-provider/project-provider.component'; -import { DatasetTabList } from './dataset-tab-list.component'; import { DatasetTabPanel } from './dataset-tab-panel.component'; +import { ExportDatasetDialog } from './export-dataset/export-dataset-dialog.component'; +import { useExportImportDatasetDialogStates } from './export-dataset/export-import-dataset-dialog-provider.component'; +import { ProjectDatasetTabActions } from './project-dataset-tab-actions.component'; +import { MAX_NUMBER_OF_DISPLAYED_DATASETS } from './utils'; import classes from './project-dataset.module.scss'; @@ -26,21 +27,18 @@ import classes from './project-dataset.module.scss'; */ export const ProjectDataset = () => { const { isSingleDomainProject } = useProject(); - const { pinnedDatasets, handleSelectDataset, selectedDataset } = useDataset(); + const { + handleSelectDataset, + selectedDataset, + createDataset, + handleCreateDataset, + pinnedDatasets, + collapsedDatasets, + } = useDataset(); + const { exportDialogState } = useExportImportDatasetDialogStates(); const isAnomalyProject = isSingleDomainProject(isAnomalyDomain); - const hasSelectedPinnedDataset = pinnedDatasets.find(hasEqualId(selectedDataset.id)) !== undefined; - - const items = useMemo(() => { - return pinnedDatasets.map((dataset) => { - return { - id: `dataset-${dataset.id}-id`, - key: dataset.id, - name: dataset.name, - dataset, - }; - }); - }, [pinnedDatasets]); + const allDatasets = [...pinnedDatasets, ...collapsedDatasets]; return ( @@ -50,26 +48,35 @@ export const ProjectDataset = () => { styles={{ fontSize: 'var(--spectrum-global-dimension-font-size-350)' }} /> )} - + items={allDatasets} selectedKey={selectedDataset.id} - onSelectionChange={(key: Key) => handleSelectDataset(String(key))} + onSelectionChange={(key) => handleSelectDataset(String(key))} + renderTabItem={(dataset) => } + addButton={ + + + {createDataset.isPending ? : } + + Create new testing set + + } + overflow={{ + maxVisibleTabs: MAX_NUMBER_OF_DISPLAYED_DATASETS, + pickerAriaLabel: 'Collapsed datasets', + onCollapsedItemSelect: handleSelectDataset, + }} + ariaLabel={'Dataset page tabs'} > - - - - {(item: { dataset: Dataset }) => ( - - - - )} - - + + + ); }; diff --git a/web_ui/src/pages/user-management/workspaces/workspace-users-toolbar.component.tsx b/web_ui/src/pages/user-management/workspaces/workspace-users-toolbar.component.tsx index 372507abf4..66c8e33408 100644 --- a/web_ui/src/pages/user-management/workspaces/workspace-users-toolbar.component.tsx +++ b/web_ui/src/pages/user-management/workspaces/workspace-users-toolbar.component.tsx @@ -1,15 +1,13 @@ // Copyright (C) 2022-2025 Intel Corporation // LIMITED EDGE SOFTWARE DISTRIBUTION LICENSE -import { Key } from 'react'; - import { useFeatureFlags } from '@geti/core/src/feature-flags/hooks/use-feature-flags.hook'; import { useActiveUser } from '@geti/core/src/users/hook/use-users.hook'; import { isOrganizationAdmin } from '@geti/core/src/users/user-role-utils'; import { RESOURCE_TYPE } from '@geti/core/src/users/users.interface'; import { useWorkspacesApi } from '@geti/core/src/workspaces/hooks/use-workspaces.hook'; import { WorkspaceEntity } from '@geti/core/src/workspaces/services/workspaces.interface'; -import { ActionButton, Flex, Item, Loading, TabList, Tabs, Tooltip, TooltipTrigger } from '@geti/ui'; +import { ActionButton, Flex, Loading, ManagedTabs, Tooltip, TooltipTrigger } from '@geti/ui'; import { Add } from '@geti/ui/icons'; import { useOverlayTriggerState } from 'react-stately'; @@ -23,8 +21,6 @@ import { CustomTabItemWithMenu } from '../../landing-page/workspaces-tabs/custom import { useWorkspaceActions } from '../../landing-page/workspaces-tabs/hooks/use-workspace-actions.hook'; import { CreateWorkspaceDialog } from './create-workspace-dialog/create-workspace-dialog.component'; -import classes from '../../../shared/components/custom-tab-item/custom-tab-item.module.scss'; - interface WorkspaceUsersToolbarProps { workspaces: WorkspaceEntity[]; selectedWorkspaceId: string | undefined; @@ -48,71 +44,57 @@ export const WorkspaceUsersToolbar = ({ const { deleteDialog, editDialog } = useWorkspaceActions(workspaces.length, selectedWorkspaceId); - const tabItems = workspaces.map((workspace) => ({ key: workspace.id, name: workspace.name })); const workspacesNames = workspaces.map((workspace) => workspace.name); - const handleSelection = (key: Key) => { - onSelectWorkspace(key.toString()); - }; - return ( - - - -
- - {(item: { key: string; name: string }) => { - return ( - - {item.key === selectedWorkspaceId && FEATURE_FLAG_WORKSPACE_ACTIONS ? ( - } - > - handleSelection(id)} - /> - - ) : ( - - )} - - ); - }} - -
- {FEATURE_FLAG_WORKSPACE_ACTIONS && ( - - - - {createWorkspace.isPending ? : } - - Create workspace - - - )} -
-
+ + + items={workspaces} + selectedKey={selectedWorkspaceId || ''} + onSelectionChange={(key) => onSelectWorkspace(String(key))} + renderTabItem={(workspace) => { + const isSelected = workspace.id === selectedWorkspaceId; + + if (isSelected && FEATURE_FLAG_WORKSPACE_ACTIONS) { + return ( + } + > + + + ); + } + + return ; + }} + addButton={ + FEATURE_FLAG_WORKSPACE_ACTIONS ? ( + + + {createWorkspace.isPending ? : } + + Create workspace + + ) : undefined + } + ariaLabel={'Workspace tabs'} + /> {selectedWorkspace && deleteDialog.deleteWorkspaceDialogState.isOpen && ( void; - items: { id: string; name: string }[]; - ariaLabel: string; -} - -export const CollapsedItemsPicker = ({ - items, - ariaLabel, - onSelectionChange, - hasSelectedPinnedItem, - numberOfCollapsedItems, -}: CollapsedItemsPickerProps) => { - return ( - onSelectionChange(String(key))} - > - {(item) => {item.name}} - - ); -}; diff --git a/web_ui/src/shared/components/collapsed-items-picker/collapsed-items-picker.module.scss b/web_ui/src/shared/components/collapsed-items-picker/collapsed-items-picker.module.scss deleted file mode 100644 index fa4db6d23f..0000000000 --- a/web_ui/src/shared/components/collapsed-items-picker/collapsed-items-picker.module.scss +++ /dev/null @@ -1,19 +0,0 @@ -.collapsedItemsPicker { - padding-right: var(--spectrum-global-dimension-size-125); - position: relative; - - &.selected::after { - content: ''; - position: absolute; - height: var(--spectrum-global-dimension-size-25); - left: 0; - bottom: -15px; - right: var(--spectrum-global-dimension-size-400); - background-color: white; - } - - span[class*='spectrum-Dropdown-label'] { - font-size: var(--spectrum-global-dimension-font-size-100) !important; - font-style: normal !important; - } -} diff --git a/web_ui/src/shared/components/custom-tab-item/custom-tab-item.module.scss b/web_ui/src/shared/components/custom-tab-item/custom-tab-item.module.scss index 2fdee32dfe..c50c02630e 100644 --- a/web_ui/src/shared/components/custom-tab-item/custom-tab-item.module.scss +++ b/web_ui/src/shared/components/custom-tab-item/custom-tab-item.module.scss @@ -18,94 +18,3 @@ border: none !important; } } - -.noneSelected .tabList div[class*='is-selected']::after { - display: none; -} - -/* - Overriding vertical tabs is necessary because if the user's monitor display settings - are higher than 100%, then spectrum's tabs collapses to a Picker. This doesn't happen on - vertical tabs. So we decided to use the vertical tabs logic with horizontal tabs layout. - - https://react-spectrum.adobe.com/react-spectrum/Tabs.html#collapse-overflow-behavior - */ -.componentWrapper { - box-sizing: border-box; - - div[class*='spectrum-Tabs--vertical'] { - border-left: 0 !important; - flex-direction: row !important; - padding-right: var(--spectrum-global-dimension-size-100) !important; - - > div[class*='Tabs-item'] { - margin-bottom: 0 !important; - margin-left: 0 !important; - box-sizing: border-box !important; - display: flex; - align-items: center; - - &:first-child { - padding-left: 0 !important; - } - } - } - - div[class*='TabsPanel--vertical'] { - flex-direction: column !important; - padding: 0 !important; - } -} - -.tabList { - span[class*='spectrum-Tabs-itemLabel'] { - font-size: var(--spectrum-global-dimension-font-size-200); - } - - div[class*='is-selected']::after { - transition: background-color 0.15s ease-in-out; - content: ''; - height: var(--spectrum-global-dimension-size-25); - background-color: transparent; - position: absolute; - left: 0; - right: 0; - bottom: 0; - } - - div[class*='is-selected'] { - position: relative; - - &:after { - background-color: var(--energy-blue); - } - } - - div[class*='spectrum-Tabs-selectionIndicator'] { - display: none; - } - - &.emptySelection { - div[class*='is-selected'] { - color: var(--spectrum-tabs-text-color, var(--spectrum-alias-label-text-color)); - } - } - - > div { - border-bottom-width: 0; - } -} - -.tabWrapper { - border-bottom: var(--spectrum-global-dimension-size-10) solid - var(--spectrum-tabs-rule-color, var(--spectrum-global-color-gray-200)) !important; - box-sizing: border-box !important; - display: flex; - align-items: center; -} - -.tabListScrollContainer { - flex: 1 1 auto; - min-width: 0; - overflow: auto hidden; -}