npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

l-formula-editor

v1.0.0

Published

公式编辑器、代码编辑器,支持sql,html,js,json,customScript

Downloads

121

Readme

公式编辑器、代码编辑器,支持sql,html,js,json,customScript


title: 代码/公式编辑器; order: 11

nav: title: 组件 order: 1 path: /components

group: title: 基本元素控件 order: 1, path: "/basic" demo: tocDepth: 4

介绍

  • 技术栈:react:^16.14.0, typescript less codemirror 6.x

  • 目标:实现一个编辑器组件,支持javaScriptjsonhtmlsql,以及自定义的公式编辑器customScript

  • 实现:基于codemirror二次封装实现;文档地址

  • 优势: Codemirror6 和 Codemirror5 的主要区别体现在以下几个方面:

    • 架构:CM6 是对 CM5 进行了全面重写和重构的版本,采用了新的架构。CM6 的设计目标是提供更好的可扩展性、模块化和定制性,以适应不同的编辑器需求。
    • 模块化:CM6 引入了模块化的概念,使得编辑器的功能可以以更灵活的方式组织和扩展。它提供了一套核心模块,以及可选的插件和扩展模块,使开发人员能够根据自己的需求选择和组合功能。
    • 插件系统:CM6 的插件系统更加强大和灵活。它采用了新的基于状态和触发器的机制,使得插件能够更好地与编辑器交互并响应变化。这使得开发人员能够创建更复杂和定制化的编辑器功能。
    • 渲染方式:CM6 使用新的渲染引擎,采用了虚拟 DOM 的概念,以提高性能和响应能力。它还支持可选的受限制的线性渲染模式,可以在大型文档上提供更好的性能。
  • 使用的插件集合

      "@codemirror/lang-html": "^6.4.9",
      "@codemirror/lang-javascript": "^6.2.2",
      "@codemirror/lang-json": "^6.0.1",
      "@codemirror/lang-sql": "^6.6.4",
      "@lezer/highlight": "^1.2.0",
      "codemirror": ^6.0.1;
      "antd": "3.x"

Demo

基础场景下使用

import React from "react";
import { Radio } from "antd";
import { HqCodemirror } from "hquipro";
import { parseToFunction } from "./utils";
import "./style/index.less";
import { variables, functionList, completions, hintPaths } from "./mockData.ts";
const { useState } = React;
export default () => {
    const [mode, setMode] = useState<"json" | "javascript" | "sql" | "html" | "customScript">("customScript");
    const placeholderThemeFiled = "id";
    const placeholderThemes = {
        [placeholderThemeFiled]: {
            textColor: "#d46b08",
            backgroudColor: "#fff7e6",
            borderColor: "#ffd591"
        }
    };
    const changeRadio = (e: any) => {
        setMode(e.target.value);
    };
    return (
      <div>
        <Radio.Group onChange={changeRadio} value={mode} buttonStyle="solid">
            <Radio.Button value="json">JSON</Radio.Button>
            <Radio.Button value="javascript">JavaScript</Radio.Button>
            <Radio.Button value="sql">SQL</Radio.Button>
            <Radio.Button value="html">HTML</Radio.Button>
            <Radio.Button value="customScript">自定义脚本</Radio.Button>
        </Radio.Group>
        <HqCodemirror
            completions={completions}
            mode={mode}
            variables={variables}
            withSelect={mode === "customScript"}
            height={180}
            showTool={true}
            showOutLine={false}
            defaultValue={"SUM([[id.常量:var.姓名:name]],[[id.常量:var.年龄:age]],[[id.常量:var.年龄2:age2]], [[id.哈哈哈:ha]])"}
            hintPaths={hintPaths}
            placeholderThemes={placeholderThemes}
            placeholderThemeFiled={placeholderThemeFiled}
            keywordsColor="#081cd4"
            keywords={["let", "const", "wen", "if", "else", "for", "while", "switch"]}
            functions={functionList}
            onChange={(data: any) => {
                const res = parseToFunction({
                    codeStr: data,
                    placeholderThemeFiled,
                    functions: functionList,
                    variables
                });
                console.log("func", res);
                if (res) {
                    const { func, funcs, data, funIng } = res;
                    console.log('res',funIng())
                    console.log("res", func(funcs, data));
                }
            }}
        />
      </div>
    );
};

在 sql 中使用

import React from "react";
import { HqCodemirror } from "hquipro";
import "./style/index.less";
import { variables, functionSqlList, tablesData } from "./mockData.ts";
const { useState } = React;
export default () => {
    const placeholderThemeFiled = 'sql'
    const placeholderThemes = {
        [placeholderThemeFiled]: {
            textColor: "#4284D3",
            backgroudColor: "rgba(66,132,211,0.1)",
        }
    };
    return (
        <div>
            <HqCodemirror
                mode={"sql"}
                variables={variables}
                withSelect={true}
                showHeader={false}
                actionBoxHeight={"180px"}
                height={120}
                defaultValue={"单行输入框"}
                placeholderThemeFiled={placeholderThemeFiled}
                placeholderThemes={placeholderThemes}
                showOutLine={false}
                tables={tablesData}
                functions={functionSqlList}
                onChange={(data: any) => {
                    console.log(data);
                }}
            />
        </div>
    );
};

API

| 属性  | 描述  |  值 |  备注 | | ------------ | ------------------------------------------------ | -------------------------------------------------------- | -------------------------- | | defaultValue | 文档的默认值 | string | | | mode | 编辑器支持的语言类型 | "json" / "javascript" / "sql" / "html" / "customScript"; | 必填 | | trigger | 触发长 change 的方式, blur 触发还是 change 触发 | "blur"/"change" | 默认值 blur | | editable | 编辑器是否可编辑 | Boolean | true | | showOutLine | 是否focus聚焦时显示外层的虚线边框 | Boolean | 默认值true | | height | 编辑器的高度 | Number | | | minHeight | 编辑器的最小高度 | Number | | | placeholder | 编辑器的 placeholder | string / HTMLElement | | | tables | mode = sql 时,注入表格的一些字段到 sql 语言库中 | ITableSchema | | | extensions | codemirror 的扩展配置 | Extension[] | 如果现有配置不满足可以覆盖 | | themeStyle | 对于codemirror View.them的扩展,会覆盖掉hqcodemirror的一些默认配置 | 对象格式{[K: string]: {} } | eg:{".cm-content": {minHeight: 80px} } | | onChange | change 回调函数 | (value: string, rely?: any) => void | |

自定义的情况下(mode=customScript|sql),会用到的api | 属性 | 描述 | 值 | 备注 | |-----|-----|-----|-----| | withSelect|是否展示选择区域|Boolean| 默认值 false | | actionBoxHeight | 选择区域的最大高度 | String | 200px | | showHeader | 编译器是否需要展示头部 | Boolean | true | | showTool | 是否展示工具栏,当且showHeader=true时生效 | Boolean | false | | customTool | 自定义工具栏,当showHeader=true&showTool=false生效 | React.ReactNode/string | | | renderHeader | 头部左边区域的内容过可自定义 | React.ReactNode/string | 默认值“公式=” | | keywordsClassName | 给关键字 Dom 添加的 className | | | | keywords | 关键字集合,没需要可不传 | String[] | [] | | keywordsColor | 关键字的颜色值 | 色值 | | | functions | 自定义函数集合,可用于自动提示,或者操作区域的可选函数 | FunctionType[] | | | variables | 外部的一些变量,可用于自动提示,或者操作区域的可选变量 | variablesModel[] | | | hintPaths | 设置一些含有子集的对象,来激活自动匹配的,例如对象 a:{age:{b:1}}, 输入 a.会弹出可选项 age,输入 a.age.同样弹出 b 可选项; | HintPathType[] | | | completions | 设置自动匹配的值,这里做了优化,变量 variables 和函数 functions 的值会被自动合到这个数组里; | CompletionsType[] | | | placeholderThemes | 定义变量的样式 | PlaceholderThemesType | | | placeholderThemeFiled| 定义变量的 filed, 主要用于最后解析显示用 | String | | | clickDebug | 调试按钮的回调函数,返回IParseFunctionResult | clickDebug?: (result: IParseFunctionResult) => void; | |

组件向外暴露的方法

通过 ref 获取组件实例,可以调用以下方法

  1. insertText: 插入文本
    • 参数:text: string; isTemplate: boolean;
    • 作用:插入文本到编辑器中,isTemplate 为 true 时,会将 text 包裹在模板字符串中
  2. clearText: 清空文本
    • 作用:清空编辑器中的文本
  3. setText: 设置文本
    • 参数:text: string;
    • 作用:设置编辑器中的文本
  4. getValue: 获取编辑器中的文本内容; - 作用:getValue();

解析方法

parseToFunction: 用在 customScript 的解析;

import {parseToFunction} from "hquipro/es/hq-codemirror/utils";
const res = parseToFunction({codeStr: data, placeholderThemeFiled, functions: functionList, variables});
const {func, funcs, data, formatResult} = res;

数据说明

数据结构

1.函数的定义

interface FunctionType {
    template: string;
    label: string;
    detail: string;
    type: string; // 类型 function
    parent?: any;
    handle?: any; // 函数的具体实现方法
    returnType?: string; // 函数的返回值类型
    briefly?: string; // 函数的简要描述
    paramNum?: number; // 函数的参数个数,-1表示多个参数。大于等于1个
    example?: string; // 函数的使用示例
    instruction?: string; // 函数的详细说明
    children?: FunctionType[];
}

// data
const functionList: FunctionType[] = [
    {
        label: "基础函数",
        detail: "基础函数",
        template: "基础函数",
        paramNum: 0,
        type: "groupName",
        children: [
            {
                label: "sum1",
                detail: "求和函数,求和函数,把所有参数加一起返回",
                template: "func.sum1(${1})",
                paramNum: -1, // 表示多个参数。大于等于1个
                type: "function",
                returnType: "number",
                instruction: "该求和方法需要传递数值变量, 参数可以为多个",
                example: "sum1(1,2,3),计算结果为6",
                briefly: "求和函数",
                handle: `(...args) => {
                    return args.reduce((prev, cur) => {
                      return prev + +cur;
                    }, 0);
                }`
            }
        ]
    }
];

2.变量

interface variablesModel {
  code: string;
  name: string;  // 名称
  children?: variablesModel[];
  type: "model" | "field"; // 区分 分组和字段
  value?: any;
  fieldType?: string;  // 字段的类型
}
const variables: any[] = [
    {
      code: "var",
      name: "常量",
      type: "model",
      children: [
        {
          name: "userId",
          code: "id",
          type: "field",
          fieldType: "Number",
          value: 1,
        },
        ...
      ]
    }
]

3.completions 定义

输入提示的集合;在自定义时生效;上面的 functions 和 variables 在程序内部会自动注入的;

interface CompletionsType {
    template: string;
    label: string;
    detail: string;
    type: string;
    parent?: any;
}
const completions: CompletionsType[] = [
    {
        label: "let",
        type: "variable",
        detail: "定义变量let",
        template: "let ${1:variable} = ${2:value};" // 模板形式注入
    }
];

4.keywords 传入字符串数组

const keywords = ["a", "switch", "b"];

5.Hint 链式

支持输入 a.b.c 的提示输入

interface HintPathType {
    label: string;
    detail: string;
    type: "function" | "keyword" | "variable" | "text" | "property";
    template: string;
    children?: HintPathType[];
}
const data = [
    {
        label: "user",
        template: "user",
        detail: "用户",
        type: "variable",
        children: [
            {
                label: "department",
                template: "department",
                detail: "部门",
                type: "property",
                children: [
                    {
                        label: "name",
                        template: "name",
                        detail: "名称",
                        type: "property"
                    }
                ]
            }
        ]
    }
];

6.自定义函数的解析结果

interface IParseFunctionResult {
    funIng: Function; // 可直接用来执行的函数;
    func: Function;
    funcs: any[];
    data: any;
    formatResult: string;
}

变量主题色

placeholderThemeFiled: string;
interface PlaceholderThemesType {
  [placeholderThemeFiled]: CommonPlaceholderTheme;
}
interface CommonPlaceholderTheme {
  textColor: string;
  backgroudColor: string;
}

类型Map

enum typeMap = {
    "string": "字符串",
    "number": "数字",
    "boolean": "布尔",
    "object": "对象",
    "array": "数组",
    "date": "日期",
    "function": "函数",
    "null": "空",
    "undefined": "未定义",
    "symbol": "符号",
    "bigInt": "大整数",
    "regExp": "正则表达式",
    "error": "错误对象",
    "promise": "Promise对象",
    "timeStamp": "时间",
    'decimal': "数字",
    'percentage':'数字',
    'integer': "数字",
    'multiOption': '字符串',
    'select': "字符串"
};

输出是怎么样的?

输出是一个字符串形式,现在的设计是,通过保存输出字符串,在使用的地方在进行解析执行;

// 自动选择的情况下:求和函数sum1,年龄求和
func.sum1([[id.常量:var.年龄:age]] ,[[id.常量:var.年龄:age]] )
// 手动输入的情况下:
sum1(1,2,3)

怎么解析执行的?

组件提供了统一的解析方法,只需要传入参数,即可获取返回的函数结果;

const res = parseToFunction({ codeStr: data, placeholderThemeFiled, functions: functionList, variables });
const { funIng, func, funcs, data } = res;
// 方式一:func是主体函数, funcs是对应的函数集合,data是对应的数据
func(funcs, data);
// 方式一:或者直接执行funIng;funIng内部实现了方式一的执行过程;
funIng();

实现:通过 new window.Function('func', 'data', return ${result}); result即为FunctionType中的handle

普通的加减运算也是可以直接执行的

符合基础的 JavaScript 的运算规则;

// 插入的变量
[[id.常量:var.年龄:age]] + [[id.常量:var.年龄:age]]
输出: 36
// 简单的计算:+ - * / %
12 + 34
输出: 46
// 字符串的拼接
"hello" + " world"
输出: "hello world"
// Boolean的运算
true && false
输出: false
34>=13 | 34<=32 ...
45!==21 | 23!=122 ...