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 type { PropsWithChildren } from 'react';
import { httpClient, setAuthToken, clearAuthToken } from '../lib/apiClient'; import { httpClient, setAuthToken, clearAuthToken } from '../lib/apiClient';
import { createSafeContext } from '../utils/createSafeContext';
type HttpClient = typeof httpClient; type HttpClient = typeof httpClient;
@@ -10,7 +11,7 @@ interface ApiContextValue {
clearAuthToken: () => void; clearAuthToken: () => void;
} }
const ApiContext = createContext<ApiContextValue | null>(null); const [ApiContext, useApi] = createSafeContext<ApiContextValue>('Api');
export const ApiProvider: React.FC<PropsWithChildren<{ initialToken?: string | null }>> = ({ export const ApiProvider: React.FC<PropsWithChildren<{ initialToken?: string | null }>> = ({
initialToken = null, initialToken = null,
@@ -34,10 +35,4 @@ export const ApiProvider: React.FC<PropsWithChildren<{ initialToken?: string | n
return <ApiContext.Provider value={value}>{children}</ApiContext.Provider>; return <ApiContext.Provider value={value}>{children}</ApiContext.Provider>;
}; };
export const useApi = (): ApiContextValue => { export { useApi };
const ctx = useContext(ApiContext);
if (!ctx) {
throw new Error('useApi must be used within an ApiProvider');
}
return ctx;
};
+3 -10
View File
@@ -1,8 +1,6 @@
import React, { import React, {
ReactNode, ReactNode,
createContext,
useCallback, useCallback,
useContext,
useEffect, useEffect,
useMemo, useMemo,
useRef, useRef,
@@ -20,6 +18,7 @@ import {
SIDEBAR_SOLO_THRESHOLD, SIDEBAR_SOLO_THRESHOLD,
type PanelKey, type PanelKey,
} from '../constants/layout'; } from '../constants/layout';
import { createSafeContext } from '../utils/createSafeContext';
interface SetPanelWidthOptions { interface SetPanelWidthOptions {
commit?: boolean; commit?: boolean;
@@ -51,7 +50,7 @@ type PanelResizeBindings = {
isPanelResizing: boolean; isPanelResizing: boolean;
}; };
const PanelManagerContext = createContext<PanelManagerContextValue | null>(null); const [PanelManagerContext, usePanelManager] = createSafeContext<PanelManagerContextValue>('PanelManager');
const clampPanelWidth = (panel: PanelKey, value: number): number => { const clampPanelWidth = (panel: PanelKey, value: number): number => {
const numeric = Number(value); const numeric = Number(value);
@@ -307,13 +306,7 @@ export const PanelManagerProvider: React.FC<PanelManagerProviderProps> = ({ chil
return <PanelManagerContext.Provider value={contextValue}>{children}</PanelManagerContext.Provider>; return <PanelManagerContext.Provider value={contextValue}>{children}</PanelManagerContext.Provider>;
}; };
export const usePanelManager = () => { export { usePanelManager };
const context = useContext(PanelManagerContext);
if (!context) {
throw new Error('usePanelManager must be used within a PanelManagerProvider');
}
return context;
};
export const usePanelResizeBindings = ( export const usePanelResizeBindings = (
panel: PanelKey, panel: PanelKey,
@@ -1,9 +1,10 @@
import React, { createContext, useContext } from 'react'; import React from 'react';
import type { useWorkspaceSelection } from './useWorkspaceSelection'; import type { useWorkspaceSelection } from './useWorkspaceSelection';
import { createSafeContext } from '../utils/createSafeContext';
export type WorkspaceSelectionValue = ReturnType<typeof useWorkspaceSelection>; export type WorkspaceSelectionValue = ReturnType<typeof useWorkspaceSelection>;
const WorkspaceSelectionContext = createContext<WorkspaceSelectionValue | null>(null); const [WorkspaceSelectionContext, useWorkspaceSelectionContext] = createSafeContext<WorkspaceSelectionValue>('WorkspaceSelection');
interface WorkspaceSelectionProviderProps { interface WorkspaceSelectionProviderProps {
value: WorkspaceSelectionValue; value: WorkspaceSelectionValue;
@@ -16,10 +17,4 @@ export const WorkspaceSelectionProvider: React.FC<WorkspaceSelectionProviderProp
</WorkspaceSelectionContext.Provider> </WorkspaceSelectionContext.Provider>
); );
export const useWorkspaceSelectionContext = () => { export { useWorkspaceSelectionContext };
const context = useContext(WorkspaceSelectionContext);
if (!context) {
throw new Error('useWorkspaceSelectionContext must be used within a WorkspaceSelectionProvider');
}
return context;
};
+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 { clearAuthToken, setAuthToken, setAuthRefreshHandlers } from '../lib/apiClient';
import { ApiProvider } from './ApiContext'; import { ApiProvider } from './ApiContext';
import { listTenants } from '../lib/apiClient'; import { listTenants } from '../lib/apiClient';
@@ -76,8 +77,8 @@ const initialAppState: AppState = {
tenants: [], tenants: [],
}; };
const AppStateContext = React.createContext<AppState | null>(null); const [AppStateContext, useAppState] = createSafeContext<AppState>('AppState');
const AppDispatchContext = React.createContext<React.Dispatch<AppAction> | null>(null); const [AppDispatchContext, useAppDispatch] = createSafeContext<React.Dispatch<AppAction>>('AppDispatch');
const appStateReducer = (state: AppState, action: AppAction): AppState => { const appStateReducer = (state: AppState, action: AppAction): AppState => {
switch (action.type) { 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 }; 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>; type AppShellContextValue = Record<string, unknown>;
export const AppShellContext = React.createContext<AppShellContextValue | null>(null); export const [AppShellContext, useAppShell] = createSafeContext<AppShellContextValue>('AppShell');
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;
};
+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 { Document } from '../types/documents';
import type { Identifier } from '../types/identifiers'; import type { Identifier } from '../types/identifiers';
import { createSafeContext } from '../utils/createSafeContext';
type DocumentOpenTarget = 'preview' | 'sidepanel' | 'viewer'; type DocumentOpenTarget = 'preview' | 'sidepanel' | 'viewer';
@@ -8,15 +9,7 @@ interface DocumentOpenContextValue {
openDocument: (doc: Document, target?: DocumentOpenTarget) => void; openDocument: (doc: Document, target?: DocumentOpenTarget) => void;
} }
const DocumentOpenContext = createContext<DocumentOpenContextValue | null>(null); const [DocumentOpenContext, useDocumentOpen] = createSafeContext<DocumentOpenContextValue>('DocumentOpen');
export const useDocumentOpen = () => {
const context = useContext(DocumentOpenContext);
if (!context) {
throw new Error('useDocumentOpen must be used within a DocumentOpenProvider');
}
return context;
};
interface DocumentOpenProviderProps { interface DocumentOpenProviderProps {
children: React.ReactNode; children: React.ReactNode;
@@ -59,3 +52,5 @@ export const DocumentOpenProvider: React.FC<DocumentOpenProviderProps> = ({
</DocumentOpenContext.Provider> </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'; export type ToastVariant = 'info' | 'success' | 'error';
@@ -16,7 +17,7 @@ interface StatusToastContextValue {
removeToast: (id: string) => void; removeToast: (id: string) => void;
} }
const StatusToastContext = createContext<StatusToastContextValue | null>(null); const [StatusToastContext, useStatusToast] = createSafeContext<StatusToastContextValue>('StatusToast');
const DEFAULT_DURATIONS: Record<ToastVariant, number> = { const DEFAULT_DURATIONS: Record<ToastVariant, number> = {
success: 3000, success: 3000,
@@ -102,10 +103,4 @@ export const StatusToastProvider: React.FC<{ children: React.ReactNode }> = ({ c
); );
}; };
export const useStatusToast = (): StatusToastContextValue => { export { useStatusToast };
const context = useContext(StatusToastContext);
if (!context) {
throw new Error('useStatusToast must be used within StatusToastProvider');
}
return context;
};
@@ -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 { interface PointerTrackingContextType {
activePointersRef: React.MutableRefObject<Map<number, string | undefined>>; activePointersRef: React.MutableRefObject<Map<number, string | undefined>>;
@@ -6,7 +7,7 @@ interface PointerTrackingContextType {
removePointer: (id: number) => void; 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 }) => { export const PointerTrackingProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const activePointersRef = useRef(new Map<number, string | undefined>()); const activePointersRef = useRef(new Map<number, string | undefined>());
@@ -26,10 +27,4 @@ export const PointerTrackingProvider: React.FC<{ children: React.ReactNode }> =
); );
}; };
export const usePointerTracking = () => { export { usePointerTracking };
const context = useContext(PointerTrackingContext);
if (!context) {
throw new Error('usePointerTracking must be used within a PointerTrackingProvider');
}
return context;
};
@@ -1,5 +1,6 @@
import React, { createContext, useContext } from 'react'; import React from 'react';
import type { Identifier } from '../../types/identifiers'; import type { Identifier } from '../../types/identifiers';
import { createSafeContext } from '../../utils/createSafeContext';
export interface DocumentsFilterValue { export interface DocumentsFilterValue {
query: string; query: string;
@@ -17,7 +18,7 @@ export interface DocumentsFilterValue {
toggleIncludeDescendants: () => void; toggleIncludeDescendants: () => void;
} }
const DocumentsFilterContext = createContext<DocumentsFilterValue | null>(null); const [DocumentsFilterContext, useDocumentsFilter] = createSafeContext<DocumentsFilterValue>('DocumentsFilter');
interface DocumentsFilterProviderProps { interface DocumentsFilterProviderProps {
value: DocumentsFilterValue; value: DocumentsFilterValue;
@@ -28,10 +29,4 @@ export const DocumentsFilterProvider: React.FC<DocumentsFilterProviderProps> = (
<DocumentsFilterContext.Provider value={value}>{children}</DocumentsFilterContext.Provider> <DocumentsFilterContext.Provider value={value}>{children}</DocumentsFilterContext.Provider>
); );
export const useDocumentsFilter = (): DocumentsFilterValue => { export { useDocumentsFilter };
const context = useContext(DocumentsFilterContext);
if (!context) {
throw new Error('useDocumentsFilter must be used within a DocumentsFilterProvider');
}
return context;
};
+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 PreviewZoomOverlay from '../detail/PreviewZoomOverlay';
import type { Document } from '../types/documents'; import type { Document } from '../types/documents';
import type { Identifier } from '../types/identifiers'; import type { Identifier } from '../types/identifiers';
import { createSafeContext } from '../utils/createSafeContext';
interface PreviewContextType { interface PreviewContextType {
openPreview: (doc: Document) => void; openPreview: (doc: Document) => void;
closePreview: () => void; closePreview: () => void;
} }
const PreviewContext = createContext<PreviewContextType | null>(null); const [PreviewContext, usePreviewContext] = createSafeContext<PreviewContextType>('Preview');
export const usePreviewContext = () => {
const context = useContext(PreviewContext);
if (!context) {
throw new Error('usePreviewContext must be used within a PreviewProvider');
}
return context;
};
interface PreviewProviderProps { interface PreviewProviderProps {
children: React.ReactNode; children: React.ReactNode;
@@ -66,3 +59,5 @@ export const PreviewProvider: React.FC<PreviewProviderProps> = ({ children, onNa
</PreviewContext.Provider> </PreviewContext.Provider>
); );
}; };
export { usePreviewContext };
+1 -1
View File
@@ -7,7 +7,7 @@ import React, {
} from 'react'; } from 'react';
import PanelHeader from '../ui/PanelHeader'; import PanelHeader from '../ui/PanelHeader';
import { CloseIcon } from '../ui/icons'; import { CloseIcon } from '../ui/icons';
import { DEFAULT_SETTINGS_SECTIONS } from './sections'; import { DEFAULT_SETTINGS_SECTIONS } from '../constants/settings';
export interface SettingsSectionConfig { export interface SettingsSectionConfig {
id: string; 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, { import React, {
createContext,
useContext,
useMemo, useMemo,
useState, useState,
useCallback, useCallback,
@@ -15,6 +13,7 @@ import {
THEME_MODES, THEME_MODES,
THEME_STORAGE_KEY, THEME_STORAGE_KEY,
} from '../constants/sidebar'; } from '../constants/sidebar';
import { createSafeContext } from '../utils/createSafeContext';
interface SidebarContextValue { interface SidebarContextValue {
collapsed: boolean; collapsed: boolean;
@@ -33,7 +32,7 @@ interface SidebarContextValue {
type ThemeMode = (typeof THEME_MODES)[number]; type ThemeMode = (typeof THEME_MODES)[number];
const SidebarContext = createContext<SidebarContextValue | null>(null); const [SidebarContext, useSidebarContext] = createSafeContext<SidebarContextValue>('Sidebar');
const loadInitialThemeSettings = () => { const loadInitialThemeSettings = () => {
const defaults = { const defaults = {
@@ -288,10 +287,4 @@ export const SidebarProvider = ({ initialCollapsed = false, children }) => {
return <SidebarContext.Provider value={contextValue}>{children}</SidebarContext.Provider>; return <SidebarContext.Provider value={contextValue}>{children}</SidebarContext.Provider>;
}; };
export const useSidebarContext = () => { export { useSidebarContext };
const context = useContext(SidebarContext);
if (!context) {
throw new Error('useSidebarContext must be used within a SidebarProvider');
}
return context;
};
@@ -79,7 +79,7 @@ const SidebarHeader: React.FC<SidebarHeaderProps> = ({
}, [tenantMenuOpen, refreshTenantMenuPosition, tenants.length]); }, [tenantMenuOpen, refreshTenantMenuPosition, tenants.length]);
const handleCollapse = useCallback(() => { const handleCollapse = useCallback(() => {
collapseSidebar('sidebar'); collapseSidebar();
}, [collapseSidebar]); }, [collapseSidebar]);
const handleUploadButtonClick = useCallback(() => { const handleUploadButtonClick = useCallback(() => {
+66 -407
View File
@@ -54,36 +54,76 @@ import { composeClassName } from './classNames';
type TablerIconComponent = (props: TablerIconProps) => JSX.Element; type TablerIconComponent = (props: TablerIconProps) => JSX.Element;
export const ChevronIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => ( // Factory for creating standard icon wrappers with consistent defaults
<TablerChevronRight const createIcon = (
className={composeClassName('icon', className)} 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} size={size}
stroke={stroke} stroke={stroke}
{...rest} {...rest}
/> />
); );
return WrappedIcon;
};
export const TrashIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => ( // Standard stroke icons (stroke = 1.6)
<IconTrash export const ChevronIcon = createIcon(TablerChevronRight);
className={composeClassName('icon', className)} export const TrashIcon = createIcon(IconTrash);
size={size} export const EditIcon = createIcon(IconPencil);
stroke={stroke} export const DownloadIcon = createIcon(TablerDownload);
{...rest} 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 }) => ( // Icons with different default stroke
<IconPencil export const LoaderIcon = createIcon(IconLoader, { defaultStroke: 1.8 });
className={composeClassName('icon', className)} export const WarningIcon = createIcon(IconAlertTriangle, { defaultStroke: 1.8 });
size={size}
stroke={stroke}
{...rest}
/>
);
// 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 }) => { export const FolderIcon: TablerIconComponent = ({ className, size = 16, title, ...rest }) => {
const dimensionProps = Number.isFinite(size) ? { width: Number(size), height: Number(size) } : {}; const dimensionProps = Number.isFinite(size) ? { width: Number(size), height: Number(size) } : {};
return ( return (
<FolderSvg <FolderSvg
className={composeClassName('folder-icon', className)} className={composeClassName('folder-icon', className)}
@@ -119,211 +159,7 @@ export const LogoIcon: React.FC<LogoIconProps> = ({ className, width = 24, heigh
); );
}; };
export const TagIcon: TablerIconComponent = ({ className, size = '1em', stroke = 0, ...rest }) => ( export const IconFileStack: TablerIconComponent = ({ className, size = 24, stroke = 160, ...rest }) => (
<IconTagFilled
className={composeClassName('icon icon--fill', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
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 <svg
className={composeClassName('icon', className)} className={composeClassName('icon', className)}
width={size} width={size}
@@ -334,193 +170,16 @@ export const IconFileStack: TablerIconComponent = ({ className, size = 24, strok
strokeWidth={stroke} strokeWidth={stroke}
strokeLinecap="round" strokeLinecap="round"
strokeLinejoin="round" strokeLinejoin="round"
{...rest}
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink" xmlnsXlink="http://www.w3.org/1999/xlink"
{...rest}
> >
<path <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" />
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" />
<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> </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 }) => ( export const FolderMoveIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<svg <svg
xmlns="http://www.w3.org/2000/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];
}