Initial commit
This commit is contained in:
10
packages/ui/lib/assets/warning.svg
Normal file
10
packages/ui/lib/assets/warning.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24" width="400" fill="none" stroke="currentColor">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
fill="none"
|
||||
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
|
||||
/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 409 B |
11
packages/ui/lib/components/LoadingSpinner.tsx
Normal file
11
packages/ui/lib/components/LoadingSpinner.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import { RingLoader } from 'react-spinners';
|
||||
|
||||
interface ILoadingSpinnerProps {
|
||||
size?: number;
|
||||
}
|
||||
|
||||
export const LoadingSpinner = ({ size }: ILoadingSpinnerProps) => (
|
||||
<div className={'flex min-h-screen items-center justify-center'}>
|
||||
<RingLoader size={size ?? 100} color={'aqua'} />
|
||||
</div>
|
||||
);
|
||||
23
packages/ui/lib/components/ToggleButton.tsx
Normal file
23
packages/ui/lib/components/ToggleButton.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import { cn } from '@/lib/utils';
|
||||
import { useStorage } from '@extension/shared';
|
||||
import { exampleThemeStorage } from '@extension/storage';
|
||||
import type { ComponentPropsWithoutRef } from 'react';
|
||||
|
||||
type ToggleButtonProps = ComponentPropsWithoutRef<'button'>;
|
||||
|
||||
export const ToggleButton = ({ className, children, ...props }: ToggleButtonProps) => {
|
||||
const { isLight } = useStorage(exampleThemeStorage);
|
||||
|
||||
return (
|
||||
<button
|
||||
className={cn(
|
||||
'mt-4 rounded border-2 px-4 py-1 font-bold shadow hover:scale-105',
|
||||
isLight ? 'border-black bg-white text-black' : 'border-white bg-black text-white',
|
||||
className,
|
||||
)}
|
||||
onClick={exampleThemeStorage.toggle}
|
||||
{...props}>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
13
packages/ui/lib/components/error-display/ErrorDisplay.tsx
Normal file
13
packages/ui/lib/components/error-display/ErrorDisplay.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import { ErrorHeader } from '@/lib/components/error-display/ErrorHeader';
|
||||
import { ErrorResetButton } from '@/lib/components/error-display/ErrorResetButton';
|
||||
import { ErrorStackTraceList } from '@/lib/components/error-display/ErrorStackTraceList';
|
||||
|
||||
export const ErrorDisplay = ({ error, resetErrorBoundary }: { error?: Error; resetErrorBoundary?: () => void }) => (
|
||||
<div className="flex items-center justify-center bg-gray-50 px-4 py-6 sm:px-6 lg:px-8">
|
||||
<div className="w-full max-w-md space-y-8">
|
||||
<ErrorHeader />
|
||||
<ErrorStackTraceList error={error} />
|
||||
<ErrorResetButton resetErrorBoundary={resetErrorBoundary} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
21
packages/ui/lib/components/error-display/ErrorHeader.tsx
Normal file
21
packages/ui/lib/components/error-display/ErrorHeader.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import { t } from '@extension/i18n';
|
||||
|
||||
// FIXME: IMPORT SVG ICON INSTEAD OF DEFINING INLINE IT HERE
|
||||
const WarningIcon = ({ className }: { className: string }) => (
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" className={className}>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export const ErrorHeader = () => (
|
||||
<div className="text-center">
|
||||
<WarningIcon className={'mx-auto h-24 w-24 text-red-500'} />
|
||||
<h2 className="mt-6 text-3xl font-extrabold text-gray-900">{t('displayErrorInfo')}</h2>
|
||||
<p className="mt-2 text-sm text-gray-600">{t('displayErrorDescription')}.</p>
|
||||
</div>
|
||||
);
|
||||
@@ -0,0 +1,11 @@
|
||||
import { t } from '@extension/i18n';
|
||||
|
||||
export const ErrorResetButton = ({ resetErrorBoundary }: { resetErrorBoundary?: () => void }) => (
|
||||
<div className="flex items-center justify-center">
|
||||
<button
|
||||
onClick={resetErrorBoundary}
|
||||
className="inline-flex items-center rounded-md border border-transparent bg-red-600 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2">
|
||||
{t('displayErrorReset')}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
@@ -0,0 +1,20 @@
|
||||
import { t } from '@extension/i18n';
|
||||
|
||||
export const ErrorStackTraceList = ({ error }: { error?: Error }) => (
|
||||
<div className="overflow-hidden rounded-lg bg-white shadow">
|
||||
<div className="px-4 py-5 sm:p-6">
|
||||
<div className="text-sm text-gray-500">
|
||||
<p className="mb-2 font-medium text-gray-700">{t('displayErrorDetailsInfo')}</p>
|
||||
<div className="overflow-auto rounded-md bg-red-50 p-4">
|
||||
<p className="break-all font-mono text-red-700">{error?.message || t('displayErrorUnknownErrorInfo')}</p>
|
||||
{error?.stack && (
|
||||
<details className="mt-3">
|
||||
<summary className="cursor-pointer text-sm text-red-700">Stack trace</summary>
|
||||
<pre className="mt-2 overflow-auto p-2 text-xs text-red-800">{error?.stack}</pre>
|
||||
</details>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
3
packages/ui/lib/components/index.ts
Normal file
3
packages/ui/lib/components/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from './ToggleButton';
|
||||
export * from './LoadingSpinner';
|
||||
export * from './error-display/ErrorDisplay';
|
||||
3
packages/ui/lib/index.ts
Normal file
3
packages/ui/lib/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from './components/index';
|
||||
export * from './utils';
|
||||
export * from './with-ui';
|
||||
5
packages/ui/lib/utils.ts
Normal file
5
packages/ui/lib/utils.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { clsx } from 'clsx';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
import type { ClassValue } from 'clsx';
|
||||
|
||||
export const cn = (...inputs: ClassValue[]) => twMerge(clsx(inputs));
|
||||
7
packages/ui/lib/with-ui.ts
Normal file
7
packages/ui/lib/with-ui.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import deepmerge from 'deepmerge';
|
||||
import type { Config } from 'tailwindcss';
|
||||
|
||||
export const withUI = (tailwindConfig: Config): Config =>
|
||||
deepmerge(tailwindConfig, {
|
||||
content: ['../../packages/ui/lib/**/*.tsx'],
|
||||
});
|
||||
Reference in New Issue
Block a user