mirror of
https://github.com/Sonny93/my-links.git
synced 2025-12-09 23:15:36 +00:00
feat: create dedicated settings page instead of creating many modals
This commit is contained in:
@@ -1,11 +1,9 @@
|
||||
import { Avatar, Group, Menu, Text, UnstyledButton } from '@mantine/core';
|
||||
import { useDisclosure } from '@mantine/hooks';
|
||||
import { modals } from '@mantine/modals';
|
||||
import cx from 'clsx';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { TbChevronDown, TbLogout, TbSettings, TbShield } from 'react-icons/tb';
|
||||
import { InternalLinkUnstyled } from '~/components/common/links/internal_link_unstyled';
|
||||
import { UserPreferences } from '~/components/common/user_preferences/user_preferences';
|
||||
import { useAuth } from '~/hooks/use_auth';
|
||||
import classes from './user_dropdown.module.css';
|
||||
|
||||
@@ -15,13 +13,6 @@ export function UserDropdown() {
|
||||
useDisclosure(false);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handlePreferencesModal = () => {
|
||||
modals.open({
|
||||
title: t('user-preferences'),
|
||||
children: <UserPreferences />,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Menu
|
||||
width={260}
|
||||
@@ -64,12 +55,13 @@ export function UserDropdown() {
|
||||
</>
|
||||
)}
|
||||
|
||||
<Menu.Label>{t('common:settings')}</Menu.Label>
|
||||
<Menu.Label>{t('common:user')}</Menu.Label>
|
||||
<Menu.Item
|
||||
leftSection={<TbSettings size={16} />}
|
||||
onClick={handlePreferencesModal}
|
||||
component={InternalLinkUnstyled}
|
||||
href="/user/settings"
|
||||
>
|
||||
{t('common:preferences')}
|
||||
{t('common:settings')}
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
leftSection={<TbLogout size={16} />}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
.list {
|
||||
position: relative;
|
||||
margin-bottom: var(--mantine-spacing-md);
|
||||
}
|
||||
|
||||
.indicator {
|
||||
z-index: -1 !important;
|
||||
background-color: var(--mantine-color-white);
|
||||
border-radius: var(--mantine-radius-md);
|
||||
border: 1px solid var(--mantine-color-gray-2);
|
||||
box-shadow: var(--mantine-shadow-sm);
|
||||
|
||||
@mixin dark {
|
||||
background-color: var(--mantine-color-dark-6);
|
||||
border-color: var(--mantine-color-dark-4);
|
||||
}
|
||||
}
|
||||
|
||||
.tab {
|
||||
z-index: 1;
|
||||
font-weight: 500;
|
||||
transition: color 100ms ease;
|
||||
color: var(--mantine-color-gray-7);
|
||||
|
||||
&[data-active] {
|
||||
color: var(--mantine-color-black);
|
||||
}
|
||||
|
||||
@mixin dark {
|
||||
color: var(--mantine-color-dark-1);
|
||||
|
||||
&[data-active] {
|
||||
color: var(--mantine-color-white);
|
||||
}
|
||||
}
|
||||
}
|
||||
79
inertia/components/common/floating_tabs/floating_tabs.tsx
Normal file
79
inertia/components/common/floating_tabs/floating_tabs.tsx
Normal file
@@ -0,0 +1,79 @@
|
||||
import {
|
||||
FloatingIndicator,
|
||||
Indicator,
|
||||
Tabs as MantineTabs,
|
||||
Stack,
|
||||
} from '@mantine/core';
|
||||
import { useState } from 'react';
|
||||
import classes from './floating_tabs.module.css';
|
||||
|
||||
export type FloatingTab = {
|
||||
value: string;
|
||||
label: string;
|
||||
content: React.ReactNode;
|
||||
disabled?: boolean;
|
||||
indicator?: {
|
||||
content: string;
|
||||
color?: string;
|
||||
pulse?: boolean;
|
||||
disabled?: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
interface FloatingTabsProps {
|
||||
tabs: FloatingTab[];
|
||||
keepMounted?: boolean;
|
||||
}
|
||||
|
||||
export function FloatingTabs({ tabs, keepMounted = false }: FloatingTabsProps) {
|
||||
const [rootRef, setRootRef] = useState<HTMLDivElement | null>(null);
|
||||
const [value, setValue] = useState<string | null>(tabs[0].value);
|
||||
const [controlsRefs, setControlsRefs] = useState<
|
||||
Record<string, HTMLButtonElement | null>
|
||||
>({});
|
||||
const setControlRef = (val: string) => (node: HTMLButtonElement) => {
|
||||
controlsRefs[val] = node;
|
||||
setControlsRefs(controlsRefs);
|
||||
};
|
||||
|
||||
return (
|
||||
<MantineTabs
|
||||
variant="none"
|
||||
value={value}
|
||||
onChange={setValue}
|
||||
keepMounted={keepMounted}
|
||||
>
|
||||
<MantineTabs.List ref={setRootRef} className={classes.list}>
|
||||
{tabs.map((tab) => (
|
||||
<Indicator
|
||||
label={tab.indicator?.content}
|
||||
color={tab.indicator?.color}
|
||||
processing={tab.indicator?.pulse}
|
||||
disabled={!tab.indicator || tab.indicator.disabled}
|
||||
size={16}
|
||||
key={tab.value}
|
||||
>
|
||||
<MantineTabs.Tab
|
||||
value={tab.value}
|
||||
ref={setControlRef(tab.value)}
|
||||
className={classes.tab}
|
||||
disabled={tab.disabled}
|
||||
>
|
||||
{tab.label}
|
||||
</MantineTabs.Tab>
|
||||
</Indicator>
|
||||
))}
|
||||
<FloatingIndicator
|
||||
target={value ? controlsRefs[value] : null}
|
||||
parent={rootRef}
|
||||
className={classes.indicator}
|
||||
/>
|
||||
</MantineTabs.List>
|
||||
{tabs.map((tab) => (
|
||||
<MantineTabs.Panel key={tab.value} value={tab.value}>
|
||||
<Stack>{tab.content}</Stack>
|
||||
</MantineTabs.Panel>
|
||||
))}
|
||||
</MantineTabs>
|
||||
);
|
||||
}
|
||||
@@ -1,15 +1,10 @@
|
||||
import { LinkListDisplay } from '#shared/types/index';
|
||||
import { Fieldset, Stack, Text } from '@mantine/core';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ComboList } from '~/components/common/combo_list/combo_list';
|
||||
import { CollectionListSelector } from '~/components/dashboard/collection/collection_list_selector';
|
||||
import { useDisplayPreferences } from '~/hooks/use_display_preferences';
|
||||
import { LinkListSelector } from '~/components/dashboard/link/link_list_selector';
|
||||
import { useIsMobile } from '~/hooks/use_is_mobile';
|
||||
import { getLinkListDisplayOptions } from '~/lib/display_preferences';
|
||||
|
||||
export function UserPreferences() {
|
||||
const { displayPreferences, handleUpdateDisplayPreferences } =
|
||||
useDisplayPreferences();
|
||||
const { t } = useTranslation();
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
@@ -28,15 +23,7 @@ export function UserPreferences() {
|
||||
<Text size="sm" c="dimmed">
|
||||
{t('display-preferences.link-list-display')}
|
||||
</Text>
|
||||
<ComboList
|
||||
selectedValue={displayPreferences.linkListDisplay}
|
||||
values={getLinkListDisplayOptions()}
|
||||
setValue={(value) =>
|
||||
handleUpdateDisplayPreferences({
|
||||
linkListDisplay: value as LinkListDisplay,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<LinkListSelector />
|
||||
</Stack>
|
||||
</Fieldset>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user