fix icon shrink and use avatar for logo and icons (#9117)

- Fixed icon shrinking on tabs. The introduction of EllipsisDisplay
takes 100% of the width, thus shrinking the icons.
- Removed scroll wrapper tablist. This was removed in #9016 but
reintroduced in #9089. This reintroduction made the dark border below
the active tab disappear.
- Used Avatar for icon and logo rendering following the changes made in
#9093
This commit is contained in:
nitin
2024-12-18 23:36:08 +05:30
committed by GitHub
parent a2423fad5e
commit 5e03f4dfb1
3 changed files with 100 additions and 84 deletions

View File

@ -1,10 +1,10 @@
import { EllipsisDisplay } from '@/ui/field/display/components/EllipsisDisplay';
import isPropValid from '@emotion/is-prop-valid'; import isPropValid from '@emotion/is-prop-valid';
import { useTheme } from '@emotion/react'; import { useTheme } from '@emotion/react';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { ReactElement } from 'react'; import { ReactElement } from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { IconComponent, Pill } from 'twenty-ui'; import { Avatar, IconComponent, Pill } from 'twenty-ui';
import { EllipsisDisplay } from '@/ui/field/display/components/EllipsisDisplay';
type TabProps = { type TabProps = {
id: string; id: string;
@ -54,7 +54,7 @@ const StyledHover = styled.span`
padding-left: ${({ theme }) => theme.spacing(2)}; padding-left: ${({ theme }) => theme.spacing(2)};
padding-right: ${({ theme }) => theme.spacing(2)}; padding-right: ${({ theme }) => theme.spacing(2)};
font-weight: ${({ theme }) => theme.font.weight.medium}; font-weight: ${({ theme }) => theme.font.weight.medium};
width: 100%;
&:hover { &:hover {
background: ${({ theme }) => theme.background.tertiary}; background: ${({ theme }) => theme.background.tertiary};
border-radius: ${({ theme }) => theme.border.radius.sm}; border-radius: ${({ theme }) => theme.border.radius.sm};
@ -63,9 +63,9 @@ const StyledHover = styled.span`
background: ${({ theme }) => theme.background.quaternary}; background: ${({ theme }) => theme.background.quaternary};
} }
`; `;
const StyledLogo = styled.img`
height: 14px; const StyledIconContainer = styled.div`
width: 14px; flex-shrink: 0;
`; `;
export const Tab = ({ export const Tab = ({
@ -81,6 +81,10 @@ export const Tab = ({
logo, logo,
}: TabProps) => { }: TabProps) => {
const theme = useTheme(); const theme = useTheme();
const iconColor = active
? theme.font.color.primary
: theme.font.color.secondary;
return ( return (
<StyledTab <StyledTab
onClick={onClick} onClick={onClick}
@ -92,8 +96,24 @@ export const Tab = ({
to={to} to={to}
> >
<StyledHover> <StyledHover>
{logo && <StyledLogo src={logo} alt={`${title} logo`} />} <StyledIconContainer>
{Icon && <Icon size={theme.icon.size.md} />} {logo && (
<Avatar
avatarUrl={logo}
size="md"
placeholder={title}
iconColor={iconColor}
/>
)}
{Icon && (
<Avatar
Icon={Icon}
size="md"
placeholder={title}
iconColor={iconColor}
/>
)}
</StyledIconContainer>
<EllipsisDisplay>{title}</EllipsisDisplay> <EllipsisDisplay>{title}</EllipsisDisplay>
{pill && typeof pill === 'string' ? <Pill label={pill} /> : pill} {pill && typeof pill === 'string' ? <Pill label={pill} /> : pill}
</StyledHover> </StyledHover>

View File

@ -1,14 +1,12 @@
import styled from '@emotion/styled'; import { TabListFromUrlOptionalEffect } from '@/ui/layout/tab/components/TabListFromUrlOptionalEffect';
import * as React from 'react';
import { IconComponent } from 'twenty-ui';
import { useTabList } from '@/ui/layout/tab/hooks/useTabList'; import { useTabList } from '@/ui/layout/tab/hooks/useTabList';
import { TabListScope } from '@/ui/layout/tab/scopes/TabListScope'; import { TabListScope } from '@/ui/layout/tab/scopes/TabListScope';
import { TabListFromUrlOptionalEffect } from '@/ui/layout/tab/components/TabListFromUrlOptionalEffect';
import { LayoutCard } from '@/ui/layout/tab/types/LayoutCard'; import { LayoutCard } from '@/ui/layout/tab/types/LayoutCard';
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper'; import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
import styled from '@emotion/styled';
import * as React from 'react';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { IconComponent } from 'twenty-ui';
import { Tab } from './Tab'; import { Tab } from './Tab';
export type SingleTabProps = { export type SingleTabProps = {
@ -26,33 +24,25 @@ type TabListProps = {
tabListInstanceId: string; tabListInstanceId: string;
tabs: SingleTabProps[]; tabs: SingleTabProps[];
loading?: boolean; loading?: boolean;
className?: string;
behaveAsLinks?: boolean; behaveAsLinks?: boolean;
className?: string;
}; };
const StyledTabsContainer = styled.div` const StyledContainer = styled.div`
border-bottom: ${({ theme }) => `1px solid ${theme.border.color.light}`};
box-sizing: border-box; box-sizing: border-box;
display: flex; display: flex;
gap: ${({ theme }) => theme.spacing(2)}; gap: ${({ theme }) => theme.spacing(2)};
height: 40px; height: 40px;
user-select: none; user-select: none;
margin-bottom: -1px;
overflow-y: scroll;
::-webkit-scrollbar {
display: none;
}
`;
const StyledContainer = styled.div`
border-bottom: ${({ theme }) => `1px solid ${theme.border.color.light}`};
`; `;
export const TabList = ({ export const TabList = ({
tabs, tabs,
tabListInstanceId, tabListInstanceId,
loading, loading,
className,
behaveAsLinks = true, behaveAsLinks = true,
className,
}: TabListProps) => { }: TabListProps) => {
const visibleTabs = tabs.filter((tab) => !tab.hide); const visibleTabs = tabs.filter((tab) => !tab.hide);
@ -69,39 +59,37 @@ export const TabList = ({
} }
return ( return (
<StyledContainer className={className}> <TabListScope tabListScopeId={tabListInstanceId}>
<TabListScope tabListScopeId={tabListInstanceId}> <TabListFromUrlOptionalEffect
<TabListFromUrlOptionalEffect componentInstanceId={tabListInstanceId}
componentInstanceId={tabListInstanceId} tabListIds={tabs.map((tab) => tab.id)}
tabListIds={tabs.map((tab) => tab.id)} />
/> <ScrollWrapper
<ScrollWrapper defaultEnableYScroll={false}
defaultEnableYScroll={false} contextProviderName="tabList"
contextProviderName="tabList" componentInstanceId={`scroll-wrapper-tab-list-${tabListInstanceId}`}
componentInstanceId={`scroll-wrapper-tab-list-${tabListInstanceId}`} >
> <StyledContainer className={className}>
<StyledTabsContainer> {visibleTabs.map((tab) => (
{visibleTabs.map((tab) => ( <Tab
<Tab id={tab.id}
id={tab.id} key={tab.id}
key={tab.id} title={tab.title}
title={tab.title} Icon={tab.Icon}
Icon={tab.Icon} logo={tab.logo}
logo={tab.logo} active={tab.id === activeTabId}
active={tab.id === activeTabId} disabled={tab.disabled ?? loading}
disabled={tab.disabled ?? loading} pill={tab.pill}
pill={tab.pill} to={behaveAsLinks ? `#${tab.id}` : undefined}
to={behaveAsLinks ? `#${tab.id}` : undefined} onClick={() => {
onClick={() => { if (!behaveAsLinks) {
if (!behaveAsLinks) { setActiveTabId(tab.id);
setActiveTabId(tab.id); }
} }}
}} />
/> ))}
))} </StyledContainer>
</StyledTabsContainer> </ScrollWrapper>
</ScrollWrapper> </TabListScope>
</TabListScope>
</StyledContainer>
); );
}; };

View File

@ -8,6 +8,22 @@ import { workflowIdState } from '@/workflow/states/workflowIdState';
import { WorkflowCodeAction } from '@/workflow/types/Workflow'; import { WorkflowCodeAction } from '@/workflow/types/Workflow';
import { setNestedValue } from '@/workflow/utils/setNestedValue'; import { setNestedValue } from '@/workflow/utils/setNestedValue';
import { CmdEnterActionButton } from '@/action-menu/components/CmdEnterActionButton';
import { ServerlessFunctionExecutionResult } from '@/serverless-functions/components/ServerlessFunctionExecutionResult';
import { INDEX_FILE_PATH } from '@/serverless-functions/constants/IndexFilePath';
import { useTestServerlessFunction } from '@/serverless-functions/hooks/useTestServerlessFunction';
import { getFunctionInputFromSourceCode } from '@/serverless-functions/utils/getFunctionInputFromSourceCode';
import { getFunctionOutputSchema } from '@/serverless-functions/utils/getFunctionOutputSchema';
import { mergeDefaultFunctionInputAndFunctionInput } from '@/serverless-functions/utils/mergeDefaultFunctionInputAndFunctionInput';
import { InputLabel } from '@/ui/input/components/InputLabel';
import { RightDrawerFooter } from '@/ui/layout/right-drawer/components/RightDrawerFooter';
import { TabList } from '@/ui/layout/tab/components/TabList';
import { useTabList } from '@/ui/layout/tab/hooks/useTabList';
import { WorkflowStepBody } from '@/workflow/components/WorkflowStepBody';
import { WorkflowVariablePicker } from '@/workflow/components/WorkflowVariablePicker';
import { serverlessFunctionTestDataFamilyState } from '@/workflow/states/serverlessFunctionTestDataFamilyState';
import { WorkflowEditActionFormServerlessFunctionFields } from '@/workflow/workflow-actions/components/WorkflowEditActionFormServerlessFunctionFields';
import { WORKFLOW_SERVERLESS_FUNCTION_TAB_LIST_COMPONENT_ID } from '@/workflow/workflow-actions/constants/WorkflowServerlessFunctionTabListComponentId';
import { useTheme } from '@emotion/react'; import { useTheme } from '@emotion/react';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { Monaco } from '@monaco-editor/react'; import { Monaco } from '@monaco-editor/react';
@ -17,22 +33,6 @@ import { useEffect, useState } from 'react';
import { useRecoilState, useRecoilValue } from 'recoil'; import { useRecoilState, useRecoilValue } from 'recoil';
import { CodeEditor, IconCode, IconPlayerPlay, isDefined } from 'twenty-ui'; import { CodeEditor, IconCode, IconPlayerPlay, isDefined } from 'twenty-ui';
import { useDebouncedCallback } from 'use-debounce'; import { useDebouncedCallback } from 'use-debounce';
import { WorkflowStepBody } from '@/workflow/components/WorkflowStepBody';
import { TabList } from '@/ui/layout/tab/components/TabList';
import { useTabList } from '@/ui/layout/tab/hooks/useTabList';
import { WorkflowVariablePicker } from '@/workflow/components/WorkflowVariablePicker';
import { serverlessFunctionTestDataFamilyState } from '@/workflow/states/serverlessFunctionTestDataFamilyState';
import { ServerlessFunctionExecutionResult } from '@/serverless-functions/components/ServerlessFunctionExecutionResult';
import { INDEX_FILE_PATH } from '@/serverless-functions/constants/IndexFilePath';
import { InputLabel } from '@/ui/input/components/InputLabel';
import { RightDrawerFooter } from '@/ui/layout/right-drawer/components/RightDrawerFooter';
import { CmdEnterActionButton } from '@/action-menu/components/CmdEnterActionButton';
import { useTestServerlessFunction } from '@/serverless-functions/hooks/useTestServerlessFunction';
import { getFunctionOutputSchema } from '@/serverless-functions/utils/getFunctionOutputSchema';
import { getFunctionInputFromSourceCode } from '@/serverless-functions/utils/getFunctionInputFromSourceCode';
import { mergeDefaultFunctionInputAndFunctionInput } from '@/serverless-functions/utils/mergeDefaultFunctionInputAndFunctionInput';
import { WorkflowEditActionFormServerlessFunctionFields } from '@/workflow/workflow-actions/components/WorkflowEditActionFormServerlessFunctionFields';
import { WORKFLOW_SERVERLESS_FUNCTION_TAB_LIST_COMPONENT_ID } from '@/workflow/workflow-actions/constants/WorkflowServerlessFunctionTabListComponentId';
const StyledContainer = styled.div` const StyledContainer = styled.div`
display: flex; display: flex;
@ -45,10 +45,14 @@ const StyledCodeEditorContainer = styled.div`
flex-direction: column; flex-direction: column;
`; `;
const StyledTabList = styled(TabList)` const StyledTabListContainer = styled.div`
background: ${({ theme }) => theme.background.secondary}; align-items: center;
padding-left: ${({ theme }) => theme.spacing(2)}; padding-left: ${({ theme }) => theme.spacing(2)};
padding-right: ${({ theme }) => theme.spacing(2)}; border-bottom: ${({ theme }) => `1px solid ${theme.border.color.light}`};
box-sizing: border-box;
display: flex;
gap: ${({ theme }) => theme.spacing(2)};
height: ${({ theme }) => theme.spacing(10)};
`; `;
type WorkflowEditActionFormServerlessFunctionProps = { type WorkflowEditActionFormServerlessFunctionProps = {
@ -263,11 +267,15 @@ export const WorkflowEditActionFormServerlessFunction = ({
return ( return (
!loading && ( !loading && (
<StyledContainer> <StyledContainer>
<StyledTabList <StyledTabListContainer>
tabListInstanceId={WORKFLOW_SERVERLESS_FUNCTION_TAB_LIST_COMPONENT_ID} <TabList
tabs={tabs} tabListInstanceId={
behaveAsLinks={false} WORKFLOW_SERVERLESS_FUNCTION_TAB_LIST_COMPONENT_ID
/> }
tabs={tabs}
behaveAsLinks={false}
/>
</StyledTabListContainer>
<WorkflowStepHeader <WorkflowStepHeader
onTitleChange={(newName: string) => { onTitleChange={(newName: string) => {
updateAction({ name: newName }); updateAction({ name: newName });