Skip to content

Commit

Permalink
feat: Support mobile view better (casbin#144)
Browse files Browse the repository at this point in the history
* refactor: improve layout and responsiveness in editor component

* feat: add custom configuration toggle and improve layout responsiveness

* refactor: remove commented-out code in EditorScreen component

* docs: update title in metadata

* style: adjust padding in editor component
  • Loading branch information
HashCookie authored Jul 23, 2024
1 parent 09095ed commit 6446634
Show file tree
Hide file tree
Showing 3 changed files with 165 additions and 130 deletions.
282 changes: 159 additions & 123 deletions app/components/editor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export const EditorScreen = () => {
data: enforceContextData,
});
const [casbinVersion, setCasbinVersion] = useState('');
const [showCustomConfig, setShowCustomConfig] = useState(false);
const sidePanelChatRef = useRef<{ openDrawer: (message: string) => void } | null>(null);
const openDrawerWithMessage = (message: string) => {
if (sidePanelChatRef.current) {
Expand Down Expand Up @@ -90,23 +91,31 @@ export const EditorScreen = () => {
const textClass = clsx(theme === 'dark' ? 'text-gray-200' : 'text-gray-800');

return (
<div className="flex flex-row h-full">
<div className={clsx(open ? 'w-72' : 'w-5', 'relative', 'pl-2 pr-2 border-r border-[#dddddd]')}>
<div className="flex flex-col sm:flex-row h-full">
<div
className={clsx('sm:relative', 'pl-0 sm:pl-2 pr-0 sm:pr-2 border-r border-[#dddddd]', 'transition-all duration-300', {
'hidden sm:block': !showCustomConfig,
block: showCustomConfig,
'sm:w-72': open,
'sm:w-5': !open,
})}
>
<div className="flex flex-col h-full">
<button
className={clsx(
'absolute top-.5 right-0 translate-x-1/2',
'h-7 w-7',
'bg-[#ffffff]',
'border-[1.5px] rounded-full',
'flex items-center justify-center',
'items-center justify-center',
'hidden sm:flex',
)}
onClick={() => {
return setOpen(!open);
}}
>
<svg
className={clsx('h-8 w-8', '')}
className={clsx('h-8 w-8')}
style={{
transform: open ? 'rotateZ(0deg)' : 'rotateZ(180deg)',
}}
Expand All @@ -116,9 +125,11 @@ export const EditorScreen = () => {
</svg>
</button>

<div className={'pt-6 h-12 flex items-center font-bold'}>{open && <div className={textClass}>{t('Custom config')}</div>}</div>
<div className={'pt-6 h-12 pl-2 flex items-center font-bold'}>
{(showCustomConfig || open) && <div className={textClass}>{t('Custom config')}</div>}
</div>
<div className="flex-grow overflow-auto h-full">
{open && (
{(showCustomConfig || open) && (
<div className="flex flex-col h-full">
<CodeMirror
height="100%"
Expand All @@ -139,8 +150,8 @@ export const EditorScreen = () => {
</div>
</div>
</div>
<div className={clsx('flex flex-col grow h-full')}>
<div className="flex flex-row gap-1 pt-4 flex-1 overflow-hidden">
<div className={clsx('flex flex-col grow h-full w-full')}>
<div className="flex flex-col sm:flex-row gap-1 pt-4 flex-1 overflow-hidden">
<div className="flex-1 flex flex-col h-full overflow-hidden">
<div className={clsx('h-10 pl-2', 'flex items-center justify-start gap-2')}>
<div className={clsx(textClass, 'font-bold')}>{t('Model')}</div>
Expand Down Expand Up @@ -181,7 +192,34 @@ export const EditorScreen = () => {
>
{t('RESET')}
</button>
<div className="sm:hidden ml-auto mr-2">
<button
className={clsx(
'rounded',
'flex items-center justify-center',
'border border-[#453d7d]',
'text-[#453d7a]',
'bg-[#efefef]',
'hover:bg-[#453d7d] hover:text-white',
'transition-colors duration-500',
)}
onClick={() => {
return setShowCustomConfig(!showCustomConfig);
}}
>
<svg
className={clsx('h-6 w-6')}
style={{
transform: showCustomConfig ? 'rotateZ(90deg)' : 'rotateZ(-90deg)',
}}
viewBox="0 0 24 24"
>
<path fill={'currentColor'} d="M15.41,16.58L10.83,12L15.41,7.41L14,6L8,12L14,18L15.41,16.58Z" />
</svg>
</button>
</div>
</div>

<div className="flex-grow overflow-auto h-full">
<div className="flex flex-col h-full">
<CodeMirror
Expand All @@ -201,14 +239,14 @@ export const EditorScreen = () => {
EditorView.lineWrapping,
buttonPlugin(openDrawerWithMessage, extractContent, 'model'),
]}
className={'function flex-grow'}
className={'function flex-grow h-[300px]'}
value={modelText}
/>
</div>
</div>
</div>
<div className="flex-1 flex flex-col h-full overflow-hidden">
<div className="h-10 font-bold flex items-center justify-between">
<div className="h-10 pl-2 font-bold flex items-center justify-between">
<div className={textClass}>{t('Policy')}</div>
<div className="text-right font-bold mr-4 text-sm text-[#e13c3c]">
<a href={`https://github.com/casbin/node-casbin/releases/tag/v${casbinVersion}`} target="_blank" rel="noopener noreferrer">
Expand All @@ -235,14 +273,14 @@ export const EditorScreen = () => {
}}
theme={monokai}
onChange={setPolicyPersistent}
className={'function flex-grow '}
className={'function flex-grow h-[300px]'}
value={policy}
/>
</div>
</div>
</div>
</div>
<div className="flex flex-row gap-1 pt-2 flex-1 overflow-hidden">
<div className="flex flex-col sm:flex-row gap-1 pt-2 flex-1 overflow-hidden">
<div className="flex-1 flex flex-col h-full overflow-hidden">
<div className={clsx('h-10 pl-2', 'flex items-center justify-start gap-3')}>
<div className={clsx(textClass, 'font-bold')}>{t('Request')}</div>
Expand Down Expand Up @@ -302,14 +340,14 @@ export const EditorScreen = () => {
bracketMatching: true,
indentOnInput: true,
}}
className={'function flex-grow '}
className={'function flex-grow h-[300px]'}
value={request}
/>
</div>
</div>
</div>
<div className="flex-1 flex flex-col h-full overflow-hidden">
<div className={clsx('h-10 font-bold', 'flex items-center justify-between')}>
<div className={clsx('h-10 pl-2 font-bold', 'flex items-center justify-between')}>
<div className={textClass}>{t('Enforcement Result')}</div>
<div className="mr-4">
<SidePanelChat ref={sidePanelChatRef} />
Expand Down Expand Up @@ -337,76 +375,74 @@ export const EditorScreen = () => {
bracketMatching: true,
indentOnInput: true,
}}
className={'cursor-not-allowed flex-grow'}
className={'cursor-not-allowed flex-grow h-[300px]'}
value={requestResult}
/>
</div>
</div>
</div>
</div>
<div className={clsx('pt-2 px-1 flex items-center')}>
<button
className={clsx(
'rounded',
'px-2 py-1',
'border border-[#453d7d]',
'bg-[#efefef]',
'text-[#453d7a]',
'hover:bg-[#453d7d] hover:text-white',
'transition-colors duration-500',
)}
style={{ marginRight: 8 }}
onClick={() => {
try {
Config.newConfigFromText(modelText);
setEcho(<div>Passed</div>);
} catch (e) {
setEcho(<div>{(e as any).message}</div>);
}
}}
>
{t('SYNTAX VALIDATE')}
</button>
<button
className={clsx(
'rounded',
'px-2 py-1',
'border border-[#453d7d]',
'text-[#453d7a]',
'bg-[#efefef]',
'hover:bg-[#453d7d] hover:text-white',
'transition-colors duration-500',
)}
style={{ marginRight: 8 }}
onClick={() => {
return enforcer({
modelKind,
model: modelText,
policy,
customConfig,
request,
enforceContextData,
onResponse: (v) => {
if (isValidElement(v)) {
setEcho(v);
} else if (Array.isArray(v)) {
const formattedResults = v.map((res) => {
if (typeof res === 'object') {
const reasonString = Array.isArray(res.reason) && res.reason.length > 0 ? ` Reason: ${JSON.stringify(res.reason)}` : '';
return `${res.okEx}${reasonString}`;
}
return res;
});
setRequestResult(formattedResults.join('\n'));
}
},
});
}}
>
{t('RUN THE TEST')}
</button>
{!share ? (
<span>
<div className={clsx('pt-2 px-1 flex flex-col sm:flex-row items-start sm:items-center')}>
<div className="flex flex-row flex-wrap gap-2 mb-2 sm:mb-0 w-full sm:w-auto">
<button
className={clsx(
'rounded',
'px-2 py-1',
'border border-[#453d7d]',
'bg-[#efefef]',
'text-[#453d7a]',
'hover:bg-[#453d7d] hover:text-white',
'transition-colors duration-500',
)}
onClick={() => {
try {
Config.newConfigFromText(modelText);
setEcho(<div>Passed</div>);
} catch (e) {
setEcho(<div>{(e as any).message}</div>);
}
}}
>
{t('SYNTAX VALIDATE')}
</button>
<button
className={clsx(
'rounded',
'px-2 py-1',
'border border-[#453d7d]',
'text-[#453d7a]',
'bg-[#efefef]',
'hover:bg-[#453d7d] hover:text-white',
'transition-colors duration-500',
)}
onClick={() => {
return enforcer({
modelKind,
model: modelText,
policy,
customConfig,
request,
enforceContextData,
onResponse: (v) => {
if (isValidElement(v)) {
setEcho(v);
} else if (Array.isArray(v)) {
const formattedResults = v.map((res) => {
if (typeof res === 'object') {
const reasonString = Array.isArray(res.reason) && res.reason.length > 0 ? ` Reason: ${JSON.stringify(res.reason)}` : '';
return `${res.okEx}${reasonString}`;
}
return res;
});
setRequestResult(formattedResults.join('\n'));
}
},
});
}}
>
{t('RUN THE TEST')}
</button>
{!share ? (
<button
className={clsx(
'rounded',
Expand All @@ -417,7 +453,6 @@ export const EditorScreen = () => {
'hover:bg-[#453d7d] hover:text-white',
'transition-colors duration-500',
)}
style={{ marginRight: 8 }}
onClick={() => {
return shareInfo({
onResponse: (v) => {
Expand All @@ -434,51 +469,52 @@ export const EditorScreen = () => {
>
{t('SHARE')}
</button>
</span>
) : (
<button
className={clsx(
'rounded',
'px-2 py-1',
'border border-[#453d7d]',
'text-[#453d7a]',
'bg-[#efefef]',
'hover:bg-[#453d7d] hover:text-white',
'transition-colors duration-500',
)}
style={{ marginRight: 8 }}
onClick={() => {
return copy(
() => {
setShare('');
setEcho(<div>{t('Copied')}</div>);
},
`${window.location.origin + window.location.pathname}#${share}`,
);
}}
>
{t('COPY')}
</button>
)}
<div style={{ display: 'inline-block', marginRight: 'auto' }} className={textClass}>
{echo}
</div>
<div className="mr-3">
<button
onClick={toggleTheme}
aria-label={theme !== 'dark' ? 'Switch to light mode' : 'Switch to dark mode'}
className="theme-toggle-button mr-2"
>
<img
src={theme !== 'dark' ? 'sun.svg' : 'moon.svg'}
alt={theme !== 'dark' ? 'Light mode' : 'Dark mode'}
className="w-6 h-6 transition-opacity duration-300"
style={{
filter: theme === 'dark' ? 'invert(1)' : 'invert(0)',
) : (
<button
className={clsx(
'rounded',
'px-2 py-1',
'border border-[#453d7d]',
'text-[#453d7a]',
'bg-[#efefef]',
'hover:bg-[#453d7d] hover:text-white',
'transition-colors duration-500',
)}
onClick={() => {
return copy(
() => {
setShare('');
setEcho(<div>{t('Copied')}</div>);
},
`${window.location.origin + window.location.pathname}#${share}`,
);
}}
/>
</button>
<LanguageMenu />
>
{t('COPY')}
</button>
)}
</div>

<div className="flex flex-row justify-between items-center w-full sm:w-auto sm:ml-auto mt-2 sm:mt-0">
<div className={clsx(textClass)}>{echo}</div>

<div className="flex flex-row items-center ml-auto sm:ml-3">
<button
onClick={toggleTheme}
aria-label={theme !== 'dark' ? 'Switch to light mode' : 'Switch to dark mode'}
className="theme-toggle-button mr-2"
>
<img
src={theme !== 'dark' ? 'sun.svg' : 'moon.svg'}
alt={theme !== 'dark' ? 'Light mode' : 'Dark mode'}
className="w-6 h-6 transition-opacity duration-300"
style={{
filter: theme === 'dark' ? 'invert(1)' : 'invert(0)',
}}
/>
</button>
<LanguageMenu />
</div>
</div>
</div>
</div>
Expand Down
Loading

0 comments on commit 6446634

Please sign in to comment.