feat: support dark mode (#2945)

This commit is contained in:
Kilu.He 2023-07-07 21:58:15 +08:00 committed by GitHub
parent 0401fc4b22
commit 4c17298432
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
123 changed files with 1351 additions and 465 deletions

View File

@ -1,21 +1,14 @@
import { Routes, Route, BrowserRouter } from 'react-router-dom';
import { BrowserRouter } from 'react-router-dom';
import { ColorPalette } from './components/tests/ColorPalette';
import { Provider } from 'react-redux';
import { store } from './stores/store';
import { DocumentPage } from './views/DocumentPage';
import { BoardPage } from './views/BoardPage';
import { GridPage } from './views/GridPage';
import { LoginPage } from './views/LoginPage';
import { ProtectedRoutes } from './components/auth/ProtectedRoutes';
import { SignUpPage } from './views/SignUpPage';
import { ConfirmAccountPage } from './views/ConfirmAccountPage';
import { ErrorHandlerPage } from './components/error/ErrorHandlerPage';
import initializeI18n from './stores/i18n/initializeI18n';
import { TestAPI } from './components/tests/TestAPI';
import { GetStarted } from './components/auth/GetStarted/GetStarted';
import { ErrorBoundary } from 'react-error-boundary';
import { AllIcons } from '$app/components/tests/AllIcons';
import AppMain from '$app/AppMain';
initializeI18n();
@ -24,20 +17,7 @@ const App = () => {
<BrowserRouter>
<Provider store={store}>
<ErrorBoundary FallbackComponent={ErrorHandlerPage}>
<Routes>
<Route path={'/'} element={<ProtectedRoutes />}>
<Route path={'/page/all-icons'} element={<AllIcons />} />
<Route path={'/page/colors'} element={<ColorPalette />} />
<Route path={'/page/api-test'} element={<TestAPI />} />
<Route path={'/page/document/:id'} element={<DocumentPage />} />
<Route path={'/page/board/:id'} element={<BoardPage />} />
<Route path={'/page/grid/:id'} element={<GridPage />} />
</Route>
<Route path={'/auth/login'} element={<LoginPage />}></Route>
<Route path={'/auth/getStarted'} element={<GetStarted />}></Route>
<Route path={'/auth/signUp'} element={<SignUpPage />}></Route>
<Route path={'/auth/confirm-account'} element={<ConfirmAccountPage />}></Route>
</Routes>
<AppMain />
</ErrorBoundary>
</Provider>
</BrowserRouter>

View File

@ -0,0 +1,43 @@
import { useAppDispatch, useAppSelector } from '$app/stores/store';
import { useEffect, useMemo } from 'react';
import { UserSettingController } from '$app/stores/effects/user/user_setting_controller';
import { currentUserActions } from '$app_reducers/current-user/slice';
import { Theme as ThemeType, Theme, ThemeMode } from '$app/interfaces';
import { createTheme } from '@mui/material/styles';
import { getDesignTokens } from '$app/utils/mui';
export function useUserSetting() {
const dispatch = useAppDispatch();
const currentUser = useAppSelector((state) => state.currentUser);
const userSettingController = useMemo(() => {
if (!currentUser?.id) return;
const controller = new UserSettingController(currentUser.id);
return controller;
}, [currentUser?.id]);
useEffect(() => {
userSettingController?.getAppearanceSetting().then((res) => {
if (!res) return;
dispatch(
currentUserActions.setUserSetting({
themeMode: res.theme_mode,
theme: res.theme as Theme,
})
);
});
}, [dispatch, userSettingController]);
const { themeMode = ThemeMode.Light, theme: themeType = ThemeType.Default } = useAppSelector((state) => {
return state.currentUser.userSetting || {};
});
const muiTheme = useMemo(() => createTheme(getDesignTokens(themeMode)), [themeMode]);
return {
muiTheme,
themeMode,
themeType,
userSettingController,
};
}

View File

@ -0,0 +1,43 @@
import React from 'react';
import { Route, Routes } from 'react-router-dom';
import { ProtectedRoutes } from '$app/components/auth/ProtectedRoutes';
import { AllIcons } from '$app/components/tests/AllIcons';
import { ColorPalette } from '$app/components/tests/ColorPalette';
import { TestAPI } from '$app/components/tests/TestAPI';
import { DocumentPage } from '$app/views/DocumentPage';
import { BoardPage } from '$app/views/BoardPage';
import { GridPage } from '$app/views/GridPage';
import { LoginPage } from '$app/views/LoginPage';
import { GetStarted } from '$app/components/auth/GetStarted/GetStarted';
import { SignUpPage } from '$app/views/SignUpPage';
import { ConfirmAccountPage } from '$app/views/ConfirmAccountPage';
import { ThemeProvider } from '@mui/material';
import { useUserSetting } from '$app/AppMain.hooks';
import { UserSettingControllerContext } from '$app/components/_shared/app-hooks/useUserSettingControllerContext';
function AppMain() {
const { muiTheme, userSettingController } = useUserSetting();
return (
<UserSettingControllerContext.Provider value={userSettingController}>
<ThemeProvider theme={muiTheme}>
<Routes>
<Route path={'/'} element={<ProtectedRoutes />}>
<Route path={'/page/all-icons'} element={<AllIcons />} />
<Route path={'/page/colors'} element={<ColorPalette />} />
<Route path={'/page/api-test'} element={<TestAPI />} />
<Route path={'/page/document/:id'} element={<DocumentPage />} />
<Route path={'/page/board/:id'} element={<BoardPage />} />
<Route path={'/page/grid/:id'} element={<GridPage />} />
</Route>
<Route path={'/auth/login'} element={<LoginPage />}></Route>
<Route path={'/auth/getStarted'} element={<GetStarted />}></Route>
<Route path={'/auth/signUp'} element={<SignUpPage />}></Route>
<Route path={'/auth/confirm-account'} element={<ConfirmAccountPage />}></Route>
</Routes>
</ThemeProvider>
</UserSettingControllerContext.Provider>
);
}
export default AppMain;

View File

@ -10,24 +10,27 @@ export const Button = ({
onClick?: MouseEventHandler<HTMLButtonElement>;
}) => {
const [cls, setCls] = useState('');
useEffect(() => {
switch (size) {
case 'primary':
setCls('w-[340px] h-[48px] flex items-center justify-center rounded-lg bg-main-accent text-white');
setCls('w-[340px] h-[48px] flex items-center justify-center rounded-lg bg-content-default text-content-onfill');
break;
case 'medium':
setCls('w-[170px] h-[48px] flex items-center justify-center rounded-lg bg-main-accent text-white');
setCls('w-[170px] h-[48px] flex items-center justify-center rounded-lg bg-content-default text-content-onfill');
break;
case 'small':
setCls('w-[68px] h-[32px] flex items-center justify-center rounded-lg bg-main-accent text-white text-xs');
setCls(
'w-[68px] h-[32px] flex items-center justify-center rounded-lg bg-content-default text-content-onfill text-xs hover:bg-content-hover'
);
break;
case 'medium-transparent':
setCls(
'w-[170px] h-[48px] flex items-center justify-center rounded-lg border border-main-accent text-main-accent transition-colors duration-300 hover:bg-main-hovered hover:text-white'
'w-[170px] h-[48px] flex items-center justify-center rounded-lg border border-content-default text-content-default transition-colors duration-300 hover:bg-content-hover hover:text-content-onfill'
);
break;
case 'box-small-transparent':
setCls('text-black hover:text-main-accent w-[24px] h-[24px]');
setCls('text-icon-default w-[24px] h-[24px] rounded hover:bg-fill-hover');
break;
}
}, [size]);

View File

@ -32,7 +32,7 @@ export const ChangeFieldTypePopup = ({
<button
onClick={() => onClick(t)}
key={i}
className={'flex cursor-pointer items-center gap-2 rounded-lg px-2 py-2 pr-8 hover:bg-main-secondary'}
className={'flex cursor-pointer items-center gap-2 rounded-lg px-2 py-2 pr-8 hover:bg-fill-hover'}
>
<i className={'h-5 w-5'}>
<FieldTypeIcon fieldType={t}></FieldTypeIcon>

View File

@ -29,11 +29,16 @@ export const CheckList = ({
const onClick = () => {
if (!ref.current) return;
const { left, top } = ref.current.getBoundingClientRect();
onEditClick(left, top);
};
return (
<div ref={ref} onClick={onClick} className={'flex w-full flex-wrap items-center gap-2 px-4 py-1 text-xs text-black'}>
<div
ref={ref}
onClick={onClick}
className={'flex w-full flex-wrap items-center gap-2 px-4 py-1 text-xs text-text-title'}
>
<CheckListProgress completed={selectedOptionsCount} max={allOptionsCount} />
</div>
);

View File

@ -31,12 +31,13 @@ export const CheckListOption = ({
});
const { right: _left, top: _top } = target.getBoundingClientRect();
openCheckListDetail(_left, _top, selectOption);
};
return (
<div
className={'flex cursor-pointer items-center justify-between rounded-lg px-2 py-1.5 hover:bg-main-secondary'}
className={'flex cursor-pointer items-center justify-between rounded-lg px-2 py-1.5 hover:bg-fill-hover'}
onClick={() =>
onToggleOptionClick(
new SelectOptionPB({

View File

@ -42,6 +42,7 @@ export const EditCheckListPopup = ({
const onBlur = async () => {
const svc = new SelectOptionCellBackendService(cellIdentifier);
await svc.updateOption(
new SelectOptionPB({
id: editingSelectOption.id,
@ -52,6 +53,7 @@ export const EditCheckListPopup = ({
const onDeleteOptionClick = async () => {
const svc = new SelectOptionCellBackendService(cellIdentifier);
await svc.deleteOption([editingSelectOption]);
onOutsideClick();
};
@ -67,7 +69,7 @@ export const EditCheckListPopup = ({
top={top}
>
<div onKeyDown={onKeyDownWrapper} className={'flex flex-col gap-2 p-2'}>
<div className={'border-shades-3 flex flex-1 items-center gap-2 rounded border bg-main-selector px-2 '}>
<div className={'flex flex-1 items-center gap-2 rounded border border-line-border bg-fill-hover px-2 '}>
<input
ref={inputRef}
className={'py-2'}
@ -80,9 +82,7 @@ export const EditCheckListPopup = ({
</div>
<button
onClick={() => onDeleteOptionClick()}
className={
'flex cursor-pointer items-center gap-2 rounded-lg px-2 py-2 text-main-alert hover:bg-main-secondary'
}
className={'flex cursor-pointer items-center gap-2 rounded-lg px-2 py-2 text-fill-default hover:bg-fill-hover'}
>
<i className={'h-5 w-5'}>
<TrashSvg></TrashSvg>

View File

@ -80,9 +80,7 @@ function PopupItem({
return (
<button
onClick={() => changeFormat(format)}
className={
'flex w-full cursor-pointer items-center justify-between rounded-lg px-2 py-1.5 hover:bg-main-secondary'
}
className={'flex w-full cursor-pointer items-center justify-between rounded-lg px-2 py-1.5 hover:bg-fill-hover'}
>
{text}

View File

@ -59,6 +59,7 @@ export const DateTypeOptions = ({
}
const { right: _left, top: _top } = target.getBoundingClientRect();
onDateFormatClick(_left, _top);
};
@ -72,6 +73,7 @@ export const DateTypeOptions = ({
}
const { right: _left, top: _top } = target.getBoundingClientRect();
onTimeFormatClick(_left, _top);
};
@ -88,21 +90,17 @@ export const DateTypeOptions = ({
<hr className={'-mx-2 my-2 border-shade-6'} />
<button
onClick={_onDateFormatClick}
className={
'flex w-full cursor-pointer items-center justify-between rounded-lg px-2 py-2 hover:bg-main-secondary'
}
className={'flex w-full cursor-pointer items-center justify-between rounded-lg px-2 py-2 hover:bg-fill-hover'}
>
<span>{t('grid.field.dateFormat')}</span>
<i className={'h-5 w-5'}>
<MoreSvg></MoreSvg>
</i>
</button>
<hr className={'-mx-2 my-2 border-shade-6'} />
<hr className={'-mx-2 my-2 border-line-border'} />
<button
onClick={() => toggleIncludeTime()}
className={
'flex w-full cursor-pointer items-center justify-between rounded-lg px-2 py-2 hover:bg-main-secondary'
}
className={'flex w-full cursor-pointer items-center justify-between rounded-lg px-2 py-2 hover:bg-fill-hover'}
>
<div className={'flex items-center gap-2'}>
<span>{t('grid.field.includeTime')}</span>
@ -114,9 +112,7 @@ export const DateTypeOptions = ({
<button
onClick={_onTimeFormatClick}
className={
'flex w-full cursor-pointer items-center justify-between rounded-lg px-2 py-2 hover:bg-main-secondary'
}
className={'flex w-full cursor-pointer items-center justify-between rounded-lg px-2 py-2 hover:bg-fill-hover'}
>
<span>{t('grid.field.timeFormat')}</span>
<i className={'h-5 w-5'}>

View File

@ -93,9 +93,7 @@ const FormatButton = ({ title, checked, onClick }: { title: string; checked: boo
return (
<button
onClick={() => onClick()}
className={
'flex w-full cursor-pointer items-center justify-between rounded-lg py-1.5 px-2 hover:bg-main-secondary'
}
className={'flex w-full cursor-pointer items-center justify-between rounded-lg px-2 py-1.5 hover:bg-fill-hover'}
>
<span className={'block pr-8'}>{title}</span>
{checked && (

View File

@ -41,9 +41,7 @@ export const TimeFormatPopup = ({
<PopupWindow className={'p-2 text-xs'} onOutsideClick={onOutsideClick} left={left} top={top}>
<button
onClick={() => changeFormat(TimeFormatPB.TwelveHour)}
className={
'flex w-full cursor-pointer items-center justify-between rounded-lg px-2 py-1.5 hover:bg-main-secondary'
}
className={'flex w-full cursor-pointer items-center justify-between rounded-lg px-2 py-1.5 hover:bg-fill-hover'}
>
{t('grid.field.timeFormatTwelveHour')}
@ -55,9 +53,7 @@ export const TimeFormatPopup = ({
</button>
<button
onClick={() => changeFormat(TimeFormatPB.TwentyFourHour)}
className={
'flex w-full cursor-pointer items-center justify-between rounded-lg px-2 py-1.5 hover:bg-main-secondary'
}
className={'flex w-full cursor-pointer items-center justify-between rounded-lg px-2 py-1.5 hover:bg-fill-hover'}
>
{t('grid.field.timeFormatTwentyFourHour')}

View File

@ -42,6 +42,7 @@ export const EditCellWrapper = ({
const onClick = () => {
if (!el.current) return;
const { top, right } = el.current.getBoundingClientRect();
onEditFieldClick(cellIdentifier, right, top);
};
@ -54,24 +55,24 @@ export const EditCellWrapper = ({
{...provided.dragHandleProps}
className={'flex w-full flex-col items-start gap-2 text-xs'}
>
<div
className={
'relative flex cursor-pointer items-center gap-2 rounded-lg text-white transition-colors duration-200 hover:text-shade-3'
}
>
<div ref={el} onClick={() => onClick()} className={'flex h-5 w-5'}>
<div className={'relative flex cursor-pointer items-center gap-2 rounded-lg transition-colors duration-200'}>
<div
ref={el}
onClick={() => onClick()}
className={'flex h-5 w-5 rounded text-icon-default hover:bg-fill-hover'}
>
<DragElementSvg></DragElementSvg>
</div>
<div className={'flex h-5 w-5 flex-shrink-0 items-center justify-center text-shade-3'}>
<div className={'flex h-5 w-5 flex-shrink-0 items-center justify-center text-text-caption'}>
<FieldTypeIcon fieldType={cellIdentifier.fieldType}></FieldTypeIcon>
</div>
<span className={'overflow-hidden text-ellipsis whitespace-nowrap text-shade-3'}>
<span className={'overflow-hidden text-ellipsis whitespace-nowrap text-text-caption'}>
{databaseStore.fields[cellIdentifier.fieldId]?.title ?? ''}
</span>
</div>
<div className={'w-full cursor-pointer rounded-lg pl-3 text-sm hover:bg-shade-6'}>
<div className={'w-full cursor-pointer rounded-lg pl-3 text-sm hover:bg-fill-selector'}>
{(cellIdentifier.fieldType === FieldType.SingleSelect ||
cellIdentifier.fieldType === FieldType.MultiSelect) &&
cellController && (

View File

@ -57,6 +57,7 @@ export const EditFieldPopup = ({
const save = async () => {
if (!fieldInfo) return;
const controller = new TypeOptionController(viewId, Some(fieldInfo));
await controller.initialize();
await controller.setFieldName(name);
};
@ -64,6 +65,7 @@ export const EditFieldPopup = ({
const onChangeFieldTypeClick = () => {
if (!changeTypeButtonRef.current) return;
const { top: buttonTop, right: buttonRight } = changeTypeButtonRef.current.getBoundingClientRect();
changeFieldTypeClick(buttonTop, buttonRight);
};
@ -77,6 +79,7 @@ export const EditFieldPopup = ({
}
const { right: _left, top: _top } = target.getBoundingClientRect();
onNumberFormat?.(_left, _top);
};
@ -97,14 +100,16 @@ export const EditFieldPopup = ({
value={name}
onChange={(e) => setName(e.target.value)}
onBlur={() => save()}
className={'border-shades-3 flex-1 rounded border bg-main-selector px-2 py-2'}
className={
'flex-1 rounded border border-line-border px-2 py-2 hover:border-fill-default focus:border-fill-default'
}
/>
<div
ref={changeTypeButtonRef}
onClick={() => onChangeFieldTypeClick()}
className={
'relative flex cursor-pointer items-center justify-between rounded-lg py-2 text-black hover:bg-main-secondary'
'relative flex cursor-pointer items-center justify-between rounded-lg py-2 text-text-title hover:bg-fill-hover'
}
>
<button className={'flex cursor-pointer items-center gap-2 rounded-lg pl-2'}>
@ -124,12 +129,10 @@ export const EditFieldPopup = ({
{cellIdentifier.fieldType === FieldType.Number && (
<>
<hr className={'-mx-2 border-shade-6'} />
<hr className={'-mx-2 border-line-border'} />
<button
onClick={onNumberFormatClick}
className={
'flex w-full cursor-pointer items-center justify-between rounded-lg py-2 hover:bg-main-secondary'
}
className={'flex w-full cursor-pointer items-center justify-between rounded-lg py-2 hover:bg-fill-hover'}
>
<span className={'pl-2'}>{t('grid.field.numberFormat')}</span>
<span className={'pr-2'}>

View File

@ -112,9 +112,11 @@ export const EditRow = ({
if (!editingCell) return;
const currentField = controller.fieldController.getField(editingCell.fieldId);
if (!currentField) return;
const typeOptionController = new TypeOptionController(viewId, Some(currentField));
await typeOptionController.switchToField(newType);
setEditingCell(new CellIdentifier(viewId, rowInfo.row.id, editingCell.fieldId, newType));
@ -180,8 +182,10 @@ export const EditRow = ({
const onDelete = async () => {
if (!deletingPropertyId) return;
const fieldInfo = controller.fieldController.getField(deletingPropertyId);
if (!fieldInfo) return;
const typeController = new TypeOptionController(viewId, Some(fieldInfo));
await typeController.initialize();
await typeController.deleteField();
setShowDeletePropertyPrompt(false);
@ -199,16 +203,16 @@ export const EditRow = ({
onClick={(e) => {
e.stopPropagation();
}}
className={`relative flex h-[90%] w-[70%] flex-col gap-8 rounded-xl bg-white `}
className={`relative flex h-[90%] w-[70%] flex-col gap-8 rounded-xl bg-bg-body `}
>
<div onClick={() => onCloseClick()} className={'absolute right-1 top-1'}>
<button className={'block h-8 w-8 rounded-lg text-shade-2 hover:bg-main-secondary'}>
<button className={'block h-8 w-8 rounded-lg text-text-title hover:bg-fill-hover'}>
<CloseSvg></CloseSvg>
</button>
</div>
<div className={'flex h-full'}>
<div className={'flex h-full flex-1 flex-col border-r border-shade-6 pb-4 pt-6'}>
<div className={'flex h-full flex-1 flex-col border-r border-line-border pb-4 pt-6'}>
<div className={'pb-4 pl-12'}>
<button className={'flex items-center gap-2 p-4'}>
<i className={'h-5 w-5'}>
@ -250,10 +254,10 @@ export const EditRow = ({
</Droppable>
</DragDropContext>
<div className={'border-t border-shade-6 px-8 pt-2'}>
<div className={'border-t border-line-border px-8 pt-2'}>
<button
onClick={() => onNewColumnClick()}
className={'flex w-full items-center gap-2 rounded-lg px-4 py-2 hover:bg-shade-6'}
className={'flex w-full items-center gap-2 rounded-lg px-4 py-2 hover:bg-fill-hover'}
>
<i className={'h-5 w-5'}>
<AddSvg></AddSvg>

View File

@ -36,6 +36,7 @@ export const CellOption = ({
});
const { right: _left, top: _top } = target.getBoundingClientRect();
openOptionDetail(_left, _top, selectOption);
};
@ -45,15 +46,16 @@ export const CellOption = ({
} else {
await new SelectOptionCellBackendService(cellIdentifier).selectOption([option.selectOptionId]);
}
clearValue();
};
return (
<div
onClick={onToggleOptionClick}
className={'flex cursor-pointer items-center justify-between rounded-lg px-2 py-1.5 hover:bg-main-secondary'}
className={'flex cursor-pointer items-center justify-between rounded-lg px-2 py-1.5 hover:bg-fill-hover'}
>
<div className={`${getBgColor(option.color)} rounded px-2 py-0.5`}>{option.title}</div>
<div className={`${getBgColor(option.color)} rounded px-2 py-0.5 text-content-onfill`}>{option.title}</div>
<div className={'flex items-center'}>
{checked && (
<button className={'h-5 w-5 p-1'}>

View File

@ -14,11 +14,16 @@ export const CellOptions = ({
const onClick: MouseEventHandler = () => {
if (!ref.current) return;
const { left, top } = ref.current.getBoundingClientRect();
onEditClick(left, top);
};
return (
<div ref={ref} onClick={onClick} className={'flex w-full flex-wrap items-center gap-2 px-4 py-1 text-xs text-black'}>
<div
ref={ref}
onClick={onClick}
className={'flex w-full flex-wrap items-center gap-2 px-4 py-1 text-xs text-content-onfill'}
>
{data?.select_options?.map((option, index) => (
<div className={`${getBgColor(option.color)} rounded px-2 py-0.5`} key={index}>
{option?.name ?? ''}

View File

@ -57,8 +57,12 @@ export const CellOptionsPopup = ({
return (
<PopupWindow className={'p-2 text-xs'} onOutsideClick={onOutsideClick} left={left} top={top}>
<div onKeyDown={onKeyDownWrapper} className={'flex flex-col gap-2 p-2'}>
<div className={'border-shades-3 flex flex-1 items-center gap-2 rounded border bg-main-selector px-2 '}>
<div className={'flex flex-wrap items-center gap-2 text-black'}>
<div
className={
'flex flex-1 items-center gap-2 rounded border border-line-border px-2 hover:border-fill-default focus:border-fill-default'
}
>
<div className={'flex flex-wrap items-center gap-2 text-text-title'}>
{(data as SelectOptionCellDataPB)?.select_options?.map((option, index) => (
<SelectedOption
option={option}
@ -76,10 +80,10 @@ export const CellOptionsPopup = ({
placeholder={t('grid.selectOption.searchOption') ?? ''}
onKeyDown={onKeyDown}
/>
<div className={'font-mono text-shade-3'}>{value.length}/30</div>
<div className={'font-mono text-text-caption'}>{value.length}/30</div>
</div>
<div className={'-mx-4 h-[1px] bg-shade-6'}></div>
<div className={'font-medium text-shade-3'}>{t('grid.selectOption.panelTitle') ?? ''}</div>
<div className={'-mx-4 h-[1px] bg-line-border'}></div>
<div className={'font-medium text-text-caption'}>{t('grid.selectOption.panelTitle') ?? ''}</div>
<div className={'flex flex-col gap-1'}>
{(databaseStore.fields[cellIdentifier.fieldId]?.fieldOptions as ISelectOptionType).selectOptions.map(
(option, index) => (

View File

@ -44,6 +44,7 @@ export const EditCellOptionPopup = ({
const onBlur = async () => {
const svc = new SelectOptionCellBackendService(cellIdentifier);
await svc.updateOption(
new SelectOptionPB({
id: editingSelectOption.id,
@ -55,6 +56,7 @@ export const EditCellOptionPopup = ({
const onColorClick = async (color: SelectOptionColorPB) => {
const svc = new SelectOptionCellBackendService(cellIdentifier);
await svc.updateOption(
new SelectOptionPB({
id: editingSelectOption.id,
@ -66,6 +68,7 @@ export const EditCellOptionPopup = ({
const onDeleteOptionClick = async () => {
const svc = new SelectOptionCellBackendService(cellIdentifier);
await svc.deleteOption([editingSelectOption]);
onOutsideClick();
};
@ -81,7 +84,11 @@ export const EditCellOptionPopup = ({
top={top}
>
<div onKeyDown={onKeyDownWrapper} className={'flex flex-col gap-2 p-2'}>
<div className={'border-shades-3 flex flex-1 items-center gap-2 rounded border bg-main-selector px-2 '}>
<div
className={
'flex flex-1 items-center gap-2 rounded border border-line-border px-2 hover:border-fill-hover focus:border-fill-hover'
}
>
<input
ref={inputRef}
className={'py-2'}
@ -90,21 +97,19 @@ export const EditCellOptionPopup = ({
onKeyDown={onKeyDown}
onBlur={() => onBlur()}
/>
<div className={'font-mono text-shade-3'}>{value.length}/30</div>
<div className={'text-shade-3 font-mono'}>{value.length}/30</div>
</div>
<button
onClick={() => onDeleteOptionClick()}
className={
'flex cursor-pointer items-center gap-2 rounded-lg px-2 py-2 text-main-alert hover:bg-main-secondary'
}
className={'text-main-alert flex cursor-pointer items-center gap-2 rounded-lg px-2 py-2 hover:bg-fill-hover'}
>
<i className={'h-5 w-5'}>
<TrashSvg></TrashSvg>
</i>
<span>{t('grid.selectOption.deleteTag')}</span>
</button>
<div className={'-mx-4 h-[1px] bg-shade-6'}></div>
<div className={'my-2 font-medium text-shade-3'}>{t('grid.selectOption.colorPanelTitle')}</div>
<div className={'bg-shade-6 -mx-4 h-[1px]'}></div>
<div className={'text-shade-3 my-2 font-medium'}>{t('grid.selectOption.colorPanelTitle')}</div>
<div className={'flex flex-col'}>
<ColorItem
title={t('grid.selectOption.purpleColor')}
@ -179,7 +184,7 @@ const ColorItem = ({
}) => {
return (
<div
className={'flex cursor-pointer items-center justify-between rounded-lg p-2 hover:bg-main-secondary'}
className={'flex cursor-pointer items-center justify-between rounded-lg p-2 hover:bg-fill-hover'}
onClick={() => onClick()}
>
<div className={'flex items-center gap-2'}>

View File

@ -20,7 +20,7 @@ export const SelectedOption = ({
};
return (
<div className={`${getBgColor(option.color)} flex items-center gap-0.5 rounded px-1 py-0.5`}>
<div className={`${getBgColor(option.color)} flex items-center gap-0.5 rounded px-1 py-0.5 text-content-onfill`}>
<span>{option?.name ?? ''}</span>
<button onClick={onUnselectOptionClick} className={'h-5 w-5 cursor-pointer'}>
<CloseSvg></CloseSvg>

View File

@ -52,8 +52,10 @@ export const PropertiesPanel = ({
const toggleHideProperty = async (v: boolean, index: number) => {
const fieldInfo = controller.fieldController.getField(cells[index].fieldId);
if (fieldInfo) {
const typeController = new TypeOptionController(viewId, Some(fieldInfo));
await typeController.initialize();
if (fieldInfo.field.visibility) {
await typeController.hideField();
@ -65,6 +67,7 @@ export const PropertiesPanel = ({
const addSelectedFieldType = async (fieldType: FieldType) => {
let name = 'New Field';
switch (fieldType) {
case FieldType.RichText:
name = t('grid.field.textFieldName');
@ -96,10 +99,12 @@ export const PropertiesPanel = ({
};
return (
<div className={'flex flex-col gap-2 overflow-auto py-12 px-4'}>
<div className={'flex flex-col gap-2 overflow-auto px-4 py-12'}>
<div
onClick={() => setShowAddedProperties(!showAddedProperties)}
className={'flex cursor-pointer items-center justify-between gap-8 rounded-lg px-2 py-2 hover:bg-shade-6'}
className={
'flex cursor-pointer items-center justify-between gap-8 rounded-lg px-2 py-2 text-text-title hover:bg-bg-base'
}
>
<div className={'text-sm'}>Added Properties</div>
<i className={`h-5 w-5 transition-transform duration-500 ${showAddedProperties && 'rotate-180'}`}>
@ -113,10 +118,10 @@ export const PropertiesPanel = ({
key={cellIndex}
onMouseEnter={() => setHoveredPropertyIndex(cellIndex)}
className={
'flex cursor-pointer items-center justify-between gap-4 rounded-lg px-2 py-1 hover:bg-main-secondary'
'flex cursor-pointer items-center justify-between gap-4 rounded-lg px-2 py-1 hover:bg-fill-hover'
}
>
<div className={'flex items-center gap-2 text-black'}>
<div className={'flex items-center gap-2 text-text-title '}>
<div className={'flex h-5 w-5 flex-shrink-0 items-center justify-center'}>
<FieldTypeIcon fieldType={cell.cellIdentifier.fieldType}></FieldTypeIcon>
</div>
@ -127,7 +132,7 @@ export const PropertiesPanel = ({
<div className={'flex items-center'}>
<i
onClick={() => onDeletePropertyClick(cell.cellIdentifier.fieldId)}
className={`h-[16px] w-[16px] text-black transition-opacity duration-300 ${
className={`h-[16px] w-[16px] text-text-title transition-opacity duration-300 ${
hoveredPropertyIndex === cellIndex ? 'opacity-100' : 'opacity-0'
}`}
>
@ -143,7 +148,7 @@ export const PropertiesPanel = ({
</div>
<div
onClick={() => setShowBasicProperties(!showBasicProperties)}
className={'flex cursor-pointer items-center justify-between gap-8 rounded-lg px-2 py-2 hover:bg-shade-6'}
className={'flex cursor-pointer items-center justify-between gap-8 rounded-lg px-2 py-2 hover:bg-fill-hover'}
>
<div className={'text-sm'}>Basic Properties</div>
<i className={`h-5 w-5 transition-transform duration-500 ${showBasicProperties && 'rotate-180'}`}>
@ -157,7 +162,7 @@ export const PropertiesPanel = ({
<button
onClick={() => addSelectedFieldType(type)}
key={i}
className={'flex cursor-pointer items-center gap-2 rounded-lg px-2 py-2 pr-8 hover:bg-main-secondary'}
className={'flex cursor-pointer items-center gap-2 rounded-lg px-2 py-2 pr-8 hover:bg-fill-hover'}
>
<i className={'h-5 w-5'}>
<FieldTypeIcon fieldType={type}></FieldTypeIcon>
@ -172,7 +177,7 @@ export const PropertiesPanel = ({
</div>
<div
onClick={() => setShowAdvancedProperties(!showAdvancedProperties)}
className={'flex cursor-pointer items-center justify-between gap-8 rounded-lg px-2 py-2 hover:bg-shade-6'}
className={'flex cursor-pointer items-center justify-between gap-8 rounded-lg px-2 py-2 hover:bg-fill-hover'}
>
<div className={'text-sm'}>Advanced Properties</div>
<i className={`h-5 w-5 transition-transform duration-500 ${showAdvancedProperties && 'rotate-180'}`}>
@ -182,25 +187,19 @@ export const PropertiesPanel = ({
<div className={'flex flex-col gap-2 text-xs'}>
{showAdvancedProperties && (
<div className={'flex flex-col'}>
<button
className={'flex cursor-pointer items-center gap-2 rounded-lg px-2 py-2 pr-8 hover:bg-main-secondary'}
>
<button className={'flex cursor-pointer items-center gap-2 rounded-lg px-2 py-2 pr-8 hover:bg-fill-hover'}>
<i className={'h-5 w-5'}>
<MultiSelectTypeSvg></MultiSelectTypeSvg>
</i>
<span>Last edited time</span>
</button>
<button
className={'flex cursor-pointer items-center gap-2 rounded-lg px-2 py-2 pr-8 hover:bg-main-secondary'}
>
<button className={'flex cursor-pointer items-center gap-2 rounded-lg px-2 py-2 pr-8 hover:bg-fill-hover'}>
<i className={'h-5 w-5'}>
<DocumentSvg></DocumentSvg>
</i>
<span>Document</span>
</button>
<button
className={'flex cursor-pointer items-center gap-2 rounded-lg px-2 py-2 pr-8 hover:bg-main-secondary'}
>
<button className={'flex cursor-pointer items-center gap-2 rounded-lg px-2 py-2 pr-8 hover:bg-fill-hover'}>
<i className={'h-5 w-5'}>
<SingleSelectTypeSvg></SingleSelectTypeSvg>
</i>

View File

@ -21,6 +21,7 @@ export const PopupSelect = ({
style?: any;
}) => {
const ref = useRef<HTMLDivElement>(null);
useOutsideClick(ref, () => onOutsideClick && onOutsideClick());
const handleClick = (e: MouseEvent, item: IPopupItem) => {
@ -29,7 +30,7 @@ export const PopupSelect = ({
};
return (
<div ref={ref} className={`${className} rounded-lg bg-white px-2 py-2 shadow-md`} style={style}>
<div ref={ref} className={`${className} rounded-lg bg-bg-body px-2 py-2 text-text-title shadow-md`} style={style}>
<div
className={
(columns === 2 ? 'grid grid-cols-2' : '') + (columns === 3 ? 'grid grid-cols-3' : '') + ' w-full gap-x-4'
@ -38,7 +39,7 @@ export const PopupSelect = ({
{items.map((item, index) => (
<button
key={index}
className={'flex w-full cursor-pointer items-center gap-2 rounded-lg px-2 py-2 hover:bg-main-secondary'}
className={'flex w-full cursor-pointer items-center gap-2 rounded-lg px-2 py-2 hover:bg-fill-hover'}
onClick={(e) => handleClick(e, item)}
>
<>

View File

@ -15,6 +15,7 @@ export const PopupWindow = ({
top: number;
}) => {
const ref = useRef<HTMLDivElement>(null);
useOutsideClick(ref, onOutsideClick);
const [adjustedTop, setAdjustedTop] = useState(-100);
@ -23,11 +24,13 @@ export const PopupWindow = ({
useEffect(() => {
if (!ref.current) return;
const { height, width } = ref.current.getBoundingClientRect();
if (top + height > window.innerHeight) {
setAdjustedTop(window.innerHeight - height);
} else {
setAdjustedTop(top);
}
if (left + width > window.innerWidth) {
setAdjustedLeft(window.innerWidth - width);
} else {
@ -39,7 +42,7 @@ export const PopupWindow = ({
<div
ref={ref}
className={
'fixed z-10 rounded-lg bg-white shadow-md transition-opacity duration-300 ' +
'fixed z-10 rounded-lg bg-bg-base shadow-md transition-opacity duration-300 ' +
(adjustedTop === -100 && adjustedLeft === -100 ? 'opacity-0 ' : 'opacity-100 ') +
(className ?? '')
}

View File

@ -8,7 +8,7 @@ export const PromptWindow = ({ msg, onYes, onCancel }: { msg: string; onYes: ()
>
<div className={'rounded-xl bg-white p-16'} onClick={(e) => e.stopPropagation()}>
<div className={'flex flex-col items-center justify-center gap-8'}>
<div className={'text-black'}>{msg}</div>
<div className={'text-text-title'}>{msg}</div>
<div className={'flex items-center justify-around gap-4'}>
<Button onClick={() => onCancel()} size={'medium-transparent'}>
Cancel

View File

@ -5,14 +5,14 @@ export const SearchInput = () => {
const [active, setActive] = useState(false);
return (
<div className={`flex items-center rounded-lg p-2 ${active && 'bg-main-selector'}`}>
<div className={`flex items-center rounded-lg border p-2 ${active ? 'border-fill-default' : 'border-line-border'}`}>
<i className='mr-2 h-5 w-5'>
<SearchSvg />
</i>
<input
onFocus={() => setActive(true)}
onBlur={() => setActive(false)}
className='w-52 text-sm placeholder-gray-400 focus:placeholder-gray-500'
className='w-52 text-sm text-text-placeholder focus:text-text-title'
placeholder='Search'
type='search'
/>

View File

@ -0,0 +1,9 @@
import { UserSettingController } from '$app/stores/effects/user/user_setting_controller';
import { createContext, useContext } from 'react';
export const UserSettingControllerContext = createContext<UserSettingController | undefined>(undefined);
export function useUserSettingControllerContext() {
const context = useContext(UserSettingControllerContext);
return context;
}

View File

@ -0,0 +1,77 @@
export const AppflowyLogoDark = () => {
return (
<svg width='103' height='24' viewBox='0 0 103 24' fill='none' xmlns='http://www.w3.org/2000/svg'>
<g clipPath='url(#clip0_175_6957)'>
<path
d='M34.7324 7.84144H36.892V8.63601C37.4179 8.08166 38.4132 7.63818 39.5212 7.63818C41.8498 7.63818 43.3709 9.4121 43.3709 11.8882C43.3709 14.4197 41.6057 16.3969 38.8827 16.3969C38.1315 16.3969 37.3991 16.2675 36.9108 15.9904V19.9078H34.7512V7.84144H34.7324ZM36.9108 10.5393V13.9208C37.493 14.2904 37.9812 14.4012 38.6949 14.4012C40.1972 14.4012 41.0611 13.3295 41.0611 11.9436C41.0611 10.6132 40.2723 9.65231 38.8451 9.65231C38.1127 9.63384 37.4366 9.96644 36.9108 10.5393Z'
fill='white'
/>
<path
d='M29.3802 7.6748C26.6572 7.6748 24.892 9.6335 24.892 12.1835C24.892 14.6596 26.3943 16.4335 28.7417 16.4335C29.8309 16.4335 30.8262 15.99 31.3708 15.4357V16.2302H33.5305V9.22698C31.9154 8.02589 30.1314 7.6748 29.3802 7.6748ZM31.3521 13.5324C30.8262 14.1052 30.1502 14.4378 29.4178 14.4378C27.9906 14.4378 27.2018 13.477 27.2018 12.1465C27.2018 10.7607 28.0657 9.67046 29.568 9.67046C30.2816 9.67046 30.7699 9.78133 31.3521 10.1509V13.5324Z'
fill='white'
/>
<path
d='M44.291 7.84144H46.4506V8.63601C46.9765 8.08166 47.9718 7.63818 49.0797 7.63818C51.4084 7.63818 52.9295 9.4121 52.9295 11.8882C52.9295 14.4197 51.1642 16.3969 48.4412 16.3969C47.6901 16.3969 46.9577 16.2675 46.4694 15.9904V19.9078H44.3098V7.84144H44.291ZM46.4694 10.5393V13.9208C47.0516 14.2904 47.5398 14.4012 48.2535 14.4012C49.7558 14.4012 50.6196 13.3295 50.6196 11.9436C50.6196 10.6132 49.8309 9.65231 48.4037 9.65231C47.6713 9.63384 46.9952 9.96644 46.4694 10.5393Z'
fill='white'
/>
<path
d='M59.2019 5.43925C58.8826 5.21751 58.3756 4.99577 57.8685 4.99577C56.892 4.99577 56.3849 5.49469 56.3849 6.64034V7.82295H57.9061V9.83708H56.3849V16.1751H54.2065V6.49251C54.2065 4.18273 55.3333 3.00012 57.23 3.00012C58.0939 3.00012 58.8826 3.20338 59.446 3.55447H61.3615V13.0153C61.3615 13.9577 61.6056 14.3643 62.0939 14.3643C62.4507 14.3643 62.7324 14.2164 62.9953 14.0316L63.446 15.6577C62.9577 16.0458 62.2253 16.3599 61.2112 16.3599C59.9155 16.3599 59.2019 15.5838 59.2019 13.8838V5.43925Z'
fill='white'
/>
<path
d='M68.0846 7.63818C70.9015 7.63818 72.7043 9.50449 72.7043 12.0175C72.7043 14.5121 70.9015 16.3969 68.0846 16.3969C65.2677 16.3969 63.4648 14.5306 63.4648 12.0175C63.4648 9.50449 65.2864 7.63818 68.0846 7.63818ZM68.0846 14.4012C69.4179 14.4012 70.3944 13.4588 70.3944 12.0175C70.3944 10.6132 69.3991 9.65231 68.0846 9.65231C66.8076 9.65231 65.7935 10.5762 65.7935 12.0175C65.7935 13.4034 66.77 14.4012 68.0846 14.4012Z'
fill='white'
/>
<path
d='M83.4274 16.2308H81.4931L79.6903 11.5928C79.5588 11.2786 79.5025 10.9275 79.4086 10.6134C79.3335 10.983 79.2396 11.2971 79.1081 11.6297L77.3053 16.2308H75.4649L72.7043 7.84167H75.0142L76.3663 12.3873C76.4978 12.7938 76.5353 13.108 76.6105 13.496C76.7043 13.1449 76.7607 12.8493 76.9297 12.3873L78.5072 7.84167H80.4227L82.0565 12.3688C82.1879 12.7384 82.2818 13.1265 82.3757 13.5145C82.4508 13.1265 82.5447 12.7199 82.6386 12.3134L83.8781 7.84167H86.0565L83.4274 16.2308Z'
fill='white'
/>
<path
d='M90.6573 16.1936C89.4178 19.187 88.6291 19.9077 87.3709 19.9077C86.6009 19.9077 86.0375 19.612 85.5493 19.2609L86.2441 17.6533C86.4695 17.7827 86.7887 17.949 87.1267 17.949C87.6713 17.949 88.0657 17.524 88.4225 16.6555L88.6103 16.212L84.6854 7.85986H87.2958L89.2676 12.424C89.4366 12.8305 89.5493 13.2186 89.6807 13.6251C89.7558 13.237 89.831 12.8305 89.9812 12.4055L91.5023 7.85986H93.9624L90.6573 16.1936Z'
fill='white'
/>
<path
d='M19.0141 13.4033C18.5258 16.0088 16.385 18.2816 13.9249 19.5566C13.6244 19.7229 13.2676 19.8153 12.9296 19.8338H18.1502C18.6573 19.8338 19.0141 19.4642 19.0141 19.0022V13.4033Z'
fill='#F7931E'
/>
<path
d='M8.57282 8.37731C8.49771 8.43274 8.42259 8.48817 8.34747 8.54361C7.07048 9.43057 3.20193 12.2762 2.3193 11.0382C1.45545 9.83709 2.37564 6.43709 4.5916 4.79252C4.62916 4.75557 4.6855 4.73709 4.72306 4.70013C7.14559 3.01861 8.94841 3.25883 9.83104 4.47839C10.6573 5.62404 9.73714 7.43491 8.57282 8.37731Z'
fill='#8427E0'
/>
<path
d='M18.0376 11.0204C16.8357 11.852 14.9578 10.8911 14.0188 9.69C13.9812 9.63456 13.9437 9.59761 13.9061 9.54217C13.0047 8.28565 10.1127 4.47913 11.3521 3.62913C12.5916 2.76065 16.1784 3.70304 17.8122 5.99434C17.8498 6.04978 17.8873 6.08674 17.9249 6.14217C19.5024 8.45195 19.2582 10.1704 18.0376 11.0204Z'
fill='#00B5FF'
/>
<path
d='M16.4226 18.5219C16.385 18.5589 16.3475 18.5773 16.2911 18.6143C13.8686 20.2958 12.0658 20.0556 11.1832 18.836C10.3569 17.6904 11.277 15.8795 12.4414 14.9371C12.5165 14.8817 12.5916 14.8263 12.6667 14.7708C13.9437 13.9023 17.8123 11.0382 18.6761 12.2763C19.5587 13.4773 18.6573 16.8773 16.4226 18.5219Z'
fill='#FFBD00'
/>
<path
d='M9.66194 19.6861C8.4225 20.5545 4.85443 19.6121 3.22063 17.3208C3.18307 17.2839 3.14551 17.2285 3.12673 17.1915C1.53049 14.8817 1.7934 13.1448 3.01405 12.3132C4.21593 11.4817 6.09387 12.4426 7.03283 13.6437C7.07039 13.6991 7.10795 13.7361 7.14551 13.7915C8.02814 15.0295 10.9202 18.8361 9.66194 19.6861Z'
fill='#E3006D'
/>
<path
d='M8.57283 8.37731C6.84513 9.13491 3.16438 10.6871 2.61978 9.43057C2.1503 8.37731 3.01415 6.23383 4.59161 4.79252C4.62917 4.75557 4.68551 4.73709 4.72307 4.70013C7.1456 3.01861 8.94842 3.25883 9.83105 4.47839C10.6573 5.62404 9.73715 7.43491 8.57283 8.37731Z'
fill='#9327FF'
/>
<path
d='M18.0375 11.0196C16.8357 11.8511 14.9577 10.8902 14.0188 9.68912C13.2488 7.93368 11.7652 4.47825 13.0047 3.96086C14.1314 3.48042 16.4601 4.44129 17.9436 6.14129C19.5023 8.45107 19.2582 10.1696 18.0375 11.0196Z'
fill='#00C8FF'
/>
<path
d='M16.4226 18.5218C16.385 18.5587 16.3475 18.5772 16.2911 18.6142C13.8686 20.2957 12.0658 20.0555 11.1832 18.8359C10.3569 17.6903 11.277 15.8794 12.4414 14.937C14.1691 14.1794 17.8498 12.6272 18.3944 13.8837C18.8827 14.937 18.0188 17.0805 16.4226 18.5218Z'
fill='#F7CF46'
/>
<path
d='M8.04692 19.3535C6.92016 19.8339 4.59152 18.8915 3.12673 17.1915C1.53049 14.8817 1.7934 13.1448 3.01405 12.3132C4.21593 11.4817 6.09387 12.4426 7.03283 13.6437C7.80279 15.3806 9.28635 18.8361 8.04692 19.3535Z'
fill='#FB006D'
/>
</g>
<defs>
<clipPath id='clip0_175_6957'>
<rect width='92' height='17' fill='white' transform='translate(2 3)' />
</clipPath>
</defs>
</svg>
);
};

View File

@ -0,0 +1,53 @@
export const AppflowyLogoLight = () => (
<svg width='103' height='24' viewBox='0 0 103 24' fill='none' xmlns='http://www.w3.org/2000/svg'>
<g clipPath='url(#clip0_159_6088)'>
<path
fillRule='evenodd'
clipRule='evenodd'
d='M59.2019 5.43913C58.8826 5.21739 58.3756 4.99565 57.8685 4.99565C56.892 4.99565 56.3849 5.49457 56.3849 6.64022V7.82283H57.9061V9.83696H56.3849V16.175H54.2065V6.49239C54.2065 4.18261 55.3333 3 57.23 3C58.0939 3 58.8826 3.20326 59.446 3.55435H61.3615V13.0152C61.3615 13.9576 61.6056 14.3641 62.0939 14.3641C62.4507 14.3641 62.7324 14.2163 62.9953 14.0315L63.446 15.6576C62.9577 16.0457 62.2253 16.3598 61.2112 16.3598C59.9155 16.3598 59.2019 15.5837 59.2019 13.8837V5.43913ZM34.7512 7.84144H36.892V8.63601C37.4179 8.08166 38.4132 7.63818 39.5212 7.63818C41.8498 7.63818 43.3709 9.4121 43.3709 11.8882C43.3709 14.4197 41.6057 16.3969 38.8827 16.3969C38.1315 16.3969 37.3991 16.2675 36.9108 15.9904V19.9077H34.7512V7.84144ZM36.9108 10.5393V13.9208C37.493 14.2904 37.9812 14.4012 38.6949 14.4012C40.1972 14.4012 41.0611 13.3295 41.0611 11.9436C41.0611 10.6132 40.2723 9.65231 38.8451 9.65231C38.1127 9.63384 37.4366 9.96644 36.9108 10.5393ZM29.3804 7.6748C26.6573 7.6748 24.8921 9.6335 24.8921 12.1835C24.8921 14.6596 26.3944 16.4335 28.7419 16.4335C29.8311 16.4335 30.8264 15.99 31.371 15.4357V16.2302H33.5306V9.22698C31.9156 8.02589 30.1315 7.6748 29.3804 7.6748ZM31.3522 13.5324C30.8264 14.1052 30.1503 14.4378 29.4179 14.4378C27.9907 14.4378 27.2019 13.477 27.2019 12.1465C27.2019 10.7607 28.0658 9.67046 29.5681 9.67046C30.2818 9.67046 30.77 9.78133 31.3522 10.1509V13.5324ZM46.4506 7.84144H44.3098V19.9077H46.4694V15.9904C46.9577 16.2675 47.6901 16.3969 48.4412 16.3969C51.1642 16.3969 52.9295 14.4197 52.9295 11.8882C52.9295 9.4121 51.4084 7.63818 49.0797 7.63818C47.9718 7.63818 46.9765 8.08166 46.4506 8.63601V7.84144ZM46.4694 13.9208V10.5393C46.9952 9.96644 47.6713 9.63384 48.4037 9.65231C49.8309 9.65231 50.6196 10.6132 50.6196 11.9436C50.6196 13.3295 49.7558 14.4012 48.2535 14.4012C47.5398 14.4012 47.0516 14.2904 46.4694 13.9208ZM72.7043 12.0175C72.7043 9.50449 70.9015 7.63818 68.0846 7.63818C65.2864 7.63818 63.4648 9.50449 63.4648 12.0175C63.4648 14.5306 65.2677 16.3969 68.0846 16.3969C70.9015 16.3969 72.7043 14.5121 72.7043 12.0175ZM70.3944 12.0175C70.3944 13.4588 69.4179 14.4012 68.0846 14.4012C66.77 14.4012 65.7935 13.4034 65.7935 12.0175C65.7935 10.5762 66.8076 9.65231 68.0846 9.65231C69.3991 9.65231 70.3944 10.6132 70.3944 12.0175ZM83.4274 16.2307H81.4931L79.6903 11.5926C79.6057 11.3905 79.5522 11.173 79.4998 10.96C79.4708 10.842 79.4421 10.7253 79.4086 10.6133C79.3335 10.9829 79.2396 11.297 79.1081 11.6296L77.3053 16.2307H75.4649L72.7043 7.84155H75.0142L76.3663 12.3872C76.4663 12.6964 76.512 12.9521 76.5611 13.2276L76.5612 13.228C76.5767 13.3146 76.5925 13.4031 76.6105 13.4959C76.6339 13.4081 76.655 13.3239 76.6762 13.2396C76.7396 12.9866 76.8029 12.7337 76.9297 12.3872L78.5072 7.84155H80.4227L82.0565 12.3687C82.1879 12.7383 82.2818 13.1263 82.3757 13.5144C82.4508 13.1263 82.5447 12.7198 82.6386 12.3133L83.8781 7.84155H86.0565L86.0507 7.85986H87.2959L89.2677 12.424C89.4023 12.7476 89.5011 13.0596 89.6021 13.3784C89.628 13.4601 89.654 13.5422 89.6808 13.6251L89.6916 13.5696C89.7633 13.1984 89.8382 12.8103 89.9813 12.4055L91.5024 7.85986H93.9625L90.6574 16.1936C89.4179 19.187 88.6292 19.9077 87.371 19.9077C86.601 19.9077 86.0377 19.612 85.5494 19.2609L86.2442 17.6533C86.4696 17.7827 86.7888 17.949 87.1269 17.949C87.6715 17.949 88.0658 17.524 88.4226 16.6555L88.6104 16.212L85.5045 9.60269L83.4274 16.2307Z'
fill='black'
/>
<path
d='M19.0142 13.4033C18.5259 16.0088 16.3851 18.2816 13.925 19.5566C13.6245 19.7229 13.2677 19.8153 12.9297 19.8338H18.1503C18.6574 19.8338 19.0142 19.4642 19.0142 19.0022V13.4033Z'
fill='#F7931E'
/>
<path
d='M8.57282 8.3773C8.49771 8.43274 8.42259 8.48817 8.34747 8.54361C7.07048 9.43057 3.20193 12.2762 2.3193 11.0382C1.45545 9.83709 2.37564 6.43709 4.5916 4.79252C4.62916 4.75557 4.6855 4.73709 4.72306 4.70013C7.14559 3.01861 8.94841 3.25883 9.83104 4.47839C10.6573 5.62404 9.73714 7.43491 8.57282 8.3773Z'
fill='#8427E0'
/>
<path
d='M18.0376 11.0204C16.8357 11.852 14.9578 10.8911 14.0188 9.69C13.9812 9.63456 13.9437 9.59761 13.9061 9.54217C13.0047 8.28565 10.1127 4.47913 11.3521 3.62913C12.5916 2.76065 16.1784 3.70304 17.8122 5.99434C17.8498 6.04978 17.8873 6.08674 17.9249 6.14217C19.5024 8.45195 19.2582 10.1704 18.0376 11.0204Z'
fill='#00B5FF'
/>
<path
d='M16.4226 18.5219C16.385 18.5589 16.3475 18.5773 16.2911 18.6143C13.8686 20.2958 12.0658 20.0556 11.1832 18.836C10.3569 17.6904 11.277 15.8795 12.4414 14.9371C12.5165 14.8817 12.5916 14.8263 12.6667 14.7708C13.9437 13.9023 17.8123 11.0382 18.6761 12.2763C19.5587 13.4773 18.6573 16.8773 16.4226 18.5219Z'
fill='#FFBD00'
/>
<path
d='M9.66206 19.6861C8.42263 20.5545 4.85455 19.6121 3.22075 17.3208C3.18319 17.2839 3.14563 17.2285 3.12685 17.1915C1.53061 14.8817 1.79352 13.1448 3.01418 12.3132C4.21605 11.4817 6.09399 12.4426 7.03296 13.6437C7.07051 13.6991 7.10807 13.7361 7.14563 13.7915C8.02826 15.0295 10.9203 18.8361 9.66206 19.6861Z'
fill='#E3006D'
/>
<path
d='M8.57283 8.37731C6.84513 9.13491 3.16438 10.6871 2.61978 9.43057C2.1503 8.37731 3.01415 6.23383 4.59161 4.79252C4.62917 4.75557 4.68551 4.73709 4.72307 4.70013C7.1456 3.01861 8.94842 3.25883 9.83105 4.47839C10.6573 5.62404 9.73715 7.43491 8.57283 8.37731Z'
fill='#9327FF'
/>
<path
d='M18.0377 11.0196C16.8358 11.8511 14.9578 10.8902 14.0189 9.68912C13.2489 7.93368 11.7654 4.47825 13.0048 3.96086C14.1316 3.48042 16.4602 4.44129 17.9438 6.14129C19.5024 8.45107 19.2583 10.1696 18.0377 11.0196Z'
fill='#00C8FF'
/>
<path
d='M16.4226 18.5218C16.385 18.5587 16.3475 18.5772 16.2911 18.6142C13.8686 20.2957 12.0658 20.0555 11.1832 18.8359C10.3569 17.6903 11.277 15.8794 12.4414 14.937C14.1691 14.1794 17.8498 12.6272 18.3944 13.8837C18.8827 14.937 18.0188 17.0805 16.4226 18.5218Z'
fill='#FFCE00'
/>
<path
d='M8.04704 19.3535C6.92028 19.8339 4.59164 18.8915 3.12685 17.1915C1.53061 14.8817 1.79352 13.1448 3.01418 12.3132C4.21605 11.4817 6.09399 12.4426 7.03296 13.6437C7.80291 15.3806 9.28648 18.8361 8.04704 19.3535Z'
fill='#FB006D'
/>
</g>
<defs>
<clipPath id='clip0_159_6088'>
<rect width='92' height='17' fill='white' transform='translate(2 3)' />
</clipPath>
</defs>
</svg>
);

View File

@ -1,8 +1,13 @@
export const EditorCheckSvg = () => {
return (
<svg width='100%' height='100%' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'>
<rect x='2' y='2' width='12' height='12' rx='4' fill='#00BCF0' />
<path d='M6 8L7.61538 9.5L10.5 6.5' stroke='white' strokeLinecap='round' strokeLinejoin='round' />
<rect x='2' y='2' width='12' height='12' rx='4' fill={'var(--color-fill-default)'} />
<path
d='M6 8L7.61538 9.5L10.5 6.5'
stroke={'var(--color-content-onfill)'}
strokeLinecap='round'
strokeLinejoin='round'
/>
</svg>
);
};

View File

@ -1,7 +1,7 @@
export const EditorUncheckSvg = () => {
return (
<svg width='100%' height='100%' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'>
<rect x='2.5' y='2.5' width='11' height='11' rx='3.5' stroke='#BDBDBD' />
<rect x='2.5' y='2.5' width='11' height='11' rx='3.5' stroke={'var(--color-icon-secondary)'} />
</svg>
);
};

View File

@ -1,10 +1,10 @@
export const FullView = () => {
return (
<svg width='100%' height='100%' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'>
<path d='M6 13H3V10' stroke='#333333' strokeLinecap='round' strokeLinejoin='round' />
<path d='M10 3H13V6' stroke='#333333' strokeLinecap='round' strokeLinejoin='round' />
<path d='M3 13L7 9' stroke='#333333' strokeLinecap='round' strokeLinejoin='round' />
<path d='M13 3L9 7' stroke='#333333' strokeLinecap='round' strokeLinejoin='round' />
<path d='M6 13H3V10' stroke='var(--color-text-title)' strokeLinecap='round' strokeLinejoin='round' />
<path d='M10 3H13V6' stroke='var(--color-text-title)' strokeLinecap='round' strokeLinejoin='round' />
<path d='M3 13L7 9' stroke='var(--color-text-title)' strokeLinecap='round' strokeLinejoin='round' />
<path d='M13 3L9 7' stroke='var(--color-text-title)' strokeLinecap='round' strokeLinejoin='round' />
</svg>
);
};

View File

@ -1,11 +1,31 @@
export const GroupBySvg = () => {
return (
<svg width='100%' height='100%' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'>
<path d='M10 2H13C13.5523 2 14 2.44772 14 3V6' stroke='#333333' strokeLinecap='round' strokeLinejoin='round' />
<path d='M6 2H3C2.44772 2 2 2.44772 2 3V6' stroke='#333333' strokeLinecap='round' strokeLinejoin='round' />
<path d='M6 14H3C2.44772 14 2 13.5523 2 13V10' stroke='#333333' strokeLinecap='round' strokeLinejoin='round' />
<path d='M10 14H13C13.5523 14 14 13.5523 14 13V10' stroke='#333333' strokeLinecap='round' strokeLinejoin='round' />
<rect x='6' y='6' width='4' height='4' rx='1' stroke='#333333' />
<path
d='M10 2H13C13.5523 2 14 2.44772 14 3V6'
stroke='var(--color-text-title)'
strokeLinecap='round'
strokeLinejoin='round'
/>
<path
d='M6 2H3C2.44772 2 2 2.44772 2 3V6'
stroke='var(--color-text-title)'
strokeLinecap='round'
strokeLinejoin='round'
/>
<path
d='M6 14H3C2.44772 14 2 13.5523 2 13V10'
stroke='var(--color-text-title)'
strokeLinecap='round'
strokeLinejoin='round'
/>
<path
d='M10 14H13C13.5523 14 14 13.5523 14 13V10'
stroke='var(--color-text-title)'
strokeLinecap='round'
strokeLinejoin='round'
/>
<rect x='6' y='6' width='4' height='4' rx='1' stroke='var(--color-text-title)' />
</svg>
);
};

View File

@ -17,7 +17,7 @@ export const ConfirmAccount = () => {
<div>
<span className='block text-gray-500'>Confirm that this phone belongs to you.</span>
<span className='block text-gray-500'>
Code sent to <span className='text-black'>+86 10 6764 5489</span>
Code sent to <span className='text-text-title'>+86 10 6764 5489</span>
</span>
</div>
</div>

View File

@ -5,10 +5,11 @@ import { useLogin } from '../Login/Login.hooks';
export const GetStarted = () => {
const { onAutoSignInClick } = useLogin();
return (
<>
<form onSubmit={(e) => e.preventDefault()} method='POST'>
<div className='relative flex h-screen w-screen flex-col items-center justify-center gap-12 text-center'>
<div className='relative flex h-screen w-screen flex-col items-center justify-center gap-12 bg-bg-body text-center text-text-title'>
<div className='flex h-10 w-10 justify-center' id='appflowy'>
<AppflowyLogo />
</div>
@ -19,8 +20,8 @@ export const GetStarted = () => {
</span>
</div>
<div id='Get-Started' className='flex w-full max-w-[340px] flex-col gap-6 ' aria-label='Get-Started' >
<Button size={'primary'} onClick={() => onAutoSignInClick()} >
<div id='Get-Started' className='flex w-full max-w-[340px] flex-col gap-6 ' aria-label='Get-Started'>
<Button size={'primary'} onClick={() => onAutoSignInClick()}>
{t('signUp.getStartedText')}
</Button>
</div>

View File

@ -18,7 +18,7 @@ export const Login = () => {
return (
<>
<form onSubmit={(e) => e.preventDefault()} method='POST'>
<div className='relative flex h-screen w-screen flex-col items-center justify-center gap-12 text-center'>
<div className='relative flex h-screen w-screen flex-col items-center justify-center gap-12 bg-bg-body text-center text-text-title'>
<div className='flex h-10 w-10 justify-center'>
<AppflowyLogo />
</div>
@ -61,7 +61,7 @@ export const Login = () => {
<div className='flex justify-center'>
{/* Forget password link */}
<Link to={'/auth/confirm-account'}>
<span className='text-xs text-main-accent hover:text-main-hovered'>{t('signIn.forgotPassword')}</span>
<span className='text-xs text-fill-default hover:text-fill-hover'>{t('signIn.forgotPassword')}</span>
</Link>
</div>
</div>
@ -76,7 +76,7 @@ export const Login = () => {
<span className='text-xs text-gray-400'>
{t('signIn.dontHaveAnAccount')}
<Link to={'/auth/signUp'}>
<span className='ml-2 text-main-accent hover:text-main-hovered'>{t('signUp.buttonText')}</span>
<span className='ml-2 text-fill-default hover:text-fill-hover'>{t('signUp.buttonText')}</span>
</Link>
</span>
</div>
@ -84,7 +84,10 @@ export const Login = () => {
<div className={'absolute right-0 top-0 px-12 py-8'}>
<div className={'relative h-full w-full'}>
<button className={'h-8 w-8 text-shade-3 hover:text-black'} onClick={() => setShowLanguagePopup(true)}>
<button
className={'h-8 w-8 text-text-caption hover:text-text-title'}
onClick={() => setShowLanguagePopup(true)}
>
<EarthSvg></EarthSvg>
</button>
{showLanguagePopup && (

View File

@ -112,7 +112,10 @@ export const SignUp = () => {
<div className={'absolute right-0 top-0 px-12 py-8'}>
<div className={'relative h-full w-full'}>
<button className={'h-8 w-8 text-shade-3 hover:text-black'} onClick={() => setShowLanguagePopup(true)}>
<button
className={'h-8 w-8 text-text-caption hover:text-text-title'}
onClick={() => setShowLanguagePopup(true)}
>
<EarthSvg></EarthSvg>
</button>
{showLanguagePopup && (

View File

@ -16,6 +16,7 @@ export const useAuth = () => {
if (result.ok) {
const userProfile = result.val;
const workspaceSetting = await _openWorkspace().then((r) => {
if (r.ok) {
return r.val;

View File

@ -44,6 +44,7 @@ export const BoardCard = ({
}
const { right: left, top } = target.getBoundingClientRect();
setCardPopupLeft(left);
setCardPopupTop(top);
setShowCardPopup(true);
@ -63,9 +64,9 @@ export const BoardCard = ({
{...provided.draggableProps}
{...provided.dragHandleProps}
onClick={() => onOpenRow(rowInfo)}
className={`relative cursor-pointer select-none rounded-lg border border-shade-6 bg-white px-3 py-2 transition-transform duration-100 hover:bg-main-selector `}
className={`relative cursor-pointer select-none rounded-lg border border-line-border bg-bg-body px-3 py-2 transition-transform duration-100 hover:bg-fill-selector `}
>
<button onClick={onDetailClick} className={'absolute right-4 top-2.5 h-5 w-5 rounded hover:bg-surface-2'}>
<button onClick={onDetailClick} className={'absolute right-4 top-2.5 h-5 w-5 rounded hover:bg-fill-hover'}>
<Details2Svg></Details2Svg>
</button>
<div className={'flex flex-col gap-3'}>
@ -94,7 +95,7 @@ export const BoardCard = ({
>
<button
key={index}
className={'flex w-full cursor-pointer items-center gap-2 rounded-lg px-2 py-2 hover:bg-main-secondary'}
className={'flex w-full cursor-pointer items-center gap-2 rounded-lg px-2 py-2 hover:bg-fill-hover'}
onClick={() => onDeleteRowClick()}
>
<i className={'h-5 w-5'}>

View File

@ -8,13 +8,14 @@ export const BoardFieldsPopup = ({ hidePopup }: { hidePopup: () => void }) => {
const columns = useAppSelector((state) => state.database.columns);
const fields = useAppSelector((state) => state.database.fields);
const ref = useRef<HTMLDivElement>(null);
useOutsideClick(ref, () => hidePopup());
return (
<div ref={ref} className={'absolute top-full left-full z-10 rounded-lg bg-white px-2 py-2 text-xs shadow-md'}>
<div ref={ref} className={'absolute left-full top-full z-10 rounded-lg bg-white px-2 py-2 text-xs shadow-md'}>
{columns.map((column, index) => (
<div
className={'flex cursor-pointer items-center justify-between rounded-lg px-2 py-2 hover:bg-main-secondary'}
className={'flex cursor-pointer items-center justify-between rounded-lg px-2 py-2 hover:bg-fill-hover'}
key={index}
>
<div className={'flex items-center gap-2 '}>

View File

@ -26,10 +26,12 @@ export const BoardGroup = ({
const { t } = useTranslation();
const [rows, setRows] = useState<RowInfo[]>([]);
useEffect(() => {
const reloadRows = () => {
setRows(group.rows.map((rowPB) => new RowInfo(viewId, controller.fieldController.fieldInfos, rowPB)));
};
reloadRows();
group.subscribe({
onRemoveRow: reloadRows,
@ -43,17 +45,17 @@ export const BoardGroup = ({
}, [controller, group, viewId]);
return (
<div className={'flex h-full w-[250px] flex-col rounded-lg bg-surface-1'}>
<div className={'flex h-full w-[250px] flex-col rounded-lg bg-bg-base'}>
<div className={'flex items-center justify-between p-4'}>
<div className={'flex items-center gap-2'}>
<span>{group.name}</span>
<span className={'text-shade-4'}>({group.rows.length})</span>
</div>
<div className={'flex items-center gap-2'}>
<button className={'h-5 w-5 rounded hover:bg-surface-2'}>
<button className={'h-5 w-5 rounded hover:bg-fill-hover'}>
<Details2Svg></Details2Svg>
</button>
<button className={'h-5 w-5 rounded hover:bg-surface-2'}>
<button className={'h-5 w-5 rounded hover:bg-fill-hover'}>
<AddSvg></AddSvg>
</button>
</div>
@ -84,7 +86,7 @@ export const BoardGroup = ({
<div className={'p-2'}>
<button
onClick={onNewRowClick}
className={'flex w-full items-center gap-2 rounded-lg px-2 py-2 hover:bg-surface-2'}
className={'flex w-full items-center gap-2 rounded-lg px-2 py-2 hover:bg-fill-hover'}
>
<span className={'h-5 w-5'}>
<AddSvg></AddSvg>

View File

@ -8,13 +8,14 @@ export const BoardGroupFieldsPopup = ({ hidePopup }: { hidePopup: () => void })
const columns = useAppSelector((state) => state.database.columns);
const fields = useAppSelector((state) => state.database.fields);
const ref = useRef<HTMLDivElement>(null);
useOutsideClick(ref, () => hidePopup());
return (
<div ref={ref} className={'absolute top-full left-full z-10 rounded-lg bg-white px-2 py-2 text-xs shadow-md'}>
<div ref={ref} className={'absolute left-full top-full z-10 rounded-lg bg-white px-2 py-2 text-xs shadow-md'}>
{columns.map((column, index) => (
<div
className={'flex cursor-pointer items-center justify-between rounded-lg px-2 py-2 hover:bg-main-secondary'}
className={'flex cursor-pointer items-center justify-between rounded-lg px-2 py-2 hover:bg-fill-hover'}
key={index}
>
<div className={'flex items-center gap-2 '}>

View File

@ -17,7 +17,7 @@ export const BoardOptionsCell = ({
const { data } = useCell(cellIdentifier, cellCache, fieldController);
return (
<div className={'flex flex-wrap items-center gap-2 py-2 text-xs text-black'}>
<div className={'flex flex-wrap items-center gap-2 py-2 text-xs text-text-title'}>
{(data as SelectOptionCellDataPB)?.select_options?.map((option, index) => (
<div className={`${getBgColor(option.color)} rounded px-2 py-0.5`} key={index}>
{option?.name ?? ''}

View File

@ -41,6 +41,7 @@ export function useBlockRectSelection({ container, getIntersectedBlockIds }: Blo
const y = Math.min(startY, endY);
const width = Math.abs(endX - startX);
const height = Math.abs(endY - startY);
return {
left: x - container.scrollLeft + 'px',
top: y - container.scrollTop + 'px',
@ -54,11 +55,13 @@ export function useBlockRectSelection({ container, getIntersectedBlockIds }: Blo
if (isPointInBlock(e.target as HTMLElement)) {
return;
}
e.preventDefault();
setDragging(true);
const startX = e.clientX + container.scrollLeft;
const startY = e.clientY + container.scrollTop;
startPointRef.current = [startX, startY];
setRect({
startX,
@ -84,6 +87,7 @@ export function useBlockRectSelection({ container, getIntersectedBlockIds }: Blo
endY,
};
const blockIds = getIntersectedBlockIds(newRect);
setRect(newRect);
dispatch(
setRectSelectionThunk({
@ -103,11 +107,14 @@ export function useBlockRectSelection({ container, getIntersectedBlockIds }: Blo
updateSelctionsByPoint(e.clientX, e.clientY);
const { top, bottom } = container.getBoundingClientRect();
if (e.clientY >= bottom) {
const delta = e.clientY - bottom;
container.scrollBy(0, delta);
} else if (e.clientY <= top) {
const delta = e.clientY - top;
container.scrollBy(0, delta);
}
},
@ -125,6 +132,7 @@ export function useBlockRectSelection({ container, getIntersectedBlockIds }: Blo
);
return;
}
if (!isDragging) return;
e.preventDefault();
updateSelctionsByPoint(e.clientX, e.clientY);
@ -135,16 +143,16 @@ export function useBlockRectSelection({ container, getIntersectedBlockIds }: Blo
);
useEffect(() => {
document.addEventListener('mousedown', handleDragStart);
container.addEventListener('mousedown', handleDragStart);
document.addEventListener('mousemove', handleDraging);
document.addEventListener('mouseup', handleDragEnd);
return () => {
document.removeEventListener('mousedown', handleDragStart);
container.removeEventListener('mousedown', handleDragStart);
document.removeEventListener('mousemove', handleDraging);
document.removeEventListener('mouseup', handleDragEnd);
};
}, [handleDragStart, handleDragEnd, handleDraging]);
}, [container, handleDragStart, handleDragEnd, handleDraging]);
return {
isDragging,

View File

@ -8,7 +8,7 @@ function BlockRectSelection(props: BlockRectSelectionProps) {
const { isDragging, style } = useBlockRectSelection(props);
if (!isDragging) return null;
return <div className='z-99 absolute bg-[#00d5ff] opacity-25' style={style} />;
return <div className='z-99 absolute bg-fill-default opacity-10' style={style} />;
}
export default BlockRectSelection;

View File

@ -17,7 +17,7 @@ export default function CalloutBlock({
const { openEmojiSelect, open, closeEmojiSelect, id, anchorEl, onEmojiSelect } = useCalloutBlock(node.id);
return (
<div className={'my-1 flex rounded border border-solid border-main-accent bg-main-selector p-4'}>
<div className={'my-1 flex rounded border border-solid border-line-border bg-fill-selector p-4'}>
<div className={'w-[1.5em]'} onMouseDown={(e) => e.stopPropagation()}>
<div className={'flex h-[calc(1.5em_+_2px)] w-[24px] select-none items-center justify-start'}>
<IconButton

View File

@ -5,7 +5,8 @@ import { useChange } from '$app/components/document/_shared/EditorHooks/useChang
import { useKeyDown } from './useKeyDown';
import CodeEditor from '$app/components/document/_shared/SlateEditor/CodeEditor';
import { useSelection } from '$app/components/document/_shared/EditorHooks/useSelection';
import { useSubscribeDecorate } from '$app/components/document/_shared/SubscribeSelection.hooks';
import { useAppSelector } from '$app/stores/store';
import { ThemeMode } from '$app/interfaces';
export default function CodeBlock({
node,
@ -18,13 +19,15 @@ export default function CodeBlock({
const className = props.className ? ` ${props.className}` : '';
const { value, onChange } = useChange(node);
const selectionProps = useSelection(id);
const isDark = useAppSelector((state) => state.currentUser.userSetting.themeMode === ThemeMode.Dark);
return (
<div {...props} className={`rounded bg-shade-6 p-6 ${className}`}>
<div {...props} className={`my-1 rounded border border-solid border-line-border bg-fill-selector p-6 ${className}`}>
<div className={'mb-2 w-[100%]'}>
<SelectLanguage id={id} language={language} />
</div>
<CodeEditor
isDark={isDark}
value={value}
onChange={onChange}
placeholder={placeholder}

View File

@ -1,7 +1,7 @@
export default function DividerBlock() {
return (
<div className={`flex h-[1em] w-[100%] items-center justify-center`}>
<div className={'h-[1px] w-[100%] bg-shade-5'} />
<div className={'h-[1px] w-[100%] bg-line-border'} />
</div>
);
}

View File

@ -65,12 +65,12 @@ function EquationBlock({ node }: { node: NestedBlock<BlockType.EquationBlock> })
<div
ref={anchorElRef}
onClick={openPopover}
className={'my-1 flex min-h-[59px] cursor-pointer flex-col overflow-hidden rounded hover:bg-main-secondary'}
className={'my-1 flex min-h-[59px] cursor-pointer flex-col overflow-hidden rounded hover:bg-fill-selector'}
>
{displayFormula ? (
<KatexMath latex={displayFormula} />
) : (
<div className={'flex h-[100%] w-[100%] flex-1 items-center bg-main-selector px-1 text-shade-2'}>
<div className={'flex h-[100%] w-[100%] flex-1 items-center bg-fill-selector px-1 text-text-title'}>
<Functions />
<span>Add a TeX equation</span>
</div>

View File

@ -87,12 +87,11 @@ function ImageAlign({
onClose={() => setAnchorEl(undefined)}
PaperProps={{
style: {
backgroundColor: '#1E1E1E',
opacity: 0.8,
backgroundColor: 'var(--color-bg-body)',
},
}}
>
<div className='flex items-center justify-center bg-transparent p-1'>
<div className='flex items-center justify-center p-1'>
{[Align.Left, Align.Center, Align.Right].map((item: Align) => {
return (
<div

View File

@ -40,7 +40,7 @@ function ImagePlaceholder({
{isEmpty && (
<div
onClick={openPopover}
className={'flex h-[100%] w-[100%] flex-1 items-center bg-main-selector px-1 text-shade-2'}
className={'flex h-[100%] w-[100%] flex-1 items-center rounded bg-fill-selector px-1 text-text-title'}
>
<i className={'mx-2 h-5 w-5'}>
<ImageSvg />

View File

@ -29,7 +29,7 @@ function ImageRender({
} top-0 flex h-[100%] w-[15px] cursor-col-resize items-center justify-center`}
>
<div
className={`h-[48px] max-h-[50%] w-2 rounded-[20px] border border-solid border-main-selector bg-shade-3 ${
className={`h-[48px] max-h-[50%] w-2 rounded-[20px] border border-solid border-line-border bg-line-border ${
toolbarOpen ? 'opacity-1' : 'opacity-0'
} transition-opacity duration-300 `}
/>

View File

@ -18,7 +18,7 @@ function ImageToolbar({ id, open, align }: { id: string; open: boolean; align: A
<div
className={`${
visible ? 'opacity-1 pointer-events-auto' : 'pointer-events-none opacity-0'
} absolute right-2 top-2 z-[1px] flex h-[26px] max-w-[calc(100%-16px)] cursor-pointer items-center justify-center whitespace-nowrap rounded bg-shade-1 bg-opacity-50 text-sm text-white transition-opacity`}
} absolute right-2 top-2 z-[1px] flex h-[26px] max-w-[calc(100%-16px)] cursor-pointer items-center justify-center whitespace-nowrap rounded bg-bg-body text-sm text-text-title transition-opacity`}
>
<ImageAlign id={id} align={align} onOpen={() => setPopoverOpen(true)} onClose={() => setPopoverOpen(false)} />
<MenuTooltip title={'Delete'}>
@ -26,7 +26,7 @@ function ImageToolbar({ id, open, align }: { id: string; open: boolean; align: A
onClick={() => {
dispatch(deleteNodeThunk({ id, controller }));
}}
className='flex items-center justify-center bg-transparent p-1'
className='flex items-center justify-center p-1'
>
<DeleteOutline />
</div>

View File

@ -28,9 +28,7 @@ function ImageBlock({ node }: { node: NestedBlock<BlockType.ImageBlock> }) {
<>
<div
ref={anchorElRef}
className={
'my-1 flex min-h-[59px] cursor-pointer flex-col justify-center overflow-hidden hover:bg-main-secondary'
}
className={'my-1 flex min-h-[59px] cursor-pointer flex-col justify-center overflow-hidden rounded'}
>
<ImageRender
node={node}

View File

@ -82,7 +82,7 @@ function NodeComponent({ id, ...props }: { id: string } & React.HTMLAttributes<H
{renderBlock()}
<BlockOverlay id={id} />
{isSelected ? (
<div className='pointer-events-none absolute inset-0 z-[-1] m-[1px] rounded-[4px] bg-[#E0F8FF]' />
<div className='pointer-events-none absolute inset-0 z-[-1] my-[1px] rounded-[4px] bg-fill-hover' />
) : null}
</div>
</NodeIdContext.Provider>

View File

@ -11,7 +11,7 @@ export default function QuoteBlock({
}) {
return (
<div className={'py-[2px]'}>
<div className={'border-l-4 border-solid border-main-accent px-3 '}>
<div className={'border-l-4 border-solid border-fill-default px-3 '}>
<TextBlock node={node} />
<NodeChildren childIds={childIds} />
</div>

View File

@ -20,7 +20,7 @@ function Root({ documentData }: { documentData: DocumentData }) {
return (
<>
<div id='appflowy-block-doc' className='h-[100%] overflow-hidden caret-custom-caret'>
<div id='appflowy-block-doc' className='h-[100%] overflow-hidden text-text-title caret-text-title'>
<VirtualizedList node={node} childIds={childIds} renderNode={renderNode} />
</div>
</>

View File

@ -18,7 +18,7 @@ const TextActionComponent = ({ container }: { container: HTMLDivElement }) => {
style={{
opacity: 0,
}}
className='absolute mt-[-6px] inline-flex h-[32px] min-w-[100px] items-stretch overflow-hidden rounded-[8px] bg-black leading-tight text-white shadow-lg transition-opacity duration-100'
className='absolute mt-[-6px] inline-flex h-[32px] min-w-[100px] items-stretch overflow-hidden rounded-[8px] bg-bg-base leading-tight text-text-title shadow-lg transition-opacity duration-100'
onMouseDown={(e) => {
// prevent toolbar from taking focus away from editor
e.preventDefault();

View File

@ -29,7 +29,7 @@ const FormatButton = ({ format, icon }: { format: TextAction; icon: string }) =>
const { node: focusNode } = useSubscribeNode(focusId);
const [isActive, setIsActive] = React.useState(false);
const color = useMemo(() => (isActive ? '#00BCF0' : 'white'), [isActive]);
const color = useMemo(() => (isActive ? 'text-content-hover' : 'text-text-title'), [isActive]);
const isFormatActive = useCallback(async () => {
if (!focusNode) return false;
@ -144,9 +144,9 @@ const FormatButton = ({ format, icon }: { format: TextAction; icon: string }) =>
return (
<MenuTooltip title={formatTooltips[format]}>
<IconButton size='small' sx={{ color }} onClick={() => formatClick(format)}>
<div className={`${color} cursor-pointer px-1 hover:text-fill-default`} onClick={() => formatClick(format)}>
{formatIcon}
</IconButton>
</div>
</MenuTooltip>
);
};

View File

@ -5,11 +5,7 @@ function MenuTooltip({ title, children }: { children: JSX.Element; title?: strin
return (
<Tooltip
slotProps={{ tooltip: { style: { background: '#E0F8FF', borderRadius: 8 } } }}
title={
<div className='flex flex-col'>
<span className='text-base font-medium text-black'>{title}</span>
</div>
}
title={title}
placement='top-start'
>
<div>{children}</div>

View File

@ -12,7 +12,7 @@ function TurnIntoSelect({ id }: { id: string }) {
}>();
const { node } = useSubscribeNode(id);
const handleClick = useCallback((event: React.MouseEvent<HTMLButtonElement>) => {
const handleClick = useCallback((event: React.MouseEvent<HTMLDivElement>) => {
const rect = event.currentTarget.getBoundingClientRect();
setAnchorPosition({
@ -30,12 +30,10 @@ function TurnIntoSelect({ id }: { id: string }) {
return (
<>
<MenuTooltip title='Turn into'>
<Button size={'small'} variant='text' onClick={handleClick}>
<div className='flex items-center text-main-accent'>
<span>{node.type}</span>
<ArrowDropDown />
</div>
</Button>
<div onClick={handleClick} className='flex cursor-pointer items-center px-2 text-sm text-fill-default'>
<span>{node.type}</span>
<ArrowDropDown />
</div>
</MenuTooltip>
<TurnIntoPopover
id={id}

View File

@ -31,7 +31,7 @@ function TextActionMenuList() {
{groupItems.map(
(group, i: number) =>
group.length > 0 && (
<div className={'flex border-r border-solid border-shade-2 px-1 last:border-r-0'} key={i}>
<div className={'flex border-r border-solid border-line-border px-1 last:border-r-0'} key={i}>
{group.map((item) => (
<div key={item} className={'flex items-center'}>
{renderNode(item)}

View File

@ -32,7 +32,7 @@ export default function TodoListBlock({
/>
</div>
</div>
<div className={`flex-1 ${checked ? 'text-shade-2 line-through' : ''}`}>
<div className={`flex-1 ${checked ? 'text-text-caption line-through' : ''}`}>
<TextBlock node={node} />
</div>
</div>

View File

@ -6,7 +6,7 @@ import { decorateCode } from '$app/components/document/_shared/SlateEditor/decor
import { CodeBlockElement } from '$app/components/document/_shared/SlateEditor/CodeElements';
import TextLeaf from '$app/components/document/_shared/SlateEditor/TextLeaf';
function CodeEditor({ language, ...props }: CodeEditorProps) {
function CodeEditor({ language, isDark, ...props }: CodeEditorProps) {
const { editor, onChange, value, ref, ...editableProps } = useEditor({
...props,
isCodeBlock: true,
@ -18,8 +18,9 @@ function CodeEditor({ language, ...props }: CodeEditorProps) {
<Editable
{...editableProps}
decorate={(entry) => {
const codeRange = decorateCode(entry, language);
const codeRange = decorateCode(entry, language, isDark);
const range = editableProps.decorate?.(entry) || [];
return [...range, ...codeRange];
}}
renderLeaf={(leafProps) => <TextLeaf editor={editor} {...leafProps} isCodeBlock={true} />}

View File

@ -9,7 +9,7 @@ function TextEditor({ placeholder = "Type '/' for commands", ...props }: EditorP
const { editor, onChange, value, ref, ...editableProps } = useEditor(props);
return (
<div ref={ref} className={'py-0.5'}>
<div ref={ref} className={'px-1 py-0.5'}>
<Slate editor={editor} onChange={onChange} value={value}>
<Editable
renderLeaf={(leafProps) => <TextLeaf {...leafProps} editor={editor} />}

View File

@ -40,7 +40,7 @@ const TextLeaf = (props: TextLeafProps) => {
if (leaf.code) {
newChildren = (
<span
className={`bg-custom-code text-main-hovered`}
className={`bg-fill-selector text-text-title`}
style={{
fontSize: '85%',
lineHeight: 'normal',
@ -97,8 +97,8 @@ const TextLeaf = (props: TextLeafProps) => {
isCodeBlock && 'token',
leaf.prism_token && leaf.prism_token,
leaf.strikethrough && 'line-through',
leaf.selection_high_lighted && 'bg-main-secondary',
leaf.link_selection_lighted && 'text-link bg-main-secondary',
leaf.selection_high_lighted && 'bg-fill-selector',
leaf.link_selection_lighted && 'text-text-link-selector bg-fill-selector',
leaf.code && 'inline-code',
leaf.bold && 'font-bold',
leaf.italic && 'italic',

View File

@ -1,5 +1,5 @@
import Prism from 'prismjs';
import 'prismjs/themes/prism.css';
import 'prismjs/components/prism-javascript';
import 'prismjs/components/prism-json';
import 'prismjs/components/prism-typescript';
@ -15,6 +15,7 @@ const push_string = (
token_type = 'text'
) => {
let newStart = start;
ranges.push({
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
@ -38,13 +39,16 @@ const recurseTokenize = (
if (typeof token === 'string') {
return push_string(token, path, start, ranges, parent_tag);
}
if ('content' in token) {
if (token.content instanceof Array) {
// Calls recurseTokenize on nested Tokens in content
let newStart = start;
for (const subToken of token.content) {
newStart = recurseTokenize(subToken, path, ranges, newStart, token.type) || 0;
}
return newStart;
}
@ -52,8 +56,28 @@ const recurseTokenize = (
}
};
export const decorateCode = ([node, path]: NodeEntry, language: string) => {
function switchCodeTheme(isDark: boolean) {
const link = document.getElementById('prism-css');
if (link) {
document.head.removeChild(link);
}
const newLink = document.createElement('link');
newLink.rel = 'stylesheet';
newLink.href = isDark
? 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.24.1/themes/prism-dark.min.css'
: 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.24.1/themes/prism.min.css';
newLink.id = 'prism-css';
document.head.appendChild(newLink);
}
export const decorateCode = ([node, path]: NodeEntry, language: string, isDark: boolean) => {
switchCodeTheme(isDark);
const ranges: BaseRange[] = [];
if (!Text.isText(node)) {
return ranges;
}
@ -62,9 +86,11 @@ export const decorateCode = ([node, path]: NodeEntry, language: string) => {
const tokens = Prism.tokenize(node.text, Prism.languages[language]);
let start = 0;
for (const token of tokens) {
start = recurseTokenize(token, path, ranges, start) || 0;
}
return ranges;
} catch {
return ranges;

View File

@ -17,7 +17,7 @@ function EquationEditContent({
multiline?: boolean;
}) {
return (
<div className={'flex p-2'}>
<div className={'flex items-center p-2'}>
<TextField
placeholder={placeholder}
autoFocus={true}
@ -45,8 +45,14 @@ function EquationEditContent({
onChange(newVal);
}}
/>
<Divider sx={{ height: 'initial', marginLeft: '10px' }} orientation='vertical' />
<IconButton onClick={onConfirm} color='primary' sx={{ p: '10px' }} aria-label='directions'>
<IconButton
className={'h-[23px] w-[23px]'}
onClick={onConfirm}
color='primary'
sx={{ p: '10px', m: '10px' }}
aria-label='directions'
>
<CheckOutlined />
</IconButton>
</div>

View File

@ -4,11 +4,11 @@ import KatexMath from '$app/components/document/_shared/KatexMath';
function TemporaryEquation({ latex }: { latex: string }) {
return (
<span className={'rounded bg-shade-6 px-1 py-0.5'} contentEditable={false}>
<span className={'rounded bg-fill-selector px-1 py-0.5'} contentEditable={false}>
{latex ? (
<KatexMath latex={latex} isInline />
) : (
<span className={'text-shade-3'}>
<span className={'text-text-title'}>
<Functions /> {'New equation'}
</span>
)}

View File

@ -1,4 +1,5 @@
import React, { useEffect, useState } from 'react';
import TextField from '@mui/material/TextField';
function EditLink({
autoFocus,
@ -18,19 +19,19 @@ function EditLink({
}, [val, onChange]);
return (
<div className={'mb-2 text-sm'}>
<div className={'mb-1 text-shade-2'}>{text}</div>
<div className={'flex rounded border bg-main-selector p-1 focus-within:border-main-hovered'}>
<input
autoFocus={autoFocus}
className={'flex-1 outline-none'}
onChange={(e) => {
const newValue = e.target.value;
setVal(newValue);
}}
value={val}
/>
</div>
<div className={'mb-2 w-[100%] text-sm'}>
<TextField
className={'w-[100%]'}
label={text}
autoFocus={autoFocus}
variant='standard'
onChange={(e) => {
const newValue = e.target.value;
setVal(newValue);
}}
value={val}
/>
</div>
);
}

View File

@ -10,6 +10,7 @@ const iconSize = {
width: '1rem',
height: '1rem',
};
function EditLinkToolbar({
blockId,
linkElement,
@ -29,16 +30,20 @@ function EditLinkToolbar({
}) {
const { show, contentHolder } = useMessage();
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
const toolbarDom = ref.current;
if (!toolbarDom) return;
const linkRect = linkElement.getBoundingClientRect();
const node = getNode(blockId);
if (!node) return;
const nodeRect = node.getBoundingClientRect();
const top = linkRect.top - nodeRect.top + linkRect.height + 4;
const left = linkRect.left - nodeRect.left;
toolbarDom.style.top = `${top}px`;
toolbarDom.style.left = `${left}px`;
toolbarDom.style.opacity = '1';
@ -54,7 +59,7 @@ function EditLinkToolbar({
style={{
opacity: 0,
}}
className='absolute z-10 inline-flex h-[32px] min-w-[200px] max-w-[400px] items-stretch overflow-hidden rounded-[8px] bg-white leading-tight text-black shadow-md transition-opacity duration-100'
className='absolute z-10 inline-flex h-[32px] min-w-[200px] max-w-[400px] items-stretch overflow-hidden rounded-[8px] bg-bg-body leading-tight text-text-title shadow-md transition-opacity duration-100'
>
<div className={'flex w-[100%] items-center justify-between px-2 text-[75%]'}>
<div className={'mr-2'}>

View File

@ -1,22 +0,0 @@
import React from 'react';
import Button from '@mui/material/Button';
import { DeleteOutline } from '@mui/icons-material';
function LinkButton({ icon, title, onClick }: { icon: React.ReactNode; title: string; onClick: () => void }) {
return (
<div className={'pt-1'}>
<Button
className={'w-[100%]'}
style={{
justifyContent: 'flex-start',
}}
startIcon={icon}
onClick={onClick}
>
{title}
</Button>
</div>
);
}
export default LinkButton;

View File

@ -1,14 +1,13 @@
import React, { useCallback, useContext } from 'react';
import React, { useCallback } from 'react';
import Popover from '@mui/material/Popover';
import { Divider } from '@mui/material';
import { DeleteOutline, Done } from '@mui/icons-material';
import EditLink from '$app/components/document/_shared/TextLink/EditLink';
import { useAppDispatch, useAppSelector } from '$app/stores/store';
import { useAppDispatch } from '$app/stores/store';
import { linkPopoverActions, rangeActions } from '$app_reducers/document/slice';
import { formatLinkThunk } from '$app_reducers/document/async-actions/link';
import LinkButton from '$app/components/document/_shared/TextLink/LinkButton';
import { useSubscribeDocument } from '$app/components/document/_shared/SubscribeDoc.hooks';
import { useSubscribeLinkPopover } from '$app/components/document/_shared/SubscribeLinkPopover.hooks';
import Button from '@mui/material/Button';
function LinkEditPopover() {
const dispatch = useAppDispatch();
@ -27,6 +26,7 @@ function LinkEditPopover() {
index: selection.index,
length: title.length,
};
dispatch(
rangeActions.setRange({
docId,
@ -120,18 +120,12 @@ function LinkEditPopover() {
})
}
/>
<Divider />
<LinkButton
title={'Remove link'}
icon={<DeleteOutline />}
onClick={() => {
onChange({
title,
});
onDone();
}}
/>
<LinkButton title={'Done'} icon={<Done />} onClick={onDone} />
<div className={'flex items-center justify-end'}>
<Button onClick={onDone}>
<Done />
Done
</Button>
</div>
</div>
</Popover>
);

View File

@ -8,13 +8,7 @@ function LinkHighLight({ children, leaf, title }: { leaf: { text: string }; titl
<span contentEditable={false}>{title}</span>
) : null}
<span
style={{
display: 'none',
}}
>
{children}
</span>
<span className={'absolute opacity-0'}>{children}</span>
</>
);
}

View File

@ -28,8 +28,10 @@ function TextLink({
const onEdit = useCallback(() => {
if (!ref.current) return;
const selection = getSelection(ref.current);
if (!selection) return;
const rect = ref.current?.getBoundingClientRect();
if (!rect) return;
dispatch(
linkPopoverActions.setLinkPopover({
@ -48,6 +50,7 @@ function TextLink({
})
);
}, [blockId, dispatch, docId, getSelection, href, ref, title]);
if (!blockId) return null;
return (
@ -59,9 +62,9 @@ function TextLink({
href={href}
target='_blank'
rel='noopener noreferrer'
className='cursor-pointer text-main-hovered'
className='cursor-pointer text-text-link-default'
>
<span className={' border-b-[1px] border-b-main-hovered '}>{children}</span>
<span className={' border-b-[1px] border-b-text-link-default '}>{children}</span>
</a>
{ref.current && (
<EditLinkToolbar

View File

@ -195,7 +195,7 @@ const TurnIntoPopover = ({
return (
<Popover disableAutoFocus={true} onClose={onClose} {...props}>
<div className={'min-w-[220px]'}>
<div className={'min-w-[220px] p-2'}>
{options.map((option) => {
return (
<MenuItem

View File

@ -88,7 +88,7 @@ function UploadImage({ onChange }: UploadImageProps) {
<input onChange={handleChange} ref={inputRef} type='file' className={'hidden'} accept={'image/*'} />
<div
className={
'flex flex-col items-center justify-center rounded-md border border-dashed border-main-accent bg-main-selector py-10 text-main-accent'
'flex flex-col items-center justify-center rounded-md border border-dashed border-content-hover py-10 text-content-hover'
}
style={{
borderColor: errorColor,
@ -110,7 +110,7 @@ function UploadImage({ onChange }: UploadImageProps) {
style={{
color: errorColor,
}}
className={`mt-5 text-sm text-shade-3`}
className={`mt-5 text-sm text-text-caption`}
>
The maximum file size is 5MB. Supported formats: JPG, PNG, GIF, SVG.
</div>

View File

@ -11,7 +11,7 @@ export const ErrorModal = ({ message, onClose }: { message: string; onClose: ()
>
<button
onClick={() => onClose()}
className={'absolute right-0 top-0 z-10 px-2 py-2 text-shade-5 hover:text-black'}
className={'absolute right-0 top-0 z-10 px-2 py-2 text-text-caption hover:text-text-title'}
>
<i className={'block h-8 w-8'}>
<CloseSvg></CloseSvg>

View File

@ -2,7 +2,7 @@ import AddSvg from '../../_shared/svg/AddSvg';
export const GridAddView = () => {
return (
<button className='flex cursor-pointer items-center rounded-lg p-2 text-sm hover:bg-main-selector'>
<button className='flex cursor-pointer items-center rounded-lg p-2 text-sm hover:bg-fill-hover'>
<i className='mr-2 h-5 w-5'>
<AddSvg />
</i>

View File

@ -18,15 +18,15 @@ export const GridTableHeader = ({ controller }: { controller: DatabaseController
return <GridTableHeaderItem field={field} controller={controller} key={i} />;
})}
<th className='m-0 w-40 border border-r-0 border-shade-6 p-0'>
<th className='m-0 w-40 border border-r-0 border-line-border p-0'>
<div
className='flex cursor-pointer items-center px-4 py-2 text-shade-3 hover:bg-main-secondary hover:text-black'
className='flex cursor-pointer items-center px-4 py-2 text-text-caption hover:bg-fill-hover hover:text-text-title'
onClick={onAddField}
>
<i className='mr-2 h-5 w-5'>
<AddSvg />
</i>
<span>{t('grid.field.newColumn')}</span>
<span>{t('grid.newCol')}</span>
</div>
</th>
</tr>

View File

@ -45,9 +45,11 @@ export const GridTableHeaderItem = ({
if (!editingField) return;
const currentField = controller.fieldController.getField(editingField.fieldId);
if (!currentField) return;
const typeOptionController = new TypeOptionController(controller.viewId, Some(currentField));
await typeOptionController.switchToField(newType);
setEditingField({
@ -59,9 +61,9 @@ export const GridTableHeaderItem = ({
};
return (
<th key={field.fieldId} className='m-0 border border-l-0 border-shade-6 p-0'>
<th key={field.fieldId} className='m-0 border border-l-0 border-line-border p-0'>
<div
className={'flex w-full cursor-pointer items-center px-4 py-2 hover:bg-main-secondary'}
className={'flex w-full cursor-pointer items-center px-4 py-2 hover:bg-fill-hover'}
ref={ref}
onClick={() => {
if (!ref.current) return;
@ -73,7 +75,7 @@ export const GridTableHeaderItem = ({
setShowFieldEditor(true);
}}
>
<i className={'mr-2 h-5 w-5 text-shade-3'}>
<i className={'mr-2 h-5 w-5 text-text-caption'}>
{field.fieldType === FieldType.RichText && <TextTypeSvg></TextTypeSvg>}
{field.fieldType === FieldType.Number && <NumberTypeSvg></NumberTypeSvg>}
{field.fieldType === FieldType.DateTime && <DateTypeSvg></DateTypeSvg>}

View File

@ -2,13 +2,14 @@ import { DatabaseController } from '@/appflowy_app/stores/effects/database/datab
import AddSvg from '../../_shared/svg/AddSvg';
import { useGridAddRow } from './GridAddRow.hooks';
import { useTranslation } from 'react-i18next';
export const GridAddRow = ({ controller }: { controller: DatabaseController }) => {
const { addRow } = useGridAddRow(controller);
const { t } = useTranslation();
return (
<div>
<button className='flex cursor-pointer items-center text-gray-500 hover:text-black' onClick={addRow}>
<button className='flex cursor-pointer items-center text-text-caption hover:text-text-title' onClick={addRow}>
<i className='mr-2 h-5 w-5'>
<AddSvg />
</i>

View File

@ -21,7 +21,7 @@ export const GridTableRow = ({
<tr className='group'>
{cells.map((cell, cellIndex) => {
return (
<td className='m-0 border border-l-0 border-shade-6 p-0 ' key={cellIndex}>
<td className='m-0 border border-l-0 border-line-border p-0 ' key={cellIndex}>
<div className='flex w-full items-center justify-end'>
<GridCell
cellIdentifier={cell.cellIdentifier}
@ -32,7 +32,7 @@ export const GridTableRow = ({
{cellIndex === 0 && (
<div
onClick={() => onOpenRow(row)}
className='mr-1 hidden h-9 w-9 cursor-pointer rounded p-2 hover:bg-slate-200 group-hover:block '
className='mr-1 hidden h-8 w-8 cursor-pointer rounded p-1.5 text-text-caption hover:bg-fill-hover group-hover:block '
>
<FullView />
</div>

View File

@ -8,7 +8,7 @@ export const GridTitleOptionsPopup = ({ onClose }: { onClose?: () => void }) =>
const items: IPopupItem[] = [
{
icon: (
<i className={'h-[16px] w-[16px] text-black'}>
<i className={'h-[16px] w-[16px] text-text-title'}>
<FilterSvg />
</i>
),
@ -19,7 +19,7 @@ export const GridTitleOptionsPopup = ({ onClose }: { onClose?: () => void }) =>
},
{
icon: (
<i className={'h-[16px] w-[16px] text-black'}>
<i className={'h-[16px] w-[16px] text-text-title'}>
<SortSvg />
</i>
),
@ -30,7 +30,7 @@ export const GridTitleOptionsPopup = ({ onClose }: { onClose?: () => void }) =>
},
{
icon: (
<i className={'h-[16px] w-[16px] text-black'}>
<i className={'h-[16px] w-[16px] text-text-title'}>
<PropertiesSvg />
</i>
),
@ -41,7 +41,7 @@ export const GridTitleOptionsPopup = ({ onClose }: { onClose?: () => void }) =>
},
{
icon: (
<i className={'h-[16px] w-[16px] text-black'}>
<i className={'h-[16px] w-[16px] text-text-title'}>
<GroupBySvg />
</i>
),
@ -51,5 +51,6 @@ export const GridTitleOptionsPopup = ({ onClose }: { onClose?: () => void }) =>
title: 'Group by',
},
];
return <PopupSelect items={items} className={'absolute top-full z-10 w-fit'} onOutsideClick={onClose} />;
};

View File

@ -1,5 +1,9 @@
import { HideMenuSvg } from '../_shared/svg/HideMenuSvg';
import { ShowMenuSvg } from '../_shared/svg/ShowMenuSvg';
import { useAppSelector } from '$app/stores/store';
import { ThemeMode } from '$app/interfaces';
import { AppflowyLogoLight } from '$app/components/_shared/svg/AppflowyLogoLight';
import { AppflowyLogoDark } from '$app/components/_shared/svg/AppflowyLogoDark';
export const AppLogo = ({
iconToShow,
@ -10,9 +14,12 @@ export const AppLogo = ({
onHideMenuClick?: () => void;
onShowMenuClick?: () => void;
}) => {
const isDark = useAppSelector((state) => state.currentUser?.userSetting?.themeMode === ThemeMode.Dark);
return (
<div className={'mb-2 flex h-[60px] items-center justify-between px-6'}>
<img src={'/images/flowy_logo_with_text.svg'} alt={'logo'} />
<div className={'mb-2 flex h-[60px] items-center justify-between px-6 text-text-title'}>
{isDark ? <AppflowyLogoDark /> : <AppflowyLogoLight />}
{iconToShow === 'hide' && (
<button onClick={onHideMenuClick} className={'h-5 w-5'}>
<i>
@ -21,7 +28,7 @@ export const AppLogo = ({
</button>
)}
{iconToShow === 'show' && (
<button onClick={onShowMenuClick} className={'h-5 w-5'}>
<button onClick={onShowMenuClick} className={'h-5 w-5 text-text-title'}>
<i>
<ShowMenuSvg></ShowMenuSvg>
</i>

View File

@ -1,11 +1,11 @@
export const FooterPanel = () => {
return (
<div className={'flex items-center justify-between px-2 py-2'}>
<div className={'text-xs text-shade-4'}>
<div className={'text-xs text-text-caption'}>
&copy; 2023 AppFlowy. <a href={'https://github.com/AppFlowy-IO/AppFlowy'}>GitHub</a>
</div>
<div>
<button className={'h-8 w-8 rounded bg-main-secondary text-black'}>?</button>
<button className={'h-8 w-8 rounded bg-fill-selector text-text-title hover:bg-fill-hover'}>?</button>
</div>
</div>
);

View File

@ -2,6 +2,9 @@ import { ShowMenuSvg } from '../../_shared/svg/ShowMenuSvg';
import { useEffect, useState } from 'react';
import { useAppSelector } from '$app/stores/store';
import { useLocation } from 'react-router-dom';
import { ArrowLeft, ArrowRight } from '@mui/icons-material';
import { ArrowLeftSvg } from '$app/components/_shared/svg/ArrowLeftSvg';
import { ArrowRightSvg } from '$app/components/_shared/svg/ArrowRightSvg';
export const Breadcrumbs = ({ menuHidden, onShowMenuClick }: { menuHidden: boolean; onShowMenuClick: () => void }) => {
const [folderName, setFolderName] = useState('');
@ -14,11 +17,13 @@ export const Breadcrumbs = ({ menuHidden, onShowMenuClick }: { menuHidden: boole
const { pathname } = currentLocation;
const parts = pathname.split('/');
const pageId = parts[parts.length - 1];
setActivePageId(pageId);
}, [currentLocation]);
useEffect(() => {
const page = pagesStore.find((p) => p.id === activePageId);
// const folder = foldersStore.find((f) => f.id === page?.parentPageId);
// setFolderName(folder?.title ?? '');
setPageName(page?.title ?? '');
@ -28,16 +33,16 @@ export const Breadcrumbs = ({ menuHidden, onShowMenuClick }: { menuHidden: boole
<div className={'flex items-center'}>
<div className={'mr-4 flex items-center'}>
{menuHidden && (
<button onClick={() => onShowMenuClick()} className={'mr-2 h-5 w-5'}>
<button onClick={() => onShowMenuClick()} className={'mr-2 h-5 w-5 text-text-title'}>
<ShowMenuSvg></ShowMenuSvg>
</button>
)}
<button className={'p-1'} onClick={() => history.back()}>
<img src={'/images/home/arrow_left.svg'} alt={''} />
<button className={'h-6 w-6 rounded p-1 text-text-title hover:bg-fill-hover'} onClick={() => history.back()}>
<ArrowLeftSvg />
</button>
<button className={'p-1'} onClick={() => history.forward()}>
<img src={'/images/home/arrow_right.svg'} alt={''} />
<button className={'h-6 w-6 rounded p-1 text-text-title hover:bg-fill-hover'} onClick={() => history.forward()}>
<ArrowRightSvg />
</button>
</div>
<div className={'mr-8 flex items-center gap-4'}>

View File

@ -3,7 +3,7 @@ import { PageOptions } from './PageOptions';
export const HeaderPanel = ({ menuHidden, onShowMenuClick }: { menuHidden: boolean; onShowMenuClick: () => void }) => {
return (
<div className={'flex h-[60px] items-center justify-between border-b border-shade-6 px-8'}>
<div className={'flex h-[60px] items-center justify-between border-b border-line-border px-8'}>
<Breadcrumbs menuHidden={menuHidden} onShowMenuClick={onShowMenuClick}></Breadcrumbs>
<PageOptions></PageOptions>
</div>

View File

@ -1,13 +1,14 @@
import { EarthSvg } from '$app/components/_shared/svg/EarthSvg';
import { useState } from 'react';
import { LanguageSelectPopup } from '$app/components/_shared/LanguageSelectPopup';
import { LanguageOutlined } from '@mui/icons-material';
export const LanguageButton = () => {
const [showPopup, setShowPopup] = useState(false);
return (
<>
<button onClick={() => setShowPopup(!showPopup)} className={'h-5 w-5'}>
<EarthSvg></EarthSvg>
<button onClick={() => setShowPopup(!showPopup)} className={'h-8 w-8 rounded text-text-title hover:bg-fill-hover'}>
<LanguageOutlined />
</button>
{showPopup && <LanguageSelectPopup onClose={() => setShowPopup(false)}></LanguageSelectPopup>}
</>

View File

@ -16,7 +16,11 @@ export const PageOptions = () => {
<LanguageButton></LanguageButton>
<button id='option-button' className={'relative h-8 w-8'} onClick={onOptionsClick}>
<button
id='option-button'
className={'relative h-8 w-8 rounded text-text-title hover:bg-fill-hover'}
onClick={onOptionsClick}
>
<Details2Svg></Details2Svg>
</button>
</div>

View File

@ -15,6 +15,7 @@ export const MainPanel = ({
children: ReactNode;
}) => {
const [animation, setAnimation] = useState(false);
useEffect(() => {
if (!menuHidden) {
setTimeout(() => {
@ -27,7 +28,7 @@ export const MainPanel = ({
return (
<div
className={`absolute inset-0 flex h-full flex-1 flex-col`}
className={`absolute inset-0 flex h-full flex-1 flex-col bg-bg-body text-text-title`}
style={{
transition: menuHidden || animation ? `left ${ANIMATION_DURATION}ms ease-out` : 'none',
left: `${menuHidden ? 0 : left}px`,

View File

@ -33,6 +33,7 @@ export const useNavItem = (page: IPage) => {
const loadInsidePages = async () => {
const result = await service.getChildViews();
if (!result.ok) return;
const views = result.val;
const updatedPages: IPage[] = views.map<IPage>((view) => ({
@ -42,6 +43,7 @@ export const useNavItem = (page: IPage) => {
title: view.name,
showPagesInside: false,
}));
appDispatch(pagesActions.addInsidePages({ currentPageId: page.id, insidePages: updatedPages }));
};
@ -62,6 +64,7 @@ export const useNavItem = (page: IPage) => {
const { pathname } = currentLocation;
const parts = pathname.split('/');
const pageId = parts[parts.length - 1];
setActivePageId(pageId);
}, [currentLocation]);
@ -76,6 +79,7 @@ export const useNavItem = (page: IPage) => {
// recursively get all unfolded child pages
const getChildCount: (startPage: IPage) => number = (startPage: IPage) => {
let count = 0;
count = pages.filter((p) => p.parentPageId === startPage.id).length;
pages
.filter((p) => p.parentPageId === startPage.id)
@ -92,7 +96,7 @@ export const useNavItem = (page: IPage) => {
};
const onPageOptionsClick = () => {
setShowPageOptions(!showPageOptions);
setShowPageOptions((prevState) => !prevState);
};
const startPageRename = () => {
@ -181,6 +185,7 @@ export const useNavItem = (page: IPage) => {
if (newViewResult.ok) {
const newView = newViewResult.val;
if (!page.showPagesInside) {
appDispatch(pagesActions.toggleShowPages({ id: page.id }));
}

View File

@ -46,6 +46,7 @@ export const NavItem = ({ page }: { page: IPage }) => {
useEffect(() => {
if (el.current) {
const { top } = el.current.getBoundingClientRect();
setPopupY(top);
}
}, [showPageOptions, showNewPageOptions, showRenamePopup]);
@ -56,35 +57,31 @@ export const NavItem = ({ page }: { page: IPage }) => {
className={`overflow-hidden transition-all`}
style={{ height: folderHeight, transitionDuration: `${ANIMATION_DURATION}ms` }}
>
<div
style={{ height: PAGE_ITEM_HEIGHT }}
className={`flex cursor-pointer items-center justify-between rounded-lg px-4 hover:bg-surface-2 ${
activePageId === page.id ? 'bg-surface-2' : ''
}`}
>
<div className={'flex h-full min-w-0 flex-1 items-center'}>
<button
onClick={() => onUnfoldClick()}
className={`mr-2 h-5 w-5 transition-transform duration-200 ${page.showPagesInside && 'rotate-180'}`}
>
<DropDownShowSvg></DropDownShowSvg>
</button>
<div
onClick={() => onPageClick(page)}
className={
'flex h-full min-w-0 flex-1 items-center overflow-hidden overflow-ellipsis whitespace-nowrap text-left'
}
>
{page.title}
<div style={{ height: PAGE_ITEM_HEIGHT }} className={`cursor-pointer px-1 py-1`}>
<div
className={`flex items-center justify-between rounded-lg px-2 py-1 hover:bg-fill-active ${
activePageId === page.id ? 'bg-fill-active' : ''
}`}
>
<div className={'flex h-full min-w-0 flex-1 items-center'}>
<button
onClick={() => onUnfoldClick()}
className={`mr-2 h-5 w-5 transition-transform duration-200 ${page.showPagesInside && 'rotate-180'}`}
>
<DropDownShowSvg></DropDownShowSvg>
</button>
<div onClick={() => onPageClick(page)} className={'mr-1 flex h-full min-w-0 items-center text-left'}>
<span className={'w-[100%] overflow-hidden overflow-ellipsis whitespace-nowrap'}>{page.title}</span>
</div>
</div>
<div className={'flex items-center'}>
<Button size={'box-small-transparent'} onClick={() => onPageOptionsClick()}>
<Details2Svg></Details2Svg>
</Button>
<Button size={'box-small-transparent'} onClick={() => onNewPageClick()}>
<AddSvg></AddSvg>
</Button>
</div>
</div>
<div className={'flex items-center'}>
<Button size={'box-small-transparent'} onClick={() => onPageOptionsClick()}>
<Details2Svg></Details2Svg>
</Button>
<Button size={'box-small-transparent'} onClick={() => onNewPageClick()}>
<AddSvg></AddSvg>
</Button>
</div>
</div>
<div className={'pl-4'}>
@ -101,7 +98,7 @@ export const NavItem = ({ page }: { page: IPage }) => {
onDeleteClick={() => deletePage()}
onDuplicateClick={() => duplicatePage()}
onClose={() => closePopup()}
top={popupY - 124 + 40}
top={popupY - 124 + 58}
></NavItemOptionsPopup>
)}
{showNewPageOptions && (
@ -110,7 +107,7 @@ export const NavItem = ({ page }: { page: IPage }) => {
onBoardClick={() => onAddNewPage(ViewLayoutPB.Board)}
onGridClick={() => onAddNewPage(ViewLayoutPB.Grid)}
onClose={() => closePopup()}
top={popupY - 124 + 40}
top={popupY - 124 + 58}
></NewPagePopup>
)}
{showRenamePopup && (

View File

@ -19,7 +19,7 @@ export const NavItemOptionsPopup = ({
const items: IPopupItem[] = [
{
icon: (
<i className={'h-[16px] w-[16px] text-black'}>
<i className={'h-[16px] w-[16px] text-text-title'}>
<EditSvg></EditSvg>
</i>
),
@ -28,7 +28,7 @@ export const NavItemOptionsPopup = ({
},
{
icon: (
<i className={'h-[16px] w-[16px] text-black'}>
<i className={'h-[16px] w-[16px] text-text-title'}>
<TrashSvg></TrashSvg>
</i>
),
@ -37,7 +37,7 @@ export const NavItemOptionsPopup = ({
},
{
icon: (
<i className={'h-[16px] w-[16px] text-black'}>
<i className={'h-[16px] w-[16px] text-text-title'}>
<CopySvg></CopySvg>
</i>
),

View File

@ -30,6 +30,7 @@ export const NavigationPanel = ({
const { pathname } = currentLocation;
const parts = pathname.split('/');
const pageId = parts[parts.length - 1];
setActivePageId(pageId);
}, [currentLocation]);
@ -46,7 +47,7 @@ export const NavigationPanel = ({
return (
<>
<div
className={`absolute inset-0 flex flex-col justify-between bg-surface-1 text-sm`}
className={`absolute inset-0 flex flex-col justify-between bg-bg-base text-sm text-text-title`}
style={{
transition: `left ${ANIMATION_DURATION}ms ease-out`,
width: `${width}px`,
@ -70,12 +71,12 @@ export const NavigationPanel = ({
</div>
<div className={'flex max-h-[215px] flex-col'}>
<div className={'border-b border-shade-6 px-2 pb-4'}>
<div className={'border-b border-line-border px-2 pb-4'}>
{/*<PluginsButton></PluginsButton>*/}
<DesignSpec></DesignSpec>
<AllIcons></AllIcons>
<TestBackendButton></TestBackendButton>
{/*<DesignSpec></DesignSpec>*/}
{/*<AllIcons></AllIcons>*/}
{/*<TestBackendButton></TestBackendButton>*/}
{/*Trash Button*/}
<TrashButton></TrashButton>
@ -100,10 +101,11 @@ const WorkspaceApps: React.FC<{ pages: IPage[] }> = ({ pages }) => (
export const TestBackendButton = () => {
const navigate = useNavigate();
return (
<button
onClick={() => navigate('/page/api-test')}
className={'flex w-full items-center rounded-lg px-4 py-2 hover:bg-surface-2'}
className={'flex w-full items-center rounded-lg px-4 py-2 hover:bg-fill-active'}
>
API Test
</button>
@ -116,7 +118,7 @@ export const DesignSpec = () => {
return (
<button
onClick={() => navigate('page/colors')}
className={'flex w-full items-center rounded-lg px-4 py-2 hover:bg-surface-2'}
className={'flex w-full items-center rounded-lg px-4 py-2 hover:bg-fill-active'}
>
Color Palette
</button>
@ -125,10 +127,11 @@ export const DesignSpec = () => {
export const AllIcons = () => {
const navigate = useNavigate();
return (
<button
onClick={() => navigate('page/all-icons')}
className={'flex w-full items-center rounded-lg px-4 py-2 hover:bg-surface-2'}
className={'flex w-full items-center rounded-lg px-4 py-2 hover:bg-fill-active'}
>
All Icons
</button>

View File

@ -19,7 +19,7 @@ export const NewPagePopup = ({
const items: IPopupItem[] = [
{
icon: (
<i className={'h-[16px] w-[16px] text-black'}>
<i className={'h-[16px] w-[16px] text-text-title'}>
<DocumentSvg></DocumentSvg>
</i>
),
@ -28,7 +28,7 @@ export const NewPagePopup = ({
},
{
icon: (
<i className={'h-[16px] w-[16px] text-black'}>
<i className={'h-[16px] w-[16px] text-text-title'}>
<BoardSvg></BoardSvg>
</i>
),
@ -37,7 +37,7 @@ export const NewPagePopup = ({
},
{
icon: (
<i className={'h-[16px] w-[16px] text-black'}>
<i className={'h-[16px] w-[16px] text-text-title'}>
<GridSvg></GridSvg>
</i>
),

View File

@ -10,10 +10,10 @@ export const NewViewButton = ({ scrollDown }: { scrollDown: () => void }) => {
void onNewRootView();
scrollDown();
}}
className={'flex h-[50px] w-full items-center px-6 hover:bg-surface-2'}
className={'flex h-[50px] w-full items-center px-6 hover:bg-fill-active'}
>
<div className={'mr-2 rounded-full bg-main-accent text-white'}>
<div className={'h-[24px] w-[24px] text-white'}>
<div className={'mr-2 rounded-full bg-fill-default'}>
<div className={'h-[24px] w-[24px] text-content-onfill'}>
<AddSvg></AddSvg>
</div>
</div>

View File

@ -1,6 +1,6 @@
export const PluginsButton = () => {
return (
<button className={'flex w-full items-center rounded-lg px-4 py-2 hover:bg-surface-2'}>
<button className={'flex w-full items-center rounded-lg px-4 py-2 hover:bg-fill-active'}>
<img className={'mr-2 h-[24px] w-[24px]'} src={'/images/home/page.svg'} alt={''} />
<span>Plugins</span>
</button>

View File

@ -1,8 +1,13 @@
import { DeleteForeverOutlined } from '@mui/icons-material';
import { TrashSvg } from '$app/components/_shared/svg/TrashSvg';
export const TrashButton = () => {
return (
<button className={'flex w-full items-center rounded-lg px-4 py-2 hover:bg-surface-2'}>
<img className={'mr-2'} src={'/images/home/trash.svg'} alt={''} />
<span>Trash</span>
<button className={'flex w-full items-center rounded-lg px-4 py-2 text-text-title hover:bg-fill-active'}>
<span className={'h-[23px] w-[23px]'}>
<TrashSvg />
</span>
<span className={'ml-2'}>Trash</span>
</button>
);
};

View File

@ -10,7 +10,7 @@ export const Screen = ({ children }: { children: ReactNode }) => {
const { width, onHideMenuClick, onShowMenuClick, menuHidden } = useNavigationPanelHooks();
return (
<div className='flex h-screen w-screen bg-white text-black'>
<div className='flex h-screen w-screen bg-bg-body text-text-title'>
<NavigationPanel onHideMenuClick={onHideMenuClick} width={width} menuHidden={menuHidden}></NavigationPanel>
<MainPanel left={width} menuHidden={menuHidden} onShowMenuClick={onShowMenuClick}>

View File

@ -0,0 +1,122 @@
import React, { useCallback, useEffect, useMemo } from 'react';
import Select from '@mui/material/Select';
import { Theme, ThemeMode, UserSetting } from '$app/interfaces';
import MenuItem from '@mui/material/MenuItem';
function AppearanceSetting({
theme = Theme.Default,
themeMode = ThemeMode.Light,
onChange,
}: {
theme?: Theme;
themeMode?: ThemeMode;
onChange: (setting: UserSetting) => void;
}) {
useEffect(() => {
const html = document.documentElement;
html?.setAttribute('data-dark-mode', String(themeMode === ThemeMode.Dark));
html?.setAttribute('data-theme', theme);
}, [theme, themeMode]);
const themeModeOptions = useMemo(
() => [
{
value: ThemeMode.Light,
content: 'Light',
},
{
value: ThemeMode.Dark,
content: 'Dark',
},
],
[]
);
const themeOptions = useMemo(
() => [
{
value: Theme.Default,
content: 'Default',
},
{
value: Theme.Dandelion,
content: 'Dandelion',
},
{
value: Theme.Lavender,
content: 'Lavender',
},
],
[]
);
const renderSelect = useCallback(
(
items: {
options: { value: ThemeMode | Theme; content: string }[];
label: string;
value: ThemeMode | Theme;
onChange: (newValue: ThemeMode | Theme) => void;
}[]
) => {
return items.map((item) => {
const { value, options, label, onChange } = item;
return (
<div key={value} className={'mb-2 flex items-center justify-between text-sm'}>
<div className={'flex-1 text-text-title'}>{label}</div>
<div className={'flex items-center'}>
<Select
sx={{
fontSize: '0.85rem',
}}
variant={'standard'}
value={value}
onChange={(e) => {
onChange(e.target.value as ThemeMode | Theme);
}}
>
{options.map((option) => (
<MenuItem key={option.value} value={option.value}>
{option.content}
</MenuItem>
))}
</Select>
</div>
</div>
);
});
},
[]
);
return (
<div className={'flex flex-col'}>
{renderSelect([
{
options: themeModeOptions,
label: 'Theme Mode',
value: themeMode,
onChange: (newValue) => {
onChange({
themeMode: newValue as ThemeMode,
});
},
},
{
options: themeOptions,
label: 'Theme',
value: theme,
onChange: (newValue) => {
onChange({
theme: newValue as Theme,
});
},
},
])}
</div>
);
}
export default AppearanceSetting;

Some files were not shown because too many files have changed in this diff Show More