Skip to content

Commit

Permalink
fix: CodeInput 支持 jsx css 代码补全 (#198)
Browse files Browse the repository at this point in the history
* feat: code setter support jsx css completion

* fix: refactor expression css mode

* fix: update story

* fix: revert code
  • Loading branch information
BoBoooooo authored Aug 16, 2024
1 parent 55bf417 commit 6de5488
Show file tree
Hide file tree
Showing 10 changed files with 778 additions and 11 deletions.
3 changes: 3 additions & 0 deletions apps/playground/src/helpers/prototypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,9 @@ export const nativeDomPrototypes = () => {
title: '样式',
group: 'style',
setter: 'expressionSetter',
setterProps: {
expressionType: 'cssObject',
},
},
{
name: 'className',
Expand Down
11 changes: 11 additions & 0 deletions apps/storybook/src/setting-form.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,14 @@ const prototypeHasBasicProps: IComponentPrototype = {
title: 'codeSetter',
setter: 'codeSetter',
},
{
name: 'style',
title: 'codeSetter(cssObject)',
setter: 'codeSetter',
setterProps: {
expressionType: 'cssObject',
},
},
{
name: 'text',
title: 'textSetter',
Expand Down Expand Up @@ -268,6 +276,9 @@ export function InitValues() {
name: 'style',
title: 'codeSetter',
setter: 'codeSetter',
setterProps: {
expressionType: 'cssObject',
},
},
{
name: 'object',
Expand Down
6 changes: 5 additions & 1 deletion apps/storybook/src/ui/input-code.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { Action, InputCode } from '@music163/tango-ui';
import { Action, InputCode, InputStyleCode } from '@music163/tango-ui';
import { BlockOutlined } from '@ant-design/icons';

export default {
Expand Down Expand Up @@ -28,6 +28,10 @@ export function Basic() {
);
}

export function CSS() {
return <InputStyleCode enableESLint suffix={<Action icon={<BlockOutlined />} size="small" />} />;
}

const code = `
function foo() {
console.log("test");
Expand Down
32 changes: 27 additions & 5 deletions packages/designer/src/setters/expression-setter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,15 @@ import { Dropdown, Button } from 'antd';
import { isValidExpressionCode } from '@music163/tango-core';
import { getValue, IVariableTreeNode, noop } from '@music163/tango-helpers';
import { CloseCircleFilled, MenuOutlined } from '@ant-design/icons';
import { Panel, InputCode, Action, DragPanel, PopOutOutlined } from '@music163/tango-ui';
import {
Panel,
InputCode,
Action,
DragPanel,
PopOutOutlined,
InputCodeProps,
InputStyleCode,
} from '@music163/tango-ui';
import { FormItemComponentProps } from '@music163/tango-setting-form';
import { useWorkspace, useWorkspaceData } from '@music163/tango-context';
import { VariableTree } from '../components';
Expand Down Expand Up @@ -43,6 +51,10 @@ export interface ExpressionSetterProps extends FormItemComponentProps<string> {
autoCompleteOptions?: string[];
allowClear?: boolean;
showOptionsDropDown?: boolean;
/**
* 表达式类型指定为 css object
*/
expressionType?: 'cssObject';
}

export function ExpressionSetter(props: ExpressionSetterProps) {
Expand All @@ -51,12 +63,13 @@ export function ExpressionSetter(props: ExpressionSetterProps) {
modalTitle,
modalTip,
autoCompleteOptions,
placeholder = '在这里输入JS代码',
placeholder = '在这里输入代码',
value: valueProp,
status,
allowClear = true,
newStoreTemplate,
showOptionsDropDown = true,
expressionType,
} = props;
// const codeValue = getCodeOfWrappedCode(valueProp);
const [inputValue, setInputValue] = useState(valueProp);
Expand All @@ -79,10 +92,12 @@ export function ExpressionSetter(props: ExpressionSetterProps) {
const sandbox = useSandboxQuery();
const evaluateContext = sandbox.window;

const InputCodeComponent = expressionType === 'cssObject' ? InputStyleCode : InputCode;

return (
<Box className="ExpressionSetter">
{/* 同时支持下拉框展示 */}
<InputCode
<InputCodeComponent
placeholder={placeholder}
suffix={
<Box css={suffixStyle}>
Expand Down Expand Up @@ -121,6 +136,7 @@ export function ExpressionSetter(props: ExpressionSetterProps) {
autoCompleteOptions={autoCompleteOptions}
newStoreTemplate={newStoreTemplate}
value={inputValue}
expressionType={expressionType}
onOk={(value) => {
change(value);
}}
Expand All @@ -141,7 +157,7 @@ export function ExpressionSetter(props: ExpressionSetterProps) {
);
}

export interface ExpressionPopoverProps {
export interface ExpressionPopoverProps extends InputCodeProps {
title?: string;
subTitle?: string;
placeholder?: string;
Expand All @@ -154,6 +170,10 @@ export interface ExpressionPopoverProps {
* 新建 store 的模板代码
*/
newStoreTemplate?: string;
/**
* 表达式类型指定为 css object
*/
expressionType?: 'cssObject';
children?: React.ReactNode;
}

Expand All @@ -168,10 +188,12 @@ export function ExpressionPopover({
autoCompleteOptions,
newStoreTemplate = CODE_TEMPLATES.newStoreTemplate,
children,
expressionType,
}: ExpressionPopoverProps) {
const [exp, setExp] = useState(value ?? defaultValue);
const [error, setError] = useState('');
const workspace = useWorkspace();
const InputCodeComponent = expressionType === 'cssObject' ? InputStyleCode : InputCode;

const selectNodePath = workspace.selectSource.selected[0]?.codeId;

Expand Down Expand Up @@ -207,7 +229,7 @@ export function ExpressionPopover({
body={
<Box display="flex" flexDirection="column">
<Box p="m">
<InputCode
<InputCodeComponent
shape="inset"
minHeight="56px"
maxHeight="160px"
Expand Down
2 changes: 1 addition & 1 deletion packages/setting-form/src/form-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ export function createFormItem(options: IFormItemCreateOptions) {
const CodeSetter = REGISTERED_FORM_ITEM_MAP['codeSetter']?.config?.component;

const setterNode = isCodeSetter ? (
<CodeSetter {...expProps} {...baseComponentProps} />
<CodeSetter {...expProps} {...baseComponentProps} {...setterProps} />
) : (
renderSetter({
...expProps,
Expand Down
1 change: 1 addition & 0 deletions packages/ui/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export * from './menu';
export * from './panel';
export * from './toggle-button';
export * from './input-code';
export * from './input-style-code';
export * from './input-list';
export * from './search';
export * from './json-view';
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/input-code.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ export function InputCode({
);
}

function useInputCode({
export function useInputCode({
shape,
status,
showLineNumbers,
Expand Down
99 changes: 99 additions & 0 deletions packages/ui/src/input-style-code.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import React from 'react';
import { Box } from 'coral-system';
import CodeMirror from '@uiw/react-codemirror';
import { javascript, javascriptLanguage, esLint } from '@codemirror/lang-javascript';
import { CompletionContext } from '@codemirror/autocomplete';
import { syntaxTree } from '@codemirror/language';
import { linter, lintGutter } from '@codemirror/lint';
import * as eslint from 'eslint-linter-browserify';
import { jsxCSSproperties, jsxCSSvalues } from './lang/css-object';
import type { Completion } from '@codemirror/autocomplete';
import { InputCodeProps, useInputCode } from './input-code';

const eslintConfig = {
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
},
env: {
browser: true,
},
rules: {},
};

function completeProperties(from: number, cssOptions: Completion[] | readonly Completion[]) {
return {
from,
options: cssOptions,
validFor: /^[\w$]*$/,
};
}

/**
*
* @param scope 预测 CSS 的补全上下文
* @param customOptions 自定义补全选项列表
* @returns
*/
function buildAutoComplete() {
/**
* @see context https://codemirror.net/docs/ref/#autocomplete.CompletionContext
*/
return function (context: CompletionContext) {
const nodeBefore = syntaxTree(context.state).resolveInner(context.pos, -1);
if (nodeBefore.name === 'VariableName') {
// match css property name
return completeProperties(nodeBefore.from, jsxCSSproperties());
}
// match css property value
else if (nodeBefore.name === 'String') {
return completeProperties(nodeBefore.from + 1, jsxCSSvalues);
}
return null;
};
}

export type InputStyleCodeProps = Omit<
InputCodeProps,
'autoCompleteContext' | 'autoCompleteOptions'
>;

export function InputStyleCode({
shape = 'solid',
suffix,
status,
showLineNumbers,
showFoldGutter,
enableESLint = false,
...rest
}: InputStyleCodeProps) {
const extensions = [
javascript({ jsx: true }),
javascriptLanguage.data.of({
autocomplete: buildAutoComplete(),
}),
];
if (enableESLint) {
extensions.push(lintGutter(), linter(esLint(new eslint.Linter(), eslintConfig)));
}
const { rootStyle, codeSetup } = useInputCode({
shape,
status,
showLineNumbers,
showFoldGutter,
});

return (
<Box className="InputCode" display="flex" alignItems="center" overflow="hidden" {...rootStyle}>
<Box flex="1" overflow="auto">
<CodeMirror
extensions={extensions}
basicSetup={codeSetup}
placeholder={'//输入 css 代码'}
{...rest}
/>
</Box>
{suffix}
</Box>
);
}
Loading

0 comments on commit 6de5488

Please sign in to comment.