init: the first public commit for AFFiNE

This commit is contained in:
DarkSky
2022-07-22 15:49:21 +08:00
commit e3e3741393
1451 changed files with 108124 additions and 0 deletions
@@ -0,0 +1,140 @@
import { cloneElement, useMemo, useCallback, type ReactElement } from 'react';
import { styled } from '@toeverything/components/ui';
import {
CommentIcon,
LayoutIcon,
SettingsIcon,
} from '@toeverything/components/icons';
import { LayoutSettings } from '../Layout';
import { Comments } from '../Comments';
import { useActiveComment } from '../Comments/use-comments';
import { SettingsPanel } from '../Settings';
import { TabItemTitle } from './TabItemTitle';
import { useTabs } from './use-tabs';
const _defaultTabsKeys = ['layout', 'comment', 'settings'] as const;
export const ContainerTabs = () => {
const { activeCommentId, resolveComment } = useActiveComment();
const getSettingsTabsData = useCallback((): SettingsTabItemType[] => {
return [
{
type: 'layout',
text: 'Layout',
icon: (
<IconWrapper>
<LayoutIcon />
</IconWrapper>
),
panel: <LayoutSettings />,
},
{
type: 'comment',
text: 'Comment',
icon: (
<IconWrapper>
<CommentIcon />
</IconWrapper>
),
panel: (
<StyledSidebarContent>
<Comments
activeCommentId={activeCommentId}
resolveComment={resolveComment}
/>
</StyledSidebarContent>
),
},
{
type: 'settings',
text: 'Settings',
icon: (
<IconWrapper>
<SettingsIcon />
</IconWrapper>
),
panel: <SettingsPanel />,
},
];
}, [activeCommentId, resolveComment]);
const settingsTabsData = useMemo(() => {
return getSettingsTabsData();
}, [getSettingsTabsData]);
const [activeTab, setActiveTab] = useTabs(
_defaultTabsKeys as unknown as string[],
'settings'
);
return (
<>
<StyledTabsTitlesContainer>
{settingsTabsData.map(tab => {
const { type, text, icon } = tab;
return (
<TabItemTitle
title={text}
icon={icon}
isActive={activeTab === type}
onClick={() => {
setActiveTab(type);
}}
key={type}
/>
);
})}
</StyledTabsTitlesContainer>
<StyledTabsPanelsContainer>
{settingsTabsData.map(tab => {
const { type, panel } = tab;
return type === activeTab
? cloneElement(panel, { key: type })
: null;
})}
</StyledTabsPanelsContainer>
</>
);
};
const StyledTabsTitlesContainer = styled('div')(({ theme }) => {
return {
display: 'flex',
justifyContent: 'space-between',
marginTop: 24,
marginBottom: 24,
marginLeft: theme.affine.spacing.smSpacing,
marginRight: theme.affine.spacing.smSpacing,
};
});
const StyledTabsPanelsContainer = styled('div')(({ theme }) => {
return {
height: 'calc(100vh - 80px)',
borderTop: `1px solid ${theme.affine.palette.tagHover}`,
};
});
type SettingsTabsTypes = typeof _defaultTabsKeys[number];
type SettingsTabItemType = {
type: SettingsTabsTypes;
text: string;
icon: ReactElement;
panel: ReactElement;
};
const StyledSidebarContent = styled('div')(({ theme }) => {
return {
marginLeft: theme.affine.spacing.lgSpacing,
};
});
const IconWrapper = styled('div')({
fontSize: 0,
'& > svg': {
fontSize: '20px',
},
});
@@ -0,0 +1,38 @@
import type { ReactNode } from 'react';
import { styled, ListItem, ListIcon } from '@toeverything/components/ui';
type TabItemTitleProps = {
icon: ReactNode;
title: string;
isActive?: boolean;
onClick?: () => void;
};
export const TabItemTitle = ({
icon,
title,
isActive,
onClick,
}: TabItemTitleProps) => {
return (
<Container active={isActive} onClick={onClick}>
<IconWrapper>{icon}</IconWrapper>
<StyledTitleText>{title}</StyledTitleText>
</Container>
);
};
const Container = styled(ListItem)({
width: '110px',
height: '32px',
});
const IconWrapper = styled(ListIcon)({
marginRight: '4px',
});
const StyledTitleText = styled('span')(({ theme }) => {
return {
color: theme.affine.palette.menu,
};
});
@@ -0,0 +1 @@
export { ContainerTabs } from './ContainerTabs';
@@ -0,0 +1,58 @@
import { useRef, useEffect, useState, useMemo } from 'react';
function usePrevious<T>(value: T) {
const ref = useRef<T>();
useEffect(() => {
ref.current = value;
});
return ref.current;
}
export function useTabs<K extends string>(tabs: K[], defaultTab?: K | null) {
const state = useState<K | null>();
const [selectedTab, setSelectedTab] = state;
const activeIndex = useMemo(() => {
if (selectedTab) {
return tabs.indexOf(selectedTab);
}
return -1;
}, [selectedTab, tabs]);
const previousActiveIndex = usePrevious(activeIndex);
useEffect(() => {
if (tabs.length === 0) {
setSelectedTab(undefined);
return;
}
if (
selectedTab === null ||
(selectedTab && tabs.includes(selectedTab))
) {
return;
}
if (
typeof previousActiveIndex === 'number' &&
previousActiveIndex >= 0 &&
(tabs[previousActiveIndex] || tabs[previousActiveIndex - 1])
) {
setSelectedTab(
tabs[previousActiveIndex] || tabs[previousActiveIndex - 1]
);
return;
}
if (defaultTab === null) {
return;
}
setSelectedTab(
defaultTab && tabs.includes(defaultTab) ? defaultTab : tabs[0]
);
}, [defaultTab, previousActiveIndex, selectedTab, setSelectedTab, tabs]);
return state;
}