change pkg
This commit is contained in:
20
.github/workflows/main.yml
vendored
Normal file
20
.github/workflows/main.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
name: build
|
||||
on: [push]
|
||||
jobs:
|
||||
build-and-deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout 🛎️
|
||||
uses: actions/checkout@v2 # If you're using actions/checkout@v2 you must set persist-credentials to false in most cases for the deployment to work correctly.
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Install and Build 🔧 # This example project is built using npm and outputs the result to the 'build' folder. Replace with the commands required to build your project, or remove this step entirely if your site is pre-built.
|
||||
run: |
|
||||
yarn install
|
||||
npm run deploy
|
||||
- name: Deploy 🚀
|
||||
uses: JamesIves/github-pages-deploy-action@releases/v3
|
||||
with:
|
||||
GITHUB_TOKEN: ${{secrets.ACCESS_TOKEN}}
|
||||
BRANCH: gh-pages # The branch the action should deploy to.
|
||||
FOLDER: packages/dooringx-doc/build # The folder the action should deploy.
|
||||
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
.svelte-kit
|
||||
/package
|
||||
yarn-error.log
|
||||
.vercel_build_output
|
||||
build
|
||||
dist
|
||||
4
.prettierignore
Normal file
4
.prettierignore
Normal file
@@ -0,0 +1,4 @@
|
||||
.svelte-kit/**
|
||||
static/**
|
||||
build/**
|
||||
node_modules/**
|
||||
7
.prettierrc
Normal file
7
.prettierrc
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"useTabs": true,
|
||||
"singleQuote": true,
|
||||
"printWidth": 100,
|
||||
"semi": true,
|
||||
"trailingComma": "es5"
|
||||
}
|
||||
4
lerna.json
Normal file
4
lerna.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"packages": ["packages/*"],
|
||||
"version": "0.0.0"
|
||||
}
|
||||
34
package.json
Normal file
34
package.json
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "root",
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
],
|
||||
"scripts": {
|
||||
"start": "lerna exec npm run start --scope=dooringx-lib",
|
||||
"build": "lerna exec npm run build --scope=dooringx-lib",
|
||||
"deploy": "lerna exec npm run deploy --scope=dooringx-doc",
|
||||
"pub": "node ./script/publish.js"
|
||||
},
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"lerna": "^3.22.1",
|
||||
"husky": "4.3.0",
|
||||
"lint-staged": "^11.0.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"fs-extra": "^10.0.0",
|
||||
"prettier": "^2.2.0",
|
||||
"typescript": "^4.1.2",
|
||||
"tslib": "^2.1.0"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "lint-staged"
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,jsx,tsx,ts,less,md,json}": [
|
||||
"npx prettier --write ./packages/dooringx-doc/src ./packages/dooringx-lib/src",
|
||||
"git add ."
|
||||
]
|
||||
}
|
||||
}
|
||||
38
packages/dooringx-doc/README.md
Normal file
38
packages/dooringx-doc/README.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# create-svelte
|
||||
|
||||
Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/master/packages/create-svelte);
|
||||
|
||||
## Creating a project
|
||||
|
||||
If you're seeing this, you've probably already done this step. Congrats!
|
||||
|
||||
```bash
|
||||
# create a new project in the current directory
|
||||
npm init svelte@next
|
||||
|
||||
# create a new project in my-app
|
||||
npm init svelte@next my-app
|
||||
```
|
||||
|
||||
> Note: the `@next` is temporary
|
||||
|
||||
## Developing
|
||||
|
||||
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
|
||||
# or start the server and open the app in a new browser tab
|
||||
npm run dev -- --open
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
Before creating a production version of your app, install an [adapter](https://kit.svelte.dev/docs#adapters) for your target environment. Then:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
> You can preview the built app with `npm run preview`, regardless of whether you installed an adapter. This should _not_ be used to serve your app in production.
|
||||
39
packages/dooringx-doc/package.json
Normal file
39
packages/dooringx-doc/package.json
Normal file
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"name": "dooringx-doc",
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"start": "svelte-kit dev",
|
||||
"dev": "svelte-kit dev",
|
||||
"build": "svelte-kit build",
|
||||
"deploy": "cross-env DEPLOY=TRUE svelte-kit build",
|
||||
"preview": "svelte-kit preview",
|
||||
"check": "svelte-check --tsconfig ./tsconfig.json",
|
||||
"check:watch": "svelte-check --tsconfig ./tsconfig.json --watch",
|
||||
"lint": "prettier --check --plugin-search-dir=. .",
|
||||
"format": "prettier --write --plugin-search-dir=. ."
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/kit": "next",
|
||||
"@types/cookie": "^0.4.0",
|
||||
"@types/marked": "^2.0.3",
|
||||
"prettier": "~2.2.1",
|
||||
"prettier-plugin-svelte": "^2.2.0",
|
||||
"svelte": "^3.34.0",
|
||||
"svelte-check": "^2.0.0",
|
||||
"svelte-preprocess": "^4.0.0",
|
||||
"@sveltejs/adapter-node": "^1.0.0-next.29",
|
||||
"@sveltejs/adapter-static": "^1.0.0-next.13"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@fontsource/fira-mono": "^4.2.2",
|
||||
"@lukeed/uuid": "^2.0.0",
|
||||
"cookie": "^0.4.1",
|
||||
"cross-env": "^7.0.3",
|
||||
"marked": "^2.1.3",
|
||||
"prism-svelte": "^0.4.7",
|
||||
"prismjs": "^1.24.0",
|
||||
"sass": "^1.35.1",
|
||||
"slug": "^5.1.0"
|
||||
}
|
||||
}
|
||||
15
packages/dooringx-doc/src/api/dd.md
Normal file
15
packages/dooringx-doc/src/api/dd.md
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
title: xcxzc
|
||||
sTitle: vc
|
||||
order: 4
|
||||
---
|
||||
|
||||
## dsas
|
||||
|
||||
saff
|
||||
sa
|
||||
d
|
||||
ad
|
||||
sa
|
||||
d
|
||||
fsad
|
||||
351
packages/dooringx-doc/src/app.css
Normal file
351
packages/dooringx-doc/src/app.css
Normal file
@@ -0,0 +1,351 @@
|
||||
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
|
||||
|
||||
/* Document
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Correct the line height in all browsers.
|
||||
* 2. Prevent adjustments of font size after orientation changes in iOS.
|
||||
*/
|
||||
|
||||
html {
|
||||
line-height: 1.15; /* 1 */
|
||||
-webkit-text-size-adjust: 100%; /* 2 */
|
||||
}
|
||||
|
||||
/* Sections
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the margin in all browsers.
|
||||
*/
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the `main` element consistently in IE.
|
||||
*/
|
||||
|
||||
main {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the font size and margin on `h1` elements within `section` and
|
||||
* `article` contexts in Chrome, Firefox, and Safari.
|
||||
*/
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
margin: 0.67em 0;
|
||||
}
|
||||
|
||||
/* Grouping content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Add the correct box sizing in Firefox.
|
||||
* 2. Show the overflow in Edge and IE.
|
||||
*/
|
||||
|
||||
hr {
|
||||
box-sizing: content-box; /* 1 */
|
||||
height: 0; /* 1 */
|
||||
overflow: visible; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||
* 2. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
pre {
|
||||
font-family: monospace, monospace; /* 1 */
|
||||
font-size: 1em; /* 2 */
|
||||
}
|
||||
|
||||
/* Text-level semantics
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the gray background on active links in IE 10.
|
||||
*/
|
||||
|
||||
a {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Remove the bottom border in Chrome 57-
|
||||
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
|
||||
*/
|
||||
|
||||
abbr[title] {
|
||||
border-bottom: none; /* 1 */
|
||||
text-decoration: underline; /* 2 */
|
||||
text-decoration: underline dotted; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font weight in Chrome, Edge, and Safari.
|
||||
*/
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||
* 2. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: monospace, monospace; /* 1 */
|
||||
font-size: 1em; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font size in all browsers.
|
||||
*/
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent `sub` and `sup` elements from affecting the line height in
|
||||
* all browsers.
|
||||
*/
|
||||
|
||||
sub,
|
||||
sup {
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
/* Embedded content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the border on images inside links in IE 10.
|
||||
*/
|
||||
|
||||
img {
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
/* Forms
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Change the font styles in all browsers.
|
||||
* 2. Remove the margin in Firefox and Safari.
|
||||
*/
|
||||
|
||||
button,
|
||||
input,
|
||||
optgroup,
|
||||
select,
|
||||
textarea {
|
||||
font-family: inherit; /* 1 */
|
||||
font-size: 100%; /* 1 */
|
||||
line-height: 1.15; /* 1 */
|
||||
margin: 0; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the overflow in IE.
|
||||
* 1. Show the overflow in Edge.
|
||||
*/
|
||||
|
||||
button,
|
||||
input {
|
||||
/* 1 */
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inheritance of text transform in Edge, Firefox, and IE.
|
||||
* 1. Remove the inheritance of text transform in Firefox.
|
||||
*/
|
||||
|
||||
button,
|
||||
select {
|
||||
/* 1 */
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the inability to style clickable types in iOS and Safari.
|
||||
*/
|
||||
|
||||
button,
|
||||
[type='button'],
|
||||
[type='reset'],
|
||||
[type='submit'] {
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inner border and padding in Firefox.
|
||||
*/
|
||||
|
||||
button::-moz-focus-inner,
|
||||
[type='button']::-moz-focus-inner,
|
||||
[type='reset']::-moz-focus-inner,
|
||||
[type='submit']::-moz-focus-inner {
|
||||
border-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the focus styles unset by the previous rule.
|
||||
*/
|
||||
|
||||
button:-moz-focusring,
|
||||
[type='button']:-moz-focusring,
|
||||
[type='reset']:-moz-focusring,
|
||||
[type='submit']:-moz-focusring {
|
||||
outline: 1px dotted ButtonText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the padding in Firefox.
|
||||
*/
|
||||
|
||||
fieldset {
|
||||
padding: 0.35em 0.75em 0.625em;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the text wrapping in Edge and IE.
|
||||
* 2. Correct the color inheritance from `fieldset` elements in IE.
|
||||
* 3. Remove the padding so developers are not caught out when they zero out
|
||||
* `fieldset` elements in all browsers.
|
||||
*/
|
||||
|
||||
legend {
|
||||
box-sizing: border-box; /* 1 */
|
||||
color: inherit; /* 2 */
|
||||
display: table; /* 1 */
|
||||
max-width: 100%; /* 1 */
|
||||
padding: 0; /* 3 */
|
||||
white-space: normal; /* 1 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
|
||||
*/
|
||||
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the default vertical scrollbar in IE 10+.
|
||||
*/
|
||||
|
||||
textarea {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Add the correct box sizing in IE 10.
|
||||
* 2. Remove the padding in IE 10.
|
||||
*/
|
||||
|
||||
[type='checkbox'],
|
||||
[type='radio'] {
|
||||
box-sizing: border-box; /* 1 */
|
||||
padding: 0; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the cursor style of increment and decrement buttons in Chrome.
|
||||
*/
|
||||
|
||||
[type='number']::-webkit-inner-spin-button,
|
||||
[type='number']::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the odd appearance in Chrome and Safari.
|
||||
* 2. Correct the outline style in Safari.
|
||||
*/
|
||||
|
||||
[type='search'] {
|
||||
-webkit-appearance: textfield; /* 1 */
|
||||
outline-offset: -2px; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inner padding in Chrome and Safari on macOS.
|
||||
*/
|
||||
|
||||
[type='search']::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inability to style clickable types in iOS and Safari.
|
||||
* 2. Change font properties to `inherit` in Safari.
|
||||
*/
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
-webkit-appearance: button; /* 1 */
|
||||
font: inherit; /* 2 */
|
||||
}
|
||||
|
||||
/* Interactive
|
||||
========================================================================== */
|
||||
|
||||
/*
|
||||
* Add the correct display in Edge, IE 10+, and Firefox.
|
||||
*/
|
||||
|
||||
details {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add the correct display in all browsers.
|
||||
*/
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
}
|
||||
|
||||
/* Misc
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 10+.
|
||||
*/
|
||||
|
||||
template {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 10.
|
||||
*/
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
||||
20
packages/dooringx-doc/src/app.html
Normal file
20
packages/dooringx-doc/src/app.html
Normal file
@@ -0,0 +1,20 @@
|
||||
<!--
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-06-29 11:14:15
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-01 22:13:09
|
||||
* @FilePath: \my-app\src\app.html
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="/favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
|
||||
%svelte.head%
|
||||
</head>
|
||||
<body>
|
||||
<div id="svelte">%svelte.body%</div>
|
||||
</body>
|
||||
</html>
|
||||
9
packages/dooringx-doc/src/docs/1.1.md
Normal file
9
packages/dooringx-doc/src/docs/1.1.md
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
title: dooringx-lib 是什么?
|
||||
sTitle: 介绍
|
||||
order: 1
|
||||
---
|
||||
|
||||
dooringx-lib 是 dooringx 的基座,是移除了 dooringx 插件的无代码低代码框架。
|
||||
|
||||
dooringx-lib 提供自己的一套数据流事件机制以及弹窗等解决方案,可以让你更快地自己定制开发无代码或低代码平台。
|
||||
13
packages/dooringx-doc/src/docs/1.2.md
Normal file
13
packages/dooringx-doc/src/docs/1.2.md
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
title: dooringx-lib 如何工作?
|
||||
sTitle: 介绍
|
||||
order: 2
|
||||
---
|
||||
|
||||
dooringx-lib 在载入后会进行实例化,如果有插件需要传递给 config。
|
||||
|
||||
开发者通过调用 api 来获取想要的数据,来开发出自己想要的功能。
|
||||
|
||||
对于概念部分请参考 dooringx-lib 基础,对于 api 部分请参考 api。
|
||||
|
||||
建议先学习 dooringx-lib 基础和 dooringx-lib 插件开发注意事项再去看 api
|
||||
15
packages/dooringx-doc/src/docs/1.3.md
Normal file
15
packages/dooringx-doc/src/docs/1.3.md
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
title: 快速上手
|
||||
sTitle: 介绍
|
||||
order: 3
|
||||
---
|
||||
|
||||
### 安装
|
||||
|
||||
使用 npm 或者 yarn 安装
|
||||
|
||||
```bash
|
||||
npm i dooringx-lib
|
||||
```
|
||||
|
||||
有关 api 部分请参考 api
|
||||
17
packages/dooringx-doc/src/docs/2.1.md
Normal file
17
packages/dooringx-doc/src/docs/2.1.md
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
title: store
|
||||
sTitle: dooringx-lib基础
|
||||
order: 3
|
||||
---
|
||||
|
||||
store 类似于 redux 的概念,它内部实现了 redo、undo、发布订阅、置换数据、强制刷新等功能。
|
||||
|
||||
store 可以在 config 中获取。
|
||||
|
||||
在最开始时,需要通过 useStoreState 与 react 结合,此时可以在任意位置使用 store.forceUpdate 强刷,也可以使用 state 获取 store 中的数据。
|
||||
|
||||
store 的主要数据是保存着每次修改 jsonSchema 队列。
|
||||
|
||||
如果你需要更新数据,在深拷贝后使用 setData 方法进行更新。
|
||||
|
||||
如果你需要更新时不记录在 redo 或 undo 上留下记录,那么请操作队列删除其中保存内容即可。
|
||||
5
packages/dooringx-doc/src/docs/2.2.md
Normal file
5
packages/dooringx-doc/src/docs/2.2.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
title: functionCenter
|
||||
sTitle: dooringx-lib基础
|
||||
order: 3
|
||||
---
|
||||
1
packages/dooringx-doc/src/global.d.ts
vendored
Normal file
1
packages/dooringx-doc/src/global.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/// <reference types="@sveltejs/kit" />
|
||||
43
packages/dooringx-doc/src/hooks.ts
Normal file
43
packages/dooringx-doc/src/hooks.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-06-29 11:14:15
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-01 10:13:37
|
||||
* @FilePath: \my-app\src\hooks.ts
|
||||
*/
|
||||
import cookie from 'cookie';
|
||||
import { v4 as uuid } from '@lukeed/uuid';
|
||||
import type { Handle } from '@sveltejs/kit';
|
||||
|
||||
export const handle: Handle = async ({ request, resolve }) => {
|
||||
const cookies = cookie.parse(request.headers.cookie || '');
|
||||
request.locals.userid = cookies.userid || uuid();
|
||||
// TODO https://github.com/sveltejs/kit/issues/1046
|
||||
if (request.query.has('_method')) {
|
||||
request.method = request.query.get('_method').toUpperCase();
|
||||
}
|
||||
|
||||
const response = await resolve(request);
|
||||
|
||||
if (!cookies.userid) {
|
||||
// if this is the first time the user has visited this app,
|
||||
// set a cookie so that we recognise them when they return
|
||||
response.headers['set-cookie'] = `userid=${request.locals.userid}; Path=/; HttpOnly`;
|
||||
}
|
||||
|
||||
return response;
|
||||
};
|
||||
/** @type {import('@sveltejs/kit').ServerFetch} */
|
||||
export async function serverFetch(request) {
|
||||
/*
|
||||
if (request.url.startsWith('https://api.yourapp.com/')) {
|
||||
// clone the original request, but change the URL
|
||||
request = new Request(
|
||||
request.url.replace('https://api.yourapp.com/', 'http://localhost:9999/'),
|
||||
request
|
||||
);
|
||||
}
|
||||
*/
|
||||
|
||||
return fetch(request);
|
||||
}
|
||||
37
packages/dooringx-doc/src/lib/Button/index.svelte
Normal file
37
packages/dooringx-doc/src/lib/Button/index.svelte
Normal file
@@ -0,0 +1,37 @@
|
||||
<script lang="ts">
|
||||
export let href = '';
|
||||
export let color = '#4d5164';
|
||||
let backgroundColor = 'white';
|
||||
export let style = '';
|
||||
export let onClick = () => {};
|
||||
</script>
|
||||
|
||||
<button
|
||||
class="yh-btn"
|
||||
style={`color: ${color} ; background-color:${backgroundColor};${style} `}
|
||||
on:click={() => {
|
||||
if (href !== '') {
|
||||
location.href = href;
|
||||
}
|
||||
onClick();
|
||||
}}
|
||||
>
|
||||
<slot />
|
||||
</button>
|
||||
|
||||
<style lang="scss">
|
||||
.yh-btn {
|
||||
border: none;
|
||||
transition: all 0.3s linear;
|
||||
padding: 5px 20px;
|
||||
&:active,
|
||||
&:hover,
|
||||
&:focus {
|
||||
border: none;
|
||||
outline-width: 0;
|
||||
}
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
177
packages/dooringx-doc/src/lib/DocRender/index.svelte
Normal file
177
packages/dooringx-doc/src/lib/DocRender/index.svelte
Normal file
@@ -0,0 +1,177 @@
|
||||
<script lang="ts">
|
||||
import type { MarkDownItemProps } from 'src/routes/docs/_api';
|
||||
import './prism.css';
|
||||
export let sections: Map<string, MarkDownItemProps[]>;
|
||||
let arr = [];
|
||||
$: arr = Array.from(sections.keys());
|
||||
export let active = '';
|
||||
</script>
|
||||
|
||||
<div style="display: flex;">
|
||||
<div class="sidebar">
|
||||
{#each arr as stitle}
|
||||
{#if stitle !== 'default'}
|
||||
<div
|
||||
class={`stitle ahref fbold ${active === stitle ? 'active' : ''}`}
|
||||
style="cursor: pointer;"
|
||||
title={stitle}
|
||||
on:click={() => {
|
||||
location.href = `#${stitle}`;
|
||||
active = stitle;
|
||||
}}
|
||||
>
|
||||
{@html stitle}
|
||||
</div>
|
||||
{#each sections.get(stitle) as section}
|
||||
<div class="title-item" style="cursor: pointer;">
|
||||
<span
|
||||
class={`ahref ${active === section.metadata.title ? 'active' : ''}`}
|
||||
on:click={() => {
|
||||
location.href = `#${section.slug}`;
|
||||
active = section.metadata.title;
|
||||
}}
|
||||
title={section.metadata.title}
|
||||
>
|
||||
{@html section.metadata.title}
|
||||
</span>
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
{#if stitle === 'default'}
|
||||
{#each sections.get(stitle) as section}
|
||||
<!-- 没有主标题则二级变一级 -->
|
||||
<div class="stitle fbold" style="cursor: pointer;">
|
||||
<span
|
||||
class={`ahref ${active === section.metadata.title ? 'active' : ''}`}
|
||||
on:click={() => {
|
||||
location.href = `#${section.slug}`;
|
||||
active = section.metadata.title;
|
||||
}}
|
||||
title={section.metadata.title}
|
||||
>
|
||||
{@html section.metadata.title}
|
||||
</span>
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
<div class="markdown-wrapper">
|
||||
{#each arr as stitle}
|
||||
{#if stitle !== 'default'}
|
||||
<h1 class="stitle-content" id={stitle}>
|
||||
{@html stitle}
|
||||
</h1>
|
||||
<div class="yh-interval-s" />
|
||||
{/if}
|
||||
{#each sections.get(stitle) as section}
|
||||
<section data-id={section.slug}>
|
||||
<h2>
|
||||
<span class="offset-anchor" id={section.slug} />
|
||||
{@html section.metadata.title}
|
||||
</h2>
|
||||
|
||||
{@html section.html}
|
||||
</section>
|
||||
<div class="yh-interval" />
|
||||
{/each}
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.yh-interval-s {
|
||||
width: 100%;
|
||||
padding: 2px;
|
||||
}
|
||||
.yh-interval {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
}
|
||||
.markdown-wrapper {
|
||||
height: calc(100vh - 40px);
|
||||
overflow: auto;
|
||||
width: 100%;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.fbold {
|
||||
font-weight: bold;
|
||||
}
|
||||
.sidebar {
|
||||
padding: 20px;
|
||||
overflow: auto;
|
||||
height: calc(100vh - 40px);
|
||||
border-right: 1px solid #eee;
|
||||
width: 300px;
|
||||
box-sizing: border-box;
|
||||
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, PingFang SC, Hiragino Sans GB,
|
||||
Microsoft YaHei, Helvetica Neue, Helvetica, Arial, sans-serif, Apple Color Emoji,
|
||||
Segoe UI Emoji, Segoe UI Symbol;
|
||||
.ahref {
|
||||
text-decoration: none;
|
||||
color: #717484;
|
||||
&:hover {
|
||||
color: #4569d4;
|
||||
}
|
||||
&:visited,
|
||||
&:link,
|
||||
&:active {
|
||||
color: #717484;
|
||||
}
|
||||
}
|
||||
.stitle {
|
||||
margin: 20px 0;
|
||||
}
|
||||
.title-item {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
margin: 20px 0 20px 20px;
|
||||
|
||||
& ::selection {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.active {
|
||||
color: #4569d4;
|
||||
}
|
||||
}
|
||||
section :global(blockquote) {
|
||||
color: hsl(204, 100%, 50%);
|
||||
border: 2px solid var(--flash);
|
||||
}
|
||||
section :global(blockquote) :global(code) {
|
||||
background: hsl(204, 100%, 95%) !important;
|
||||
color: hsl(204, 100%, 50%);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 5px; /*对垂直流动条有效*/
|
||||
height: 5px; /*对水平流动条有效*/
|
||||
}
|
||||
|
||||
/*定义滚动条的轨道颜色、内阴影及圆角*/
|
||||
::-webkit-scrollbar-track {
|
||||
box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.3);
|
||||
background-color: #eee;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
/*定义滑块颜色、内阴影及圆角*/
|
||||
::-webkit-scrollbar-thumb {
|
||||
border-radius: 7px;
|
||||
box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
|
||||
background-color: #444444;
|
||||
}
|
||||
|
||||
/*定义两端按钮的样式*/
|
||||
::-webkit-scrollbar-button {
|
||||
background-color: #b9c6d2;
|
||||
}
|
||||
|
||||
/*定义右下角汇合处的样式*/
|
||||
::-webkit-scrollbar-corner {
|
||||
background: #b9c6d2;
|
||||
}
|
||||
</style>
|
||||
120
packages/dooringx-doc/src/lib/DocRender/prism.css
Normal file
120
packages/dooringx-doc/src/lib/DocRender/prism.css
Normal file
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
-----------------------------------------------
|
||||
syntax-highlighting [prism]
|
||||
-----------------------------------------------
|
||||
*/
|
||||
|
||||
/* colors --------------------------------- */
|
||||
pre[class*='language-'] {
|
||||
--background: var(--back-light);
|
||||
--base: #545454;
|
||||
--comment: #696969;
|
||||
--keyword: #007f8a;
|
||||
--function: #bb5525;
|
||||
--string: #856e3d;
|
||||
--number: #008000;
|
||||
--tags: var(--function);
|
||||
--important: var(--string);
|
||||
}
|
||||
|
||||
/* type-base ------------------------------ */
|
||||
code[class*='language-'],
|
||||
pre[class*='language-'] {
|
||||
background: none;
|
||||
text-align: left;
|
||||
white-space: pre;
|
||||
word-spacing: normal;
|
||||
word-break: normal;
|
||||
word-wrap: normal;
|
||||
font: 300 var(--code-fs) / 1.7 var(--font-mono);
|
||||
color: var(--base);
|
||||
tab-size: 2;
|
||||
-moz-tab-size: 2;
|
||||
-webkit-hyphens: none;
|
||||
hyphens: none;
|
||||
}
|
||||
|
||||
/* code-blocks ---------------------------- */
|
||||
pre[class*='language-'] {
|
||||
overflow: auto;
|
||||
padding: 1.5rem 2rem;
|
||||
margin: 0.8rem 0 2.4rem;
|
||||
/* max-width: var(--code-w); */
|
||||
border-radius: var(--border-r);
|
||||
box-shadow: 1px 1px 1px rgba(68, 68, 68, 0.12) inset;
|
||||
}
|
||||
|
||||
:not(pre) > code[class*='language-'],
|
||||
pre[class*='language-'] {
|
||||
background: var(--background);
|
||||
}
|
||||
|
||||
/* tokens --------------------------------- */
|
||||
.token.comment,
|
||||
.token.prolog,
|
||||
.token.doctype,
|
||||
.token.cdata {
|
||||
color: var(--comment);
|
||||
}
|
||||
|
||||
.token.punctuation {
|
||||
color: var(--base);
|
||||
}
|
||||
|
||||
.token.property,
|
||||
.token.tag,
|
||||
.token.constant,
|
||||
.token.symbol,
|
||||
.token.deleted {
|
||||
color: var(--tags);
|
||||
}
|
||||
|
||||
.token.boolean,
|
||||
.token.number {
|
||||
color: var(--number);
|
||||
}
|
||||
|
||||
.token.selector,
|
||||
.token.attr-name,
|
||||
.token.string,
|
||||
.token.char,
|
||||
.token.builtin,
|
||||
.token.inserted {
|
||||
color: var(--string);
|
||||
}
|
||||
|
||||
.token.operator,
|
||||
.token.entity,
|
||||
.token.url,
|
||||
.language-css .token.string,
|
||||
.style .token.string,
|
||||
.token.variable {
|
||||
color: var(--base);
|
||||
}
|
||||
|
||||
.token.atrule,
|
||||
.token.attr-value,
|
||||
.token.function,
|
||||
.token.class-name {
|
||||
color: var(--function);
|
||||
}
|
||||
|
||||
.token.keyword {
|
||||
color: var(--keyword);
|
||||
}
|
||||
|
||||
.token.regex,
|
||||
.token.important {
|
||||
color: var(--important);
|
||||
}
|
||||
|
||||
.token.important,
|
||||
.token.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
.token.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
.token.entity {
|
||||
cursor: help;
|
||||
}
|
||||
85
packages/dooringx-doc/src/lib/Header/index.svelte
Normal file
85
packages/dooringx-doc/src/lib/Header/index.svelte
Normal file
@@ -0,0 +1,85 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { base } from '$app/paths';
|
||||
import Button from '../Button/index.svelte';
|
||||
import Switch from '../Switch/index.svelte';
|
||||
import logo from './svelte-logo.svg';
|
||||
import { getContext } from 'svelte';
|
||||
import type { Writable } from 'svelte/store';
|
||||
const lang = getContext<Writable<string>>('lang');
|
||||
let checked = true;
|
||||
lang.subscribe((value) => {
|
||||
value === 'cn' ? (checked = true) : (checked = false);
|
||||
});
|
||||
const home = base + '/';
|
||||
const docs = base + '/docs';
|
||||
const api = base + '/api';
|
||||
</script>
|
||||
|
||||
<header>
|
||||
<div class="corner">
|
||||
<img src={logo} alt="SvelteKit" />
|
||||
</div>
|
||||
|
||||
<nav style="width: 100%;">
|
||||
<div class="nav-item-wrapper">
|
||||
<div class:active={$page.path === '/'}>
|
||||
<Button href={home} color={$page.path === '/' ? '#4569d4' : '#4d5164'}>首页</Button>
|
||||
</div>
|
||||
<div class:active={$page.path === '/docs'}>
|
||||
<Button href={docs} color={$page.path === '/docs' ? '#4569d4' : '#4d5164'}>文档</Button>
|
||||
</div>
|
||||
<div class:active={$page.path === '/api'}>
|
||||
<Button href={api} color={$page.path === '/api' ? '#4569d4' : '#4d5164'}>API</Button>
|
||||
</div>
|
||||
<div class:active={$page.path === '/about'}>
|
||||
<Button>Github</Button>
|
||||
</div>
|
||||
|
||||
<Switch
|
||||
{checked}
|
||||
onChange={() => {
|
||||
lang.update((pre) => {
|
||||
return pre === 'cn' ? 'en' : 'cn';
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<style lang="scss">
|
||||
$height: 40px;
|
||||
header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, PingFang SC, Hiragino Sans GB,
|
||||
Microsoft YaHei, Helvetica Neue, Helvetica, Arial, sans-serif, Apple Color Emoji,
|
||||
Segoe UI Emoji, Segoe UI Symbol;
|
||||
border-bottom: 1px solid #e2e2e2;
|
||||
}
|
||||
.active {
|
||||
color: #4569d4;
|
||||
}
|
||||
.corner {
|
||||
height: $height;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-left: 20px;
|
||||
img {
|
||||
width: $height - 5px;
|
||||
height: $height - 5px;
|
||||
}
|
||||
}
|
||||
.nav-item-wrapper {
|
||||
height: $height;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
:nth-last-child(1) {
|
||||
margin-right: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
1
packages/dooringx-doc/src/lib/Header/svelte-logo.svg
Normal file
1
packages/dooringx-doc/src/lib/Header/svelte-logo.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="107" height="128" viewBox="0 0 107 128"><title>svelte-logo</title><path d="M94.1566,22.8189c-10.4-14.8851-30.94-19.2971-45.7914-9.8348L22.2825,29.6078A29.9234,29.9234,0,0,0,8.7639,49.6506a31.5136,31.5136,0,0,0,3.1076,20.2318A30.0061,30.0061,0,0,0,7.3953,81.0653a31.8886,31.8886,0,0,0,5.4473,24.1157c10.4022,14.8865,30.9423,19.2966,45.7914,9.8348L84.7167,98.3921A29.9177,29.9177,0,0,0,98.2353,78.3493,31.5263,31.5263,0,0,0,95.13,58.117a30,30,0,0,0,4.4743-11.1824,31.88,31.88,0,0,0-5.4473-24.1157" style="fill:#ff3e00"/><path d="M45.8171,106.5815A20.7182,20.7182,0,0,1,23.58,98.3389a19.1739,19.1739,0,0,1-3.2766-14.5025,18.1886,18.1886,0,0,1,.6233-2.4357l.4912-1.4978,1.3363.9815a33.6443,33.6443,0,0,0,10.203,5.0978l.9694.2941-.0893.9675a5.8474,5.8474,0,0,0,1.052,3.8781,6.2389,6.2389,0,0,0,6.6952,2.485,5.7449,5.7449,0,0,0,1.6021-.7041L69.27,76.281a5.4306,5.4306,0,0,0,2.4506-3.631,5.7948,5.7948,0,0,0-.9875-4.3712,6.2436,6.2436,0,0,0-6.6978-2.4864,5.7427,5.7427,0,0,0-1.6.7036l-9.9532,6.3449a19.0329,19.0329,0,0,1-5.2965,2.3259,20.7181,20.7181,0,0,1-22.2368-8.2427,19.1725,19.1725,0,0,1-3.2766-14.5024,17.9885,17.9885,0,0,1,8.13-12.0513L55.8833,23.7472a19.0038,19.0038,0,0,1,5.3-2.3287A20.7182,20.7182,0,0,1,83.42,29.6611a19.1739,19.1739,0,0,1,3.2766,14.5025,18.4,18.4,0,0,1-.6233,2.4357l-.4912,1.4978-1.3356-.98a33.6175,33.6175,0,0,0-10.2037-5.1l-.9694-.2942.0893-.9675a5.8588,5.8588,0,0,0-1.052-3.878,6.2389,6.2389,0,0,0-6.6952-2.485,5.7449,5.7449,0,0,0-1.6021.7041L37.73,51.719a5.4218,5.4218,0,0,0-2.4487,3.63,5.7862,5.7862,0,0,0,.9856,4.3717,6.2437,6.2437,0,0,0,6.6978,2.4864,5.7652,5.7652,0,0,0,1.602-.7041l9.9519-6.3425a18.978,18.978,0,0,1,5.2959-2.3278,20.7181,20.7181,0,0,1,22.2368,8.2427,19.1725,19.1725,0,0,1,3.2766,14.5024,17.9977,17.9977,0,0,1-8.13,12.0532L51.1167,104.2528a19.0038,19.0038,0,0,1-5.3,2.3287" style="fill:#fff"/></svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
32
packages/dooringx-doc/src/lib/Icon/index.svelte
Normal file
32
packages/dooringx-doc/src/lib/Icon/index.svelte
Normal file
@@ -0,0 +1,32 @@
|
||||
<!--
|
||||
-----------------------------------------------
|
||||
svg icon
|
||||
- https://github.com/jacobmischka/svelte-feather-icon
|
||||
- https://feathericons.com/
|
||||
-----------------------------------------------
|
||||
-->
|
||||
<script>
|
||||
export let name;
|
||||
export let size = 20;
|
||||
</script>
|
||||
|
||||
<svg class="icon" width={size} height={size}>
|
||||
<use xlink:href="#{name}" />
|
||||
</svg>
|
||||
|
||||
<style>
|
||||
.icon {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
vertical-align: middle;
|
||||
-o-object-fit: contain;
|
||||
object-fit: contain;
|
||||
-webkit-transform-origin: center center;
|
||||
transform-origin: center center;
|
||||
stroke: currentColor;
|
||||
stroke-width: 2;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
fill: none;
|
||||
}
|
||||
</style>
|
||||
123
packages/dooringx-doc/src/lib/Icons/index.svelte
Normal file
123
packages/dooringx-doc/src/lib/Icons/index.svelte
Normal file
@@ -0,0 +1,123 @@
|
||||
<div style="display: none">
|
||||
<!-- wrapper div allows use of innerHTML -->
|
||||
<svg>
|
||||
<symbol id="arrow-left" class="icon" viewBox="0 0 24 24">
|
||||
<line x1="19" y1="12" x2="5" y2="12" />
|
||||
<polyline points="12 19 5 12 12 5" />
|
||||
</symbol>
|
||||
|
||||
<symbol id="arrow-right" class="icon" viewBox="0 0 24 24">
|
||||
<line x1="5" y1="12" x2="19" y2="12" />
|
||||
<polyline points="12 5 19 12 12 19" />
|
||||
</symbol>
|
||||
|
||||
<symbol id="arrow-up" class="icon" viewBox="0 0 24 24">
|
||||
<line x1="12" y1="19" x2="12" y2="5" />
|
||||
<polyline points="5 12 12 5 19 12" />
|
||||
</symbol>
|
||||
|
||||
<symbol id="arrow-down" class="icon" viewBox="0 0 24 24">
|
||||
<line x1="12" y1="5" x2="12" y2="19" />
|
||||
<polyline points="19 12 12 19 5 12" />
|
||||
</symbol>
|
||||
|
||||
<symbol id="check" class="icon" viewBox="0 0 24 24">
|
||||
<polyline points="20 6 9 17 4 12" />
|
||||
</symbol>
|
||||
|
||||
<symbol id="close" class="icon" viewBox="0 0 24 24">
|
||||
<line x1="18" y1="6" x2="6" y2="18" />
|
||||
<line x1="6" y1="6" x2="18" y2="18" />
|
||||
</symbol>
|
||||
|
||||
<symbol id="download" class="icon" viewBox="0 0 24 24">
|
||||
<path d="M21 15V19A2 2 0 0 1 19 21H5A2 2 0 0 1 3 19V15" />
|
||||
<polyline points="7 10 12 15 17 10" />
|
||||
<line x1="12" y1="15" x2="12" y2="3" />
|
||||
</symbol>
|
||||
|
||||
<symbol id="edit" class="icon" viewBox="0 0 24 24">
|
||||
<path d="M20 14.66V20a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h5.34" />
|
||||
<polygon points="18 2 22 6 12 16 8 16 8 12 18 2" />
|
||||
</symbol>
|
||||
|
||||
<symbol id="github" class="icon" viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"
|
||||
/>
|
||||
</symbol>
|
||||
|
||||
<symbol id="git-branch" class="icon" viewBox="0 0 24 24">
|
||||
<line x1="6" y1="3" x2="6" y2="15" />
|
||||
<circle cx="18" cy="6" r="3" />
|
||||
<circle cx="6" cy="18" r="3" />
|
||||
<path d="M18 9a9 9 0 0 1-9 9" />
|
||||
</symbol>
|
||||
|
||||
<symbol id="log-in" class="icon" viewBox="0 0 24 24">
|
||||
<path d="M15 3H19A2 2 0 0 1 21 5V19A2 2 0 0 1 19 21H15" />
|
||||
<polyline points="10 17 15 12 10 7" />
|
||||
<line x1="15" y1="12" x2="3" y2="12" />
|
||||
</symbol>
|
||||
|
||||
<symbol id="maximize" class="icon" viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"
|
||||
/>
|
||||
</symbol>
|
||||
|
||||
<symbol id="maximize-2" class="icon" viewBox="0 0 24 24">
|
||||
<polyline points="15 3 21 3 21 9" />
|
||||
<polyline points="9 21 3 21 3 15" />
|
||||
<line x1="21" y1="3" x2="14" y2="10" />
|
||||
<line x1="3" y1="21" x2="10" y2="14" />
|
||||
</symbol>
|
||||
|
||||
<symbol id="menu" class="icon" viewBox="0 0 24 24">
|
||||
<line x1="3" y1="12" x2="21" y2="12" />
|
||||
<line x1="3" y1="6" x2="21" y2="6" />
|
||||
<line x1="3" y1="18" x2="21" y2="18" />
|
||||
</symbol>
|
||||
|
||||
<symbol id="message-square" class="icon" viewBox="0 0 24 24">
|
||||
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
|
||||
</symbol>
|
||||
|
||||
<symbol id="minus" class="icon" viewBox="0 0 24 24">
|
||||
<line x1="5" y1="12" x2="19" y2="12" />
|
||||
</symbol>
|
||||
|
||||
<symbol id="plus" class="icon" viewBox="0 0 24 24">
|
||||
<line x1="12" y1="5" x2="12" y2="19" />
|
||||
<line x1="5" y1="12" x2="19" y2="12" />
|
||||
</symbol>
|
||||
|
||||
<symbol id="save" class="icon" viewBox="0 0 24 24">
|
||||
<path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z" />
|
||||
<polyline points="17 21 17 13 7 13 7 21" />
|
||||
<polyline points="7 3 7 8 15 8" />
|
||||
</symbol>
|
||||
|
||||
<symbol id="link" class="icon" viewBox="0 0 24 24">
|
||||
<path d="M9,7L6,7A2 2 0 0 0 6,17L9,17" />
|
||||
<path d="M15,7L18,7A2 2 0 0 1 18,17L15,17" />
|
||||
<path d="M7,12L17,12" />
|
||||
</symbol>
|
||||
|
||||
<symbol id="chevron" class="icon" viewBox="0 0 24 24">
|
||||
<path d="M2,7 L12,17 L20,7" />
|
||||
</symbol>
|
||||
|
||||
<symbol id="404" class="icon" viewBox="0 0 128 128">
|
||||
<path
|
||||
d="M121.718 73.272v9.953c3.957-7.584 6.199-16.05 6.199-24.995C127.917 26.079 99.273 0 63.958 0 28.644 0 0 26.079 0 58.23c0 .403.028.806.028 1.21l22.97-25.953h13.34l-19.76 27.187h6.42V53.77l13.728-19.477v49.361H22.998V73.272H2.158c5.951 20.284 23.608 36.208 45.998 41.399-1.44 3.3-5.618 11.263-12.565 12.674-8.607 1.764 23.358.428 46.163-13.178 17.519-4.611 31.938-15.849 39.77-30.513h-13.506V73.272H85.02V59.464l22.998-25.977h13.008l-19.429 27.187h6.421v-7.433l13.727-19.402v39.433h-.027zm-78.24 2.822a10.516 10.516 0 01-.996-4.535V44.548c0-1.613.332-3.124.996-4.535a11.66 11.66 0 012.713-3.68c1.134-1.032 2.49-1.864 4.04-2.468 1.55-.605 3.21-.908 4.982-.908h11.292c1.77 0 3.431.303 4.981.908 1.522.604 2.85 1.41 3.986 2.418l-12.26 16.303v-2.898a1.96 1.96 0 00-.665-1.512c-.443-.403-.996-.604-1.66-.604-.665 0-1.218.201-1.661.604a1.96 1.96 0 00-.664 1.512v9.071L44.364 77.606a10.556 10.556 0 01-.886-1.512zm35.73-4.535c0 1.613-.332 3.124-.997 4.535a11.66 11.66 0 01-2.712 3.68c-1.134 1.032-2.49 1.864-4.04 2.469-1.55.604-3.21.907-4.982.907H55.185c-1.77 0-3.431-.303-4.981-.907-1.55-.605-2.906-1.437-4.041-2.47a12.49 12.49 0 01-1.384-1.512l13.727-18.217v6.375c0 .605.222 1.109.665 1.512.442.403.996.604 1.66.604.664 0 1.218-.201 1.66-.604a1.96 1.96 0 00.665-1.512V53.87L75.97 36.838c.913.932 1.66 1.99 2.214 3.175.664 1.41.996 2.922.996 4.535v27.011h.028z"
|
||||
/>
|
||||
</symbol>
|
||||
<symbol id="user" class="icon" viewBox="0 0 130 130">
|
||||
<path
|
||||
d="M63.444 64.996c20.633 0 37.359-14.308 37.359-31.953 0-17.649-16.726-31.952-37.359-31.952-20.631 0-37.36 14.303-37.358 31.952 0 17.645 16.727 31.953 37.359 31.953zM80.57 75.65H49.434c-26.652 0-48.26 18.477-48.26 41.27v2.664c0 9.316 21.608 9.325 48.26 9.325H80.57c26.649 0 48.256-.344 48.256-9.325v-2.663c0-22.794-21.605-41.271-48.256-41.271z"
|
||||
stroke="#979797"
|
||||
/>
|
||||
</symbol>
|
||||
</svg>
|
||||
</div>
|
||||
89
packages/dooringx-doc/src/lib/Switch/index.svelte
Normal file
89
packages/dooringx-doc/src/lib/Switch/index.svelte
Normal file
@@ -0,0 +1,89 @@
|
||||
<script lang="ts">
|
||||
export let checked = true;
|
||||
export let disabled = false;
|
||||
export let onChange = () => {
|
||||
checked = !checked;
|
||||
};
|
||||
export let unCheckText = 'EN';
|
||||
export let checkText = '中文';
|
||||
</script>
|
||||
|
||||
<label class="yh-switch-label">
|
||||
<input
|
||||
class="yh-swtich-input"
|
||||
type="checkbox"
|
||||
{checked}
|
||||
{disabled}
|
||||
on:change={() => {
|
||||
onChange();
|
||||
}}
|
||||
/>
|
||||
<span class={`yh-switch-sp1`}>
|
||||
{#if !checked}
|
||||
<span class="yh-switch-sp1-uncheck">
|
||||
{unCheckText}
|
||||
</span>
|
||||
{:else}
|
||||
<span class="yh-switch-sp1-check">
|
||||
{checkText}
|
||||
</span>
|
||||
{/if}
|
||||
</span>
|
||||
|
||||
<span class={`yh-switch-sp2 ${checked ? 'right' : ''}`} />
|
||||
</label>
|
||||
|
||||
<style lang="scss">
|
||||
.yh-switch-label {
|
||||
height: 22.5px;
|
||||
width: 60px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
margin: 0 10px;
|
||||
}
|
||||
.yh-swtich-input {
|
||||
opacity: 0;
|
||||
}
|
||||
.yh-switch-sp1 {
|
||||
background: #fff;
|
||||
box-shadow: inset 2px 2px 4px #d9d9d9, inset -2px -2px 4px #fff, 2px 2px 4px #d9d9d9;
|
||||
color: #595959;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
padding: 1px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
border-radius: 60px;
|
||||
&-uncheck {
|
||||
font-size: 10px;
|
||||
top: 7px;
|
||||
right: 10px;
|
||||
position: absolute;
|
||||
}
|
||||
&-check {
|
||||
font-size: 10px;
|
||||
left: 10px;
|
||||
top: 7px;
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
|
||||
.yh-switch-sp2 {
|
||||
height: 22.5px;
|
||||
width: 22.5px;
|
||||
background: #fff;
|
||||
border: none;
|
||||
box-shadow: 2px 2px 4px #d9d9d9;
|
||||
text-shadow: 1px 1px 4px #d9d9d9, -1px -1px 4px #fff;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
transition: all 0.36s cubic-bezier(0.78, 0.14, 0.15, 0.86);
|
||||
&.right {
|
||||
left: calc(100% - 22.5px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
60
packages/dooringx-doc/src/lib/form.ts
Normal file
60
packages/dooringx-doc/src/lib/form.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
// this action (https://svelte.dev/tutorial/actions) allows us to
|
||||
// progressively enhance a <form> that already works without JS
|
||||
export function enhance(
|
||||
form: HTMLFormElement,
|
||||
{
|
||||
pending,
|
||||
error,
|
||||
result,
|
||||
}: {
|
||||
pending?: (data: FormData, form: HTMLFormElement) => void;
|
||||
error?: (res: Response, error: Error, form: HTMLFormElement) => void;
|
||||
result: (res: Response, form: HTMLFormElement) => void;
|
||||
}
|
||||
) {
|
||||
let current_token: {};
|
||||
|
||||
async function handle_submit(e: Event) {
|
||||
const token = (current_token = {});
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
const body = new FormData(form);
|
||||
|
||||
if (pending) pending(body, form);
|
||||
|
||||
try {
|
||||
const res = await fetch(form.action, {
|
||||
method: form.method,
|
||||
headers: {
|
||||
accept: 'application/json',
|
||||
},
|
||||
body,
|
||||
});
|
||||
|
||||
if (token !== current_token) return;
|
||||
|
||||
if (res.ok) {
|
||||
result(res, form);
|
||||
} else if (error) {
|
||||
error(res, null, form);
|
||||
} else {
|
||||
console.error(await res.text());
|
||||
}
|
||||
} catch (e) {
|
||||
if (error) {
|
||||
error(null, e, form);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
form.addEventListener('submit', handle_submit);
|
||||
|
||||
return {
|
||||
destroy() {
|
||||
form.removeEventListener('submit', handle_submit);
|
||||
},
|
||||
};
|
||||
}
|
||||
7
packages/dooringx-doc/src/lib/types.d.ts
vendored
Normal file
7
packages/dooringx-doc/src/lib/types.d.ts
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Can be made globally available by placing this
|
||||
* inside `global.d.ts` and removing `export` keyword
|
||||
*/
|
||||
export interface Locals {
|
||||
userid: string;
|
||||
}
|
||||
39
packages/dooringx-doc/src/routes/__layout.svelte
Normal file
39
packages/dooringx-doc/src/routes/__layout.svelte
Normal file
@@ -0,0 +1,39 @@
|
||||
<script context="module">
|
||||
import { browser, dev } from '$app/env';
|
||||
// we don't need any JS on this page, though we'll load
|
||||
// it in dev so that we get hot module replacement...
|
||||
export const hydrate = dev;
|
||||
|
||||
// ...but if the client-side router is already loaded
|
||||
// (i.e. we came here from elsewhere in the app), use it
|
||||
export const router = browser;
|
||||
|
||||
// since there's no dynamic data here, we can prerender
|
||||
// it so that it gets served as a static asset in prod
|
||||
export const prerender = true;
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import Header from '$lib/Header/index.svelte';
|
||||
import '../app.css';
|
||||
import { setContext } from 'svelte';
|
||||
import { writable } from 'svelte/store';
|
||||
const lang = writable('cn');
|
||||
|
||||
setContext('lang', lang);
|
||||
</script>
|
||||
|
||||
<Header />
|
||||
<main>
|
||||
<slot />
|
||||
</main>
|
||||
|
||||
<style lang="scss">
|
||||
main {
|
||||
position: relative;
|
||||
height: calc(100vh - 41px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
45
packages/dooringx-doc/src/routes/about.svelte
Normal file
45
packages/dooringx-doc/src/routes/about.svelte
Normal file
@@ -0,0 +1,45 @@
|
||||
<script lang="ts">
|
||||
import Icon from '$lib/Icon/index.svelte';
|
||||
import Icons from '$lib/Icons/index.svelte';
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>About</title>
|
||||
</svelte:head>
|
||||
|
||||
<Icons />
|
||||
|
||||
<div class="content">
|
||||
<h1>About this app</h1>
|
||||
<Icon name="arrow-left" />
|
||||
<Icon name="arrow-right" />
|
||||
|
||||
<Icon name="arrow-up" />
|
||||
<Icon name="arrow-down" />
|
||||
<Icon name="check" />
|
||||
<Icon name="close" />
|
||||
<Icon name="download" />
|
||||
<Icon name="edit" />
|
||||
<Icon name="github" />
|
||||
<Icon name="git-branch" />
|
||||
<Icon name="log-in" />
|
||||
<Icon name="maximize" />
|
||||
<Icon name="maximize-2" />
|
||||
<Icon name="menu" />
|
||||
<Icon name="message-square" />
|
||||
<Icon name="minus" />
|
||||
<Icon name="plus" />
|
||||
<Icon name="save" />
|
||||
<Icon name="link" />
|
||||
<Icon name="chevron" />
|
||||
<Icon name="404" />
|
||||
<Icon name="user" />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.content {
|
||||
width: 100%;
|
||||
max-width: var(--column-width);
|
||||
margin: var(--column-margin-top) auto 0 auto;
|
||||
}
|
||||
</style>
|
||||
17
packages/dooringx-doc/src/routes/api/index.json.ts
Normal file
17
packages/dooringx-doc/src/routes/api/index.json.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-07-06 20:19:21
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-06 20:20:07
|
||||
* @FilePath: \my-app\src\routes\api\index.json.ts
|
||||
*/
|
||||
// 必须建立json否则不生成json文件
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { api } from '../docs/_api';
|
||||
|
||||
export const get: RequestHandler = async (request) => {
|
||||
const param = request.query;
|
||||
const name = param.get('name');
|
||||
const response = await api(name);
|
||||
return response;
|
||||
};
|
||||
64
packages/dooringx-doc/src/routes/api/index.svelte
Normal file
64
packages/dooringx-doc/src/routes/api/index.svelte
Normal file
@@ -0,0 +1,64 @@
|
||||
<script context="module" lang="ts">
|
||||
import type { Load } from '@sveltejs/kit';
|
||||
import type { MarkDownItemProps } from '../docs/_api';
|
||||
import { base } from '$app/paths';
|
||||
export const load: Load = async ({ fetch }) => {
|
||||
const path = `${base}/api.json?name=api`;
|
||||
const res = await fetch(path);
|
||||
if (res.ok) {
|
||||
const files: MarkDownItemProps[] = await res.json();
|
||||
return {
|
||||
props: { files: files }
|
||||
};
|
||||
}
|
||||
const { message } = await res.json();
|
||||
|
||||
return {
|
||||
error: new Error(message)
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import DocRender from '$lib/DocRender/index.svelte';
|
||||
import { getContext } from 'svelte';
|
||||
import type { Writable } from 'svelte/store';
|
||||
export let files: MarkDownItemProps[];
|
||||
const lang = getContext<Writable<string>>('lang');
|
||||
let sections = new Map<string, MarkDownItemProps[]>();
|
||||
lang.subscribe((la) => {
|
||||
sections = new Map<string, MarkDownItemProps[]>();
|
||||
let tmp = files.filter((v) => {
|
||||
const name = v.file;
|
||||
const sp = name.split('.');
|
||||
if (Array.isArray(sp) && sp.length > 1 && sp[sp.length - 2] === 'EN') {
|
||||
return la === 'en';
|
||||
} else {
|
||||
return la === 'cn';
|
||||
}
|
||||
});
|
||||
tmp.forEach((v) => {
|
||||
const stitle = v.sTitle || 'default';
|
||||
const value = sections.get(stitle);
|
||||
if (value) {
|
||||
value.push(v);
|
||||
} else {
|
||||
sections.set(stitle, [v]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
let active = '';
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>API Docs • Svelte</title>
|
||||
<meta name="twitter:title" content="Svelte API docs" />
|
||||
<meta name="twitter:description" content="Cybernetically enhanced web apps" />
|
||||
<meta name="Description" content="Cybernetically enhanced web apps" />
|
||||
</svelte:head>
|
||||
|
||||
<DocRender {active} {sections} />
|
||||
|
||||
<style lang="scss">
|
||||
</style>
|
||||
126
packages/dooringx-doc/src/routes/docs/_api.ts
Normal file
126
packages/dooringx-doc/src/routes/docs/_api.ts
Normal file
@@ -0,0 +1,126 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import marked from 'marked';
|
||||
import { extract_frontmatter } from '../../utils/markdown';
|
||||
import { highlight } from '../../utils/highlight';
|
||||
import slugf from 'slug';
|
||||
|
||||
export interface MarkDownItemProps {
|
||||
html: string;
|
||||
metadata: Record<string, string>;
|
||||
subsections: { slug: string; title: string; level: string }[];
|
||||
slug: string;
|
||||
file: string;
|
||||
order: number;
|
||||
sTitle: string;
|
||||
}
|
||||
|
||||
export async function api(name: string) {
|
||||
const res = await getMarkDown(name);
|
||||
return {
|
||||
status: 200,
|
||||
body: res,
|
||||
};
|
||||
}
|
||||
|
||||
const blockTypes = [
|
||||
'blockquote',
|
||||
'html',
|
||||
'heading',
|
||||
'hr',
|
||||
'list',
|
||||
'listitem',
|
||||
'paragraph',
|
||||
'table',
|
||||
'tablerow',
|
||||
'tablecell',
|
||||
];
|
||||
|
||||
export const getMarkDown = (name: string) => {
|
||||
const root = process.cwd();
|
||||
const docPath = path.resolve(root, 'src', name);
|
||||
return fs
|
||||
.readdirSync(docPath)
|
||||
.filter((file) => file[0] !== '.' && path.extname(file) === '.md')
|
||||
.map((file) => {
|
||||
const currentFilePath = path.resolve(docPath, file);
|
||||
const markdown = fs.readFileSync(currentFilePath, 'utf-8');
|
||||
const { content, metadata } = extract_frontmatter(markdown);
|
||||
const order = parseFloat(metadata.order);
|
||||
const sTitle = metadata.sTitle;
|
||||
const subsections = [];
|
||||
const section_slug = slugf(metadata.title, '_');
|
||||
const renderer = new marked.Renderer();
|
||||
let block_open = false;
|
||||
|
||||
renderer.hr = () => {
|
||||
block_open = true;
|
||||
|
||||
return '<div class="side-by-side"><div class="copy">';
|
||||
};
|
||||
|
||||
renderer.code = (source, lang) => {
|
||||
source = source.replace(/^ +/gm, (match) => match.split(' ').join('\t'));
|
||||
let prefix = '';
|
||||
let className = 'code-block';
|
||||
const html = `<div class='${className}'>${prefix}${highlight(source, lang)}</div>`;
|
||||
if (block_open) {
|
||||
block_open = false;
|
||||
return `</div><div class="code">${html}</div></div>`;
|
||||
}
|
||||
|
||||
return html;
|
||||
};
|
||||
// 这个heading是md的标题
|
||||
renderer.heading = (text, level, rawtext) => {
|
||||
let slug;
|
||||
const match = /<a href="([^"]+)"[^>]*>(.+)<\/a>/.exec(text); // 提取a标签,链接为slug
|
||||
if (match) {
|
||||
slug = match[1];
|
||||
text = match[2];
|
||||
} else {
|
||||
slug = slugf(rawtext, '_');
|
||||
}
|
||||
|
||||
if (level === 1 || level === 2 || level === 3 || level === 4) {
|
||||
const title = text
|
||||
.replace(/<\/?code>/g, '')
|
||||
.replace(/\.(\w+)(\((.+)?\))?/, (m, $1, $2, $3) => {
|
||||
if ($3) return `.${$1}(...)`;
|
||||
if ($2) return `.${$1}()`;
|
||||
return `.${$1}`;
|
||||
});
|
||||
|
||||
subsections.push({ slug, title, level });
|
||||
}
|
||||
|
||||
return `
|
||||
<h${level + 1}>
|
||||
<span id="${slug}" ></span>
|
||||
<a href="docs#${slug}" class="anchor" aria-hidden="true"></a>
|
||||
${text}
|
||||
</h${level + 1}>`;
|
||||
};
|
||||
|
||||
blockTypes.forEach((type) => {
|
||||
const fn = renderer[type];
|
||||
renderer[type] = function () {
|
||||
return fn.apply(this, arguments);
|
||||
};
|
||||
});
|
||||
|
||||
const html = marked(content, { renderer });
|
||||
|
||||
const hashes = {};
|
||||
return {
|
||||
html: html.replace(/@@(\d+)/g, (m, id) => hashes[id] || m),
|
||||
metadata,
|
||||
subsections,
|
||||
slug: section_slug,
|
||||
order,
|
||||
file,
|
||||
sTitle,
|
||||
};
|
||||
})
|
||||
.sort((a, b) => a.order - b.order);
|
||||
};
|
||||
16
packages/dooringx-doc/src/routes/docs/index.json.ts
Normal file
16
packages/dooringx-doc/src/routes/docs/index.json.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-06-30 16:57:15
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-06 14:53:18
|
||||
* @FilePath: \my-app\src\routes\docs\index.json.ts
|
||||
*/
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { api } from './_api';
|
||||
|
||||
export const get: RequestHandler = async (request) => {
|
||||
const param = request.query;
|
||||
const name = param.get('name');
|
||||
const response = await api(name);
|
||||
return response;
|
||||
};
|
||||
64
packages/dooringx-doc/src/routes/docs/index.svelte
Normal file
64
packages/dooringx-doc/src/routes/docs/index.svelte
Normal file
@@ -0,0 +1,64 @@
|
||||
<script context="module" lang="ts">
|
||||
import type { Load } from '@sveltejs/kit';
|
||||
import type { MarkDownItemProps } from './_api';
|
||||
import { base } from '$app/paths';
|
||||
export const load: Load = async ({ fetch }) => {
|
||||
const path = `${base}/docs.json?name=docs`;
|
||||
const res = await fetch(path);
|
||||
if (res.ok) {
|
||||
const files: MarkDownItemProps[] = await res.json();
|
||||
return {
|
||||
props: { files: files }
|
||||
};
|
||||
}
|
||||
|
||||
const { message } = await res.json();
|
||||
|
||||
return {
|
||||
error: new Error(message)
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import DocRender from '$lib/DocRender/index.svelte';
|
||||
import { getContext } from 'svelte';
|
||||
import type { Writable } from 'svelte/store';
|
||||
export let files: MarkDownItemProps[];
|
||||
const lang = getContext<Writable<string>>('lang');
|
||||
let sections = new Map<string, MarkDownItemProps[]>();
|
||||
lang.subscribe((la) => {
|
||||
sections = new Map<string, MarkDownItemProps[]>();
|
||||
let tmp = files.filter((v) => {
|
||||
const name = v.file;
|
||||
const sp = name.split('.');
|
||||
if (Array.isArray(sp) && sp.length > 1 && sp[sp.length - 2] === 'EN') {
|
||||
return la === 'en';
|
||||
} else {
|
||||
return la === 'cn';
|
||||
}
|
||||
});
|
||||
tmp.forEach((v) => {
|
||||
const stitle = v.sTitle || 'default';
|
||||
const value = sections.get(stitle);
|
||||
if (value) {
|
||||
value.push(v);
|
||||
} else {
|
||||
sections.set(stitle, [v]);
|
||||
}
|
||||
});
|
||||
});
|
||||
let active = '';
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>API Docs • Svelte</title>
|
||||
<meta name="twitter:title" content="Svelte API docs" />
|
||||
<meta name="twitter:description" content="Cybernetically enhanced web apps" />
|
||||
<meta name="Description" content="Cybernetically enhanced web apps" />
|
||||
</svelte:head>
|
||||
|
||||
<DocRender {active} {sections} />
|
||||
|
||||
<style lang="scss">
|
||||
</style>
|
||||
50
packages/dooringx-doc/src/routes/index.svelte
Normal file
50
packages/dooringx-doc/src/routes/index.svelte
Normal file
@@ -0,0 +1,50 @@
|
||||
<script context="module" lang="ts">
|
||||
export const prerender = true;
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Home</title>
|
||||
</svelte:head>
|
||||
|
||||
<section>
|
||||
<h1>
|
||||
<div class="welcome">
|
||||
<picture>
|
||||
<source srcset="svelte-welcome.webp" type="image/webp" />
|
||||
<img src="svelte-welcome.png" alt="Welcome" />
|
||||
</picture>
|
||||
</div>
|
||||
</h1>
|
||||
</section>
|
||||
|
||||
<style>
|
||||
section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
h1 {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.welcome {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 0;
|
||||
padding: 0 0 calc(100% * 495 / 2048) 0;
|
||||
}
|
||||
|
||||
.welcome img {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
21
packages/dooringx-doc/src/utils/highlight.ts
Normal file
21
packages/dooringx-doc/src/utils/highlight.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-06-30 19:20:22
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-06-30 21:22:04
|
||||
* @FilePath: \my-app\src\utils\highlight.ts
|
||||
*/
|
||||
import { langs } from './markdown';
|
||||
import PrismJS from 'prismjs';
|
||||
import 'prismjs/components/prism-bash.js';
|
||||
import 'prismjs/components/prism-diff.js';
|
||||
import 'prism-svelte';
|
||||
|
||||
export function highlight(source, lang) {
|
||||
const plang = langs[lang] || '';
|
||||
const highlighted = plang
|
||||
? PrismJS.highlight(source, PrismJS.languages[plang], lang)
|
||||
: source.replace(/[&<>]/g, (c) => ({ '&': '&', '<': '<', '>': '>' }[c]));
|
||||
|
||||
return `<pre class='language-${plang}'><code>${highlighted}</code></pre>`;
|
||||
}
|
||||
56
packages/dooringx-doc/src/utils/markdown.ts
Normal file
56
packages/dooringx-doc/src/utils/markdown.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-06-30 19:09:39
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-06 14:19:17
|
||||
* @FilePath: \my-app\src\utils\markdown.ts
|
||||
*/
|
||||
export function extract_frontmatter(markdown) {
|
||||
const match = /---\r?\n([\s\S]+?)\r?\n---/.exec(markdown);
|
||||
let content = '';
|
||||
let metadata: Record<string, string> = {};
|
||||
if (match) {
|
||||
const frontMatter = match[1];
|
||||
content = markdown.slice(match[0].length);
|
||||
|
||||
metadata = {};
|
||||
frontMatter.split('\n').forEach((pair) => {
|
||||
// split on the colon
|
||||
const colonIndex = pair.indexOf(':');
|
||||
let value = pair.slice(colonIndex + 1).trim();
|
||||
// if surrounded by double quotes then remove those quotes
|
||||
if (value && value.charAt(0) === '"' && value.charAt(value.length - 1) === '"') {
|
||||
value = value.substring(1, value.length - 1);
|
||||
}
|
||||
metadata[pair.slice(0, colonIndex).trim()] = value;
|
||||
});
|
||||
}
|
||||
|
||||
return { metadata, content };
|
||||
}
|
||||
|
||||
// map lang to prism-language-attr
|
||||
export const langs = {
|
||||
bash: 'bash',
|
||||
html: 'markup',
|
||||
sv: 'svelte',
|
||||
js: 'javascript',
|
||||
css: 'css',
|
||||
diff: 'diff',
|
||||
};
|
||||
|
||||
// links renderer
|
||||
export function link_renderer(href, title, text) {
|
||||
let target_attr = '';
|
||||
let title_attr = '';
|
||||
|
||||
if (href.startsWith('http')) {
|
||||
target_attr = ' target="_blank"';
|
||||
}
|
||||
|
||||
if (title !== null) {
|
||||
title_attr = ` title="${title}"`;
|
||||
}
|
||||
|
||||
return `<a href="${href}"${target_attr}${title_attr} rel="noopener noreferrer">${text}</a>`;
|
||||
}
|
||||
0
packages/dooringx-doc/static/.nojekyll
Normal file
0
packages/dooringx-doc/static/.nojekyll
Normal file
BIN
packages/dooringx-doc/static/favicon.png
Normal file
BIN
packages/dooringx-doc/static/favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
3
packages/dooringx-doc/static/robots.txt
Normal file
3
packages/dooringx-doc/static/robots.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
||||
BIN
packages/dooringx-doc/static/svelte-welcome.png
Normal file
BIN
packages/dooringx-doc/static/svelte-welcome.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 352 KiB |
BIN
packages/dooringx-doc/static/svelte-welcome.webp
Normal file
BIN
packages/dooringx-doc/static/svelte-welcome.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 113 KiB |
32
packages/dooringx-doc/svelte.config.js
Normal file
32
packages/dooringx-doc/svelte.config.js
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-06-29 11:14:15
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-08 11:29:23
|
||||
* @FilePath: \my-app\svelte.config.js
|
||||
*/
|
||||
import preprocess from 'svelte-preprocess';
|
||||
import staticAdapter from '@sveltejs/adapter-static';
|
||||
|
||||
/** @type {import('@sveltejs/kit').Config} */
|
||||
const config = {
|
||||
// Consult https://github.com/sveltejs/svelte-preprocess
|
||||
// for more information about preprocessors
|
||||
preprocess: preprocess(),
|
||||
kit: {
|
||||
// hydrate the <div id="svelte"> element in src/app.html
|
||||
target: '#svelte',
|
||||
adapter: staticAdapter({
|
||||
pages: 'build',
|
||||
assets: 'build',
|
||||
}),
|
||||
paths: process.env.DEPLOY
|
||||
? {
|
||||
base: '/svelte-kit-markdown',
|
||||
}
|
||||
: {},
|
||||
trailingSlash: 'ignore',
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
30
packages/dooringx-doc/tsconfig.json
Normal file
30
packages/dooringx-doc/tsconfig.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"moduleResolution": "node",
|
||||
"module": "es2020",
|
||||
"lib": ["es2020"],
|
||||
"target": "es2019",
|
||||
/**
|
||||
svelte-preprocess cannot figure out whether you have a value or a type, so tell TypeScript
|
||||
to enforce using \`import type\` instead of \`import\` for Types.
|
||||
*/
|
||||
"importsNotUsedAsValues": "error",
|
||||
"isolatedModules": true,
|
||||
"resolveJsonModule": true,
|
||||
/**
|
||||
To have warnings/errors of the Svelte compiler at the correct position,
|
||||
enable source maps by default.
|
||||
*/
|
||||
"sourceMap": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"baseUrl": ".",
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"paths": {
|
||||
"$lib/*": ["src/lib/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.ts", "src/**/*.svelte"]
|
||||
}
|
||||
18
packages/dooringx-lib/.babelrc
Normal file
18
packages/dooringx-lib/.babelrc
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"plugins": [
|
||||
[
|
||||
"import-v2",
|
||||
{
|
||||
"libraryName": "@ant-design",
|
||||
"libraryDirectory": "icons",
|
||||
"camel2DashComponentName": false,
|
||||
"style": false
|
||||
}
|
||||
],
|
||||
[
|
||||
"import",
|
||||
{ "libraryName": "antd", "libraryDirectory": "lib", "style": true },
|
||||
"antd"
|
||||
]
|
||||
]
|
||||
}
|
||||
10
packages/dooringx-lib/.eslintrc.js
Normal file
10
packages/dooringx-lib/.eslintrc.js
Normal file
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 05:00:20
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-09 01:34:14
|
||||
* @FilePath: \dooringx\packages\dooringx-lib\.eslintrc.js
|
||||
*/
|
||||
module.exports = {
|
||||
extends: ['react-app'],
|
||||
};
|
||||
21
packages/dooringx-lib/LICENSE
Normal file
21
packages/dooringx-lib/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 yehuozhili
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
16
packages/dooringx-lib/README.md
Normal file
16
packages/dooringx-lib/README.md
Normal file
@@ -0,0 +1,16 @@
|
||||
<!--
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-01-31 20:44:16
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-08 20:44:20
|
||||
* @FilePath: \DooringV2\packages\dooringx-lib\README.md
|
||||
-->
|
||||
|
||||
## DooringX 核心包
|
||||
|
||||
## changelog
|
||||
|
||||
- 0.1.7 修改预览特殊条件显示,删除 console
|
||||
- 0.1.6 调整初始缩放,画布初始比例,增加回正画布功能。
|
||||
- 0.1.5 删除未作按钮,增加 fixed 配置
|
||||
- 0.1.4 基础功能
|
||||
52
packages/dooringx-lib/package.json
Normal file
52
packages/dooringx-lib/package.json
Normal file
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"version": "0.1.7",
|
||||
"license": "MIT",
|
||||
"main": "dist/index.js",
|
||||
"module": "dist/dooringx-lib.esm.js",
|
||||
"browser": "dist/dooringx-lib.esm.js",
|
||||
"typings": "dist/index.d.ts",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "tsdx watch --noclean ",
|
||||
"build": "tsdx build ",
|
||||
"test": "tsdx test --passWithNoTests",
|
||||
"lint": "tsdx lint",
|
||||
"prepare": "tsdx build"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8",
|
||||
"antd": ">=4"
|
||||
},
|
||||
"name": "dooringx-lib",
|
||||
"author": "yehuozhili",
|
||||
"devDependencies": {
|
||||
"@ant-design/icons": "^4.6.2",
|
||||
"antd": "^4.15.2",
|
||||
"postcss-modules": "^4.0.0",
|
||||
"babel-plugin-import": "^1.13.3",
|
||||
"babel-plugin-import-v2": "^1.0.0",
|
||||
"@types/react-color": "^3.0.4",
|
||||
"@types/react": "^17.0.0",
|
||||
"@types/react-dom": "^17.0.0",
|
||||
"size-limit": "^4.10.1",
|
||||
"tsdx": "^0.14.1",
|
||||
"react": "17.x",
|
||||
"react-dom": "17.x",
|
||||
"postcss": "^8.2.8",
|
||||
"less": "^4.1.1",
|
||||
"rollup-plugin-postcss": "^4.0.0",
|
||||
"@types/uuid": "^8.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"deepcopy": "^2.1.0",
|
||||
"react-color": "^2.19.3",
|
||||
"react-sortable-hoc": "^2.0.0",
|
||||
"uuid": "^8.3.2",
|
||||
"animate.css": "^4.1.1"
|
||||
}
|
||||
}
|
||||
201
packages/dooringx-lib/src/components/blocks.tsx
Normal file
201
packages/dooringx-lib/src/components/blocks.tsx
Normal file
@@ -0,0 +1,201 @@
|
||||
import { IBlockType } from '../core/store/storetype';
|
||||
import { CSSProperties, PropsWithChildren, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { innerDrag } from '../core/innerDrag';
|
||||
import { BlockResizer } from '../core/resizeHandler';
|
||||
import { contextMenuEvent } from '../core/contextMenu';
|
||||
import React from 'react';
|
||||
import { transfer } from '../core/transfer';
|
||||
import { UserConfig } from '../config';
|
||||
import styles from '../index.less';
|
||||
interface BlockProps {
|
||||
data: IBlockType;
|
||||
context: 'edit' | 'preview';
|
||||
config: UserConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 用来从component里拿到渲染进行渲染,由于异步拉代码,所以需要等待代码拉取完毕
|
||||
* @param {*} props
|
||||
* @returns
|
||||
*/
|
||||
function Blocks(props: PropsWithChildren<BlockProps>) {
|
||||
const [state, setState] = useState<JSX.Element | null>(null);
|
||||
|
||||
const [previewState, setPreviewState] = useState({
|
||||
top: props.data.top,
|
||||
left: props.data.left,
|
||||
height: props.data.height,
|
||||
width: props.data.width,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const fn = () => props.config.getComponentRegister().getComp(props.data.name);
|
||||
const data = fn();
|
||||
let unregist = () => {};
|
||||
let newdata = { ...props.data };
|
||||
if (props.context === 'preview') {
|
||||
newdata = {
|
||||
...props.data,
|
||||
top: previewState.top,
|
||||
left: previewState.left,
|
||||
height: previewState.height,
|
||||
width: previewState.width,
|
||||
};
|
||||
}
|
||||
|
||||
if (data) {
|
||||
setState(data.render(newdata, props.context, props.config.getStore(), props.config));
|
||||
} else {
|
||||
const callback = () => {
|
||||
const tmp = fn();
|
||||
setState(tmp.render(newdata, props.context, props.config.getStore(), props.config));
|
||||
unregist();
|
||||
};
|
||||
unregist = props.config.getComponentRegister().on(props.data.name, callback);
|
||||
}
|
||||
return () => {
|
||||
unregist();
|
||||
};
|
||||
}, [props.data, props.context, props.config, previewState]);
|
||||
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const innerDragData = useMemo(() => {
|
||||
return { ...innerDrag(props.data, ref) };
|
||||
}, [props.data]);
|
||||
|
||||
useEffect(() => {
|
||||
const fn = () => {
|
||||
const { top, left, width, height } = transfer(
|
||||
props.data.top,
|
||||
props.data.left,
|
||||
props.data.height,
|
||||
props.data.width,
|
||||
props.data.fixed
|
||||
);
|
||||
|
||||
setPreviewState({ top, left, width, height });
|
||||
};
|
||||
fn();
|
||||
window.addEventListener('resize', fn);
|
||||
return () => {
|
||||
window.removeEventListener('resize', fn);
|
||||
};
|
||||
}, [
|
||||
previewState.height,
|
||||
previewState.left,
|
||||
previewState.top,
|
||||
previewState.width,
|
||||
props.data.height,
|
||||
props.data.left,
|
||||
props.data.top,
|
||||
props.data.width,
|
||||
props.data.fixed,
|
||||
]);
|
||||
|
||||
const animatecss = useMemo(() => {
|
||||
const animate = props.data.animate;
|
||||
if (Object.keys(animate).length > 0) {
|
||||
return `animate__animated ${animate.animate ?? ''} ${animate.delay ?? ''} ${
|
||||
animate.speed ?? ''
|
||||
}`;
|
||||
}
|
||||
return '';
|
||||
}, [
|
||||
props.data.animate.animate,
|
||||
props.data.animate.delay,
|
||||
// props.data.animate.duration,
|
||||
props.data.animate.speed,
|
||||
]);
|
||||
const animateCount = useMemo(() => {
|
||||
const animate = props.data.animate;
|
||||
|
||||
if (Object.keys(animate).length > 0) {
|
||||
return { animationIterationCount: animate.animationIterationCount };
|
||||
}
|
||||
return { animationIterationCount: 1 };
|
||||
}, [props.data.animate.animationIterationCount]);
|
||||
|
||||
const render = useMemo(() => {
|
||||
// 如果是编辑模式下,则需要包裹不能选中层,位移层,缩放控制层,平面移动层。
|
||||
if (state && props.context === 'edit') {
|
||||
const style: CSSProperties = props.data.canDrag ? { pointerEvents: 'none' } : {};
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={
|
||||
props.data.focus && props.data.position !== 'static' ? styles.yh_block_focus : ''
|
||||
}
|
||||
style={{
|
||||
position: props.data.position,
|
||||
top: props.data.top,
|
||||
left: props.data.left,
|
||||
width: props.data.width,
|
||||
height: props.data.height,
|
||||
zIndex: props.data.zIndex,
|
||||
display: props.data.display,
|
||||
}}
|
||||
{...innerDragData}
|
||||
onContextMenu={(e) => {
|
||||
if (props.data.name !== 'modalMask') {
|
||||
contextMenuEvent(e, ref);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{props.data.position !== 'static' && (
|
||||
<div className={animatecss} style={{ ...style, ...animateCount }}>
|
||||
{state}
|
||||
</div>
|
||||
)}
|
||||
{/* 这里暂不考虑布局影响 */}
|
||||
{props.data.position === 'static' && props.data.display !== 'inline' && (
|
||||
<div
|
||||
className={animatecss}
|
||||
style={{
|
||||
pointerEvents: 'none',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
...animateCount,
|
||||
}}
|
||||
>
|
||||
{state}
|
||||
</div>
|
||||
)}
|
||||
{props.data.position === 'static' && props.data.display === 'inline' && (
|
||||
<span style={{ pointerEvents: 'none' }}>{state}</span>
|
||||
)}
|
||||
<BlockResizer data={props.data} rect={ref}></BlockResizer>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div
|
||||
className={animatecss}
|
||||
style={{
|
||||
position: props.data.fixed ? 'fixed' : props.data.position,
|
||||
top: previewState.top,
|
||||
left: previewState.left,
|
||||
width: previewState.width,
|
||||
height: previewState.height,
|
||||
zIndex: props.data.zIndex,
|
||||
display: props.data.display,
|
||||
...animateCount,
|
||||
}}
|
||||
>
|
||||
{state}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}, [
|
||||
state,
|
||||
props.context,
|
||||
props.data,
|
||||
innerDragData,
|
||||
previewState.top,
|
||||
previewState.left,
|
||||
previewState.width,
|
||||
previewState.height,
|
||||
]);
|
||||
return render;
|
||||
}
|
||||
export default Blocks;
|
||||
131
packages/dooringx-lib/src/components/container.tsx
Normal file
131
packages/dooringx-lib/src/components/container.tsx
Normal file
@@ -0,0 +1,131 @@
|
||||
import { containerDragResolve } from '../core/crossDrag';
|
||||
import { containerFocusRemove } from '../core/focusHandler';
|
||||
import { innerContainerDrag } from '../core/innerDrag';
|
||||
import { NormalMarkLineRender } from '../core/markline';
|
||||
import { scaleState } from '../core/scale/state';
|
||||
import { IStoreData } from '../core/store/storetype';
|
||||
import { wrapperMoveState } from './wrapperMove/event';
|
||||
import { CSSProperties, PropsWithChildren, useMemo } from 'react';
|
||||
import Blocks from './blocks';
|
||||
import { containerResizer } from '../core/resizeHandler/containerResizer';
|
||||
import React from 'react';
|
||||
import UserConfig from '../config';
|
||||
import styles from '../index.less';
|
||||
import { getRealHeight } from '../core/transfer';
|
||||
import { IconFont } from '../core/utils/icon';
|
||||
interface ContainerProps {
|
||||
state: IStoreData;
|
||||
context: 'edit' | 'preview';
|
||||
config: UserConfig;
|
||||
editContainerStyle?: CSSProperties;
|
||||
previewContainerStyle?: CSSProperties;
|
||||
}
|
||||
function Container(props: PropsWithChildren<ContainerProps>) {
|
||||
const { editContainerStyle, previewContainerStyle } = props;
|
||||
|
||||
const transform = useMemo(() => {
|
||||
if (props.context === 'edit') {
|
||||
return `scale(${scaleState.value}) translate(${wrapperMoveState.needX}px, ${wrapperMoveState.needY}px)`;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}, [props.context]);
|
||||
|
||||
const bgColor = () => {
|
||||
const isEdit = props.config.getStoreChanger().isEdit();
|
||||
if (isEdit) {
|
||||
return 'rgba(255,255,255,1)';
|
||||
} else {
|
||||
return props.state.globalState.containerColor;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{props.context === 'edit' && (
|
||||
<>
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
height: `${props.state.container.height + 60}px`,
|
||||
width: `${props.state.container.width}px`,
|
||||
transform: `scale(${scaleState.value}) translate(${wrapperMoveState.needX}px, ${wrapperMoveState.needY}px)`,
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex' }}>
|
||||
<div
|
||||
id="yh-container"
|
||||
className={styles.yh_container}
|
||||
style={{
|
||||
height: `${props.state.container.height}px`,
|
||||
width: `${props.state.container.width}px`,
|
||||
backgroundColor: bgColor(),
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
...editContainerStyle,
|
||||
}}
|
||||
{...(props.context === 'edit' ? containerDragResolve : null)}
|
||||
{...(props.context === 'edit' ? innerContainerDrag() : null)}
|
||||
{...(props.context === 'edit' ? containerFocusRemove() : null)}
|
||||
>
|
||||
{props.context === 'edit' && <NormalMarkLineRender></NormalMarkLineRender>}
|
||||
{props.state.block.map((v) => {
|
||||
return (
|
||||
<Blocks
|
||||
config={props.config}
|
||||
key={v.id}
|
||||
data={v}
|
||||
context={props.context}
|
||||
></Blocks>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
height: '50px',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
width: `${props.state.container.width}px`,
|
||||
}}
|
||||
>
|
||||
<IconFont
|
||||
type="icon-suofang"
|
||||
onMouseDown={containerResizer.onMousedown}
|
||||
style={{ fontSize: '20px', cursor: 's-resize' }}
|
||||
></IconFont>
|
||||
{/* <BoxPlotFilled
|
||||
onMouseDown={containerResizer.onMousedown}
|
||||
style={{ fontSize: '20px', cursor: 's-resize' }}
|
||||
rotate={90}
|
||||
/> */}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{props.context === 'preview' && (
|
||||
<div
|
||||
id="yh-container-preview"
|
||||
className={styles.yh_container_preview}
|
||||
style={{
|
||||
height: `${getRealHeight(props.state.container.height)}px`,
|
||||
width: `100%`,
|
||||
position: 'relative' as 'absolute' | 'relative',
|
||||
overflow: 'hidden',
|
||||
backgroundColor: bgColor(),
|
||||
transform: transform,
|
||||
...previewContainerStyle,
|
||||
}}
|
||||
>
|
||||
{props.state.block.map((v) => {
|
||||
return (
|
||||
<Blocks key={v.id} config={props.config} data={v} context={props.context}></Blocks>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
export default Container;
|
||||
281
packages/dooringx-lib/src/components/control.tsx
Normal file
281
packages/dooringx-lib/src/components/control.tsx
Normal file
@@ -0,0 +1,281 @@
|
||||
import {
|
||||
CompressOutlined,
|
||||
DeleteOutlined,
|
||||
FullscreenExitOutlined,
|
||||
FullscreenOutlined,
|
||||
GatewayOutlined,
|
||||
MenuOutlined,
|
||||
SyncOutlined,
|
||||
UnorderedListOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { Button, Divider, Form, Input, List, Modal, Popconfirm, Popover } from 'antd';
|
||||
import React, { CSSProperties, PropsWithChildren, useState } from 'react';
|
||||
import { UserConfig } from '..';
|
||||
import { IBlockType, IStoreData } from '../core/store/storetype';
|
||||
import { deepCopy, arrayMove, changeItem, changeLayer, focusEle } from '../core/utils';
|
||||
|
||||
import { SortEnd, SortableContainer, SortableElement, SortableHandle } from 'react-sortable-hoc';
|
||||
import { wrapperMoveState } from './wrapperMove/event';
|
||||
export interface ControlProps {
|
||||
config: UserConfig;
|
||||
style?: CSSProperties;
|
||||
}
|
||||
|
||||
const DragHandle = SortableHandle(() => <MenuOutlined />);
|
||||
const SortableItem = SortableElement(
|
||||
({ value }: { value: { value: IBlockType; config: UserConfig } }) => (
|
||||
<div
|
||||
style={{
|
||||
userSelect: 'none',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
width: 430,
|
||||
}}
|
||||
>
|
||||
<div style={{ width: 30, textAlign: 'center', cursor: 'move' }}>
|
||||
<DragHandle></DragHandle>
|
||||
</div>
|
||||
<Divider type="vertical"></Divider>
|
||||
<div style={{ width: 100, textAlign: 'center' }}>
|
||||
{value.config.getComponentRegister().getMap()[value.value.name].display}
|
||||
</div>
|
||||
<Divider type="vertical"></Divider>
|
||||
<div style={{ width: 50, textAlign: 'center' }}>{value.value.id.slice(-6)}</div>
|
||||
<Divider type="vertical"></Divider>
|
||||
<div style={{ width: 50, textAlign: 'center' }}>{value.value.position}</div>
|
||||
<Divider type="vertical"></Divider>
|
||||
<div style={{ width: 200 }}>
|
||||
<Popconfirm
|
||||
title="确认变更为绝对定位吗?"
|
||||
onConfirm={() => {
|
||||
changeItem(value.config.getStore(), value.value.id, 'position', 'absolute');
|
||||
}}
|
||||
>
|
||||
<Button type="link" title="切换绝对定位" icon={<FullscreenOutlined />}></Button>
|
||||
</Popconfirm>
|
||||
<Popconfirm
|
||||
title="确认变更为静态定位吗?"
|
||||
onConfirm={() => {
|
||||
changeItem(value.config.getStore(), value.value.id, 'position', 'static');
|
||||
}}
|
||||
>
|
||||
<Button type="link" title="切换静态定位" icon={<FullscreenExitOutlined />}></Button>
|
||||
</Popconfirm>
|
||||
<Button
|
||||
type="link"
|
||||
title="选中聚焦"
|
||||
icon={<CompressOutlined />}
|
||||
onClick={() => {
|
||||
focusEle(value.config.getStore(), value.value.id);
|
||||
}}
|
||||
></Button>
|
||||
<Popconfirm
|
||||
title="确认删除操作吗?"
|
||||
onConfirm={() => {
|
||||
changeLayer(value.config.getStore(), value.value.id, 'delete');
|
||||
}}
|
||||
>
|
||||
<Button icon={<DeleteOutlined />} title="删除" type="link"></Button>
|
||||
</Popconfirm>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
const SortableList = SortableContainer(
|
||||
({ items }: { items: { data: IBlockType[]; config: UserConfig } }) => {
|
||||
return (
|
||||
<div>
|
||||
{items.data.map((value, index: number) => (
|
||||
<SortableItem key={value.id} index={index} value={{ value, config: items.config }} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export function Control(props: PropsWithChildren<ControlProps>) {
|
||||
const { style } = props;
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [configVisible, setConfigVisible] = useState(false);
|
||||
const [form] = Form.useForm();
|
||||
|
||||
const data = props.config.getStore().getData().block;
|
||||
|
||||
const onSortEnd = (sort: SortEnd) => {
|
||||
const { oldIndex, newIndex } = sort;
|
||||
const newblocks: IBlockType[] = arrayMove(data, oldIndex, newIndex);
|
||||
// 这里要判断是否edit ,如果edit时,只要看第一个是不是container,不是则不移动
|
||||
const isEdit = props.config.getStoreChanger().isEdit();
|
||||
if (isEdit) {
|
||||
const firstType = newblocks[0].name;
|
||||
if (firstType !== 'modalMask') {
|
||||
return;
|
||||
}
|
||||
}
|
||||
const store = props.config.getStore();
|
||||
const cloneData: IStoreData = deepCopy(store.getData());
|
||||
cloneData.block = newblocks;
|
||||
store.setData(cloneData);
|
||||
};
|
||||
|
||||
const content =
|
||||
data.length === 0 ? (
|
||||
<div>暂时没有组件</div>
|
||||
) : (
|
||||
<div style={{ maxHeight: 300, overflow: 'auto' }}>
|
||||
<SortableList
|
||||
distance={2}
|
||||
useDragHandle
|
||||
items={{
|
||||
data,
|
||||
config: props.config,
|
||||
}}
|
||||
onSortEnd={onSortEnd}
|
||||
axis="y"
|
||||
></SortableList>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className="ant-menu"
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
...style,
|
||||
}}
|
||||
>
|
||||
<Popover style={{ minWidth: '208px' }} content={content} trigger="click">
|
||||
<Button icon={<UnorderedListOutlined />}></Button>
|
||||
</Popover>
|
||||
|
||||
{/* <Button icon={<FolderOpenOutlined />}></Button> */}
|
||||
<Popover
|
||||
placement="left"
|
||||
content={
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
flexDirection: 'column',
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setVisible(true);
|
||||
}}
|
||||
>
|
||||
新建弹窗
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setConfigVisible(true);
|
||||
}}
|
||||
>
|
||||
弹窗配置
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Button icon={<GatewayOutlined />}></Button>
|
||||
</Popover>
|
||||
|
||||
<Button
|
||||
icon={<SyncOutlined />}
|
||||
onClick={() => {
|
||||
wrapperMoveState.needX = 0;
|
||||
wrapperMoveState.needY = 0;
|
||||
props.config.getStore().forceUpdate();
|
||||
}}
|
||||
></Button>
|
||||
</div>
|
||||
|
||||
<Modal
|
||||
title="弹窗配置"
|
||||
visible={configVisible}
|
||||
onOk={() => setConfigVisible(false)}
|
||||
onCancel={() => setConfigVisible(false)}
|
||||
footer={null}
|
||||
>
|
||||
<List>
|
||||
{props.config.getStoreChanger().getState().modalEditName !== '' && (
|
||||
<div>请退出编辑弹窗后再打开该配置</div>
|
||||
)}
|
||||
{props.config.getStoreChanger().getState().modalEditName === '' &&
|
||||
Object.keys(props.config.getStore().getData().modalMap).map((v) => {
|
||||
return (
|
||||
<List.Item
|
||||
key={v}
|
||||
actions={[
|
||||
<Popconfirm
|
||||
title="是否切换至该弹窗并进行编辑?"
|
||||
onConfirm={() => {
|
||||
props.config.getStoreChanger().updateModal(props.config.getStore(), v);
|
||||
setConfigVisible(false);
|
||||
}}
|
||||
okText={'是'}
|
||||
cancelText={'否'}
|
||||
>
|
||||
<Button type="link">修改</Button>
|
||||
</Popconfirm>,
|
||||
|
||||
<Popconfirm
|
||||
title="您确定要删除这个弹窗吗?"
|
||||
onConfirm={() => {
|
||||
props.config.getStoreChanger().removeModal(props.config.getStore(), v);
|
||||
setConfigVisible(false);
|
||||
}}
|
||||
okText={'是'}
|
||||
cancelText={'否'}
|
||||
>
|
||||
<Button type="link">删除</Button>
|
||||
</Popconfirm>,
|
||||
]}
|
||||
>
|
||||
{v}
|
||||
</List.Item>
|
||||
);
|
||||
})}
|
||||
{props.config.getStoreChanger().getState().modalEditName === '' &&
|
||||
Object.keys(props.config.getStore().getData().modalMap).length === 0 && (
|
||||
<div style={{ textAlign: 'center' }}>暂时没有弹窗</div>
|
||||
)}
|
||||
</List>
|
||||
</Modal>
|
||||
<Modal
|
||||
onOk={() => {
|
||||
form
|
||||
.validateFields()
|
||||
.then((values) => {
|
||||
form.resetFields();
|
||||
const modalName = values.modalName;
|
||||
props.config.getStoreChanger().newModalMap(props.config.getStore(), modalName);
|
||||
setVisible(false);
|
||||
})
|
||||
.catch((info) => {
|
||||
console.log('Validate Failed:', info);
|
||||
});
|
||||
}}
|
||||
title="新增弹窗"
|
||||
onCancel={() => setVisible(false)}
|
||||
visible={visible}
|
||||
>
|
||||
<Form layout="vertical" name="basic" form={form}>
|
||||
<Form.Item
|
||||
label="弹窗名称"
|
||||
name="modalName"
|
||||
rules={[{ required: true, message: '请输入弹窗名称!' }]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default Control;
|
||||
185
packages/dooringx-lib/src/components/leftConfig.tsx
Normal file
185
packages/dooringx-lib/src/components/leftConfig.tsx
Normal file
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-02-04 10:32:45
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-06 23:55:37
|
||||
* @FilePath: \dooringv2\packages\dooring-v2-lib\src\components\leftConfig.tsx
|
||||
*/
|
||||
import React, { ReactNode, useEffect, useMemo, useState } from 'react';
|
||||
import { Input, Menu } from 'antd';
|
||||
import { dragEventResolve, LeftRegistComponentMapItem } from '../core/crossDrag';
|
||||
import UserConfig from '../config';
|
||||
import { DoubleLeftOutlined, DoubleRightOutlined, SearchOutlined } from '@ant-design/icons';
|
||||
|
||||
import styles from '../index.less';
|
||||
|
||||
interface LeftConfigProps {
|
||||
config: UserConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 注册加载左侧组件方法,由于异步拉取,所以要异步加载
|
||||
* 不同tab页可以使用不同type区分
|
||||
* @param {*} props
|
||||
* @returns
|
||||
*/
|
||||
function LeftConfig(props: LeftConfigProps) {
|
||||
const [menuSelect, setMenuSelect] = useState('0');
|
||||
const [leftRender, setLeftRender] = useState<ReactNode | null>(null);
|
||||
const leftMapRenderListCategory = useMemo(() => {
|
||||
return props.config.getConfig().leftRenderListCategory;
|
||||
}, [props.config]);
|
||||
|
||||
const [search, setSearch] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
let cache: LeftRegistComponentMapItem[] = [];
|
||||
const type = leftMapRenderListCategory[parseInt(menuSelect, 10)]?.type;
|
||||
const isCustom = leftMapRenderListCategory[parseInt(menuSelect, 10)]?.custom;
|
||||
if (!isCustom) {
|
||||
const config = props.config.getConfig();
|
||||
cache = config.leftAllRegistMap.filter((k) => k.type === type);
|
||||
cache.forEach((v) => props.config.asyncRegistComponent(v.component));
|
||||
setLeftRender(
|
||||
<div className={styles.leftco}>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
padding: '10px',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: 100,
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
height: 32,
|
||||
lineHeight: '32px',
|
||||
marginRight: '10px',
|
||||
fontSize: '14px',
|
||||
fontFamily: 'PingFangSC-Medium, PingFang SC',
|
||||
fontWeight: 600,
|
||||
userSelect: 'none',
|
||||
}}
|
||||
>
|
||||
{leftMapRenderListCategory[parseInt(menuSelect, 10)].displayName}
|
||||
</div>
|
||||
<Input
|
||||
style={{
|
||||
borderRadius: '40px',
|
||||
}}
|
||||
allowClear
|
||||
value={search}
|
||||
onChange={(e) => {
|
||||
setSearch(e.target.value);
|
||||
}}
|
||||
prefix={<SearchOutlined />}
|
||||
/>
|
||||
</div>
|
||||
{search &&
|
||||
search !== '' &&
|
||||
cache
|
||||
.reduce<LeftRegistComponentMapItem[]>((prev, next) => {
|
||||
//筛选搜索条件,name或者displayName存在即显示
|
||||
if (next.displayName.includes(search) || next.component.includes(search)) {
|
||||
prev.push(next);
|
||||
}
|
||||
return prev;
|
||||
}, [])
|
||||
.map((v, index) => (
|
||||
<div className={styles.coitem} key={index} {...dragEventResolve(v)}>
|
||||
<div className={styles.redbox}>
|
||||
{v.imgCustom ? v.imgCustom : <img src={v.img}></img>}
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
textAlign: 'center',
|
||||
lineHeight: '20px',
|
||||
height: '20px',
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
{v.displayName}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{(!search || search === '') &&
|
||||
cache.map((v, index) => (
|
||||
<div className={styles.coitem} key={index} {...dragEventResolve(v)}>
|
||||
<div className={styles.redbox}>
|
||||
{v.imgCustom ? v.imgCustom : <img src={v.img}></img>}
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
textAlign: 'center',
|
||||
lineHeight: '20px',
|
||||
height: '20px',
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
{v.displayName}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
const render = leftMapRenderListCategory[parseInt(menuSelect, 10)]?.customRender;
|
||||
setLeftRender(<div className={styles.leftco}>{render}</div>);
|
||||
}
|
||||
}, [menuSelect, props.config, leftMapRenderListCategory, search]);
|
||||
|
||||
const [isCollapse, setCollapse] = useState(false);
|
||||
const [renderCollapse, setRenderCollaspe] = useState(false);
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex', height: '100%' }}>
|
||||
<div style={{ display: 'flex', flexDirection: 'column' }}>
|
||||
<Menu style={{ flex: 1 }} defaultSelectedKeys={[menuSelect]} mode="vertical">
|
||||
{leftMapRenderListCategory.map((v, i) => {
|
||||
return (
|
||||
<Menu.Item key={i} onClick={() => setMenuSelect(i + '')} icon={v.icon}></Menu.Item>
|
||||
);
|
||||
})}
|
||||
</Menu>
|
||||
<Menu selectedKeys={[]}>
|
||||
<Menu.Item
|
||||
key="1"
|
||||
onClick={() =>
|
||||
setCollapse((pre) => {
|
||||
if (pre) {
|
||||
setTimeout(() => {
|
||||
setRenderCollaspe(false);
|
||||
}, 300);
|
||||
return !pre;
|
||||
} else {
|
||||
setRenderCollaspe(true);
|
||||
return !pre;
|
||||
}
|
||||
})
|
||||
}
|
||||
className={styles.menu_footer}
|
||||
icon={isCollapse ? <DoubleRightOutlined /> : <DoubleLeftOutlined />}
|
||||
></Menu.Item>
|
||||
</Menu>
|
||||
</div>
|
||||
<div
|
||||
className={`${styles.yhLeftrender} ant-menu scrollbar`}
|
||||
style={{
|
||||
width: isCollapse ? 0 : 270,
|
||||
paddingRight: isCollapse ? 0 : 7, // 这个是滚动条宽度
|
||||
overflowX: 'hidden',
|
||||
}}
|
||||
>
|
||||
{!renderCollapse && leftRender}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default LeftConfig;
|
||||
112
packages/dooringx-lib/src/components/modalRender.tsx
Normal file
112
packages/dooringx-lib/src/components/modalRender.tsx
Normal file
@@ -0,0 +1,112 @@
|
||||
import { IStoreData } from '../core/store/storetype';
|
||||
import React, { useMemo } from 'react';
|
||||
import Blocks from './blocks';
|
||||
import UserConfig from '../config';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import { deepCopy } from '../core/utils';
|
||||
|
||||
interface ModalRenderProps {
|
||||
data: IStoreData;
|
||||
name: string; //传递的modal名字
|
||||
config: UserConfig; //需要拿到componentRegister
|
||||
parentDom: HTMLDivElement;
|
||||
rootDom: HTMLDivElement;
|
||||
}
|
||||
|
||||
export const unmountMap: Map<string, Function> = new Map();
|
||||
|
||||
export function ModalRender(props: ModalRenderProps) {
|
||||
//先获取数据
|
||||
const storeData: IStoreData = useMemo(() => {
|
||||
const z = props.data.modalMap[props.name];
|
||||
if (z) {
|
||||
const data = deepCopy(z);
|
||||
//需要把第一个mask扔了手动写一个
|
||||
data.block.shift();
|
||||
return data;
|
||||
}
|
||||
return { block: [] };
|
||||
}, [props.data.modalMap, props.name]);
|
||||
const { parentDom, rootDom } = props;
|
||||
|
||||
//这里还要添加个关闭函数,
|
||||
const unmount = useMemo(() => {
|
||||
return () => {
|
||||
if (parentDom && rootDom) {
|
||||
ReactDOM.unmountComponentAtNode(parentDom);
|
||||
rootDom.removeChild(parentDom);
|
||||
rootDom.parentElement?.removeChild(rootDom);
|
||||
}
|
||||
};
|
||||
}, [parentDom, rootDom]);
|
||||
|
||||
unmountMap.set(props.name, unmount);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className="yh-container-modal"
|
||||
style={{
|
||||
height: `100%`,
|
||||
width: `100%`,
|
||||
position: 'fixed',
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
{storeData.block.map((v) => {
|
||||
return <Blocks key={v.id} config={props.config} data={v} context={'preview'}></Blocks>;
|
||||
})}
|
||||
<div
|
||||
onClick={() => {
|
||||
unmount();
|
||||
}}
|
||||
style={{
|
||||
backgroundColor: '#716f6f9e',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
let wrap: HTMLDivElement | null;
|
||||
|
||||
export const createModal = (name: string, data: IStoreData, config: UserConfig) => {
|
||||
if (wrap) {
|
||||
wrap = null;
|
||||
}
|
||||
|
||||
if (!wrap) {
|
||||
wrap = document.createElement('div');
|
||||
wrap.style.cssText = `line-height:
|
||||
1.5;text-align:
|
||||
center;color: #333;
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
position: fixed;
|
||||
z-index: 100000;
|
||||
width: 100%;
|
||||
height:100%;
|
||||
top:0;
|
||||
left: 0;`;
|
||||
if (wrap) {
|
||||
document.body.appendChild(wrap);
|
||||
}
|
||||
}
|
||||
const divs = document.createElement('div');
|
||||
wrap.appendChild(divs);
|
||||
ReactDOM.render(
|
||||
<ModalRender
|
||||
name={name}
|
||||
data={data}
|
||||
config={config}
|
||||
parentDom={divs}
|
||||
rootDom={wrap}
|
||||
></ModalRender>,
|
||||
divs
|
||||
);
|
||||
};
|
||||
55
packages/dooringx-lib/src/components/preview.tsx
Normal file
55
packages/dooringx-lib/src/components/preview.tsx
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 05:40:37
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-08 20:44:45
|
||||
* @FilePath: \DooringV2\packages\dooringx-lib\src\components\preview.tsx
|
||||
*/
|
||||
import Container from './container';
|
||||
import React, { ReactElement, ReactNode, useEffect, useState } from 'react';
|
||||
import UserConfig from '../config';
|
||||
|
||||
function Preview(props: { config: UserConfig; loadText?: ReactNode }): ReactElement {
|
||||
const isEdit = props.config.getStoreChanger().isEdit();
|
||||
/// 这里需要在渲染组件之前必须把所有config加载完成,否则会导致先运行的函数无法运行
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
// 链接数据
|
||||
props.config
|
||||
.getDataCenter()
|
||||
.initAddToDataMap(props.config.getStore().getData(), props.config.getStoreChanger());
|
||||
// 链接事件
|
||||
props.config
|
||||
.getEventCenter()
|
||||
.syncEventMap(props.config.getStore().getData(), props.config.getStoreChanger());
|
||||
setTimeout(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
}, [props.config]);
|
||||
|
||||
if (isEdit) {
|
||||
// 正常情况不会走这
|
||||
const state = props.config.getStoreChanger().getOrigin()!.now;
|
||||
return (
|
||||
<>
|
||||
<Container config={props.config} context="preview" state={state}></Container>
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
if (loading) {
|
||||
return <div>{props.loadText ? props.loadText : 'loading'}</div>;
|
||||
} else {
|
||||
return (
|
||||
<>
|
||||
<Container
|
||||
config={props.config}
|
||||
context="preview"
|
||||
state={props.config.getStore().getData()}
|
||||
></Container>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
export default Preview;
|
||||
239
packages/dooringx-lib/src/components/rightConfig.tsx
Normal file
239
packages/dooringx-lib/src/components/rightConfig.tsx
Normal file
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 05:42:13
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-05 23:35:05
|
||||
* @FilePath: \DooringV2\packages\dooring-v2-lib\src\components\rightConfig.tsx
|
||||
*/
|
||||
import { CreateOptionsRes } from '../core/components/formTypes';
|
||||
import { IBlockType, IStoreData } from '../core/store/storetype';
|
||||
import { store } from '../runtime/store';
|
||||
import { PropsWithChildren, useEffect, useMemo, useState } from 'react';
|
||||
import React from 'react';
|
||||
import { Tabs, Input, Row, Col } from 'antd';
|
||||
import UserConfig from '../config';
|
||||
import { RGBColor, SketchPicker } from 'react-color';
|
||||
import { rgba2Obj } from '../core/utils';
|
||||
import deepcopy from 'deepcopy';
|
||||
|
||||
interface RightConfigProps {
|
||||
state: IStoreData;
|
||||
config: UserConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 这里一个需要异步拿取当前注册组件的配置项,另外需要异步加载所需的配置项。
|
||||
* @param {*} props
|
||||
* @returns
|
||||
*/
|
||||
function RightConfig(props: PropsWithChildren<RightConfigProps>) {
|
||||
const [menuSelect, setMenuSelect] = useState('0');
|
||||
const [current, setCurrent] = useState<IBlockType | null>(null);
|
||||
const rightMapRenderListCategory = useMemo(() => {
|
||||
return props.config.getConfig().rightRenderListCategory;
|
||||
}, [props.config]);
|
||||
useEffect(() => {
|
||||
const fn = () => {
|
||||
let item: IBlockType | undefined;
|
||||
store.getData().block.some((v) => {
|
||||
if (v.focus) {
|
||||
item = v;
|
||||
}
|
||||
return v.focus === true;
|
||||
});
|
||||
if (item) {
|
||||
setCurrent({ ...item });
|
||||
} else {
|
||||
setCurrent(null);
|
||||
}
|
||||
};
|
||||
const unregist = store.subscribe(fn);
|
||||
return () => {
|
||||
unregist();
|
||||
};
|
||||
}, []);
|
||||
const render = useMemo(() => {
|
||||
return (type: string, current: IBlockType) => {
|
||||
const fn = () => props.config.getComponentRegister().getComp(current.name);
|
||||
const data = fn();
|
||||
// 这里不可能拿不到组件,因为点击的那个组件已经渲染出来了
|
||||
if (data) {
|
||||
const renderList = data.props[type];
|
||||
if (renderList) {
|
||||
return renderList.map((v, i) => {
|
||||
const Component = props.config.getFormRegister().formMap[v.type];
|
||||
if (!Component) {
|
||||
console.error(`you might forgot to regist form component ${v.type}`);
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Component
|
||||
key={i}
|
||||
data={v as CreateOptionsRes<any, any>}
|
||||
current={current}
|
||||
config={props.config}
|
||||
></Component>
|
||||
);
|
||||
});
|
||||
} else {
|
||||
return <div>还没有配置属性</div>;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
}, [props.config]);
|
||||
|
||||
const initColor = useMemo(() => {
|
||||
return props.config.getStoreChanger().isEdit()
|
||||
? rgba2Obj(props.config.getStoreChanger().getOrigin()?.now.globalState.containerColor)
|
||||
: rgba2Obj(props.config.getStore().getData().globalState.containerColor);
|
||||
}, [props.config]);
|
||||
const [color, setColor] = useState<RGBColor>(initColor);
|
||||
const [colorPickerVisible, setColorPickerVisible] = useState(false);
|
||||
const initTitle = useMemo(() => {
|
||||
const title = props.config.getStoreChanger().isEdit()
|
||||
? props.config.getStoreChanger().getOrigin()?.now.globalState.title
|
||||
: props.config.getStore().getData().globalState.title;
|
||||
return title;
|
||||
}, [props.config]);
|
||||
const [title, setTitle] = useState(initTitle);
|
||||
|
||||
const customGlobal = props.config.getConfig().rightGlobalCustom;
|
||||
return (
|
||||
<div
|
||||
className="ant-menu"
|
||||
style={{
|
||||
height: '100%',
|
||||
width: '400px',
|
||||
overflow: 'auto',
|
||||
padding: '0 10px',
|
||||
lineHeight: 1.5715,
|
||||
}}
|
||||
>
|
||||
{current && (
|
||||
<Tabs
|
||||
activeKey={menuSelect}
|
||||
style={{ width: '100%' }}
|
||||
onChange={(e) => {
|
||||
setMenuSelect(e);
|
||||
}}
|
||||
>
|
||||
{rightMapRenderListCategory.map((v, i) => {
|
||||
return (
|
||||
<Tabs.TabPane tab={v.icon} key={i + ''}>
|
||||
<div
|
||||
className="scrollbar"
|
||||
style={{
|
||||
height: 'calc(100vh - 110px)',
|
||||
overflow: 'auto',
|
||||
}}
|
||||
>
|
||||
{v.custom && v.customRender && v.customRender(v.type, current)}
|
||||
{!v.custom && render(v.type, current)}
|
||||
</div>
|
||||
</Tabs.TabPane>
|
||||
);
|
||||
})}
|
||||
</Tabs>
|
||||
)}
|
||||
{!current && !customGlobal && (
|
||||
<div style={{ padding: '20px' }}>
|
||||
<Row style={{ padding: '10 0 20px 0', fontWeight: 'bold' }}>全局设置</Row>
|
||||
<Row style={{ padding: '10px 0' }}>
|
||||
<Col span={6}>标题</Col>
|
||||
<Col span={18}>
|
||||
<Input
|
||||
value={title}
|
||||
onChange={(e) => {
|
||||
const val = e.target.value;
|
||||
setTitle(val);
|
||||
const isEdit = props.config.getStoreChanger().isEdit();
|
||||
if (isEdit) {
|
||||
const originData: IStoreData = deepcopy(
|
||||
props.config.getStoreChanger().getOrigin()!.now
|
||||
);
|
||||
originData.globalState.title = val;
|
||||
props.config.getStoreChanger().updateOrigin(originData);
|
||||
} else {
|
||||
const originData = deepcopy(props.config.getStore().getData());
|
||||
originData.globalState.title = val;
|
||||
props.config.getStore().setData(originData);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{ padding: '10px 0' }}>
|
||||
<Col span={6}>容器底色</Col>
|
||||
<Col span={18}>
|
||||
{
|
||||
<div style={{ position: 'relative' }}>
|
||||
<div
|
||||
onClick={() => {
|
||||
setColorPickerVisible((pre) => !pre);
|
||||
}}
|
||||
style={{
|
||||
background: '#fff',
|
||||
borderRadius: '1px',
|
||||
boxShadow: '0 0 0 1px rgba(0,0,0,.1)',
|
||||
cursor: 'pointer',
|
||||
display: 'inline-block',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: '20px',
|
||||
height: '20px',
|
||||
borderRadius: '2px',
|
||||
background: `rgba(${color.r}, ${color.g}, ${color.b}, ${color.a})`,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{colorPickerVisible && (
|
||||
<>
|
||||
<div style={{ position: 'absolute', zIndex: 2000 }}>
|
||||
<SketchPicker
|
||||
color={color}
|
||||
onChange={(c) => {
|
||||
const newcolor = c.rgb;
|
||||
setColor(newcolor);
|
||||
const isEdit = props.config.getStoreChanger().isEdit();
|
||||
if (isEdit) {
|
||||
const originData: IStoreData = deepcopy(
|
||||
props.config.getStoreChanger().getOrigin()!.now
|
||||
);
|
||||
originData.globalState.containerColor = `rgba(${newcolor.r}, ${newcolor.g}, ${newcolor.b}, ${newcolor.a})`;
|
||||
props.config.getStoreChanger().updateOrigin(originData);
|
||||
} else {
|
||||
const originData = deepcopy(props.config.getStore().getData());
|
||||
originData.globalState.containerColor = `rgba(${newcolor.r}, ${newcolor.g}, ${newcolor.b}, ${newcolor.a})`;
|
||||
props.config.getStore().setData(originData);
|
||||
}
|
||||
}}
|
||||
></SketchPicker>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
position: 'fixed',
|
||||
top: '0px',
|
||||
right: '0px',
|
||||
bottom: '0px',
|
||||
left: '0px',
|
||||
zIndex: 1000,
|
||||
}}
|
||||
onClick={() => setColorPickerVisible(false)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
)}
|
||||
{!current && customGlobal && customGlobal}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default RightConfig;
|
||||
68
packages/dooringx-lib/src/components/wrapperMove/event.ts
Normal file
68
packages/dooringx-lib/src/components/wrapperMove/event.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-02-21 22:17:29
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-04-05 18:24:27
|
||||
* @FilePath: \dooringv2\src\components\wrapperMove\event.ts
|
||||
*/
|
||||
import { store } from '../../runtime/store';
|
||||
import { RefObject } from 'react';
|
||||
import { containerResizer } from '../../core/resizeHandler/containerResizer';
|
||||
import { contextMenuState } from '../../core/contextMenu';
|
||||
|
||||
export interface WrapperMoveStateProps {
|
||||
isDrag: boolean;
|
||||
startX: number;
|
||||
startY: number;
|
||||
needX: number;
|
||||
needY: number;
|
||||
ref: null | RefObject<HTMLDivElement>;
|
||||
}
|
||||
|
||||
export const wrapperMoveState: WrapperMoveStateProps = {
|
||||
isDrag: false,
|
||||
startX: 0,
|
||||
startY: 0,
|
||||
needX: 0,
|
||||
needY: 0,
|
||||
ref: null,
|
||||
};
|
||||
|
||||
export const wrapperEvent = (ref: RefObject<HTMLDivElement>) => {
|
||||
return {
|
||||
onMouseDown: (e: React.MouseEvent) => {
|
||||
// e.preventDefault();// 不能使用preventDefault 否则弹窗输入框焦点无法触发
|
||||
contextMenuState.unmountContextMenu();
|
||||
if (e.target !== ref.current) {
|
||||
} else {
|
||||
wrapperMoveState.isDrag = true;
|
||||
wrapperMoveState.startX = e.clientX;
|
||||
wrapperMoveState.startY = e.clientY;
|
||||
if (ref.current) {
|
||||
ref.current.style.cursor = 'grab';
|
||||
wrapperMoveState.ref = ref;
|
||||
}
|
||||
}
|
||||
},
|
||||
onMouseMove: (e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
if (wrapperMoveState.isDrag) {
|
||||
const diffX = e.clientX - wrapperMoveState.startX;
|
||||
const diffY = e.clientY - wrapperMoveState.startY;
|
||||
wrapperMoveState.needX = wrapperMoveState.needX + diffX;
|
||||
wrapperMoveState.needY = wrapperMoveState.needY + diffY;
|
||||
wrapperMoveState.startX = e.clientX;
|
||||
wrapperMoveState.startY = e.clientY;
|
||||
store.forceUpdate();
|
||||
}
|
||||
containerResizer.onMouseMove(e);
|
||||
},
|
||||
};
|
||||
};
|
||||
export const wrapperMoveMouseUp = () => {
|
||||
if (wrapperMoveState.ref && wrapperMoveState.ref.current) {
|
||||
wrapperMoveState.ref.current.style.cursor = 'default';
|
||||
}
|
||||
containerResizer.onMouseUp();
|
||||
wrapperMoveState.isDrag = false;
|
||||
};
|
||||
44
packages/dooringx-lib/src/components/wrapperMove/index.tsx
Normal file
44
packages/dooringx-lib/src/components/wrapperMove/index.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 04:29:09
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-03-14 04:58:51
|
||||
* @FilePath: \dooring-v2\src\core\wrapperMove\index.tsx
|
||||
*/
|
||||
import { AllHTMLAttributes, CSSProperties, PropsWithChildren, useRef } from 'react';
|
||||
import { wrapperEvent } from './event';
|
||||
import { onWheelEvent } from '../../core/scale';
|
||||
import React from 'react';
|
||||
|
||||
export interface ContainerWrapperProps extends AllHTMLAttributes<HTMLDivElement> {
|
||||
classNames?: string;
|
||||
style?: CSSProperties;
|
||||
}
|
||||
|
||||
function ContainerWrapper(props: PropsWithChildren<ContainerWrapperProps>) {
|
||||
const { children, style, classNames, ...rest } = props;
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
return (
|
||||
<div
|
||||
className={`ant-menu ${classNames}`}
|
||||
ref={ref}
|
||||
style={{
|
||||
backgroundColor: '#f0f0f0',
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
flex: 1,
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
...style,
|
||||
}}
|
||||
{...wrapperEvent(ref)}
|
||||
{...onWheelEvent}
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default ContainerWrapper;
|
||||
531
packages/dooringx-lib/src/config/index.tsx
Normal file
531
packages/dooringx-lib/src/config/index.tsx
Normal file
@@ -0,0 +1,531 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-02-25 21:16:58
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-08 20:42:22
|
||||
* @FilePath: \DooringV2\packages\dooringx-lib\src\config\index.tsx
|
||||
*/
|
||||
import { IBlockType, IStoreData } from '../core/store/storetype';
|
||||
import { store } from '../runtime/store';
|
||||
import { formRegister, componentRegister, commander, storeChanger } from '../runtime';
|
||||
import { ComponentClass, FunctionComponent, ReactNode } from 'react';
|
||||
import { ComponentItemFactory } from '../core/components/abstract';
|
||||
import { marklineConfig } from '../core/markline/marklineConfig';
|
||||
import { CommanderItem } from '../core/command/commanderType';
|
||||
import { contextMenuState } from '../core/contextMenu';
|
||||
import { formComponentRegisterFn } from '../core/components/formComponentRegister';
|
||||
import { deepCopy } from '../core/utils';
|
||||
import { LeftRegistComponentMapItem } from '../core/crossDrag';
|
||||
import { FunctionCenterType } from '../core/functionCenter';
|
||||
import { EventCenter } from '../core/eventCenter';
|
||||
import { DataCenter } from '../core/dataCenter';
|
||||
import { createModal, unmountMap } from '../components/modalRender';
|
||||
import { scaleState } from '../core/scale/state';
|
||||
import { CommanderItemFactory } from '../core/command/abstract';
|
||||
import MmodalMask from '../core/components/defaultFormComponents/modalMask';
|
||||
import MmodalContainer from '../core/components/defaultFormComponents/modalContainer';
|
||||
|
||||
// 组件部分
|
||||
|
||||
/**
|
||||
*
|
||||
* @urlFn 组件异步加载函数
|
||||
* @component 组件默认导出
|
||||
* @export
|
||||
* @interface CacheComponentValueType
|
||||
*/
|
||||
export interface CacheComponentValueType {
|
||||
urlFn?: () => Promise<any>;
|
||||
component?: ComponentItemFactory;
|
||||
}
|
||||
export type CacheComponentType = Record<string, CacheComponentValueType> | {};
|
||||
export type AsyncCacheComponentType = Record<string, () => Promise<any>>;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @export 左侧的图标 custom 自定义渲染
|
||||
* @interface LeftMapRenderListPropsItemCategory
|
||||
*/
|
||||
export interface LeftMapRenderListPropsItemCategory {
|
||||
type: string;
|
||||
icon: ReactNode;
|
||||
custom?: boolean;
|
||||
customRender?: ReactNode;
|
||||
displayName?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @export 右侧的图标配置
|
||||
* @interface RightMapRenderListPropsItemCategory
|
||||
*/
|
||||
export interface RightMapRenderListPropsItemCategory {
|
||||
type: string;
|
||||
icon: ReactNode;
|
||||
custom?: boolean;
|
||||
customRender?: (type: string, current: IBlockType) => ReactNode;
|
||||
}
|
||||
|
||||
// 设置部分
|
||||
export interface InitConfig {
|
||||
/**
|
||||
* 初始化store
|
||||
* @type {IStoreData[]}
|
||||
* @memberof InitConfig
|
||||
*/
|
||||
initStoreData: IStoreData[];
|
||||
|
||||
/**
|
||||
* 左边tab页组件渲染包括异步路径 { type: 'basic', component: 'button', img: 'http://xxxx/1.jpg' ,url:'' },
|
||||
* @memberof InitConfig
|
||||
*/
|
||||
leftAllRegistMap: LeftRegistComponentMapItem[];
|
||||
/**
|
||||
* 左边tab页图标配置
|
||||
* type icon custom customRender
|
||||
* @memberof InitConfig
|
||||
*/
|
||||
leftRenderListCategory: LeftMapRenderListPropsItemCategory[];
|
||||
/**
|
||||
* 右边tab页图标配置
|
||||
* type icon custom customRender
|
||||
* @memberof InitConfig
|
||||
*/
|
||||
rightRenderListCategory: RightMapRenderListPropsItemCategory[];
|
||||
|
||||
/**
|
||||
*
|
||||
* 右侧全局自定义
|
||||
* @type {ReactNode}
|
||||
* @memberof InitConfig
|
||||
*/
|
||||
rightGlobalCustom: ReactNode;
|
||||
|
||||
/**
|
||||
* 组件加载缓存判定,用来设置不异步加载的组件
|
||||
* @memberof InitConfig
|
||||
*/
|
||||
initComponentCache: CacheComponentType;
|
||||
|
||||
/**
|
||||
*
|
||||
* 内置函数配置
|
||||
* @memberof InitConfig
|
||||
*/
|
||||
initFunctionMap: FunctionCenterType;
|
||||
|
||||
/**
|
||||
*
|
||||
* 内置数据中心配置数据
|
||||
* @memberof InitConfig
|
||||
*/
|
||||
initDataCenterMap: Record<string, any>;
|
||||
|
||||
/**
|
||||
*
|
||||
* commander 指令集合
|
||||
* @type {Array<CommanderItemFactory>}
|
||||
* @memberof InitConfig
|
||||
*/
|
||||
initCommandModule: Array<CommanderItemFactory>;
|
||||
|
||||
/**
|
||||
*
|
||||
* 右侧配置项
|
||||
* @type {(Record<
|
||||
* string,
|
||||
* FunctionComponent<any> | ComponentClass<any, any>
|
||||
* >)}
|
||||
* @memberof InitConfig
|
||||
*/
|
||||
initFormComponents: Record<string, FunctionComponent<any> | ComponentClass<any, any>>;
|
||||
}
|
||||
|
||||
export const defaultStore: IStoreData = {
|
||||
container: {
|
||||
width: 375,
|
||||
height: 667,
|
||||
},
|
||||
block: [],
|
||||
modalMap: {},
|
||||
dataSource: {
|
||||
defaultKey: 'defaultValue',
|
||||
},
|
||||
globalState: {
|
||||
containerColor: 'rgba(255,255,255,1)',
|
||||
title: 'dooring',
|
||||
},
|
||||
};
|
||||
|
||||
export const defaultConfig: InitConfig = {
|
||||
initStoreData: [defaultStore],
|
||||
leftAllRegistMap: [],
|
||||
leftRenderListCategory: [],
|
||||
rightGlobalCustom: null,
|
||||
rightRenderListCategory: [],
|
||||
initComponentCache: {
|
||||
modalMask: { component: MmodalMask }, // 这2个组件不配置显示
|
||||
modalContainer: { component: MmodalContainer },
|
||||
},
|
||||
initFunctionMap: {
|
||||
打开弹窗函数: {
|
||||
fn: (_ctx, next, config, args) => {
|
||||
const modalName = args['_modal'];
|
||||
const storeData = config.getStore().getData();
|
||||
createModal(modalName, storeData, config);
|
||||
next();
|
||||
},
|
||||
config: [
|
||||
{
|
||||
name: '弹窗名称',
|
||||
data: ['modal'],
|
||||
options: {
|
||||
receive: '_modal',
|
||||
multi: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
关闭弹窗函数: {
|
||||
fn: (_ctx, next, _config, args) => {
|
||||
const modalName = args['_modal'];
|
||||
const fn = unmountMap.get(modalName);
|
||||
fn ? fn() : '';
|
||||
next();
|
||||
},
|
||||
config: [
|
||||
{
|
||||
name: '弹窗名称',
|
||||
data: ['modal'],
|
||||
options: {
|
||||
receive: '_modal',
|
||||
multi: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
initDataCenterMap: {},
|
||||
initCommandModule: [],
|
||||
initFormComponents: {},
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* 部分无法合并属性如果b传了会以b为准
|
||||
* initstore不合并
|
||||
* leftallregistmap合并
|
||||
* leftRenderListCategory合并
|
||||
* rightRenderListCategory合并
|
||||
* rightGlobalCustom 不合并
|
||||
* initComponentCache合并
|
||||
* initFunctionMap合并
|
||||
* initDataCenterMap合并
|
||||
* initCommandModule合并
|
||||
* initFormComponents合并
|
||||
*
|
||||
* @export InitConfig
|
||||
*/
|
||||
export function userConfigMerge(a: Partial<InitConfig>, b?: Partial<InitConfig>): InitConfig {
|
||||
const mergeConfig: InitConfig = {
|
||||
initStoreData: [defaultStore],
|
||||
leftAllRegistMap: [],
|
||||
leftRenderListCategory: [],
|
||||
rightRenderListCategory: [],
|
||||
initComponentCache: {},
|
||||
initFunctionMap: {},
|
||||
initDataCenterMap: {},
|
||||
initCommandModule: [],
|
||||
rightGlobalCustom: null,
|
||||
initFormComponents: {},
|
||||
};
|
||||
if (!b) {
|
||||
return userConfigMerge(mergeConfig, a);
|
||||
}
|
||||
mergeConfig.initStoreData = b.initStoreData
|
||||
? [...b.initStoreData]
|
||||
: a.initStoreData
|
||||
? [...a.initStoreData]
|
||||
: [defaultStore];
|
||||
|
||||
mergeConfig.rightGlobalCustom = b.rightGlobalCustom ? b.rightGlobalCustom : a.rightGlobalCustom;
|
||||
|
||||
mergeConfig.leftAllRegistMap = b.leftAllRegistMap
|
||||
? a.leftAllRegistMap
|
||||
? [...a.leftAllRegistMap, ...b.leftAllRegistMap]
|
||||
: [...b.leftAllRegistMap]
|
||||
: a.leftAllRegistMap
|
||||
? [...a.leftAllRegistMap]
|
||||
: [];
|
||||
mergeConfig.leftRenderListCategory = b.leftRenderListCategory
|
||||
? a.leftRenderListCategory
|
||||
? [...a.leftRenderListCategory, ...b.leftRenderListCategory]
|
||||
: [...b.leftRenderListCategory]
|
||||
: a.leftRenderListCategory
|
||||
? [...a.leftRenderListCategory]
|
||||
: [...defaultConfig.leftRenderListCategory];
|
||||
mergeConfig.rightRenderListCategory = b.rightRenderListCategory
|
||||
? a.rightRenderListCategory
|
||||
? [...a.rightRenderListCategory, ...b.rightRenderListCategory]
|
||||
: [...b.rightRenderListCategory]
|
||||
: a.rightRenderListCategory
|
||||
? [...a.rightRenderListCategory]
|
||||
: [...defaultConfig.rightRenderListCategory];
|
||||
mergeConfig.initComponentCache = {
|
||||
...a.initComponentCache,
|
||||
...b.initComponentCache,
|
||||
};
|
||||
mergeConfig.initFunctionMap = {
|
||||
...a.initFunctionMap,
|
||||
...b.initFunctionMap,
|
||||
};
|
||||
mergeConfig.initFormComponents = {
|
||||
...a.initFormComponents,
|
||||
...b.initFormComponents,
|
||||
};
|
||||
mergeConfig.initDataCenterMap = {
|
||||
...a.initDataCenterMap,
|
||||
...b.initDataCenterMap,
|
||||
};
|
||||
mergeConfig.initCommandModule = b.initCommandModule
|
||||
? a.initCommandModule
|
||||
? [...a.initCommandModule, ...b.initCommandModule]
|
||||
: [...b.initCommandModule]
|
||||
: a.initCommandModule
|
||||
? [...a.initCommandModule]
|
||||
: [];
|
||||
return mergeConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @export 用户配置项
|
||||
* @class UserConfig
|
||||
*/
|
||||
export class UserConfig {
|
||||
public initConfig: InitConfig;
|
||||
public store = store;
|
||||
public componentRegister = componentRegister;
|
||||
public componentCache = {};
|
||||
public asyncComponentUrlMap = {} as AsyncCacheComponentType;
|
||||
public marklineConfig = marklineConfig;
|
||||
public commanderRegister = commander;
|
||||
public contextMenuState = contextMenuState;
|
||||
public formRegister = formRegister;
|
||||
public storeChanger = storeChanger;
|
||||
public eventCenter: EventCenter;
|
||||
public dataCenter: DataCenter;
|
||||
public scaleState = scaleState;
|
||||
constructor(initConfig?: Partial<InitConfig>) {
|
||||
const mergeConfig = userConfigMerge(defaultConfig, initConfig);
|
||||
this.initConfig = mergeConfig;
|
||||
this.eventCenter = new EventCenter({}, mergeConfig.initFunctionMap);
|
||||
this.dataCenter = new DataCenter(mergeConfig.initDataCenterMap);
|
||||
this.init();
|
||||
// 右侧配置项注册 初始注册组件暂时固定
|
||||
}
|
||||
|
||||
toRegist() {
|
||||
const modules = this.initConfig.initFormComponents;
|
||||
formComponentRegisterFn(formRegister, modules);
|
||||
|
||||
const cache = this.initConfig.initComponentCache;
|
||||
this.componentCache = cache;
|
||||
// 拿到组件缓存后,先同步加载map上组件
|
||||
Object.values(cache).forEach((v) => {
|
||||
if ((v as CacheComponentValueType).component) {
|
||||
this.registComponent((v as CacheComponentValueType).component!);
|
||||
}
|
||||
});
|
||||
// 异步组件注册地址
|
||||
this.initConfig.leftAllRegistMap.forEach((v) => {
|
||||
if (v.urlFn) {
|
||||
//@ts-ignore
|
||||
this.asyncComponentUrlMap[v.component] = v.urlFn;
|
||||
}
|
||||
});
|
||||
// 注册画布上组件
|
||||
this.store.getData().block.forEach((v) => {
|
||||
this.asyncRegistComponent(v.name);
|
||||
});
|
||||
|
||||
// 注册data
|
||||
this.dataCenter = new DataCenter(this.initConfig.initDataCenterMap);
|
||||
//数据需要加上store上的
|
||||
this.dataCenter.initAddToDataMap(this.store.getData(), this.storeChanger);
|
||||
// 修改事件与数据初始
|
||||
this.eventCenter = new EventCenter({}, this.initConfig.initFunctionMap);
|
||||
// 注册画布事件
|
||||
this.eventCenter.syncEventMap(this.store.getData(), this.storeChanger);
|
||||
}
|
||||
|
||||
init() {
|
||||
this.store.resetToInitData(deepCopy(this.initConfig.initStoreData), true);
|
||||
this.toRegist();
|
||||
}
|
||||
|
||||
getStoreJSON() {
|
||||
return JSON.stringify(this.store.getData());
|
||||
}
|
||||
|
||||
parseStoreJson(json: string) {
|
||||
return JSON.parse(json);
|
||||
}
|
||||
|
||||
resetData(data: IStoreData[]) {
|
||||
this.store.resetToInitData(data, true);
|
||||
this.toRegist();
|
||||
}
|
||||
|
||||
getScaleState() {
|
||||
return this.scaleState;
|
||||
}
|
||||
getDataCenter() {
|
||||
return this.dataCenter;
|
||||
}
|
||||
getEventCenter() {
|
||||
return this.eventCenter;
|
||||
}
|
||||
getStoreChanger() {
|
||||
return this.storeChanger;
|
||||
}
|
||||
getConfig() {
|
||||
return this.initConfig;
|
||||
}
|
||||
getStore() {
|
||||
return this.store;
|
||||
}
|
||||
getComponentRegister() {
|
||||
return this.componentRegister;
|
||||
}
|
||||
getContextMenuState() {
|
||||
return this.contextMenuState;
|
||||
}
|
||||
getFormRegister() {
|
||||
return this.formRegister;
|
||||
}
|
||||
getCommanderRegister() {
|
||||
return this.commanderRegister;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 以默认设置重置配置项
|
||||
* @param {Partial<InitConfig>} v
|
||||
* @memberof UserConfig
|
||||
*/
|
||||
resetConfig(v: Partial<InitConfig>) {
|
||||
const mergeConfig = userConfigMerge(defaultConfig, v);
|
||||
this.initConfig = mergeConfig;
|
||||
this.init();
|
||||
store.forceUpdate();
|
||||
}
|
||||
/**
|
||||
* 会重置配置,请修改配置后增加
|
||||
* 异步增加左侧tab页
|
||||
* @memberof UserConfig
|
||||
*/
|
||||
addLeftCategory(v: LeftMapRenderListPropsItemCategory[]) {
|
||||
const obj = {} as InitConfig;
|
||||
obj.leftRenderListCategory = v;
|
||||
this.initConfig = userConfigMerge(this.initConfig, obj);
|
||||
this.init();
|
||||
store.forceUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
* 会重置配置,请修改配置后增加
|
||||
* 异步增加右侧tab页
|
||||
* @memberof UserConfig
|
||||
*/
|
||||
addRightCategory(v: RightMapRenderListPropsItemCategory[]) {
|
||||
const obj = {} as InitConfig;
|
||||
obj.rightRenderListCategory = v;
|
||||
this.initConfig = userConfigMerge(this.initConfig, obj);
|
||||
this.init();
|
||||
store.forceUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
* 会重置配置,请修改配置后增加
|
||||
* 异步增加组件map
|
||||
* @memberof UserConfig
|
||||
*/
|
||||
addCoRegistMap(v: LeftRegistComponentMapItem) {
|
||||
const obj = {} as InitConfig;
|
||||
obj.leftAllRegistMap = [v];
|
||||
this.initConfig = userConfigMerge(this.initConfig, obj);
|
||||
this.init();
|
||||
store.forceUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
*会重置配置,请修改配置后增加
|
||||
* 异步修改config 重置store
|
||||
* @memberof UserConfig
|
||||
*/
|
||||
setConfig(v: Partial<InitConfig>) {
|
||||
this.initConfig = userConfigMerge(this.initConfig, v);
|
||||
this.init();
|
||||
store.forceUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 同步注册指令
|
||||
* @param {CommanderItem} command
|
||||
* @memberof UserConfig
|
||||
*/
|
||||
registCommander(command: CommanderItem) {
|
||||
this.commanderRegister.register(command);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 用于修改markline配置
|
||||
* @returns
|
||||
* @memberof UserConfig
|
||||
*/
|
||||
getMarklineConfig() {
|
||||
return this.marklineConfig;
|
||||
}
|
||||
|
||||
getComponentCache() {
|
||||
return this.componentCache;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* 同步注册组件,不会检测缓存是否存在
|
||||
* @param {ComponentItemFactory} item
|
||||
* @memberof UserConfig
|
||||
*/
|
||||
registComponent(item: ComponentItemFactory) {
|
||||
this.componentRegister.register(item);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* 异步注册组件,会判定缓存是否存在
|
||||
* @param {string} name
|
||||
* @memberof UserConfig
|
||||
*/
|
||||
async asyncRegistComponent(name: string) {
|
||||
//判定缓存
|
||||
if (
|
||||
!(this.componentCache as Record<string, CacheComponentValueType>)[name] &&
|
||||
this.asyncComponentUrlMap[name]
|
||||
) {
|
||||
const chunk = await this.asyncComponentUrlMap[name]();
|
||||
const chunkDefault = chunk.default;
|
||||
this.componentRegister.register(chunkDefault);
|
||||
(this.componentCache as Record<string, CacheComponentValueType>)[name] = {
|
||||
component: chunkDefault,
|
||||
};
|
||||
this.componentRegister.emitEvent(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default UserConfig;
|
||||
19
packages/dooringx-lib/src/core/command/abstract.ts
Normal file
19
packages/dooringx-lib/src/core/command/abstract.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 04:29:09
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-03-14 04:39:30
|
||||
* @FilePath: \dooring-v2\src\core\command\abstract.ts
|
||||
*/
|
||||
import { CommanderItem } from './commanderType';
|
||||
|
||||
export class CommanderItemFactory implements CommanderItem {
|
||||
constructor(
|
||||
public name: CommanderItem['name'],
|
||||
public keyboard: CommanderItem['keyboard'],
|
||||
public excute: CommanderItem['excute'],
|
||||
public display: CommanderItem['display'],
|
||||
public init: CommanderItem['init'] = () => {},
|
||||
public destroy: CommanderItem['destroy'] = () => {}
|
||||
) {}
|
||||
}
|
||||
17
packages/dooringx-lib/src/core/command/commanderType.ts
Normal file
17
packages/dooringx-lib/src/core/command/commanderType.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 04:29:09
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-03-14 04:39:43
|
||||
* @FilePath: \dooring-v2\src\core\command\commanderType.ts
|
||||
*/
|
||||
import Store from '../store';
|
||||
|
||||
export interface CommanderItem {
|
||||
init: () => void;
|
||||
display: string;
|
||||
name: string;
|
||||
keyboard: string;
|
||||
excute: (store: Store, options?: any) => void;
|
||||
destroy: () => void;
|
||||
}
|
||||
84
packages/dooringx-lib/src/core/command/index.ts
Normal file
84
packages/dooringx-lib/src/core/command/index.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 04:29:09
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-08 20:42:15
|
||||
* @FilePath: \DooringV2\packages\dooringx-lib\src\core\command\index.ts
|
||||
*/
|
||||
import Store from '../store';
|
||||
import { CommanderItem } from './commanderType';
|
||||
import { keycodeFilter } from './keycode';
|
||||
|
||||
class CommanderWrapper {
|
||||
constructor(public store: Store, public commandMap: Record<string, CommanderItem> = {}) {}
|
||||
getList() {
|
||||
return this.commandMap;
|
||||
}
|
||||
register(item: CommanderItem) {
|
||||
item.init();
|
||||
if (this.commandMap[item.name]) {
|
||||
// console.error(`${item.name} commander has registed`);
|
||||
return;
|
||||
}
|
||||
this.commandMap[item.name] = item;
|
||||
//注册快捷键,快捷键执行调用excute
|
||||
const remove = this.registerKeyBoard(item);
|
||||
//改写销毁方法
|
||||
const origindestroy = item.destroy;
|
||||
const newdestroy = () => {
|
||||
remove();
|
||||
origindestroy();
|
||||
};
|
||||
item.destroy = newdestroy;
|
||||
}
|
||||
|
||||
registerKeyBoard(current: CommanderItem) {
|
||||
if (current.keyboard.length === 0) {
|
||||
return () => {};
|
||||
}
|
||||
const onKeydown = (e: KeyboardEvent) => {
|
||||
if (document.activeElement !== document.body && e.target !== document.body) {
|
||||
return;
|
||||
}
|
||||
const { shiftKey, altKey, ctrlKey, metaKey, key } = e;
|
||||
const keyString: string[] = [];
|
||||
if (ctrlKey || metaKey) keyString.push('Control');
|
||||
if (shiftKey) keyString.push('Shift');
|
||||
if (altKey) keyString.push('Alt');
|
||||
if (key !== 'Control' && key !== 'Shift' && key !== 'Alt') {
|
||||
const res = keycodeFilter(key);
|
||||
if (res !== undefined) {
|
||||
keyString.push(res);
|
||||
} else {
|
||||
keyString.push(key);
|
||||
}
|
||||
}
|
||||
const keyname = keyString.join('+');
|
||||
if (current.keyboard === keyname) {
|
||||
current.excute(this.store);
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
};
|
||||
window.addEventListener('keydown', onKeydown);
|
||||
return () => window.removeEventListener('keydown', onKeydown);
|
||||
}
|
||||
|
||||
unRegister(name: string) {
|
||||
if (!this.commandMap[name]) {
|
||||
console.error(`${name} commander not found`);
|
||||
return;
|
||||
}
|
||||
const item = this.commandMap[name];
|
||||
item.destroy();
|
||||
delete this.commandMap[item.name];
|
||||
}
|
||||
exec(name: string, options?: any) {
|
||||
if (!this.commandMap[name]) {
|
||||
console.error(`${name} commander not found`);
|
||||
return;
|
||||
}
|
||||
this.commandMap[name].excute(this.store, options);
|
||||
}
|
||||
}
|
||||
export default CommanderWrapper;
|
||||
225
packages/dooringx-lib/src/core/command/keycode.ts
Normal file
225
packages/dooringx-lib/src/core/command/keycode.ts
Normal file
@@ -0,0 +1,225 @@
|
||||
export default {
|
||||
Cancel: 3,
|
||||
Help: 6,
|
||||
Backspace: 8,
|
||||
Tab: 9,
|
||||
Clear: 12,
|
||||
Enter: 13,
|
||||
Shift: 16,
|
||||
Control: 17,
|
||||
Alt: 18,
|
||||
Pause: 19,
|
||||
CapsLock: 20,
|
||||
Escape: 27,
|
||||
Convert: 28,
|
||||
NonConvert: 29,
|
||||
Accept: 30,
|
||||
ModeChange: 31,
|
||||
' ': 32,
|
||||
PageUp: 33,
|
||||
PageDown: 34,
|
||||
End: 35,
|
||||
Home: 36,
|
||||
ArrowLeft: 37,
|
||||
ArrowUp: 38,
|
||||
ArrowRight: 39,
|
||||
ArrowDown: 40,
|
||||
Select: 41,
|
||||
Print: 42,
|
||||
Execute: 43,
|
||||
PrintScreen: 44,
|
||||
Insert: 45,
|
||||
Delete: 46,
|
||||
0: 48,
|
||||
')': 48,
|
||||
1: 49,
|
||||
'!': 49,
|
||||
2: 50,
|
||||
'@': 50,
|
||||
3: 51,
|
||||
'#': 51,
|
||||
4: 52,
|
||||
$: 52,
|
||||
5: 53,
|
||||
'%': 53,
|
||||
6: 54,
|
||||
'^': 54,
|
||||
7: 55,
|
||||
'&': 55,
|
||||
8: 56,
|
||||
'*': 56,
|
||||
9: 57,
|
||||
'(': 57,
|
||||
a: 65,
|
||||
A: 65,
|
||||
b: 66,
|
||||
B: 66,
|
||||
c: 67,
|
||||
C: 67,
|
||||
d: 68,
|
||||
D: 68,
|
||||
e: 69,
|
||||
E: 69,
|
||||
f: 70,
|
||||
F: 70,
|
||||
g: 71,
|
||||
G: 71,
|
||||
h: 72,
|
||||
H: 72,
|
||||
i: 73,
|
||||
I: 73,
|
||||
j: 74,
|
||||
J: 74,
|
||||
k: 75,
|
||||
K: 75,
|
||||
l: 76,
|
||||
L: 76,
|
||||
m: 77,
|
||||
M: 77,
|
||||
n: 78,
|
||||
N: 78,
|
||||
o: 79,
|
||||
O: 79,
|
||||
p: 80,
|
||||
P: 80,
|
||||
q: 81,
|
||||
Q: 81,
|
||||
r: 82,
|
||||
R: 82,
|
||||
s: 83,
|
||||
S: 83,
|
||||
t: 84,
|
||||
T: 84,
|
||||
u: 85,
|
||||
U: 85,
|
||||
v: 86,
|
||||
V: 86,
|
||||
w: 87,
|
||||
W: 87,
|
||||
x: 88,
|
||||
X: 88,
|
||||
y: 89,
|
||||
Y: 89,
|
||||
z: 90,
|
||||
Z: 90,
|
||||
OS: 91,
|
||||
ContextMenu: 93,
|
||||
F1: 112,
|
||||
F2: 113,
|
||||
F3: 114,
|
||||
F4: 115,
|
||||
F5: 116,
|
||||
F6: 117,
|
||||
F7: 118,
|
||||
F8: 119,
|
||||
F9: 120,
|
||||
F10: 121,
|
||||
F11: 122,
|
||||
F12: 123,
|
||||
F13: 124,
|
||||
F14: 125,
|
||||
F15: 126,
|
||||
F16: 127,
|
||||
F17: 128,
|
||||
F18: 129,
|
||||
F19: 130,
|
||||
F20: 131,
|
||||
F21: 132,
|
||||
F22: 133,
|
||||
F23: 134,
|
||||
F24: 135,
|
||||
NumLock: 144,
|
||||
ScrollLock: 145,
|
||||
VolumeMute: 181,
|
||||
VolumeDown: 182,
|
||||
VolumeUp: 183,
|
||||
';': 186,
|
||||
':': 186,
|
||||
'=': 187,
|
||||
'+': 187,
|
||||
',': 188,
|
||||
'<': 188,
|
||||
'-': 189,
|
||||
_: 189,
|
||||
'.': 190,
|
||||
'>': 190,
|
||||
'/': 191,
|
||||
'?': 191,
|
||||
'`': 192,
|
||||
'~': 192,
|
||||
'[': 219,
|
||||
'{': 219,
|
||||
'\\': 220,
|
||||
'|': 220,
|
||||
']': 221,
|
||||
'}': 221,
|
||||
"'": 222,
|
||||
'"': 222,
|
||||
Meta: 224,
|
||||
AltGraph: 225,
|
||||
Attn: 246,
|
||||
CrSel: 247,
|
||||
ExSel: 248,
|
||||
EraseEof: 249,
|
||||
Play: 250,
|
||||
ZoomOut: 251,
|
||||
};
|
||||
|
||||
const keymap: Record<string, string> = {
|
||||
a: 'a',
|
||||
A: 'a',
|
||||
b: 'b',
|
||||
B: 'b',
|
||||
c: 'c',
|
||||
C: 'c',
|
||||
d: 'd',
|
||||
D: 'd',
|
||||
e: 'e',
|
||||
E: 'e',
|
||||
f: 'f',
|
||||
F: 'f',
|
||||
g: 'g',
|
||||
G: 'g',
|
||||
h: 'h',
|
||||
H: 'h',
|
||||
i: 'i',
|
||||
I: 'i',
|
||||
j: 'j',
|
||||
J: 'j',
|
||||
k: 'k',
|
||||
K: 'k',
|
||||
l: 'l',
|
||||
L: 'l',
|
||||
m: 'm',
|
||||
M: 'm',
|
||||
n: 'n',
|
||||
N: 'n',
|
||||
o: 'o',
|
||||
O: 'o',
|
||||
p: 'p',
|
||||
P: 'p',
|
||||
q: 'q',
|
||||
Q: 'q',
|
||||
r: 'r',
|
||||
R: 'r',
|
||||
s: 's',
|
||||
S: 's',
|
||||
t: 't',
|
||||
T: 't',
|
||||
u: 'u',
|
||||
U: 'u',
|
||||
v: 'v',
|
||||
V: 'v',
|
||||
w: 'w',
|
||||
W: 'w',
|
||||
x: 'x',
|
||||
X: 'x',
|
||||
y: 'y',
|
||||
Y: 'y',
|
||||
z: 'z',
|
||||
Z: 'z',
|
||||
};
|
||||
|
||||
export const keycodeFilter = (key: string) => {
|
||||
return keymap[key];
|
||||
};
|
||||
20
packages/dooringx-lib/src/core/command/runtime.ts
Normal file
20
packages/dooringx-lib/src/core/command/runtime.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-07-04 14:18:18
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-04 14:21:33
|
||||
* @FilePath: \DooringV2\packages\dooring-v2-lib\src\core\command\runtime.ts
|
||||
*/
|
||||
import CommanderWrapper from '.';
|
||||
import { CommanderItemFactory } from './abstract';
|
||||
|
||||
export const registCommandFn = (module: CommanderItemFactory[], commander: CommanderWrapper) => {
|
||||
module.forEach((v) => {
|
||||
commander.register(v);
|
||||
});
|
||||
};
|
||||
export const unRegistCommandFn = (module: CommanderItemFactory[], commander: CommanderWrapper) => {
|
||||
module.forEach((v) => {
|
||||
commander.unRegister(v.name);
|
||||
});
|
||||
};
|
||||
22
packages/dooringx-lib/src/core/components/abstract.ts
Normal file
22
packages/dooringx-lib/src/core/components/abstract.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 04:29:09
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-04-04 23:14:00
|
||||
* @FilePath: \dooringv2\src\core\components\abstract.ts
|
||||
*/
|
||||
import { ComponentItem } from './componentItem';
|
||||
|
||||
export class ComponentItemFactory implements ComponentItem {
|
||||
constructor(
|
||||
public name: ComponentItem['name'],
|
||||
public display: ComponentItem['display'],
|
||||
public props: ComponentItem['props'],
|
||||
public initData: ComponentItem['initData'],
|
||||
public render: ComponentItem['render'],
|
||||
public resize: ComponentItem['resize'] = true,
|
||||
public needPosition: ComponentItem['needPosition'] = true,
|
||||
public init: ComponentItem['init'] = () => {},
|
||||
public destroy: ComponentItem['destroy'] = () => {}
|
||||
) {}
|
||||
}
|
||||
35
packages/dooringx-lib/src/core/components/componentItem.ts
Normal file
35
packages/dooringx-lib/src/core/components/componentItem.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 04:29:09
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-06 23:45:21
|
||||
* @FilePath: \dooringv2\packages\dooring-v2-lib\src\core\components\componentItem.ts
|
||||
*/
|
||||
import UserConfig from '../../config';
|
||||
import Store from '../store';
|
||||
import { IBlockType } from '../store/storetype';
|
||||
import { CreateOptionsResAll } from './formTypes';
|
||||
|
||||
/**
|
||||
*
|
||||
* 包装部分配置,渲染配置,条件渲染,属性
|
||||
* @export
|
||||
* @interface ComponentItem
|
||||
*/
|
||||
export interface ComponentItem {
|
||||
init: () => void;
|
||||
name: string; // map上key名
|
||||
display: string; //显示名称
|
||||
resize: boolean;
|
||||
needPosition: boolean; //是否要使用拖拽的点
|
||||
initData: Partial<IBlockType>; //初始值
|
||||
props: Record<string, CreateOptionsResAll[]>; // 配置属性
|
||||
render: (data: IBlockType, context: any, store: Store, config: UserConfig) => JSX.Element;
|
||||
destroy: () => void;
|
||||
}
|
||||
export type ComponentRenderConfigProps = {
|
||||
data: IBlockType;
|
||||
context: any;
|
||||
store: Store;
|
||||
config: UserConfig;
|
||||
};
|
||||
34
packages/dooringx-lib/src/core/components/createBlock.ts
Normal file
34
packages/dooringx-lib/src/core/components/createBlock.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 04:29:09
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-07 16:36:42
|
||||
* @FilePath: \DooringV2\packages\dooringx-lib\src\core\components\createBlock.ts
|
||||
*/
|
||||
import { IBlockType } from '../store/storetype';
|
||||
import { createUid } from '../utils';
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
import { ComponentItem } from './componentItem';
|
||||
|
||||
export function createBlock(top: number, left: number, ComponentItem: ComponentItem): IBlockType {
|
||||
return {
|
||||
id: createUid(ComponentItem.name),
|
||||
name: ComponentItem.name,
|
||||
top,
|
||||
left,
|
||||
zIndex: ComponentItem.initData.zIndex || 0,
|
||||
props: ComponentItem.initData.props || {},
|
||||
resize: ComponentItem.initData.resize || ComponentItem.resize,
|
||||
focus: false,
|
||||
position: ComponentItem.initData.position || 'absolute',
|
||||
display: ComponentItem.initData.display || 'block',
|
||||
width: ComponentItem.initData.width,
|
||||
height: ComponentItem.initData.height,
|
||||
syncList: ComponentItem.initData.syncList || [],
|
||||
canDrag: ComponentItem.initData.canDrag ?? true,
|
||||
eventMap: ComponentItem.initData.eventMap || {},
|
||||
functionList: ComponentItem.initData.functionList || [],
|
||||
animate: ComponentItem.initData.animate || {},
|
||||
fixed: ComponentItem.initData.fixed || false,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-04-05 19:21:36
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-05 23:56:07
|
||||
* @FilePath: \DooringV2\packages\dooring-v2-lib\src\core\components\defaultFormComponents\modalContainer.tsx
|
||||
*/
|
||||
import React from 'react';
|
||||
import { ComponentItemFactory } from '../abstract';
|
||||
|
||||
const MmodalContainer = new ComponentItemFactory(
|
||||
'modalContainer',
|
||||
'模态框容器',
|
||||
{},
|
||||
{
|
||||
props: {},
|
||||
width: 300,
|
||||
height: 300,
|
||||
},
|
||||
(data) => {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
zIndex: data.zIndex,
|
||||
width: data.width,
|
||||
height: data.height,
|
||||
backgroundColor: 'white',
|
||||
}}
|
||||
></div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export default MmodalContainer;
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-04-04 20:35:11
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-05 23:55:53
|
||||
* @FilePath: \DooringV2\packages\dooring-v2-lib\src\core\components\defaultFormComponents\modalMask.tsx
|
||||
*/
|
||||
import { SaveOutlined } from '@ant-design/icons';
|
||||
import { Button } from 'antd';
|
||||
import React from 'react';
|
||||
import { ComponentItemFactory } from '../abstract';
|
||||
|
||||
const MmodalMask = new ComponentItemFactory(
|
||||
'modalMask',
|
||||
'模态框遮罩',
|
||||
{},
|
||||
{
|
||||
props: {},
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
left: 0,
|
||||
zIndex: 999,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
canDrag: false,
|
||||
},
|
||||
(_, context, store, config) => {
|
||||
const container = store.getData().container;
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
width: context === 'preview' ? '100%' : container.width,
|
||||
height: context === 'preview' ? '100%' : container.height,
|
||||
backgroundColor: '#716f6f9e',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
{context === 'edit' && (
|
||||
<Button
|
||||
type="primary"
|
||||
shape="circle"
|
||||
title="save"
|
||||
style={{ position: 'absolute', right: '10px', top: '10px' }}
|
||||
icon={<SaveOutlined></SaveOutlined>}
|
||||
onClick={() => {
|
||||
config.getStoreChanger().closeModal(config.getStore());
|
||||
}}
|
||||
></Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
true,
|
||||
false
|
||||
);
|
||||
|
||||
export default MmodalMask;
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 04:29:09
|
||||
* @LastEditors: chentianshang
|
||||
* @LastEditTime: 2021-07-06 09:53:29
|
||||
* @FilePath: /DooringV2/packages/dooring-v2-lib/src/core/components/formComponentRegister.ts
|
||||
*/
|
||||
|
||||
import { ComponentClass, FunctionComponent } from 'react';
|
||||
import { CreateOptionsRes } from './formTypes';
|
||||
export interface ContainerConfigItem {
|
||||
type: string;
|
||||
option: CreateOptionsRes<any, any>;
|
||||
}
|
||||
export const formComponentRegisterFn = (
|
||||
formComponent: FormComponentRegister,
|
||||
modules: Record<string, FunctionComponent<any> | ComponentClass<any, any>>
|
||||
) => {
|
||||
Object.keys(modules).forEach((v) => {
|
||||
formComponent.register(v, modules[v]);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* 拿到form组件地址和状态
|
||||
* 获取配置container配置项和普通组件配置项
|
||||
* @export
|
||||
* @class FormComponentRegister
|
||||
*/
|
||||
export class FormComponentRegister {
|
||||
constructor(
|
||||
public formMap: Record<string, FunctionComponent<any> | ComponentClass<any, any>> = {},
|
||||
public listener: Function[] = [],
|
||||
public eventMap: Record<string, Function[]> = {},
|
||||
public containerConfig: Array<ContainerConfigItem> = []
|
||||
) {}
|
||||
getMap() {
|
||||
return this.formMap;
|
||||
}
|
||||
getComp(name: string) {
|
||||
return this.formMap[name];
|
||||
}
|
||||
getConfig() {
|
||||
return this.containerConfig;
|
||||
}
|
||||
setConfig(config: Array<ContainerConfigItem>) {
|
||||
this.containerConfig = config;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* 同步注册方法
|
||||
* @memberof FormComponentRegister
|
||||
*/
|
||||
register(name: string, ele: FunctionComponent<any> | ComponentClass<any, any>) {
|
||||
this.formMap[name] = ele;
|
||||
}
|
||||
|
||||
emit() {
|
||||
this.listener.forEach((v) => v());
|
||||
}
|
||||
|
||||
on(event: string, fn: Function) {
|
||||
if (!this.eventMap[event]) {
|
||||
this.eventMap[event] = [];
|
||||
}
|
||||
this.eventMap[event].push(fn);
|
||||
return () => this.eventMap[event].filter((v) => v !== fn);
|
||||
}
|
||||
}
|
||||
27
packages/dooringx-lib/src/core/components/formTypes.ts
Normal file
27
packages/dooringx-lib/src/core/components/formTypes.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 04:29:09
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-06 23:45:02
|
||||
* @FilePath: \dooringv2\packages\dooring-v2-lib\src\core\components\formTypes.ts
|
||||
*/
|
||||
|
||||
export interface CreateOptionsResAll {
|
||||
type: string;
|
||||
option: any;
|
||||
}
|
||||
|
||||
export interface CreateOptionsRes<T, K extends keyof T> {
|
||||
type: keyof T;
|
||||
option: T[K];
|
||||
}
|
||||
|
||||
export function createPannelOptions<T, K extends keyof T>(
|
||||
type: K,
|
||||
option: T[K]
|
||||
): CreateOptionsRes<T, K> {
|
||||
return {
|
||||
type,
|
||||
option,
|
||||
};
|
||||
}
|
||||
79
packages/dooringx-lib/src/core/components/index.ts
Normal file
79
packages/dooringx-lib/src/core/components/index.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 04:29:09
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-03-14 04:43:53
|
||||
* @FilePath: \dooring-v2\src\core\components\index.ts
|
||||
*/
|
||||
import { ComponentItem } from './componentItem';
|
||||
|
||||
/**
|
||||
*
|
||||
* 注册组件需要异步的,由注册时效果决定。
|
||||
* 主要是存放所有已注册组件。可以在其render时提供对应context
|
||||
* @class ComponentRegister
|
||||
*/
|
||||
class ComponentRegister {
|
||||
constructor(
|
||||
public componentMap: Record<string, ComponentItem> = {},
|
||||
public componentList: ComponentItem[] = [],
|
||||
public listener: Function[] = [],
|
||||
public eventMap: Record<string, Function[]> = {}
|
||||
) {}
|
||||
getMap() {
|
||||
return this.componentMap;
|
||||
}
|
||||
getList() {
|
||||
return this.componentList;
|
||||
}
|
||||
getComp(name: string) {
|
||||
return this.componentMap[name];
|
||||
}
|
||||
|
||||
subscribe(fn: Function) {
|
||||
this.listener.push(fn);
|
||||
return () => this.listener.filter((v) => v !== fn);
|
||||
}
|
||||
|
||||
emit() {
|
||||
this.listener.forEach((v) => v());
|
||||
}
|
||||
|
||||
on(event: string, fn: Function) {
|
||||
if (!this.eventMap[event]) {
|
||||
this.eventMap[event] = [];
|
||||
}
|
||||
this.eventMap[event].push(fn);
|
||||
return () => this.eventMap[event].filter((v) => v !== fn);
|
||||
}
|
||||
emitEvent(event: string) {
|
||||
if (!this.eventMap[event]) {
|
||||
return;
|
||||
}
|
||||
this.eventMap[event].forEach((v) => v());
|
||||
}
|
||||
|
||||
register(item: ComponentItem) {
|
||||
if (this.componentMap[item.name]) {
|
||||
// console.error(`${item.name} component has registed`);
|
||||
return;
|
||||
}
|
||||
this.componentMap[item.name] = item;
|
||||
this.componentList.push(item);
|
||||
this.emit();
|
||||
item.init();
|
||||
}
|
||||
|
||||
unRegister(name: string) {
|
||||
if (!this.componentMap[name]) {
|
||||
console.error(`${name} component not found`);
|
||||
return;
|
||||
}
|
||||
const item = this.componentMap[name];
|
||||
item.destroy();
|
||||
this.emit();
|
||||
this.componentList = this.componentList.filter((v) => v !== item);
|
||||
delete this.componentMap[item.name];
|
||||
}
|
||||
}
|
||||
export default ComponentRegister;
|
||||
132
packages/dooringx-lib/src/core/contextMenu/index.tsx
Normal file
132
packages/dooringx-lib/src/core/contextMenu/index.tsx
Normal file
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 04:29:09
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-05 22:46:15
|
||||
* @FilePath: \DooringV2\packages\dooring-v2-lib\src\core\contextMenu\index.tsx
|
||||
*/
|
||||
import { Button } from 'antd';
|
||||
import React, { RefObject, useState } from 'react';
|
||||
import { ReactElement } from 'react';
|
||||
import ReactDOM, { unmountComponentAtNode } from 'react-dom';
|
||||
import { scaleState } from '../scale/state';
|
||||
import { isMac } from '../utils';
|
||||
|
||||
const ContextMenu = () => {
|
||||
const handleclick = () => {
|
||||
unmountContextMenu();
|
||||
};
|
||||
const forceUpdate = useState(0)[1];
|
||||
contextMenuState.forceUpdate = () => {
|
||||
forceUpdate((pre) => pre + 1);
|
||||
};
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
left: contextMenuState.left,
|
||||
top: contextMenuState.top,
|
||||
position: 'fixed',
|
||||
background: 'rgb(24, 23, 23)',
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
onClick={() => {
|
||||
handleclick();
|
||||
}}
|
||||
>
|
||||
请参考文档自定义右键菜单
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export interface ContextMenuStateProps {
|
||||
left: number;
|
||||
top: number;
|
||||
menu: HTMLElement | null;
|
||||
parent: HTMLDivElement | null;
|
||||
contextMenu: ReactElement;
|
||||
unmountContextMenu: () => void;
|
||||
observer: null | MutationObserver;
|
||||
initLeft: number;
|
||||
initTop: number;
|
||||
forceUpdate: Function;
|
||||
state: boolean;
|
||||
}
|
||||
export const contextMenuState: ContextMenuStateProps = {
|
||||
left: 0,
|
||||
top: 0,
|
||||
menu: null,
|
||||
parent: null,
|
||||
contextMenu: <ContextMenu />,
|
||||
unmountContextMenu: unmountContextMenu,
|
||||
observer: null,
|
||||
initLeft: 0,
|
||||
initTop: 0,
|
||||
forceUpdate: () => {},
|
||||
state: false,
|
||||
};
|
||||
|
||||
export function contextMenuEvent(
|
||||
e: React.MouseEvent<HTMLDivElement, MouseEvent>,
|
||||
ref: RefObject<HTMLDivElement>
|
||||
) {
|
||||
e.preventDefault();
|
||||
contextMenuState.unmountContextMenu();
|
||||
const config: MutationObserverInit = {
|
||||
attributes: true,
|
||||
};
|
||||
const callback: MutationCallback = (mutationsList) => {
|
||||
if (isMac()) {
|
||||
//mac 有bug
|
||||
contextMenuState.unmountContextMenu();
|
||||
} else {
|
||||
for (let mutation of mutationsList) {
|
||||
if (mutation.type === 'attributes') {
|
||||
const scale = scaleState.value;
|
||||
const curLeft = parseFloat((mutation.target as HTMLDivElement).style.left);
|
||||
const curTop = parseFloat((mutation.target as HTMLDivElement).style.top);
|
||||
const diffL = (curLeft - contextMenuState.initLeft) * scale;
|
||||
const diffT = (curTop - contextMenuState.initTop) * scale;
|
||||
contextMenuState.initLeft = curLeft;
|
||||
contextMenuState.initTop = curTop;
|
||||
contextMenuState.left = contextMenuState.left + diffL;
|
||||
contextMenuState.top = contextMenuState.top + diffT;
|
||||
contextMenuState.forceUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
contextMenuState.state = true;
|
||||
contextMenuState.observer = new MutationObserver(callback);
|
||||
if (ref.current) {
|
||||
//记录初始值
|
||||
contextMenuState.initTop = parseFloat(ref.current.style.top);
|
||||
contextMenuState.initLeft = parseFloat(ref.current.style.left);
|
||||
contextMenuState.observer.observe(ref.current, config);
|
||||
}
|
||||
contextMenuState.left = e.clientX;
|
||||
contextMenuState.top = e.clientY;
|
||||
if (!contextMenuState.menu) {
|
||||
contextMenuState.menu = document.createElement('div');
|
||||
document.body && document.body.appendChild(contextMenuState.menu);
|
||||
}
|
||||
if (!contextMenuState.parent) {
|
||||
contextMenuState.parent = document.createElement('div');
|
||||
}
|
||||
contextMenuState.menu.appendChild(contextMenuState.parent);
|
||||
ReactDOM.render(contextMenuState.contextMenu, contextMenuState.parent);
|
||||
}
|
||||
|
||||
export function unmountContextMenu() {
|
||||
contextMenuState.state = false;
|
||||
if (contextMenuState.observer) {
|
||||
contextMenuState.observer.disconnect();
|
||||
}
|
||||
if (contextMenuState.menu && contextMenuState.parent) {
|
||||
unmountComponentAtNode(contextMenuState.parent);
|
||||
contextMenuState.menu.removeChild(contextMenuState.parent);
|
||||
contextMenuState.parent = null;
|
||||
}
|
||||
}
|
||||
|
||||
export default ContextMenu;
|
||||
84
packages/dooringx-lib/src/core/crossDrag/index.ts
Normal file
84
packages/dooringx-lib/src/core/crossDrag/index.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 04:29:09
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-04 15:21:54
|
||||
* @FilePath: \DooringV2\packages\dooring-v2-lib\src\core\crossDrag\index.ts
|
||||
*/
|
||||
import { store } from '../../runtime/store';
|
||||
import { componentRegister } from '../../runtime';
|
||||
import { DragEvent, ReactNode } from 'react';
|
||||
import { createBlock } from '../components/createBlock';
|
||||
import { IBlockType } from '../store/storetype';
|
||||
import { deepCopy } from '../utils';
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @interface LeftRegistComponentMapItem
|
||||
* @img 图片地址
|
||||
* @urlFn 组件异步加载函数
|
||||
*/
|
||||
export interface LeftRegistComponentMapItem {
|
||||
type: string;
|
||||
component: string;
|
||||
img: string;
|
||||
imgCustom?: ReactNode;
|
||||
displayName: string;
|
||||
urlFn?: () => Promise<any>;
|
||||
}
|
||||
|
||||
let currentDrag: LeftRegistComponentMapItem | null = null;
|
||||
export const dragEventResolve = function (item: LeftRegistComponentMapItem) {
|
||||
return {
|
||||
draggable: true,
|
||||
onDragStart: () => {
|
||||
currentDrag = item;
|
||||
},
|
||||
onDragOver: (e: DragEvent<HTMLDivElement>) => {
|
||||
e.preventDefault();
|
||||
},
|
||||
onDrop: () => {},
|
||||
onDragEnd: () => {},
|
||||
};
|
||||
};
|
||||
|
||||
export const containerDragResolve = {
|
||||
onDragStart: () => {},
|
||||
onDragOver: (e: DragEvent<HTMLDivElement>) => {
|
||||
e.preventDefault();
|
||||
},
|
||||
onDrop: (e: DragEvent<HTMLDivElement>) => {
|
||||
const offsetX = e.nativeEvent.offsetX;
|
||||
const offestY = e.nativeEvent.offsetY;
|
||||
//drop后修改store,
|
||||
if (currentDrag) {
|
||||
// 还需要拿到注册的组件状态
|
||||
const origin = componentRegister.getComp(currentDrag.component);
|
||||
if (!origin) {
|
||||
console.log(currentDrag.component, 'wait the chunk pull compeletely and retry');
|
||||
return;
|
||||
}
|
||||
const target = e.target as HTMLElement;
|
||||
let newblock: IBlockType;
|
||||
if (!origin.needPosition) {
|
||||
newblock = createBlock(
|
||||
origin.initData.top ?? offestY,
|
||||
origin.initData.left ?? offsetX,
|
||||
origin
|
||||
);
|
||||
} else {
|
||||
if (target.id !== 'yh-container') {
|
||||
newblock = createBlock(offestY + target.offsetTop, offsetX + target.offsetLeft, origin);
|
||||
} else {
|
||||
newblock = createBlock(offestY, offsetX, origin);
|
||||
}
|
||||
}
|
||||
const data = deepCopy(store.getData());
|
||||
data.block.push(newblock);
|
||||
store.setData({ ...data });
|
||||
}
|
||||
currentDrag = null;
|
||||
},
|
||||
onDragEnd: () => {},
|
||||
};
|
||||
123
packages/dooringx-lib/src/core/dataCenter/index.ts
Normal file
123
packages/dooringx-lib/src/core/dataCenter/index.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-04-13 11:20:55
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-05-04 20:34:36
|
||||
* @FilePath: \dooringv2\packages\dooring-v2-lib\src\core\dataCenter\index.ts
|
||||
*/
|
||||
|
||||
import UserConfig from '../../config';
|
||||
import { IStoreData } from '../store/storetype';
|
||||
import { StoreChanger } from '../storeChanger';
|
||||
|
||||
/**
|
||||
*
|
||||
* 用来管理页面数据,包括全局数据,做全局设置变量时可以加上
|
||||
* 使用Record<string,any>结构,每个组件的数据需要抛出并设定键进行通信。
|
||||
* @export
|
||||
* @class DataCenter
|
||||
*/
|
||||
export class DataCenter {
|
||||
public asyncMap: Record<string, Function> = {};
|
||||
constructor(public dataMap: Record<string, any> = {}) {}
|
||||
|
||||
/**
|
||||
*
|
||||
* 拿到map
|
||||
* @return {*}
|
||||
* @memberof DataCenter
|
||||
*/
|
||||
getDataMap() {
|
||||
return this.dataMap;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 用于设置map数据
|
||||
* 在异步注册时会触发get的回调,动态不需要持久化
|
||||
* @memberof DataCenter
|
||||
*/
|
||||
setToMap(data: Record<string, any>) {
|
||||
this.dataMap = Object.assign(this.dataMap, data);
|
||||
Object.keys(data).forEach((v) => {
|
||||
if (this.asyncMap[v]) {
|
||||
this.asyncMap[v]();
|
||||
delete this.asyncMap[v];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 静态设置map 和异步无关 静态需要持久化,datacenter存入store
|
||||
* 该更新不放在redo undo处
|
||||
* @param {Record<string, any>} data
|
||||
* @memberof DataCenter
|
||||
*/
|
||||
staticSetToMap(data: Record<string, any>, config: UserConfig) {
|
||||
this.dataMap = data;
|
||||
const storeChanger = config.getStoreChanger();
|
||||
const store = config.getStore();
|
||||
const storeCurrentData = store.getData();
|
||||
const sign = storeChanger.isEdit();
|
||||
if (sign) {
|
||||
const originData = storeChanger.getOrigin();
|
||||
if (originData) {
|
||||
const currentData = originData.data[originData.current];
|
||||
currentData.dataSource = data;
|
||||
}
|
||||
} else {
|
||||
storeCurrentData.dataSource = data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 初始收集使用 -> to datacenter
|
||||
* @param {IStoreData} data
|
||||
* @memberof DataCenter
|
||||
*/
|
||||
initAddToDataMap(data: IStoreData, storeChanger: StoreChanger) {
|
||||
const sign = storeChanger.isEdit();
|
||||
//这里只能初始触发,一般不会走编辑状态,否则逻辑可能会有问题
|
||||
if (sign) {
|
||||
// 编辑状态收集orgin
|
||||
const originData = storeChanger.getOrigin();
|
||||
if (originData) {
|
||||
const currentData = originData.data[originData.current];
|
||||
this.dataMap = currentData.dataSource;
|
||||
}
|
||||
} else {
|
||||
this.dataMap = data.dataSource;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 获取值可异步
|
||||
* @param {string} name
|
||||
* @memberof DataCenter
|
||||
*/
|
||||
getValue(name: string) {
|
||||
const value = this.dataMap[name];
|
||||
if (value) {
|
||||
return Promise.resolve(value);
|
||||
}
|
||||
return new Promise((resolve) => {
|
||||
this.asyncMap[name] = () => {
|
||||
resolve(this.getValue(name));
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 获取值不可异步
|
||||
* @param {string} name
|
||||
* @memberof DataCenter
|
||||
*/
|
||||
get(name: string) {
|
||||
const value = this.dataMap[name];
|
||||
return value;
|
||||
}
|
||||
}
|
||||
72
packages/dooringx-lib/src/core/eventCenter/eventQuene.ts
Normal file
72
packages/dooringx-lib/src/core/eventCenter/eventQuene.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-04-08 20:22:43
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-06-28 16:08:56
|
||||
* @FilePath: \DooringV2\packages\dooring-v2-lib\src\core\eventCenter\eventQuene.ts
|
||||
*/
|
||||
import { EventCenterMapItem, EventCenterUserSelect } from '.';
|
||||
import UserConfig from '../../config';
|
||||
import { FunctionCenterFunction } from '../functionCenter';
|
||||
|
||||
export class EventQuene {
|
||||
available: number;
|
||||
waiters: Array<{
|
||||
fn: FunctionCenterFunction;
|
||||
args: any;
|
||||
eventList: {
|
||||
arr: Array<EventCenterMapItem>;
|
||||
displayName: string;
|
||||
userSelect: Array<EventCenterUserSelect>;
|
||||
};
|
||||
iname: EventCenterMapItem;
|
||||
}>;
|
||||
context: Record<string, any>;
|
||||
config: UserConfig;
|
||||
constructor(available: number = 1, config: UserConfig, context = {}) {
|
||||
this.available = available;
|
||||
this.waiters = [];
|
||||
this._continue = this._continue.bind(this);
|
||||
this.context = context;
|
||||
this.config = config;
|
||||
}
|
||||
take(
|
||||
task: FunctionCenterFunction,
|
||||
args: Record<string, any>,
|
||||
eventList: {
|
||||
arr: Array<EventCenterMapItem>;
|
||||
displayName: string;
|
||||
userSelect: Array<EventCenterUserSelect>;
|
||||
},
|
||||
iname: EventCenterMapItem
|
||||
) {
|
||||
if (this.available > 0) {
|
||||
this.available--;
|
||||
task(this.context, this.leave.bind(this), this.config, args, eventList, iname);
|
||||
} else {
|
||||
this.waiters.push({ fn: task, args: args, eventList, iname });
|
||||
}
|
||||
}
|
||||
leave() {
|
||||
this.available++;
|
||||
if (this.waiters.length > 0) {
|
||||
this._continue();
|
||||
}
|
||||
}
|
||||
_continue() {
|
||||
if (this.available > 0) {
|
||||
this.available--;
|
||||
let task = this.waiters.shift();
|
||||
if (task?.fn) {
|
||||
task.fn(
|
||||
this.context,
|
||||
this.leave.bind(this),
|
||||
this.config,
|
||||
task.args,
|
||||
task.eventList,
|
||||
task.iname
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
149
packages/dooringx-lib/src/core/eventCenter/index.ts
Normal file
149
packages/dooringx-lib/src/core/eventCenter/index.ts
Normal file
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-04-06 19:33:17
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-04 14:41:48
|
||||
* @FilePath: \DooringV2\packages\dooring-v2-lib\src\core\eventCenter\index.ts
|
||||
*/
|
||||
import UserConfig from '../../config';
|
||||
import { FunctionCenter, FunctionCenterType } from '../functionCenter';
|
||||
import { FunctionDataType } from '../functionCenter/config';
|
||||
import { IStoreData } from '../store/storetype';
|
||||
import { StoreChanger } from '../storeChanger';
|
||||
import { EventQuene } from './eventQuene';
|
||||
|
||||
// 每个组件制作时可以抛出多个事件,事件名为id+自定义name,
|
||||
// 每个组件可以抛出多个函数,存在函数中心
|
||||
|
||||
export interface EventCenterMapItem {
|
||||
name: string; // 函数名
|
||||
args: Record<string, any>; // 输入参数都会变成对象传来,
|
||||
data: Record<string, FunctionDataType>; // 用户选的种类 键是每个配置项名
|
||||
}
|
||||
export interface EventCenterUserSelect {
|
||||
uid: string;
|
||||
value: string;
|
||||
detail: Record<string, any>;
|
||||
}
|
||||
|
||||
export type EventCenterMapType = Record<
|
||||
string,
|
||||
{
|
||||
arr: Array<EventCenterMapItem>;
|
||||
displayName: string;
|
||||
userSelect: Array<EventCenterUserSelect>;
|
||||
}
|
||||
>;
|
||||
|
||||
export class EventCenter {
|
||||
/**
|
||||
* 该map需要存入store,值为函数的key的数组
|
||||
* @param {Record<string, Array<string>>} [eventMap={}]
|
||||
* @memberof EventCenter
|
||||
*/
|
||||
public functionCenter: FunctionCenter;
|
||||
constructor(public eventMap: EventCenterMapType = {}, configFunction?: FunctionCenterType) {
|
||||
this.functionCenter = new FunctionCenter(configFunction);
|
||||
}
|
||||
|
||||
getFunctionCenter() {
|
||||
return this.functionCenter;
|
||||
}
|
||||
|
||||
getEventMap() {
|
||||
return this.eventMap;
|
||||
}
|
||||
resetEventMap() {
|
||||
this.eventMap = {};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 重置map进行收集事件 主要就是收集eventMap字段
|
||||
* 这个应该优化在换store情况下。
|
||||
* @param {IStoreData} data
|
||||
* @memberof EventCenter
|
||||
*/
|
||||
syncEventMap(data: IStoreData, storeChanger: StoreChanger) {
|
||||
// 需要判断是否在弹窗状态。如果在弹窗状态,数据以storeChanger为准,否则就以store为准
|
||||
const sign = storeChanger.isEdit();
|
||||
this.eventMap = {};
|
||||
if (sign) {
|
||||
const originData = storeChanger.getOrigin();
|
||||
if (originData) {
|
||||
const currentData = originData.data[originData.current];
|
||||
// 收集源block数据
|
||||
currentData.block.forEach((v) => {
|
||||
this.eventMap = Object.assign(this.eventMap, v.eventMap);
|
||||
});
|
||||
//收集源modal数据
|
||||
Object.keys(currentData.modalMap).forEach((v) => {
|
||||
currentData.modalMap[v].block.forEach((k) => {
|
||||
this.eventMap = Object.assign(this.eventMap, k.eventMap);
|
||||
});
|
||||
});
|
||||
//收集当前modal数据
|
||||
data.block.forEach((v) => {
|
||||
this.eventMap = Object.assign(this.eventMap, v.eventMap);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
data.block.forEach((v) => {
|
||||
this.eventMap = Object.assign(this.eventMap, v.eventMap);
|
||||
});
|
||||
Object.keys(data.modalMap).forEach((v) => {
|
||||
data.modalMap[v].block.forEach((k) => {
|
||||
this.eventMap = Object.assign(this.eventMap, k.eventMap);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 手动更新状态eventMap
|
||||
* @param {string} name
|
||||
* @memberof EventCenter
|
||||
*/
|
||||
manualUpdateMap(name: string, displayName: string, arr?: Array<EventCenterMapItem>) {
|
||||
if (!this.eventMap[name]) {
|
||||
this.eventMap[name] = {
|
||||
arr: [],
|
||||
displayName: displayName,
|
||||
userSelect: [],
|
||||
};
|
||||
}
|
||||
if (arr && this.eventMap[name].displayName) {
|
||||
this.eventMap[name].arr = arr;
|
||||
} else if (arr && this.eventMap[name]) {
|
||||
this.eventMap[name] = {
|
||||
displayName,
|
||||
arr,
|
||||
userSelect: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 执行事件链
|
||||
* @param {string} name
|
||||
* @memberof EventCenter
|
||||
*/
|
||||
async runEventQueue(name: string, config: UserConfig) {
|
||||
const eventList = this.eventMap[name];
|
||||
if (!eventList) {
|
||||
console.error(`未查询到该事件${name}`);
|
||||
return;
|
||||
}
|
||||
const arr = new EventQuene(1, config);
|
||||
//如果组件异步加载,那么函数会过段时间载入,等同于异步函数
|
||||
// 函数中心需要处理未找到时的异步处理情况
|
||||
if (Array.isArray(eventList.arr)) {
|
||||
for (let i of eventList.arr) {
|
||||
const fn = await this.functionCenter.getFunction(i.name);
|
||||
arr.take(fn, i.args, eventList, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
60
packages/dooringx-lib/src/core/focusHandler/index.tsx
Normal file
60
packages/dooringx-lib/src/core/focusHandler/index.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 04:29:09
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-03-14 12:10:16
|
||||
* @FilePath: \dooring-v2\src\core\focusHandler\index.tsx
|
||||
*/
|
||||
import { store } from '../../runtime/store';
|
||||
import { innerDragState } from '../innerDrag/state';
|
||||
import { IBlockType } from '../store/storetype';
|
||||
import { deepCopy } from '../utils';
|
||||
import { selectRangeMouseDown } from '../selectRange';
|
||||
import { unmountContextMenu } from '../contextMenu';
|
||||
import { focusState } from './state';
|
||||
|
||||
export function containerFocusRemove() {
|
||||
const onMouseDown = (e: React.MouseEvent) => {
|
||||
const clonedata = deepCopy(store.getData());
|
||||
const newBlock = clonedata.block.map((v: IBlockType) => {
|
||||
v.focus = false;
|
||||
return v;
|
||||
});
|
||||
focusState.blocks = [];
|
||||
store.setData({ ...clonedata, block: newBlock });
|
||||
if (!innerDragState.item) {
|
||||
selectRangeMouseDown(e);
|
||||
}
|
||||
unmountContextMenu();
|
||||
};
|
||||
return {
|
||||
onMouseDown,
|
||||
};
|
||||
}
|
||||
|
||||
export function blockFocus(e: React.MouseEvent, item: IBlockType) {
|
||||
const clonedata = deepCopy(store.getData());
|
||||
if (e.shiftKey) {
|
||||
const newBlock = clonedata.block.map((v: IBlockType) => {
|
||||
if (v.id === item.id) {
|
||||
v.focus = true;
|
||||
focusState.blocks.push(item);
|
||||
}
|
||||
return v;
|
||||
});
|
||||
store.setData({ ...clonedata, block: newBlock });
|
||||
} else {
|
||||
let blocks: IBlockType[] = [];
|
||||
const newBlock = clonedata.block.map((v: IBlockType) => {
|
||||
if (v.id === item.id) {
|
||||
blocks.push(item);
|
||||
v.focus = true;
|
||||
} else {
|
||||
v.focus = false;
|
||||
}
|
||||
return v;
|
||||
});
|
||||
focusState.blocks = blocks;
|
||||
store.setData({ ...clonedata, block: newBlock });
|
||||
}
|
||||
}
|
||||
14
packages/dooringx-lib/src/core/focusHandler/state.ts
Normal file
14
packages/dooringx-lib/src/core/focusHandler/state.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 12:06:20
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-03-14 12:06:50
|
||||
* @FilePath: \dooring-v2\src\core\focusHandler\state.ts
|
||||
*/
|
||||
import { IBlockType } from '../store/storetype';
|
||||
export interface FocusStateType {
|
||||
blocks: IBlockType[];
|
||||
}
|
||||
export const focusState: FocusStateType = {
|
||||
blocks: [],
|
||||
};
|
||||
27
packages/dooringx-lib/src/core/functionCenter/config.ts
Normal file
27
packages/dooringx-lib/src/core/functionCenter/config.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-06-25 10:03:21
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-06-28 19:29:21
|
||||
* @FilePath: \DooringV2\packages\dooring-v2-lib\src\core\functionCenter\config.ts
|
||||
*/
|
||||
// 设定函数配置项格式,
|
||||
|
||||
export type FunctionDataType = keyof FunctionDataMap;
|
||||
export type FunctionNameType = string;
|
||||
export type FuncitonOptionConfigType = {
|
||||
receive: string;
|
||||
multi: boolean;
|
||||
};
|
||||
export interface FunctionDataMap {
|
||||
dataSource: FuncitonOptionConfigType;
|
||||
modal: FuncitonOptionConfigType;
|
||||
input: FuncitonOptionConfigType;
|
||||
ctx: FuncitonOptionConfigType;
|
||||
}
|
||||
// data 如果是''则在datasource,input,ctx选择
|
||||
export type FunctionConfigType = {
|
||||
name: FunctionNameType; // 会放到左侧展示 唯一!
|
||||
data: FunctionDataType[];
|
||||
options: FuncitonOptionConfigType;
|
||||
}[];
|
||||
128
packages/dooringx-lib/src/core/functionCenter/index.ts
Normal file
128
packages/dooringx-lib/src/core/functionCenter/index.ts
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-04-08 19:59:01
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-07 01:46:17
|
||||
* @FilePath: \dooringv2\packages\dooringx-lib\src\core\functionCenter\index.ts
|
||||
*/
|
||||
|
||||
import UserConfig from '../../config';
|
||||
import { EventCenterMapItem, EventCenterUserSelect } from '../eventCenter';
|
||||
import { FunctionConfigType } from './config';
|
||||
|
||||
/**
|
||||
*
|
||||
* ctx可在事件链中传递,函数完成后调用next执行下一个函数
|
||||
* @export
|
||||
*/
|
||||
export type FunctionCenterFunction = (
|
||||
ctx: Record<string, any>,
|
||||
next: Function,
|
||||
config: UserConfig,
|
||||
args: Record<string, any>,
|
||||
eventList: {
|
||||
arr: Array<EventCenterMapItem>;
|
||||
displayName: string;
|
||||
userSelect: Array<EventCenterUserSelect>;
|
||||
},
|
||||
iname: EventCenterMapItem
|
||||
) => void;
|
||||
|
||||
export type FunctionCenterType = Record<
|
||||
string,
|
||||
{ fn: FunctionCenterFunction; config: FunctionConfigType }
|
||||
>;
|
||||
|
||||
/**
|
||||
*
|
||||
* 初始化时可以加载初始已配好的函数
|
||||
* @export
|
||||
* @class FunctionCenter
|
||||
*/
|
||||
export class FunctionCenter {
|
||||
/**
|
||||
*
|
||||
* 该map 用于获取函数未获取到时,异步拉取
|
||||
* @memberof FunctionCenter
|
||||
*/
|
||||
public asyncMap: Record<string, Function> = {};
|
||||
public configMap: Record<string, FunctionConfigType> = {};
|
||||
public funcitonMap: Record<string, FunctionCenterFunction> = {};
|
||||
constructor(public initConfig: FunctionCenterType = {}) {
|
||||
this.init(initConfig);
|
||||
}
|
||||
|
||||
init(initConfig: FunctionCenterType) {
|
||||
this.reset();
|
||||
this.funcitonMap = Object.keys(initConfig).reduce<Record<string, FunctionCenterFunction>>(
|
||||
(prev, next) => {
|
||||
prev[next] = initConfig[next].fn;
|
||||
return prev;
|
||||
},
|
||||
{}
|
||||
);
|
||||
this.configMap = Object.keys(initConfig).reduce<Record<string, FunctionConfigType>>(
|
||||
(prev, next) => {
|
||||
prev[next] = initConfig[next].config;
|
||||
return prev;
|
||||
},
|
||||
{}
|
||||
);
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.funcitonMap = {};
|
||||
this.configMap = {};
|
||||
}
|
||||
|
||||
getFunctionMap() {
|
||||
return this.funcitonMap;
|
||||
}
|
||||
|
||||
getConfigMap() {
|
||||
return this.configMap;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 注册函数,同名覆盖,返回删除函数
|
||||
* @param {string} name
|
||||
* @param {FunctionCenterFunction} fn
|
||||
* @return {*}
|
||||
* @memberof FunctionCenter
|
||||
*/
|
||||
register(name: string, fn: FunctionCenterFunction, config: FunctionConfigType) {
|
||||
// 注册时,需要通知asyncmap已经拿到
|
||||
this.funcitonMap[name] = fn;
|
||||
if (this.asyncMap[name]) {
|
||||
this.asyncMap[name]();
|
||||
}
|
||||
this.configMap[name] = config;
|
||||
return () => {
|
||||
delete this.funcitonMap[name];
|
||||
delete this.configMap[name];
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 获取函数,包含异步获取函数
|
||||
* @param {string} name
|
||||
* @return {*} {Promise<FunctionCenterFunction>}
|
||||
* @memberof FunctionCenter
|
||||
*/
|
||||
getFunction(name: string): Promise<FunctionCenterFunction> {
|
||||
//如果拿不到,可能是异步,进行监听回调
|
||||
const fn = this.funcitonMap[name];
|
||||
if (fn) {
|
||||
return Promise.resolve(fn);
|
||||
}
|
||||
return new Promise((resolve) => {
|
||||
console.warn(`waiting the function now ${name} `);
|
||||
this.asyncMap[name] = () => {
|
||||
delete this.asyncMap[name];
|
||||
resolve(this.getFunction(name));
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
124
packages/dooringx-lib/src/core/innerDrag/index.ts
Normal file
124
packages/dooringx-lib/src/core/innerDrag/index.ts
Normal file
@@ -0,0 +1,124 @@
|
||||
import { store } from '../../runtime/store';
|
||||
import { RefObject } from 'react';
|
||||
import { blockFocus, containerFocusRemove } from '../focusHandler';
|
||||
import { marklineConfig } from '../markline/marklineConfig';
|
||||
import { resizerMouseMove, resizerMouseUp } from '../resizeHandler';
|
||||
import { scaleState } from '../scale/state';
|
||||
import { selectRangeMouseMove, selectData, selectRangeMouseUp } from '../selectRange';
|
||||
import { IBlockType } from '../store/storetype';
|
||||
import { deepCopy, isMac } from '../utils';
|
||||
import { wrapperMoveMouseUp } from '../../components/wrapperMove/event';
|
||||
import { contextMenuState } from '../contextMenu';
|
||||
import { innerDragState } from './state';
|
||||
|
||||
export const innerDrag = function (item: IBlockType, ref: RefObject<HTMLDivElement>) {
|
||||
return {
|
||||
onMouseDown: (e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (!item.canDrag) {
|
||||
containerFocusRemove().onMouseDown(e);
|
||||
return;
|
||||
}
|
||||
blockFocus(e, item);
|
||||
if (item.id && innerDragState.lastClick && item.id !== innerDragState.lastClick.id) {
|
||||
contextMenuState.unmountContextMenu();
|
||||
}
|
||||
innerDragState.lastClick = item;
|
||||
|
||||
if (item.position === 'static') {
|
||||
return;
|
||||
}
|
||||
if (ref.current) {
|
||||
ref.current.style.cursor = 'move';
|
||||
ref.current.style.willChange = 'left,right,width,height';
|
||||
}
|
||||
innerDragState.startX = e.clientX;
|
||||
innerDragState.startY = e.clientY;
|
||||
innerDragState.item = item;
|
||||
innerDragState.isDrag = true;
|
||||
innerDragState.ref = ref;
|
||||
innerDragState.current = store.getIndex();
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const innerContainerDrag = function () {
|
||||
let lastblock: null | IBlockType;
|
||||
const onMouseMove = (e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
if (isMac() && contextMenuState.state) {
|
||||
//mac有bug
|
||||
return;
|
||||
}
|
||||
|
||||
const id = innerDragState.item?.id;
|
||||
if (id && innerDragState.isDrag) {
|
||||
const current = store.getData().block.find((v) => v.id === id);
|
||||
if (current?.position === 'static') {
|
||||
return;
|
||||
}
|
||||
let { clientX: moveX, clientY: moveY } = e;
|
||||
const { startX, startY } = innerDragState;
|
||||
const scale = scaleState.value;
|
||||
let durX = (moveX - startX) / scale;
|
||||
let durY = (moveY - startY) / scale;
|
||||
let newblock: IBlockType[];
|
||||
if (lastblock !== innerDragState.item) {
|
||||
const cloneblock: IBlockType[] = deepCopy(store.getData().block);
|
||||
lastblock = innerDragState.item;
|
||||
newblock = cloneblock.map((v) => {
|
||||
if (v.focus && v.position !== 'static') {
|
||||
v.left = v.left + durX;
|
||||
v.top = v.top + durY;
|
||||
}
|
||||
return v;
|
||||
});
|
||||
} else {
|
||||
newblock = store.getData().block.map((v) => {
|
||||
if (v.focus && v.position !== 'static') {
|
||||
v.left = v.left + durX;
|
||||
v.top = v.top + durY;
|
||||
}
|
||||
return v;
|
||||
});
|
||||
}
|
||||
innerDragState.startX = moveX;
|
||||
innerDragState.startY = moveY;
|
||||
store.setData({ ...store.getData(), block: newblock });
|
||||
}
|
||||
resizerMouseMove(e);
|
||||
if (selectData.selectDiv) {
|
||||
selectRangeMouseMove(e);
|
||||
}
|
||||
};
|
||||
return {
|
||||
onMouseMove,
|
||||
};
|
||||
};
|
||||
export const innerContainerDragUp = function () {
|
||||
const onMouseUp = (e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
wrapperMoveMouseUp();
|
||||
selectRangeMouseUp(e);
|
||||
if (innerDragState.ref && innerDragState.ref.current) {
|
||||
innerDragState.ref.current.style.cursor = 'default';
|
||||
innerDragState.ref.current.style.willChange = 'auto';
|
||||
}
|
||||
resizerMouseUp();
|
||||
if (innerDragState.current) {
|
||||
const endindex = store.getIndex();
|
||||
store.getStoreList().splice(innerDragState.current, endindex - innerDragState.current);
|
||||
store.setIndex(innerDragState.current);
|
||||
}
|
||||
innerDragState.ref = null;
|
||||
innerDragState.isDrag = false;
|
||||
innerDragState.item = null;
|
||||
innerDragState.current = 0;
|
||||
marklineConfig.marklineUnfocus = null;
|
||||
store.forceupdate();
|
||||
};
|
||||
return {
|
||||
onMouseUp,
|
||||
};
|
||||
};
|
||||
30
packages/dooringx-lib/src/core/innerDrag/state.ts
Normal file
30
packages/dooringx-lib/src/core/innerDrag/state.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 12:09:11
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-03-14 12:09:46
|
||||
* @FilePath: \dooring-v2\src\core\innerDrag\state.ts
|
||||
*/
|
||||
import { RefObject } from 'react';
|
||||
|
||||
import { IBlockType } from '../store/storetype';
|
||||
|
||||
export interface innerDragStateType {
|
||||
startX: number;
|
||||
startY: number;
|
||||
item: null | IBlockType;
|
||||
isDrag: boolean;
|
||||
ref: RefObject<HTMLDivElement> | null;
|
||||
current: number;
|
||||
lastClick: null | IBlockType;
|
||||
}
|
||||
|
||||
export const innerDragState: innerDragStateType = {
|
||||
startX: 0,
|
||||
startY: 0,
|
||||
item: null,
|
||||
isDrag: false,
|
||||
ref: null,
|
||||
current: 0,
|
||||
lastClick: null,
|
||||
};
|
||||
79
packages/dooringx-lib/src/core/markline/calcRender.ts
Normal file
79
packages/dooringx-lib/src/core/markline/calcRender.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 04:29:09
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-03-14 11:49:50
|
||||
* @FilePath: \dooring-v2\src\core\markline\calcRender.ts
|
||||
*/
|
||||
import { store } from '../../runtime/store';
|
||||
import { innerDragState } from '../innerDrag/state';
|
||||
import { scaleState } from '../scale/state';
|
||||
import { grideModeRender, gridModeDisplay } from './gridMode';
|
||||
import { switchMarklineDisplay } from './normalMode';
|
||||
import { resizeCurrentCalculate } from './resizeMarkline';
|
||||
import { marklineConfig } from './marklineConfig';
|
||||
export interface LinesTypes {
|
||||
x: number[];
|
||||
y: number[];
|
||||
}
|
||||
|
||||
export function marklineCalRender() {
|
||||
//focus可能好几个,做对比的是拖拽那个
|
||||
const lines: LinesTypes = { x: [], y: [] };
|
||||
if (innerDragState.item?.position === 'static' || innerDragState.item?.position === 'relative') {
|
||||
return lines;
|
||||
}
|
||||
|
||||
const item = innerDragState.item;
|
||||
const ref = innerDragState.ref;
|
||||
|
||||
if (item && ref && ref.current && innerDragState.isDrag) {
|
||||
const focus = store.getData().block.find((v) => v.id === item.id)!;
|
||||
if (!marklineConfig.marklineUnfocus) {
|
||||
marklineConfig.marklineUnfocus = store
|
||||
.getData()
|
||||
.block.filter(
|
||||
(v) => v.focus === false && v.position !== 'static' && v.position !== 'relative'
|
||||
);
|
||||
}
|
||||
const { width, height } = ref.current.getBoundingClientRect();
|
||||
|
||||
// left 和top 被深拷贝过,最新的值需要即时获取
|
||||
const left = focus?.left;
|
||||
const top = focus?.top;
|
||||
if (typeof left !== 'number' || typeof top !== 'number') {
|
||||
return lines; //莫名可能没有这2值
|
||||
}
|
||||
const scale = scaleState.value;
|
||||
const wwidth = width / scale;
|
||||
const wheight = height / scale;
|
||||
|
||||
marklineConfig.marklineUnfocus.forEach((v) => {
|
||||
let l = v?.left;
|
||||
let t = v?.top;
|
||||
if (typeof l !== 'number' || typeof t !== 'number') {
|
||||
console.warn(`${v} component miss top or left`);
|
||||
} else {
|
||||
// 如果不是由外层容器决定的则没有这2属性
|
||||
const w = v.width;
|
||||
const h = v.height;
|
||||
|
||||
// 只有满足要求的才进行push
|
||||
if (marklineConfig.mode === 'normal') {
|
||||
switchMarklineDisplay(l, t, w, h, left, top, wwidth, wheight, lines, focus);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (marklineConfig.mode === 'grid' && marklineConfig.isAbsorb) {
|
||||
gridModeDisplay(left, top, focus);
|
||||
}
|
||||
}
|
||||
|
||||
if (marklineConfig.mode === 'grid') {
|
||||
grideModeRender(lines);
|
||||
}
|
||||
|
||||
resizeCurrentCalculate(lines);
|
||||
|
||||
return lines;
|
||||
}
|
||||
82
packages/dooringx-lib/src/core/markline/gridMode.ts
Normal file
82
packages/dooringx-lib/src/core/markline/gridMode.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 04:29:09
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-03-14 11:49:43
|
||||
* @FilePath: \dooring-v2\src\core\markline\gridMode.ts
|
||||
*/
|
||||
import { store } from '../../runtime/store';
|
||||
import { IBlockType } from '../store/storetype';
|
||||
import { deepCopy } from '../utils';
|
||||
import { LinesTypes } from './calcRender';
|
||||
import { marklineConfig } from './marklineConfig';
|
||||
export function gridModeDisplay(left: number, top: number, focus: IBlockType) {
|
||||
// 有吸附走吸附,只吸top和left,宽高不需要
|
||||
// 无吸附拖拽时显示所有网格。
|
||||
const container = store.getData().container;
|
||||
const containerWidth = container.width;
|
||||
const containerHeight = container.height;
|
||||
const indent = marklineConfig.gridIndent;
|
||||
const diff = marklineConfig.indent;
|
||||
// 网格按照宽高除以下,每间隔一定距离给个线
|
||||
// 横线
|
||||
for (let i = 0; i < containerHeight; i++) {
|
||||
const tmp = i * indent;
|
||||
if (Math.abs(top - tmp) < diff) {
|
||||
focus.top = tmp;
|
||||
break;
|
||||
} else if (tmp + diff > top) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 竖线
|
||||
for (let i = 0; i < containerWidth; i++) {
|
||||
const tmp = i * indent;
|
||||
if (Math.abs(left - tmp) < diff) {
|
||||
focus.left = tmp;
|
||||
break;
|
||||
} else if (tmp + diff > left) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface lastGridStatusProps {
|
||||
lastWidth: number;
|
||||
lastHeight: number;
|
||||
lastIndent: number;
|
||||
lastLine: LinesTypes;
|
||||
}
|
||||
|
||||
export const lastGridStatus: lastGridStatusProps = {
|
||||
lastWidth: 0,
|
||||
lastHeight: 0,
|
||||
lastIndent: 0,
|
||||
lastLine: { x: [], y: [] },
|
||||
};
|
||||
|
||||
export function grideModeRender(lines: LinesTypes) {
|
||||
const container = store.getData().container;
|
||||
const containerWidth = container.width;
|
||||
const containerHeight = container.height;
|
||||
const indent = marklineConfig.gridIndent;
|
||||
if (
|
||||
lastGridStatus.lastWidth === containerWidth &&
|
||||
lastGridStatus.lastHeight === containerHeight &&
|
||||
lastGridStatus.lastIndent === indent
|
||||
) {
|
||||
lines.x = lastGridStatus.lastLine.x;
|
||||
lines.y = lastGridStatus.lastLine.y;
|
||||
} else {
|
||||
for (let i = 0; i < containerWidth; i++) {
|
||||
lines.x.push(i * indent);
|
||||
}
|
||||
for (let i = 0; i < containerHeight; i++) {
|
||||
lines.y.push(i * indent);
|
||||
}
|
||||
lastGridStatus.lastLine = deepCopy(lines);
|
||||
lastGridStatus.lastWidth = containerWidth;
|
||||
lastGridStatus.lastHeight = containerHeight;
|
||||
lastGridStatus.lastIndent = indent;
|
||||
}
|
||||
}
|
||||
80
packages/dooringx-lib/src/core/markline/index.tsx
Normal file
80
packages/dooringx-lib/src/core/markline/index.tsx
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 04:29:09
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-03-14 11:45:22
|
||||
* @FilePath: \dooring-v2\src\core\markline\index.tsx
|
||||
*/
|
||||
import React from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import { IBlockType } from '../store/storetype';
|
||||
import { marklineCalRender } from './calcRender';
|
||||
|
||||
// 主要逻辑需要注入组件内拖拽
|
||||
|
||||
export interface MarklineConfigType {
|
||||
indent: number;
|
||||
isAbsorb: boolean;
|
||||
mode: 'normal' | 'grid';
|
||||
gridIndent: number;
|
||||
resizeIndent: number;
|
||||
marklineUnfocus: null | IBlockType[];
|
||||
}
|
||||
|
||||
// 间隔距离执行吸附
|
||||
export const marklineConfig: MarklineConfigType = {
|
||||
indent: 2,
|
||||
isAbsorb: true,
|
||||
mode: 'normal',
|
||||
gridIndent: 50,
|
||||
resizeIndent: 0,
|
||||
marklineUnfocus: null,
|
||||
};
|
||||
|
||||
export function MarklineX(props: any) {
|
||||
return (
|
||||
<div
|
||||
className="yh-markline"
|
||||
style={{
|
||||
borderTop: '1px dashed black',
|
||||
position: 'absolute',
|
||||
width: '100%',
|
||||
top: props.top,
|
||||
display: props.display,
|
||||
zIndex: 9999,
|
||||
}}
|
||||
></div>
|
||||
);
|
||||
}
|
||||
export function MarklineY(props: any) {
|
||||
return (
|
||||
<div
|
||||
className="yh-markline"
|
||||
style={{
|
||||
borderLeft: '1px dashed black',
|
||||
position: 'absolute',
|
||||
height: '100%',
|
||||
left: props.left,
|
||||
display: props.display,
|
||||
zIndex: 9999,
|
||||
}}
|
||||
></div>
|
||||
);
|
||||
}
|
||||
|
||||
export function NormalMarkLineRender() {
|
||||
const lines = marklineCalRender();
|
||||
const render = useMemo(() => {
|
||||
return (
|
||||
<>
|
||||
{lines.x.map((v, i) => {
|
||||
return <MarklineX key={i} top={v}></MarklineX>;
|
||||
})}
|
||||
{lines.y.map((v, i) => {
|
||||
return <MarklineY key={i} left={v}></MarklineY>;
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}, [lines]);
|
||||
return <>{render}</>;
|
||||
}
|
||||
27
packages/dooringx-lib/src/core/markline/marklineConfig.ts
Normal file
27
packages/dooringx-lib/src/core/markline/marklineConfig.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 11:49:13
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-03-14 11:49:31
|
||||
* @FilePath: \dooring-v2\src\core\markline\marklineConfig.ts
|
||||
*/
|
||||
import { IBlockType } from '../store/storetype';
|
||||
|
||||
export interface MarklineConfigType {
|
||||
indent: number;
|
||||
isAbsorb: boolean;
|
||||
mode: 'normal' | 'grid';
|
||||
gridIndent: number;
|
||||
resizeIndent: number;
|
||||
marklineUnfocus: null | IBlockType[];
|
||||
}
|
||||
|
||||
// 间隔距离执行吸附
|
||||
export const marklineConfig: MarklineConfigType = {
|
||||
indent: 2,
|
||||
isAbsorb: true,
|
||||
mode: 'normal',
|
||||
gridIndent: 50,
|
||||
resizeIndent: 0,
|
||||
marklineUnfocus: null,
|
||||
};
|
||||
279
packages/dooringx-lib/src/core/markline/normalMode.ts
Normal file
279
packages/dooringx-lib/src/core/markline/normalMode.ts
Normal file
@@ -0,0 +1,279 @@
|
||||
import { IBlockType } from '../store/storetype';
|
||||
import { LinesTypes } from './calcRender';
|
||||
import { marklineConfig } from './marklineConfig';
|
||||
export function switchMarklineDisplay(
|
||||
l: number,
|
||||
t: number,
|
||||
w: number | string | undefined,
|
||||
h: number | string | undefined,
|
||||
left: number,
|
||||
top: number,
|
||||
width: number,
|
||||
height: number,
|
||||
lines: LinesTypes,
|
||||
focus: IBlockType
|
||||
) {
|
||||
// 做吸附只能选择一个接近的吸,所以只匹配一个即可。
|
||||
// 头对头
|
||||
if (marklineConfig.isAbsorb) {
|
||||
if (Math.abs(top - t) < marklineConfig.indent) {
|
||||
lines.x.push(t);
|
||||
focus.top = t;
|
||||
}
|
||||
// 中对头
|
||||
else if (Math.abs(top + height / 2 - t) < marklineConfig.indent) {
|
||||
lines.x.push(t);
|
||||
focus.top = t - height / 2;
|
||||
}
|
||||
// 尾对头
|
||||
else if (Math.abs(top + height - t) < marklineConfig.indent) {
|
||||
lines.x.push(t);
|
||||
focus.top = t - height;
|
||||
} else if (h && typeof h === 'number') {
|
||||
// 头对中
|
||||
if (Math.abs(t + h / 2 - top) < marklineConfig.indent) {
|
||||
lines.x.push(t + h / 2);
|
||||
focus.top = t + h / 2;
|
||||
}
|
||||
// 中对中
|
||||
else if (Math.abs(t + h / 2 - top - height / 2) < marklineConfig.indent) {
|
||||
lines.x.push(t + h / 2);
|
||||
focus.top = t + h / 2 - height / 2;
|
||||
}
|
||||
// 尾对中
|
||||
else if (Math.abs(t + h / 2 - top - height) < marklineConfig.indent) {
|
||||
lines.x.push(t + h / 2);
|
||||
focus.top = t + h / 2 - height;
|
||||
}
|
||||
// 头对尾
|
||||
else if (Math.abs((t + h - top) / 2) < marklineConfig.indent) {
|
||||
lines.x.push(t + h);
|
||||
focus.top = t + h;
|
||||
}
|
||||
// 中对尾
|
||||
else if (Math.abs(t + h - top - height / 2) < marklineConfig.indent) {
|
||||
lines.x.push(t + h);
|
||||
focus.top = t + h - height / 2;
|
||||
}
|
||||
// 尾对尾
|
||||
else if (Math.abs((t + h - top - height) / 2) < marklineConfig.indent) {
|
||||
lines.x.push(t + h);
|
||||
focus.top = t + h - height;
|
||||
}
|
||||
}
|
||||
// x轴方向
|
||||
// 头对头
|
||||
if (Math.abs(left - l) < marklineConfig.indent) {
|
||||
lines.y.push(l);
|
||||
focus.left = l;
|
||||
}
|
||||
// 中对头
|
||||
else if (Math.abs(left + width / 2 - l) < marklineConfig.indent) {
|
||||
lines.y.push(l);
|
||||
focus.left = l - width / 2;
|
||||
}
|
||||
// 尾对头
|
||||
else if (Math.abs(left + width - l) < marklineConfig.indent) {
|
||||
lines.y.push(l);
|
||||
focus.left = l - width;
|
||||
} else if (w && typeof w === 'number') {
|
||||
// 头对中
|
||||
if (Math.abs(l + w / 2 - left) < marklineConfig.indent) {
|
||||
lines.y.push(l + w / 2);
|
||||
focus.left = l + w / 2;
|
||||
}
|
||||
// 中对中
|
||||
else if (Math.abs(l + w / 2 - left - width / 2) < marklineConfig.indent) {
|
||||
lines.y.push(l + w / 2);
|
||||
focus.left = l + w / 2 - width / 2;
|
||||
}
|
||||
// 尾对中
|
||||
else if (Math.abs(l + w / 2 - left - width) < marklineConfig.indent) {
|
||||
lines.y.push(l + w / 2);
|
||||
focus.left = l + w / 2 - width;
|
||||
}
|
||||
// 头对尾
|
||||
else if (Math.abs((l + w - left) / 2) < marklineConfig.indent) {
|
||||
lines.y.push(l + w);
|
||||
focus.left = l + w;
|
||||
}
|
||||
// 中对尾
|
||||
else if (Math.abs(l + w - left - width / 2) < marklineConfig.indent) {
|
||||
lines.y.push(l + w);
|
||||
focus.left = l + w - width / 2;
|
||||
}
|
||||
// 尾对尾
|
||||
else if (Math.abs((l + w - left - width) / 2) < marklineConfig.indent) {
|
||||
lines.y.push(l + w);
|
||||
focus.left = l + w - width;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (Math.abs(top - t) < marklineConfig.indent) {
|
||||
lines.x.push(t);
|
||||
}
|
||||
// 中对头
|
||||
if (Math.abs(top + height / 2 - t) < marklineConfig.indent) {
|
||||
lines.x.push(t);
|
||||
}
|
||||
// 尾对头
|
||||
if (Math.abs(top + height - t) < marklineConfig.indent) {
|
||||
lines.x.push(t);
|
||||
}
|
||||
if (h && typeof h === 'number') {
|
||||
// 头对中
|
||||
if (Math.abs(t + h / 2 - top) < marklineConfig.indent) {
|
||||
lines.x.push(t + h / 2);
|
||||
}
|
||||
// 中对中
|
||||
if (Math.abs(t + h / 2 - top - height / 2) < marklineConfig.indent) {
|
||||
lines.x.push(t + h / 2);
|
||||
}
|
||||
// 尾对中
|
||||
if (Math.abs(t + h / 2 - top - height) < marklineConfig.indent) {
|
||||
lines.x.push(t + h / 2);
|
||||
}
|
||||
// 头对尾
|
||||
if (Math.abs((t + h - top) / 2) < marklineConfig.indent) {
|
||||
lines.x.push(t + h);
|
||||
}
|
||||
// 中对尾
|
||||
if (Math.abs(t + h - top - height / 2) < marklineConfig.indent) {
|
||||
lines.x.push(t + h);
|
||||
}
|
||||
// 尾对尾
|
||||
if (Math.abs((t + h - top - height) / 2) < marklineConfig.indent) {
|
||||
lines.x.push(t + h);
|
||||
}
|
||||
}
|
||||
// x轴方向
|
||||
// 头对头
|
||||
if (Math.abs(left - l) < marklineConfig.indent) {
|
||||
lines.y.push(l);
|
||||
}
|
||||
// 中对头
|
||||
if (Math.abs(left + width / 2 - l) < marklineConfig.indent) {
|
||||
lines.y.push(l);
|
||||
}
|
||||
// 尾对头
|
||||
if (Math.abs(left + width - l) < marklineConfig.indent) {
|
||||
lines.y.push(l);
|
||||
}
|
||||
if (w && typeof w === 'number') {
|
||||
// 头对中
|
||||
if (Math.abs(l + w / 2 - left) < marklineConfig.indent) {
|
||||
lines.y.push(l + w / 2);
|
||||
}
|
||||
// 中对中
|
||||
if (Math.abs(l + w / 2 - left - width / 2) < marklineConfig.indent) {
|
||||
lines.y.push(l + w / 2);
|
||||
}
|
||||
// 尾对中
|
||||
if (Math.abs(l + w / 2 - left - width) < marklineConfig.indent) {
|
||||
lines.y.push(l + w / 2);
|
||||
}
|
||||
// 头对尾
|
||||
if (Math.abs((l + w - left) / 2) < marklineConfig.indent) {
|
||||
lines.y.push(l + w);
|
||||
}
|
||||
// 中对尾
|
||||
if (Math.abs(l + w - left - width / 2) < marklineConfig.indent) {
|
||||
lines.y.push(l + w);
|
||||
}
|
||||
// 尾对尾
|
||||
if (Math.abs((l + w - left - width) / 2) < marklineConfig.indent) {
|
||||
lines.y.push(l + w);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function switchMarklineResizeDisplay(
|
||||
l: number,
|
||||
t: number,
|
||||
w: number | string | undefined,
|
||||
h: number | string | undefined,
|
||||
left: number,
|
||||
top: number,
|
||||
width: number,
|
||||
height: number,
|
||||
lines: LinesTypes
|
||||
) {
|
||||
// 头对头
|
||||
if (Math.abs(top - t) <= marklineConfig.resizeIndent) {
|
||||
lines.x.push(t);
|
||||
}
|
||||
// 中对头
|
||||
if (Math.abs(top + height / 2 - t) <= marklineConfig.resizeIndent) {
|
||||
lines.x.push(t);
|
||||
}
|
||||
// 尾对头
|
||||
if (Math.abs(top + height - t) <= marklineConfig.resizeIndent) {
|
||||
lines.x.push(t);
|
||||
}
|
||||
if (h && typeof h === 'number') {
|
||||
// 头对中
|
||||
if (Math.abs(t + h / 2 - top) <= marklineConfig.resizeIndent) {
|
||||
lines.x.push(t + h / 2);
|
||||
}
|
||||
// 中对中
|
||||
if (Math.abs(t + h / 2 - top - height / 2) <= marklineConfig.resizeIndent) {
|
||||
lines.x.push(t + h / 2);
|
||||
}
|
||||
// 尾对中
|
||||
if (Math.abs(t + h / 2 - top - height) <= marklineConfig.resizeIndent) {
|
||||
lines.x.push(t + h / 2);
|
||||
}
|
||||
// 头对尾
|
||||
if (Math.abs((t + h - top) / 2) <= marklineConfig.resizeIndent) {
|
||||
lines.x.push(t + h);
|
||||
}
|
||||
// 中对尾
|
||||
if (Math.abs(t + h - top - height / 2) <= marklineConfig.resizeIndent) {
|
||||
lines.x.push(t + h);
|
||||
}
|
||||
// 尾对尾
|
||||
if (Math.abs((t + h - top - height) / 2) <= marklineConfig.resizeIndent) {
|
||||
lines.x.push(t + h);
|
||||
}
|
||||
}
|
||||
// x轴方向
|
||||
// 头对头
|
||||
if (Math.abs(left - l) <= marklineConfig.resizeIndent) {
|
||||
lines.y.push(l);
|
||||
}
|
||||
// 中对头
|
||||
if (Math.abs(left + width / 2 - l) <= marklineConfig.resizeIndent) {
|
||||
lines.y.push(l);
|
||||
}
|
||||
// 尾对头
|
||||
if (Math.abs(left + width - l) <= marklineConfig.resizeIndent) {
|
||||
lines.y.push(l);
|
||||
}
|
||||
if (w && typeof w === 'number') {
|
||||
// 头对中
|
||||
if (Math.abs(l + w / 2 - left) <= marklineConfig.resizeIndent) {
|
||||
lines.y.push(l + w / 2);
|
||||
}
|
||||
// 中对中
|
||||
if (Math.abs(l + w / 2 - left - width / 2) <= marklineConfig.resizeIndent) {
|
||||
lines.y.push(l + w / 2);
|
||||
}
|
||||
// 尾对中
|
||||
if (Math.abs(l + w / 2 - left - width) <= marklineConfig.resizeIndent) {
|
||||
lines.y.push(l + w / 2);
|
||||
}
|
||||
// 头对尾
|
||||
if (Math.abs((l + w - left) / 2) <= marklineConfig.resizeIndent) {
|
||||
lines.y.push(l + w);
|
||||
}
|
||||
// 中对尾
|
||||
if (Math.abs(l + w - left - width / 2) <= marklineConfig.resizeIndent) {
|
||||
lines.y.push(l + w);
|
||||
}
|
||||
// 尾对尾
|
||||
if (Math.abs((l + w - left - width) / 2) <= marklineConfig.resizeIndent) {
|
||||
lines.y.push(l + w);
|
||||
}
|
||||
}
|
||||
}
|
||||
41
packages/dooringx-lib/src/core/markline/resizeMarkline.ts
Normal file
41
packages/dooringx-lib/src/core/markline/resizeMarkline.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-02-18 11:52:38
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-03-14 11:49:58
|
||||
* @FilePath: \dooring-v2\src\core\markline\resizeMarkline.ts
|
||||
*/
|
||||
|
||||
import { store } from '../../runtime/store';
|
||||
import { resizeState } from '../resizeHandler';
|
||||
import { scaleState } from '../scale/state';
|
||||
import { LinesTypes } from './calcRender';
|
||||
import { switchMarklineResizeDisplay } from './normalMode';
|
||||
import { marklineConfig } from './marklineConfig';
|
||||
|
||||
export function resizeCurrentCalculate(lines: LinesTypes) {
|
||||
const id = resizeState.item?.id;
|
||||
|
||||
if (resizeState.ref?.current && id) {
|
||||
const newblock = store.getData().block;
|
||||
const unfocus = newblock.filter((v) => v.id !== id);
|
||||
const { width, height } = resizeState.ref.current.getBoundingClientRect();
|
||||
const focus = store.getData().block.find((v) => v.id === id)!;
|
||||
const { left, top } = focus;
|
||||
const scale = scaleState.value;
|
||||
const wwidth = width / scale;
|
||||
const wheight = height / scale;
|
||||
unfocus.forEach((v) => {
|
||||
const { left: l, top: t } = v;
|
||||
// 如果不是由外层容器决定的则没有这2属性
|
||||
const w = v.width;
|
||||
const h = v.height;
|
||||
const ww = w && typeof w === 'number' ? w / scale : w;
|
||||
const wh = h && typeof h === 'number' ? h / scale : h;
|
||||
// 只有满足要求的才进行push
|
||||
if (marklineConfig.mode === 'normal') {
|
||||
switchMarklineResizeDisplay(l, t, ww, wh, left, top, wwidth, wheight, lines);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-09 15:19:36
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-03-14 04:52:57
|
||||
* @FilePath: \dooring-v2\src\core\resizeHandler\containerResizer.ts
|
||||
*/
|
||||
|
||||
import { store } from '../../runtime/store';
|
||||
import { scaleState } from '../scale/state';
|
||||
import { IStoreData } from '../store/storetype';
|
||||
import { deepCopy } from '../utils';
|
||||
|
||||
export const containerState = {
|
||||
isDrag: false,
|
||||
startY: 0,
|
||||
startIndex: 0,
|
||||
};
|
||||
|
||||
export const containerResizer = {
|
||||
onMousedown: (e: React.MouseEvent) => {
|
||||
containerState.isDrag = true;
|
||||
containerState.startY = e.clientY;
|
||||
containerState.startIndex = store.getIndex();
|
||||
},
|
||||
onMouseMove: (e: React.MouseEvent) => {
|
||||
if (containerState.isDrag) {
|
||||
const scale = scaleState.value;
|
||||
const diff = ((e.clientY - containerState.startY) / scale) * 2;
|
||||
const clonedata: IStoreData = deepCopy(store.getData());
|
||||
const height = clonedata.container.height;
|
||||
let tmpHeight = height + diff < 600 ? 600 : height + diff;
|
||||
clonedata.container.height = tmpHeight;
|
||||
store.setData(clonedata);
|
||||
containerState.startY = e.clientY;
|
||||
}
|
||||
},
|
||||
onMouseUp: () => {
|
||||
if (containerState.isDrag) {
|
||||
containerState.isDrag = false;
|
||||
const endIndex = store.getIndex();
|
||||
store.getStoreList().splice(containerState.startIndex, endIndex - containerState.startIndex);
|
||||
store.setIndex(containerState.startIndex);
|
||||
}
|
||||
},
|
||||
};
|
||||
204
packages/dooringx-lib/src/core/resizeHandler/index.tsx
Normal file
204
packages/dooringx-lib/src/core/resizeHandler/index.tsx
Normal file
@@ -0,0 +1,204 @@
|
||||
import { store } from '../../runtime/store';
|
||||
import { RefObject, useMemo } from 'react';
|
||||
import { scaleState } from '../scale/state';
|
||||
import { IBlockType } from '../store/storetype';
|
||||
import { deepCopy } from '../utils';
|
||||
import React from 'react';
|
||||
import classnames from 'classnames';
|
||||
import styles from '../../index.less';
|
||||
interface BlockResizerProps {
|
||||
data: IBlockType;
|
||||
rect: RefObject<HTMLDivElement>;
|
||||
}
|
||||
interface resizeStateType {
|
||||
startX: number;
|
||||
startY: number;
|
||||
item: null | IBlockType;
|
||||
isResize: boolean;
|
||||
direction: directionType;
|
||||
ref: RefObject<HTMLDivElement> | null;
|
||||
current: number;
|
||||
}
|
||||
type directionType =
|
||||
| 'top'
|
||||
| 'topleft'
|
||||
| 'topright'
|
||||
| 'left'
|
||||
| 'bottomleft'
|
||||
| 'bottom'
|
||||
| 'bottomright'
|
||||
| 'right';
|
||||
export const resizeState: resizeStateType = {
|
||||
startX: 0,
|
||||
startY: 0,
|
||||
item: null,
|
||||
isResize: false,
|
||||
direction: 'bottom',
|
||||
ref: null,
|
||||
current: 0,
|
||||
};
|
||||
|
||||
const onMouseDown = (
|
||||
e: React.MouseEvent,
|
||||
direction: directionType,
|
||||
item: IBlockType,
|
||||
ref: RefObject<HTMLDivElement>
|
||||
) => {
|
||||
e.stopPropagation();
|
||||
resizeState.isResize = true;
|
||||
resizeState.item = item;
|
||||
resizeState.startX = e.clientX;
|
||||
resizeState.startY = e.clientY;
|
||||
resizeState.direction = direction;
|
||||
resizeState.ref = ref;
|
||||
resizeState.current = store.getIndex();
|
||||
};
|
||||
|
||||
export const resizerMouseUp = () => {
|
||||
resizeState.isResize = false;
|
||||
resizeState.item = null;
|
||||
if (resizeState.current) {
|
||||
const endindex = store.getIndex();
|
||||
store.getStoreList().splice(resizeState.current, endindex - resizeState.current);
|
||||
store.setIndex(resizeState.current);
|
||||
}
|
||||
resizeState.current = 0;
|
||||
};
|
||||
const changePosition = (v: IBlockType, durX: number, durY: number) => {
|
||||
const direction = resizeState.direction;
|
||||
const { width, height } = resizeState.ref!.current!.getBoundingClientRect();
|
||||
const scale = scaleState.value;
|
||||
let tmpy = height / scale - durY;
|
||||
let tmpx = width / scale - durX;
|
||||
switch (direction) {
|
||||
case 'right':
|
||||
v.width = width / scale + durX;
|
||||
break;
|
||||
case 'bottom':
|
||||
v.height = height / scale + durY;
|
||||
break;
|
||||
case 'left':
|
||||
v.left = width / scale > 0 ? v.left + durX : v.left;
|
||||
v.width = tmpx > 0 ? tmpx : 0;
|
||||
break;
|
||||
case 'top':
|
||||
v.top = height / scale > 0 ? v.top + durY : v.top;
|
||||
v.height = tmpy > 0 ? tmpy : 0;
|
||||
break;
|
||||
case 'bottomright':
|
||||
v.width = width / scale + durX;
|
||||
v.height = height / scale + durY;
|
||||
break;
|
||||
case 'topright':
|
||||
v.width = width / scale + durX;
|
||||
v.top = height / scale > 0 ? v.top + durY : v.top;
|
||||
v.height = tmpy > 0 ? tmpy : 0;
|
||||
break;
|
||||
case 'topleft':
|
||||
v.top = height / scale > 0 ? v.top + durY : v.top;
|
||||
v.height = tmpy > 0 ? tmpy : 0;
|
||||
v.left = width / scale > 0 ? v.left + durX : v.left;
|
||||
v.width = tmpx > 0 ? tmpx : 0;
|
||||
break;
|
||||
case 'bottomleft':
|
||||
v.left = width / scale > 0 ? v.left + durX : v.left;
|
||||
v.width = tmpx > 0 ? tmpx : 0;
|
||||
v.height = height / scale + durY;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
export const resizerMouseMove = (e: React.MouseEvent) => {
|
||||
//根据direction修改位置
|
||||
if (resizeState.isResize && resizeState.item) {
|
||||
let { clientX: moveX, clientY: moveY } = e;
|
||||
const { startX, startY } = resizeState;
|
||||
const scale = scaleState.value;
|
||||
let durX = (moveX - startX) / scale;
|
||||
let durY = (moveY - startY) / scale;
|
||||
const clonedata = deepCopy(store.getData());
|
||||
const newblock: IBlockType[] = clonedata.block.map((v: IBlockType) => {
|
||||
if (v.id === resizeState.item!.id) {
|
||||
changePosition(v, durX, durY);
|
||||
}
|
||||
return v;
|
||||
});
|
||||
resizeState.startX = moveX;
|
||||
resizeState.startY = moveY;
|
||||
store.setData({ ...clonedata, block: newblock });
|
||||
}
|
||||
};
|
||||
|
||||
export function BlockResizer(props: BlockResizerProps) {
|
||||
const render = useMemo(() => {
|
||||
if (props.data.focus && props.data.resize) {
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={classnames(styles.resizepoint, styles.top)}
|
||||
onMouseDown={(e) => {
|
||||
onMouseDown(e, 'top', props.data, props.rect);
|
||||
}}
|
||||
onMouseUp={resizerMouseUp}
|
||||
></div>
|
||||
<div
|
||||
className={classnames(styles.resizepoint, styles.topleft)}
|
||||
onMouseDown={(e) => {
|
||||
onMouseDown(e, 'topleft', props.data, props.rect);
|
||||
}}
|
||||
onMouseUp={resizerMouseUp}
|
||||
></div>
|
||||
<div
|
||||
className={classnames(styles.resizepoint, styles.left)}
|
||||
onMouseDown={(e) => {
|
||||
onMouseDown(e, 'left', props.data, props.rect);
|
||||
}}
|
||||
onMouseUp={resizerMouseUp}
|
||||
></div>
|
||||
<div
|
||||
className={classnames(styles.resizepoint, styles.topright)}
|
||||
onMouseDown={(e) => {
|
||||
onMouseDown(e, 'topright', props.data, props.rect);
|
||||
}}
|
||||
onMouseUp={resizerMouseUp}
|
||||
></div>
|
||||
<div
|
||||
className={classnames(styles.resizepoint, styles.bottomleft)}
|
||||
onMouseDown={(e) => {
|
||||
onMouseDown(e, 'bottomleft', props.data, props.rect);
|
||||
}}
|
||||
onMouseUp={resizerMouseUp}
|
||||
></div>
|
||||
<div
|
||||
className={classnames(styles.resizepoint, styles.bottom)}
|
||||
onMouseDown={(e) => {
|
||||
onMouseDown(e, 'bottom', props.data, props.rect);
|
||||
}}
|
||||
onMouseUp={resizerMouseUp}
|
||||
></div>
|
||||
<div
|
||||
className={classnames(styles.resizepoint, styles.right)}
|
||||
onMouseDown={(e) => {
|
||||
onMouseDown(e, 'right', props.data, props.rect);
|
||||
}}
|
||||
onMouseUp={resizerMouseUp}
|
||||
></div>
|
||||
<div
|
||||
className={classnames(styles.resizepoint, styles.bottomright)}
|
||||
onMouseDown={(e) => {
|
||||
onMouseDown(e, 'bottomright', props.data, props.rect);
|
||||
}}
|
||||
onMouseUp={resizerMouseUp}
|
||||
></div>
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [props.data.focus, props.data.resize]);
|
||||
|
||||
return <>{render}</>;
|
||||
}
|
||||
5
packages/dooringx-lib/src/core/scale/cancel.ts
Normal file
5
packages/dooringx-lib/src/core/scale/cancel.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { unmountContextMenu } from '../contextMenu';
|
||||
|
||||
export const scaleCancelFn = () => {
|
||||
unmountContextMenu();
|
||||
};
|
||||
54
packages/dooringx-lib/src/core/scale/index.ts
Normal file
54
packages/dooringx-lib/src/core/scale/index.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 04:29:09
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-04-05 22:18:43
|
||||
* @FilePath: \dooringv2\src\core\scale\index.ts
|
||||
*/
|
||||
import { store } from '../../runtime/store';
|
||||
import Store from '../store';
|
||||
import { scaleCancelFn } from './cancel';
|
||||
import { scaleState } from './state';
|
||||
|
||||
export const onWheelEvent = {
|
||||
onWheel: (e: React.WheelEvent<HTMLDivElement>) => {
|
||||
const dom = document.querySelector('.ant-modal-mask');
|
||||
if (dom) {
|
||||
//出现弹窗禁止滚动
|
||||
return;
|
||||
}
|
||||
if (e.deltaY > 0) {
|
||||
scaleCancelFn();
|
||||
if (scaleState.value < scaleState.maxValue) {
|
||||
scaleState.value = scaleState.value + 0.1;
|
||||
store.forceUpdate();
|
||||
}
|
||||
} else {
|
||||
scaleCancelFn();
|
||||
//往上滚缩小
|
||||
if (scaleState.value > scaleState.minValue) {
|
||||
scaleState.value = scaleState.value - 0.1;
|
||||
store.forceUpdate();
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export const scaleFn = {
|
||||
increase(number: number = 0.1, store: Store) {
|
||||
if (scaleState.value < scaleState.maxValue) {
|
||||
scaleCancelFn();
|
||||
scaleState.value = scaleState.value + number;
|
||||
store.forceUpdate();
|
||||
}
|
||||
return scaleState.value;
|
||||
},
|
||||
decrease(number: number = 0.1, store: Store) {
|
||||
scaleCancelFn();
|
||||
if (scaleState.value > scaleState.minValue) {
|
||||
scaleState.value = scaleState.value - number;
|
||||
store.forceUpdate();
|
||||
}
|
||||
return scaleState.value;
|
||||
},
|
||||
};
|
||||
12
packages/dooringx-lib/src/core/scale/state.ts
Normal file
12
packages/dooringx-lib/src/core/scale/state.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-07-07 10:28:09
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-07 23:16:31
|
||||
* @FilePath: \DooringV2\packages\dooringx-lib\src\core\scale\state.ts
|
||||
*/
|
||||
export const scaleState = {
|
||||
value: 0.8,
|
||||
maxValue: 1.3,
|
||||
minValue: 0.4,
|
||||
};
|
||||
100
packages/dooringx-lib/src/core/selectRange/index.ts
Normal file
100
packages/dooringx-lib/src/core/selectRange/index.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import { store } from '../../runtime/store';
|
||||
import { IStoreData } from '../store/storetype';
|
||||
import { deepCopy } from '../utils';
|
||||
import { focusState } from '../focusHandler/state';
|
||||
import { scaleState } from '../scale/state';
|
||||
import style from '../../index.less';
|
||||
export interface SelectDataProps {
|
||||
selectDiv: HTMLDivElement | null;
|
||||
posx: number;
|
||||
posy: number;
|
||||
startX: number;
|
||||
startY: number;
|
||||
}
|
||||
|
||||
export const selectData: SelectDataProps = {
|
||||
selectDiv: null,
|
||||
posx: 0,
|
||||
posy: 0,
|
||||
startX: 0,
|
||||
startY: 0,
|
||||
};
|
||||
|
||||
export function selectRangeMouseDown(e: React.MouseEvent) {
|
||||
if (!selectData.selectDiv) {
|
||||
selectData.selectDiv = document.createElement('div');
|
||||
}
|
||||
if (selectData.selectDiv) {
|
||||
selectData.startX = e.nativeEvent.offsetX;
|
||||
selectData.startY = e.nativeEvent.offsetY;
|
||||
selectData.posx = e.clientX;
|
||||
selectData.posy = e.clientY;
|
||||
selectData.selectDiv.className = style.yhTempDiv;
|
||||
selectData.selectDiv.style.left = e.clientX + 'px';
|
||||
selectData.selectDiv.style.top = e.clientY + 'px';
|
||||
selectData.selectDiv.style.position = 'fixed';
|
||||
document.body.appendChild(selectData.selectDiv);
|
||||
selectData.selectDiv.onmouseup = (e) => selectRangeMouseUp(e);
|
||||
selectData.selectDiv.onmousemove = (e) => selectRangeMouseMove(e);
|
||||
}
|
||||
}
|
||||
|
||||
export function selectRangeMouseMove(ev: React.MouseEvent | MouseEvent) {
|
||||
if (selectData.selectDiv) {
|
||||
selectData.selectDiv.style.left = Math.min(ev.clientX, selectData.posx) + 'px';
|
||||
selectData.selectDiv.style.top = Math.min(ev.clientY, selectData.posy) + 'px';
|
||||
selectData.selectDiv.style.width = Math.abs(selectData.posx - ev.clientX) + 'px';
|
||||
selectData.selectDiv.style.height = Math.abs(selectData.posy - ev.clientY) + 'px';
|
||||
}
|
||||
}
|
||||
|
||||
function typeGuard(e: React.MouseEvent | MouseEvent): e is React.MouseEvent {
|
||||
return !(e instanceof Event);
|
||||
}
|
||||
|
||||
function selectFocus(left: number, top: number, width: number, height: number) {
|
||||
if (width === 0 || height === 0) {
|
||||
return;
|
||||
}
|
||||
const clonedata: IStoreData = deepCopy(store.getData());
|
||||
const blocks = clonedata.block;
|
||||
let change = false;
|
||||
const maxleft = left + width;
|
||||
const maxtop = top + height;
|
||||
blocks.forEach((v) => {
|
||||
const l = v.left;
|
||||
const t = v.top;
|
||||
if (l >= left && l <= maxleft && t >= top && t <= maxtop) {
|
||||
change = true;
|
||||
v.focus = true;
|
||||
focusState.blocks.push(v);
|
||||
}
|
||||
});
|
||||
if (change) {
|
||||
store.setData(clonedata);
|
||||
}
|
||||
}
|
||||
|
||||
export function selectRangeMouseUp(e: React.MouseEvent | MouseEvent) {
|
||||
if (selectData.selectDiv) {
|
||||
// 这里需要判定区域
|
||||
// 如果是react触发 ,left和top就是起始值和终止值的最小值
|
||||
// 如果是原生触发,left和top是起始点减去其宽高
|
||||
let left = 0;
|
||||
let top = 0;
|
||||
const { width, height } = selectData.selectDiv.getBoundingClientRect();
|
||||
const scale = scaleState.value;
|
||||
const wwidth = width / scale;
|
||||
const wheight = height / scale;
|
||||
if (typeGuard(e)) {
|
||||
left = Math.min(e.nativeEvent.offsetX, selectData.startX);
|
||||
top = Math.min(e.nativeEvent.offsetY, selectData.startY);
|
||||
} else {
|
||||
left = selectData.startX - wwidth;
|
||||
top = selectData.startY - wheight;
|
||||
}
|
||||
selectFocus(left, top, wwidth, wheight);
|
||||
selectData.selectDiv.parentNode!.removeChild(selectData.selectDiv);
|
||||
selectData.selectDiv = null;
|
||||
}
|
||||
}
|
||||
143
packages/dooringx-lib/src/core/store/index.ts
Normal file
143
packages/dooringx-lib/src/core/store/index.ts
Normal file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 04:29:09
|
||||
* @LastEditors: Please set LastEditors
|
||||
* @LastEditTime: 2021-06-19 17:07:10
|
||||
* @FilePath: \dooringv2\packages\dooring-v2-lib\src\core\store\index.ts
|
||||
*/
|
||||
import { IStoreData } from './storetype';
|
||||
import { storeChangerState } from '../storeChanger/state';
|
||||
|
||||
export const initialData: IStoreData = {
|
||||
container: {
|
||||
width: 375,
|
||||
height: 600,
|
||||
},
|
||||
block: [],
|
||||
modalMap: {},
|
||||
dataSource: {},
|
||||
globalState: {},
|
||||
};
|
||||
|
||||
class Store {
|
||||
constructor(
|
||||
public storeDataList: IStoreData[] = [initialData],
|
||||
public listeners: Array<Function> = [],
|
||||
public current: number = 0,
|
||||
public forceupdate: Function = () => {}
|
||||
) {}
|
||||
|
||||
getData() {
|
||||
return this.storeDataList[this.current];
|
||||
}
|
||||
getStoreList() {
|
||||
return this.storeDataList;
|
||||
}
|
||||
|
||||
getListeners() {
|
||||
return this.listeners;
|
||||
}
|
||||
|
||||
getIndex() {
|
||||
return this.current;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* 注意重置需要注册事件
|
||||
* @param {IStoreData[]} initData
|
||||
* @param {boolean} [check=false]
|
||||
* @memberof Store
|
||||
*/
|
||||
resetToInitData(initData: IStoreData[], check = false) {
|
||||
this.storeDataList = initData;
|
||||
this.current = 0;
|
||||
//如果是编辑模式,需要修改
|
||||
if (storeChangerState.modalEditName !== '' && check) {
|
||||
storeChangerState.modalEditName = '';
|
||||
}
|
||||
this.emit();
|
||||
}
|
||||
/**
|
||||
*
|
||||
* 注意重置需要注册事件
|
||||
* @param {IStoreData[]} initData
|
||||
* @param {number} current
|
||||
* @param {boolean} [check=false]
|
||||
* @memberof Store
|
||||
*/
|
||||
resetToCustomData(initData: IStoreData[], current: number, check = false) {
|
||||
this.storeDataList = initData;
|
||||
this.current = current;
|
||||
//如果是编辑模式,需要修改
|
||||
if (storeChangerState.modalEditName !== '' && check) {
|
||||
storeChangerState.modalEditName = '';
|
||||
}
|
||||
this.emit();
|
||||
}
|
||||
resetListeners() {
|
||||
this.listeners = [];
|
||||
}
|
||||
|
||||
replaceList(list: IStoreData[]) {
|
||||
this.storeDataList = list;
|
||||
}
|
||||
|
||||
setForceUpdate(fn: Function) {
|
||||
this.forceupdate = fn;
|
||||
}
|
||||
forceUpdate() {
|
||||
this.forceupdate();
|
||||
}
|
||||
|
||||
setIndex(num: number) {
|
||||
this.current = num;
|
||||
}
|
||||
|
||||
redo() {
|
||||
const maxLength = this.storeDataList.length;
|
||||
if (this.current + 1 < maxLength) {
|
||||
this.current = this.current + 1;
|
||||
this.emit();
|
||||
}
|
||||
}
|
||||
|
||||
undo() {
|
||||
if (this.current > 0) {
|
||||
this.current = this.current - 1;
|
||||
this.emit();
|
||||
}
|
||||
}
|
||||
|
||||
cleanRedundant(index: number) {
|
||||
this.storeDataList = this.storeDataList.slice(0, index + 1);
|
||||
}
|
||||
|
||||
setData(data: IStoreData) {
|
||||
// 如果current不是最后那个,说明后面的被undo过的,如果要新增,那么需要清除之前的
|
||||
let flag = true;
|
||||
if (this.current + 1 !== this.storeDataList.length) {
|
||||
this.cleanRedundant(this.current);
|
||||
flag = false;
|
||||
}
|
||||
this.current = this.current + 1;
|
||||
this.storeDataList[this.current] = data;
|
||||
if (flag && this.current + 1 !== this.storeDataList.length) {
|
||||
this.storeDataList.length = this.current + 1;
|
||||
}
|
||||
|
||||
this.emit();
|
||||
}
|
||||
|
||||
emit() {
|
||||
this.listeners.forEach((fn) => {
|
||||
fn(this.getData());
|
||||
});
|
||||
}
|
||||
|
||||
subscribe(listener: Function) {
|
||||
this.listeners.push(listener);
|
||||
return () => (this.listeners = this.listeners.filter((v) => v !== listener));
|
||||
}
|
||||
}
|
||||
|
||||
export default Store;
|
||||
48
packages/dooringx-lib/src/core/store/storetype.ts
Normal file
48
packages/dooringx-lib/src/core/store/storetype.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 04:29:09
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-07 17:00:07
|
||||
* @FilePath: \DooringV2\packages\dooringx-lib\src\core\store\storetype.ts
|
||||
*/
|
||||
|
||||
import { EventCenterMapType } from '../eventCenter';
|
||||
|
||||
export interface IStoreData {
|
||||
container: {
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
block: Array<IBlockType>;
|
||||
modalMap: Record<string, IStoreData>;
|
||||
dataSource: Record<string, any>;
|
||||
globalState: Record<string, any>;
|
||||
}
|
||||
|
||||
export interface IBlockType {
|
||||
id: string;
|
||||
name: string;
|
||||
top: number;
|
||||
left: number;
|
||||
zIndex: number;
|
||||
position: 'absolute' | 'relative' | 'fixed' | 'static' | 'sticky';
|
||||
width?: number | string;
|
||||
height?: number | string;
|
||||
display?: 'inline-block' | 'block' | 'inline';
|
||||
focus: boolean;
|
||||
resize: boolean;
|
||||
canDrag: boolean;
|
||||
props: Record<string, any>;
|
||||
syncList: Array<string>;
|
||||
eventMap: EventCenterMapType; //调用的event 与对应的函数名 如果要增加参数,则类型不能是Array<string>,需要[{name:string,...args}]
|
||||
functionList: Array<string>; //抛出的函数名
|
||||
animate: {
|
||||
animate?: string; //动画名
|
||||
animationIterationCount?: any;
|
||||
speed?: //动画速度
|
||||
'animate__slow' | 'animate__slower' | 'animate__fast' | 'animate__faster' | '';
|
||||
delay?: //首次延迟
|
||||
'animate__delay-2s' | 'animate__delay-3s' | 'animate__delay-4s' | 'animate__delay-5s' | '';
|
||||
};
|
||||
fixed: boolean; // 用于制作fixed组件
|
||||
}
|
||||
239
packages/dooringx-lib/src/core/storeChanger/index.ts
Normal file
239
packages/dooringx-lib/src/core/storeChanger/index.ts
Normal file
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-04-05 14:55:31
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-07 16:59:54
|
||||
* @FilePath: \DooringV2\packages\dooringx-lib\src\core\storeChanger\index.ts
|
||||
*/
|
||||
|
||||
import { message } from 'antd';
|
||||
import Store from '../store';
|
||||
import { IStoreData } from '../store/storetype';
|
||||
import { createUid, deepCopy } from '../utils';
|
||||
import { storeChangerState } from './state';
|
||||
|
||||
export type StoreChangerMap = Record<
|
||||
'ORIGIN',
|
||||
{
|
||||
data: Array<IStoreData>;
|
||||
current: number;
|
||||
now: IStoreData;
|
||||
} | null
|
||||
>;
|
||||
|
||||
function createDefaultModalBlock(): IStoreData['block'] {
|
||||
return [
|
||||
{
|
||||
id: createUid('modal-mask'),
|
||||
name: 'modalMask',
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
zIndex: 0,
|
||||
props: {},
|
||||
resize: true,
|
||||
focus: false,
|
||||
position: 'fixed',
|
||||
display: 'block',
|
||||
syncList: [],
|
||||
canDrag: false,
|
||||
eventMap: {},
|
||||
functionList: [],
|
||||
animate: {},
|
||||
fixed: false,
|
||||
},
|
||||
{
|
||||
id: createUid('modal-container'),
|
||||
name: 'modalContainer',
|
||||
top: 100,
|
||||
left: 35,
|
||||
zIndex: 0,
|
||||
props: {},
|
||||
resize: true,
|
||||
focus: true,
|
||||
position: 'absolute',
|
||||
display: 'block',
|
||||
width: 300,
|
||||
height: 300,
|
||||
syncList: [],
|
||||
canDrag: true,
|
||||
eventMap: {},
|
||||
functionList: [],
|
||||
animate: {},
|
||||
fixed: false,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
// 用来存储主store
|
||||
const ORIGIN = 'ORIGIN';
|
||||
|
||||
const defaultModalStore: () => IStoreData = () => {
|
||||
const newblock = createDefaultModalBlock();
|
||||
return {
|
||||
container: {
|
||||
width: 375,
|
||||
height: 600,
|
||||
},
|
||||
block: newblock,
|
||||
modalMap: {},
|
||||
dataSource: {},
|
||||
globalState: {},
|
||||
};
|
||||
};
|
||||
|
||||
export class StoreChanger {
|
||||
public map: StoreChangerMap;
|
||||
constructor() {
|
||||
this.map = { ORIGIN: null };
|
||||
}
|
||||
|
||||
getState() {
|
||||
return storeChangerState;
|
||||
}
|
||||
|
||||
getOrigin() {
|
||||
return this.map[ORIGIN];
|
||||
}
|
||||
|
||||
isEdit() {
|
||||
if (storeChangerState.modalEditName !== '') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
isInModalMap(store: Store, name: string) {
|
||||
const modalNameList = Object.keys(store.getData().modalMap);
|
||||
if (modalNameList.includes(name)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
initStoreChanger() {
|
||||
storeChangerState.modalEditName = '';
|
||||
this.map = { ORIGIN: null };
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 更新origin内容,用于编辑模式下更新全局属性
|
||||
* 需要判断是否在编辑模式,否则会报错
|
||||
* @memberof StoreChanger
|
||||
*/
|
||||
updateOrigin(data: IStoreData) {
|
||||
const origin = this.getOrigin();
|
||||
if (origin!.data.length === origin!.current + 1) {
|
||||
//说明为末尾,
|
||||
origin!.data.push(data);
|
||||
} else {
|
||||
//替换下一个索引
|
||||
origin!.data[origin!.current + 1] = data;
|
||||
}
|
||||
origin!.now = data;
|
||||
origin!.current = origin!.current + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 保存现阶段store,将store替换为新modal数据
|
||||
* @memberof StoreChanger
|
||||
*/
|
||||
newModalMap(store: Store, name: string) {
|
||||
const sign = this.isEdit();
|
||||
if (sign) {
|
||||
message.error('请保存弹窗后编辑其他弹窗');
|
||||
return;
|
||||
}
|
||||
//新建modal name不能重名,否则直接报错
|
||||
const sign2 = this.isInModalMap(store, name);
|
||||
if (sign2) {
|
||||
message.error(`已有重名弹窗:${name}`);
|
||||
return;
|
||||
}
|
||||
storeChangerState.modalEditName = name;
|
||||
this.map[ORIGIN] = {
|
||||
data: store.getStoreList(),
|
||||
current: store.getIndex(),
|
||||
now: store.getStoreList()[store.getIndex()],
|
||||
};
|
||||
store.resetToInitData([defaultModalStore()]);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 存储modal到主store的map中,切换主store
|
||||
* @param {Store} store
|
||||
* @memberof StoreChanger
|
||||
*/
|
||||
closeModal(store: Store) {
|
||||
const sign = this.isEdit();
|
||||
if (!sign) {
|
||||
message.error('您并没有正在编辑弹窗');
|
||||
return;
|
||||
}
|
||||
const main = this.map[ORIGIN];
|
||||
const tmpModalData = deepCopy(store.getData());
|
||||
if (main) {
|
||||
store.resetToCustomData(main.data, main.current);
|
||||
const cloneData: IStoreData = deepCopy(store.getData());
|
||||
cloneData.modalMap[storeChangerState.modalEditName] = tmpModalData;
|
||||
store.setData(cloneData);
|
||||
storeChangerState.modalEditName = '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 在已经保存的map中获取,如果正在编辑别的弹窗,则报错。
|
||||
* @param {Store} store store必须为主store
|
||||
* @param {string} name
|
||||
* @memberof StoreChanger
|
||||
*/
|
||||
updateModal(store: Store, name: string) {
|
||||
const sign = this.isEdit();
|
||||
if (sign) {
|
||||
message.error('请保存弹窗后编辑其他弹窗');
|
||||
return;
|
||||
}
|
||||
const sign2 = this.isInModalMap(store, name);
|
||||
if (!sign2) {
|
||||
message.error(`未找到该弹窗:${name}`);
|
||||
return;
|
||||
}
|
||||
storeChangerState.modalEditName = name;
|
||||
const modalData = store.getData().modalMap[name];
|
||||
this.map[ORIGIN] = {
|
||||
data: store.getStoreList(),
|
||||
current: store.getIndex(),
|
||||
now: store.getStoreList()[store.getIndex()],
|
||||
};
|
||||
store.resetToInitData([modalData]);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 删除弹窗,不能处于编辑弹窗状态
|
||||
* @param {Store} store
|
||||
* @param {string} name
|
||||
* @returns
|
||||
* @memberof StoreChanger
|
||||
*/
|
||||
removeModal(store: Store, name: string) {
|
||||
const sign = this.isEdit();
|
||||
if (sign) {
|
||||
message.error('请保存弹窗后删除其他弹窗');
|
||||
return;
|
||||
}
|
||||
const sign2 = this.isInModalMap(store, name);
|
||||
if (!sign2) {
|
||||
message.error(`未找到该弹窗:${name}`);
|
||||
return;
|
||||
}
|
||||
const cloneData: IStoreData = deepCopy(store.getData());
|
||||
delete cloneData.modalMap[name];
|
||||
store.setData(cloneData);
|
||||
}
|
||||
}
|
||||
11
packages/dooringx-lib/src/core/storeChanger/state.ts
Normal file
11
packages/dooringx-lib/src/core/storeChanger/state.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-04-05 15:40:04
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-04-05 15:40:47
|
||||
* @FilePath: \dooringv2\src\core\storeChanger\state.ts
|
||||
*/
|
||||
|
||||
export const storeChangerState = {
|
||||
modalEditName: '',
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user