Skip to content

Commit

Permalink
feat: 🎸 UI optimizations
Browse files Browse the repository at this point in the history
  • Loading branch information
jsimck committed Dec 15, 2021
1 parent 361c546 commit a059078
Show file tree
Hide file tree
Showing 25 changed files with 546 additions and 431 deletions.
2 changes: 1 addition & 1 deletion packages/error-overlay/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ module.exports = {
'tailwindcss/no-custom-classname': [
'error',
{
whitelist: ['hljs', 'language\\-javascript']
whitelist: ['^.*\\/\\d+$', 'hover\\:bg\\-slate\\-600']
}
],
'import/no-unresolved': 'off',
Expand Down
1 change: 1 addition & 0 deletions packages/error-overlay/client/src/utils/overlayIndex.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<link rel="stylesheet" href="/__error-overlay-static/overlay.css">
</head>

Expand Down
23 changes: 14 additions & 9 deletions packages/error-overlay/overlay/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
import 'tailwindcss/tailwind.css';
import { Fragment, FunctionComponent, useMemo, useState } from 'react';

import { Button, ChevronIcon, Frame, Header } from '#/components';
import { Frame, Header, Hero, Icon, Button } from '#/components';
import { useConnectOverlay } from '#/hooks';
import { useErrorsStore } from '#/stores';

/**
* TODO
* - Create bundle with source-map wasm included
* - Save viewCompiled toggle to cookies
* - make context lines editable
* - support for build errors
* - performance optimizations
* - source map parsing performance optimization "with"
*/
const App: FunctionComponent = () => {
Expand Down Expand Up @@ -50,17 +48,24 @@ const App: FunctionComponent = () => {
}

return (
<div className="min-w-full min-h-screen bg-white">
<div className="min-w-full min-h-screen font-mono bg-white text-slate-900">
<div className="container p-4 mx-auto">
<Header error={currentError} showOriginal={showOriginal} />
<Header />
<Hero error={currentError} showOriginal={showOriginal} />
{visibleFrames.map((frameWrapper, index) => (
<Fragment key={frameWrapper.id}>
<Frame frameWrapper={frameWrapper} errorId={currentError?.id} />
{index === 0 && collapsedFramesCount > 1 && (
<div className="flex justify-center items-center mt-8 mb-6">
<Button bordered onClick={() => setCollapsed(false)}>
<ChevronIcon className="mr-1 w-4 h-4" />
<span>Show {collapsedFramesCount} collapsed frames</span>
<div className="flex justify-center items-center mt-8 mb-8">
<Button
className="inline-flex items-center"
onClick={() => setCollapsed(false)}>
<Icon icon="chevron" size="sm" className="mr-1" />
<span>
Show{' '}
<span className="underline">{collapsedFramesCount}</span>{' '}
collapsed frames
</span>
</Button>
</div>
)}
Expand Down
67 changes: 54 additions & 13 deletions packages/error-overlay/overlay/src/components/button/Button.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,68 @@
import { FunctionComponent, SyntheticEvent } from 'react';
import clsx from 'clsx';
import { FunctionComponent } from 'react';

type ButtonProps = {
type?: 'submit' | 'reset' | 'button';
export type ButtonProps = {
btn?: 'primary' | 'secondary';
color?: 'orange' | 'gray' | 'green' | 'light';
size?: 'sm' | 'xs';
linkStyle?: boolean;
bordered?: boolean;
onClick?(event: SyntheticEvent): void;
};

const Button: FunctionComponent<ButtonProps> = ({
type = 'button',
const Button: FunctionComponent<
ButtonProps &
React.DetailedHTMLProps<
React.ButtonHTMLAttributes<HTMLButtonElement>,
HTMLButtonElement
>
> = ({
children,
className,
color = 'gray',
size = 'sm',
bordered = false,
onClick,
children
linkStyle = false,
type = 'button',
...restProps
}) => {
return (
<button
type={type}
onClick={event => onClick && onClick(event)}
className={`flex items-center py-2 px-3 font-mono text-sm text-gray-600 hover:text-blue-500 active:text-blue-600 transition-all duration-100 ease-in-out active:scale-95 ${
bordered ? 'border border-gray-600 hover:border-blue-500' : ''
}`}>
className={clsx(
'tracking-tighter border-2 transition-all',
{
'p-2 text-xs': size === 'xs',
'py-2 px-3 text-sm': size === 'sm',
'border-transparent': linkStyle,
'active:scale-90': linkStyle || size === 'xs',
'active:scale-95': !linkStyle,
'text-slate-600 hover:text-slate-800': color === 'gray',
[bordered
? 'border-slate-500 hover:border-slate-700 hover:bg-slate-100'
: 'border-slate-200 bg-slate-200 hover:border-slate-300 hover:bg-slate-300']:
!linkStyle && color === 'gray',
'text-slate-500 hover:text-slate-300': color === 'light',
[bordered
? 'border-slate-500 hover:border-slate-700 hover:bg-slate-100'
: 'border-slate-200 bg-slate-200 hover:border-slate-300 hover:bg-slate-300']:
!linkStyle && color === 'gray',
'text-emerald-600 hover:text-emerald-700': color === 'green',
[bordered
? 'border-emerald-500 hover:border-emerald-600 hover:bg-emerald-50'
: 'border-emerald-100 bg-emerald-100 hover:border-emerald-200 hover:bg-emerald-200']:
!linkStyle && color === 'green',
'text-orange-600 hover:text-orange-700': color === 'orange',
[bordered
? 'border-orange-500 hover:border-orange-600 hover:bg-orange-50'
: 'border-orange-100 bg-orange-100 hover:border-orange-200 hover:bg-orange-200']:
!linkStyle && color === 'orange'
},
className
)}
{...restProps}>
{children}
</button>
);
};

export { Button };
export default Button;
129 changes: 77 additions & 52 deletions packages/error-overlay/overlay/src/components/frame/Frame.tsx
Original file line number Diff line number Diff line change
@@ -1,72 +1,97 @@
import hljs from 'highlight.js/lib/core';
import hljsJS from 'highlight.js/lib/languages/javascript';
import { FunctionComponent, memo } from 'react';
import PrismJS from 'prismjs';
import { FunctionComponent, memo, useCallback, useState } from 'react';
import { FrameWrapper } from 'types';

import { ChevronIcon } from '#/components';
import { ErrorWrapper } from '#/reducers';

import { FrameHeader } from './FrameHeader';
import FrameHeader from './FrameHeader';

import 'highlight.js/styles/github-dark-dimmed.css';
import 'prismjs/components/prism-jsx';
import 'prismjs/components/prism-javascript';
import './prismjs.css';

hljs.registerLanguage('javascript', hljsJS);

type FrameProps = {
export type FrameProps = {
errorId: ErrorWrapper['id'];
frameWrapper: FrameWrapper;
};

const FrameBase: FunctionComponent<FrameProps> = ({
frameWrapper,
errorId
}) => {
if (!frameWrapper) {
return null;
function getPrismLanguage(
fileUri: string
): {
grammar: PrismJS.Grammar;
language: PrismJS.Language;
} {
let language: PrismJS.Language = 'javascript';

if (fileUri.endsWith('.jsx')) {
language = 'jsx';
}

return { grammar: PrismJS.languages[language], language };
}

const Frame: FunctionComponent<FrameProps> = ({ frameWrapper, errorId }) => {
const { frame } = frameWrapper;
const sourceFragment = frameWrapper.showOriginal
? frame.originalSourceFragment
: frame.sourceFragment;
const hasFragment =
Array.isArray(sourceFragment) && sourceFragment.length > 0;

const [isFragmentVisible, setIsFragmentVisible] = useState<boolean>(
hasFragment
);

const handleHeaderClick = useCallback(() => {
setIsFragmentVisible(visible => !visible);
}, []);

const { grammar, language } = getPrismLanguage(
frame.originalFileName || frame.fileName
);

return (
<div className="group mb-4">
<FrameHeader frameWrapper={frameWrapper} errorId={errorId} />
<div className="overflow-hidden mb-4 rounded-md shadow-lg text-slate-50 bg-slate-700 shadow-slate-700/50">
<FrameHeader
onHeaderClick={handleHeaderClick}
frameWrapper={frameWrapper}
isFragmentVisible={isFragmentVisible}
hasFragment={hasFragment}
errorId={errorId}
/>

<div className="overflow-y-auto p-3 font-mono text-xs leading-5 bg-gray-100 rounded language-javascript hljs">
<pre>
{(frameWrapper.showOriginal
? frameWrapper.frame.originalSourceFragment
: frameWrapper.frame.sourceFragment
)?.map(line => (
<div
key={line.line}
className={`flex items-center ${
line.highlight ? 'bg-red-500 bg-opacity-25' : ''
}`}>
{line.highlight && (
<span className="text-red-500">
<ChevronIcon
className="w-4 h-4"
style={{
marginLeft: '-1px'
{isFragmentVisible && (
<div className="overflow-y-auto py-3 text-sm leading-6 rounded-b-xl text-slate-50 bg-slate-700">
<pre>
<code>
{(frameWrapper.showOriginal
? frame.originalSourceFragment
: frame.sourceFragment
)?.map(line => (
<div
key={line.line}
className={`flex items-center border-l-4 ${
line.highlight
? 'bg-rose-500/20 border-rose-500'
: 'border-transparent'
}`}>
<div className="pr-3 pl-3 mr-3 border-r-2 text-slate-400 border-slate-600/75">
{line.line}
</div>
<div
dangerouslySetInnerHTML={{
__html: PrismJS.highlight(line.source, grammar, language)
}}
/>
</span>
)}
<span className="pr-2 border-r border-gray-500">
{line.highlight ? line.line : ` ${line.line}`}
</span>
<div
className="pl-2"
dangerouslySetInnerHTML={{
__html: hljs.highlight(line.source, {
language: 'javascript'
}).value
}}></div>
</div>
))}
</pre>
</div>
</div>
))}
</code>
</pre>
</div>
)}
</div>
);
};

const Frame = memo(FrameBase);
export { Frame, FrameBase };
export { Frame, getPrismLanguage };
export default memo(Frame);
Loading

0 comments on commit a059078

Please sign in to comment.