refactor
This commit is contained in:
@@ -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 };
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import {
|
||||
DEFAULT_SETTINGS_SECTIONS,
|
||||
} from '../../constants/settings';
|
||||
|
||||
export { DEFAULT_SETTINGS_SECTIONS };
|
||||
@@ -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
@@ -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"
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
Reference in New Issue
Block a user