├── .eslintrc ├── .gitignore ├── .npmignore ├── .npmrc ├── .travis.yml ├── LICENSE ├── docs ├── basic.tsx ├── custom-component-config.md ├── custom-plugin.md └── images │ ├── array-boolean.png │ ├── array-object.png │ ├── array-string.png │ ├── boolean.png │ ├── box-editor.png │ ├── color.png │ ├── display.png │ ├── gaea-editor.png │ ├── home-snapshot.png │ ├── number.png │ ├── object.png │ ├── select.png │ └── string.png ├── package.json ├── priconfig.json ├── readme.md ├── src ├── components │ ├── box-editor │ │ └── src │ │ │ ├── box-editor │ │ │ ├── box-editor.component.tsx │ │ │ ├── box-editor.style.ts │ │ │ └── box-editor.type.ts │ │ │ └── index.ts │ ├── icon │ │ └── src │ │ │ ├── icon │ │ │ ├── icon.component.tsx │ │ │ ├── icon.style.ts │ │ │ └── icon.type.ts │ │ │ ├── icons │ │ │ ├── add-file.tsx │ │ │ ├── add-folder.tsx │ │ │ ├── add.tsx │ │ │ ├── border-radius.tsx │ │ │ ├── border.tsx │ │ │ ├── center-line.tsx │ │ │ ├── close.tsx │ │ │ ├── component.tsx │ │ │ ├── dashed.tsx │ │ │ ├── database.tsx │ │ │ ├── display-block.tsx │ │ │ ├── display-flex.tsx │ │ │ ├── display-inline-block.tsx │ │ │ ├── display-inline.tsx │ │ │ ├── dotted.tsx │ │ │ ├── edit.tsx │ │ │ ├── eye-slash.tsx │ │ │ ├── eye.tsx │ │ │ ├── file.tsx │ │ │ ├── flex-align-center.tsx │ │ │ ├── flex-align-start.tsx │ │ │ ├── flex-align-stretch.tsx │ │ │ ├── flex-baseline.tsx │ │ │ ├── flex-direction-center.tsx │ │ │ ├── flex-direction-end.tsx │ │ │ ├── flex-row.tsx │ │ │ ├── flex-space-around.tsx │ │ │ ├── flex-space-between.tsx │ │ │ ├── folder.tsx │ │ │ ├── keybroad.tsx │ │ │ ├── page.tsx │ │ │ ├── position-relative.tsx │ │ │ ├── reload.tsx │ │ │ ├── remove.tsx │ │ │ ├── right-arrow.tsx │ │ │ ├── setting.tsx │ │ │ ├── solid.tsx │ │ │ ├── text-align-left.tsx │ │ │ ├── trash.tsx │ │ │ └── under-line.tsx │ │ │ └── index.ts │ └── tree │ │ └── src │ │ ├── index.ts │ │ ├── tree-node │ │ ├── tree-node.component.tsx │ │ ├── tree-node.style.ts │ │ └── tree-node.type.ts │ │ └── tree │ │ ├── tree.component.tsx │ │ ├── tree.style.ts │ │ └── tree.type.ts ├── defines │ ├── application.d.ts │ ├── event.d.ts │ ├── npm.d.ts │ └── viewport.d.ts ├── develop-entry │ └── index.tsx ├── gaea-editor.component.tsx ├── gaea-editor.type.tsx ├── index.tsx ├── page │ ├── page.component.tsx │ ├── page.style.ts │ └── viewport │ │ ├── edit-helper │ │ ├── edit-helper.component.tsx │ │ └── edit-helper.type.ts │ │ ├── viewport.component.tsx │ │ └── viewport.style.ts ├── plugins │ ├── copy-paste-keyboard │ │ ├── action.ts │ │ ├── index.tsx │ │ ├── index.type.ts │ │ └── store.ts │ ├── crumbs │ │ ├── index.style.ts │ │ ├── index.tsx │ │ └── index.type.ts │ ├── delete-keyboard │ │ └── index.tsx │ ├── drag-menu-button │ │ ├── index.tsx │ │ └── style.ts │ ├── drag-menu │ │ ├── index.style.ts │ │ ├── index.tsx │ │ └── index.type.ts │ ├── main-tool-editor-event-action │ │ ├── index.style.ts │ │ ├── index.tsx │ │ └── index.type.ts │ ├── main-tool-editor-event-trigger │ │ ├── index.style.ts │ │ ├── index.tsx │ │ └── index.type.ts │ ├── main-tool-editor-event │ │ ├── index.style.ts │ │ ├── index.tsx │ │ └── index.type.ts │ ├── main-tool-editor-manager │ │ ├── index.style.ts │ │ ├── index.tsx │ │ └── index.type.ts │ ├── main-tool-editor-type-array │ │ ├── index.style.ts │ │ ├── index.tsx │ │ └── index.type.ts │ ├── main-tool-editor-type-boolean │ │ ├── index.style.ts │ │ ├── index.tsx │ │ └── index.type.ts │ ├── main-tool-editor-type-box-editor │ │ ├── index.style.ts │ │ ├── index.tsx │ │ └── index.type.ts │ ├── main-tool-editor-type-color │ │ ├── index.style.ts │ │ ├── index.tsx │ │ └── index.type.ts │ ├── main-tool-editor-type-display │ │ ├── index.style.ts │ │ ├── index.tsx │ │ └── index.type.ts │ ├── main-tool-editor-type-number │ │ ├── index.style.ts │ │ ├── index.tsx │ │ └── index.type.ts │ ├── main-tool-editor-type-object │ │ ├── index.style.ts │ │ ├── index.tsx │ │ └── index.type.ts │ ├── main-tool-editor-type-select │ │ ├── index.style.ts │ │ ├── index.tsx │ │ └── index.type.ts │ ├── main-tool-editor-type-string │ │ ├── index.style.ts │ │ ├── index.tsx │ │ └── index.type.ts │ ├── main-tool-editor-variable │ │ ├── index.style.ts │ │ ├── index.tsx │ │ └── index.type.ts │ ├── main-tool-editor │ │ ├── index.style.ts │ │ ├── index.tsx │ │ └── index.type.ts │ ├── main-tool-tree │ │ ├── action.ts │ │ ├── guideline │ │ │ ├── guideline.component.tsx │ │ │ ├── guideline.style.ts │ │ │ └── guideline.type.ts │ │ ├── index.style.ts │ │ ├── index.tsx │ │ ├── index.type.ts │ │ ├── store.ts │ │ └── tree-node │ │ │ ├── tree-node.component.tsx │ │ │ ├── tree-node.style.ts │ │ │ └── tree-node.type.ts │ ├── main-tool │ │ ├── index.style.ts │ │ ├── index.tsx │ │ └── index.type.ts │ ├── preview │ │ ├── index.tsx │ │ ├── index.type.ts │ │ └── style.ts │ ├── save │ │ ├── index.tsx │ │ ├── index.type.ts │ │ └── style.ts │ ├── view-mode │ │ ├── index.tsx │ │ ├── index.type.ts │ │ └── style.ts │ └── viewport-guideline │ │ ├── index.style.ts │ │ ├── index.tsx │ │ └── index.type.ts ├── stores │ ├── application │ │ ├── action.ts │ │ └── store.ts │ ├── event │ │ ├── action.ts │ │ └── store.ts │ ├── index.ts │ └── viewport │ │ ├── action.ts │ │ └── store.ts └── utils │ ├── dom.ts │ ├── functional.ts │ ├── react-helper.ts │ └── scroll-to.ts ├── tests └── index.ts └── tsconfig.json /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "parserOptions": { 4 | "project": "./tsconfig.json" 5 | }, 6 | "env": { 7 | "jest": true, 8 | "browser": true 9 | }, 10 | "settings": { 11 | "react": { 12 | "version": "16.8" 13 | }, 14 | "import/resolver": { 15 | "node": { 16 | "extensions": [ 17 | ".js", 18 | ".jsx", 19 | ".ts", 20 | ".tsx", 21 | ".eslintrc" 22 | ] 23 | }, 24 | "webpack": { 25 | "config": { 26 | "resolve": { 27 | "alias": { 28 | "src": "src" 29 | } 30 | } 31 | } 32 | } 33 | } 34 | }, 35 | "plugins": [ 36 | "babel", 37 | "@typescript-eslint", 38 | "react-hooks" 39 | ], 40 | "extends": [ 41 | "eslint:recommended", 42 | "plugin:@typescript-eslint/recommended", 43 | "plugin:react/recommended", 44 | "airbnb-base", 45 | "prettier/@typescript-eslint", 46 | "plugin:prettier/recommended", 47 | "prettier" 48 | ], 49 | "rules": { 50 | "prettier/prettier": [ 51 | "error", 52 | { 53 | "printWidth": 120, 54 | "semi": true, 55 | "singleQuote": true, 56 | "tabWidth": 2, 57 | "useTabs": false, 58 | "trailingComma": "all" 59 | } 60 | ], 61 | "func-names": 0, 62 | "one-var": [ 63 | 1, 64 | "never" 65 | ], 66 | "prefer-const": 1, 67 | "no-unused-expressions": 1, 68 | "new-cap": 2, 69 | "prefer-arrow-callback": 2, 70 | "arrow-body-style": 0, 71 | "max-len": [ 72 | 1, 73 | { 74 | "code": 120, 75 | "ignoreStrings": true, 76 | "ignoreUrls": true, 77 | "ignoreRegExpLiterals": true 78 | } 79 | ], 80 | "consistent-return": "off", 81 | "default-case": 2, 82 | "prefer-rest-params": 2, 83 | "no-script-url": 0, 84 | "no-console": [ 85 | 2, 86 | { 87 | "allow": [ 88 | "info", 89 | "error", 90 | "warn" 91 | ] 92 | } 93 | ], 94 | "no-duplicate-imports": 2, 95 | "newline-per-chained-call": 2, 96 | "no-underscore-dangle": 2, 97 | "eol-last": 2, 98 | "no-useless-rename": 2, 99 | "class-methods-use-this": 0, 100 | "prefer-destructuring": 0, 101 | "no-unused-vars": 0, 102 | "@typescript-eslint/no-unused-vars": 1, 103 | "react/self-closing-comp": 2, 104 | "react/jsx-indent-props": [ 105 | 2, 106 | 2 107 | ], 108 | "no-plusplus": 0, 109 | "react/jsx-uses-vars": 1, 110 | "react/no-multi-comp": [ 111 | 2, 112 | { 113 | "ignoreStateless": true 114 | } 115 | ], 116 | "react/jsx-uses-react": 2, 117 | "react/react-in-jsx-scope": 2, 118 | "react/sort-comp": 1, 119 | "react/jsx-tag-spacing": 2, 120 | "react/jsx-no-bind": 0, 121 | "react/jsx-closing-bracket-location": 2, 122 | "react/prefer-stateless-function": 0, 123 | "react/display-name": 0, 124 | "react/prop-types": 0, 125 | "import/prefer-default-export": 0, 126 | "import/no-dynamic-require": 2, 127 | "@typescript-eslint/no-var-requires": 2, 128 | "no-use-before-define": [ 129 | "error", 130 | { 131 | "functions": false 132 | } 133 | ], 134 | "@typescript-eslint/no-use-before-define": 0, 135 | "@typescript-eslint/explicit-function-return-type": 0, 136 | "@typescript-eslint/interface-name-prefix": 0, 137 | "no-invalid-this": 0, 138 | "babel/no-invalid-this": 2, 139 | "no-await-in-loop": "off", 140 | "array-callback-return": "off", 141 | "no-restricted-syntax": "off", 142 | "@typescript-eslint/no-explicit-any": 0, 143 | "import/no-extraneous-dependencies": 0, 144 | "import/no-unresolved": 0, 145 | "react-hooks/rules-of-hooks": "error", 146 | "react-hooks/exhaustive-deps": "warn", 147 | "@typescript-eslint/explicit-member-accessibility": 0, 148 | "@typescript-eslint/no-object-literal-type-assertion": 0, 149 | "react/no-find-dom-node": 1, 150 | "no-param-reassign": [ 151 | 2, 152 | { 153 | "props": false 154 | } 155 | ] 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /.cache 3 | /.vscode 4 | /.temp 5 | /dist 6 | /.DS_Store 7 | /coverage 8 | /.nyc_output 9 | /npm-debug.log 10 | /.idea 11 | /package-lock.json 12 | /declaration 13 | /yarn.lock 14 | /.node -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /.cache 3 | /.vscode 4 | /.temp 5 | /.DS_Store 6 | /coverage 7 | /.nyc_output 8 | /npm-debug.log 9 | /tests 10 | /.idea 11 | /package-lock.json 12 | /packages 13 | /yarn.lock 14 | /.node -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=true -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | os: 3 | - linux 4 | install: 5 | - npm install 6 | node_js: 7 | - 10 8 | script: 9 | - npm test 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017-present, ziyi huang(ascoders) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /docs/basic.tsx: -------------------------------------------------------------------------------- 1 | import GaeaComponents from 'gaea-basic-components'; 2 | import * as React from 'react'; 3 | import Component from '../src/index'; 4 | 5 | class Props {} 6 | 7 | class State {} 8 | 9 | class TestComponent extends React.PureComponent { 10 | public static defaultProps = { 11 | editSetting: { 12 | key: 'aaaa', 13 | name: '66666', 14 | isContainer: false 15 | } 16 | }; 17 | 18 | public render() { 19 | return
123
; 20 | } 21 | } 22 | 23 | export default class Page extends React.PureComponent { 24 | public static defaultProps = new Props(); 25 | 26 | public state = new State(); 27 | 28 | public render() { 29 | return ( 30 | { 32 | console.log('data', data); 33 | }} 34 | componentClasses={[TestComponent, ...GaeaComponents]} 35 | /> 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /docs/custom-plugin.md: -------------------------------------------------------------------------------- 1 | # custom plugin 2 | 3 | `gaea-editor` has many built-in plug-ins, and it's also the best demo: 4 | 5 | ## UI plugins 6 | 7 | - [save](../src/plugins/save/index.tsx) 8 | - [preview](../src/plugins/preview/index.tsx) 9 | - [page](../src/plugins/page/index.tsx) 10 | - [main-tool](../src/plugins/main-tool/index.tsx) 11 | - ... 12 | 13 | ## Editor type plugins 14 | 15 | - [string](../src/plugins/main-tool-editor-type-string/index.tsx) 16 | - [number](../src/plugins/main-tool-editor-type-number/index.tsx) 17 | - [boolean](../src/plugins/main-tool-editor-type-boolean/index.tsx) 18 | - [color](../src/plugins/main-tool-editor-type-color/index.tsx) 19 | - ... 20 | 21 | ## Usage 22 | 23 | ```tsx 24 | import Editor from 'gaea-editor'; 25 | import MyPlugin from './my-plugin'; 26 | 27 | export default () => ; 28 | ``` 29 | 30 | ## Features 31 | 32 | - use `Connect` decorator from 'dob-react', then you can access all [built-in stores and actions](../src/stores). [Demo](../src/plugins/main-tool-editor-type-string/index.tsx#L20). 33 | - `gaea-editor` has some [built-in position](../src/page/page.component.tsx), and plugins can register positions by using `ApplicationAction.loadPluginByPosition(position: string)`. 34 | 35 | ## All built-in positions 36 | 37 | ![All built-in positions](images/gaea-editor.png) 38 | -------------------------------------------------------------------------------- /docs/images/array-boolean.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ascoders/gaea-editor/9d14876882fca5d4f11785464c305a000fcf51b0/docs/images/array-boolean.png -------------------------------------------------------------------------------- /docs/images/array-object.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ascoders/gaea-editor/9d14876882fca5d4f11785464c305a000fcf51b0/docs/images/array-object.png -------------------------------------------------------------------------------- /docs/images/array-string.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ascoders/gaea-editor/9d14876882fca5d4f11785464c305a000fcf51b0/docs/images/array-string.png -------------------------------------------------------------------------------- /docs/images/boolean.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ascoders/gaea-editor/9d14876882fca5d4f11785464c305a000fcf51b0/docs/images/boolean.png -------------------------------------------------------------------------------- /docs/images/box-editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ascoders/gaea-editor/9d14876882fca5d4f11785464c305a000fcf51b0/docs/images/box-editor.png -------------------------------------------------------------------------------- /docs/images/color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ascoders/gaea-editor/9d14876882fca5d4f11785464c305a000fcf51b0/docs/images/color.png -------------------------------------------------------------------------------- /docs/images/display.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ascoders/gaea-editor/9d14876882fca5d4f11785464c305a000fcf51b0/docs/images/display.png -------------------------------------------------------------------------------- /docs/images/gaea-editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ascoders/gaea-editor/9d14876882fca5d4f11785464c305a000fcf51b0/docs/images/gaea-editor.png -------------------------------------------------------------------------------- /docs/images/home-snapshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ascoders/gaea-editor/9d14876882fca5d4f11785464c305a000fcf51b0/docs/images/home-snapshot.png -------------------------------------------------------------------------------- /docs/images/number.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ascoders/gaea-editor/9d14876882fca5d4f11785464c305a000fcf51b0/docs/images/number.png -------------------------------------------------------------------------------- /docs/images/object.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ascoders/gaea-editor/9d14876882fca5d4f11785464c305a000fcf51b0/docs/images/object.png -------------------------------------------------------------------------------- /docs/images/select.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ascoders/gaea-editor/9d14876882fca5d4f11785464c305a000fcf51b0/docs/images/select.png -------------------------------------------------------------------------------- /docs/images/string.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ascoders/gaea-editor/9d14876882fca5d4f11785464c305a000fcf51b0/docs/images/string.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gaea-editor", 3 | "version": "2.3.26", 4 | "description": "", 5 | "main": "dist/main", 6 | "types": "declaration/index.d.ts", 7 | "scripts": { 8 | "start": "pri dev", 9 | "build": "pri build", 10 | "preview": "pri preview", 11 | "posttest": "codecov -f coverage/*.json -t c5e24697-a432-447e-81ab-2a642de423c2", 12 | "analyse": "pri analyse", 13 | "test": "pri test", 14 | "lint": "pri lint", 15 | "prettier": "prettier --write './src/**/*.?(ts|tsx)'", 16 | "format": "pri lint", 17 | "docs": "pri docs", 18 | "bundle": "pri bundle", 19 | "prepublishOnly": "npm run build && npm run bundle --skipLint", 20 | "publish": "pri publish" 21 | }, 22 | "peerDependencies": {}, 23 | "dependencies": { 24 | "@babel/runtime": "^7.0.0", 25 | "antd": "3.16.1", 26 | "dob": "^2.5.10", 27 | "dob-react": "^2.4.17", 28 | "gaea-basic-components": "^1.1.2", 29 | "gaea-render": "^1.0.18", 30 | "keymaster": "^1.6.2", 31 | "lodash": "^4.17.11", 32 | "react-color": "^2.14.1", 33 | "sortablejs": "^1.7.0", 34 | "styled-components": "^4.1.1" 35 | }, 36 | "devDependencies": { 37 | "pri": "^3.3.25" 38 | }, 39 | "author": "", 40 | "license": "ISC", 41 | "husky": { 42 | "hooks": { 43 | "pre-commit": "npm test -- --package root" 44 | } 45 | }, 46 | "tnpm": { 47 | "mode": "npm" 48 | }, 49 | "module": "dist/module" 50 | } 51 | -------------------------------------------------------------------------------- /priconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "useHttps": false, 3 | "type": "component", 4 | "packageLock": true 5 | } 6 | -------------------------------------------------------------------------------- /src/components/box-editor/src/box-editor/box-editor.style.ts: -------------------------------------------------------------------------------- 1 | import styled, { css } from 'styled-components'; 2 | 3 | const leftRightTopBottom = css` 4 | display: flex; 5 | position: absolute; 6 | `; 7 | 8 | const leftRight = css` 9 | flex-direction: row; 10 | cursor: ew-resize; 11 | `; 12 | 13 | const topBottom = css` 14 | flex-direction: column; 15 | cursor: ns-resize; 16 | `; 17 | 18 | export const Container = styled.div` 19 | position: relative; 20 | `; 21 | 22 | export const Left = styled.div` 23 | ${leftRightTopBottom} ${leftRight}; 24 | `; 25 | 26 | export const Right = styled.div` 27 | ${leftRightTopBottom} ${leftRight}; 28 | `; 29 | 30 | export const Top = styled.div` 31 | ${leftRightTopBottom} ${topBottom}; 32 | `; 33 | 34 | export const Bottom = styled.div` 35 | ${leftRightTopBottom} ${topBottom}; 36 | `; 37 | 38 | export const NumberBox = styled.div` 39 | display: flex; 40 | position: absolute; 41 | justify-content: center; 42 | align-items: center; 43 | user-select: none; 44 | `; 45 | 46 | export const Input = styled.input` 47 | outline: none; 48 | width: calc(100% - 5px); 49 | height: calc(100% - 8px); 50 | text-align: center; 51 | border: none; 52 | background-color: transparent; 53 | `; 54 | 55 | export const ButtonContainer = styled.div` 56 | overflow: hidden; 57 | `; 58 | 59 | export const ButtonTriangle = styled.div` 60 | transition: all 0.2s; 61 | user-select: none; 62 | ${(props: any) => { 63 | switch (props.theme.position) { 64 | case 'left': 65 | return ` 66 | border-right-color: #666; 67 | &:hover { 68 | border-right-color: black; 69 | } 70 | `; 71 | case 'top': 72 | return ` 73 | border-bottom-color: #666; 74 | &:hover { 75 | border-bottom-color: black; 76 | } 77 | `; 78 | case 'right': 79 | return ` 80 | border-left-color: #666; 81 | &:hover { 82 | border-left-color: black; 83 | } 84 | `; 85 | case 'bottom': 86 | return ` 87 | border-top-color: #666; 88 | &:hover { 89 | border-top-color: black; 90 | } 91 | `; 92 | default: 93 | } 94 | }}; 95 | `; 96 | -------------------------------------------------------------------------------- /src/components/box-editor/src/box-editor/box-editor.type.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export type MarginPaddingField = 4 | | 'paddingLeft' 5 | | 'paddingTop' 6 | | 'paddingRight' 7 | | 'paddingBottom' 8 | | 'marginLeft' 9 | | 'marginTop' 10 | | 'marginRight' 11 | | 'marginBottom' 12 | | ''; 13 | 14 | export class Props { 15 | /** 16 | * 大小 17 | */ 18 | public size?: number = 200; 19 | 20 | /** 21 | * paddingLeft 初始值 22 | */ 23 | public paddingLeft?: number = 0; 24 | 25 | // paddingTop 初始值 26 | public paddingTop?: number = 0; 27 | 28 | // paddingRight 初始值 29 | public paddingRight?: number = 0; 30 | 31 | // paddingBottom 初始值 32 | public paddingBottom?: number = 0; 33 | 34 | // marginLeft 初始值 35 | public marginLeft?: number = 0; 36 | 37 | // marginTop 初始值 38 | public marginTop?: number = 0; 39 | 40 | // marginRight 初始值 41 | public marginRight?: number = 0; 42 | 43 | // marginBottom 初始值 44 | public marginBottom?: number = 0; 45 | 46 | /** 47 | * 当鼠标按下的时候 48 | */ 49 | public onStart?: () => void = () => { 50 | // 51 | }; 52 | 53 | /** 54 | * 当值修改的时候 55 | */ 56 | public onChange?: (type: MarginPaddingField, value: number) => void = () => { 57 | // 58 | }; 59 | 60 | /** 61 | * 忽略拖动的改动,这个方法会在修改完最终调用一次 62 | * 在记录历史记录时,用这个会保证低频,而且不会遗漏每次修改,只会忽略拖动的中间过程 63 | */ 64 | public onFinalChange?: (type?: MarginPaddingField, value?: number) => void = () => { 65 | // 66 | }; 67 | } 68 | 69 | export class State { 70 | public paddingLeft?: number; 71 | 72 | public paddingTop?: number; 73 | 74 | public paddingRight?: number; 75 | 76 | public paddingBottom?: number; 77 | 78 | public marginLeft?: number; 79 | 80 | public marginTop?: number; 81 | 82 | public marginRight?: number; 83 | 84 | public marginBottom?: number; 85 | } 86 | -------------------------------------------------------------------------------- /src/components/box-editor/src/index.ts: -------------------------------------------------------------------------------- 1 | export { BoxEditor } from './box-editor/box-editor.component'; 2 | -------------------------------------------------------------------------------- /src/components/icon/src/icon/icon.component.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import * as Styled from './icon.style'; 4 | import * as typings from './icon.type'; 5 | 6 | import add from '../icons/add'; 7 | import addFile from '../icons/add-file'; 8 | import addFolder from '../icons/add-folder'; 9 | import border from '../icons/border'; 10 | import borderRadius from '../icons/border-radius'; 11 | import centerLine from '../icons/center-line'; 12 | import close from '../icons/close'; 13 | import component from '../icons/component'; 14 | import dashed from '../icons/dashed'; 15 | import database from '../icons/database'; 16 | import displayBlock from '../icons/display-block'; 17 | import displayFlex from '../icons/display-flex'; 18 | import displayInline from '../icons/display-inline'; 19 | import displayInlineBlock from '../icons/display-inline-block'; 20 | import dotted from '../icons/dotted'; 21 | import edit from '../icons/edit'; 22 | import eye from '../icons/eye'; 23 | import eyeSlash from '../icons/eye-slash'; 24 | import file from '../icons/file'; 25 | import flexAlignCenter from '../icons/flex-align-center'; 26 | import flexAlignStart from '../icons/flex-align-start'; 27 | import flexAlignStretch from '../icons/flex-align-stretch'; 28 | import flexBaseline from '../icons/flex-baseline'; 29 | import flexDirectionCenter from '../icons/flex-direction-center'; 30 | import flexDirectionEnd from '../icons/flex-direction-end'; 31 | import flexRow from '../icons/flex-row'; 32 | import flexSpaceAround from '../icons/flex-space-around'; 33 | import flexSpaceBetween from '../icons/flex-space-between'; 34 | import folder from '../icons/folder'; 35 | import keybroad from '../icons/keybroad'; 36 | import page from '../icons/page'; 37 | import positionRelative from '../icons/position-relative'; 38 | import reload from '../icons/reload'; 39 | import remove from '../icons/remove'; 40 | import rightArrow from '../icons/right-arrow'; 41 | import setting from '../icons/setting'; 42 | import solid from '../icons/solid'; 43 | import textAlignLeft from '../icons/text-align-left'; 44 | import trash from '../icons/trash'; 45 | import underLine from '../icons/under-line'; 46 | 47 | const iconMap = new Map React.ReactElement>(); 48 | iconMap.set('close', close); 49 | iconMap.set('page', page); 50 | iconMap.set('component', component); 51 | iconMap.set('folder', folder); 52 | iconMap.set('file', file); 53 | iconMap.set('addFile', addFile); 54 | iconMap.set('addFolder', addFolder); 55 | iconMap.set('setting', setting); 56 | iconMap.set('remove', remove); 57 | iconMap.set('trash', trash); 58 | iconMap.set('add', add); 59 | iconMap.set('keybroad', keybroad); 60 | iconMap.set('database', database); 61 | iconMap.set('rightArrow', rightArrow); 62 | iconMap.set('edit', edit); 63 | iconMap.set('borderRadius', borderRadius); 64 | iconMap.set('border', border); 65 | iconMap.set('centerLine', centerLine); 66 | iconMap.set('dashed', dashed); 67 | iconMap.set('displayBlock', displayBlock); 68 | iconMap.set('displayFlex', displayFlex); 69 | iconMap.set('displayInlineBlock', displayInlineBlock); 70 | iconMap.set('displayInline', displayInline); 71 | iconMap.set('dotted', dotted); 72 | iconMap.set('eye', eye); 73 | iconMap.set('flexAlignStart', flexAlignStart); 74 | iconMap.set('flexBaseline', flexBaseline); 75 | iconMap.set('flexDirectionCenter', flexDirectionCenter); 76 | iconMap.set('flexDirectionEnd', flexDirectionEnd); 77 | iconMap.set('flexRow', flexRow); 78 | iconMap.set('flexSpaceAround', flexSpaceAround); 79 | iconMap.set('flexSpaceBetween', flexSpaceBetween); 80 | iconMap.set('positionRelative', positionRelative); 81 | iconMap.set('reload', reload); 82 | iconMap.set('solid', solid); 83 | iconMap.set('textAlignLeft', textAlignLeft); 84 | iconMap.set('underLine', underLine); 85 | iconMap.set('eyeSlash', eyeSlash); 86 | iconMap.set('flexAlignCenter', flexAlignCenter); 87 | iconMap.set('flexAlignStretch', flexAlignStretch); 88 | 89 | export class Icon extends React.Component { 90 | public static defaultProps = new typings.Props(); 91 | 92 | public state = new typings.State(); 93 | 94 | public render() { 95 | return ( 96 | 97 | {iconMap.get(this.props.type)(this.props.size)} 98 | 99 | ); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/components/icon/src/icon/icon.style.ts: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const Container = styled.div` 4 | display: flex; 5 | justify-content: center; 6 | align-items: center; 7 | `; 8 | -------------------------------------------------------------------------------- /src/components/icon/src/icon/icon.type.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export class Props { 4 | /** 5 | * 图标类型 6 | */ 7 | public type?: string = 'close'; 8 | 9 | /** 10 | * 图标大小 11 | */ 12 | public size?: number = 20; 13 | 14 | public className?: string = null; 15 | } 16 | 17 | export class State {} 18 | -------------------------------------------------------------------------------- /src/components/icon/src/icons/add-file.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default (size: number) => ( 4 | 5 | 6 | 7 | ); 8 | -------------------------------------------------------------------------------- /src/components/icon/src/icons/add-folder.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default (size: number) => ( 4 | 5 | 6 | 7 | ); 8 | -------------------------------------------------------------------------------- /src/components/icon/src/icons/add.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default (size: number) => ( 4 | 5 | 6 | 7 | ); 8 | -------------------------------------------------------------------------------- /src/components/icon/src/icons/border-radius.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default (size: number) => ( 4 | 5 | 15 | 16 | ); 17 | -------------------------------------------------------------------------------- /src/components/icon/src/icons/border.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default (size: number) => ( 4 | 5 | 6 | 7 | 8 | 9 | ); 10 | -------------------------------------------------------------------------------- /src/components/icon/src/icons/center-line.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default (size: number) => ( 4 | 5 | 10 | 11 | 12 | ); 13 | -------------------------------------------------------------------------------- /src/components/icon/src/icons/close.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default (size: number) => ( 4 | 5 | 6 | 7 | ); 8 | -------------------------------------------------------------------------------- /src/components/icon/src/icons/component.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default (size: number) => ( 4 | 5 | 6 | 7 | ); 8 | -------------------------------------------------------------------------------- /src/components/icon/src/icons/dashed.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default (size: number) => ( 4 | 5 | 15 | 25 | 35 | 36 | ); 37 | -------------------------------------------------------------------------------- /src/components/icon/src/icons/database.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default (size: number) => ( 4 | 5 | 6 | 7 | ); 8 | -------------------------------------------------------------------------------- /src/components/icon/src/icons/display-block.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default (size: number) => ( 4 | 5 | 14 | 15 | ); 16 | -------------------------------------------------------------------------------- /src/components/icon/src/icons/display-flex.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default (size: number) => ( 4 | 5 | 15 | 25 | 35 | 45 | 46 | ); 47 | -------------------------------------------------------------------------------- /src/components/icon/src/icons/display-inline-block.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default (size: number) => ( 4 | 5 | 15 | 20 | 21 | ); 22 | -------------------------------------------------------------------------------- /src/components/icon/src/icons/display-inline.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default (size: number) => ( 4 | 5 | 10 | 11 | ); 12 | -------------------------------------------------------------------------------- /src/components/icon/src/icons/dotted.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default (size: number) => ( 4 | 5 | 15 | 25 | 35 | 36 | ); 37 | -------------------------------------------------------------------------------- /src/components/icon/src/icons/edit.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default (size: number) => ( 4 | 5 | 6 | 7 | ); 8 | -------------------------------------------------------------------------------- /src/components/icon/src/icons/eye-slash.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default (size: number) => ( 4 | 5 | 9 | 10 | ); 11 | -------------------------------------------------------------------------------- /src/components/icon/src/icons/eye.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default (size: number) => ( 4 | 5 | 10 | 15 | 16 | ); 17 | -------------------------------------------------------------------------------- /src/components/icon/src/icons/file.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default (size: number) => ( 4 | 5 | 6 | 7 | ); 8 | -------------------------------------------------------------------------------- /src/components/icon/src/icons/flex-align-center.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default (size: number) => ( 4 | 5 | 6 | 10 | 14 | 15 | 16 | ); 17 | -------------------------------------------------------------------------------- /src/components/icon/src/icons/flex-align-start.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default (size: number) => ( 4 | 5 | 6 | 10 | 14 | 15 | ); 16 | -------------------------------------------------------------------------------- /src/components/icon/src/icons/flex-align-stretch.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default (size: number) => ( 4 | 5 | 6 | 10 | 14 | 15 | 16 | ); 17 | -------------------------------------------------------------------------------- /src/components/icon/src/icons/flex-baseline.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default (size: number) => ( 4 | 5 | 10 | 15 | 25 | 35 | 36 | 37 | ); 38 | -------------------------------------------------------------------------------- /src/components/icon/src/icons/flex-direction-center.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default (size: number) => ( 4 | 5 | 6 | 7 | 11 | 15 | 16 | ); 17 | -------------------------------------------------------------------------------- /src/components/icon/src/icons/flex-direction-end.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default (size: number) => ( 4 | 5 | 6 | 10 | 14 | 15 | ); 16 | -------------------------------------------------------------------------------- /src/components/icon/src/icons/flex-row.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default (size: number) => ( 4 | 5 | 10 | 15 | 20 | 21 | ); 22 | -------------------------------------------------------------------------------- /src/components/icon/src/icons/flex-space-around.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default (size: number) => ( 4 | 5 | 6 | 7 | 12 | 17 | 18 | ); 19 | -------------------------------------------------------------------------------- /src/components/icon/src/icons/flex-space-between.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default (size: number) => ( 4 | 5 | 6 | 7 | 12 | 17 | 18 | ); 19 | -------------------------------------------------------------------------------- /src/components/icon/src/icons/folder.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default (size: number) => ( 4 | 5 | 6 | 7 | ); 8 | -------------------------------------------------------------------------------- /src/components/icon/src/icons/keybroad.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default (size: number) => ( 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | ); 29 | -------------------------------------------------------------------------------- /src/components/icon/src/icons/page.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default (size: number) => ( 4 | 5 | 6 | 7 | ); 8 | -------------------------------------------------------------------------------- /src/components/icon/src/icons/position-relative.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default (size: number) => ( 4 | 5 | 15 | 20 | 21 | ); 22 | -------------------------------------------------------------------------------- /src/components/icon/src/icons/reload.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default (size: number) => ( 4 | 5 | 9 | 10 | ); 11 | -------------------------------------------------------------------------------- /src/components/icon/src/icons/remove.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default (size: number) => ( 4 | 5 | 6 | 7 | 8 | ); 9 | -------------------------------------------------------------------------------- /src/components/icon/src/icons/right-arrow.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default (size: number) => ( 4 | 5 | 6 | 7 | ); 8 | -------------------------------------------------------------------------------- /src/components/icon/src/icons/setting.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default (size: number) => ( 4 | 5 | 6 | 7 | ); 8 | -------------------------------------------------------------------------------- /src/components/icon/src/icons/solid.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default (size: number) => ( 4 | 5 | 15 | 16 | ); 17 | -------------------------------------------------------------------------------- /src/components/icon/src/icons/text-align-left.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default (size: number) => ( 4 | 5 | 6 | 7 | 8 | 9 | 10 | ); 11 | -------------------------------------------------------------------------------- /src/components/icon/src/icons/trash.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default (size: number) => ( 4 | 5 | 6 | 7 | ); 8 | -------------------------------------------------------------------------------- /src/components/icon/src/icons/under-line.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default (size: number) => ( 4 | 5 | 10 | 11 | 12 | ); 13 | -------------------------------------------------------------------------------- /src/components/icon/src/index.ts: -------------------------------------------------------------------------------- 1 | export { Icon as default } from './icon/icon.component'; 2 | -------------------------------------------------------------------------------- /src/components/tree/src/index.ts: -------------------------------------------------------------------------------- 1 | export { Tree } from './tree/tree.component'; 2 | export { TreeNode } from './tree-node/tree-node.component'; 3 | -------------------------------------------------------------------------------- /src/components/tree/src/tree-node/tree-node.component.tsx: -------------------------------------------------------------------------------- 1 | import * as classNames from 'classnames'; 2 | import * as React from 'react'; 3 | import * as Styled from './tree-node.style'; 4 | import * as typings from './tree-node.type'; 5 | 6 | export class TreeNode extends React.Component { 7 | public static defaultProps = new typings.Props(); 8 | 9 | public state = new typings.State(); 10 | 11 | public componentWillMount() { 12 | this.setState((_, props) => ({ 13 | showChildren: props.defaultExpendAll || props.showChildren, 14 | })); 15 | } 16 | 17 | public handleContainerClick = (event: Event) => { 18 | this.props.onClick(event); 19 | if (!this.props.toggleByArrow) { 20 | this.setState(state => ({ 21 | showChildren: !state.showChildren, 22 | })); 23 | if (this.props.onToggleShow) { 24 | this.props.onToggleShow(event); 25 | } 26 | } 27 | }; 28 | 29 | public handleArrowClick = (event: Event) => { 30 | event.stopPropagation(); 31 | 32 | this.setState(state => ({ 33 | showChildren: !state.showChildren, 34 | })); 35 | if (this.props.onToggleShow) { 36 | this.props.onToggleShow(event); 37 | } 38 | }; 39 | 40 | public render() { 41 | const childrenStyle = { 42 | display: this.state.showChildren ? 'block' : null, 43 | }; 44 | 45 | const Children = React.Children.map(this.props.children, (item: any) => { 46 | if (item) { 47 | return React.cloneElement(item, { 48 | defaultExpendAll: this.props.defaultExpendAll, 49 | toggleByArrow: this.props.toggleByArrow, 50 | }); 51 | } 52 | }); 53 | 54 | return ( 55 | 56 | 57 | {React.Children.count(this.props.children) > 0 ? ( 58 | 59 | {renderCaret()} 60 | 61 | ) : ( 62 | 63 | )} 64 | {this.props.title || this.props.render()} 65 | 66 | 67 | {Children || null} 68 | 69 | ); 70 | } 71 | } 72 | 73 | function renderCaret() { 74 | return ( 75 | 76 | 77 | 78 | ); 79 | } 80 | -------------------------------------------------------------------------------- /src/components/tree/src/tree-node/tree-node.style.ts: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const Container = (styled.div as any).withConfig({ componentId: 'tree-node-container' })` 4 | flex-grow: 1; 5 | `; 6 | 7 | export const Title = styled.div` 8 | cursor: pointer; 9 | display: flex; 10 | user-select: none; 11 | font-size: 14px; 12 | color: #666; 13 | flex-grow: 1; 14 | align-items: center; 15 | `; 16 | 17 | export const TitleCaret = styled.div` 18 | width: 15px; 19 | height: 15px; 20 | margin-right: 5px; 21 | padding-left: 5px; 22 | display: flex; 23 | justify-content: center; 24 | align-items: center; 25 | fill: #666; 26 | transition: all 0.2s; 27 | ${(props: any) => 28 | props.theme.down && 29 | ` 30 | transform: rotate(90deg); 31 | `}; 32 | `; 33 | 34 | export const EmptyCaret = styled.div` 35 | width: 25px; 36 | `; 37 | 38 | export const Children = (styled.div as any).withConfig({ componentId: 'childs-container' })` 39 | display: none; 40 | padding-left: 10px; 41 | `; 42 | -------------------------------------------------------------------------------- /src/components/tree/src/tree-node/tree-node.type.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export class Props { 4 | /** 5 | * 标题 6 | */ 7 | public title?: string = ''; 8 | 9 | /** 10 | * 是否展示子元素 11 | */ 12 | public showChildren?: boolean = false; 13 | 14 | /** 15 | * 是否默认展开全部 16 | */ 17 | public defaultExpendAll?: boolean = false; 18 | 19 | /** 20 | * 标题渲染 21 | */ 22 | public render?: () => void; 23 | 24 | /** 25 | * 点击展开/隐藏后的回调 26 | */ 27 | public onToggleShow?: (title?: any) => void; 28 | 29 | /** 30 | * 整体被点击回调 31 | */ 32 | public onClick?: (event?: Event) => void; 33 | 34 | /** 35 | * 被划过 36 | */ 37 | public onMouseOver?: (event?: Event) => void; 38 | 39 | /** 40 | * 是否通过点击小箭头展开/隐藏 41 | */ 42 | public toggleByArrow?: boolean = false; 43 | } 44 | 45 | export class State { 46 | /** 47 | * 是否显示children 48 | */ 49 | public showChildren?: boolean = false; 50 | } 51 | -------------------------------------------------------------------------------- /src/components/tree/src/tree/tree.component.tsx: -------------------------------------------------------------------------------- 1 | import * as classNames from 'classnames'; 2 | import * as React from 'react'; 3 | import * as ReactDOM from 'react-dom'; 4 | import * as Styled from './tree.style'; 5 | import * as typings from './tree.type'; 6 | 7 | export class Tree extends React.Component { 8 | public static defaultProps = new typings.Props(); 9 | 10 | public state = new typings.State(); 11 | 12 | public render() { 13 | const Children = React.Children.map(this.props.children, (item: any) => { 14 | return React.cloneElement(item, { 15 | defaultExpendAll: this.props.defaultExpendAll, 16 | toggleByArrow: this.props.toggleByArrow, 17 | }); 18 | }); 19 | 20 | return {Children}; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/components/tree/src/tree/tree.style.ts: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const Container = (styled.div as any).withConfig({ componentId: 'tree-container' })` 4 | display: flex; 5 | flex-direction: column; 6 | width: 100%; 7 | `; 8 | -------------------------------------------------------------------------------- /src/components/tree/src/tree/tree.type.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export class Props { 4 | /** 5 | * 默认是否展开全部 6 | */ 7 | public defaultExpendAll?: boolean = false; 8 | 9 | /** 10 | * 点击箭头才会展开 11 | */ 12 | public toggleByArrow?: boolean = false; 13 | } 14 | 15 | export class State {} 16 | -------------------------------------------------------------------------------- /src/defines/application.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Plugin 3 | */ 4 | declare interface IPlugin { 5 | /** 6 | * Position insert to editor 7 | */ 8 | position: string; 9 | /** 10 | * React class 11 | */ 12 | class: any; 13 | /** 14 | * Plugin's action 15 | */ 16 | actions?: { 17 | [name: string]: any; 18 | }; 19 | /** 20 | * Plugin's store 21 | */ 22 | stores?: { 23 | [name: string]: any; 24 | }; 25 | } 26 | 27 | /** 28 | * Drag source's props should extends this interface 29 | */ 30 | declare interface IGaeaProps extends React.HTMLProps { 31 | editSetting: IGaeaSetting; 32 | } 33 | 34 | declare interface IGaeaSetting { 35 | /** 36 | * Unique key 37 | */ 38 | key: string; 39 | /** 40 | * Custom show name 41 | */ 42 | name: string; 43 | /** 44 | * Edit infos 45 | */ 46 | editors?: (string | IEditor)[]; 47 | /** 48 | * Is in preview mode 49 | */ 50 | isPreview?: boolean; 51 | /** 52 | * Container can be dragged into component 53 | */ 54 | isContainer?: boolean; 55 | /** 56 | * 自定义事件 57 | * 组件设置的事件,只支持回调事件 58 | */ 59 | events?: { 60 | text: string; 61 | field: string; 62 | }[]; 63 | } 64 | 65 | declare interface IDefaultProps { 66 | [key: string]: any; 67 | } 68 | 69 | declare interface IEditor { 70 | /** 71 | * Which field to control? 72 | * EX. text, size, user.nickname 73 | */ 74 | field: string; 75 | /** 76 | * Which Editor want to show? 77 | * Basic type like `string` `number`, or custom editor like `layout` 78 | */ 79 | type: string; 80 | /** 81 | * Show label 82 | */ 83 | text: string; 84 | /** 85 | * 特殊类型的额外描述信息 86 | */ 87 | data?: IEditor[] | IEditor | IEditorNumberData | IEditorSelectData | number | string; 88 | } 89 | 90 | declare interface IEditorNumberData { 91 | /** 92 | * 是否使用滑块 93 | */ 94 | useSlider?: boolean; 95 | /** 96 | * 滑块、输入框步长 97 | */ 98 | step: number; 99 | /** 100 | * 输入范围 101 | */ 102 | inputRange: number[]; 103 | /** 104 | * 输出范围 105 | */ 106 | outputRange?: number[]; 107 | } 108 | 109 | declare type IEditorSelectData = { 110 | /** 111 | * 选择框展示的 label 112 | */ 113 | text: string; 114 | /** 115 | * 选择框真正的值 116 | */ 117 | value: string; 118 | }[]; 119 | 120 | declare interface IPage { 121 | /** 122 | * Can create a folder or page 123 | */ 124 | type: 'page' | 'folder'; 125 | /** 126 | * Is home page 127 | */ 128 | isHomePage?: boolean; 129 | /** 130 | * description name 131 | */ 132 | name: string; 133 | /** 134 | * Real path 135 | */ 136 | path: string; 137 | parentKey: string; 138 | /** 139 | * Only exist in folder 140 | */ 141 | childs?: string[]; 142 | } 143 | 144 | declare interface IPages { 145 | [pageKey: string]: IPage; 146 | } 147 | 148 | declare type InstancesArray = { 149 | /** 150 | * The page instances belong to 151 | */ 152 | pageKey: string; 153 | instances: { 154 | [instanceKey: string]: InstanceInfo; 155 | }; 156 | }[]; 157 | 158 | declare interface IPreComponent { 159 | /** 160 | * gaea Key 161 | */ 162 | name: string; 163 | /** 164 | * Pre-setting props 165 | */ 166 | props: any; 167 | } 168 | 169 | declare type IOnComponentDragStart = ( 170 | gaeaKeyOrPreGaeaKey?: string, 171 | ) => Promise | IOnComponentDragStartReturn; 172 | 173 | declare interface IOnComponentDragStartReturn { 174 | setting: any; 175 | props: any; 176 | } 177 | -------------------------------------------------------------------------------- /src/defines/event.d.ts: -------------------------------------------------------------------------------- 1 | declare interface InstanceInfoEvent { 2 | trigger: InstanceEventTriggerInit | InstanceEventTriggerSubscribe | InstanceEventTriggerCallback; 3 | action: 4 | | InstanceEventActionNone 5 | | InstanceEventActionEmit 6 | | InstanceEventActionPassingSiblingNodes 7 | | InstanceEventActionJump; 8 | } 9 | 10 | /** 11 | * Triggers 12 | */ 13 | 14 | declare interface InstanceEventTriggerInit { 15 | type: 'init'; 16 | } 17 | 18 | declare interface InstanceEventTriggerSubscribe { 19 | type: 'subscribe'; 20 | name: string; 21 | } 22 | 23 | declare interface InstanceEventTriggerCallback { 24 | type: 'callback'; 25 | /** 26 | * Callback field. 27 | */ 28 | field?: string; 29 | } 30 | 31 | /** 32 | * Actions 33 | */ 34 | 35 | declare interface InstanceEventActionNone { 36 | type: 'none'; 37 | } 38 | 39 | declare interface InstanceEventActionEmit { 40 | type: 'emit'; 41 | name: string; 42 | } 43 | 44 | declare interface InstanceEventActionJump { 45 | type: 'jump'; 46 | url: string; 47 | } 48 | 49 | declare interface InstanceEventActionPassingSiblingNodes { 50 | type: 'passingSiblingNodes'; 51 | } 52 | -------------------------------------------------------------------------------- /src/defines/npm.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'sortablejs'; 2 | declare module 'react-color'; 3 | declare module 'keymaster'; 4 | -------------------------------------------------------------------------------- /src/defines/viewport.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Viewport's component instance info 3 | */ 4 | declare interface InstanceInfo { 5 | /** 6 | * Gaea key, use it can find componentClass, and access defaultProps.editSetting 7 | */ 8 | gaeaKey: string; 9 | /** 10 | * 预设组件专有属性,实例化 componentClass 还是根据 gaeaKey 找,但是配置会优先根据 preGaeaKey 找 11 | */ 12 | preGaeaKey?: string; 13 | /** 14 | * Component data, all operate save here 15 | */ 16 | data: { 17 | /** 18 | * Merge to props 19 | */ 20 | props?: { 21 | [prop: string]: any; 22 | }; 23 | /** 24 | * Event data 25 | */ 26 | events?: InstanceInfoEvent[]; 27 | }; 28 | /** 29 | * Children's instanceKey(only isContainer) 30 | * Component who's property isContainer is false will not have the property 31 | */ 32 | childs?: string[]; 33 | /** 34 | * Parent component's instanceKey 35 | * Root component's parentInstanceKey is null 36 | */ 37 | parentInstanceKey: string; 38 | /** 39 | * 哪些 props 字段使用的是变量 40 | * eg: key: style.backgroundColor 41 | */ 42 | variables?: { 43 | [realField: string]: InstanceInfoVariable; 44 | }; 45 | } 46 | 47 | /** 48 | * 编辑字段使用变量的描述 49 | */ 50 | declare interface InstanceInfoVariable { 51 | /** 52 | * 什么类型,比如从:全局、url参数、当前层级 53 | */ 54 | type: InstanceInfoVariableType; 55 | /** 56 | * 取值的唯一 key 57 | * 之所以不用 name,因为可能被修改 58 | */ 59 | key: string; 60 | } 61 | 62 | declare type InstanceInfoVariableType = 'global' | 'urlParam' | 'sibling'; 63 | 64 | declare interface IDragInfo { 65 | /** 66 | * Drag from application menu or viewport or a combo component? 67 | */ 68 | type: 'new' | 'viewport' | 'combo'; 69 | dragStartParentDom: HTMLElement; 70 | dragStartIndex: number; 71 | info: IDragInfoNew | IDragInfoViewport; 72 | } 73 | 74 | declare interface IDragInfoNew { 75 | gaeaKey?: string; 76 | /** 77 | * 预设 props 78 | */ 79 | props?: any; 80 | /** 81 | * 预设 gaeaKey 82 | */ 83 | preGaeaKey?: string; 84 | targetInstanceKey?: string; 85 | targetIndex?: number; 86 | } 87 | 88 | declare interface IDragInfoViewport { 89 | /** 90 | * Current drag instance key 91 | */ 92 | instanceKey?: string; 93 | /** 94 | * Drag target instance key 95 | */ 96 | targetInstanceKey?: string; 97 | /** 98 | * Index where drag to 99 | */ 100 | targetIndex?: number; 101 | } 102 | 103 | /** 104 | * full viewport information 105 | */ 106 | declare interface IFullInformation { 107 | [instanceKey: string]: InstanceInfo; 108 | } 109 | -------------------------------------------------------------------------------- /src/develop-entry/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import GaeaEditor from '../index'; 4 | 5 | ReactDOM.render( 6 |
7 | 8 |
, 9 | document.getElementById('react-dom'), 10 | ); 11 | -------------------------------------------------------------------------------- /src/gaea-editor.type.tsx: -------------------------------------------------------------------------------- 1 | import GaeaComponents from 'gaea-basic-components'; 2 | 3 | export class Props { 4 | /** 5 | * React class you want to drag with. 6 | */ 7 | public componentClasses?: React.ComponentClass[] = GaeaComponents; 8 | 9 | /** 10 | * Trigger when onSave button clicked. 11 | */ 12 | public onSave?: (value?: IFullInformation) => void; 13 | 14 | /** 15 | * Default value. 16 | */ 17 | public defaultValue?: IFullInformation = null; 18 | 19 | /** 20 | * Custom plugins include jsx and stores. 21 | */ 22 | public plugins?: IPlugin[] = []; 23 | 24 | /** 25 | * Locale 26 | */ 27 | public locale?: 'zh' | 'en' = 'zh'; 28 | 29 | /** 30 | * You can rewrite viewport element. 31 | */ 32 | public ViewportRender?: React.ReactElement; 33 | 34 | /** 35 | * Disable built-in plugin list 36 | */ 37 | public disableBuiltInPlugin: string[] = []; 38 | 39 | /** 40 | * 预设组件 41 | * gaeaKey 必须从已有组件中读取 42 | */ 43 | public preComponents?: { 44 | gaeaKey: string; 45 | components: IPreComponent[]; 46 | }[] = []; 47 | 48 | /** 49 | * 组件被拖拽起来时的回调,你可以填充 props 为即将渲染的组件。 50 | * 也可以发请求获取数据再填充到 props,只要返回一个 promise,编辑器会等到返回数据再执行组件渲染 51 | */ 52 | public onComponentDragStart?: IOnComponentDragStart = () => null; 53 | } 54 | 55 | export class State {} 56 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | // tslint:disable-next-line:no-reference 2 | // / 3 | // tslint:disable-next-line:no-reference 4 | // / 5 | // tslint:disable-next-line:no-reference 6 | // / 7 | // tslint:disable-next-line:no-reference 8 | // / 9 | 10 | import GaeaEditor from './gaea-editor.component'; 11 | 12 | export default GaeaEditor; 13 | 14 | export { StoreProps } from './stores/index'; 15 | export { GaeaEditor }; 16 | -------------------------------------------------------------------------------- /src/page/viewport/edit-helper/edit-helper.type.ts: -------------------------------------------------------------------------------- 1 | import { StoreProps } from '../../../stores'; 2 | 3 | export class Props extends StoreProps { 4 | /** 5 | * 实例唯一的 key 6 | */ 7 | public instanceKey?: string; 8 | } 9 | 10 | export class State {} 11 | -------------------------------------------------------------------------------- /src/page/viewport/viewport.component.tsx: -------------------------------------------------------------------------------- 1 | import { Connect } from 'dob-react'; 2 | import * as React from 'react'; 3 | import * as ReactDOM from 'react-dom'; 4 | import { StoreProps } from '../../stores'; 5 | import EditHelper from './edit-helper/edit-helper.component'; 6 | import * as Styled from './viewport.style'; 7 | 8 | class Props extends StoreProps {} 9 | 10 | class State {} 11 | 12 | @Connect 13 | export default class Viewport extends React.Component { 14 | public static defaultProps = new Props(); 15 | 16 | public state = new State(); 17 | 18 | /** 19 | * 获取自己的实例 20 | */ 21 | public getRef = (ref: React.ReactInstance) => { 22 | this.props.actions.ViewportAction.setViewportDOM(ReactDOM.findDOMNode(ref) as HTMLElement); 23 | }; 24 | 25 | /** 26 | * 鼠标移开视图区域 27 | */ 28 | public handleMouseLeave = (event: React.MouseEvent) => { 29 | event.stopPropagation(); 30 | 31 | // 触发事件 32 | this.props.actions.EventAction.emit(this.props.stores.EventStore.mouseLeaveViewport); 33 | 34 | // 设置当前 hover 的元素为 null 35 | this.props.actions.ViewportAction.setCurrentHoverInstanceKey(null); 36 | }; 37 | 38 | public render() { 39 | if (this.props.stores.ViewportStore.rootInstanceKey === null) { 40 | return null; 41 | } 42 | 43 | return ( 44 | 45 | 49 | 50 | ); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/page/viewport/viewport.style.ts: -------------------------------------------------------------------------------- 1 | import styled, { css } from 'styled-components'; 2 | 3 | export const Container = styled.div` 4 | display: block; 5 | height: 100%; 6 | `; 7 | -------------------------------------------------------------------------------- /src/plugins/copy-paste-keyboard/action.ts: -------------------------------------------------------------------------------- 1 | import { Action, inject } from 'dob'; 2 | import * as _ from 'lodash'; 3 | import ApplicationAction from '../../stores/application/action'; 4 | import ViewportAction from '../../stores/viewport/action'; 5 | import ViewportStore from '../../stores/viewport/store'; 6 | import { CopyPasteStore } from './store'; 7 | 8 | export class CopyPasteAction { 9 | @inject(CopyPasteStore) private copyPasteStore: CopyPasteStore; 10 | 11 | @inject(ViewportAction) private viewportAction: ViewportAction; 12 | 13 | @inject(ViewportStore) private viewportStore: ViewportStore; 14 | 15 | @inject(ApplicationAction) private applicationAction: ApplicationAction; 16 | 17 | @Action 18 | public copyCurrentHoverInstance() { 19 | if (!this.viewportStore.currentHoverInstanceKey) { 20 | return; 21 | } 22 | 23 | this.copyPasteStore.currentInstances = {}; 24 | 25 | const allChilds = this.viewportAction.getAllChilds(this.viewportStore.currentHoverInstanceKey); 26 | 27 | allChilds.forEach(instanceKey => { 28 | this.copyPasteStore.currentInstances[instanceKey] = _.cloneDeep( 29 | (this.viewportStore.instances.get(instanceKey) as any).$raw, 30 | ); 31 | }); 32 | 33 | this.copyPasteStore.currentInstances[this.viewportStore.currentHoverInstanceKey] = { 34 | ...(this.viewportStore.instances.get(this.viewportStore.currentHoverInstanceKey) as any).$raw, 35 | parentInstanceKey: null, 36 | }; 37 | } 38 | 39 | @Action 40 | public pasteToCurrentHoverInstance() { 41 | if (!this.viewportStore.currentHoverInstanceKey) { 42 | return; 43 | } 44 | 45 | if (this.copyPasteStore.currentInstances === null) { 46 | return; 47 | } 48 | 49 | const currentHoverInstanceInfo = this.viewportStore.instances.get(this.viewportStore.currentHoverInstanceKey); 50 | 51 | // If not container 52 | if (!this.viewportAction.getInstanceProps(this.viewportStore.currentHoverInstanceKey, 'editSetting.isContainer')) { 53 | return; 54 | } 55 | 56 | const pasteInstances: { [instanceKey: string]: InstanceInfo } = {}; 57 | 58 | // Generate new instance keys for copy instances. 59 | const instanceKeysMap = new Map(); 60 | Object.keys(this.copyPasteStore.currentInstances).forEach(oldInstanceKey => { 61 | const newInstanceKey = this.viewportAction.createNewInstanceKey(Array.from(instanceKeysMap.values())); 62 | instanceKeysMap.set(oldInstanceKey, newInstanceKey); 63 | pasteInstances[newInstanceKey] = { ...this.copyPasteStore.currentInstances[oldInstanceKey] }; 64 | }); 65 | 66 | // Replace pasteInstances keys 67 | Object.keys(pasteInstances).forEach(instanceKey => { 68 | const instanceInfo = pasteInstances[instanceKey]; 69 | if (instanceInfo.parentInstanceKey) { 70 | instanceInfo.parentInstanceKey = instanceKeysMap.get(instanceInfo.parentInstanceKey); 71 | } 72 | }); 73 | 74 | const rootInstanceKey = Object.keys(pasteInstances).find( 75 | instanceKey => !pasteInstances[instanceKey].parentInstanceKey, 76 | ); 77 | 78 | // Replace root instance parentKey 79 | pasteInstances[rootInstanceKey].parentInstanceKey = this.viewportStore.currentHoverInstanceKey; 80 | 81 | // Add instances 82 | Object.keys(pasteInstances).forEach(instanceKey => { 83 | const instanceInfo = pasteInstances[instanceKey]; 84 | this.viewportStore.instances.set(instanceKey, _.cloneDeep(instanceInfo)); 85 | }); 86 | 87 | // Add child for hover instance 88 | this.viewportStore.instances.get(this.viewportStore.currentHoverInstanceKey).childs.push(rootInstanceKey); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/plugins/copy-paste-keyboard/index.tsx: -------------------------------------------------------------------------------- 1 | import { Connect } from 'dob-react'; 2 | import * as React from 'react'; 3 | import { PureComponent } from '../../utils/react-helper'; 4 | import { CopyPasteAction } from './action'; 5 | import { Props, State } from './index.type'; 6 | import { CopyPasteStore } from './store'; 7 | 8 | // eslint-disable-next-line @typescript-eslint/no-var-requires 9 | const keymaster = require('keymaster'); 10 | 11 | @Connect 12 | class CopyPaste extends PureComponent { 13 | public componentDidMount() { 14 | keymaster('⌘+c, ctrl+c', this.copy); 15 | keymaster('⌘+v, ctrl+v', this.paste); 16 | } 17 | 18 | public componentWillUnmount() { 19 | keymaster.unbind('⌘+c, ctrl+c'); 20 | keymaster.unbind('⌘+v, ctrl+v'); 21 | } 22 | 23 | public render() { 24 | return null as any; 25 | } 26 | 27 | private copy = () => { 28 | this.props.actions.CopyPasteAction.copyCurrentHoverInstance(); 29 | }; 30 | 31 | private paste = () => { 32 | this.props.actions.CopyPasteAction.pasteToCurrentHoverInstance(); 33 | }; 34 | } 35 | 36 | export default { 37 | position: 'navbarRight', 38 | class: CopyPaste, 39 | actions: { 40 | CopyPasteAction, 41 | }, 42 | stores: { 43 | CopyPasteStore, 44 | }, 45 | }; 46 | -------------------------------------------------------------------------------- /src/plugins/copy-paste-keyboard/index.type.ts: -------------------------------------------------------------------------------- 1 | import { StoreProps } from '../../stores'; 2 | import { CopyPasteAction } from './action'; 3 | import { CopyPasteStore } from './store'; 4 | 5 | export class Props extends StoreProps<{ CopyPasteAction: CopyPasteAction }, { CopyPasteStore: CopyPasteStore }> {} 6 | export class State {} 7 | -------------------------------------------------------------------------------- /src/plugins/copy-paste-keyboard/store.ts: -------------------------------------------------------------------------------- 1 | import { inject } from 'dob'; 2 | 3 | export class CopyPasteStore { 4 | public currentInstances: { 5 | [instanceKey: string]: InstanceInfo; 6 | } = null; 7 | } 8 | -------------------------------------------------------------------------------- /src/plugins/crumbs/index.style.ts: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const Container = styled.div` 4 | display: flex; 5 | align-items: stretch; 6 | flex-grow: 1; 7 | background-color: whitesmoke; 8 | `; 9 | 10 | export const AutoWidthContainer = styled.div` 11 | display: flex; 12 | align-items: stretch; 13 | `; 14 | 15 | export const FooterItem = styled.div` 16 | position: relative; 17 | display: flex; 18 | align-items: center; 19 | padding-left: 8px; 20 | color: #666; 21 | font-size: 13px; 22 | &:hover { 23 | cursor: pointer; 24 | color: #146f8c; 25 | transition: color 0.2s; 26 | } 27 | &:last-child { 28 | color: #146f8c; 29 | } 30 | `; 31 | 32 | export const rightIconContainer = styled.div` 33 | overflow: hidden; 34 | position: relative; 35 | width: 20px; 36 | height: 25px; 37 | margin-left: 5px; 38 | `; 39 | 40 | export const rightIcon = styled.div` 41 | position: absolute; 42 | width: 25px; 43 | height: 25px; 44 | transform: rotate(45deg); 45 | right: 7px; 46 | border: 1px solid #ddd; 47 | `; 48 | -------------------------------------------------------------------------------- /src/plugins/crumbs/index.tsx: -------------------------------------------------------------------------------- 1 | import { Connect } from 'dob-react'; 2 | import * as React from 'react'; 3 | import * as Styled from './index.style'; 4 | import * as typings from './index.type'; 5 | 6 | @Connect 7 | class Crumbs extends React.Component { 8 | public static defaultProps = new typings.Props(); 9 | 10 | public state = new typings.State(); 11 | 12 | public render() { 13 | let childs: React.ReactElement[]; 14 | 15 | if (this.props.stores.ViewportStore.currentEditInstanceKey) { 16 | // 递归寻找这个组件父元素 17 | childs = this.props.actions.ViewportAction.getInstancePath( 18 | this.props.stores.ViewportStore.currentEditInstanceKey, 19 | ).map((instanceKey, index) => { 20 | const instance = this.props.stores.ViewportStore.instances.get(instanceKey); 21 | const componentClass = this.props.actions.ApplicationAction.getComponentClassByKey(instance.gaeaKey); 22 | const setting = this.props.actions.ApplicationAction.getSettingByInstance(instance); 23 | 24 | return ( 25 | 30 | {setting.name} 31 | 32 | 33 | 34 | 35 | 36 | ); 37 | }); 38 | } 39 | 40 | return ( 41 | 42 | {childs} 43 | 44 | ); 45 | } 46 | 47 | private handleClick(instanceKey: string) { 48 | this.props.actions.ViewportAction.setCurrentEditInstanceKey(instanceKey); 49 | } 50 | 51 | private handleHover(instanceKey: string) { 52 | this.props.actions.ViewportAction.setCurrentHoverInstanceKey(instanceKey); 53 | } 54 | 55 | private handleMouseLeave = () => { 56 | this.props.actions.ViewportAction.setCurrentHoverInstanceKey(null); 57 | }; 58 | } 59 | 60 | export default { 61 | position: 'bottomBar', 62 | class: Crumbs, 63 | }; 64 | -------------------------------------------------------------------------------- /src/plugins/crumbs/index.type.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { StoreProps } from '../../stores'; 3 | 4 | export class Props extends StoreProps {} 5 | 6 | export class State {} 7 | -------------------------------------------------------------------------------- /src/plugins/delete-keyboard/index.tsx: -------------------------------------------------------------------------------- 1 | import { Connect } from 'dob-react'; 2 | import * as React from 'react'; 3 | import { PureComponent } from '../../utils/react-helper'; 4 | 5 | // eslint-disable-next-line @typescript-eslint/no-var-requires 6 | const keymaster = require('keymaster'); 7 | 8 | @Connect 9 | class Delete extends PureComponent { 10 | public componentDidMount() { 11 | keymaster('backspace', this.delete); 12 | } 13 | 14 | public componentWillUnmount() { 15 | keymaster.unbind('backspace'); 16 | } 17 | 18 | public render() { 19 | return null as any; 20 | } 21 | 22 | private delete = () => { 23 | if (!this.props.stores.ViewportStore.currentHoverInstanceKey) { 24 | return; 25 | } 26 | this.props.actions.ViewportAction.removeInstance(this.props.stores.ViewportStore.currentHoverInstanceKey); 27 | }; 28 | } 29 | 30 | export default { 31 | position: 'navbarRight', 32 | class: Delete, 33 | }; 34 | -------------------------------------------------------------------------------- /src/plugins/drag-menu-button/index.tsx: -------------------------------------------------------------------------------- 1 | import { Tooltip } from 'antd'; 2 | import { Connect } from 'dob-react'; 3 | import * as React from 'react'; 4 | import Icon from '../../components/icon/src'; 5 | import { StoreProps } from '../../stores'; 6 | import * as Styled from './style'; 7 | 8 | export class Props extends StoreProps {} 9 | 10 | export class State {} 11 | 12 | @Connect 13 | class DragMenuButton extends React.Component { 14 | public static defaultProps = new Props(); 15 | 16 | public state = new State(); 17 | 18 | public render() { 19 | return ( 20 | 21 | 25 | 26 | 27 | 28 | ); 29 | } 30 | 31 | private handleClick = () => { 32 | if (this.props.stores.ApplicationStore.leftTool !== 'dragMenu') { 33 | this.props.actions.ApplicationAction.setLeftTool('dragMenu'); 34 | this.props.actions.ApplicationAction.setRightTool(null); 35 | } else { 36 | this.props.actions.ApplicationAction.setLeftTool(null); 37 | this.props.actions.ApplicationAction.setRightTool(null); 38 | } 39 | }; 40 | } 41 | 42 | export default { 43 | position: 'leftBarTop', 44 | class: DragMenuButton, 45 | }; 46 | -------------------------------------------------------------------------------- /src/plugins/drag-menu-button/style.ts: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const Container = styled.div` 4 | ${(props: any) => 5 | props.theme.active && 6 | ` 7 | background-color: white; 8 | box-shadow: inset 0 0 10px #cacaca; 9 | &:hover { 10 | background-color: white; 11 | } 12 | `}; 13 | `; 14 | -------------------------------------------------------------------------------- /src/plugins/drag-menu/index.style.ts: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const Component = styled.div` 4 | display: flex; 5 | align-items: center; 6 | height: 30px; 7 | padding: 0 10px; 8 | cursor: pointer; 9 | font-size: 14px; 10 | color: #666; 11 | background-color: #eee; 12 | transition: background-color 0.3s; 13 | &:hover { 14 | background-color: white; 15 | } 16 | border-bottom: 1px solid #ddd; 17 | `; 18 | 19 | export const Container = styled.div` 20 | display: flex; 21 | flex-direction: column; 22 | background-color: whitesmoke; 23 | flex-grow: 1; 24 | `; 25 | 26 | export const Title = styled.div` 27 | display: flex; 28 | justify-content: space-between; 29 | padding: 0 10px; 30 | height: 40px; 31 | font-size: 16px; 32 | align-items: center; 33 | color: #777; 34 | font-weight: bold; 35 | border-bottom: 1px solid #ddd; 36 | `; 37 | 38 | export const CloseContainer = styled.div` 39 | padding: 5px; 40 | cursor: pointer; 41 | fill: #999; 42 | align-items: center; 43 | &:hover { 44 | fill: #333; 45 | } 46 | `; 47 | 48 | export const SearchInput = styled.input` 49 | outline: none; 50 | padding: 5px 10px; 51 | font-size: 14px; 52 | color: #666; 53 | border-right: none; 54 | border-left: none; 55 | border-top: none; 56 | border-bottom: 1px solid #ddd; 57 | background-color: #fbfbfb; 58 | &:focus { 59 | background-color: white; 60 | color: #333; 61 | } 62 | &::-webkit-input-placeholder { 63 | color: #999; 64 | } 65 | `; 66 | 67 | export const ListContainer = styled.div` 68 | overflow-y: auto; 69 | flex-grow: 1; 70 | flex-basis: 0; 71 | `; 72 | -------------------------------------------------------------------------------- /src/plugins/drag-menu/index.tsx: -------------------------------------------------------------------------------- 1 | import { Connect } from 'dob-react'; 2 | import * as React from 'react'; 3 | import * as ReactDOM from 'react-dom'; 4 | import Icon from '../../components/icon/src'; 5 | import * as Styled from './index.style'; 6 | import { Props, State } from './index.type'; 7 | 8 | @Connect 9 | class DragMenu extends React.Component { 10 | public static defaultProps = new Props(); 11 | 12 | public state = new State(); 13 | 14 | private listContainer: React.ReactInstance; 15 | 16 | public componentDidMount() { 17 | this.props.actions.ViewportAction.registerOuterDrag(ReactDOM.findDOMNode(this.listContainer) as HTMLElement); 18 | } 19 | 20 | public render() { 21 | return ( 22 | 23 | 24 |
{this.props.stores.ApplicationStore.setLocale('拖拽组件', 'Drag Component')}
25 | 26 | 27 | 28 |
29 | 30 | 35 | 36 | { 38 | this.listContainer = ref; 39 | }} 40 | > 41 | {this.getList()} 42 | 43 | 44 | {this.props.actions.ApplicationAction.loadPluginByPosition('toolContainerDragMenuList')} 45 |
46 | ); 47 | } 48 | 49 | private getList = () => { 50 | return Array.from(this.props.stores.ApplicationStore.componentClasses) 51 | .filter(([gaeaKey, componentClass]) => { 52 | const setting = this.props.stores.ApplicationStore.componentSetting.get(gaeaKey); 53 | 54 | // 如果被设置为了预设组件,过滤掉 55 | if ( 56 | Array.from(this.props.stores.ApplicationStore.preComponents.keys()).some( 57 | preGaeaKey => preGaeaKey === setting.key, 58 | ) 59 | ) { 60 | return false; 61 | } 62 | 63 | // 如果搜索框没有输入,展示 64 | if (this.state.searchContent === '') { 65 | return true; 66 | } 67 | 68 | return new RegExp(this.state.searchContent).test(setting.name); 69 | }) 70 | .map(([gaeaKey, componentClass], index) => { 71 | const setting = this.props.stores.ApplicationStore.componentSetting.get(gaeaKey); 72 | 73 | return ( 74 | 75 | {setting.name} 76 | 77 | ); 78 | }) 79 | .concat(Array.from(this.props.stores.ApplicationStore.preComponents).map( 80 | ([gaeaKey, preComponentInfos], index) => { 81 | const componentClass = this.props.stores.ApplicationStore.componentClasses.get(gaeaKey); 82 | return Array.prototype.concat.apply( 83 | [], 84 | preComponentInfos 85 | .filter(preComponentInfo => { 86 | const setting = this.props.stores.ApplicationStore.componentSetting.get(gaeaKey); 87 | 88 | // 如果搜索框没有输入,展示 89 | if (this.state.searchContent === '') { 90 | return true; 91 | } 92 | 93 | return new RegExp(this.state.searchContent).test(setting.name); 94 | }) 95 | .map((preComponentInfo, childIndex) => { 96 | const setting = this.props.stores.ApplicationStore.componentSetting.get(gaeaKey); 97 | 98 | return ( 99 | 105 | {preComponentInfo.name} 106 | 107 | ); 108 | }), 109 | ); 110 | }, 111 | ) as any); 112 | }; 113 | 114 | private handleCloseLeftBar = () => { 115 | this.props.actions.ApplicationAction.setLeftTool(null); 116 | this.props.actions.ApplicationAction.setRightTool(null); 117 | }; 118 | 119 | private handleSearch = (event: React.FormEvent) => { 120 | this.setState({ 121 | searchContent: event.currentTarget.value as string, 122 | }); 123 | }; 124 | } 125 | 126 | export default { 127 | position: 'toolContainerLeftDragMenu', 128 | class: DragMenu, 129 | }; 130 | -------------------------------------------------------------------------------- /src/plugins/drag-menu/index.type.ts: -------------------------------------------------------------------------------- 1 | import { StoreProps } from '../../stores'; 2 | 3 | export class Props extends StoreProps {} 4 | 5 | export class State { 6 | /** 7 | * 当前搜索内容 8 | */ 9 | public searchContent = ''; 10 | } 11 | -------------------------------------------------------------------------------- /src/plugins/main-tool-editor-event-action/index.style.ts: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const Container = styled.div` 4 | display: flex; 5 | flex-direction: column; 6 | flex-grow: 1; 7 | `; 8 | 9 | export const Label = styled.div` 10 | font-size: 13px; 11 | color: #666; 12 | margin-bottom: 5px; 13 | white-space: nowrap; 14 | `; 15 | 16 | export const BodyContainer = styled.div` 17 | display: flex; 18 | flex-direction: column; 19 | flex-basis: 0; 20 | `; 21 | 22 | export const HeaderContainer = styled.div` 23 | display: flex; 24 | flex-direction: column; 25 | justify-content: space-between; 26 | `; 27 | 28 | export const DataContainer = styled.div` 29 | margin-top: 10px; 30 | flex-basis: 0; 31 | `; 32 | -------------------------------------------------------------------------------- /src/plugins/main-tool-editor-event-action/index.tsx: -------------------------------------------------------------------------------- 1 | import { Input, Select, Tooltip } from 'antd'; 2 | import { Connect } from 'dob-react'; 3 | import * as _ from 'lodash'; 4 | import * as React from 'react'; 5 | import * as ReactDOM from 'react-dom'; 6 | import Icon from '../../components/icon/src'; 7 | import { pipeEvent } from '../../utils/functional'; 8 | import * as S from './index.style'; 9 | import { Props, State } from './index.type'; 10 | 11 | // TODO: 12 | const SelectAny = Select as any; 13 | 14 | const ActionOptions = [ 15 | { 16 | key: 'none', 17 | value: 'Do nothing', 18 | }, 19 | { 20 | key: 'emit', 21 | value: 'Trigger Event', 22 | }, 23 | { 24 | key: 'jump', 25 | value: 'Jump url', 26 | }, 27 | // { 28 | // key: 'passingSiblingNodes', 29 | // value: 'Pass value to brother node' 30 | // } 31 | ].map((each, index) => { 32 | return ( 33 | 34 | {each.value} 35 | 36 | ); 37 | }); 38 | 39 | @Connect 40 | class MainToolEditorEventAction extends React.Component { 41 | public static defaultProps = new Props(); 42 | 43 | public state = new State(); 44 | 45 | /** 46 | * 组件实例的信息 47 | */ 48 | private instanceInfo: InstanceInfo; 49 | 50 | /** 51 | * 当前事件数据 52 | */ 53 | private currentEventInfo: InstanceInfoEvent = null; 54 | 55 | public render() { 56 | // 当前编辑组件的 key 57 | const instanceKey = this.props.stores.ViewportStore.currentEditInstanceKey; 58 | 59 | if (!this.props.stores.ViewportStore.instances.has(instanceKey)) { 60 | return null; 61 | } 62 | 63 | this.instanceInfo = this.props.stores.ViewportStore.instances.get(instanceKey); 64 | 65 | if (!this.instanceInfo.data.events) { 66 | return null; 67 | } 68 | 69 | this.currentEventInfo = this.instanceInfo.data.events[this.props.index]; 70 | 71 | if (!this.currentEventInfo) { 72 | return null; 73 | } 74 | 75 | return ( 76 | 77 | 78 | {this.props.stores.ApplicationStore.setLocale('动作', 'Action')} 79 | 82 | 83 | 84 | {this.renderActionBody()} 85 | 86 | ); 87 | } 88 | 89 | private handleChangeAction = (value: string) => { 90 | this.props.actions.ViewportAction.instanceSetEvent( 91 | this.props.stores.ViewportStore.currentEditInstanceKey, 92 | this.props.index, 93 | { 94 | ...this.currentEventInfo, 95 | action: { 96 | type: value as any, 97 | }, 98 | }, 99 | ); 100 | }; 101 | 102 | private renderActionBody = () => { 103 | switch (this.currentEventInfo.action.type) { 104 | case 'emit': 105 | return ( 106 | 107 | 117 | 118 | ); 119 | case 'passingSiblingNodes': 120 | return null; 121 | case 'jump': 122 | return ( 123 | 124 | 129 | 130 | ); 131 | case 'none': 132 | default: 133 | return null; 134 | } 135 | }; 136 | 137 | private handleChangeActionData = (key: string, value: string) => { 138 | this.props.actions.ViewportAction.setInstanceEvent( 139 | this.props.stores.ViewportStore.currentEditInstanceKey, 140 | `${this.props.index}.action.${key}`, 141 | value, 142 | ); 143 | }; 144 | } 145 | 146 | export default { 147 | position: 'mainToolEditorEventAction', 148 | class: MainToolEditorEventAction, 149 | }; 150 | -------------------------------------------------------------------------------- /src/plugins/main-tool-editor-event-action/index.type.ts: -------------------------------------------------------------------------------- 1 | import { StoreProps } from '../../stores'; 2 | 3 | export class Props extends StoreProps { 4 | public index?: number; 5 | } 6 | export class State {} 7 | -------------------------------------------------------------------------------- /src/plugins/main-tool-editor-event-trigger/index.style.ts: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const Container = styled.div` 4 | display: flex; 5 | flex-direction: column; 6 | flex-grow: 1; 7 | `; 8 | 9 | export const Label = styled.div` 10 | font-size: 13px; 11 | color: #666; 12 | margin-bottom: 5px; 13 | white-space: nowrap; 14 | `; 15 | 16 | export const HeaderContainer = styled.div` 17 | display: flex; 18 | flex-direction: column; 19 | justify-content: space-between; 20 | `; 21 | 22 | export const BodyContainer = styled.div` 23 | display: flex; 24 | flex-direction: column; 25 | flex-basis: 0; 26 | `; 27 | 28 | export const DataContainer = styled.div` 29 | margin-top: 10px; 30 | flex-basis: 0; 31 | `; 32 | -------------------------------------------------------------------------------- /src/plugins/main-tool-editor-event-trigger/index.tsx: -------------------------------------------------------------------------------- 1 | import { Select, Tooltip } from 'antd'; 2 | import { Connect } from 'dob-react'; 3 | import * as _ from 'lodash'; 4 | import * as React from 'react'; 5 | import * as ReactDOM from 'react-dom'; 6 | import Icon from '../../components/icon/src'; 7 | import * as S from './index.style'; 8 | import { Props, State } from './index.type'; 9 | 10 | const triggerOptions = [ 11 | { 12 | key: 'init', 13 | value: 'Init', 14 | }, 15 | { 16 | key: 'subscribe', 17 | value: 'Listen event', 18 | }, 19 | ]; 20 | 21 | @Connect 22 | class MainToolEditorEventTrigger extends React.Component { 23 | public static defaultProps = new Props(); 24 | 25 | public state = new State(); 26 | 27 | /** 28 | * 组件实例的信息 29 | */ 30 | private instanceInfo: InstanceInfo; 31 | 32 | /** 33 | * 设置 34 | */ 35 | private setting: IGaeaSetting; 36 | 37 | /** 38 | * 当前事件数据 39 | */ 40 | private currentEventInfo: InstanceInfoEvent = null; 41 | 42 | public render() { 43 | // 当前编辑组件的 key 44 | const instanceKey = this.props.stores.ViewportStore.currentEditInstanceKey; 45 | 46 | if (!this.props.stores.ViewportStore.instances.has(instanceKey)) { 47 | return null; 48 | } 49 | 50 | this.instanceInfo = this.props.stores.ViewportStore.instances.get(instanceKey); 51 | 52 | // 当前事件数据 53 | if (!this.instanceInfo.data.events) { 54 | return null; 55 | } 56 | 57 | this.currentEventInfo = this.instanceInfo.data.events[this.props.index]; 58 | 59 | if (!this.currentEventInfo) { 60 | return null; 61 | } 62 | 63 | this.setting = this.props.actions.ApplicationAction.getSettingByInstance(this.instanceInfo); 64 | 65 | const mergedTriggerOptions = triggerOptions.concat( 66 | (this.setting.events || []).map((event, index) => { 67 | return { key: `callback-${event.field}`, value: event.text }; 68 | }), 69 | ); 70 | const MergedTriggerOptions = mergedTriggerOptions.map((each, index) => { 71 | return ( 72 | 73 | {each.value} 74 | 75 | ); 76 | }); 77 | 78 | let triggerValue: string = this.currentEventInfo.trigger.type; 79 | 80 | if (this.currentEventInfo.trigger.type === 'callback') { 81 | triggerValue = `callback-${this.currentEventInfo.trigger.field}`; 82 | } 83 | 84 | return ( 85 | 86 | 87 | {this.props.stores.ApplicationStore.setLocale('触发', 'Trigger')} 88 | 91 | 92 | 93 | {this.renderTriggerBody()} 94 | 95 | ); 96 | } 97 | 98 | private handleChangeTrigger = (value: string) => { 99 | let type = value; 100 | let field = ''; 101 | 102 | if (type.startsWith('callback-')) { 103 | field = type.slice(9); 104 | type = 'callback'; 105 | } 106 | 107 | const eventInfo: InstanceInfoEvent = { 108 | // Refresh trigger and triggerData only. 109 | ...this.currentEventInfo, 110 | trigger: { 111 | type: type as any, 112 | }, 113 | }; 114 | 115 | switch (type) { 116 | case 'callback': 117 | (eventInfo.trigger as InstanceEventTriggerCallback).field = field; 118 | break; 119 | default: 120 | } 121 | 122 | this.props.actions.ViewportAction.instanceSetEvent( 123 | this.props.stores.ViewportStore.currentEditInstanceKey, 124 | this.props.index, 125 | eventInfo, 126 | ); 127 | }; 128 | 129 | private renderTriggerBody = () => { 130 | switch (this.currentEventInfo.trigger.type) { 131 | case 'init': 132 | return null; 133 | case 'callback': 134 | return null; 135 | case 'subscribe': 136 | return ( 137 | 138 | 40 | {data.map((each, index) => { 41 | return ( 42 | 43 | {each.text} 44 | 45 | ); 46 | })} 47 | 48 | 49 | ); 50 | } 51 | 52 | private handleChange = (value: SelectValue) => { 53 | this.props.actions.ViewportAction.setInstanceProps( 54 | this.props.stores.ViewportStore.currentEditInstanceKey, 55 | this.props.realField, 56 | value, 57 | ); 58 | }; 59 | } 60 | 61 | export default { 62 | position: 'mainToolEditorTypeSelect', 63 | class: MainToolEditorSelect, 64 | }; 65 | -------------------------------------------------------------------------------- /src/plugins/main-tool-editor-type-select/index.type.ts: -------------------------------------------------------------------------------- 1 | import { StoreProps } from '../../stores'; 2 | 3 | export class Props extends StoreProps { 4 | /** 5 | * Real value field 6 | */ 7 | public realField?: string; 8 | 9 | /** 10 | * Editor 11 | */ 12 | public editor?: IEditor; 13 | } 14 | 15 | export class State {} 16 | -------------------------------------------------------------------------------- /src/plugins/main-tool-editor-type-string/index.style.ts: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const Container = styled.div` 4 | display: flex; 5 | align-items: center; 6 | justify-content: flex-end; 7 | flex-grow: 1; 8 | flex-basis: 0; 9 | padding: 5px 10px; 10 | `; 11 | -------------------------------------------------------------------------------- /src/plugins/main-tool-editor-type-string/index.tsx: -------------------------------------------------------------------------------- 1 | import { Input } from 'antd'; 2 | import { Connect } from 'dob-react'; 3 | import * as _ from 'lodash'; 4 | import * as React from 'react'; 5 | import * as ReactDOM from 'react-dom'; 6 | import { pipeEvent } from '../../utils/functional'; 7 | import * as Styled from './index.style'; 8 | import { Props, State } from './index.type'; 9 | 10 | @Connect 11 | class MainToolEditorString extends React.Component { 12 | public static defaultProps = new Props(); 13 | 14 | public state = new State(); 15 | 16 | /** 17 | * 组件实例的信息 18 | */ 19 | private instanceInfo: InstanceInfo; 20 | 21 | public render() { 22 | if (!this.props.stores.ViewportStore.instances.has(this.props.stores.ViewportStore.currentEditInstanceKey)) { 23 | return null; 24 | } 25 | 26 | this.instanceInfo = this.props.stores.ViewportStore.instances.get( 27 | this.props.stores.ViewportStore.currentEditInstanceKey, 28 | ); 29 | 30 | let currentValue: string = this.props.actions.ViewportAction.getInstanceProps( 31 | this.props.stores.ViewportStore.currentEditInstanceKey, 32 | this.props.realField, 33 | ); 34 | 35 | currentValue = currentValue ? currentValue.toString() : ''; 36 | 37 | return ( 38 | 39 | 40 | 41 | ); 42 | } 43 | 44 | private handleChange = (value: string) => { 45 | this.props.actions.ViewportAction.setInstanceProps( 46 | this.props.stores.ViewportStore.currentEditInstanceKey, 47 | this.props.realField, 48 | value, 49 | ); 50 | }; 51 | } 52 | 53 | export default { 54 | position: 'mainToolEditorTypeString', 55 | class: MainToolEditorString, 56 | }; 57 | -------------------------------------------------------------------------------- /src/plugins/main-tool-editor-type-string/index.type.ts: -------------------------------------------------------------------------------- 1 | import { StoreProps } from '../../stores'; 2 | 3 | export class Props extends StoreProps { 4 | /** 5 | * injected 6 | */ 7 | public realField?: string; 8 | } 9 | 10 | export class State {} 11 | -------------------------------------------------------------------------------- /src/plugins/main-tool-editor-variable/index.style.ts: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const Container = styled.div` 4 | padding: 5px 0 5px 10px; 5 | display: flex; 6 | align-items: center; 7 | justify-content: space-between; 8 | `; 9 | -------------------------------------------------------------------------------- /src/plugins/main-tool-editor-variable/index.tsx: -------------------------------------------------------------------------------- 1 | import { Select } from 'antd'; 2 | import { SelectValue } from 'antd/lib/select'; 3 | import { Connect } from 'dob-react'; 4 | import * as _ from 'lodash'; 5 | import * as React from 'react'; 6 | import * as ReactDOM from 'react-dom'; 7 | import { pipeEvent } from '../../utils/functional'; 8 | import * as Styled from './index.style'; 9 | import { Props, State } from './index.type'; 10 | 11 | const sep = ':'; 12 | 13 | @Connect 14 | class MainToolEditorVariable extends React.Component { 15 | public static defaultProps = new Props(); 16 | 17 | public state = new State(); 18 | 19 | /** 20 | * 组件实例的信息 21 | */ 22 | private instanceInfo: InstanceInfo; 23 | 24 | public render() { 25 | if (!this.props.stores.ViewportStore.instances.has(this.props.stores.ViewportStore.currentEditInstanceKey)) { 26 | return null; 27 | } 28 | 29 | this.instanceInfo = this.props.stores.ViewportStore.instances.get( 30 | this.props.stores.ViewportStore.currentEditInstanceKey, 31 | ); 32 | 33 | let value = null; 34 | const variable = this.instanceInfo.variables && this.instanceInfo.variables[this.props.realField]; 35 | 36 | if (variable) { 37 | value = variable.type + sep + variable.key; 38 | } 39 | 40 | return ( 41 | 42 | 45 | 46 | ); 47 | } 48 | 49 | /** 50 | * 获得当前元素能得到的变量列表 51 | */ 52 | private getVariableOptions = () => { 53 | const options = []; 54 | 55 | // 先看看是否存在当前层级的变量 56 | const siblingOptions: { 57 | key: string; 58 | value: string; 59 | }[] = []; 60 | 61 | // 从同级实例中查找 62 | this.props.actions.ViewportAction.getSiblingInstances( 63 | this.props.stores.ViewportStore.currentEditInstanceKey, 64 | ).forEach(instance => { 65 | if (instance.data.events) { 66 | instance.data.events.forEach(event => { 67 | const params = this.props.actions.ViewportAction.eventGetSiblingParam(event); 68 | if (params) { 69 | // TODO: 70 | // params.forEach(param => { 71 | // siblingOptions.push({ 72 | // key: 'sibling:' + param, 73 | // value: param 74 | // }); 75 | // }); 76 | } 77 | }); 78 | } 79 | }); 80 | 81 | if (siblingOptions.length > 0) { 82 | options.push({ 83 | groupValue: this.props.stores.ApplicationStore.setLocale('当前层级', 'Current group'), 84 | children: siblingOptions, 85 | }); 86 | } 87 | 88 | return options; 89 | }; 90 | 91 | private handleSelectVariable = (value: SelectValue) => { 92 | // 使用 : 分割分类与值 93 | const splitValue = (value as string).split(sep); 94 | const type = splitValue[0] as InstanceInfoVariableType; 95 | const key = splitValue.slice(1, splitValue.length).join(sep); 96 | this.props.actions.ViewportAction.instanceSetVariable( 97 | this.props.stores.ViewportStore.currentEditInstanceKey, 98 | this.props.realField, 99 | { 100 | type, 101 | key, 102 | }, 103 | ); 104 | }; 105 | } 106 | 107 | export default { 108 | position: 'mainToolEditorVariable', 109 | class: MainToolEditorVariable, 110 | }; 111 | -------------------------------------------------------------------------------- /src/plugins/main-tool-editor-variable/index.type.ts: -------------------------------------------------------------------------------- 1 | import { StoreProps } from '../../stores'; 2 | 3 | export class Props extends StoreProps { 4 | /** 5 | * injected 6 | */ 7 | public realField?: string; 8 | } 9 | 10 | export class State {} 11 | -------------------------------------------------------------------------------- /src/plugins/main-tool-editor/index.style.ts: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | export const EmptyContainer = styled.div` 4 | flex-grow: 1; 5 | `; 6 | 7 | export const EmptyTitle = styled.div` 8 | display: flex; 9 | justify-content: center; 10 | color: #666; 11 | font-weight: bold; 12 | padding: 10px; 13 | margin-top: 10px; 14 | font-size: 14px; 15 | `; 16 | 17 | export const EmptyDescription = styled.div` 18 | margin: 15px 10px 0 10px; 19 | padding: 20px; 20 | color: #999; 21 | border: 1px solid #ddd; 22 | border-radius: 3px; 23 | font-size: 13px; 24 | `; 25 | 26 | export const Container = styled.div` 27 | flex-grow: 1; 28 | `; 29 | 30 | export const TabTitle = styled.div` 31 | padding: 5px 10px; 32 | font-size: 13px; 33 | color: #666; 34 | background-color: #eee; 35 | `; 36 | 37 | export const Nav = styled.div` 38 | display: flex; 39 | align-items: center; 40 | padding: 10px; 41 | `; 42 | 43 | export const Name = styled.div` 44 | font-weight: bold; 45 | `; 46 | 47 | export const RightContainer = styled.div` 48 | display: flex; 49 | justify-content: flex-end; 50 | flex-basis: 0; 51 | flex-grow: 1; 52 | `; 53 | -------------------------------------------------------------------------------- /src/plugins/main-tool-editor/index.tsx: -------------------------------------------------------------------------------- 1 | import { Button, Tooltip } from 'antd'; 2 | import { Connect } from 'dob-react'; 3 | import * as React from 'react'; 4 | import * as Styled from './index.style'; 5 | import { Props, State } from './index.type'; 6 | 7 | @Connect 8 | class MainToolEditor extends React.Component { 9 | public static defaultProps = new Props(); 10 | 11 | public state = new State(); 12 | 13 | /** 14 | * 组件的类 15 | */ 16 | private componentClass: React.ComponentClass; 17 | 18 | /** 19 | * 组件实例的信息 20 | */ 21 | private instanceInfo: InstanceInfo; 22 | 23 | /** 24 | * 组件的编辑信息 25 | */ 26 | private setting: IGaeaSetting; 27 | 28 | public render() { 29 | // 当前编辑组件的 key 30 | const instanceKey = this.props.stores.ViewportStore.currentEditInstanceKey; 31 | 32 | if (!instanceKey) { 33 | return ( 34 | 35 | 36 | {this.props.stores.ApplicationStore.setLocale('没有选择的组件', 'No component selected')} 37 | 38 | 39 | {this.props.stores.ApplicationStore.setLocale( 40 | '在屏幕左侧点击一个组件', 41 | 'Click one component in the left of the screen.', 42 | )} 43 | 44 | 45 | ); 46 | } 47 | 48 | if (!this.props.stores.ViewportStore.instances.has(instanceKey)) { 49 | return null; 50 | } 51 | 52 | this.instanceInfo = this.props.stores.ViewportStore.instances.get(instanceKey); 53 | 54 | this.componentClass = this.props.actions.ApplicationAction.getComponentClassByKey(this.instanceInfo.gaeaKey); 55 | 56 | // 优先从 preGaeaKey 取配置,因为可能是一个预设组件 57 | this.setting = this.props.actions.ApplicationAction.getSettingByInstance(this.instanceInfo); 58 | 59 | return ( 60 | 61 | 62 | {this.setting.name} 63 | 64 | {this.instanceInfo.parentInstanceKey !== null && ( 65 | 66 |