diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..856b010 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +node_modules +package-lock.json + +.temp +.temp-pub +.vscode +.DS_Store +.history +.VSCodeCounter + +yarn.lock +.idea/ + +dist/ +coverage/ diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..1bdee18 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,10 @@ +{ + "printWidth": 100, + "tabWidth": 2, + "useTabs": false, + "semi": true, + "singleQuote": true, + "trailingComma": "none", + "bracketSpacing": true, + "arrowParens": "always" +} diff --git a/README.md b/README.md index ba47ae7..b958ee5 100644 --- a/README.md +++ b/README.md @@ -1 +1,66 @@ -# comlib-pc-normal-lite \ No newline at end of file +# Mybricks PC端通用组件库 (Lite版本) + +
+

轻量级的PC端通用组件库,为MyBricks低代码平台提供基础组件支持

+
+ +## ✨ 特性 + +- 🚀 轻量级设计,包含最常用的基础组件 +- 🎨 简洁的UI设计,开箱即用 +- 📦 零外部依赖(除React外) +- 🔧 完整的TypeScript支持 +- 🌍 支持国际化 + +## 📦 组件列表 + +### 基础组件 + +- **文本 (Text)**: 用于展示文本内容的基础组件 +- **按钮 (Button)**: 可点击的按钮组件,支持多种样式 +- **输入框 (Input)**: 用于接收用户输入的文本框组件 + +## 🔨 开发 + +### 安装依赖 + +```bash +npm install +``` + +### 启动开发服务器 + +```bash +npm run dev +``` + +### 构建 + +```bash +npm run build +``` + +## 📝 使用说明 + +本组件库基于MyBricks平台开发,可在MyBricks编辑器中直接使用。 + +### 环境依赖 + +组件运行时需要以下环境方法: + +```typescript +const env = { + i18n(text: string) { + // 多语言定制 + return text; + } +}; +``` + +## 🤝 贡献 + +欢迎提交 Issue 和 Pull Request! + +## 📄 License + +ISC \ No newline at end of file diff --git a/mybricks.json b/mybricks.json new file mode 100644 index 0000000..46e92b7 --- /dev/null +++ b/mybricks.json @@ -0,0 +1,14 @@ +{ + "title": "PC常规组件库(Lite)", + "description": "PC端通用组件库的轻量级版本,包含最基础和常用的组件", + "comAry": [ + { + "title": "基础组件", + "comAry": [ + "./src/text/com.json", + "./src/button/com.json", + "./src/input/com.json" + ] + } + ] +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..b0e53a3 --- /dev/null +++ b/package.json @@ -0,0 +1,37 @@ +{ + "name": "@mybricks/comlib-pc-normal-lite", + "version": "1.0.0", + "description": "PC端通用组件库(Lite版本)", + "main": "./dist/index.js", + "scripts": { + "dev": "mybricks dev", + "build": "mybricks build", + "publish": "mybricks publish" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/mybricks/comlib-pc-normal-lite.git" + }, + "keywords": [ + "mybricks", + "component-library", + "pc", + "lite", + "low-code" + ], + "author": "MyBricks", + "license": "ISC", + "bugs": { + "url": "https://github.com/mybricks/comlib-pc-normal-lite/issues" + }, + "homepage": "https://github.com/mybricks/comlib-pc-normal-lite#readme", + "dependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + }, + "devDependencies": { + "@types/react": "^18.0.0", + "@types/react-dom": "^18.0.0", + "typescript": "^5.0.0" + } +} diff --git a/src/button/com.json b/src/button/com.json new file mode 100644 index 0000000..4d6da72 --- /dev/null +++ b/src/button/com.json @@ -0,0 +1,38 @@ +{ + "title": "按钮", + "namespace": "mybricks.normal-pc-lite.button", + "version": "1.0.0", + "description": "可点击的按钮组件", + "author": "MyBricks", + "icon": "./icon.svg", + "data": "./data.json", + "runtime": "./runtime.tsx", + "inputs": [ + { + "id": "setText", + "title": "设置文本", + "desc": "设置按钮文本", + "schema": { + "type": "string" + } + }, + { + "id": "setDisabled", + "title": "设置禁用", + "desc": "设置按钮是否禁用", + "schema": { + "type": "boolean" + } + } + ], + "outputs": [ + { + "id": "click", + "title": "点击", + "desc": "按钮点击时触发", + "schema": { + "type": "any" + } + } + ] +} diff --git a/src/button/data.json b/src/button/data.json new file mode 100644 index 0000000..38b3946 --- /dev/null +++ b/src/button/data.json @@ -0,0 +1,6 @@ +{ + "text": "按钮", + "type": "primary", + "disabled": false, + "style": {} +} diff --git a/src/button/icon.svg b/src/button/icon.svg new file mode 100644 index 0000000..b3944fe --- /dev/null +++ b/src/button/icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/button/runtime.tsx b/src/button/runtime.tsx new file mode 100644 index 0000000..11890b4 --- /dev/null +++ b/src/button/runtime.tsx @@ -0,0 +1,86 @@ +import React, { useEffect } from 'react'; + +interface Data { + text: string; + type?: 'primary' | 'default' | 'dashed' | 'text' | 'link'; + disabled?: boolean; + style?: React.CSSProperties; +} + +export default function ({ data, inputs, outputs, env }: RuntimeParams) { + useEffect(() => { + inputs['setText']?.((value: string) => { + data.text = value; + }); + + inputs['setDisabled']?.((value: boolean) => { + data.disabled = value; + }); + }, []); + + const handleClick = () => { + if (!data.disabled && outputs && outputs['click']) { + outputs['click'](); + } + }; + + const getButtonStyle = (): React.CSSProperties => { + const baseStyle: React.CSSProperties = { + padding: '4px 15px', + fontSize: '14px', + borderRadius: '2px', + border: '1px solid #d9d9d9', + cursor: data.disabled ? 'not-allowed' : 'pointer', + transition: 'all 0.3s', + display: 'inline-block', + textAlign: 'center', + userSelect: 'none', + ...data.style + }; + + if (data.disabled) { + baseStyle.opacity = 0.6; + baseStyle.backgroundColor = '#f5f5f5'; + baseStyle.color = 'rgba(0, 0, 0, 0.25)'; + baseStyle.borderColor = '#d9d9d9'; + } else { + switch (data.type) { + case 'primary': + baseStyle.backgroundColor = '#1890ff'; + baseStyle.color = '#fff'; + baseStyle.borderColor = '#1890ff'; + break; + case 'dashed': + baseStyle.borderStyle = 'dashed'; + baseStyle.backgroundColor = '#fff'; + baseStyle.color = 'rgba(0, 0, 0, 0.85)'; + break; + case 'text': + baseStyle.border = 'none'; + baseStyle.backgroundColor = 'transparent'; + baseStyle.color = 'rgba(0, 0, 0, 0.85)'; + break; + case 'link': + baseStyle.border = 'none'; + baseStyle.backgroundColor = 'transparent'; + baseStyle.color = '#1890ff'; + break; + default: + baseStyle.backgroundColor = '#fff'; + baseStyle.color = 'rgba(0, 0, 0, 0.85)'; + } + } + + return baseStyle; + }; + + return ( + + ); +} diff --git a/src/input/com.json b/src/input/com.json new file mode 100644 index 0000000..304b6cd --- /dev/null +++ b/src/input/com.json @@ -0,0 +1,54 @@ +{ + "title": "输入框", + "namespace": "mybricks.normal-pc-lite.input", + "version": "1.0.0", + "description": "用于接收用户输入的文本框组件", + "author": "MyBricks", + "icon": "./icon.svg", + "data": "./data.json", + "runtime": "./runtime.tsx", + "inputs": [ + { + "id": "setValue", + "title": "设置值", + "desc": "设置输入框的值", + "schema": { + "type": "string" + } + }, + { + "id": "setPlaceholder", + "title": "设置提示文本", + "desc": "设置输入框的提示文本", + "schema": { + "type": "string" + } + }, + { + "id": "setDisabled", + "title": "设置禁用", + "desc": "设置输入框是否禁用", + "schema": { + "type": "boolean" + } + } + ], + "outputs": [ + { + "id": "onChange", + "title": "值变化", + "desc": "输入框值变化时触发", + "schema": { + "type": "string" + } + }, + { + "id": "onBlur", + "title": "失去焦点", + "desc": "输入框失去焦点时触发", + "schema": { + "type": "string" + } + } + ] +} diff --git a/src/input/data.json b/src/input/data.json new file mode 100644 index 0000000..b0c2c73 --- /dev/null +++ b/src/input/data.json @@ -0,0 +1,6 @@ +{ + "value": "", + "placeholder": "请输入", + "disabled": false, + "style": {} +} diff --git a/src/input/icon.svg b/src/input/icon.svg new file mode 100644 index 0000000..e4a1744 --- /dev/null +++ b/src/input/icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/input/runtime.tsx b/src/input/runtime.tsx new file mode 100644 index 0000000..0dd752d --- /dev/null +++ b/src/input/runtime.tsx @@ -0,0 +1,71 @@ +import React, { useEffect, useState } from 'react'; + +interface Data { + value: string; + placeholder?: string; + disabled?: boolean; + style?: React.CSSProperties; +} + +export default function ({ data, inputs, outputs, env }: RuntimeParams) { + const [value, setValue] = useState(data.value || ''); + + useEffect(() => { + inputs['setValue']?.((val: string) => { + const newValue = val !== undefined && val !== null ? String(val) : ''; + data.value = newValue; + setValue(newValue); + }); + + inputs['setPlaceholder']?.((val: string) => { + data.placeholder = val; + }); + + inputs['setDisabled']?.((val: boolean) => { + data.disabled = val; + }); + }, []); + + const handleChange = (e: React.ChangeEvent) => { + const newValue = e.target.value; + data.value = newValue; + setValue(newValue); + + if (outputs && outputs['onChange']) { + outputs['onChange'](newValue); + } + }; + + const handleBlur = () => { + if (outputs && outputs['onBlur']) { + outputs['onBlur'](value); + } + }; + + const inputStyle: React.CSSProperties = { + width: '100%', + padding: '4px 11px', + fontSize: '14px', + lineHeight: '1.5715', + color: 'rgba(0, 0, 0, 0.85)', + backgroundColor: data.disabled ? '#f5f5f5' : '#fff', + border: '1px solid #d9d9d9', + borderRadius: '2px', + transition: 'all 0.3s', + outline: 'none', + cursor: data.disabled ? 'not-allowed' : 'text', + ...data.style + }; + + return ( + + ); +} diff --git a/src/text/com.json b/src/text/com.json new file mode 100644 index 0000000..9d93c35 --- /dev/null +++ b/src/text/com.json @@ -0,0 +1,41 @@ +{ + "title": "文本", + "namespace": "mybricks.normal-pc-lite.text", + "version": "1.0.0", + "description": "用于展示文本内容的基础组件", + "author": "MyBricks", + "icon": "./icon.svg", + "data": "./data.json", + "runtime": "./runtime.tsx", + "inputs": [ + { + "id": "content", + "title": "设置内容", + "desc": "设置文本内容", + "schema": { + "type": "string" + }, + "rels": [ + "setContentDone" + ] + } + ], + "outputs": [ + { + "id": "setContentDone", + "title": "设置内容完成", + "desc": "文本内容设置完成", + "schema": { + "type": "string" + } + }, + { + "id": "click", + "title": "点击", + "desc": "点击文本时触发", + "schema": { + "type": "string" + } + } + ] +} diff --git a/src/text/data.json b/src/text/data.json new file mode 100644 index 0000000..6fbc6cc --- /dev/null +++ b/src/text/data.json @@ -0,0 +1,4 @@ +{ + "content": "文本内容", + "style": {} +} diff --git a/src/text/icon.svg b/src/text/icon.svg new file mode 100644 index 0000000..bc52076 --- /dev/null +++ b/src/text/icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/text/runtime.tsx b/src/text/runtime.tsx new file mode 100644 index 0000000..0221278 --- /dev/null +++ b/src/text/runtime.tsx @@ -0,0 +1,39 @@ +import React, { useEffect } from 'react'; + +interface Data { + content: string; + style?: React.CSSProperties; +} + +export default function ({ data, inputs, outputs, env }: RuntimeParams) { + useEffect(() => { + inputs['content']((value: string, relOutputs) => { + let res = value; + if (res !== undefined && typeof res !== 'string') { + res = JSON.stringify(res); + } + data.content = res; + if (relOutputs && relOutputs['setContentDone']) { + relOutputs['setContentDone'](res); + } + }); + }, []); + + const handleClick = () => { + if (outputs && outputs['click']) { + outputs['click'](data.content || ''); + } + }; + + return ( +
+ {env?.i18n ? env.i18n(data.content || '') : data.content || ''} +
+ ); +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..58f17a8 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "module": "es2015", + "target": "es5", + "lib": ["dom", "es2016", "dom.iterable", "esnext"], + "noImplicitAny": false, + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "allowSyntheticDefaultImports": true, + "strict": false, + "forceConsistentCasingInFileNames": true, + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react", + "baseUrl": "." + }, + "exclude": ["node_modules", "dist", ".temp", ".temp-pub"] +} diff --git a/typings.d.ts b/typings.d.ts new file mode 100644 index 0000000..243597c --- /dev/null +++ b/typings.d.ts @@ -0,0 +1,75 @@ +declare module "*.less" { + const resource: { [key: string]: string }; + export = resource; +} + +declare module "*.css" { + const resource: { [key: string]: string }; + export = resource; +} + +interface RuntimeParams { + /** 环境注入 */ + env: Env; + + /** 组件的配置数据 */ + data: T; + + /** 组件的输入 */ + inputs: Inputs; + + /** 组件的输出 */ + outputs: Outputs; + + /** 插槽 */ + slots?: Record; + + /** 日志插件 */ + logger?: Logger; + + /** 用户输入的组件名称 */ + title?: string; + + /** 组件的外部样式 */ + readonly style?: React.CSSProperties; +} + +interface EditorResult { + data: T; + focusArea: any; + output: any; + input: any; + slot?: any; + diagram?: any; +} + +interface Data { + [key: string]: any; +} + +interface Env { + preview?: {}; + edit?: {}; + runtime?: any; + i18n?: (text: string) => string; + [x: string]: any; +} + +interface Inputs { + [key: string]: (fn: (val: any, relOutputs?: any) => void) => void; +} + +interface Outputs { + [key: string]: (val?: any) => void; +} + +interface Slot { + render: (props?: any) => React.ReactNode; + [key: string]: any; +} + +interface Logger { + info: (msg: string) => void; + warn: (msg: string) => void; + error: (msg: string) => void; +}