mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-25 04:46:40 +07:00
Добавил тему "Веспера"
This commit is contained in:
parent
5d34090238
commit
aee4b662d5
148 changed files with 1331 additions and 1338 deletions
|
@ -1,55 +0,0 @@
|
|||
import React, { VFC } from 'react';
|
||||
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
|
||||
import { PageCoverProvider } from '~/components/containers/PageCoverProvider';
|
||||
import { Modal } from '~/containers/dialogs/Modal';
|
||||
import { BottomContainer } from '~/containers/main/BottomContainer';
|
||||
import { MainRouter } from '~/containers/main/MainRouter';
|
||||
import { DragDetectorProvider } from '~/hooks/dom/useDragDetector';
|
||||
import { useGlobalLoader } from '~/hooks/dom/useGlobalLoader';
|
||||
import { MainLayout } from '~/layouts/MainLayout';
|
||||
import { Sprites } from '~/sprites/Sprites';
|
||||
import { UserContextProvider } from '~/utils/context/UserContextProvider';
|
||||
import { AudioPlayerProvider } from '~/utils/providers/AudioPlayerProvider';
|
||||
import { AuthProvider } from '~/utils/providers/AuthProvider';
|
||||
import { MetadataProvider } from '~/utils/providers/MetadataProvider';
|
||||
import { SWRConfigProvider } from '~/utils/providers/SWRConfigProvider';
|
||||
import { SearchProvider } from '~/utils/providers/SearchProvider';
|
||||
import { ToastProvider } from '~/utils/providers/ToastProvider';
|
||||
|
||||
const App: VFC = observer(() => {
|
||||
useGlobalLoader();
|
||||
|
||||
return (
|
||||
<BrowserRouter>
|
||||
<SWRConfigProvider>
|
||||
<UserContextProvider>
|
||||
<DragDetectorProvider>
|
||||
<PageCoverProvider>
|
||||
<SearchProvider>
|
||||
<AudioPlayerProvider>
|
||||
<MetadataProvider>
|
||||
<AuthProvider>
|
||||
<MainLayout>
|
||||
<ToastProvider />
|
||||
<Modal />
|
||||
<Sprites />
|
||||
|
||||
<MainRouter />
|
||||
</MainLayout>
|
||||
<BottomContainer />
|
||||
</AuthProvider>
|
||||
</MetadataProvider>
|
||||
</AudioPlayerProvider>
|
||||
</SearchProvider>
|
||||
</PageCoverProvider>
|
||||
</DragDetectorProvider>
|
||||
</UserContextProvider>
|
||||
</SWRConfigProvider>
|
||||
</BrowserRouter>
|
||||
);
|
||||
});
|
||||
|
||||
export { App };
|
|
@ -4,9 +4,10 @@ import { BorisContacts } from '~/components/boris/BorisContacts';
|
|||
import { BorisStats } from '~/components/boris/BorisStats';
|
||||
import { Group } from '~/components/containers/Group';
|
||||
import { SuperPowersToggle } from '~/containers/auth/SuperPowersToggle';
|
||||
import styles from '~/layouts/BorisLayout/styles.module.scss';
|
||||
import { BorisUsageStats } from '~/types/boris';
|
||||
|
||||
import styles from './styles.module.scss';
|
||||
|
||||
interface Props {
|
||||
isUser: boolean;
|
||||
stats: BorisUsageStats;
|
||||
|
@ -14,14 +15,14 @@ interface Props {
|
|||
}
|
||||
|
||||
const BorisSidebar: FC<Props> = ({ isUser, stats, isLoading }) => (
|
||||
<Group className={styles.stats__container}>
|
||||
<Group className={styles.container}>
|
||||
<div className={styles.super_powers}>
|
||||
<SuperPowersToggle />
|
||||
</div>
|
||||
|
||||
<BorisContacts />
|
||||
|
||||
<div className={styles.stats__wrap}>
|
||||
<div className={styles.wrap}>
|
||||
<BorisStats stats={stats} isLoading={isLoading} />
|
||||
</div>
|
||||
</Group>
|
||||
|
|
23
src/containers/boris/BorisSidebar/styles.module.scss
Normal file
23
src/containers/boris/BorisSidebar/styles.module.scss
Normal file
|
@ -0,0 +1,23 @@
|
|||
@import 'src/styles/variables';
|
||||
|
||||
.wrap {
|
||||
@include tablet {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.container {
|
||||
background: $content_bg_dark;
|
||||
border-radius: 0 $radius $radius 0;
|
||||
padding: $gap;
|
||||
box-sizing: border-box;
|
||||
flex: 0 0 auto;
|
||||
|
||||
@include tablet {
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.super_powers {
|
||||
padding: $gap * 2 0;
|
||||
}
|
|
@ -1,10 +1,4 @@
|
|||
@import "src/styles/variables";
|
||||
|
||||
$secondary_color: darken(desaturate($blue, 100%), 30%);
|
||||
$vk_color: $secondary_color;
|
||||
|
||||
.dialog {
|
||||
}
|
||||
@import 'src/styles/variables';
|
||||
|
||||
.wrap {
|
||||
display: flex;
|
||||
|
@ -17,32 +11,6 @@ $vk_color: $secondary_color;
|
|||
}
|
||||
}
|
||||
|
||||
.secondary_button {
|
||||
background: $content_bg;
|
||||
box-shadow: inset $vk_color 0 0 0 2px;
|
||||
color: $vk_color;
|
||||
|
||||
svg {
|
||||
fill: $vk_color;
|
||||
margin-right: $gap;
|
||||
}
|
||||
}
|
||||
|
||||
.forgot_button {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
margin: $gap * 2 0 0 0 !important;
|
||||
padding: $gap * 2 0 0 0;
|
||||
border-top: 1px solid black;
|
||||
}
|
||||
|
||||
.links {
|
||||
font: $font_14_regular;
|
||||
text-align: center;
|
||||
|
||||
a {
|
||||
color: lighten($content_bg, 40%);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@import "src/styles/variables";
|
||||
@import 'src/styles/variables';
|
||||
|
||||
.wrap {
|
||||
padding: $login_dialog_padding;
|
||||
|
@ -13,6 +13,6 @@
|
|||
|
||||
span {
|
||||
padding-left: $gap;
|
||||
color: darken(white, 50%);
|
||||
color: $gray_50;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@import "src/styles/variables";
|
||||
@import 'src/styles/variables';
|
||||
|
||||
.wrap {
|
||||
padding: $gap $gap $gap * 4;
|
||||
|
@ -11,7 +11,7 @@
|
|||
.text {
|
||||
font: $font_14_regular;
|
||||
padding: $gap;
|
||||
color: darken(white, 50%);
|
||||
color: $gray_50;
|
||||
}
|
||||
|
||||
.shade,
|
||||
|
@ -34,18 +34,18 @@
|
|||
text-transform: uppercase;
|
||||
font: $font_18_semibold;
|
||||
text-align: center;
|
||||
color: $wisegreen;
|
||||
color: $color_primary;
|
||||
|
||||
svg {
|
||||
fill: $wisegreen;
|
||||
fill: $color_primary;
|
||||
}
|
||||
}
|
||||
|
||||
.error_shade {
|
||||
color: $red;
|
||||
color: $color_danger;
|
||||
|
||||
svg {
|
||||
fill: $red;
|
||||
fill: $color_danger;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,24 +15,24 @@ import styles from './styles.module.scss';
|
|||
|
||||
interface RestoreRequestDialogProps extends DialogComponentProps {}
|
||||
|
||||
const RestoreRequestDialog: VFC<RestoreRequestDialogProps> = ({ onRequestClose }) => {
|
||||
const RestoreRequestDialog: VFC<RestoreRequestDialogProps> = ({
|
||||
onRequestClose,
|
||||
}) => {
|
||||
useCloseOnEscape(onRequestClose);
|
||||
|
||||
const [isSent, setIsSent] = useState(false);
|
||||
const onSent = useCallback(() => setIsSent(true), [setIsSent]);
|
||||
|
||||
const { isSubmitting, handleSubmit, handleChange, errors, values } = useRestoreRequestForm(
|
||||
apiRequestRestoreCode,
|
||||
onSent
|
||||
);
|
||||
const { isSubmitting, handleSubmit, handleChange, errors, values } =
|
||||
useRestoreRequestForm(apiRequestRestoreCode, onSent);
|
||||
|
||||
const buttons = useMemo(
|
||||
() => (
|
||||
<Group className={styles.buttons}>
|
||||
<Button color="secondary">Восстановить</Button>
|
||||
<Button>Восстановить</Button>
|
||||
</Group>
|
||||
),
|
||||
[]
|
||||
[],
|
||||
);
|
||||
|
||||
const header = useMemo(() => <div className={styles.illustration} />, []);
|
||||
|
@ -58,7 +58,8 @@ const RestoreRequestDialog: VFC<RestoreRequestDialogProps> = ({ onRequestClose }
|
|||
/>
|
||||
|
||||
<div className={styles.text}>
|
||||
Введите имя пользователя или адрес почты. Мы пришлем ссылку для сброса пароля.
|
||||
Введите имя пользователя или адрес почты. Мы пришлем ссылку для
|
||||
сброса пароля.
|
||||
</div>
|
||||
</Group>
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@import "src/styles/variables";
|
||||
@import 'src/styles/variables';
|
||||
|
||||
.wrap {
|
||||
padding: $gap $gap $gap * 4;
|
||||
|
@ -11,7 +11,7 @@
|
|||
.text {
|
||||
font: $font_14_regular;
|
||||
padding: $gap;
|
||||
color: darken(white, 50%);
|
||||
color: $gray_50;
|
||||
}
|
||||
|
||||
.illustration {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@import "../../../styles/variables";
|
||||
@import '../../../styles/variables';
|
||||
|
||||
.wrap {
|
||||
display: flex;
|
||||
|
@ -25,7 +25,7 @@
|
|||
left: 0;
|
||||
height: 60px;
|
||||
width: 100%;
|
||||
background: linear-gradient(transparentize($content_bg, 1), $content_bg 90%);
|
||||
background: linear-gradient(transparent, $content_bg 90%);
|
||||
pointer-events: none;
|
||||
touch-action: none;
|
||||
|
||||
|
@ -73,7 +73,7 @@
|
|||
.search {
|
||||
@include outer_shadow();
|
||||
|
||||
background: lighten($content_bg, 3%);
|
||||
background: $content_bg_lighter;
|
||||
padding: $gap;
|
||||
border-radius: $radius;
|
||||
}
|
||||
|
@ -91,7 +91,6 @@
|
|||
}
|
||||
|
||||
.toggles {
|
||||
|
||||
& > div {
|
||||
padding: $gap;
|
||||
font: $font_14_semibold;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@import "src/styles/variables.scss";
|
||||
@import 'src/styles/variables.scss';
|
||||
|
||||
.title {
|
||||
padding-bottom: $gap;
|
||||
|
@ -16,7 +16,7 @@
|
|||
@include outer_shadow;
|
||||
|
||||
border-radius: $radius;
|
||||
background: $comment_bg;
|
||||
background: $content_bg_light;
|
||||
padding: $gap;
|
||||
}
|
||||
|
||||
|
|
|
@ -52,9 +52,10 @@ const Header: FC<HeaderProps> = observer(() => {
|
|||
[borisCommentedAt, isUser, user.last_seen_boris],
|
||||
);
|
||||
|
||||
const hasLabUpdates = useMemo(() => labStats.updates.length > 0, [
|
||||
labStats.updates,
|
||||
]);
|
||||
const hasLabUpdates = useMemo(
|
||||
() => labStats.updates.length > 0,
|
||||
[labStats.updates],
|
||||
);
|
||||
const hasFlowUpdates = useMemo(() => flowUpdates.length > 0, [flowUpdates]);
|
||||
|
||||
// Needed for SSR
|
||||
|
@ -113,12 +114,7 @@ const Header: FC<HeaderProps> = observer(() => {
|
|||
)}
|
||||
|
||||
{!isUser && (
|
||||
<Button
|
||||
className={styles.user_button}
|
||||
onClick={onLogin}
|
||||
round
|
||||
color="secondary"
|
||||
>
|
||||
<Button className={styles.user_button} onClick={onLogin} round>
|
||||
ВДОХ
|
||||
</Button>
|
||||
)}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@import "../../../styles/variables";
|
||||
@import '../../../styles/variables';
|
||||
|
||||
@keyframes appear {
|
||||
from {
|
||||
|
@ -28,7 +28,7 @@
|
|||
}
|
||||
|
||||
&.is_scrolled {
|
||||
@include blur();
|
||||
@include blur;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -86,7 +86,7 @@
|
|||
white-space: nowrap;
|
||||
|
||||
&:hover {
|
||||
color: $red;
|
||||
color: $color_link;
|
||||
}
|
||||
|
||||
&::before {
|
||||
|
@ -109,7 +109,7 @@
|
|||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 4px;
|
||||
background: lighten($red, 10%);
|
||||
background: $color_link;
|
||||
right: 12px;
|
||||
top: 6px;
|
||||
transition: opacity 0.5s;
|
||||
|
@ -123,11 +123,11 @@
|
|||
}
|
||||
|
||||
&.lab::after {
|
||||
background: lighten($blue, 10%);
|
||||
background: $color_lab;
|
||||
}
|
||||
|
||||
&.boris::after {
|
||||
background: lighten($wisegreen, 10%);
|
||||
background: $color_primary;
|
||||
}
|
||||
|
||||
@include tablet {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@import "src/styles/variables.scss";
|
||||
@import 'src/styles/variables.scss';
|
||||
|
||||
.sticky {
|
||||
width: 100%;
|
||||
|
@ -22,7 +22,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
.panel {
|
||||
flex: 1 3;
|
||||
display: flex;
|
||||
|
@ -38,13 +37,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.buttons {
|
||||
background: $node_buttons_bg;
|
||||
flex: 1;
|
||||
border-radius: $panel_radius;
|
||||
box-shadow: $comment_shadow;
|
||||
}
|
||||
|
||||
.left {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
@import "../../../styles/variables";
|
||||
@import '../../../styles/variables';
|
||||
|
||||
.wrap {
|
||||
}
|
||||
|
||||
.list {
|
||||
border-radius: $radius;
|
||||
background: transparentize(white, 0.95);
|
||||
background: $gray_90;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
|
@ -38,7 +38,7 @@
|
|||
grid-template-columns: 20px auto 20px;
|
||||
grid-column-gap: $gap * 1.5;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid transparentize(white, 0.9);
|
||||
border-bottom: 1px solid $gray_90;
|
||||
padding: $gap;
|
||||
|
||||
&:last-child {
|
||||
|
@ -70,7 +70,7 @@
|
|||
cursor: pointer;
|
||||
opacity: 0.5;
|
||||
transition: opacity 0.25s;
|
||||
fill: $red;
|
||||
fill: $color_danger;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@import "src/styles/variables";
|
||||
@import 'src/styles/variables';
|
||||
|
||||
.wrap {
|
||||
justify-content: flex-start;
|
||||
|
@ -25,6 +25,6 @@
|
|||
}
|
||||
|
||||
.description {
|
||||
color: transparentize($color: white, $amount: 0.7);
|
||||
color: $gray_25;
|
||||
font: $font_14_regular;
|
||||
}
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
import React, { FC } from 'react';
|
||||
|
||||
import { Anchor } from '~/components/common/Anchor';
|
||||
import { NodeNoComments } from '~/components/node/NodeNoComments';
|
||||
import { Message } from '~/components/profile/Message';
|
||||
import { useUser } from '~/hooks/auth/useUser';
|
||||
import { useMessages } from '~/hooks/messages/useMessages';
|
||||
import { useProfileContext } from '~/utils/providers/ProfileProvider';
|
||||
|
||||
import styles from './styles.module.scss';
|
||||
|
||||
const ProfileMessages: FC = () => {
|
||||
const { profile, isLoading: isLoadingProfile } = useProfileContext();
|
||||
const { user } = useUser();
|
||||
const { messages, isLoading: isLoadingMessages } = useMessages(profile?.username || '');
|
||||
|
||||
if (!messages.length || isLoadingProfile)
|
||||
return <NodeNoComments is_loading={isLoadingMessages || isLoadingProfile} />;
|
||||
|
||||
if (messages.length <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.messages}>
|
||||
<div className={styles.warning}>
|
||||
<p>В будущем мы собираемся убрать сообщения, превратив их в заметки.</p>
|
||||
|
||||
<p>
|
||||
Вся твоя история сообщений, написанных себе, сохранится. Исчезнут только сообщения другим
|
||||
участникам.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Давай обсудим это в <Anchor href="/boris">Борисе</Anchor>, если это так важно.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{messages
|
||||
.filter(message => !!message.text)
|
||||
.map((
|
||||
message // TODO: show files / memo
|
||||
) => (
|
||||
<Message message={message} incoming={user.id !== message.from.id} key={message.id} />
|
||||
))}
|
||||
|
||||
{!isLoadingMessages && messages.length > 0 && (
|
||||
<div className={styles.placeholder}>Когда-нибудь здесь будут еще сообщения</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { ProfileMessages };
|
|
@ -1,43 +0,0 @@
|
|||
@import "src/styles/variables";
|
||||
|
||||
.messages {
|
||||
padding: $gap;
|
||||
background: $node_bg;
|
||||
display: flex;
|
||||
flex-direction: column !important;
|
||||
overflow: auto;
|
||||
|
||||
& > * {
|
||||
margin: $gap * 0.5 0;
|
||||
|
||||
&:last-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
font: $font_12_regular;
|
||||
padding: $gap;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div.warning.warning {
|
||||
padding: $gap;
|
||||
box-shadow: $red 0 0 0 2px;
|
||||
border-radius: $radius;
|
||||
font: $font_14_semibold;
|
||||
margin-bottom: $gap * 2;
|
||||
|
||||
p {
|
||||
margin-bottom: $gap;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
@import "src/styles/variables";
|
||||
@import 'src/styles/variables';
|
||||
|
||||
.wrap {
|
||||
@include outer_shadow;
|
||||
|
@ -9,7 +9,7 @@
|
|||
align-items: stretch;
|
||||
justify-content: stretch;
|
||||
flex-direction: column;
|
||||
background: $comment_bg;
|
||||
background: $content_bg_light;
|
||||
height: 100%;
|
||||
border-radius: $radius;
|
||||
}
|
||||
|
@ -39,7 +39,7 @@
|
|||
font: $font_14_regular;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
color: transparentize(white, 0.5);
|
||||
color: $gray_50;
|
||||
}
|
||||
|
||||
.description {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@import "src/styles/variables";
|
||||
@import 'src/styles/variables';
|
||||
|
||||
.wrapper {
|
||||
@include outer_shadow;
|
||||
|
@ -35,10 +35,10 @@ div.top.top {
|
|||
.status {
|
||||
font: $font_12_regular;
|
||||
margin-top: $gap;
|
||||
color: darken(white, 50%);
|
||||
color: $gray_50;
|
||||
|
||||
&.active {
|
||||
color: $olive;
|
||||
color: $color_online;
|
||||
}
|
||||
|
||||
&::before {
|
||||
|
|
|
@ -4,13 +4,12 @@ import classNames from 'classnames';
|
|||
|
||||
import { Filler } from '~/components/containers/Filler';
|
||||
import { Group } from '~/components/containers/Group';
|
||||
import { Button } from '~/components/input/Button';
|
||||
import { Icon } from '~/components/input/Icon';
|
||||
import { MenuButton, MenuItemWithIcon } from '~/components/menu';
|
||||
import { Zone } from '~/components/containers/Zone';
|
||||
import { VerticalMenu } from '~/components/menu/VerticalMenu';
|
||||
import { useStackContext } from '~/components/sidebar/SidebarStack';
|
||||
import { ProfileSidebarHead } from '~/containers/profile/ProfileSidebarHead';
|
||||
import { ProfileStats } from '~/containers/profile/ProfileStats';
|
||||
import { ThemeSwitcher } from '~/containers/settings/ThemeSwitcher';
|
||||
import { useAuth } from '~/hooks/auth/useAuth';
|
||||
import markdown from '~/styles/common/markdown.module.scss';
|
||||
|
||||
|
@ -50,9 +49,15 @@ const ProfileSidebarMenu: VFC<ProfileSidebarMenuProps> = ({ onClose }) => {
|
|||
</VerticalMenu.Item>
|
||||
</VerticalMenu>
|
||||
|
||||
<div className={styles.toggles}>
|
||||
<ProfileToggles />
|
||||
</div>
|
||||
<Group className={styles.toggles}>
|
||||
<Zone>
|
||||
<ProfileToggles />
|
||||
</Zone>
|
||||
|
||||
<Zone>
|
||||
<ThemeSwitcher />
|
||||
</Zone>
|
||||
</Group>
|
||||
|
||||
<div className={styles.stats}>
|
||||
<ProfileStats />
|
||||
|
|
|
@ -7,11 +7,9 @@ import { SuperPowersToggle } from '~/containers/auth/SuperPowersToggle';
|
|||
interface ProfileTogglesProps {}
|
||||
|
||||
const ProfileToggles: FC<ProfileTogglesProps> = () => (
|
||||
<Zone>
|
||||
<Group>
|
||||
<SuperPowersToggle />
|
||||
</Group>
|
||||
</Zone>
|
||||
<Group>
|
||||
<SuperPowersToggle />
|
||||
</Group>
|
||||
);
|
||||
|
||||
export { ProfileToggles };
|
||||
|
|
|
@ -25,7 +25,7 @@ const List = () => {
|
|||
|
||||
return (
|
||||
<>
|
||||
{notes.map(note => (
|
||||
{notes.map((note) => (
|
||||
<NoteCard
|
||||
remove={() => onRemove(note.id)}
|
||||
update={(text, callback) => update(note.id, text, callback)}
|
||||
|
@ -59,7 +59,6 @@ const SettingsNotes: VFC<SettingsNotesProps> = () => {
|
|||
onClick={() => setFormIsShown(true)}
|
||||
size="mini"
|
||||
iconRight="plus"
|
||||
color="secondary"
|
||||
>
|
||||
Добавить
|
||||
</Button>
|
||||
|
|
47
src/containers/settings/ThemeSwitcher/index.tsx
Normal file
47
src/containers/settings/ThemeSwitcher/index.tsx
Normal file
|
@ -0,0 +1,47 @@
|
|||
import React, { FC } from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { Card } from '~/components/containers/Card';
|
||||
import { Group } from '~/components/containers/Group';
|
||||
import { Theme, themeColors } from '~/constants/themes';
|
||||
import { useTheme } from '~/utils/providers/ThemeProvider';
|
||||
|
||||
import styles from './styles.module.scss';
|
||||
|
||||
interface ThemeSwitcherProps {}
|
||||
|
||||
const ThemeSwitcher: FC<ThemeSwitcherProps> = () => {
|
||||
const { theme, setTheme } = useTheme();
|
||||
|
||||
return (
|
||||
<Group horizontal>
|
||||
{Object.entries(themeColors).map(([id, item]) => (
|
||||
<Card
|
||||
key={id}
|
||||
className={classNames(styles.card, {
|
||||
[styles.active]: theme === id,
|
||||
})}
|
||||
style={{ background: item.background }}
|
||||
role="button"
|
||||
onClick={() => setTheme(id as Theme)}
|
||||
>
|
||||
<Group>
|
||||
<Group horizontal>
|
||||
{item.colors.map((color) => (
|
||||
<div
|
||||
key={color}
|
||||
className={styles.sample}
|
||||
style={{ background: color }}
|
||||
/>
|
||||
))}
|
||||
</Group>
|
||||
<div className={styles.title}>{item.name}</div>
|
||||
</Group>
|
||||
</Card>
|
||||
))}
|
||||
</Group>
|
||||
);
|
||||
};
|
||||
|
||||
export { ThemeSwitcher };
|
30
src/containers/settings/ThemeSwitcher/styles.module.scss
Normal file
30
src/containers/settings/ThemeSwitcher/styles.module.scss
Normal file
|
@ -0,0 +1,30 @@
|
|||
@import 'src/styles/variables';
|
||||
|
||||
.button {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: $gap;
|
||||
flex: 1;
|
||||
cursor: pointer;
|
||||
|
||||
&.active {
|
||||
outline: 1px solid $color_primary;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
font: $font_12_semibold;
|
||||
text-align: left;
|
||||
text-transform: uppercase;
|
||||
color: $gray_50;
|
||||
}
|
||||
|
||||
.sample {
|
||||
@include outer_shadow;
|
||||
|
||||
border-radius: 100%;
|
||||
flex: 0 1 20px;
|
||||
height: 20px;
|
||||
}
|
|
@ -1,7 +1,4 @@
|
|||
@import "src/styles/variables";
|
||||
|
||||
$pad_danger: mix($red, $content_bg, 70%);
|
||||
$pad_usual: mix(white, $content_bg, 10%);
|
||||
@import 'src/styles/variables';
|
||||
|
||||
.about {
|
||||
flex: 4;
|
||||
|
@ -17,9 +14,6 @@ $pad_usual: mix(white, $content_bg, 10%);
|
|||
padding: 0 $gap 0;
|
||||
}
|
||||
|
||||
.grid {
|
||||
}
|
||||
|
||||
div.base_info.base_info {
|
||||
align-items: stretch;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@import "src/styles/variables";
|
||||
@import 'src/styles/variables';
|
||||
|
||||
.wrap {
|
||||
@include sidebar_content(400px);
|
||||
|
@ -56,7 +56,7 @@
|
|||
padding-left: $gap * 2;
|
||||
|
||||
svg {
|
||||
fill: transparentize(white, 0.5);
|
||||
fill: $gray_50;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -65,28 +65,20 @@
|
|||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
background: darken($content_bg, 2%);
|
||||
background: $content_bg_dark;
|
||||
height: 2px;
|
||||
z-index: 2;
|
||||
pointer-events: none;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
.bar {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
background: lighten($content_bg, 20%);
|
||||
}
|
||||
|
||||
.none {
|
||||
padding: 40px $gap;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: transparentize(white, 0.9);
|
||||
color: $gray_90;
|
||||
fill: currentColor;
|
||||
font: $font_18_semibold;
|
||||
stroke: none;
|
||||
|
|
|
@ -19,13 +19,8 @@ const placeholder = 'Добавить';
|
|||
const prepareInput = (input: string): string[] => {
|
||||
return input
|
||||
.split(',')
|
||||
.map((title: string) =>
|
||||
title
|
||||
.trim()
|
||||
.substring(0, 64)
|
||||
.toLowerCase(),
|
||||
)
|
||||
.filter(el => el.length > 0);
|
||||
.map((title: string) => title.trim().substring(0, 64).toLowerCase())
|
||||
.filter((el) => el.length > 0);
|
||||
};
|
||||
|
||||
interface IProps {
|
||||
|
@ -88,7 +83,7 @@ const TagInput: FC<IProps> = ({ exclude, onAppend, onClearTag, onSubmit }) => {
|
|||
|
||||
const onFocus = useCallback(() => setFocused(true), []);
|
||||
const onBlur = useCallback(
|
||||
event => {
|
||||
(event) => {
|
||||
if (!wrapper.current || !ref.current) {
|
||||
return;
|
||||
}
|
||||
|
@ -122,9 +117,7 @@ const TagInput: FC<IProps> = ({ exclude, onAppend, onClearTag, onSubmit }) => {
|
|||
[onAppend, setInput],
|
||||
);
|
||||
|
||||
const feature = useMemo(() => (input?.substr(0, 1) === '/' ? 'green' : ''), [
|
||||
input,
|
||||
]);
|
||||
const isAlbumTag = useMemo(() => input?.substr(0, 1) === '/', [input]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!focused) return;
|
||||
|
@ -138,7 +131,7 @@ const TagInput: FC<IProps> = ({ exclude, onAppend, onClearTag, onSubmit }) => {
|
|||
<TagWrapper
|
||||
title={input || placeholder}
|
||||
hasInput={true}
|
||||
feature={feature}
|
||||
color={isAlbumTag ? 'primary' : undefined}
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue