fixes
This commit is contained in:
@@ -4,11 +4,20 @@ set -euo pipefail
|
||||
API_PROXY_PASS_TRIMMED="${API_PROXY_PASS:-}"
|
||||
API_PROXY_PASS_TRIMMED="${API_PROXY_PASS_TRIMMED%%/}"
|
||||
|
||||
cat <<'BASE' > /etc/nginx/conf.d/default.conf
|
||||
MAX_BODY_SIZE_RAW="${UPLOAD_BODY_LIMIT_BYTES:-}"
|
||||
if [ -n "$MAX_BODY_SIZE_RAW" ]; then
|
||||
MAX_BODY_SIZE=$(printf '%sm' "$((MAX_BODY_SIZE_RAW / (1024 * 1024)))")
|
||||
else
|
||||
MAX_BODY_SIZE="128m"
|
||||
fi
|
||||
|
||||
cat <<BASE > /etc/nginx/conf.d/default.conf
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
|
||||
client_max_body_size ${MAX_BODY_SIZE};
|
||||
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
@@ -21,6 +30,7 @@ if [ -n "$API_PROXY_PASS_TRIMMED" ]; then
|
||||
cat <<PROXY >> /etc/nginx/conf.d/default.conf
|
||||
|
||||
location /api/ {
|
||||
client_max_body_size ${MAX_BODY_SIZE};
|
||||
proxy_pass ${API_PROXY_PASS_TRIMMED};
|
||||
proxy_set_header Host \$host;
|
||||
proxy_set_header X-Real-IP \$remote_addr;
|
||||
@@ -29,6 +39,7 @@ cat <<PROXY >> /etc/nginx/conf.d/default.conf
|
||||
}
|
||||
|
||||
location /download/ {
|
||||
client_max_body_size ${MAX_BODY_SIZE};
|
||||
proxy_pass ${API_PROXY_PASS_TRIMMED};
|
||||
proxy_set_header Host \$host;
|
||||
proxy_set_header X-Real-IP \$remote_addr;
|
||||
|
||||
@@ -28,6 +28,7 @@ const DEFAULT_FOLDER_NAME = 'All Documents';
|
||||
const ROW_KEY_SEPARATOR = ':';
|
||||
const DOCUMENT_ROW_PREFIX = 'document';
|
||||
const FOLDER_ROW_PREFIX = 'folder';
|
||||
const THEME_STORAGE_KEY = 'papercrate_theme_settings';
|
||||
|
||||
const resolveApiPath = (path = '') => path;
|
||||
|
||||
@@ -197,22 +198,66 @@ const AppLayout = () => {
|
||||
const stored = window.localStorage.getItem('papercrate_view_mode');
|
||||
return stored === 'grid' ? 'grid' : 'list';
|
||||
});
|
||||
const [neutralHue, setNeutralHue] = useState(() => {
|
||||
|
||||
const loadThemeSettings = () => {
|
||||
const defaults = { neutralHue: 260, mode: 'system' };
|
||||
if (typeof window === 'undefined') {
|
||||
return 260;
|
||||
if (typeof document !== 'undefined') {
|
||||
const current = document.documentElement.style.getPropertyValue('--neutral-hue');
|
||||
const parsed = Number.parseInt(current, 10);
|
||||
return {
|
||||
neutralHue: Number.isNaN(parsed) ? defaults.neutralHue : parsed,
|
||||
mode: defaults.mode,
|
||||
};
|
||||
}
|
||||
return defaults;
|
||||
}
|
||||
const stored = window.localStorage.getItem('papercrate_neutral_hue');
|
||||
if (stored) {
|
||||
const parsedStored = Number.parseInt(stored, 10);
|
||||
if (!Number.isNaN(parsedStored)) {
|
||||
return parsedStored;
|
||||
|
||||
const root = document.documentElement;
|
||||
const loadFromRoot = () => {
|
||||
const current = root.style.getPropertyValue('--neutral-hue');
|
||||
const parsed = Number.parseInt(current, 10);
|
||||
return Number.isNaN(parsed) ? defaults.neutralHue : parsed;
|
||||
};
|
||||
|
||||
let neutralHueValue = loadFromRoot();
|
||||
let modeValue = defaults.mode;
|
||||
|
||||
const composite = window.localStorage.getItem(THEME_STORAGE_KEY);
|
||||
if (composite) {
|
||||
try {
|
||||
const parsed = JSON.parse(composite);
|
||||
const storedHue = Number.parseInt(parsed?.neutralHue, 10);
|
||||
if (!Number.isNaN(storedHue)) {
|
||||
neutralHueValue = storedHue;
|
||||
}
|
||||
const storedMode = parsed?.mode;
|
||||
if (storedMode === 'light' || storedMode === 'dark' || storedMode === 'system') {
|
||||
modeValue = storedMode;
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('[theme] failed to parse stored theme settings', error);
|
||||
}
|
||||
} else {
|
||||
const legacyHue = window.localStorage.getItem('papercrate_neutral_hue');
|
||||
if (legacyHue) {
|
||||
const parsedLegacyHue = Number.parseInt(legacyHue, 10);
|
||||
if (!Number.isNaN(parsedLegacyHue)) {
|
||||
neutralHueValue = parsedLegacyHue;
|
||||
}
|
||||
}
|
||||
const legacyMode = window.localStorage.getItem('papercrate_theme_mode');
|
||||
if (legacyMode === 'light' || legacyMode === 'dark' || legacyMode === 'system') {
|
||||
modeValue = legacyMode;
|
||||
}
|
||||
}
|
||||
const root = document.documentElement;
|
||||
const current = root.style.getPropertyValue('--neutral-hue');
|
||||
const parsed = Number.parseInt(current, 10);
|
||||
return Number.isNaN(parsed) ? 260 : parsed;
|
||||
});
|
||||
|
||||
return { neutralHue: neutralHueValue, mode: modeValue };
|
||||
};
|
||||
|
||||
const initialThemeSettings = loadThemeSettings();
|
||||
const [neutralHue, setNeutralHue] = useState(initialThemeSettings.neutralHue);
|
||||
const [themeMode, setThemeMode] = useState(initialThemeSettings.mode);
|
||||
const initialRowSelection = [];
|
||||
const [selectedRowKeys, setSelectedRowKeys] = useState(initialRowSelection);
|
||||
const [selectionOrder, setSelectionOrder] = useState(initialRowSelection);
|
||||
@@ -314,11 +359,33 @@ const AppLayout = () => {
|
||||
if (typeof document !== 'undefined') {
|
||||
document.documentElement.style.setProperty('--neutral-hue', `${neutralHue}deg`);
|
||||
}
|
||||
if (typeof window !== 'undefined') {
|
||||
window.localStorage.setItem('papercrate_neutral_hue', String(neutralHue));
|
||||
}
|
||||
}, [neutralHue]);
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof document !== 'undefined') {
|
||||
const root = document.documentElement;
|
||||
if (themeMode === 'system') {
|
||||
root.removeAttribute('data-theme');
|
||||
} else {
|
||||
root.setAttribute('data-theme', themeMode);
|
||||
}
|
||||
}
|
||||
}, [themeMode]);
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window === 'undefined') {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const payload = JSON.stringify({ neutralHue, mode: themeMode });
|
||||
window.localStorage.setItem(THEME_STORAGE_KEY, payload);
|
||||
window.localStorage.removeItem('papercrate_neutral_hue');
|
||||
window.localStorage.removeItem('papercrate_theme_mode');
|
||||
} catch (error) {
|
||||
console.warn('[theme] failed to persist theme settings', error);
|
||||
}
|
||||
}, [neutralHue, themeMode]);
|
||||
|
||||
const handleNeutralHueChange = useCallback((value) => {
|
||||
if (value === '') {
|
||||
setNeutralHue(260);
|
||||
@@ -331,6 +398,13 @@ const AppLayout = () => {
|
||||
setNeutralHue(parsed);
|
||||
}, []);
|
||||
|
||||
const handleThemeModeChange = useCallback((mode) => {
|
||||
if (mode !== 'light' && mode !== 'dark' && mode !== 'system') {
|
||||
return;
|
||||
}
|
||||
setThemeMode(mode);
|
||||
}, []);
|
||||
|
||||
const clearFilters = useCallback(() => {
|
||||
setSearchQuery('');
|
||||
setActiveTagFilters([]);
|
||||
@@ -4764,6 +4838,8 @@ const AppLayout = () => {
|
||||
creatingFolder,
|
||||
onNeutralHueChange: handleNeutralHueChange,
|
||||
neutralHue,
|
||||
themeMode,
|
||||
onThemeModeChange: handleThemeModeChange,
|
||||
tags,
|
||||
activeTagIds: activeTagFilters,
|
||||
onToggleTagFilter: toggleTagFilter,
|
||||
@@ -4824,6 +4900,8 @@ const AppLayout = () => {
|
||||
creatingFolder,
|
||||
handleNeutralHueChange,
|
||||
neutralHue,
|
||||
themeMode,
|
||||
handleThemeModeChange,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState, useId } from 'react';
|
||||
import {
|
||||
ChevronIcon,
|
||||
TrashIcon,
|
||||
@@ -12,6 +12,9 @@ import {
|
||||
PlusIcon,
|
||||
FolderPlusIcon,
|
||||
RestoreIcon,
|
||||
SunIcon,
|
||||
MoonIcon,
|
||||
DesktopIcon,
|
||||
} from '../ui/icons';
|
||||
import PanelHeader from '../ui/PanelHeader';
|
||||
|
||||
@@ -129,6 +132,13 @@ const FolderNode = ({
|
||||
);
|
||||
};
|
||||
|
||||
const THEME_MODES = ['system', 'light', 'dark'];
|
||||
const THEME_MODE_LABELS = {
|
||||
system: 'System',
|
||||
light: 'Light',
|
||||
dark: 'Dark',
|
||||
};
|
||||
|
||||
const Sidebar = ({
|
||||
folderNodes,
|
||||
onToggle,
|
||||
@@ -146,6 +156,8 @@ const Sidebar = ({
|
||||
creatingFolder = false,
|
||||
onNeutralHueChange,
|
||||
neutralHue,
|
||||
themeMode = 'system',
|
||||
onThemeModeChange,
|
||||
tags = [],
|
||||
activeTagIds = [],
|
||||
onToggleTagFilter,
|
||||
@@ -170,6 +182,7 @@ const Sidebar = ({
|
||||
onSelectTenant,
|
||||
onOpenSettings,
|
||||
}) => {
|
||||
const neutralHueInputId = useId();
|
||||
const sortedCorrespondents = useMemo(() => {
|
||||
if (!Array.isArray(correspondents)) {
|
||||
return [];
|
||||
@@ -230,6 +243,28 @@ const Sidebar = ({
|
||||
onNeutralHueChange?.('');
|
||||
}, [onNeutralHueChange]);
|
||||
|
||||
const themeModeIndex = THEME_MODES.indexOf(themeMode);
|
||||
const safeThemeModeIndex = themeModeIndex === -1 ? 0 : themeModeIndex;
|
||||
const resolvedThemeMode = THEME_MODES[safeThemeModeIndex];
|
||||
const nextThemeMode = THEME_MODES[(safeThemeModeIndex + 1) % THEME_MODES.length];
|
||||
const themeModeLabel = THEME_MODE_LABELS[resolvedThemeMode];
|
||||
const nextThemeLabel = THEME_MODE_LABELS[nextThemeMode];
|
||||
const themeModeIcon = resolvedThemeMode === 'dark'
|
||||
? <MoonIcon size={16} />
|
||||
: resolvedThemeMode === 'light'
|
||||
? <SunIcon size={16} />
|
||||
: <DesktopIcon size={16} />;
|
||||
|
||||
const handleThemeModeToggle = useCallback(() => {
|
||||
if (!onThemeModeChange) {
|
||||
return;
|
||||
}
|
||||
const currentIndex = THEME_MODES.indexOf(themeMode);
|
||||
const safeIndex = currentIndex === -1 ? 0 : currentIndex;
|
||||
const nextMode = THEME_MODES[(safeIndex + 1) % THEME_MODES.length];
|
||||
onThemeModeChange(nextMode);
|
||||
}, [onThemeModeChange, themeMode]);
|
||||
|
||||
const handleSearchInputChange = useCallback(
|
||||
(event) => {
|
||||
onSearchChange?.(event.target.value);
|
||||
@@ -350,6 +385,50 @@ const Sidebar = ({
|
||||
);
|
||||
|
||||
const rootNode = folderNodes.get('root');
|
||||
const themeControls =
|
||||
typeof neutralHue === 'number' || typeof neutralHue === 'string'
|
||||
? (
|
||||
<div className="sidebar-section sidebar-theme">
|
||||
<div className="sidebar-section__header">
|
||||
<h3>Theme</h3>
|
||||
<div className="sidebar-section__actions">
|
||||
<button
|
||||
type="button"
|
||||
className="icon-button"
|
||||
onClick={handleThemeModeToggle}
|
||||
aria-label={`Switch theme (next: ${nextThemeLabel})`}
|
||||
title={`Theme: ${themeModeLabel} (next: ${nextThemeLabel})`}
|
||||
>
|
||||
{themeModeIcon}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="icon-button"
|
||||
onClick={handleNeutralHueReset}
|
||||
aria-label="Reset neutral hue"
|
||||
title="Reset neutral hue"
|
||||
>
|
||||
<RestoreIcon size={16} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<label className="sidebar-slider" htmlFor={neutralHueInputId}>
|
||||
<span className="sidebar-slider__label">Hue</span>
|
||||
<input
|
||||
id={neutralHueInputId}
|
||||
type="range"
|
||||
min="0"
|
||||
max="360"
|
||||
step="1"
|
||||
value={neutralHue}
|
||||
onChange={(event) => onNeutralHueChange?.(event.target.value)}
|
||||
/>
|
||||
<span className="sidebar-slider__value">{neutralHue}°</span>
|
||||
</label>
|
||||
</div>
|
||||
)
|
||||
: null;
|
||||
|
||||
return (
|
||||
<aside className="sidebar">
|
||||
<PanelHeader
|
||||
@@ -606,36 +685,8 @@ const Sidebar = ({
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
{typeof neutralHue === 'number' || typeof neutralHue === 'string' ? (
|
||||
<div className="sidebar-section">
|
||||
<div className="sidebar-section__header">
|
||||
<h3>Theme</h3>
|
||||
<div className="sidebar-section__actions">
|
||||
<button
|
||||
type="button"
|
||||
className="icon-button"
|
||||
onClick={handleNeutralHueReset}
|
||||
aria-label="Reset neutral hue"
|
||||
>
|
||||
<RestoreIcon size={16} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<label className="sidebar-slider">
|
||||
<span className="sidebar-slider__label">Neutral hue</span>
|
||||
<input
|
||||
type="range"
|
||||
min="0"
|
||||
max="360"
|
||||
step="1"
|
||||
value={neutralHue}
|
||||
onChange={(event) => onNeutralHueChange?.(event.target.value)}
|
||||
/>
|
||||
<span className="sidebar-slider__value">{neutralHue}°</span>
|
||||
</label>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
{themeControls ? <div className="sidebar__footer">{themeControls}</div> : null}
|
||||
</aside>
|
||||
);
|
||||
};
|
||||
|
||||
+98
-7
@@ -96,8 +96,88 @@
|
||||
--documents-grid-title-size: 0.8rem;
|
||||
}
|
||||
|
||||
:root[data-theme='light'] {
|
||||
color-scheme: light;
|
||||
}
|
||||
|
||||
:root[data-theme='dark'] {
|
||||
color-scheme: dark;
|
||||
|
||||
--dark-neutral-hue: calc(var(--neutral-hue) + var(--dark-hue-offset));
|
||||
--dark-foreground-hue: calc(var(--neutral-hue) + var(--dark-foreground-hue-offset));
|
||||
|
||||
--bg: oklch(0.15 0.01 var(--dark-neutral-hue));
|
||||
--surface: oklch(0.19 0.012 var(--dark-neutral-hue));
|
||||
--surface-subtle: oklch(0.23 0.012 var(--dark-neutral-hue));
|
||||
--fg: oklch(0.89 0.015 var(--dark-foreground-hue));
|
||||
--muted: oklch(0.72 0.02 var(--dark-foreground-hue));
|
||||
--sidebar-fg: oklch(0.78 0.02 var(--dark-foreground-hue));
|
||||
--border: oklch(0.33 0.01 var(--dark-foreground-hue));
|
||||
|
||||
--accent: oklch(0.75 0.2 calc(var(--dark-foreground-hue) + var(--accent-hue-offset)));
|
||||
--accent-hover: oklch(0.82 0.16 calc(var(--dark-foreground-hue) + var(--accent-hue-offset)));
|
||||
--on-accent: oklch(0.15 0.015 var(--dark-foreground-hue));
|
||||
|
||||
--folder-icon-back: color-mix(in oklch, var(--accent) 72%, black 12%);
|
||||
--folder-icon-mid: color-mix(in oklch, var(--accent) 50%, white 24%);
|
||||
--folder-icon-front: color-mix(in oklch, var(--accent) 30%, white 50%);
|
||||
|
||||
--accent-soft: color-mix(in oklch, var(--accent) 22%, transparent);
|
||||
--accent-elevated: color-mix(in oklch, var(--accent) 28%, transparent);
|
||||
--accent-elevated-strong: color-mix(in oklch, var(--accent) 38%, transparent);
|
||||
--accent-outline: color-mix(in oklch, var(--accent) 55%, transparent);
|
||||
--accent-outline-strong: color-mix(in oklch, var(--accent) 90%, transparent);
|
||||
--accent-focus: color-mix(in oklch, var(--accent) 80%, transparent);
|
||||
--surface-overlay: color-mix(in oklch, black 60%, transparent);
|
||||
|
||||
--selection: oklch(0.44 0.04 220deg);
|
||||
--selection-soft: color-mix(in oklch, var(--selection) 18%, transparent);
|
||||
|
||||
--shadow-faint: color-mix(in oklch, black 35%, transparent);
|
||||
--shadow-soft: color-mix(in oklch, black 50%, transparent);
|
||||
--shadow-medium: color-mix(in oklch, black 65%, transparent);
|
||||
--shadow-strong: color-mix(in oklch, black 80%, transparent);
|
||||
--shadow-deep: color-mix(in oklch, black 90%, transparent);
|
||||
--shadow-pop: color-mix(in oklch, black 70%, transparent);
|
||||
--outline-subtle: color-mix(in oklch, white 10%, transparent);
|
||||
--overlay-dark: color-mix(in oklch, black 70%, transparent);
|
||||
--overlay-dim: color-mix(in oklch, oklch(0.42 0.04 var(--dark-neutral-hue)) 35%, transparent);
|
||||
--overlay-darker: color-mix(in oklch, black 85%, transparent);
|
||||
--surface-danger-subtle: color-mix(in oklch, var(--danger) 26%, transparent);
|
||||
--overlay-accent-subtle: color-mix(in oklch, var(--accent) 32%, transparent);
|
||||
--border-strong: color-mix(in oklch, var(--fg) 30%, transparent);
|
||||
--surface-hover: color-mix(in oklch, white 6%, transparent);
|
||||
--overlay-accent-strong: color-mix(in oklch, var(--accent) 55%, transparent);
|
||||
--overlay-backdrop: color-mix(in oklch, oklch(0.1 0.015 var(--dark-neutral-hue)) 70%, transparent);
|
||||
--overlay-shadow: color-mix(in oklch, oklch(0.12 0.015 var(--dark-neutral-hue)) 55%, transparent);
|
||||
|
||||
--success: oklch(0.62 0.16 var(--success-hue));
|
||||
--warning: oklch(0.68 0.17 var(--warning-hue));
|
||||
--danger: oklch(0.60 0.2 var(--danger-hue));
|
||||
|
||||
--success-subtle: color-mix(in oklch, var(--success) 20%, transparent);
|
||||
--warning-subtle: color-mix(in oklch, var(--warning) 20%, transparent);
|
||||
--danger-border: color-mix(in oklch, var(--danger) 45%, transparent);
|
||||
--danger-soft: color-mix(in oklch, var(--danger) 24%, transparent);
|
||||
--danger-subtle: color-mix(in oklch, var(--danger) 18%, transparent);
|
||||
|
||||
--row-hover-bg: color-mix(in oklch, white 4%, transparent);
|
||||
--row-active-bg: color-mix(in oklch, var(--selection) 20%, transparent);
|
||||
--sidebar-hover-bg: color-mix(in oklch, white 6%, transparent);
|
||||
--sidebar-active-bg: color-mix(in oklch, var(--selection) 26%, transparent);
|
||||
--selection-ring: oklch(0.68 0.16 var(--dark-foreground-hue));
|
||||
--sidebar-active-pill-border: color-mix(in oklch, var(--accent) 72%, transparent);
|
||||
--link: color-mix(in oklch, var(--accent) 92%, transparent);
|
||||
--link-visited: color-mix(in oklch, var(--accent) 70%, white 20%);
|
||||
--preview-nav-bg: oklch(0.3 0.04 var(--dark-neutral-hue));
|
||||
--preview-nav-bg-hover: oklch(0.35 0.04 var(--dark-neutral-hue));
|
||||
--preview-nav-fg: var(--fg);
|
||||
--text-on-dark: color-mix(in oklch, white 92%, transparent);
|
||||
--surface-ink-soft: color-mix(in oklch, white 12%, transparent);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
:root:not([data-theme='light']) {
|
||||
color-scheme: dark;
|
||||
|
||||
--dark-neutral-hue: calc(var(--neutral-hue) + var(--dark-hue-offset));
|
||||
@@ -1201,7 +1281,7 @@ button.danger:hover:not([disabled]) {
|
||||
|
||||
.sidebar {
|
||||
gap: 0;
|
||||
overflow-y: auto;
|
||||
overflow: hidden;
|
||||
color: var(--sidebar-fg);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -1216,6 +1296,18 @@ button.danger:hover:not([disabled]) {
|
||||
flex-direction: column;
|
||||
padding: 0.5rem 1rem;
|
||||
gap: 0.75rem;
|
||||
overflow-y: auto;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.sidebar__footer {
|
||||
margin-top: auto;
|
||||
padding: 0.75rem 1rem 1rem;
|
||||
border-top: 1px solid var(--border);
|
||||
background: var(--surface-subtle);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.sidebar__title {
|
||||
@@ -1433,13 +1525,12 @@ button.danger:hover:not([disabled]) {
|
||||
background: var(--sidebar-hover-bg);
|
||||
}
|
||||
|
||||
.sidebar__footer {
|
||||
margin-top: auto;
|
||||
padding-top: 1rem;
|
||||
.sidebar__footer .sidebar-section {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.sidebar__footer button {
|
||||
width: 100%;
|
||||
.sidebar__footer .sidebar-section__actions {
|
||||
gap: 0.4rem;
|
||||
}
|
||||
|
||||
.sidebar-section:first-of-type,
|
||||
|
||||
@@ -25,6 +25,9 @@ import {
|
||||
IconSettings,
|
||||
IconCheck,
|
||||
IconPlus,
|
||||
IconSun,
|
||||
IconMoon,
|
||||
IconDeviceLaptop,
|
||||
} from '@tabler/icons-react';
|
||||
import FolderSvg from '../assets/folder.svg';
|
||||
|
||||
@@ -226,6 +229,33 @@ export const PlusIcon = ({ className, size = '1em', stroke = 1.6, ...rest }) =>
|
||||
/>
|
||||
);
|
||||
|
||||
export const SunIcon = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
|
||||
<IconSun
|
||||
className={composeClassName('icon', className)}
|
||||
size={size}
|
||||
stroke={stroke}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
|
||||
export const MoonIcon = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
|
||||
<IconMoon
|
||||
className={composeClassName('icon', className)}
|
||||
size={size}
|
||||
stroke={stroke}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
|
||||
export const DesktopIcon = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
|
||||
<IconDeviceLaptop
|
||||
className={composeClassName('icon', className)}
|
||||
size={size}
|
||||
stroke={stroke}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
|
||||
export const CheckIcon = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
|
||||
<IconCheck
|
||||
className={composeClassName('icon', className)}
|
||||
|
||||
Reference in New Issue
Block a user