headplane/app/routes/acls/components/cm.client.tsx

113 lines
3 KiB
TypeScript
Raw Normal View History

2024-12-31 10:30:14 +05:30
import * as shopify from '@shopify/lang-jsonc';
2025-02-04 11:42:57 -05:00
import { xcodeDark, xcodeLight } from '@uiw/codemirror-theme-xcode';
2025-01-28 17:46:16 -05:00
import CodeMirror from '@uiw/react-codemirror';
2025-02-04 11:42:57 -05:00
import { BookCopy, CircleX } from 'lucide-react';
import { useEffect, useState } from 'react';
2025-01-28 17:46:16 -05:00
import Merge from 'react-codemirror-merge';
import { ErrorBoundary } from 'react-error-boundary';
import { ClientOnly } from 'remix-utils/client-only';
2024-12-31 10:30:14 +05:30
import Fallback from './fallback';
interface EditorProps {
2024-12-31 10:30:14 +05:30
isDisabled?: boolean;
value: string;
onChange: (value: string) => void;
}
2025-04-03 03:00:11 -04:00
// TODO: Remove ClientOnly
export function Editor(props: EditorProps) {
2024-12-31 10:30:14 +05:30
const [light, setLight] = useState(false);
useEffect(() => {
2024-12-31 10:30:14 +05:30
const theme = window.matchMedia('(prefers-color-scheme: light)');
setLight(theme.matches);
theme.addEventListener('change', (theme) => {
2024-12-31 10:30:14 +05:30
setLight(theme.matches);
});
});
return (
2025-02-04 11:42:57 -05:00
<div className="overflow-y-scroll h-editor text-sm">
<ErrorBoundary
fallback={
<div className="flex flex-col items-center gap-2.5 py-8">
<CircleX />
<p className="text-lg font-semibold">Failed to load the editor.</p>
</div>
}
>
<ClientOnly fallback={<Fallback acl={props.value} />}>
{() => (
<CodeMirror
value={props.value}
2025-04-03 03:00:11 -04:00
editable={!props.isDisabled} // Allow editing unless disabled
readOnly={props.isDisabled} // Use readOnly if disabled
2025-02-04 11:42:57 -05:00
height="100%"
extensions={[shopify.jsonc()]}
style={{ height: '100%' }}
theme={light ? xcodeLight : xcodeDark}
onChange={(value) => props.onChange(value)}
/>
)}
</ClientOnly>
</ErrorBoundary>
</div>
2024-12-31 10:30:14 +05:30
);
}
interface DifferProps {
2024-12-31 10:30:14 +05:30
left: string;
right: string;
}
export function Differ(props: DifferProps) {
2024-12-31 10:30:14 +05:30
const [light, setLight] = useState(false);
useEffect(() => {
2024-12-31 10:30:14 +05:30
const theme = window.matchMedia('(prefers-color-scheme: light)');
setLight(theme.matches);
theme.addEventListener('change', (theme) => {
2024-12-31 10:30:14 +05:30
setLight(theme.matches);
});
});
return (
2025-02-04 11:42:57 -05:00
<div className="text-sm">
{props.left === props.right ? (
<div className="flex flex-col items-center gap-2.5 py-8">
<BookCopy />
<p className="text-lg font-semibold">No changes</p>
</div>
) : (
<div className="h-editor overflow-y-scroll">
2024-12-31 10:30:14 +05:30
<ErrorBoundary
fallback={
2025-02-04 11:42:57 -05:00
<div className="flex flex-col items-center gap-2.5 py-8">
<CircleX />
<p className="text-lg font-semibold">
Failed to load the editor.
</p>
</div>
2024-12-31 10:30:14 +05:30
}
>
<ClientOnly fallback={<Fallback acl={props.right} />}>
{() => (
2025-02-04 11:42:57 -05:00
<Merge orientation="a-b" theme={light ? xcodeLight : xcodeDark}>
2024-12-31 10:30:14 +05:30
<Merge.Original
readOnly
value={props.left}
extensions={[shopify.jsonc()]}
/>
<Merge.Modified
readOnly
value={props.right}
extensions={[shopify.jsonc()]}
/>
</Merge>
)}
</ClientOnly>
</ErrorBoundary>
2025-02-04 11:42:57 -05:00
</div>
)}
</div>
2024-12-31 10:30:14 +05:30
);
}