mirror of
https://github.com/datahub-project/datahub.git
synced 2025-06-27 05:03:31 +00:00
Updated drawer to match entity drawer styling (#13665)
This commit is contained in:
parent
c913aa4161
commit
c997668110
@ -76,7 +76,7 @@ export function AboutFieldTab({ properties }: AboutFieldTabProps) {
|
||||
notes={notes}
|
||||
refetch={delayedRefetchNotes}
|
||||
/>
|
||||
{!!notes?.length && <StyledDivider dashed />}
|
||||
{!!notes?.length && <StyledDivider />}
|
||||
<FieldDescription expandedField={expandedField} editableFieldInfo={editableFieldInfo} />
|
||||
<FieldTags
|
||||
expandedField={expandedField}
|
||||
|
@ -9,7 +9,7 @@ import { DeprecationIcon } from '@app/entityV2/shared/components/styled/Deprecat
|
||||
import { REDESIGN_COLORS } from '@app/entityV2/shared/constants';
|
||||
import { FieldPopularity } from '@app/entityV2/shared/tabs/Dataset/Schema/components/SchemaFieldDrawer/FieldPopularity';
|
||||
import SchemaEditableContext from '@app/shared/SchemaEditableContext';
|
||||
import { Button } from '@src/alchemy-components';
|
||||
import { Button, colors } from '@src/alchemy-components';
|
||||
import MarkAsDeprecatedButton from '@src/app/entityV2/shared/components/styled/MarkAsDeprecatedButton';
|
||||
|
||||
import { Deprecation, SubResourceType, UsageQueryResult } from '@types';
|
||||
@ -21,13 +21,13 @@ const FieldDetailsWrapper = styled.div`
|
||||
const FieldDetailsContent = styled.div`
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
border-bottom: 1px dashed;
|
||||
border-color: rgba(0, 0, 0, 0.3);
|
||||
border-bottom: 1px solid;
|
||||
border-color: ${colors.gray[100]};
|
||||
padding-bottom: 16px;
|
||||
& > div {
|
||||
&:not(:first-child) {
|
||||
border-left: 1px dashed;
|
||||
border-color: rgba(0, 0, 0, 0.3);
|
||||
border-left: 1px solid;
|
||||
border-color: ${colors.gray[100]};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
@ -22,7 +22,7 @@ const FieldHeaderWrapper = styled.div`
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
border-bottom: 1px solid rgb(213, 213, 213);
|
||||
border-bottom: 1px solid ${colors.gray[100]};
|
||||
`;
|
||||
|
||||
const NameTypesWrapper = styled.div`
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { CodeOutlined, ReadOutlined, UnorderedListOutlined } from '@ant-design/icons';
|
||||
import QueryStatsOutlinedIcon from '@mui/icons-material/QueryStatsOutlined';
|
||||
import { BookOpen, ChartBar, Code, ListBullets } from '@phosphor-icons/react';
|
||||
import { Drawer, Typography } from 'antd';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
@ -181,7 +180,7 @@ export default function SchemaFieldDrawer({
|
||||
const tabs: any = [
|
||||
{
|
||||
name: 'About',
|
||||
icon: ReadOutlined,
|
||||
icon: BookOpen,
|
||||
component: AboutFieldTab,
|
||||
properties: {
|
||||
schemaFields,
|
||||
@ -198,7 +197,7 @@ export default function SchemaFieldDrawer({
|
||||
},
|
||||
{
|
||||
name: 'Statistics',
|
||||
icon: QueryStatsOutlinedIcon,
|
||||
icon: ChartBar,
|
||||
component: StatsSidebarView,
|
||||
properties: {
|
||||
expandedField,
|
||||
@ -212,14 +211,14 @@ export default function SchemaFieldDrawer({
|
||||
name: 'Queries',
|
||||
component: SchemaFieldQueriesSidebarTab,
|
||||
description: 'View queries about this field',
|
||||
icon: CodeOutlined,
|
||||
icon: Code,
|
||||
properties: { fieldPath: expandedField?.fieldPath },
|
||||
},
|
||||
{
|
||||
name: 'Properties',
|
||||
component: PropertiesTab,
|
||||
description: 'View additional properties about this field',
|
||||
icon: UnorderedListOutlined,
|
||||
icon: ListBullets,
|
||||
properties: {
|
||||
fieldPath: expandedField?.fieldPath,
|
||||
fieldUrn: expandedField?.schemaFieldEntity?.urn,
|
||||
|
@ -1,39 +1,126 @@
|
||||
import { Tooltip } from '@components';
|
||||
import { Tooltip, colors } from '@components';
|
||||
import { Tabs } from 'antd';
|
||||
import React from 'react';
|
||||
import styled from 'styled-components/macro';
|
||||
|
||||
import { EntitySidebarTab } from '@app/entityV2/shared/types';
|
||||
|
||||
export const TABS_WIDTH = 56;
|
||||
export const TABS_WIDTH = 64;
|
||||
|
||||
const UnborderedTabs = styled(Tabs)`
|
||||
const UnborderedTabs = styled(Tabs).attrs({ className: 'schema-field-drawer-tabs' })`
|
||||
height: 100%;
|
||||
width: ${TABS_WIDTH}px;
|
||||
&&& .ant-tabs-nav {
|
||||
margin-bottom: 0;
|
||||
box-sizing: border-box;
|
||||
user-select: none;
|
||||
overflow: visible;
|
||||
background-color: #ffffff;
|
||||
|
||||
.ant-tabs-nav {
|
||||
margin: 0;
|
||||
width: ${TABS_WIDTH}px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
overflow: visible;
|
||||
}
|
||||
&&& .ant-tabs-ink-bar {
|
||||
|
||||
.ant-tabs-nav-operations {
|
||||
display: none;
|
||||
}
|
||||
&&& .ant-tabs-tab {
|
||||
padding: 10px 10px 10px 10px;
|
||||
margin: 8px 8px 16px 8px;
|
||||
border-radius: 6px;
|
||||
|
||||
.ant-tabs-nav-wrap {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
min-width: ${TABS_WIDTH}px;
|
||||
box-sizing: border-box;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.ant-tabs-nav-list {
|
||||
margin: 0;
|
||||
gap: 4px;
|
||||
width: ${TABS_WIDTH}px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 4px 0px;
|
||||
box-sizing: border-box;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.ant-tabs-ink-bar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ant-tabs-tab {
|
||||
padding: 0;
|
||||
margin: 0 0 4px 0;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 52px;
|
||||
height: 52px;
|
||||
transition: none;
|
||||
overflow: visible;
|
||||
|
||||
.anticon {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.ant-tabs-tab-btn {
|
||||
color: inherit;
|
||||
transition: none;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
rgba(243, 244, 246, 0.5) -3.99%,
|
||||
rgba(235, 236, 240, 0.5) 53.04%,
|
||||
rgba(235, 236, 240, 0.5) 100%
|
||||
);
|
||||
box-shadow: 0px 0px 0px 1px rgba(139, 135, 157, 0.08);
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
&&& .ant-tabs-tab-active {
|
||||
background-color: ${(p) => p.theme.styles['primary-color']};
|
||||
|
||||
.ant-tabs-tab-active {
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
rgba(83, 63, 209, 0.04) -3.99%,
|
||||
rgba(112, 94, 228, 0.04) 53.04%,
|
||||
rgba(112, 94, 228, 0.04) 100%
|
||||
);
|
||||
box-shadow: 0px 0px 0px 1px rgba(108, 71, 255, 0.08);
|
||||
|
||||
.ant-tabs-tab-btn {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
rgba(83, 63, 209, 0.04) -3.99%,
|
||||
rgba(112, 94, 228, 0.04) 53.04%,
|
||||
rgba(112, 94, 228, 0.04) 100%
|
||||
);
|
||||
box-shadow: 0px 0px 0px 1px rgba(139, 135, 157, 0.08);
|
||||
}
|
||||
}
|
||||
&&& .ant-tabs-tab-active .ant-tabs-tab-btn {
|
||||
color: #ffffff;
|
||||
}
|
||||
&&& .ant-tabs-content-holder {
|
||||
|
||||
.ant-tabs-content-holder {
|
||||
display: none;
|
||||
}
|
||||
background-color: #ffffff;
|
||||
`;
|
||||
|
||||
const Tab = styled(Tabs.TabPane)`
|
||||
@ -43,35 +130,178 @@ const Tab = styled(Tabs.TabPane)`
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
const tabIconStyle = { fontSize: 20, margin: 0, display: 'flex' };
|
||||
const TabIconContainer = styled.div<{ $isSelected?: boolean }>`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: none;
|
||||
color: ${colors.gray[1800]};
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
gap: 2px;
|
||||
user-select: none;
|
||||
`;
|
||||
|
||||
const TabText = styled.span<{ $isSelected?: boolean }>`
|
||||
font-size: 10px;
|
||||
font-weight: ${(props) => (props.$isSelected ? '500' : '400')};
|
||||
text-align: center;
|
||||
transition: none;
|
||||
user-select: none;
|
||||
display: block;
|
||||
width: 48px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: ${colors.gray[1800]};
|
||||
|
||||
${(props) =>
|
||||
props.$isSelected &&
|
||||
`
|
||||
color: transparent;
|
||||
background: linear-gradient(#7565d6 20%, #5340cc 80%);
|
||||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
`}
|
||||
`;
|
||||
|
||||
const IconWrapper = styled.div<{ $isSelected?: boolean }>`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: none;
|
||||
width: 20px;
|
||||
height: 18px;
|
||||
position: relative;
|
||||
|
||||
/* For Phosphor icons */
|
||||
svg {
|
||||
${(props) => (props.$isSelected ? 'fill: url(#menu-item-selected-gradient) #533fd1;' : 'color: #8088a3;')}
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
min-width: 20px;
|
||||
min-height: 20px;
|
||||
max-width: 20px;
|
||||
max-height: 20px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
/* For Ant Design icons */
|
||||
span {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
${(props) => (props.$isSelected ? 'color: url(#menu-item-selected-gradient) #533fd1;' : 'color: #8088a3;')}
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
|
||||
svg {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
min-width: 20px;
|
||||
min-height: 20px;
|
||||
max-width: 20px;
|
||||
max-height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Ensure Phosphor icon weights are correctly applied */
|
||||
.ph-fill {
|
||||
fill: ${(props) => (props.$isSelected ? 'url(#menu-item-selected-gradient) #533fd1' : '#8088a3')};
|
||||
}
|
||||
`;
|
||||
|
||||
const TabTextWithTooltip = ({ text, isSelected }: { text: string; isSelected?: boolean }) => {
|
||||
const textRef = React.useRef<HTMLSpanElement>(null);
|
||||
const [isOverflowing, setIsOverflowing] = React.useState(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
const element = textRef.current;
|
||||
if (element) {
|
||||
setIsOverflowing(element.scrollWidth > element.clientWidth);
|
||||
}
|
||||
}, [text]);
|
||||
|
||||
return (
|
||||
<Tooltip title={isOverflowing ? text : null} placement="left" showArrow={false}>
|
||||
<TabText ref={textRef} $isSelected={isSelected}>
|
||||
{text}
|
||||
</TabText>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
const GradientDefs = () => (
|
||||
<svg width="0" height="0">
|
||||
<defs>
|
||||
<linearGradient id="menu-item-selected-gradient" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="20%" stopColor="#7565d6" />
|
||||
<stop offset="80%" stopColor="#5340cc" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
|
||||
const TabsWrapper = styled.div`
|
||||
width: ${TABS_WIDTH}px;
|
||||
flex-shrink: 0;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
overflow: visible;
|
||||
`;
|
||||
|
||||
type Props = {
|
||||
tabs: EntitySidebarTab[];
|
||||
selectedTab?: EntitySidebarTab;
|
||||
onSelectTab: (name: string) => void;
|
||||
};
|
||||
|
||||
export const SchemaFieldDrawerTabs = ({ tabs, selectedTab, onSelectTab }: Props) => {
|
||||
return (
|
||||
<UnborderedTabs
|
||||
animated={false}
|
||||
tabPosition="right"
|
||||
activeKey={selectedTab?.name || ''}
|
||||
size="large"
|
||||
onTabClick={(name: string) => onSelectTab(name)}
|
||||
>
|
||||
{tabs.map((tab) => {
|
||||
const TabIcon = tab.icon;
|
||||
const { name } = tab;
|
||||
return (
|
||||
<Tab
|
||||
tab={
|
||||
<Tooltip title={name} placement="left" showArrow={false}>
|
||||
<TabIcon style={tabIconStyle} data-testid={`${name}-field-drawer-tab-header`} />
|
||||
</Tooltip>
|
||||
}
|
||||
key={tab.name}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</UnborderedTabs>
|
||||
<TabsWrapper>
|
||||
<GradientDefs />
|
||||
<UnborderedTabs
|
||||
animated={false}
|
||||
tabPosition="right"
|
||||
activeKey={selectedTab?.name || ''}
|
||||
onTabClick={(name: string) => onSelectTab(name)}
|
||||
>
|
||||
{tabs.map((tab) => {
|
||||
const TabIcon = tab.icon;
|
||||
const SelectedTabIcon = tab.selectedIcon || tab.icon;
|
||||
const { name } = tab;
|
||||
const isSelected = selectedTab?.name === tab.name;
|
||||
|
||||
return (
|
||||
<Tab
|
||||
tab={
|
||||
<TabIconContainer $isSelected={isSelected}>
|
||||
<IconWrapper $isSelected={isSelected}>
|
||||
{isSelected ? (
|
||||
<SelectedTabIcon size={20} weight="fill" />
|
||||
) : (
|
||||
<TabIcon size={20} weight="regular" />
|
||||
)}
|
||||
</IconWrapper>
|
||||
<TabTextWithTooltip text={name} isSelected={isSelected} />
|
||||
</TabIconContainer>
|
||||
}
|
||||
key={tab.name}
|
||||
data-testid={`${name}-field-drawer-tab-header`}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</UnborderedTabs>
|
||||
</TabsWrapper>
|
||||
);
|
||||
};
|
||||
|
@ -8,6 +8,7 @@ import { decimalToPercentStr } from '@app/entityV2/shared/tabs/Dataset/Schema/ut
|
||||
import SampleValueTag from '@app/entityV2/shared/tabs/Dataset/Stats/snapshot/SampleValueTag';
|
||||
import { extractChartValuesFromFieldProfiles } from '@app/entityV2/shared/utils';
|
||||
import { formatNumberWithoutAbbreviation } from '@app/shared/formatNumber';
|
||||
import { colors } from '@src/alchemy-components';
|
||||
|
||||
import { DatasetFieldProfile, SchemaField } from '@types';
|
||||
|
||||
@ -38,8 +39,7 @@ const StatLabel = styled.div`
|
||||
padding-top: 12px;
|
||||
padding-bottom: 12px;
|
||||
:not(:last-child) {
|
||||
border-bottom: 1px dashed;
|
||||
border-color: rgba(0, 0, 0, 0.3);
|
||||
border-bottom: 1px solid ${colors.gray[100]};
|
||||
}
|
||||
`;
|
||||
|
||||
|
@ -2,6 +2,7 @@ import { Divider } from 'antd';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { REDESIGN_COLORS } from '@app/entityV2/shared/constants';
|
||||
import { colors } from '@src/alchemy-components';
|
||||
|
||||
export const SectionHeader = styled.div`
|
||||
margin-bottom: 8px;
|
||||
@ -13,5 +14,6 @@ export const SectionHeader = styled.div`
|
||||
`;
|
||||
|
||||
export const StyledDivider = styled(Divider)`
|
||||
border-color: rgba(0, 0, 0, 0.3);
|
||||
border-color: ${colors.gray[100]};
|
||||
border-style: solid;
|
||||
`;
|
||||
|
Loading…
x
Reference in New Issue
Block a user