Skip to content

Commit

Permalink
feat: Main layout NEO header (#2844)
Browse files Browse the repository at this point in the history
Resolves #2838

## Neo Header

![image.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/XqC2uNFuj0wg8I60sMUh/f89f704f-9534-464f-a472-d6d5a277c190.png)

![image.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/XqC2uNFuj0wg8I60sMUh/583b2b0a-3f47-416c-8fcd-7c8d9eb5d7c0.png)

![image.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/XqC2uNFuj0wg8I60sMUh/bdc65869-af10-4aeb-818e-aa0993f08f81.png)

<img width="1291" alt="image" src="https://github.com/user-attachments/assets/d603357c-9691-4cd1-a399-f54e0fef7b9c">

**Changes:**
- Redesigned the WebUI header with a new orange theme and improved layout
- Added ghost style select component for better visibility on colored backgrounds
- Added reverse theme provider component to handle light/dark theme switching
- Updated login session extension UI with a more compact design
- Added classnames dependency for better CSS class handling
- Updated translations for "Extend Login Session" across all languages

**Key UI/UX Improvements:**
- Header now uses a distinct orange background color for better visual hierarchy
- Project selector uses ghost style for better contrast on colored backgrounds
- Login session timer shows condensed format on smaller screens
- Notification and user menu buttons have consistent styling with the header theme

**Technical Details:**
- Removed inline styles in favor of styled components
- Added ReverseThemeProvider for proper light/dark theme handling in the header
- Updated theme configuration with new color tokens and layout settings
- Improved responsive behavior for mobile devices
  • Loading branch information
yomybaby committed Nov 20, 2024
1 parent 19f959b commit f3ac7fa
Show file tree
Hide file tree
Showing 36 changed files with 356 additions and 248 deletions.
2 changes: 1 addition & 1 deletion main.js
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ function createWindow() {
height: windowHeight,
title: 'Backend.AI',
frame: true,
titleBarStyle: 'hiddenInset',
titleBarStyle: 'customButtonsOnHover',
webPreferences: {
nativeWindowOpen: true,
nodeIntegration: false,
Expand Down
1 change: 1 addition & 0 deletions react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"ansi_up": "^6.0.2",
"antd": "^5.20.0",
"antd-style": "^3.6.2",
"classnames": "^2.5.1",
"dayjs": "^1.11.12",
"graphql": "^16.9.0",
"i18next": "^23.12.2",
Expand Down
3 changes: 3 additions & 0 deletions react/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

67 changes: 20 additions & 47 deletions react/src/components/BAIMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,56 +1,29 @@
import { ConfigProvider, Menu, MenuProps, theme } from 'antd';
import { ConfigProvider, Menu, MenuProps } from 'antd';
import React from 'react';

// interface BAIMenuProps extends MenuProps {

// }

const BAIMenu: React.FC<MenuProps> = ({ ...props }) => {
const { token } = theme.useToken();
return (
<>
<style>
{`
.bai-menu li.ant-menu-item.ant-menu-item-selected {
overflow: visible;
font-weight: 600;
}
.bai-menu li.ant-menu-item.ant-menu-item-selected::before {
left: 0px;
top: 0;
bottom: 0;
position: absolute;
right: auto;
border-right: 3px solid ${token.colorPrimary};
transform: scaleY(1);
opacity: 1;
content: "";
}
`}
</style>
<ConfigProvider
theme={{
components: {
Menu: {
itemBorderRadius: 2,
itemMarginInline: 0,
},
<ConfigProvider
theme={{
components: {
Menu: {
itemBorderRadius: 2,
itemMarginInline: 0,
},
},
}}
>
<Menu
style={{
backgroundColor: 'transparent',
borderRight: 'none',
paddingRight: 4,
}}
>
<Menu
style={{
backgroundColor: 'transparent',
borderRight: 'none',
paddingRight: 4,
}}
mode="inline"
{...props}
className="bai-menu"
/>
</ConfigProvider>
</>
// mode=""
{...props}
className="bai-menu"
/>
</ConfigProvider>
);
};

Expand Down
32 changes: 19 additions & 13 deletions react/src/components/BAINotificationButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,15 @@ import { atom, useAtom } from 'jotai';
import _ from 'lodash';
import React, { useEffect } from 'react';

interface Props extends ButtonProps {}
interface Props extends ButtonProps {
buttonRender?: (defaultButton: React.ReactNode) => React.ReactNode;
}
export const isOpenDrawerState = atom(false);

const BAINotificationButton: React.FC<Props> = ({ ...props }) => {
const BAINotificationButton: React.FC<Props> = ({
buttonRender = (btn) => btn,
...props
}) => {
const [notifications, { upsertNotification }] = useBAINotificationState();
useBAINotificationEffect();

Expand All @@ -33,17 +38,18 @@ const BAINotificationButton: React.FC<Props> = ({ ...props }) => {
});
return (
<>
<Button
size="large"
icon={
<Badge dot={hasRunningBackgroundTask}>
<BellOutlined />
</Badge>
}
type="text"
onClick={() => setIsOpenDrawer((v) => !v)}
{...props}
/>
{buttonRender(
<Button
icon={
<Badge dot={hasRunningBackgroundTask}>
<BellOutlined />
</Badge>
}
type="text"
onClick={() => setIsOpenDrawer((v) => !v)}
{...props}
/>,
)}
<WEBUINotificationDrawer
open={isOpenDrawer}
onClose={() => setIsOpenDrawer((v) => !v)}
Expand Down
54 changes: 52 additions & 2 deletions react/src/components/BAISelect.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,45 @@
import { Select, SelectProps } from 'antd';
import { createStyles } from 'antd-style';
import classNames from 'classnames';
import _ from 'lodash';
import React, { useLayoutEffect } from 'react';

interface BAISelectProps extends SelectProps {
const useStyles = createStyles(({ css, token }) => ({
ghostSelect: css`
&.ant-select {
.ant-select-selector {
background-color: transparent;
border-color: ${token.colorBgBase} !important;
/* box-shadow: none; */
color: ${token.colorBgBase};
/* transition: color 0.3s, border-color 0.3s; */
}
&:hover .ant-select-selector {
background-color: rgb(255 255 255 / 10%);
}
&:active .ant-select-selector {
background-color: rgb(255 255 255 / 10%);
}
.ant-select-arrow {
color: ${token.colorBgBase};
}
&:hover .ant-select-arrow {
color: ${token.colorBgBase};
}
&:active .ant-select-arrow {
color: ${token.colorBgBase};
}
}
`,
}));

export interface BAISelectProps extends SelectProps {
ghost?: boolean;
autoSelectOption?: boolean | ((options: SelectProps['options']) => any);
}
/**
Expand All @@ -18,9 +55,12 @@ interface BAISelectProps extends SelectProps {
*/
const BAISelect: React.FC<BAISelectProps> = ({
autoSelectOption,
ghost,
...selectProps
}) => {
const { value, options, onChange } = selectProps;
const { styles } = useStyles();

useLayoutEffect(() => {
if (autoSelectOption && _.isEmpty(value) && options?.[0]) {
if (_.isBoolean(autoSelectOption)) {
Expand All @@ -30,7 +70,17 @@ const BAISelect: React.FC<BAISelectProps> = ({
}
}
}, [value, options, onChange, autoSelectOption]);
return <Select {...selectProps} />;

return (
<Select
{...selectProps}
className={
ghost
? classNames(styles.ghostSelect, selectProps.className)
: selectProps.className
}
/>
);
};

export default BAISelect;
84 changes: 53 additions & 31 deletions react/src/components/LoginSessionExtendButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import { useSuspenseTanQuery } from '../hooks/reactQueryAlias';
import BAIIntervalView from './BAIIntervalView';
import Flex from './Flex';
import { ClockCircleOutlined } from '@ant-design/icons';
import { Button, Tooltip } from 'antd';
import { Button, ConfigProvider, Grid, Tooltip } from 'antd';
import { default as dayjs } from 'dayjs';
import { Repeat2Icon } from 'lucide-react';
import React, { useTransition } from 'react';
import { useTranslation } from 'react-i18next';

Expand All @@ -19,6 +20,8 @@ const LoginSessionExtendButton: React.FC<
const [isPending, startTransition] = useTransition();
const [fetchKey, updateFetchKey] = useUpdatableState('first');

const gridBreakpoint = Grid.useBreakpoint();

const { data } = useSuspenseTanQuery<{
expires: string;
}>({
Expand All @@ -33,37 +36,56 @@ const LoginSessionExtendButton: React.FC<
});

return (
<Flex direction="row" gap="xxs">
<Tooltip title={t('general.RemainingLoginSessionTime')}>
<Flex gap={'xxs'}>
<ClockCircleOutlined />
<BAIIntervalView
callback={() => {
const diff = dayjs(data?.expires).diff(dayjs(), 'seconds');
const duration = dayjs.duration(Math.max(0, diff), 'seconds');
const days = duration.days();
if (duration.seconds() === 0) {
// @ts-ignore
if (globalThis.isElectron) {
// @ts-ignore
globalThis.location.href = globalThis.electronInitialHref;
} else {
globalThis.location.reload();
}
}
return `${days ? days + 'd ' : ''}${duration.format('HH:mm:ss')}`;
}}
delay={100}
></BAIIntervalView>
</Flex>
</Tooltip>
<Button
loading={isPending}
onClick={() => startTransition(() => updateFetchKey())}
size="small"
<Flex direction="row" gap="xs">
<BAIIntervalView
callback={() => {
const diff = dayjs(data?.expires).diff(dayjs(), 'seconds');
const duration = dayjs.duration(Math.max(0, diff), 'seconds');
const days = duration.days();
if (duration.seconds() === 0) {
// @ts-ignore
if (globalThis.isElectron) {
// @ts-ignore
globalThis.location.href = globalThis.electronInitialHref;
} else {
globalThis.location.reload();
}
}
return gridBreakpoint.lg
? `${days ? days + 'd ' : ''}${duration.format('HH:mm:ss')}`
: days
? days + 'd'
: duration.format('HH:mm:ss');
}}
delay={100}
render={(text) => {
return (
<Tooltip title={t('general.RemainingLoginSessionTime')}>
<Flex gap={'xxs'}>
<ClockCircleOutlined />
{text}
</Flex>
</Tooltip>
);
}}
/>
<ConfigProvider
theme={{
token: {
// hack to change the primary hover color for header
colorPrimaryHover: 'rgb(255,255,255,0.15)',
},
}}
>
{t('general.Extend')}
</Button>
<Tooltip title={t('general.ExtendLoginSession')}>
<Button
type="primary"
loading={isPending}
onClick={() => startTransition(() => updateFetchKey())}
icon={<Repeat2Icon />}
/>
</Tooltip>
</ConfigProvider>
</Flex>
);
};
Expand Down
8 changes: 0 additions & 8 deletions react/src/components/MainLayout/WebUIHeader.css

This file was deleted.

Loading

0 comments on commit f3ac7fa

Please sign in to comment.