├── .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 | 
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 |
7 | );
8 |
--------------------------------------------------------------------------------
/src/components/icon/src/icons/add-folder.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export default (size: number) => (
4 |
7 | );
8 |
--------------------------------------------------------------------------------
/src/components/icon/src/icons/add.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export default (size: number) => (
4 |
7 | );
8 |
--------------------------------------------------------------------------------
/src/components/icon/src/icons/border-radius.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export default (size: number) => (
4 |
16 | );
17 |
--------------------------------------------------------------------------------
/src/components/icon/src/icons/border.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export default (size: number) => (
4 |
9 | );
10 |
--------------------------------------------------------------------------------
/src/components/icon/src/icons/center-line.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export default (size: number) => (
4 |
12 | );
13 |
--------------------------------------------------------------------------------
/src/components/icon/src/icons/close.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export default (size: number) => (
4 |
7 | );
8 |
--------------------------------------------------------------------------------
/src/components/icon/src/icons/component.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export default (size: number) => (
4 |
7 | );
8 |
--------------------------------------------------------------------------------
/src/components/icon/src/icons/dashed.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export default (size: number) => (
4 |
36 | );
37 |
--------------------------------------------------------------------------------
/src/components/icon/src/icons/database.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export default (size: number) => (
4 |
7 | );
8 |
--------------------------------------------------------------------------------
/src/components/icon/src/icons/display-block.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export default (size: number) => (
4 |
15 | );
16 |
--------------------------------------------------------------------------------
/src/components/icon/src/icons/display-flex.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export default (size: number) => (
4 |
46 | );
47 |
--------------------------------------------------------------------------------
/src/components/icon/src/icons/display-inline-block.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export default (size: number) => (
4 |
21 | );
22 |
--------------------------------------------------------------------------------
/src/components/icon/src/icons/display-inline.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export default (size: number) => (
4 |
11 | );
12 |
--------------------------------------------------------------------------------
/src/components/icon/src/icons/dotted.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export default (size: number) => (
4 |
36 | );
37 |
--------------------------------------------------------------------------------
/src/components/icon/src/icons/edit.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export default (size: number) => (
4 |
7 | );
8 |
--------------------------------------------------------------------------------
/src/components/icon/src/icons/eye-slash.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export default (size: number) => (
4 |
10 | );
11 |
--------------------------------------------------------------------------------
/src/components/icon/src/icons/eye.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export default (size: number) => (
4 |
16 | );
17 |
--------------------------------------------------------------------------------
/src/components/icon/src/icons/file.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export default (size: number) => (
4 |
7 | );
8 |
--------------------------------------------------------------------------------
/src/components/icon/src/icons/flex-align-center.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export default (size: number) => (
4 |
16 | );
17 |
--------------------------------------------------------------------------------
/src/components/icon/src/icons/flex-align-start.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export default (size: number) => (
4 |
15 | );
16 |
--------------------------------------------------------------------------------
/src/components/icon/src/icons/flex-align-stretch.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export default (size: number) => (
4 |
16 | );
17 |
--------------------------------------------------------------------------------
/src/components/icon/src/icons/flex-baseline.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export default (size: number) => (
4 |
37 | );
38 |
--------------------------------------------------------------------------------
/src/components/icon/src/icons/flex-direction-center.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export default (size: number) => (
4 |
16 | );
17 |
--------------------------------------------------------------------------------
/src/components/icon/src/icons/flex-direction-end.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export default (size: number) => (
4 |
15 | );
16 |
--------------------------------------------------------------------------------
/src/components/icon/src/icons/flex-row.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export default (size: number) => (
4 |
21 | );
22 |
--------------------------------------------------------------------------------
/src/components/icon/src/icons/flex-space-around.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export default (size: number) => (
4 |
18 | );
19 |
--------------------------------------------------------------------------------
/src/components/icon/src/icons/flex-space-between.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export default (size: number) => (
4 |
18 | );
19 |
--------------------------------------------------------------------------------
/src/components/icon/src/icons/folder.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export default (size: number) => (
4 |
7 | );
8 |
--------------------------------------------------------------------------------
/src/components/icon/src/icons/keybroad.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export default (size: number) => (
4 |
28 | );
29 |
--------------------------------------------------------------------------------
/src/components/icon/src/icons/page.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export default (size: number) => (
4 |
7 | );
8 |
--------------------------------------------------------------------------------
/src/components/icon/src/icons/position-relative.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export default (size: number) => (
4 |
21 | );
22 |
--------------------------------------------------------------------------------
/src/components/icon/src/icons/reload.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export default (size: number) => (
4 |
10 | );
11 |
--------------------------------------------------------------------------------
/src/components/icon/src/icons/remove.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export default (size: number) => (
4 |
8 | );
9 |
--------------------------------------------------------------------------------
/src/components/icon/src/icons/right-arrow.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export default (size: number) => (
4 |
7 | );
8 |
--------------------------------------------------------------------------------
/src/components/icon/src/icons/setting.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export default (size: number) => (
4 |
7 | );
8 |
--------------------------------------------------------------------------------
/src/components/icon/src/icons/solid.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export default (size: number) => (
4 |
16 | );
17 |
--------------------------------------------------------------------------------
/src/components/icon/src/icons/text-align-left.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export default (size: number) => (
4 |
10 | );
11 |
--------------------------------------------------------------------------------
/src/components/icon/src/icons/trash.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export default (size: number) => (
4 |
7 | );
8 |
--------------------------------------------------------------------------------
/src/components/icon/src/icons/under-line.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export default (size: number) => (
4 |
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 |
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 |
145 |
146 | );
147 | default:
148 | return null;
149 | }
150 | };
151 |
152 | private handleChangeTriggerData = (key: string, value: string) => {
153 | this.props.actions.ViewportAction.setInstanceEvent(
154 | this.props.stores.ViewportStore.currentEditInstanceKey,
155 | `${this.props.index}.trigger.${key}`,
156 | value,
157 | );
158 | };
159 | }
160 |
161 | export default {
162 | position: 'mainToolEditorEventTrigger',
163 | class: MainToolEditorEventTrigger,
164 | };
165 |
--------------------------------------------------------------------------------
/src/plugins/main-tool-editor-event-trigger/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/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 TabTitle = styled.div`
10 | display: flex;
11 | align-items: center;
12 | padding: 5px 10px;
13 | font-size: 14px;
14 | color: #666;
15 | background-color: #eee;
16 | width: 100%;
17 | font-weight: bold;
18 | `;
19 |
20 | export const AddButton = styled.div`
21 | margin-left: 10px;
22 | fill: #666;
23 | cursor: pointer;
24 | transition: color 0.3s;
25 | &:hover {
26 | fill: #333;
27 | }
28 | `;
29 |
30 | export const EventContainer = styled.div`
31 | position: relative;
32 | display: flex;
33 | height: 100px;
34 | padding: 10px;
35 | flex-direction: row;
36 | &:not(:last-child) {
37 | border-bottom: 1px dotted #ddd;
38 | }
39 | `;
40 |
41 | export const EventTrigger = styled.div`
42 | display: flex;
43 | flex-direction: column;
44 | flex-grow: 1;
45 | flex-basis: 0;
46 | margin-right: 5px;
47 | `;
48 |
49 | export const EventAction = styled.div`
50 | display: flex;
51 | flex-direction: column;
52 | flex-grow: 1;
53 | flex-basis: 0;
54 | `;
55 |
56 | export const EventList = styled.div`
57 | display: flex;
58 | flex-direction: column;
59 | `;
60 |
61 | export const RemoveIconContainer = styled.div`
62 | position: absolute;
63 | right: 10px;
64 | top: 12px;
65 | fill: #999;
66 | cursor: pointer;
67 | transition: color 0.3s;
68 | &:hover {
69 | fill: #333;
70 | }
71 | `;
72 |
--------------------------------------------------------------------------------
/src/plugins/main-tool-editor-event/index.tsx:
--------------------------------------------------------------------------------
1 | import { 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 | @Connect
11 | class MainToolEditorEvent 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 | // 当前编辑组件的 key
23 | const instanceKey = this.props.stores.ViewportStore.currentEditInstanceKey;
24 |
25 | if (!this.props.stores.ViewportStore.instances.has(instanceKey)) {
26 | return null;
27 | }
28 |
29 | this.instanceInfo = this.props.stores.ViewportStore.instances.get(instanceKey);
30 |
31 | const Events =
32 | this.instanceInfo.data &&
33 | this.instanceInfo.data.events &&
34 | this.instanceInfo.data.events.map((event, index) => {
35 | return (
36 |
37 |
38 | {this.props.actions.ApplicationAction.loadPluginByPosition(`mainToolEditorEventTrigger`, {
39 | index,
40 | })}
41 |
42 |
43 |
44 | {this.props.actions.ApplicationAction.loadPluginByPosition(`mainToolEditorEventAction`, {
45 | index,
46 | })}
47 |
48 |
49 |
53 |
54 |
55 |
56 |
57 |
58 | );
59 | });
60 |
61 | return (
62 |
63 |
64 | {this.props.stores.ApplicationStore.setLocale('事件', 'Event')}
65 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | {Events}
76 |
77 | );
78 | }
79 |
80 | private handleAdd = () => {
81 | this.props.actions.ViewportAction.instanceAddEvent(this.props.stores.ViewportStore.currentEditInstanceKey);
82 | };
83 |
84 | private handleRemove = (index: number) => {
85 | this.props.actions.ViewportAction.instanceRemoveEvent(
86 | this.props.stores.ViewportStore.currentEditInstanceKey,
87 | index,
88 | );
89 | };
90 | }
91 |
92 | export default {
93 | position: 'mainToolEditorEvent',
94 | class: MainToolEditorEvent,
95 | };
96 |
--------------------------------------------------------------------------------
/src/plugins/main-tool-editor-event/index.type.ts:
--------------------------------------------------------------------------------
1 | import { StoreProps } from '../../stores';
2 |
3 | export class Props extends StoreProps {}
4 | export class State {}
5 |
--------------------------------------------------------------------------------
/src/plugins/main-tool-editor-manager/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 | background-color: whitesmoke;
8 | `;
9 |
10 | export const ComponentName = styled.div`
11 | display: flex;
12 | padding: 7px 10px;
13 | font-size: 14px;
14 | color: #666;
15 | font-weight: bold;
16 | border-bottom: 1px solid #ddd;
17 | `;
18 |
19 | export const TabTitle = styled.div`
20 | padding: 5px 10px;
21 | font-size: 14px;
22 | color: #666;
23 | background-color: #eee;
24 | width: 100%;
25 | font-weight: bold;
26 | `;
27 |
28 | export const EditorContainer = styled.div`
29 | display: flex;
30 | position: relative;
31 | justify-content: space-between;
32 | ${(props: any) =>
33 | props.theme.isObjectType &&
34 | `
35 | flex-direction: column;
36 | justify-content: flex-start;
37 | `};
38 | `;
39 |
40 | export const EditorBoxContainer = styled.div`
41 | display: flex;
42 | justify-content: flex-end;
43 | flex-grow: 1;
44 | `;
45 |
46 | export const Variable = styled.div`
47 | display: none;
48 | /* display: flex; */
49 | align-items: center;
50 | justify-content: center;
51 | position: absolute;
52 | right: 0;
53 | top: 0;
54 | width: 30px;
55 | height: 20px;
56 | background-color: #eee;
57 | border-left: 1px solid #ddd;
58 | border-bottom: 1px solid #ddd;
59 | border-top: 1px solid #ddd;
60 | border-bottom-left-radius: 5px;
61 | cursor: pointer;
62 | fill: #666;
63 | &:hover {
64 | background-color: white;
65 | fill: #333;
66 | }
67 | ${(props: any) =>
68 | props.theme.isVariable &&
69 | `
70 | background-color: #cef1ff;
71 | &:hover {
72 | background-color: #e4f7ff;
73 | }
74 | `};
75 | `;
76 |
77 | export const Label = styled.div`
78 | display: flex;
79 | flex-direction: row;
80 | align-items: center;
81 | font-size: 14px;
82 | color: #666;
83 | white-space: nowrap;
84 | padding: 5px 0;
85 | margin-left: 10px;
86 | ${(props: any) =>
87 | props.theme.isObjectType &&
88 | `
89 | align-items: flex-start;
90 | `};
91 | `;
92 |
93 | export const AddButton = styled.div`
94 | margin-left: 10px;
95 | fill: #666;
96 | cursor: pointer;
97 | transition: color 0.3s;
98 | &:hover {
99 | fill: #333;
100 | }
101 | `;
102 |
--------------------------------------------------------------------------------
/src/plugins/main-tool-editor-manager/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 | public editors?: IEditor[];
10 | }
11 | export class State {
12 | /**
13 | * 记录 array object 等类型数据展开状态
14 | * 属性 key 形成规则 `${type}_${field}`
15 | */
16 | public expandStates: Map = new Map();
17 | }
18 |
--------------------------------------------------------------------------------
/src/plugins/main-tool-editor-type-array/index.style.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const Container = styled.div`
4 | padding: 5px 0 5px 10px;
5 | margin-left: 10px;
6 | display: flex;
7 | flex-grow: 1;
8 | `;
9 |
10 | export const AddButton = styled.div`
11 | display: flex;
12 | margin-right: 10px;
13 | fill: #666;
14 | cursor: pointer;
15 | transition: color 0.3s;
16 | margin-left: -10px;
17 | &:hover {
18 | fill: #333;
19 | background-color: white;
20 | }
21 | transition: background-color 0.3s;
22 | background-color: #eee;
23 | border: 1px solid #ddd;
24 | padding: 2px 3px;
25 | border-radius: 3px;
26 | `;
27 |
28 | export const ListContainer = styled.div`
29 | display: flex;
30 | flex-grow: 1;
31 | flex-direction: column;
32 | border-left: 1px solid #ddd;
33 | `;
34 |
35 | export const ChildContainer = styled.div`
36 | padding: 5px;
37 | display: flex;
38 | flex-direction: column;
39 | `;
40 |
41 | export const EachItem = styled.div`
42 | position: relative;
43 | &:not(:last-child) {
44 | border-bottom: 1px solid #ddd;
45 | }
46 | `;
47 |
48 | export const RemoveIconContainer = styled.div`
49 | position: absolute;
50 | left: -7px;
51 | top: calc(50% - 7px);
52 | background-color: whitesmoke;
53 | fill: #999;
54 | cursor: pointer;
55 | transition: color 0.3s;
56 | &:hover {
57 | fill: #333;
58 | }
59 | `;
60 |
--------------------------------------------------------------------------------
/src/plugins/main-tool-editor-type-array/index.tsx:
--------------------------------------------------------------------------------
1 | import { 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 Styled from './index.style';
8 | import { Props, State } from './index.type';
9 |
10 | @Connect
11 | class MainToolEditorArray 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 | // 数组配置
31 | const editors = this.props.editor.data as IEditor[];
32 |
33 | // 当前控制数组的值
34 | const currentValue: any[] =
35 | this.props.actions.ViewportAction.getInstanceProps(
36 | this.props.stores.ViewportStore.currentEditInstanceKey,
37 | this.props.realField,
38 | ) || [];
39 |
40 | // 增加 删除
41 | const Editors = currentValue.map((eachValue, index) => {
42 | return (
43 |
44 | {this.props.actions.ApplicationAction.loadPluginByPosition('mainToolEditorManager', {
45 | editors,
46 | realField: `${this.props.realField}.${index}`,
47 | })}
48 |
49 |
50 |
51 |
52 |
53 |
54 | );
55 | });
56 |
57 | return (
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | {currentValue.length > 0 && {Editors}}
66 |
67 | );
68 | }
69 |
70 | private handleAdd = () => {
71 | const currentValue: any[] =
72 | this.props.actions.ViewportAction.getInstanceProps(
73 | this.props.stores.ViewportStore.currentEditInstanceKey,
74 | this.props.realField,
75 | ) || [];
76 |
77 | const assignValue = [...currentValue];
78 |
79 | if (typeof this.props.editor.data === 'string') {
80 | assignValue.push(null);
81 | } else {
82 | // 对象类型
83 | assignValue.push({});
84 | }
85 |
86 | this.props.actions.ViewportAction.setInstanceProps(
87 | this.props.stores.ViewportStore.currentEditInstanceKey,
88 | this.props.realField,
89 | assignValue,
90 | );
91 | };
92 |
93 | private handleRemove = (index: number) => {
94 | const currentValue: any[] =
95 | this.props.actions.ViewportAction.getInstanceProps(
96 | this.props.stores.ViewportStore.currentEditInstanceKey,
97 | this.props.realField,
98 | ) || [];
99 |
100 | const assignValue = [...currentValue];
101 |
102 | // 将此项从数组中移除
103 | assignValue.splice(index, 1);
104 |
105 | this.props.actions.ViewportAction.setInstanceProps(
106 | this.props.stores.ViewportStore.currentEditInstanceKey,
107 | this.props.realField,
108 | assignValue,
109 | );
110 | };
111 | }
112 |
113 | export default {
114 | position: 'mainToolEditorTypeArray',
115 | class: MainToolEditorArray,
116 | };
117 |
--------------------------------------------------------------------------------
/src/plugins/main-tool-editor-type-array/index.type.ts:
--------------------------------------------------------------------------------
1 | import { StoreProps } from '../../stores';
2 |
3 | export class Props extends StoreProps {
4 | public editor?: IEditor;
5 |
6 | public realField?: string = '';
7 | }
8 |
9 | export class State {}
10 |
--------------------------------------------------------------------------------
/src/plugins/main-tool-editor-type-boolean/index.style.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const Container = styled.div`
4 | padding: 5px 30px 5px 0;
5 | display: flex;
6 | align-items: center;
7 | justify-content: flex-end;
8 | flex-grow: 1;
9 | `;
10 |
--------------------------------------------------------------------------------
/src/plugins/main-tool-editor-type-boolean/index.tsx:
--------------------------------------------------------------------------------
1 | import { Switch } 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 * as Styled from './index.style';
7 | import { Props, State } from './index.type';
8 |
9 | @Connect
10 | class MainToolEditorBoolean extends React.Component {
11 | public static defaultProps = new Props();
12 |
13 | public state = new State();
14 |
15 | /**
16 | * 组件实例的信息
17 | */
18 | private instanceInfo: InstanceInfo;
19 |
20 | public render() {
21 | if (!this.props.stores.ViewportStore.instances.has(this.props.stores.ViewportStore.currentEditInstanceKey)) {
22 | return null;
23 | }
24 |
25 | this.instanceInfo = this.props.stores.ViewportStore.instances.get(
26 | this.props.stores.ViewportStore.currentEditInstanceKey,
27 | );
28 |
29 | const currentValue = !!this.props.actions.ViewportAction.getInstanceProps(
30 | this.props.stores.ViewportStore.currentEditInstanceKey,
31 | this.props.realField,
32 | );
33 |
34 | return (
35 |
36 |
37 |
38 | );
39 | }
40 |
41 | private handleChange = (value: boolean) => {
42 | this.props.actions.ViewportAction.setInstanceProps(
43 | this.props.stores.ViewportStore.currentEditInstanceKey,
44 | this.props.realField,
45 | value,
46 | );
47 | };
48 | }
49 |
50 | export default {
51 | position: 'mainToolEditorTypeBoolean',
52 | class: MainToolEditorBoolean,
53 | };
54 |
--------------------------------------------------------------------------------
/src/plugins/main-tool-editor-type-boolean/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-type-box-editor/index.style.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const Container = styled.div`
4 | display: flex;
5 | flex-grow: 1;
6 | justify-content: center;
7 | padding: 5px 0 5px 0;
8 | `;
9 |
--------------------------------------------------------------------------------
/src/plugins/main-tool-editor-type-box-editor/index.tsx:
--------------------------------------------------------------------------------
1 | import { Connect } from 'dob-react';
2 | import * as React from 'react';
3 | import * as ReactDOM from 'react-dom';
4 | import { BoxEditor } from '../../components/box-editor/src';
5 | import * as Styled from './index.style';
6 | import { Props, State } from './index.type';
7 |
8 | @Connect
9 | class MainToolEditorColor extends React.Component {
10 | public static defaultProps = new Props();
11 |
12 | public state = new State();
13 |
14 | /**
15 | * 组件实例的信息
16 | */
17 | private instanceInfo: InstanceInfo;
18 |
19 | public render() {
20 | if (!this.props.stores.ViewportStore.instances.has(this.props.stores.ViewportStore.currentEditInstanceKey)) {
21 | return null;
22 | }
23 |
24 | this.instanceInfo = this.props.stores.ViewportStore.instances.get(
25 | this.props.stores.ViewportStore.currentEditInstanceKey,
26 | );
27 |
28 | const style: React.CSSProperties =
29 | this.props.actions.ViewportAction.getInstanceProps(
30 | this.props.stores.ViewportStore.currentEditInstanceKey,
31 | 'style',
32 | ) || {};
33 |
34 | return (
35 |
36 |
49 |
50 | );
51 | }
52 |
53 | private handleStart = () => {
54 | //
55 | };
56 |
57 | private handleChange = (name: string, value: number) => {
58 | this.props.actions.ViewportAction.setInstanceProps(
59 | this.props.stores.ViewportStore.currentEditInstanceKey,
60 | `style.${name}`,
61 | value,
62 | );
63 | };
64 |
65 | private handleFinalChange = (name: string, value: number) => {
66 | //
67 | };
68 | }
69 |
70 | export default {
71 | position: 'mainToolEditorTypeBoxEditor',
72 | class: MainToolEditorColor,
73 | };
74 |
--------------------------------------------------------------------------------
/src/plugins/main-tool-editor-type-box-editor/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-type-color/index.style.ts:
--------------------------------------------------------------------------------
1 | import styled, { createGlobalStyle } from 'styled-components';
2 |
3 | // tslint:disable-next-line:no-unused-expression
4 | export const GlobalStyle = createGlobalStyle`
5 | .gaea-container {
6 | border: 1px dotted #ccc;
7 | }
8 |
9 | .gaea-draggable {
10 |
11 | }
12 |
13 | .gaea-highlight {
14 | outline-offset: -1px !important;
15 | outline: 1px solid #75b1ff !important;
16 | }
17 |
18 | .main-tool-editor-type-color {
19 | .ant-tooltip-inner {
20 | background-color: transparent;
21 | box-shadow: none;
22 | }
23 | .ant-tooltip-arrow {
24 | display: none;
25 | }
26 | }
27 | `;
28 |
29 | export const Container = styled.div`
30 | padding: 5px 0 5px 0;
31 | display: flex;
32 | align-items: center;
33 | justify-content: flex-end;
34 | flex-grow: 1;
35 | margin-right: 35px;
36 | `;
37 |
38 | export const ColorContainer = styled.div`
39 | display: flex;
40 | justify-content: center;
41 | align-items: center;
42 | width: 23px;
43 | height: 20px;
44 | border-radius: 3px;
45 | border: 1px solid #ddd;
46 | background-color: white;
47 | cursor: pointer;
48 | &:hover {
49 | border-color: #ccc;
50 | }
51 | `;
52 |
53 | export const ColorBox = styled.div`
54 | width: 15px;
55 | height: 14px;
56 | border-radius: 3px;
57 | border: 1px solid #eee;
58 | `;
59 |
--------------------------------------------------------------------------------
/src/plugins/main-tool-editor-type-color/index.tsx:
--------------------------------------------------------------------------------
1 | import { Tooltip } from 'antd';
2 | import { Connect } from 'dob-react';
3 | import * as _ from 'lodash';
4 | import * as React from 'react';
5 | import { ChromePicker } from 'react-color';
6 | import * as ReactDOM from 'react-dom';
7 | import * as Styled from './index.style';
8 | import { Props, State } from './index.type';
9 |
10 | @Connect
11 | class MainToolEditorColor 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 | }
45 | >
46 |
47 |
48 |
49 |
50 |
51 | );
52 | }
53 |
54 | private handleChangeComplete = (color: any) => {
55 | this.props.actions.ViewportAction.setInstanceProps(
56 | this.props.stores.ViewportStore.currentEditInstanceKey,
57 | this.props.realField,
58 | `rgba(${color.rgb.r}, ${color.rgb.g}, ${color.rgb.b}, ${color.rgb.a})`,
59 | );
60 | };
61 | }
62 |
63 | export default {
64 | position: 'mainToolEditorTypeColor',
65 | class: MainToolEditorColor,
66 | };
67 |
--------------------------------------------------------------------------------
/src/plugins/main-tool-editor-type-color/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-type-display/index.style.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const Container = styled.div`
4 | display: flex;
5 | flex-direction: column;
6 | justify-content: center;
7 | flex-grow: 1;
8 | margin: 10px 15px;
9 | padding: 5px;
10 | border: 1px solid #eee;
11 | border-radius: 5px;
12 |
13 | .rotate-45 {
14 | transform: rotate(45deg);
15 | }
16 |
17 | .rotate-90 {
18 | transform: rotate(90deg);
19 | }
20 |
21 | .rotate-135 {
22 | transform: rotate(135deg);
23 | }
24 |
25 | .rotate-180 {
26 | transform: rotate(180deg);
27 | }
28 |
29 | .rotate-270 {
30 | transform: rotate(270deg);
31 | }
32 | `;
33 |
34 | export const DisplayContainer = styled.div`
35 | display: flex;
36 | justify-content: space-between;
37 | `;
38 |
39 | export const FlexContainer = styled.div`
40 | display: flex;
41 | flex-direction: column;
42 | justify-content: center;
43 | `;
44 |
45 | export const FlexRow = styled.div`
46 | display: flex;
47 | justify-content: space-between;
48 | `;
49 |
--------------------------------------------------------------------------------
/src/plugins/main-tool-editor-type-display/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-type-number/index.style.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const Container = styled.div`
4 | padding: 5px 10px;
5 | display: flex;
6 | align-items: center;
7 | justify-content: flex-end;
8 | flex-grow: 1;
9 | flex-basis: 0;
10 | `;
11 |
12 | export const SliderContainer = styled.div`
13 | width: 100%;
14 | margin-right: 5px;
15 | `;
16 |
--------------------------------------------------------------------------------
/src/plugins/main-tool-editor-type-number/index.tsx:
--------------------------------------------------------------------------------
1 | import { InputNumber, Slider } from 'antd';
2 | import { SliderValue } from 'antd/lib/slider';
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 * as S from './index.style';
8 | import { Props, State } from './index.type';
9 |
10 | // 根据 inputRange outputRange 转换值
11 | const parseInputToOutRange = (value: number | string, inputRange: number[], outputRange: number[]) => {
12 | if (value === undefined || value === null) {
13 | return null;
14 | }
15 |
16 | // eslint-disable-next-line no-param-reassign
17 | value = Number(value);
18 |
19 | if (inputRange[0] === outputRange[0] && inputRange[1] === outputRange[1]) {
20 | return value;
21 | }
22 |
23 | if (value >= inputRange[0] && value <= inputRange[1]) {
24 | // 给的值必须在 input 范围内
25 | // 转换成 0~1 的小数
26 | const percentage = (value - inputRange[0]) / (inputRange[1] - inputRange[0]);
27 | // 转换成 output 的长度
28 | const outputLength = (outputRange[1] - outputRange[0]) * percentage;
29 | // 数值是加上最小值
30 | // eslint-disable-next-line no-param-reassign
31 | value = outputLength + outputRange[0];
32 | }
33 | return value;
34 | };
35 |
36 | @Connect
37 | class MainToolEditorNumber extends React.Component {
38 | public static defaultProps = new Props();
39 |
40 | public state = new State();
41 |
42 | /**
43 | * 组件实例的信息
44 | */
45 | private instanceInfo: InstanceInfo;
46 |
47 | private numberData?: IEditorNumberData;
48 |
49 | public render() {
50 | if (!this.props.stores.ViewportStore.instances.has(this.props.stores.ViewportStore.currentEditInstanceKey)) {
51 | return null;
52 | }
53 |
54 | this.instanceInfo = this.props.stores.ViewportStore.instances.get(
55 | this.props.stores.ViewportStore.currentEditInstanceKey,
56 | );
57 |
58 | let currentValue: number = this.props.actions.ViewportAction.getInstanceProps(
59 | this.props.stores.ViewportStore.currentEditInstanceKey,
60 | this.props.realField,
61 | );
62 |
63 | this.numberData = {
64 | useSlider: false,
65 | step: 1,
66 | inputRange: [-9999999, 9999999],
67 | ...((this.props.editor.data as any) || {}),
68 | };
69 |
70 | // 如果只设置了 inputRange, 默认 outputRange 要与其相同
71 | if (this.numberData.inputRange && !this.numberData.outputRange) {
72 | this.numberData.outputRange = this.numberData.inputRange.slice();
73 | }
74 |
75 | // 当前值转换成 inputValue
76 | currentValue = parseInputToOutRange(currentValue, this.numberData.outputRange, this.numberData.inputRange);
77 |
78 | return (
79 |
80 | {currentValue !== null && this.numberData.useSlider && (
81 |
82 |
89 |
90 | )}
91 |
92 |
99 |
100 | );
101 | }
102 |
103 | private handleChange = (value: number | string | undefined) => {
104 | // 转换
105 | // eslint-disable-next-line no-param-reassign
106 | value = parseInputToOutRange(value, this.numberData.inputRange, this.numberData.outputRange);
107 |
108 | this.props.actions.ViewportAction.setInstanceProps(
109 | this.props.stores.ViewportStore.currentEditInstanceKey,
110 | this.props.realField,
111 | value,
112 | );
113 | };
114 |
115 | private handleSliderChange = (value: SliderValue) => {
116 | this.handleChange(value as number);
117 | };
118 | }
119 |
120 | export default {
121 | position: 'mainToolEditorTypeNumber',
122 | class: MainToolEditorNumber,
123 | };
124 |
--------------------------------------------------------------------------------
/src/plugins/main-tool-editor-type-number/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 | public editor?: IEditor;
10 | }
11 |
12 | export class State {}
13 |
--------------------------------------------------------------------------------
/src/plugins/main-tool-editor-type-object/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 | flex-direction: column;
7 | flex-grow: 1;
8 | `;
9 |
10 | export const ChildContainer = styled.div`
11 | padding: 5px;
12 | display: flex;
13 | flex-direction: column;
14 | `;
15 |
16 | export const EachItem = styled.div`
17 | position: relative;
18 | border-left: 1px solid #ddd;
19 | &:not(:last-child) {
20 | border-bottom: 1px solid #ddd;
21 | }
22 | `;
23 |
--------------------------------------------------------------------------------
/src/plugins/main-tool-editor-type-object/index.tsx:
--------------------------------------------------------------------------------
1 | import { 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 Styled from './index.style';
9 | import { Props, State } from './index.type';
10 |
11 | @Connect
12 | class MainToolEditorObject extends React.Component {
13 | public static defaultProps = new Props();
14 |
15 | public state = new State();
16 |
17 | /**
18 | * 组件实例的信息
19 | */
20 | private instanceInfo: InstanceInfo;
21 |
22 | public render() {
23 | if (!this.props.stores.ViewportStore.instances.has(this.props.stores.ViewportStore.currentEditInstanceKey)) {
24 | return null;
25 | }
26 |
27 | this.instanceInfo = this.props.stores.ViewportStore.instances.get(
28 | this.props.stores.ViewportStore.currentEditInstanceKey,
29 | );
30 |
31 | // 对象配置
32 | const editors = this.props.editor.data as IEditor[];
33 |
34 | const Editors = editors.map((editor, index) => {
35 | return (
36 |
37 | {this.props.actions.ApplicationAction.loadPluginByPosition('mainToolEditorManager', {
38 | editors: [editor],
39 | realField: this.props.realField,
40 | })}
41 |
42 | );
43 | });
44 |
45 | return {Editors};
46 | }
47 | }
48 |
49 | export default {
50 | position: 'mainToolEditorTypeObject',
51 | class: MainToolEditorObject,
52 | };
53 |
--------------------------------------------------------------------------------
/src/plugins/main-tool-editor-type-object/index.type.ts:
--------------------------------------------------------------------------------
1 | import { StoreProps } from '../../stores';
2 |
3 | export class Props extends StoreProps {
4 | public editor?: IEditor;
5 |
6 | public realField?: string = '';
7 | }
8 |
9 | export class State {}
10 |
--------------------------------------------------------------------------------
/src/plugins/main-tool-editor-type-select/index.style.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const Container = styled.div`
4 | padding: 5px 0 5px 0;
5 | display: flex;
6 | align-items: center;
7 | justify-content: flex-end;
8 | flex-grow: 1;
9 | `;
10 |
--------------------------------------------------------------------------------
/src/plugins/main-tool-editor-type-select/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 * as Styled from './index.style';
8 | import { Props, State } from './index.type';
9 |
10 | @Connect
11 | class MainToolEditorSelect 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 | const currentValue: string = this.props.actions.ViewportAction.getInstanceProps(
31 | this.props.stores.ViewportStore.currentEditInstanceKey,
32 | this.props.realField,
33 | );
34 |
35 | const data = this.props.editor.data as IEditorSelectData;
36 |
37 | return (
38 |
39 |
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 |
67 |
68 | )}
69 |
70 |
71 | {this.renderEditor(this.setting)}
72 |
73 | );
74 | }
75 |
76 | private renderEditor(setting: IGaeaSetting) {
77 | if (!this.setting || !this.setting.editors || this.setting.editors.length === 0) {
78 | return (
79 |
80 |
81 | {this.props.stores.ApplicationStore.setLocale('无编辑信息', 'No edit info')}
82 |
83 |
84 | {this.props.stores.ApplicationStore.setLocale(
85 | '该组件还未添加编辑信息,',
86 | 'This component has no edit info yet,',
87 | )}
88 |
89 | {this.props.stores.ApplicationStore.setLocale('点击了解如何添加', 'Click to know learn it')}
90 |
91 |
92 |
93 | );
94 | }
95 |
96 | return [
97 | this.props.actions.ApplicationAction.loadPluginByPosition('mainToolEditorManager'),
98 | this.props.actions.ApplicationAction.loadPluginByPosition('mainToolEditorEvent'),
99 | this.props.actions.ApplicationAction.loadPluginByPosition('mainToolEditorAddon'),
100 | ];
101 | }
102 |
103 | private removeCurrentInstance = () => {
104 | this.props.actions.ViewportAction.removeInstance(this.props.stores.ViewportStore.currentEditInstanceKey);
105 | };
106 | }
107 |
108 | export default {
109 | position: 'mainToolEditor',
110 | class: MainToolEditor,
111 | };
112 |
--------------------------------------------------------------------------------
/src/plugins/main-tool-editor/index.type.ts:
--------------------------------------------------------------------------------
1 | import { StoreProps } from '../../stores';
2 |
3 | export class Props extends StoreProps {}
4 | export class State {}
5 |
--------------------------------------------------------------------------------
/src/plugins/main-tool-tree/action.ts:
--------------------------------------------------------------------------------
1 | import { Action, inject } from 'dob';
2 | import { hasClass, removeClass } from '../../utils/dom';
3 | import TreeStore from './store';
4 |
5 | export default class TreeAction {
6 | @inject(TreeStore) private store: TreeStore;
7 |
8 | /**
9 | * 设置树根节点
10 | */
11 | @Action
12 | public setTreeRootDom(dom: HTMLElement) {
13 | this.store.treeRootDom = dom;
14 | }
15 |
16 | /**
17 | * 新增树 dom
18 | */
19 | @Action
20 | public addTreeDom(instanceKey: string, dom: HTMLElement) {
21 | this.store.treeDoms.set(instanceKey, dom);
22 | }
23 |
24 | /**
25 | * 移除树 dom
26 | */
27 | @Action
28 | public removeTreeDom(instanceKey: string) {
29 | this.store.treeDoms.delete(instanceKey);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/plugins/main-tool-tree/guideline/guideline.component.tsx:
--------------------------------------------------------------------------------
1 | import { Connect } from 'dob-react';
2 | import * as React from 'react';
3 | import * as ReactDOM from 'react-dom';
4 | import scrollTo from '../../../utils/scroll-to';
5 | import * as Styled from './guideline.style';
6 | import * as typings from './guideline.type';
7 |
8 | @Connect
9 | export default class Guideline extends React.Component {
10 | public static defaultProps = new typings.Props();
11 |
12 | public state = new typings.State();
13 |
14 | public componentWillReact() {
15 | if (
16 | this.props.stores.ViewportStore.currentHoverInstanceKey === null ||
17 | this.props.stores.TreeStore.currentHoverTreeDom === undefined
18 | ) {
19 | return;
20 | }
21 |
22 | // 正在拖拽中不显示
23 | if (this.props.stores.ViewportStore.currentDragInfo !== null) {
24 | return;
25 | }
26 |
27 | // 让 dom 树外层滚动到这个元素上
28 | const nodeDom = ReactDOM.findDOMNode(this.props.stores.TreeStore.currentHoverTreeDom) as HTMLElement;
29 | const nodeDomRect = nodeDom.getBoundingClientRect();
30 | const containerDom = this.props.stores.TreeStore.treeRootDom;
31 | const containerDomRect = containerDom.getBoundingClientRect();
32 |
33 | // 如果超过一定范围,就移动
34 | if (
35 | nodeDomRect.top - containerDomRect.top < 20 ||
36 | nodeDomRect.top - containerDomRect.top > containerDomRect.height - 50
37 | ) {
38 | scrollTo(containerDom, nodeDomRect.top - containerDomRect.top + containerDom.scrollTop - 50, 50);
39 | }
40 | }
41 |
42 | public render() {
43 | if (
44 | this.props.stores.ViewportStore.currentHoverInstanceKey === null ||
45 | this.props.stores.TreeStore.currentHoverTreeDom === undefined
46 | ) {
47 | return null;
48 | }
49 |
50 | // 正在拖拽中不显示
51 | if (this.props.stores.ViewportStore.currentDragInfo !== null) {
52 | return null;
53 | }
54 |
55 | const hoverBoundingClientRect = this.props.stores.TreeStore.currentHoverTreeDom.getBoundingClientRect();
56 | const rootBoundingClientRect = this.props.stores.TreeStore.treeRootDom.getBoundingClientRect();
57 |
58 | const style = {
59 | width: hoverBoundingClientRect.width - 4,
60 | height: hoverBoundingClientRect.height - 4,
61 | left: hoverBoundingClientRect.left - rootBoundingClientRect.left,
62 | top: hoverBoundingClientRect.top - rootBoundingClientRect.top + this.props.stores.TreeStore.treeRootDom.scrollTop,
63 | };
64 |
65 | return ;
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/plugins/main-tool-tree/guideline/guideline.style.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const Container = styled.div`
4 | position: absolute;
5 | border: 1px solid #23b7e5;
6 | transition: all 0.07s;
7 | pointer-events: none;
8 | z-index: 10;
9 | `;
10 |
--------------------------------------------------------------------------------
/src/plugins/main-tool-tree/guideline/guideline.type.ts:
--------------------------------------------------------------------------------
1 | import { StoreProps } from '../../../stores';
2 | import TreeAction from '../action';
3 | import TreeStore from '../store';
4 |
5 | export class Props extends StoreProps<
6 | {
7 | TreeAction: TreeAction;
8 | },
9 | {
10 | TreeStore: TreeStore;
11 | }
12 | > {}
13 | export class State {}
14 |
--------------------------------------------------------------------------------
/src/plugins/main-tool-tree/index.style.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const Container = styled.div`
4 | position: relative;
5 | border-top: 1px solid #ddd;
6 | flex-grow: 1;
7 | width: 100%;
8 | display: flex;
9 | flex-direction: column;
10 | `;
11 |
12 | export const TreeContainer = styled.div`
13 | position: relative;
14 | width: 100%;
15 | overflow-y: auto;
16 | overflow-x: hidden;
17 | `;
18 |
19 | export const AbsoluteContainer = styled.div`
20 | position: absolute;
21 | bottom: 0;
22 | right: 0;
23 | padding: 5px;
24 | background-color: white;
25 | font-size: 12px;
26 | color: #666;
27 | `;
28 |
--------------------------------------------------------------------------------
/src/plugins/main-tool-tree/index.tsx:
--------------------------------------------------------------------------------
1 | import { Connect } from 'dob-react';
2 | import * as React from 'react';
3 | import * as ReactDOM from 'react-dom';
4 | import { Tree } from '../../components/tree/src';
5 | import TreeAction from './action';
6 | import Guideline from './guideline/guideline.component';
7 | import * as Styled from './index.style';
8 | import { Props, State } from './index.type';
9 | import TreeStore from './store';
10 | import CustomTreeNode from './tree-node/tree-node.component';
11 |
12 | @Connect
13 | class MainToolTree extends React.Component {
14 | public static defaultProps = new Props();
15 |
16 | public state = new State();
17 |
18 | private treeContainer: React.ReactInstance;
19 |
20 | public componentDidMount() {
21 | const treeContainerDom = ReactDOM.findDOMNode(this.treeContainer) as HTMLElement;
22 | this.props.actions.TreeAction.setTreeRootDom(treeContainerDom);
23 | }
24 |
25 | public handleMouseLeave = () => {
26 | this.props.actions.ViewportAction.setCurrentHoverInstanceKey(null);
27 | };
28 |
29 | public render() {
30 | const rootInstanceKey = this.props.stores.ViewportStore.rootInstanceKey;
31 |
32 | return (
33 |
34 | {
37 | this.treeContainer = ref;
38 | }}
39 | >
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | {this.props.stores.ApplicationStore.setLocale('实例数量', 'Instance count')}:{' '}
48 | {this.props.stores.ViewportStore.instances.size}
49 |
50 |
51 | );
52 | }
53 | }
54 |
55 | export default {
56 | position: 'mainToolTree',
57 | class: MainToolTree,
58 | actions: {
59 | TreeAction,
60 | },
61 | stores: {
62 | TreeStore,
63 | },
64 | };
65 |
--------------------------------------------------------------------------------
/src/plugins/main-tool-tree/index.type.ts:
--------------------------------------------------------------------------------
1 | import { StoreProps } from '../../stores';
2 | import TreeAction from './action';
3 | import TreeStore from './store';
4 |
5 | export class Props extends StoreProps<
6 | {
7 | TreeAction: TreeAction;
8 | },
9 | {
10 | TreeStore: TreeStore;
11 | }
12 | > {}
13 | export class State {}
14 |
--------------------------------------------------------------------------------
/src/plugins/main-tool-tree/store.ts:
--------------------------------------------------------------------------------
1 | import { inject } from 'dob';
2 | import ViewportStore from '../../stores/viewport/store';
3 |
4 | export default class TreeStore {
5 | @inject(ViewportStore) public viewportStore: ViewportStore;
6 |
7 | // 树根节点实例
8 | public treeRootDom: HTMLElement = null;
9 |
10 | // 所有树节点实例
11 | public treeDoms = new Map();
12 |
13 | // 当前 hover 的树 dom 节点
14 | public get currentHoverTreeDom() {
15 | return this.treeDoms.get(this.viewportStore.currentHoverInstanceKey);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/plugins/main-tool-tree/tree-node/tree-node.component.tsx:
--------------------------------------------------------------------------------
1 | import { Connect } from 'dob-react';
2 | import * as React from 'react';
3 | import * as ReactDOM from 'react-dom';
4 | import { TreeNode } from '../../../components/tree/src';
5 | import * as Styled from './tree-node.style';
6 | import * as typings from './tree-node.type';
7 |
8 | class CustomTreeNode extends React.Component {
9 | public static defaultProps = new typings.Props();
10 |
11 | public state = new typings.State();
12 |
13 | /**
14 | * 当前绑定节点信息
15 | */
16 | public instanceInfo: InstanceInfo;
17 |
18 | /**
19 | * 组件的类
20 | */
21 | private componentClass: React.ComponentClass;
22 |
23 | /**
24 | * 记录组件生命值,只要大于 0,说明没有被销毁
25 | */
26 | private health = 1;
27 |
28 | /**
29 | * 当前实例设置信息
30 | */
31 | private setting: IGaeaSetting;
32 |
33 | public componentWillMount() {
34 | // 从 store 找到自己信息
35 | this.instanceInfo = this.props.stores.ViewportStore.instances.get(this.props.instanceKey);
36 | this.componentClass = this.props.actions.ApplicationAction.getComponentClassByKey(this.instanceInfo.gaeaKey);
37 | this.setting = this.props.actions.ApplicationAction.getSettingByInstance(this.instanceInfo);
38 | }
39 |
40 | public componentDidMount() {
41 | this.health += 1;
42 |
43 | this.props.actions.TreeAction.addTreeDom(this.props.instanceKey, ReactDOM.findDOMNode(this) as HTMLElement);
44 |
45 | // 如果自己是布局元素, 给子元素绑定 sortable
46 | if (this.setting.isContainer) {
47 | // 添加可排序拖拽
48 | this.props.actions.ViewportAction.registerInnerDrag(
49 | this.props.instanceKey,
50 | (ReactDOM.findDOMNode(this) as HTMLElement).getElementsByClassName('childs-container')[0] as HTMLElement,
51 | 'gaea-tree-container',
52 | );
53 | }
54 | }
55 |
56 | public componentWillUnmount() {
57 | this.health -= 1;
58 |
59 | // 因为一个组件跨层级拖动,会先生成再销毁,这时组件没有死亡,所以不要把监听移除
60 | if (this.health === 0) {
61 | // 在 dom 列表中移除
62 | this.props.actions.TreeAction.removeTreeDom(this.props.instanceKey);
63 | }
64 | }
65 |
66 | /**
67 | * 更新此元素的 dom 信息
68 | */
69 | public updateDom = () => {
70 | this.props.actions.TreeAction.addTreeDom(this.props.instanceKey, ReactDOM.findDOMNode(this) as HTMLElement);
71 | };
72 |
73 | public handleRenderTitle = () => {
74 | // TODO: 如果有事件,显示出标识
75 | const eventTag: React.ReactElement = null;
76 | // if (this.instanceInfo.props.gaeaEventData && this.instanceInfo.props.gaeaEventData.length > 0 || (this.instanceInfo.props.gaeaNativeEventData && this.instanceInfo.props.gaeaNativeEventData.length)) {
77 | // eventTag = (
78 | //
79 | // )
80 | // }
81 |
82 | return (
83 |
84 |
85 | {this.setting.name}
86 | {eventTag}
87 |
88 |
89 | );
90 | };
91 |
92 | public handleMouseOver = (event: MouseEvent) => {
93 | event.stopPropagation();
94 | this.props.actions.ViewportAction.setCurrentHoverInstanceKey(this.props.instanceKey);
95 | };
96 |
97 | public handleClick = (event: MouseEvent) => {
98 | event.stopPropagation();
99 | this.props.actions.ViewportAction.setCurrentEditInstanceKey(this.props.instanceKey);
100 | };
101 |
102 | public render() {
103 | // 子元素
104 | let childs: React.ReactElement[] = null;
105 |
106 | if (this.setting.isContainer && this.instanceInfo.childs) {
107 | childs = this.instanceInfo.childs.map(childKey => {
108 | return ;
109 | });
110 | }
111 |
112 | const childProps: any = {
113 | render: this.handleRenderTitle,
114 | defaultExpendAll: true,
115 | toggleByArrow: true,
116 | onMouseOver: this.handleMouseOver,
117 | onClick: this.handleClick,
118 | };
119 |
120 | return React.createElement(TreeNode, childProps, childs);
121 | }
122 | }
123 |
124 | const ConnectedCustomTreeNode = Connect(CustomTreeNode);
125 | export default ConnectedCustomTreeNode;
126 |
--------------------------------------------------------------------------------
/src/plugins/main-tool-tree/tree-node/tree-node.style.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const Container = styled.div`
4 | font-size: 12px;
5 | `;
6 |
7 | export const TreeSelected = styled.div`
8 | background-color: #e0ecf5;
9 | `;
10 |
11 | export const ItemContainer = styled.div`
12 | display: flex;
13 | align-items: center;
14 | height: 30px;
15 | `;
16 |
17 | export const IconContainer = styled.div`
18 | display: flex;
19 | justify-content: center;
20 | align-items: center;
21 | width: 20px;
22 | height: 20px;
23 | color: #999;
24 | font-size: 14px;
25 | `;
26 |
27 | export const EventContainer = styled.div`
28 | margin-left: 5px;
29 | color: #0388a7;
30 | `;
31 |
--------------------------------------------------------------------------------
/src/plugins/main-tool-tree/tree-node/tree-node.type.ts:
--------------------------------------------------------------------------------
1 | import { StoreProps } from '../../../stores';
2 | import TreeAction from '../action';
3 | import TreeStore from '../store';
4 |
5 | export class Props extends StoreProps<
6 | {
7 | TreeAction: TreeAction;
8 | },
9 | {
10 | TreeStore: TreeStore;
11 | }
12 | > {
13 | public instanceKey: string;
14 | }
15 | export class State {}
16 |
--------------------------------------------------------------------------------
/src/plugins/main-tool/index.style.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const Container = (styled.div as any).withConfig({ componentId: 'main-tool-container' })`
4 | display: flex;
5 | flex-grow: 1;
6 | height: 100%;
7 | .ant-tabs {
8 | flex-grow: 1;
9 | display: flex;
10 | flex-direction: column;
11 | }
12 | .ant-tabs-content {
13 | flex-grow: 1;
14 | }
15 | .ant-tabs-nav .ant-tabs-tab {
16 | padding: 10px 16px;
17 | }
18 | .ant-tabs-bar {
19 | margin-bottom: 0;
20 | height: 40px;
21 | min-height: 40px;
22 | }
23 | `;
24 |
25 | export const ScrollContainer = styled.div`
26 | display: block;
27 | overflow-x: hidden;
28 | overflow-y: auto;
29 | height: 100%;
30 | width: 300px;
31 | `;
32 |
--------------------------------------------------------------------------------
/src/plugins/main-tool/index.tsx:
--------------------------------------------------------------------------------
1 | import { Tabs } from 'antd';
2 | import { observe } from 'dob';
3 | import { Connect } from 'dob-react';
4 | import * as React from 'react';
5 | import * as S from './index.style';
6 | import { Props, State } from './index.type';
7 |
8 | @Connect
9 | class MainTool extends React.Component {
10 | public static defaultProps = new Props();
11 |
12 | public state = new State();
13 |
14 | public handleChange = (activeKey: string) => {
15 | this.setState({
16 | activeKey,
17 | });
18 | };
19 |
20 | public render() {
21 | return (
22 |
23 |
24 |
25 |
26 | {this.props.actions.ApplicationAction.loadPluginByPosition('mainToolEditor')}
27 |
28 |
29 |
30 |
31 | {this.props.actions.ApplicationAction.loadPluginByPosition('mainToolTree')}
32 |
33 |
34 |
35 |
36 | );
37 | }
38 | }
39 |
40 | export default {
41 | position: 'mainTool',
42 | class: MainTool,
43 | };
44 |
--------------------------------------------------------------------------------
/src/plugins/main-tool/index.type.ts:
--------------------------------------------------------------------------------
1 | import { StoreProps } from '../../stores';
2 |
3 | export class Props extends StoreProps {}
4 | export class State {
5 | /**
6 | * 当前 tab 选择
7 | */
8 | public activeKey?: string = 'editor';
9 | }
10 |
--------------------------------------------------------------------------------
/src/plugins/preview/index.tsx:
--------------------------------------------------------------------------------
1 | import { Connect } from 'dob-react';
2 | import * as React from 'react';
3 | import { Props, State } from './index.type';
4 | import * as Styled from './style';
5 |
6 | @Connect
7 | class Preview extends React.Component {
8 | public static defaultProps = new Props();
9 |
10 | public state = new State();
11 |
12 | public render() {
13 | return (
14 |
15 | {this.props.stores.ApplicationStore.isPreview
16 | ? this.props.stores.ApplicationStore.setLocale('取消', 'Cancel')
17 | : this.props.stores.ApplicationStore.setLocale('预览', 'Preview')}
18 |
19 | );
20 | }
21 |
22 | private handleClick = () => {
23 | const setPreview = !this.props.stores.ApplicationStore.isPreview;
24 | this.props.actions.ApplicationAction.resetApplication();
25 | this.props.actions.ApplicationAction.setPreview(setPreview);
26 | };
27 | }
28 |
29 | export default {
30 | position: 'navbarRight',
31 | class: Preview,
32 | };
33 |
--------------------------------------------------------------------------------
/src/plugins/preview/index.type.ts:
--------------------------------------------------------------------------------
1 | import { StoreProps } from '../../stores';
2 |
3 | export class Props extends StoreProps {}
4 |
5 | export class State {}
6 |
--------------------------------------------------------------------------------
/src/plugins/preview/style.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const Container = styled.div`
4 | color: #666;
5 | `;
6 |
--------------------------------------------------------------------------------
/src/plugins/save/index.tsx:
--------------------------------------------------------------------------------
1 | import { Connect } from 'dob-react';
2 | import * as React from 'react';
3 | import { Props, State } from './index.type';
4 | import * as Styled from './style';
5 |
6 | @Connect
7 | class Save extends React.Component {
8 | public static defaultProps = new Props();
9 |
10 | public state = new State();
11 |
12 | public render() {
13 | return (
14 |
15 | {this.props.stores.ApplicationStore.setLocale('保存', 'Save')}
16 |
17 | );
18 | }
19 |
20 | private handleClick = () => {
21 | this.props.actions.EventAction.emit(this.props.stores.EventStore.emitEditorCallback, {
22 | funcName: 'onSave',
23 | data: this.props.stores.ViewportStore.currentFullInformation.$raw,
24 | });
25 | };
26 | }
27 |
28 | export default {
29 | position: 'navbarRight',
30 | class: Save,
31 | };
32 |
--------------------------------------------------------------------------------
/src/plugins/save/index.type.ts:
--------------------------------------------------------------------------------
1 | import { StoreProps } from '../../stores';
2 |
3 | export class Props extends StoreProps {}
4 |
5 | export class State {}
6 |
--------------------------------------------------------------------------------
/src/plugins/save/style.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const Container = styled.div`
4 | color: #666;
5 | `;
6 |
--------------------------------------------------------------------------------
/src/plugins/view-mode/index.tsx:
--------------------------------------------------------------------------------
1 | import { Icon } from 'antd';
2 | import { Connect } from 'dob-react';
3 | import * as React from 'react';
4 | import { Props, State } from './index.type';
5 | import * as S from './style';
6 |
7 | const selections: {
8 | title: string;
9 | icon: string;
10 | width: number | string;
11 | height: number | string;
12 | }[] = [
13 | {
14 | title: 'PC',
15 | icon: 'laptop',
16 | width: '100%',
17 | height: '100%',
18 | },
19 | {
20 | title: 'Iphone6/7/8',
21 | icon: 'mobile',
22 | width: 414,
23 | height: 736,
24 | },
25 | {
26 | title: 'IphoneX',
27 | icon: 'mobile',
28 | width: 375,
29 | height: 812,
30 | },
31 | {
32 | title: 'iPad',
33 | icon: 'tablet',
34 | width: 768,
35 | height: 1024,
36 | },
37 | ];
38 |
39 | @Connect
40 | class ViewMode extends React.Component {
41 | public static defaultProps = new Props();
42 |
43 | public state = new State();
44 |
45 | public componentWillMount() {
46 | this.changeCurrentIndex(0);
47 | }
48 |
49 | public render() {
50 | return (
51 |
52 |
53 |
54 | {this.renderModel()}
55 |
56 | );
57 | }
58 |
59 | private showModel = () => {
60 | this.setState({ showModel: true });
61 | };
62 |
63 | private hideModel = () => {
64 | this.setState({ showModel: false });
65 | };
66 |
67 | private renderModel = () => {
68 | if (!this.state.showModel) {
69 | return null;
70 | }
71 |
72 | return (
73 |
74 | {selections.map((each, index) => {
75 | return this.renderViewMode(each.icon, each.title, index);
76 | })}
77 |
78 | );
79 | };
80 |
81 | private renderViewMode = (icon: string, text: string, index: number) => {
82 | return (
83 |
88 |
89 | {text}
90 |
91 | );
92 | };
93 |
94 | private changeCurrentIndex = (index: number) => {
95 | this.setState(
96 | {
97 | currentIndex: index,
98 | },
99 | () => {
100 | this.props.actions.ApplicationAction.setViewportStyle({
101 | width: selections[this.state.currentIndex].width,
102 | height: selections[this.state.currentIndex].height,
103 | });
104 | },
105 | );
106 | };
107 | }
108 |
109 | export default {
110 | position: 'navbarRight',
111 | class: ViewMode,
112 | };
113 |
--------------------------------------------------------------------------------
/src/plugins/view-mode/index.type.ts:
--------------------------------------------------------------------------------
1 | import { StoreProps } from '../../stores';
2 |
3 | export class Props extends StoreProps {}
4 |
5 | export class State {
6 | public showModel = false;
7 |
8 | public currentIndex = 0;
9 | }
10 |
--------------------------------------------------------------------------------
/src/plugins/view-mode/style.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const Container = styled.div`
4 | position: relative;
5 | color: #666;
6 | `;
7 |
8 | export const ModelContainer = styled.div`
9 | display: flex;
10 | flex-direction: column;
11 | position: absolute;
12 | left: -85px;
13 | top: 39px;
14 | width: 200px;
15 | height: 120px;
16 | background-color: white;
17 | border: 1px solid #ddd;
18 | z-index: 10;
19 | cursor: default;
20 | `;
21 |
22 | export const ViewModeItem = styled.div`
23 | display: flex;
24 | align-items: center;
25 | height: 30px;
26 | padding: 0 10px;
27 | cursor: pointer;
28 | &.active,
29 | &:hover {
30 | background-color: whitesmoke;
31 | }
32 | `;
33 |
34 | export const ViewModeText = styled.div`
35 | margin-left: 5px;
36 | font-size: 13px;
37 | `;
38 |
--------------------------------------------------------------------------------
/src/plugins/viewport-guideline/index.style.ts:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const Container = styled.div`
4 | position: absolute;
5 | z-index: 10;
6 | border: 1px solid #23b7e5;
7 | transition: all 0.07s;
8 | pointer-events: none;
9 | `;
10 |
--------------------------------------------------------------------------------
/src/plugins/viewport-guideline/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 ViewportGuideline extends React.Component {
8 | public static defaultProps = new typings.Props();
9 |
10 | public state = new typings.State();
11 |
12 | private timeOut: any;
13 |
14 | public componentDidMount() {
15 | this.props.actions.EventAction.on(this.props.stores.EventStore.viewportUpdated, this.handleViewportUpdated);
16 | this.updateTimeout();
17 | }
18 |
19 | public componentWillUnmount() {
20 | this.props.actions.EventAction.off(this.props.stores.EventStore.viewportUpdated, this.handleViewportUpdated);
21 | clearTimeout(this.timeOut);
22 | }
23 |
24 | public render() {
25 | // 正在拖拽中不显示
26 | if (this.props.stores.ViewportStore.currentDragInfo !== null) {
27 | return null;
28 | }
29 |
30 | // 没有 hover 元素不显示
31 | if (this.props.stores.ViewportStore.currentHoverInstanceKey === null) {
32 | return null;
33 | }
34 |
35 | const targetBoundingClientRect = this.props.stores.ViewportStore.instanceDoms
36 | .get(this.props.stores.ViewportStore.currentHoverInstanceKey)
37 | .getBoundingClientRect();
38 | const viewportBoundingClientRect = this.props.stores.ViewportStore.viewportDOM.getBoundingClientRect();
39 |
40 | const style = {
41 | width: targetBoundingClientRect.width - 1,
42 | height: targetBoundingClientRect.height - 1,
43 | top: targetBoundingClientRect.top - viewportBoundingClientRect.top,
44 | left: targetBoundingClientRect.left - viewportBoundingClientRect.left,
45 | };
46 |
47 | return ;
48 | }
49 |
50 | /**
51 | * 视图区域更新时触发
52 | */
53 | private handleViewportUpdated = () => {
54 | this.forceUpdate();
55 | this.updateTimeout();
56 | };
57 |
58 | /**
59 | * 更新定时器
60 | */
61 | private updateTimeout = () => {
62 | if (this.timeOut) {
63 | clearTimeout(this.timeOut);
64 | }
65 |
66 | this.timeOut = setInterval(() => {
67 | if (this.props.stores.ViewportStore.currentDragInfo !== null) {
68 | return null;
69 | }
70 |
71 | // 没有 hover 元素不显示
72 | if (this.props.stores.ViewportStore.currentHoverInstanceKey === null) {
73 | return null;
74 | }
75 |
76 | this.forceUpdate();
77 | }, 200);
78 | };
79 | }
80 |
81 | export default {
82 | position: 'viewport',
83 | class: ViewportGuideline,
84 | };
85 |
--------------------------------------------------------------------------------
/src/plugins/viewport-guideline/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/stores/application/store.ts:
--------------------------------------------------------------------------------
1 | import { observable, Static } from 'dob';
2 | import * as React from 'react';
3 |
4 | @observable
5 | export default class ApplicationStore {
6 | /**
7 | * Navbar height
8 | */
9 | public navbarHeight = 40;
10 |
11 | /**
12 | * Is in preview
13 | */
14 | public isPreview = false;
15 |
16 | /**
17 | * Viewport parent container's style
18 | */
19 | public viewportContainerStyle = {};
20 |
21 | /**
22 | * Viewport style
23 | */
24 | public viewportStyle: React.CSSProperties = {};
25 |
26 | /**
27 | * All gaea plugins
28 | */
29 | public plugins?: IPlugin[] = [];
30 |
31 | /**
32 | * All component's class
33 | * key: component's name
34 | * value: component's class
35 | */
36 | public componentClasses = new Map>();
37 |
38 | /**
39 | * 所有组件的编辑属性 GaeaProps
40 | * key: gaeaKey | preGaeaKey
41 | */
42 | public componentSetting = new Map();
43 |
44 | /**
45 | * 所有组件 defaultProps
46 | * key: gaeaKey | preGaeaKey
47 | */
48 | public componentDefaultProps = new Map();
49 |
50 | /**
51 | * Viewport's initialization data
52 | */
53 | public defaultValue?: any = null;
54 |
55 | /**
56 | * Viewport root component's name
57 | */
58 | public rootComponentName = '';
59 |
60 | /**
61 | * left tool name
62 | */
63 | public leftTool: string = null;
64 |
65 | public rightTool: string = null;
66 |
67 | /**
68 | * Show modal?
69 | */
70 | public isShowModal = false;
71 |
72 | public modalTitle = '';
73 |
74 | public modalContentRender: (closeModal?: () => void) => React.ReactElement = null;
75 |
76 | /**
77 | * 预设组件
78 | * 将一个组件加入此配置,这个组件会从组件列表中移除,并根据配置的 props 信息展示为 N 个独立组件
79 | * 在显示时,这个组件与普通组件别无二致,只是会加上默认配置,并且这个配置可以不在[可编辑配置中]
80 | * key: gaeaKey
81 | */
82 | public preComponents = new Map();
83 |
84 | /**
85 | * GaeaEditor props: onComponentDragStart
86 | */
87 | public onComponentDragStart: IOnComponentDragStart = Static(() => null as any);
88 |
89 | /**
90 | * Locale
91 | */
92 | public locale?: 'zh' | 'en' = null;
93 |
94 | public setLocale = (zh: string, en: string) => {
95 | switch (this.locale) {
96 | case 'zh':
97 | return zh;
98 | case 'en':
99 | return en;
100 | default:
101 | return null;
102 | }
103 | };
104 | }
105 |
--------------------------------------------------------------------------------
/src/stores/event/action.ts:
--------------------------------------------------------------------------------
1 | import { Action } from 'dob';
2 |
3 | export declare type EventType = number | string;
4 |
5 | /**
6 | * 事件
7 | */
8 | export interface IEvent {
9 | callback: (eventContext?: any, context?: any) => void;
10 | context: any;
11 | }
12 |
13 | export type ICallback = (eventContext?: any, context?: any) => void;
14 |
15 | export default class EventAction {
16 | // 所有事件
17 | private events: Map = new Map();
18 |
19 | /**
20 | * 订阅事件
21 | */
22 | @Action
23 | public on(eventType: EventType, callback: ICallback, context?: any) {
24 | const event: IEvent = {
25 | callback,
26 | context,
27 | };
28 |
29 | if (this.events.get(eventType)) {
30 | // 存在, push 一个事件监听
31 | this.events.get(eventType).push(event);
32 | } else {
33 | // 不存在, 赋值
34 | this.events.set(eventType, [event]);
35 | }
36 | }
37 |
38 | /**
39 | * 取消订阅
40 | */
41 | @Action
42 | public off(eventType: EventType, callback: ICallback) {
43 | if (!this.events.get(eventType)) {
44 | return false;
45 | }
46 |
47 | const events = this.events.get(eventType).filter(event => {
48 | return event.callback !== callback;
49 | });
50 |
51 | this.events.set(eventType, events);
52 |
53 | return true;
54 | }
55 |
56 | /**
57 | * 广播事件
58 | */
59 | @Action
60 | public emit(eventType: EventType, context?: any) {
61 | if (!eventType || !this.events.get(eventType)) {
62 | return false;
63 | }
64 |
65 | this.events.get(eventType).forEach(event => {
66 | event.callback(event.context, context);
67 | });
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/stores/event/store.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 存储所有事件
3 | */
4 | export default class ViewportStore {
5 | /**
6 | * 鼠标离开视图区域
7 | */
8 | public mouseLeaveViewport = 'mouseLeaveViewport';
9 |
10 | /**
11 | * 鼠标移动到某个组件上
12 | */
13 | public mouseHoveringComponent = 'mouseHoveringComponent';
14 |
15 | /**
16 | * 视图区域发生更新
17 | */
18 | public viewportUpdated = 'viewportUpdated';
19 |
20 | /**
21 | * 刷新某个实例
22 | */
23 | public instanceUpdate = 'instanceUpdate';
24 |
25 | /**
26 | * 页面重渲染
27 | */
28 | public refreshPage = 'refreshPage';
29 |
30 | /**
31 | * 触发编辑器回调
32 | */
33 | public emitEditorCallback = 'emitEditorCallback';
34 | }
35 |
--------------------------------------------------------------------------------
/src/stores/index.ts:
--------------------------------------------------------------------------------
1 | import { Container } from 'dependency-inject';
2 | import * as React from 'react';
3 | import ApplicationAction from './application/action';
4 | import ApplicationStore from './application/store';
5 | import EventAction from './event/action';
6 | import EventStore from './event/store';
7 | import ViewportAction from './viewport/action';
8 | import ViewportStore from './viewport/store';
9 |
10 | export interface IActionsOrStores {
11 | [x: string]: any;
12 | }
13 |
14 | export class StoreProps {
15 | public actions?: {
16 | ApplicationAction: ApplicationAction;
17 | ViewportAction: ViewportAction;
18 | EventAction: EventAction;
19 | } & Actions;
20 |
21 | public stores?: {
22 | ApplicationStore: ApplicationStore;
23 | ViewportStore: ViewportStore;
24 | EventStore: EventStore;
25 | } & Stores;
26 | }
27 |
28 | export class Store {
29 | private container = new Container();
30 |
31 | private actions: IActionsOrStores = {};
32 |
33 | private stores: IActionsOrStores = {};
34 |
35 | /**
36 | * 全局 actions + stores 映射
37 | */
38 | private allActions = new Map();
39 |
40 | private allStores = new Map();
41 |
42 | public constructor() {
43 | this.container = new Container();
44 |
45 | this.allActions.set('ApplicationAction', ApplicationAction);
46 | this.allStores.set('ApplicationStore', ApplicationStore);
47 | this.allActions.set('ViewportAction', ViewportAction);
48 | this.allStores.set('ViewportStore', ViewportStore);
49 | this.allActions.set('EventAction', EventAction);
50 | this.allStores.set('EventStore', EventStore);
51 |
52 | this.allActions.forEach((ActionClass, actionName) => {
53 | this.container.set(ActionClass, new ActionClass());
54 | });
55 |
56 | this.allStores.forEach((StoreClass, storeName) => {
57 | this.container.set(StoreClass, new StoreClass());
58 | });
59 | }
60 |
61 | public addActions(actions: IActionsOrStores) {
62 | Object.keys(actions).forEach(key => {
63 | this.container.set(actions[key], new actions[key]());
64 | this.allActions.set(key, actions[key]);
65 | });
66 | }
67 |
68 | public addStores(stores: IActionsOrStores) {
69 | Object.keys(stores).forEach(key => {
70 | this.container.set(stores[key], new stores[key]());
71 | this.allStores.set(key, stores[key]);
72 | });
73 | }
74 |
75 | /**
76 | * 获得 store
77 | */
78 | public getStore() {
79 | this.allActions.forEach((actionClass, actionName) => {
80 | this.actions[actionName] = this.container.get(actionClass);
81 | });
82 | this.allStores.forEach((storeClass, storeName) => {
83 | this.stores[storeName] = this.container.get(storeClass);
84 | });
85 |
86 | return {
87 | actions: this.actions,
88 | stores: this.stores,
89 | };
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/stores/viewport/store.ts:
--------------------------------------------------------------------------------
1 | import { observable } from 'dob';
2 |
3 | /**
4 | * 存储所有编辑状态视图区域的数据
5 | */
6 | @observable
7 | export default class ViewportStore {
8 | /**
9 | * 视图区域 dom
10 | */
11 | public viewportDOM: HTMLElement = null;
12 |
13 | /**
14 | * 根级实例的 key
15 | */
16 | public rootInstanceKey: string = null;
17 |
18 | /**
19 | * 当前所有组件实例
20 | */
21 | public instances = new Map();
22 |
23 | /**
24 | * 组件实例到dom节点的映射
25 | */
26 | public instanceDoms = new Map();
27 |
28 | /**
29 | * current drag info
30 | */
31 | public currentDragInfo: IDragInfo = null;
32 |
33 | public currentHoverInstanceKey: string = null;
34 |
35 | public currentEditInstanceKey: string;
36 |
37 | public get currentFullInformation() {
38 | const fullObj: IFullInformation = {};
39 | this.instances.forEach((instanceInfo, instanceKey) => {
40 | fullObj[instanceKey] = instanceInfo;
41 | });
42 | return fullObj;
43 | }
44 |
45 | /**
46 | * 拖拽前数据获取是否完毕
47 | */
48 | public dragStartDataReady = false;
49 | }
50 |
--------------------------------------------------------------------------------
/src/utils/dom.ts:
--------------------------------------------------------------------------------
1 | export function hasClass(obj: HTMLElement, cls: string) {
2 | return obj.className.match(new RegExp(`(\\s|^)${cls}(\\s|$)`));
3 | }
4 |
5 | export function removeClass(obj: HTMLElement, cls: string) {
6 | if (hasClass(obj, cls)) {
7 | const reg = new RegExp(`(\\s|^)${cls}(\\s|$)`);
8 | obj.className = obj.className.replace(reg, ' ');
9 | }
10 | }
11 |
12 | export function addClass(obj: HTMLElement, cls: string) {
13 | if (!hasClass(obj, cls)) {
14 | obj.className === '' ? (obj.className = `${cls}`) : (obj.className += ` ${cls}`);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/utils/functional.ts:
--------------------------------------------------------------------------------
1 | export const pipeEvent = (func: any) => {
2 | return (event: React.ChangeEvent) => {
3 | return func(event.target.value, event);
4 | };
5 | };
6 |
--------------------------------------------------------------------------------
/src/utils/react-helper.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { StoreProps } from '../stores';
3 |
4 | export class PureComponent extends React.PureComponent {}
5 |
--------------------------------------------------------------------------------
/src/utils/scroll-to.ts:
--------------------------------------------------------------------------------
1 | export default function scrollTo(element: HTMLElement, target: number, duration: number) {
2 | // eslint-disable-next-line no-param-reassign
3 | target = Math.round(target);
4 | // eslint-disable-next-line no-param-reassign
5 | duration = Math.round(duration);
6 | if (duration < 0) {
7 | return Promise.reject(Error('bad duration'));
8 | }
9 | if (duration === 0) {
10 | element.scrollTop = target;
11 | return Promise.resolve();
12 | }
13 |
14 | const startTime = Date.now();
15 | const endTime = startTime + duration;
16 |
17 | const startTop = element.scrollTop;
18 | const distance = target - startTop;
19 |
20 | // based on http://en.wikipedia.org/wiki/Smoothstep
21 | const smoothStep = (start: number, end: number, point: number) => {
22 | if (point <= start) {
23 | return 0;
24 | }
25 | if (point >= end) {
26 | return 1;
27 | }
28 | const x = (point - start) / (end - start); // interpolation
29 | return x * x * (3 - 2 * x);
30 | };
31 |
32 | return new Promise((resolve, reject) => {
33 | // This is to keep track of where the element's scrollTop is
34 | // supposed to be, based on what we're doing
35 | let previousTop = element.scrollTop;
36 |
37 | // This is like a think function from a game loop
38 | const scrollFrame = () => {
39 | if (element.scrollTop !== previousTop) {
40 | return;
41 | }
42 |
43 | // set the scrollTop for this frame
44 | const now = Date.now();
45 | const point = smoothStep(startTime, endTime, now);
46 | const frameTop = Math.round(startTop + distance * point);
47 | element.scrollTop = frameTop;
48 |
49 | // check if we're done!
50 | if (now >= endTime) {
51 | resolve();
52 | return;
53 | }
54 |
55 | // If we were supposed to scroll but didn't, then we
56 | // probably hit the limit, so consider it done not
57 | // interrupted.
58 | if (element.scrollTop === previousTop && element.scrollTop !== frameTop) {
59 | resolve();
60 | return;
61 | }
62 | previousTop = element.scrollTop;
63 |
64 | // schedule next frame for execution
65 | setTimeout(scrollFrame, 0);
66 | };
67 |
68 | // boostrap the animation process
69 | setTimeout(scrollFrame, 0);
70 | });
71 | }
72 |
--------------------------------------------------------------------------------
/tests/index.ts:
--------------------------------------------------------------------------------
1 | test('Example', () => {
2 | expect(true).toBe(true);
3 | });
4 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "esnext",
4 | "moduleResolution": "node",
5 | "strict": true,
6 | "strictNullChecks": false,
7 | "jsx": "react",
8 | "target": "esnext",
9 | "experimentalDecorators": true,
10 | "skipLibCheck": true,
11 | "outDir": "dist",
12 | "baseUrl": ".",
13 | "lib": [
14 | "dom",
15 | "es5",
16 | "es6",
17 | "es7",
18 | "scripthost",
19 | "es2018.promise"
20 | ],
21 | "emitDecoratorMetadata": true,
22 | "preserveConstEnums": true,
23 | "paths": {
24 | "pri/*": [
25 | "pri",
26 | ".temp/types/*"
27 | ]
28 | }
29 | },
30 | "include": [
31 | ".temp/**/*",
32 | "typings/**/*",
33 | "src/**/*"
34 | ],
35 | "exclude": [
36 | "node_modules",
37 | "dist"
38 | ]
39 | }
40 |
--------------------------------------------------------------------------------