Skip to content

Commit 98b5d42

Browse files
authored
Merge pull request #689 from code0-tech/feat/#687
EditorInput with input wrapper
2 parents b0cddc9 + 9285854 commit 98b5d42

6 files changed

Lines changed: 160 additions & 7 deletions

File tree

src/components/editor/Editor.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export interface EditorTokenHighlights {
5454
[tokenName: string]: (props: EditorRendererProps) => React.ReactNode
5555
}
5656

57-
export interface EditorInputProps extends Omit<Component<HTMLDivElement>, 'onChange' | 'defaultValue' | 'value'>, ValidationProps<any> {
57+
export interface EditorProps extends Omit<Component<HTMLDivElement>, 'onChange' | 'defaultValue' | 'value'>, ValidationProps<any> {
5858
language?: 'json' | StreamLanguage<unknown>
5959
tokenizer?: EditorTokenizer
6060
tokenHighlights?: EditorTokenHighlights
@@ -105,7 +105,7 @@ const isReactNode = (value: any): value is React.ReactNode => {
105105
return isValidElement(value) || typeof value === 'string' || typeof value === 'number' || Array.isArray(value);
106106
}
107107

108-
export const Editor: React.FC<EditorInputProps> = (props) => {
108+
export const Editor: React.FC<EditorProps> = (props) => {
109109
const {
110110
language,
111111
tokenizer,
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
.editor-input {
2+
width: 100%;
3+
flex: 1 1 auto;
4+
overflow: hidden;
5+
padding: 0.59rem 0;
6+
position: relative;
7+
align-self: stretch;
8+
9+
.cm-editor {
10+
height: 100%;
11+
outline: none !important;
12+
}
13+
14+
.cm-content {
15+
padding: 0;
16+
position: relative;
17+
}
18+
19+
.cm-line {
20+
align-self: stretch;
21+
padding-left: 0;
22+
}
23+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import React from "react";
2+
import {ValidationProps} from "./useForm";
3+
import {InputWrapper, InputWrapperProps} from "./InputWrapper";
4+
import {CompletionContext, CompletionResult} from "@codemirror/autocomplete";
5+
import CodeMirror, {Extension} from "@uiw/react-codemirror";
6+
import {StreamLanguage, TagStyle} from "@codemirror/language";
7+
import {createTheme} from "@uiw/codemirror-themes";
8+
import {tags as t} from "@lezer/highlight";
9+
import {hashToColor, mergeComponentProps} from "../../utils";
10+
import "./EditorInput.style.scss"
11+
12+
export interface EditorInputProps extends Omit<InputWrapperProps, 'onChange'>, ValidationProps<any> {
13+
language?: StreamLanguage<unknown>
14+
suggestions?: (context: CompletionContext) => CompletionResult
15+
extensions?: Extension[]
16+
disabled?: boolean
17+
readonly?: boolean
18+
tokenStyles?: TagStyle[]
19+
onChange?: (value: string) => void
20+
}
21+
22+
export const EditorInput: React.FC<EditorInputProps> = (props) => {
23+
24+
const {title, right, left, rightType, leftType, language, description, extensions = [], tokenStyles = [], formValidation, onChange, ...rest} = props
25+
26+
const internalExtensions: Extension[] = [...extensions, language!]
27+
28+
const myTheme = React.useMemo(
29+
() => createTheme({
30+
theme: 'light',
31+
settings: {
32+
background: 'transparent',
33+
backgroundImage: '',
34+
foreground: 'rgba(255,255,255, 0.75)',
35+
caret: 'gray',
36+
selection: 'rgba(112,179,255,0.25)',
37+
selectionMatch: 'rgba(112,179,255,0.1)',
38+
fontSize: "0.8rem",
39+
gutterBackground: 'transparent',
40+
gutterForeground: 'rgba(255,255,255, 0.5)',
41+
gutterBorder: 'transparent',
42+
gutterActiveForeground: 'rgba(255,255,255, 1)',
43+
lineHighlight: 'rgba(255,255,255, 0.1)',
44+
},
45+
styles: [
46+
{tag: t.squareBracket, color: hashToColor("squareBracket")},
47+
{tag: t.bracket, color: hashToColor("bracket")},
48+
{tag: t.string, color: hashToColor("Text")},
49+
{tag: t.bool, color: hashToColor("Boolean")},
50+
{tag: t.number, color: hashToColor("Number")},
51+
...tokenStyles
52+
]
53+
}),
54+
[tokenStyles]
55+
)
56+
57+
return <InputWrapper title={title}
58+
description={description}
59+
right={right}
60+
left={left}
61+
rightType={rightType}
62+
leftType={leftType}
63+
formValidation={formValidation}>
64+
65+
<CodeMirror extensions={internalExtensions} onChange={value => {
66+
onChange?.(value)
67+
formValidation?.setValue(value)
68+
}} theme={myTheme} {...mergeComponentProps("editor-input", rest)} basicSetup={{
69+
lineNumbers: false,
70+
foldGutter: false,
71+
highlightActiveLine: false,
72+
highlightActiveLineGutter: false,
73+
}}/>
74+
</InputWrapper>
75+
}

src/components/form/Input.stories.tsx

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ import {
2626
} from "./SelectInput";
2727
import {Flex} from "../flex/Flex";
2828
import {ButtonGroup} from "../button-group/ButtonGroup";
29+
import {EditorInput} from "./EditorInput";
30+
import {StreamLanguage} from "@codemirror/language";
31+
import {tags as t} from "@lezer/highlight";
32+
import {hashToColor} from "../../utils";
2933

3034
export default {
3135
title: "Form"
@@ -380,4 +384,47 @@ export const Select = () => {
380384
</SelectPortal>
381385
</SelectInput>
382386
</Card>
387+
}
388+
389+
export const Editor = () => {
390+
391+
392+
const [inputs, validate] = useForm({
393+
initialValues: {
394+
editor: undefined
395+
},
396+
validate: {
397+
editor: (value) => {
398+
if (!value) return "Please type something"
399+
return null
400+
}
401+
},
402+
onSubmit: (values) => {
403+
console.log(values)
404+
}
405+
})
406+
407+
return <Card color={"secondary"} w={"400px"}>
408+
<EditorInput {...inputs.getInputProps("editor")} onChange={() => validate("editor")} placeholder={"sd"} language={StreamLanguage.define({
409+
token(stream) {
410+
if (stream.match(/\{\{\s*(.*?)\s*\}\}/)) {
411+
return "keyword";
412+
}
413+
414+
stream.next();
415+
return null;
416+
}
417+
})} tokenStyles={[
418+
{tag: t.keyword, color: hashToColor("bracket")},
419+
]} title={"Bla"} description={"test"} right={
420+
<ButtonGroup color={"primary"}>
421+
<Button paddingSize={"xxs"}>
422+
<IconVariable size={13}/>
423+
</Button>
424+
<Button paddingSize={"xxs"}>
425+
<IconX size={13}/>
426+
</Button>
427+
</ButtonGroup>
428+
} rightType={"action"}/>
429+
</Card>
383430
}

src/components/form/InputWrapper.style.scss

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,9 @@
99
display: flex;
1010
z-index: 1;
1111
width: 100%;
12-
align-items: center;
12+
align-items: start;
1313
box-sizing: border-box;
14-
gap: $padding;
15-
padding: 0 $padding / 4 0 $padding / 2;
14+
gap: $padding / 4;
1615

1716
& {
1817
@include box.box(variables.$tertiary, variables.$white, variables.$white);
@@ -30,11 +29,17 @@
3029
@include box.box(variables.$error, variables.$white, variables.$error);
3130
}
3231

32+
& > :first-child {
33+
padding-left: $padding;
34+
}
35+
3336
&__left, &__right {
3437
display: flex;
35-
align-items: stretch;
38+
align-items: center;
39+
justify-content: center;
40+
min-height: 30px;
3641
gap: $padding;
37-
margin: $padding / 4 0;
42+
margin: $padding / 4 $padding / 4;
3843

3944
> button {
4045
height: 100%;
@@ -59,6 +64,8 @@
5964
&--icon {
6065
align-items: center;
6166
}
67+
&--placeholder {
68+
}
6269
}
6370

6471
&__right {

src/components/form/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export * from "./CheckboxInput"
2+
export * from "./EditorInput"
23
export * from "./EmailInput"
34
export * from "./Input"
45
export * from "./InputDescription"

0 commit comments

Comments
 (0)