mirror of https://github.com/buster-so/buster.git
diff component v1
This commit is contained in:
parent
c76bd59622
commit
4167bb439d
|
@ -1,7 +1,13 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
import type { editor } from 'monaco-editor';
|
import type { editor } from 'monaco-editor';
|
||||||
|
|
||||||
const editorBackground = '#ffffff';
|
const editorBackground = getComputedStyle(document.documentElement).getPropertyValue(
|
||||||
const primaryColor = '#7C3AED';
|
'--color-background'
|
||||||
|
);
|
||||||
|
const primaryColor = getComputedStyle(document.documentElement).getPropertyValue('--color-primary');
|
||||||
|
const borderColor = getComputedStyle(document.documentElement).getPropertyValue('--color-border');
|
||||||
|
const textColor = getComputedStyle(document.documentElement).getPropertyValue('--color-foreground');
|
||||||
|
|
||||||
const theme: editor.IStandaloneThemeData = {
|
const theme: editor.IStandaloneThemeData = {
|
||||||
base: 'vs',
|
base: 'vs',
|
||||||
|
@ -356,7 +362,11 @@ const theme: editor.IStandaloneThemeData = {
|
||||||
'editorIndentGuide.background': '#959da5',
|
'editorIndentGuide.background': '#959da5',
|
||||||
'editorIndentGuide.activeBackground': '#24292e',
|
'editorIndentGuide.activeBackground': '#24292e',
|
||||||
'editor.selectionHighlightBorder': '#fafbfc',
|
'editor.selectionHighlightBorder': '#fafbfc',
|
||||||
focusBorder: '#00000000'
|
focusBorder: '#00000000',
|
||||||
|
'editorHoverWidget.background': editorBackground,
|
||||||
|
'editorHoverWidget.border': borderColor,
|
||||||
|
'inputValidation.infoBorder': borderColor,
|
||||||
|
'editorHoverWidget.foreground': textColor
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,160 @@
|
||||||
|
import type { Meta, StoryObj } from '@storybook/react';
|
||||||
|
import { AppDiffCodeEditor } from './AppDiffCodeEditor';
|
||||||
|
|
||||||
|
const meta: Meta<typeof AppDiffCodeEditor> = {
|
||||||
|
title: 'UI/Inputs/AppDiffCodeEditor',
|
||||||
|
component: AppDiffCodeEditor,
|
||||||
|
parameters: {
|
||||||
|
layout: 'centered'
|
||||||
|
},
|
||||||
|
tags: ['autodocs'],
|
||||||
|
argTypes: {
|
||||||
|
viewMode: {
|
||||||
|
control: 'radio',
|
||||||
|
options: ['side-by-side', 'inline'],
|
||||||
|
defaultValue: 'side-by-side',
|
||||||
|
description: 'Controls whether the diff is displayed side-by-side or inline'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
decorators: [
|
||||||
|
(Story) => (
|
||||||
|
<div className="min-h-[500px] min-w-[1000px]">
|
||||||
|
<Story />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
type Story = StoryObj<typeof AppDiffCodeEditor>;
|
||||||
|
|
||||||
|
const originalYaml = `# Original YAML configuration
|
||||||
|
server:
|
||||||
|
port: 8080
|
||||||
|
host: localhost
|
||||||
|
database:
|
||||||
|
url: jdbc:mysql://localhost:3306/mydb
|
||||||
|
username: admin
|
||||||
|
password: secret
|
||||||
|
logging:
|
||||||
|
level: INFO
|
||||||
|
path: /var/logs`;
|
||||||
|
|
||||||
|
const modifiedYaml = `# Updated YAML configuration
|
||||||
|
server:
|
||||||
|
port: 9090
|
||||||
|
host: localhost
|
||||||
|
timeout: 30s
|
||||||
|
database:
|
||||||
|
url: jdbc:mysql://localhost:3306/mydb
|
||||||
|
username: admin
|
||||||
|
password: secret
|
||||||
|
pool:
|
||||||
|
maxConnections: 20
|
||||||
|
minIdle: 5
|
||||||
|
logging:
|
||||||
|
level: DEBUG
|
||||||
|
path: /var/logs/app`;
|
||||||
|
|
||||||
|
const originalSql = `-- Original SQL query
|
||||||
|
SELECT
|
||||||
|
customers.id,
|
||||||
|
customers.name,
|
||||||
|
orders.order_date
|
||||||
|
FROM customers
|
||||||
|
JOIN orders ON customers.id = orders.customer_id
|
||||||
|
WHERE orders.status = 'completed'
|
||||||
|
ORDER BY orders.order_date DESC;`;
|
||||||
|
|
||||||
|
const modifiedSql = `-- Updated SQL query
|
||||||
|
SELECT
|
||||||
|
customers.id,
|
||||||
|
customers.name,
|
||||||
|
customers.email,
|
||||||
|
orders.order_date,
|
||||||
|
orders.total_amount
|
||||||
|
FROM customers
|
||||||
|
JOIN orders ON customers.id = orders.customer_id
|
||||||
|
LEFT JOIN order_items ON orders.id = order_items.order_id
|
||||||
|
WHERE orders.status = 'completed'
|
||||||
|
AND orders.total_amount > 100
|
||||||
|
GROUP BY customers.id
|
||||||
|
ORDER BY orders.order_date DESC
|
||||||
|
LIMIT 100;`;
|
||||||
|
|
||||||
|
export const Default: Story = {
|
||||||
|
args: {
|
||||||
|
original: originalYaml,
|
||||||
|
modified: modifiedYaml,
|
||||||
|
height: '300px',
|
||||||
|
language: 'yaml',
|
||||||
|
variant: 'bordered',
|
||||||
|
viewMode: 'side-by-side'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const InlineView: Story = {
|
||||||
|
args: {
|
||||||
|
original: originalYaml,
|
||||||
|
modified: modifiedYaml,
|
||||||
|
height: '300px',
|
||||||
|
language: 'yaml',
|
||||||
|
variant: 'bordered',
|
||||||
|
viewMode: 'inline'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SQL: Story = {
|
||||||
|
args: {
|
||||||
|
original: originalSql,
|
||||||
|
modified: modifiedSql,
|
||||||
|
height: '300px',
|
||||||
|
language: 'sql',
|
||||||
|
variant: 'bordered',
|
||||||
|
viewMode: 'side-by-side'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SQLInline: Story = {
|
||||||
|
args: {
|
||||||
|
original: originalSql,
|
||||||
|
modified: modifiedSql,
|
||||||
|
height: '300px',
|
||||||
|
language: 'sql',
|
||||||
|
variant: 'bordered',
|
||||||
|
viewMode: 'inline'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ReadOnly: Story = {
|
||||||
|
args: {
|
||||||
|
original: originalYaml,
|
||||||
|
modified: modifiedYaml,
|
||||||
|
height: '300px',
|
||||||
|
language: 'yaml',
|
||||||
|
readOnly: true,
|
||||||
|
variant: 'bordered',
|
||||||
|
readOnlyMessage: 'This is a read-only view',
|
||||||
|
viewMode: 'side-by-side'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const TallerView: Story = {
|
||||||
|
args: {
|
||||||
|
original: originalSql,
|
||||||
|
modified: modifiedSql,
|
||||||
|
height: '500px',
|
||||||
|
language: 'sql',
|
||||||
|
variant: 'bordered',
|
||||||
|
viewMode: 'side-by-side'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const EmptyEditor: Story = {
|
||||||
|
args: {
|
||||||
|
height: '300px',
|
||||||
|
language: 'yaml',
|
||||||
|
variant: 'bordered',
|
||||||
|
viewMode: 'side-by-side'
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,138 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import React, { forwardRef, useRef, useMemo } from 'react';
|
||||||
|
import { CircleSpinnerLoaderContainer } from '../../loaders/CircleSpinnerLoaderContainer';
|
||||||
|
import { useMemoizedFn } from '@/hooks';
|
||||||
|
import { cn } from '@/lib/classMerge';
|
||||||
|
import type { editor } from 'monaco-editor/esm/vs/editor/editor.api';
|
||||||
|
import { DiffEditor } from '@monaco-editor/react';
|
||||||
|
import { useTheme } from 'next-themes';
|
||||||
|
|
||||||
|
export interface AppDiffCodeEditorProps {
|
||||||
|
className?: string;
|
||||||
|
height?: string;
|
||||||
|
isDarkMode?: boolean;
|
||||||
|
onMount?: (editor: editor.IStandaloneDiffEditor, monaco: typeof import('monaco-editor')) => void;
|
||||||
|
original?: string;
|
||||||
|
modified?: string;
|
||||||
|
onChange?: (value: string) => void;
|
||||||
|
style?: React.CSSProperties;
|
||||||
|
language?: string;
|
||||||
|
readOnly?: boolean;
|
||||||
|
readOnlyMessage?: string;
|
||||||
|
monacoEditorOptions?: editor.IStandaloneDiffEditorConstructionOptions;
|
||||||
|
variant?: 'bordered' | null;
|
||||||
|
viewMode?: 'side-by-side' | 'inline';
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AppDiffCodeEditorHandle {
|
||||||
|
resetCodeEditor: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AppDiffCodeEditor = forwardRef<AppDiffCodeEditorHandle, AppDiffCodeEditorProps>(
|
||||||
|
(
|
||||||
|
{
|
||||||
|
style,
|
||||||
|
monacoEditorOptions,
|
||||||
|
language = 'typescript',
|
||||||
|
className,
|
||||||
|
readOnly,
|
||||||
|
onChange,
|
||||||
|
height = '100%',
|
||||||
|
isDarkMode,
|
||||||
|
onMount,
|
||||||
|
original = '',
|
||||||
|
modified = '',
|
||||||
|
readOnlyMessage = 'Editing code is not allowed',
|
||||||
|
variant,
|
||||||
|
viewMode = 'side-by-side'
|
||||||
|
},
|
||||||
|
ref
|
||||||
|
) => {
|
||||||
|
const isDarkModeContext = useTheme()?.theme === 'dark';
|
||||||
|
const useDarkMode = isDarkMode ?? isDarkModeContext;
|
||||||
|
|
||||||
|
const memoizedMonacoEditorOptions: editor.IStandaloneDiffEditorConstructionOptions =
|
||||||
|
useMemo(() => {
|
||||||
|
return {
|
||||||
|
originalEditable: false,
|
||||||
|
automaticLayout: true,
|
||||||
|
readOnly,
|
||||||
|
renderSideBySide: viewMode === 'side-by-side',
|
||||||
|
folding: false,
|
||||||
|
lineDecorationsWidth: 15,
|
||||||
|
lineNumbersMinChars: 3,
|
||||||
|
renderOverviewRuler: false,
|
||||||
|
wordWrap: 'off',
|
||||||
|
wordWrapColumn: 999,
|
||||||
|
wrappingStrategy: 'simple',
|
||||||
|
scrollBeyondLastLine: false,
|
||||||
|
minimap: {
|
||||||
|
enabled: false
|
||||||
|
},
|
||||||
|
contextmenu: false,
|
||||||
|
readOnlyMessage: {
|
||||||
|
value: readOnlyMessage
|
||||||
|
},
|
||||||
|
...monacoEditorOptions
|
||||||
|
};
|
||||||
|
}, [readOnlyMessage, monacoEditorOptions, viewMode]);
|
||||||
|
|
||||||
|
const onMountDiffEditor = useMemoizedFn(
|
||||||
|
async (editor: editor.IStandaloneDiffEditor, monaco: typeof import('monaco-editor')) => {
|
||||||
|
const [GithubLightTheme, NightOwlTheme] = await Promise.all([
|
||||||
|
(await import('../AppCodeEditor/themes/github_light_theme')).default,
|
||||||
|
(await import('../AppCodeEditor/themes/tomorrow_night_theme')).default
|
||||||
|
]);
|
||||||
|
|
||||||
|
monaco.editor.defineTheme('github-light', GithubLightTheme);
|
||||||
|
monaco.editor.defineTheme('night-owl', NightOwlTheme);
|
||||||
|
|
||||||
|
// Apply theme to diff editor
|
||||||
|
const theme = useDarkMode ? 'night-owl' : 'github-light';
|
||||||
|
monaco.editor.setTheme(theme);
|
||||||
|
|
||||||
|
console.log('theme', theme, GithubLightTheme);
|
||||||
|
|
||||||
|
// Get the modified editor and add change listener
|
||||||
|
const modifiedEditor = editor.getModifiedEditor();
|
||||||
|
if (!readOnly) {
|
||||||
|
modifiedEditor.onDidChangeModelContent(() => {
|
||||||
|
onChange?.(modifiedEditor.getValue() || '');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount?.(editor, monaco);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
'app-diff-code-editor relative h-full w-full',
|
||||||
|
variant === 'bordered' && 'overflow-hidden border',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
style={style}>
|
||||||
|
<DiffEditor
|
||||||
|
key={`${useDarkMode ? 'dark' : 'light'}-${viewMode}`}
|
||||||
|
height={height}
|
||||||
|
loading={<LoadingContainer />}
|
||||||
|
language={language}
|
||||||
|
className={className}
|
||||||
|
original={original}
|
||||||
|
modified={modified}
|
||||||
|
theme={useDarkMode ? 'night-owl' : 'github-light'}
|
||||||
|
onMount={onMountDiffEditor}
|
||||||
|
options={memoizedMonacoEditorOptions}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
AppDiffCodeEditor.displayName = 'AppDiffCodeEditor';
|
||||||
|
|
||||||
|
const LoadingContainer = React.memo(() => {
|
||||||
|
return <CircleSpinnerLoaderContainer className="animate-in fade-in-0 duration-300" />;
|
||||||
|
});
|
||||||
|
LoadingContainer.displayName = 'LoadingContainer';
|
|
@ -0,0 +1 @@
|
||||||
|
export { AppDiffCodeEditor } from './AppDiffCodeEditor';
|
|
@ -1,2 +1,3 @@
|
||||||
export * from './Input';
|
export * from './Input';
|
||||||
export * from './InputNumber';
|
export * from './InputNumber';
|
||||||
|
export * from './AppDiffCodeEditor';
|
||||||
|
|
Loading…
Reference in New Issue