Skip to content

Commit f01e161

Browse files
committed
import docs of packages from [email protected]; updated kit docs;
1 parent 703a872 commit f01e161

File tree

5 files changed

+357
-3
lines changed

5 files changed

+357
-3
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ CodeMirror v6 integration for React, with MUI widgets for [UI-Schema](https://gi
1212
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)
1313
![Typed](https://flat.badgen.net/badge/icon/Typed?icon=typescript&label&labelColor=blue&color=555555)
1414

15-
- [CodeMirror](https://codemirror.net/) v6 as plain React integration: `@ui-schema/kit-codemirror` [![npm (scoped)](https://img.shields.io/npm/v/@ui-schema/kit-codemirror?style=flat-square)](https://www.npmjs.com/package/@ui-schema/kit-codemirror) [![Component Documentation](https://img.shields.io/badge/Docs-green?labelColor=0a6e8a&color=61dafb&logoColor=ffffff&style=flat-square&logo=react)](https://ui-schema.bemit.codes/docs/kit-codemirror/kit-codemirror)
16-
- UI-Schema Widgets for [MUI](https://mui.com): `@ui-schema/material-code` [![npm (scoped)](https://img.shields.io/npm/v/@ui-schema/material-code?style=flat-square)](https://www.npmjs.com/package/@ui-schema/material-code) [![Component Documentation](https://img.shields.io/badge/Docs-green?labelColor=1a237e&color=0d47a1&logoColor=ffffff&style=flat-square&logo=mui)](https://ui-schema.bemit.codes/docs/material-code/material-code)
15+
- [CodeMirror](https://codemirror.net/) v6 as plain React integration: `@ui-schema/kit-codemirror` [![npm (scoped)](https://img.shields.io/npm/v/@ui-schema/kit-codemirror?style=flat-square)](https://www.npmjs.com/package/@ui-schema/kit-codemirror) [![Component Documentation](https://img.shields.io/badge/Docs-green?labelColor=0a6e8a&color=61dafb&logoColor=ffffff&style=flat-square&logo=react)](./docs/kit-codemirror/kit-codemirror.md)
16+
- UI-Schema Widgets for [MUI](https://mui.com): `@ui-schema/material-code` [![npm (scoped)](https://img.shields.io/npm/v/@ui-schema/material-code?style=flat-square)](https://www.npmjs.com/package/@ui-schema/material-code) [![Component Documentation](https://img.shields.io/badge/Docs-green?labelColor=1a237e&color=0d47a1&logoColor=ffffff&style=flat-square&logo=mui)](./docs/material-code/material-code.md)
1717

1818
## Demo
1919

docs/kit-codemirror/kit-codemirror.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# Kit CodeMirror
2+
3+
Thin-wrapper for CodeMirror v6 to use as React Component, with hooks and stuff to build more advanced editors easily.
4+
5+
```bash
6+
npm install --save @ui-schema/kit-codemirror @codemirror/state @codemirror/view
7+
8+
# doesn't require any other module of `@ui-schema`
9+
```
10+
11+
Contains only the minimal necessities for CodeMirror to work in ReactJS, nothing more.
12+
13+
- supports `value` as string
14+
- bind state changes with `onChange` - or omit it for a **read only editor**
15+
- add native CodeMirror extensions with props
16+
17+
> [!CAUTION]
18+
>
19+
> It is important to only supply safely referenced props! Always use `useMemo`/`useCallback` and so on for extensions, function and other complex values.
20+
>
21+
> Otherwise, the editors extension are unnecessarily destroyed and will cause significant performance degradation or even subtle bugs.
22+
>
23+
> For granular control of reactive extension reconfiguration, use the `useCodeMirror` and `useExtension` hooks, check the [`CodeMirror` base component](https://github.com/ui-schema/react-codemirror/blob/main/packages/kit-codemirror/src/CodeMirror/CodeMirror.tsx) as reference.
24+
25+
## Components
26+
27+
The `CodeMirror` component serves as read-to-use ReactJS integration.
28+
29+
Check the `CustomCodeMirror` in [`demo/src/pages/PageDemoComponent.tsx`](../../packages/demo/src/pages/PageDemoComponent.tsx) for an example.
30+
31+
## Hooks
32+
33+
### useCodeMirror
34+
35+
The `useCodeMirror` hook is used by the `CodeMirror` component and can be used to built more advanced customizations easily.
36+
37+
Check the [`CodeMirror` component as an example](../../packages/kit-codemirror/src/CodeMirror/CodeMirror.tsx).
38+
39+
### useExtension
40+
41+
The `useExtension` hook allows to define a setup function, which returns a configured CodeMirror extension.
42+
43+
> [!IMPORTANT]
44+
>
45+
> The `useExtension` hook can only be used when using the `useCodeMirror` hook, not with the `CodeMirror` component.
46+
47+
> [!IMPORTANT]
48+
>
49+
> The `useExtension` hook must be positioned directly after the `useCodeMirror` hook, it can not be used in parent components.
50+
51+
It automatically enables the extension and reconfigures it whenever the setup function changes. Always use `useCallback` for the setup function to prevent unnecessary reconfigurations.
52+
53+
The extension is added to the end of the editors configuration, to control the order use the `Prec` to set the precedence.
54+
55+
```tsx
56+
import { Prec } from '@codemirror/state'
57+
import { useExtension } from '@ui-schema/kit-codemirror/useExtension'
58+
59+
// must be called before using the `useExtension` hook
60+
const [editorRef] = useCodeMirror(/* ... */)
61+
62+
// the complete code for a "editorAttributes" extension, which keeps the `class` up to date
63+
useExtension(
64+
useCallback(() => {
65+
return Prec.lowest(EditorView.editorAttributes.of({class: classNameContent || ''}))
66+
}, [classNameContent]),
67+
editorRef,
68+
)
69+
```

docs/material-code/material-code.md

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
# DS-Material Code
2+
3+
Code editor widgets using CodeMirror, for UI-Schema and Material-UI.
4+
5+
[![Component Examples](https://img.shields.io/badge/Examples-green?labelColor=1d3d39&color=1a6754&logoColor=ffffff&style=flat-square)](#demo-ui-generator)
6+
7+
- type: `string`, `array`
8+
- widget keywords:
9+
- `Code` for single `format`
10+
- `CodeSelectable` for selectable `format: string[]` and data as `[format, code]` array tuple or `{lang, code}` object
11+
12+
>
13+
> 🚧 Work in progress, semi stable [#188](https://github.com/ui-schema/ui-schema/issues/188)
14+
>
15+
> For issues and more check the separate [ui-schema/react-codemirror repository](https://github.com/ui-schema/react-codemirror)
16+
17+
## Install
18+
19+
```bash
20+
npm install --save @ui-schema/ds-material @ui-schema/material-code @ui-schema/kit-codemirror @codemirror/state @codemirror/view @codemirror/language
21+
```
22+
23+
**Uses CodeMirror v6 since `0.4.0-beta.0`.**
24+
25+
## Widgets
26+
27+
**Keywords:**
28+
29+
- `view.hideTitle` when `true` it does not show the title, only the current format
30+
- `format` keyword to select the enabled language mode, `string` or `string[]`
31+
- uses translations: `formats.<schema.format>` for nicer labels, check [examples in docs](https://github.com/ui-schema/ui-schema/blob/master/packages/dictionary/src/en/formats.js)
32+
- only for `CodeSelectable`:
33+
- `formatDefault` keyword to specify the initial code-language without persisting it in the array
34+
- must be implemented in your custom widget wire-up
35+
36+
## Hooks
37+
38+
### useEditorTheme
39+
40+
Code editor theming, built using the current MUI theming context, makes it look similar to any other `TextField`.
41+
42+
**Params:**
43+
44+
- `readOnly`: when true, doesn't apply focus / interactive styles
45+
46+
```typescript jsx
47+
import { useEditorTheme } from '@ui-schema/material-code/useEditorTheme'
48+
49+
// in a component:
50+
const {onChange} = props
51+
const theme = useEditorTheme(typeof onChange === 'undefined')
52+
```
53+
54+
### useHighlightStyle
55+
56+
Syntax highlighting theming, built using the current MUI theming context, *not yet that optimized*.
57+
58+
```typescript jsx
59+
import { useHighlightStyle } from '@ui-schema/material-code/useHighlightStyle'
60+
61+
// in a component:
62+
const highlightStyle = useHighlightStyle()
63+
```
64+
65+
## Example
66+
67+
First create a `CustomCodeMirror` component, this component can be used to build the UI-Schema widgets and outside UI-Schema as pure CodeMirror v6 React integration:
68+
69+
```typescript jsx
70+
import React from 'react'
71+
import {
72+
lineNumbers, highlightActiveLineGutter, highlightSpecialChars,
73+
drawSelection, dropCursor,
74+
rectangularSelection, highlightActiveLine, keymap,
75+
} from '@codemirror/view'
76+
import { foldGutter, indentOnInput, syntaxHighlighting, defaultHighlightStyle, bracketMatching, foldKeymap } from '@codemirror/language'
77+
import { history, defaultKeymap, historyKeymap, indentWithTab } from '@codemirror/commands'
78+
import { highlightSelectionMatches, searchKeymap } from '@codemirror/search'
79+
import { closeBrackets, autocompletion, closeBracketsKeymap, completionKeymap } from '@codemirror/autocomplete'
80+
import { lintKeymap } from '@codemirror/lint'
81+
import { Compartment, EditorState } from '@codemirror/state'
82+
import { CodeMirrorComponentProps, CodeMirror, CodeMirrorProps } from '@ui-schema/kit-codemirror/CodeMirror'
83+
import { useEditorTheme } from '@ui-schema/material-code/useEditorTheme'
84+
import { useHighlightStyle } from '@ui-schema/material-code/useHighlightStyle'
85+
86+
export const CustomCodeMirror: React.FC<CodeMirrorComponentProps> = (
87+
{
88+
// values we want to override in this component
89+
value, extensions,
90+
// everything else is just passed down
91+
...props
92+
},
93+
) => {
94+
const {onChange} = props
95+
const theme = useEditorTheme(typeof onChange === 'undefined')
96+
const highlightStyle = useHighlightStyle()
97+
98+
const extensionsAll = React.useMemo(() => [
99+
lineNumbers(),
100+
highlightActiveLineGutter(),
101+
highlightSpecialChars(),
102+
history(),
103+
foldGutter(),
104+
drawSelection(),
105+
dropCursor(),
106+
EditorState.allowMultipleSelections.of(true),
107+
indentOnInput(),
108+
syntaxHighlighting(highlightStyle || defaultHighlightStyle, {fallback: true}),
109+
bracketMatching(),
110+
closeBrackets(),
111+
autocompletion(),
112+
rectangularSelection(),
113+
// crosshairCursor(),
114+
highlightActiveLine(),
115+
highlightSelectionMatches(),
116+
new Compartment().of(EditorState.tabSize.of(4)),
117+
keymap.of([
118+
...closeBracketsKeymap,
119+
...defaultKeymap,
120+
...searchKeymap,
121+
...historyKeymap,
122+
...foldKeymap,
123+
...completionKeymap,
124+
...lintKeymap,
125+
indentWithTab,
126+
]),
127+
theme,
128+
...(extensions || []),
129+
], [highlightStyle, extensions, theme])
130+
131+
const onViewLifecycle: CodeMirrorProps['onViewLifecycle'] = React.useCallback((view) => {
132+
console.log('on-view-lifecycle', view)
133+
}, [])
134+
135+
return <CodeMirror
136+
value={value || ''}
137+
extensions={extensionsAll}
138+
onViewLifecycle={onViewLifecycle}
139+
{...props}
140+
// className={className}
141+
/>
142+
}
143+
```
144+
145+
Then wire up the widgets and build your own widgets binding:
146+
147+
```typescript jsx
148+
import React from 'react'
149+
import {
150+
WidgetsBindingFactory,
151+
WidgetProps, WithScalarValue, memo, WithValue, StoreKeyType,
152+
} from '@ui-schema/ui-schema'
153+
import { MuiWidgetsBindingCustom, MuiWidgetsBindingTypes, widgets } from '@ui-schema/ds-material/widgetsBinding'
154+
import Button from '@mui/material/Button'
155+
import { json } from '@codemirror/lang-json'
156+
import { javascript } from '@codemirror/lang-javascript'
157+
import { html } from '@codemirror/lang-html'
158+
import { css } from '@codemirror/lang-css'
159+
import { extractValue } from '@ui-schema/ui-schema/UIStore'
160+
import { WidgetCode } from '@ui-schema/material-code'
161+
import { WidgetCodeSelectable } from '@ui-schema/material-code/WidgetCodeSelectable'
162+
import { CustomCodeMirror } from './CustomCodeMirror'
163+
164+
export const CustomWidgetCode: React.ComponentType<WidgetProps & WithScalarValue> = (props) => {
165+
const format = props.schema.get('format')
166+
// map the to-be-supported CodeMirror language, or add other extensions
167+
const extensions = React.useMemo(() => [
168+
...(format === 'json' ? [json()] : []),
169+
...(format === 'javascript' ? [javascript()] : []),
170+
...(format === 'html' ? [html()] : []),
171+
...(format === 'css' ? [css()] : []),
172+
], [format])
173+
174+
return <WidgetCode
175+
{...props}
176+
CodeMirror={CustomCodeMirror}
177+
// `extensions` will be passed down again to `CustomCodeMirror`
178+
extensions={extensions}
179+
formatValue={format}
180+
/>
181+
}
182+
183+
const CustomWidgetCodeSelectableBase: React.ComponentType<WidgetProps & WithValue> = (
184+
{value, ...props},
185+
) => {
186+
const {schema, onChange, storeKeys} = props
187+
const valueType = schema.get('type') as 'array' | 'object'
188+
// supporting different types requires mapping the actual key of `format` and `value` inside the non-scalar value of this component
189+
// - for tuples: [0: format, 1: code]
190+
// - for objects: {lang, code}
191+
const formatKey: StoreKeyType = valueType === 'array' ? 0 : 'lang'
192+
const valueKey: StoreKeyType = valueType === 'array' ? 1 : 'code'
193+
const format = value?.get(formatKey) as string | undefined || schema.get('formatDefault') as string | undefined
194+
const codeValue = value?.get(valueKey) as string | undefined
195+
196+
// map the to-be-supported CodeMirror language, or add other extensions
197+
const extensions = React.useMemo(() => [
198+
...(format === 'json' ? [json()] : []),
199+
...(format === 'javascript' ? [javascript()] : []),
200+
...(format === 'html' ? [html()] : []),
201+
...(format === 'css' ? [css()] : []),
202+
], [format])
203+
204+
return <WidgetCodeSelectable
205+
{...props}
206+
CodeMirror={CustomCodeMirror}
207+
// `extensions` will be passed down again to `CustomCodeMirror`
208+
extensions={extensions}
209+
formatKey={formatKey}
210+
valueKey={valueKey}
211+
value={codeValue}
212+
formatValue={format}
213+
/>
214+
}
215+
const CustomWidgetCodeSelectable = extractValue(memo(CustomWidgetCodeSelectableBase))
216+
217+
export type CustomWidgetsBinding = WidgetsBindingFactory<{}, MuiWidgetsBindingTypes<{}>, MuiWidgetsBindingCustom<{}>>
218+
219+
export const customWidgets: CustomWidgetsBinding = {
220+
...widgets,
221+
types: widgets.types,
222+
custom: {
223+
...widgets.custom,
224+
Code: CustomWidgetCode,
225+
CodeSelectable: CustomWidgetCodeSelectable,
226+
},
227+
}
228+
```
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
const demoCode = [
2+
[
3+
`
4+
### Demo
5+
`,
6+
{
7+
type: 'object',
8+
properties: {
9+
code: {
10+
type: 'string',
11+
format: 'css',
12+
widget: 'Code',
13+
view: {
14+
sizeXs: 12,
15+
},
16+
},
17+
code_bg: {
18+
type: 'string',
19+
format: 'css',
20+
widget: 'Code',
21+
view: {
22+
sizeXs: 12,
23+
bg: true,
24+
},
25+
},
26+
code_selectable: {
27+
type: 'array',
28+
format: ['json', 'js', 'html'],
29+
widget: 'CodeSelectable',
30+
formatDefault: 'json',
31+
},
32+
code_no_title: {
33+
type: 'string',
34+
format: 'css',
35+
widget: 'Code',
36+
view: {
37+
hideTitle: true,
38+
},
39+
},
40+
code_selectable_no_title: {
41+
type: 'array',
42+
format: ['json', 'js', 'css', 'html'],
43+
widget: 'CodeSelectable',
44+
formatDefault: 'js',
45+
view: {
46+
hideTitle: true,
47+
},
48+
},
49+
normal_string: {
50+
type: 'string',
51+
},
52+
},
53+
},
54+
],
55+
];
56+
57+
export {demoCode}

packages/demo/src/pages/PageDemoComponent.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export const PageDemoComponent: React.ComponentType = () => {
4949
</>
5050
}
5151

52-
export const CustomCodeMirror: React.FC<CodeMirrorComponentProps> = (
52+
const CustomCodeMirror: React.FC<CodeMirrorComponentProps> = (
5353
{
5454
value,
5555
extensions,

0 commit comments

Comments
 (0)