This commit is contained in:
2025-12-04 12:43:10 +01:00
parent 2ea74816fb
commit b5d0a8cb66
16 changed files with 152 additions and 545 deletions
+4 -9
View File
@@ -1,6 +1,7 @@
import React, { createContext, useContext, useEffect, useMemo } from 'react';
import React, { useEffect, useMemo } from 'react';
import type { PropsWithChildren } from 'react';
import { httpClient, setAuthToken, clearAuthToken } from '../lib/apiClient';
import { createSafeContext } from '../utils/createSafeContext';
type HttpClient = typeof httpClient;
@@ -10,7 +11,7 @@ interface ApiContextValue {
clearAuthToken: () => void;
}
const ApiContext = createContext<ApiContextValue | null>(null);
const [ApiContext, useApi] = createSafeContext<ApiContextValue>('Api');
export const ApiProvider: React.FC<PropsWithChildren<{ initialToken?: string | null }>> = ({
initialToken = null,
@@ -34,10 +35,4 @@ export const ApiProvider: React.FC<PropsWithChildren<{ initialToken?: string | n
return <ApiContext.Provider value={value}>{children}</ApiContext.Provider>;
};
export const useApi = (): ApiContextValue => {
const ctx = useContext(ApiContext);
if (!ctx) {
throw new Error('useApi must be used within an ApiProvider');
}
return ctx;
};
export { useApi };
+3 -10
View File
@@ -1,8 +1,6 @@
import React, {
ReactNode,
createContext,
useCallback,
useContext,
useEffect,
useMemo,
useRef,
@@ -20,6 +18,7 @@ import {
SIDEBAR_SOLO_THRESHOLD,
type PanelKey,
} from '../constants/layout';
import { createSafeContext } from '../utils/createSafeContext';
interface SetPanelWidthOptions {
commit?: boolean;
@@ -51,7 +50,7 @@ type PanelResizeBindings = {
isPanelResizing: boolean;
};
const PanelManagerContext = createContext<PanelManagerContextValue | null>(null);
const [PanelManagerContext, usePanelManager] = createSafeContext<PanelManagerContextValue>('PanelManager');
const clampPanelWidth = (panel: PanelKey, value: number): number => {
const numeric = Number(value);
@@ -307,13 +306,7 @@ export const PanelManagerProvider: React.FC<PanelManagerProviderProps> = ({ chil
return <PanelManagerContext.Provider value={contextValue}>{children}</PanelManagerContext.Provider>;
};
export const usePanelManager = () => {
const context = useContext(PanelManagerContext);
if (!context) {
throw new Error('usePanelManager must be used within a PanelManagerProvider');
}
return context;
};
export { usePanelManager };
export const usePanelResizeBindings = (
panel: PanelKey,
@@ -1,9 +1,10 @@
import React, { createContext, useContext } from 'react';
import React from 'react';
import type { useWorkspaceSelection } from './useWorkspaceSelection';
import { createSafeContext } from '../utils/createSafeContext';
export type WorkspaceSelectionValue = ReturnType<typeof useWorkspaceSelection>;
const WorkspaceSelectionContext = createContext<WorkspaceSelectionValue | null>(null);
const [WorkspaceSelectionContext, useWorkspaceSelectionContext] = createSafeContext<WorkspaceSelectionValue>('WorkspaceSelection');
interface WorkspaceSelectionProviderProps {
value: WorkspaceSelectionValue;
@@ -16,10 +17,4 @@ export const WorkspaceSelectionProvider: React.FC<WorkspaceSelectionProviderProp
</WorkspaceSelectionContext.Provider>
);
export const useWorkspaceSelectionContext = () => {
const context = useContext(WorkspaceSelectionContext);
if (!context) {
throw new Error('useWorkspaceSelectionContext must be used within a WorkspaceSelectionProvider');
}
return context;
};
export { useWorkspaceSelectionContext };
+4 -19
View File
@@ -1,4 +1,5 @@
import React, { useContext, useEffect, useMemo, useReducer } from 'react';
import React, { useEffect, useMemo, useReducer } from 'react';
import { createSafeContext } from '../utils/createSafeContext';
import { clearAuthToken, setAuthToken, setAuthRefreshHandlers } from '../lib/apiClient';
import { ApiProvider } from './ApiContext';
import { listTenants } from '../lib/apiClient';
@@ -76,8 +77,8 @@ const initialAppState: AppState = {
tenants: [],
};
const AppStateContext = React.createContext<AppState | null>(null);
const AppDispatchContext = React.createContext<React.Dispatch<AppAction> | null>(null);
const [AppStateContext, useAppState] = createSafeContext<AppState>('AppState');
const [AppDispatchContext, useAppDispatch] = createSafeContext<React.Dispatch<AppAction>>('AppDispatch');
const appStateReducer = (state: AppState, action: AppAction): AppState => {
switch (action.type) {
@@ -269,20 +270,4 @@ const AppStateProvider: React.FC<{ children?: React.ReactNode }> = ({ children }
);
};
const useAppState = (): AppState => {
const context = useContext(AppStateContext);
if (!context) {
throw new Error('useAppState must be used within an AppStateProvider.');
}
return context;
};
const useAppDispatch = (): React.Dispatch<AppAction> => {
const context = useContext(AppDispatchContext);
if (!context) {
throw new Error('useAppDispatch must be used within an AppStateProvider.');
}
return context;
};
export { AppStateProvider, useAppState, useAppDispatch };
+2 -10
View File
@@ -1,13 +1,5 @@
import React from 'react';
import { createSafeContext } from './utils/createSafeContext';
type AppShellContextValue = Record<string, unknown>;
export const AppShellContext = React.createContext<AppShellContextValue | null>(null);
export const useAppShell = (): AppShellContextValue => {
const context = React.useContext(AppShellContext);
if (!context) {
throw new Error('AppShellContext not found. Ensure routes are nested under AppLayout.');
}
return context;
};
export const [AppShellContext, useAppShell] = createSafeContext<AppShellContextValue>('AppShell');
+5 -10
View File
@@ -1,6 +1,7 @@
import React, { createContext, useContext, useCallback } from 'react';
import React, { useCallback } from 'react';
import type { Document } from '../types/documents';
import type { Identifier } from '../types/identifiers';
import { createSafeContext } from '../utils/createSafeContext';
type DocumentOpenTarget = 'preview' | 'sidepanel' | 'viewer';
@@ -8,15 +9,7 @@ interface DocumentOpenContextValue {
openDocument: (doc: Document, target?: DocumentOpenTarget) => void;
}
const DocumentOpenContext = createContext<DocumentOpenContextValue | null>(null);
export const useDocumentOpen = () => {
const context = useContext(DocumentOpenContext);
if (!context) {
throw new Error('useDocumentOpen must be used within a DocumentOpenProvider');
}
return context;
};
const [DocumentOpenContext, useDocumentOpen] = createSafeContext<DocumentOpenContextValue>('DocumentOpen');
interface DocumentOpenProviderProps {
children: React.ReactNode;
@@ -59,3 +52,5 @@ export const DocumentOpenProvider: React.FC<DocumentOpenProviderProps> = ({
</DocumentOpenContext.Provider>
);
};
export { useDocumentOpen };
+4 -9
View File
@@ -1,4 +1,5 @@
import React, { createContext, useContext, useState, useCallback, useEffect, useRef } from 'react';
import React, { useState, useCallback, useEffect, useRef } from 'react';
import { createSafeContext } from '../utils/createSafeContext';
export type ToastVariant = 'info' | 'success' | 'error';
@@ -16,7 +17,7 @@ interface StatusToastContextValue {
removeToast: (id: string) => void;
}
const StatusToastContext = createContext<StatusToastContextValue | null>(null);
const [StatusToastContext, useStatusToast] = createSafeContext<StatusToastContextValue>('StatusToast');
const DEFAULT_DURATIONS: Record<ToastVariant, number> = {
success: 3000,
@@ -102,10 +103,4 @@ export const StatusToastProvider: React.FC<{ children: React.ReactNode }> = ({ c
);
};
export const useStatusToast = (): StatusToastContextValue => {
const context = useContext(StatusToastContext);
if (!context) {
throw new Error('useStatusToast must be used within StatusToastProvider');
}
return context;
};
export { useStatusToast };
@@ -1,4 +1,5 @@
import React, { createContext, useContext, useRef, useCallback } from 'react';
import React, { useRef, useCallback } from 'react';
import { createSafeContext } from '../utils/createSafeContext';
interface PointerTrackingContextType {
activePointersRef: React.MutableRefObject<Map<number, string | undefined>>;
@@ -6,7 +7,7 @@ interface PointerTrackingContextType {
removePointer: (id: number) => void;
}
const PointerTrackingContext = createContext<PointerTrackingContextType | null>(null);
const [PointerTrackingContext, usePointerTracking] = createSafeContext<PointerTrackingContextType>('PointerTracking');
export const PointerTrackingProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const activePointersRef = useRef(new Map<number, string | undefined>());
@@ -26,10 +27,4 @@ export const PointerTrackingProvider: React.FC<{ children: React.ReactNode }> =
);
};
export const usePointerTracking = () => {
const context = useContext(PointerTrackingContext);
if (!context) {
throw new Error('usePointerTracking must be used within a PointerTrackingProvider');
}
return context;
};
export { usePointerTracking };
@@ -1,5 +1,6 @@
import React, { createContext, useContext } from 'react';
import React from 'react';
import type { Identifier } from '../../types/identifiers';
import { createSafeContext } from '../../utils/createSafeContext';
export interface DocumentsFilterValue {
query: string;
@@ -17,7 +18,7 @@ export interface DocumentsFilterValue {
toggleIncludeDescendants: () => void;
}
const DocumentsFilterContext = createContext<DocumentsFilterValue | null>(null);
const [DocumentsFilterContext, useDocumentsFilter] = createSafeContext<DocumentsFilterValue>('DocumentsFilter');
interface DocumentsFilterProviderProps {
value: DocumentsFilterValue;
@@ -28,10 +29,4 @@ export const DocumentsFilterProvider: React.FC<DocumentsFilterProviderProps> = (
<DocumentsFilterContext.Provider value={value}>{children}</DocumentsFilterContext.Provider>
);
export const useDocumentsFilter = (): DocumentsFilterValue => {
const context = useContext(DocumentsFilterContext);
if (!context) {
throw new Error('useDocumentsFilter must be used within a DocumentsFilterProvider');
}
return context;
};
export { useDocumentsFilter };
+5 -10
View File
@@ -1,22 +1,15 @@
import React, { createContext, useContext, useState, useCallback, useMemo, useRef } from 'react';
import React, { useState, useCallback, useMemo, useRef } from 'react';
import PreviewZoomOverlay from '../detail/PreviewZoomOverlay';
import type { Document } from '../types/documents';
import type { Identifier } from '../types/identifiers';
import { createSafeContext } from '../utils/createSafeContext';
interface PreviewContextType {
openPreview: (doc: Document) => void;
closePreview: () => void;
}
const PreviewContext = createContext<PreviewContextType | null>(null);
export const usePreviewContext = () => {
const context = useContext(PreviewContext);
if (!context) {
throw new Error('usePreviewContext must be used within a PreviewProvider');
}
return context;
};
const [PreviewContext, usePreviewContext] = createSafeContext<PreviewContextType>('Preview');
interface PreviewProviderProps {
children: React.ReactNode;
@@ -66,3 +59,5 @@ export const PreviewProvider: React.FC<PreviewProviderProps> = ({ children, onNa
</PreviewContext.Provider>
);
};
export { usePreviewContext };
+1 -1
View File
@@ -7,7 +7,7 @@ import React, {
} from 'react';
import PanelHeader from '../ui/PanelHeader';
import { CloseIcon } from '../ui/icons';
import { DEFAULT_SETTINGS_SECTIONS } from './sections';
import { DEFAULT_SETTINGS_SECTIONS } from '../constants/settings';
export interface SettingsSectionConfig {
id: string;
-5
View File
@@ -1,5 +0,0 @@
import {
DEFAULT_SETTINGS_SECTIONS,
} from '../../constants/settings';
export { DEFAULT_SETTINGS_SECTIONS };
+3 -10
View File
@@ -1,6 +1,4 @@
import React, {
createContext,
useContext,
useMemo,
useState,
useCallback,
@@ -15,6 +13,7 @@ import {
THEME_MODES,
THEME_STORAGE_KEY,
} from '../constants/sidebar';
import { createSafeContext } from '../utils/createSafeContext';
interface SidebarContextValue {
collapsed: boolean;
@@ -33,7 +32,7 @@ interface SidebarContextValue {
type ThemeMode = (typeof THEME_MODES)[number];
const SidebarContext = createContext<SidebarContextValue | null>(null);
const [SidebarContext, useSidebarContext] = createSafeContext<SidebarContextValue>('Sidebar');
const loadInitialThemeSettings = () => {
const defaults = {
@@ -288,10 +287,4 @@ export const SidebarProvider = ({ initialCollapsed = false, children }) => {
return <SidebarContext.Provider value={contextValue}>{children}</SidebarContext.Provider>;
};
export const useSidebarContext = () => {
const context = useContext(SidebarContext);
if (!context) {
throw new Error('useSidebarContext must be used within a SidebarProvider');
}
return context;
};
export { useSidebarContext };
@@ -79,7 +79,7 @@ const SidebarHeader: React.FC<SidebarHeaderProps> = ({
}, [tenantMenuOpen, refreshTenantMenuPosition, tenants.length]);
const handleCollapse = useCallback(() => {
collapseSidebar('sidebar');
collapseSidebar();
}, [collapseSidebar]);
const handleUploadButtonClick = useCallback(() => {
+83 -424
View File
@@ -54,36 +54,76 @@ import { composeClassName } from './classNames';
type TablerIconComponent = (props: TablerIconProps) => JSX.Element;
export const ChevronIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<TablerChevronRight
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
// Factory for creating standard icon wrappers with consistent defaults
const createIcon = (
Icon: TablerIconComponent,
{ baseClass = 'icon', defaultStroke = 1.6 }: { baseClass?: string; defaultStroke?: number } = {},
): TablerIconComponent => {
const WrappedIcon: TablerIconComponent = ({ className, size = '1em', stroke = defaultStroke, ...rest }) => (
<Icon
className={composeClassName(baseClass, className)}
size={size}
stroke={stroke}
{...rest}
/>
);
return WrappedIcon;
};
export const TrashIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconTrash
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
// Standard stroke icons (stroke = 1.6)
export const ChevronIcon = createIcon(TablerChevronRight);
export const TrashIcon = createIcon(IconTrash);
export const EditIcon = createIcon(IconPencil);
export const DownloadIcon = createIcon(TablerDownload);
export const IconZoomInArea = createIcon(TablerZoomInArea);
export const GithubIcon = createIcon(IconBrandGithub);
export const MatrixIcon = createIcon(IconBrandMatrix);
export const WorldIcon = createIcon(IconWorld);
export const ViewListIcon = createIcon(IconLayoutList);
export const ViewGridIcon = createIcon(IconLayoutGrid);
export const UploadIcon = createIcon(IconUpload);
export const ArrowLeftIcon = createIcon(IconArrowLeft);
export const SidebarCollapseIcon = createIcon(IconLayoutSidebarLeftCollapse);
export const SidebarExpandIcon = createIcon(IconLayoutSidebarLeftExpand);
export const InfoIcon = createIcon(IconInfoCircle);
export const FileInfoIcon = createIcon(IconFileInfo);
export const BottombarCollapseIcon = createIcon(IconLayoutBottombarCollapse);
export const BottombarExpandIcon = createIcon(IconLayoutBottombarExpand);
export const FolderPlusIcon = createIcon(IconFolderPlus);
export const FoldersIcon = createIcon(IconFolders);
export const FoldersOffIcon = createIcon(IconFoldersOff);
export const RefreshIcon = createIcon(IconRefresh);
export const RestoreIcon = createIcon(IconRestore);
export const MinusVerticalIcon = createIcon(IconMinusVertical);
export const SortAscendingLettersIcon = createIcon(IconSortAscendingLetters);
export const SortDescendingLettersIcon = createIcon(IconSortDescendingLetters);
export const IconX = createIcon(TablerIconX);
export const CloseIcon = createIcon(TablerIconX);
export const SettingsIcon = createIcon(IconSettings);
export const PlusIcon = createIcon(IconPlus);
export const SunIcon = createIcon(IconSun);
export const MoonIcon = createIcon(IconMoon);
export const DesktopIcon = createIcon(IconDeviceLaptop);
export const CheckIcon = createIcon(IconCheck);
export const CircleDashedCheckIcon = createIcon(IconCircleDashedCheck);
export const FileIcon = createIcon(IconFile);
export const FolderOutlineIcon = createIcon(IconFolder);
export const AnalyzeIcon = createIcon(IconAnalyze);
export const WindowMaximizeIcon = createIcon(IconWindowMaximize);
export const LogoutIcon = createIcon(IconLogout);
export const ChevronDownIcon = createIcon(IconChevronDown);
export const EditIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconPencil
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
// Icons with different default stroke
export const LoaderIcon = createIcon(IconLoader, { defaultStroke: 1.8 });
export const WarningIcon = createIcon(IconAlertTriangle, { defaultStroke: 1.8 });
// Filled icons (stroke = 0)
export const TagIcon = createIcon(IconTagFilled, { baseClass: 'icon icon--fill', defaultStroke: 0 });
export const CorrespondentIcon = createIcon(IconUserFilled, { baseClass: 'icon icon--fill', defaultStroke: 0 });
// Custom icons that need special handling
export const FolderIcon: TablerIconComponent = ({ className, size = 16, title, ...rest }) => {
const dimensionProps = Number.isFinite(size) ? { width: Number(size), height: Number(size) } : {};
return (
<FolderSvg
className={composeClassName('folder-icon', className)}
@@ -119,408 +159,27 @@ export const LogoIcon: React.FC<LogoIconProps> = ({ className, width = 24, heigh
);
};
export const TagIcon: TablerIconComponent = ({ className, size = '1em', stroke = 0, ...rest }) => (
<IconTagFilled
className={composeClassName('icon icon--fill', className)}
size={size}
stroke={stroke}
export const IconFileStack: TablerIconComponent = ({ className, size = 24, stroke = 160, ...rest }) => (
<svg
className={composeClassName('icon', className)}
width={size}
height={size}
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={stroke}
strokeLinecap="round"
strokeLinejoin="round"
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
{...rest}
/>
>
<path d="M 17.9392 19.2642 c 0.6299 -0.3377 1.0608 -1.0031 1.0608 -1.7642 l 0 -11.4909 l 2.502 0.5318 c 1.0329 0.2195 1.6984 1.2443 1.4788 2.2772 l -2.6346 12.3951 c -0.2196 1.0329 -1.2443 1.6984 -2.2772 1.4789 l -5.1403 -1.0926 l 3.4203 -0.727 c 0.8245 -0.1753 1.4308 -0.8288 1.5902 -1.6083 Z" />
<path d="M 5 5.173 l 0 -1.673 c 0 -1.1 0.9 -2 2 -2 l 10 0 c 1.1 0 2 0.9 2 2 l 0 14 c 0 0.7611 -0.4309 1.4265 -1.0608 1.7642 c 0.0548 -0.2682 0.0567 -0.5513 -0.0036 -0.835 l -2.8267 -13.2989 c -0.2356 -1.1082 -1.3351 -1.8223 -2.4433 -1.5867 l -7.6656 1.6294 Z" />
<path d="M 16.349 20.8725 l -10.075 2.1415 c -1.1082 0.2355 -2.2077 -0.4785 -2.4432 -1.5867 l -2.8268 -13.2989 c -0.2356 -1.1083 0.4784 -2.2077 1.5867 -2.4433 l 10.0749 -2.1415 c 1.1082 -0.2356 2.2077 0.4785 2.4433 1.5867 l 2.8267 13.2989 c 0.2356 1.1082 -0.4784 2.2077 -1.5866 2.4433 Z" />
</svg>
);
export const CorrespondentIcon: TablerIconComponent = ({ className, size = '1em', stroke = 0, ...rest }) => (
<IconUserFilled
className={composeClassName('icon icon--fill', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
export const DownloadIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<TablerDownload
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
export const IconZoomInArea: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<TablerZoomInArea
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
export const GithubIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconBrandGithub
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
export const MatrixIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconBrandMatrix
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
export const WorldIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconWorld
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
export const ViewListIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconLayoutList
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
export const ViewGridIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconLayoutGrid
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
export const UploadIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconUpload
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
export const ArrowLeftIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconArrowLeft
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
export const SidebarCollapseIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconLayoutSidebarLeftCollapse
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
export const SidebarExpandIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconLayoutSidebarLeftExpand
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
export const InfoIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconInfoCircle
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
export const FileInfoIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconFileInfo
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
export const BottombarCollapseIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconLayoutBottombarCollapse
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
export const BottombarExpandIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconLayoutBottombarExpand
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
export const FolderPlusIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconFolderPlus
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
export const FoldersIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconFolders
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
export const FoldersOffIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconFoldersOff
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
export const RefreshIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconRefresh
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
export const RestoreIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconRestore
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
export const MinusVerticalIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconMinusVertical
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
export const IconFileStack: TablerIconComponent = ({ className, size = 24, stroke = 160, ...rest }) => {
return (
<svg
className={composeClassName('icon', className)}
width={size}
height={size}
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={stroke}
strokeLinecap="round"
strokeLinejoin="round"
{...rest}
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
>
<path
d="M 17.9392 19.2642 c 0.6299 -0.3377 1.0608 -1.0031 1.0608 -1.7642 l 0 -11.4909 l 2.502 0.5318 c 1.0329 0.2195 1.6984 1.2443 1.4788 2.2772 l -2.6346 12.3951 c -0.2196 1.0329 -1.2443 1.6984 -2.2772 1.4789 l -5.1403 -1.0926 l 3.4203 -0.727 c 0.8245 -0.1753 1.4308 -0.8288 1.5902 -1.6083 Z"
/>
<path
d="M 5 5.173 l 0 -1.673 c 0 -1.1 0.9 -2 2 -2 l 10 0 c 1.1 0 2 0.9 2 2 l 0 14 c 0 0.7611 -0.4309 1.4265 -1.0608 1.7642 c 0.0548 -0.2682 0.0567 -0.5513 -0.0036 -0.835 l -2.8267 -13.2989 c -0.2356 -1.1082 -1.3351 -1.8223 -2.4433 -1.5867 l -7.6656 1.6294 Z"
/>
<path
d="M 16.349 20.8725 l -10.075 2.1415 c -1.1082 0.2355 -2.2077 -0.4785 -2.4432 -1.5867 l -2.8268 -13.2989 c -0.2356 -1.1083 0.4784 -2.2077 1.5867 -2.4433 l 10.0749 -2.1415 c 1.1082 -0.2356 2.2077 0.4785 2.4433 1.5867 l 2.8267 13.2989 c 0.2356 1.1082 -0.4784 2.2077 -1.5866 2.4433 Z"
/>
</svg>
);
};
export const SortAscendingLettersIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconSortAscendingLetters
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
export const SortDescendingLettersIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconSortDescendingLetters
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
export const IconX: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<TablerIconX
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
export const CloseIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconX
className={className}
size={size}
stroke={stroke}
{...rest}
/>
);
export const SettingsIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconSettings
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
export const PlusIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconPlus
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
export const SunIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconSun
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
export const MoonIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconMoon
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
export const DesktopIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconDeviceLaptop
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
export const CheckIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconCheck
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
export const CircleDashedCheckIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconCircleDashedCheck
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
export const FileIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconFile
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
export const FolderOutlineIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconFolder
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
export const AnalyzeIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconAnalyze
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
export const LoaderIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.8, ...rest }) => (
<IconLoader
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
export const WarningIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.8, ...rest }) => (
<IconAlertTriangle
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
export const WindowMaximizeIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconWindowMaximize
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
export const LogoutIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconLogout
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
export const ChevronDownIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconChevronDown
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
export const FolderMoveIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<svg
xmlns="http://www.w3.org/2000/svg"
+25
View File
@@ -0,0 +1,25 @@
import { createContext, useContext, type Context } from 'react';
/**
* Creates a React context with a paired hook that throws a helpful error
* if used outside of the provider.
*
* @example
* const [ApiContext, useApi] = createSafeContext<ApiContextValue>('Api');
*
* // In component:
* const api = useApi(); // throws if outside ApiProvider
*/
export function createSafeContext<T>(name: string): [Context<T | null>, () => T] {
const Context = createContext<T | null>(null);
const useContextHook = (): T => {
const ctx = useContext(Context);
if (!ctx) {
throw new Error(`use${name} must be used within a ${name}Provider`);
}
return ctx;
};
return [Context, useContextHook];
}