change pkg
This commit is contained in:
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: '',
|
||||
};
|
||||
117
packages/dooringx-lib/src/core/transfer/index.ts
Normal file
117
packages/dooringx-lib/src/core/transfer/index.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-04-21 22:59:57
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-07 19:50:12
|
||||
* @FilePath: \DooringV2\packages\dooringx-lib\src\core\transfer\index.ts
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @export 这里的转换可能有问题,需要都使用mobileWidth去转换
|
||||
* @param {number} top
|
||||
* @param {number} left
|
||||
* @param {(string | number | undefined)} height
|
||||
* @param {(string | number | undefined)} width
|
||||
* @param {boolean} isFixed
|
||||
* @returns
|
||||
*/
|
||||
export function transfer(
|
||||
top: number,
|
||||
left: number,
|
||||
height: string | number | undefined,
|
||||
width: string | number | undefined,
|
||||
isFixed: boolean
|
||||
) {
|
||||
if (isFixed) {
|
||||
// 由于是375x667基准,所以top大于667的,那么top为底部高度
|
||||
let newtop = 0;
|
||||
|
||||
const newleft = getRealWidth(left);
|
||||
let newheight: string | number | undefined;
|
||||
let newwidth: string | number | undefined;
|
||||
if (typeof height === 'string' || typeof height === 'undefined') {
|
||||
newheight = height;
|
||||
} else {
|
||||
newheight = getRealHeight(height);
|
||||
}
|
||||
if (typeof width === 'string' || typeof width === 'undefined') {
|
||||
newwidth = width;
|
||||
} else {
|
||||
newwidth = getRealWidth(width);
|
||||
}
|
||||
|
||||
if (top >= 667) {
|
||||
if (typeof newheight === 'number') {
|
||||
newtop = getRealHeight() - newheight;
|
||||
} else {
|
||||
// 如果没有高度或者高度是百分比,则定位会有问题
|
||||
newtop = getRealHeight();
|
||||
}
|
||||
} else {
|
||||
if (typeof height === 'number' && top >= 667 - height && typeof newheight === 'number') {
|
||||
// 这种是距离底部比高多 按底部计算
|
||||
newtop = getRealHeight() - newheight;
|
||||
} else {
|
||||
newtop = getRealHeight(top);
|
||||
}
|
||||
}
|
||||
return {
|
||||
top: newtop,
|
||||
left: newleft,
|
||||
height: newheight,
|
||||
width: newwidth,
|
||||
};
|
||||
} else {
|
||||
const newtop = getRealHeight(top);
|
||||
const newleft = getRealWidth(left);
|
||||
let newheight: string | number | undefined;
|
||||
let newwidth: string | number | undefined;
|
||||
if (typeof height === 'string' || typeof height === 'undefined') {
|
||||
newheight = height;
|
||||
} else {
|
||||
newheight = getRealHeight(height);
|
||||
}
|
||||
if (typeof width === 'string' || typeof width === 'undefined') {
|
||||
newwidth = width;
|
||||
} else {
|
||||
newwidth = getRealWidth(width);
|
||||
}
|
||||
|
||||
return {
|
||||
top: newtop,
|
||||
left: newleft,
|
||||
height: newheight,
|
||||
width: newwidth,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function getCurrentMobileInfo() {
|
||||
let userAgentMatched = window.navigator.userAgent.match(
|
||||
/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i
|
||||
);
|
||||
const width = userAgentMatched
|
||||
? window.innerWidth
|
||||
: window.innerWidth < 500
|
||||
? window.innerWidth
|
||||
: 375;
|
||||
|
||||
const height = userAgentMatched
|
||||
? window.screen.availHeight
|
||||
: window.screen.availHeight < 667
|
||||
? window.screen.availHeight
|
||||
: 667;
|
||||
return [width, height];
|
||||
}
|
||||
|
||||
export function getRealWidth(w: number | string = 375) {
|
||||
const width = typeof w === 'string' ? parseFloat(w) : w;
|
||||
return (getCurrentMobileInfo()[0] / 375) * width;
|
||||
}
|
||||
|
||||
export function getRealHeight(H: number | string = 667) {
|
||||
const height = typeof H === 'string' ? parseFloat(H) : H;
|
||||
return (getCurrentMobileInfo()[0] / 375) * height;
|
||||
}
|
||||
9
packages/dooringx-lib/src/core/utils/icon.ts
Normal file
9
packages/dooringx-lib/src/core/utils/icon.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { createFromIconfontCN } from '@ant-design/icons';
|
||||
|
||||
export const IconFont = createFromIconfontCN({
|
||||
scriptUrl: '//at.alicdn.com/t/font_2607370_myr2zkz3ku.js', // 在 iconfont.cn 上生成
|
||||
extraCommonProps: {
|
||||
fill: 'currentColor',
|
||||
stroke: 'currentColor',
|
||||
},
|
||||
});
|
||||
281
packages/dooringx-lib/src/core/utils/index.ts
Normal file
281
packages/dooringx-lib/src/core/utils/index.ts
Normal file
@@ -0,0 +1,281 @@
|
||||
import { message } from 'antd';
|
||||
import { RGBColor } from 'react-color';
|
||||
import * as uuid from 'uuid';
|
||||
import Store from '../store';
|
||||
import { IBlockType, IStoreData } from '../store/storetype';
|
||||
import { specialCoList } from './special';
|
||||
import deepCopys from 'deepcopy';
|
||||
import { FunctionDataMap } from '../functionCenter/config';
|
||||
import UserConfig from '../../config';
|
||||
|
||||
export function deepCopy(obj: any) {
|
||||
return deepCopys(obj);
|
||||
}
|
||||
|
||||
export function swap(indexa: number, indexb: number, arr: Array<any>) {
|
||||
arr[indexa] = arr.splice(indexb, 1, arr[indexa])[0];
|
||||
return arr;
|
||||
}
|
||||
|
||||
// 将rgba字符串对象转化为rgba对象
|
||||
export function rgba2Obj(rgba = '') {
|
||||
let reg = /rgba\(\s*?(\d+)\s*?,\s*?(\d+)\s*?,\s*?(\d+)\s*?,\s*?(\d+)\s*?\)/g;
|
||||
let rgbaObj: RGBColor = { r: 0, g: 0, b: 0, a: 0 };
|
||||
|
||||
rgba.replace(reg, (_m, r, g, b, a) => {
|
||||
rgbaObj = { r, g, b, a };
|
||||
return rgba;
|
||||
});
|
||||
return rgbaObj;
|
||||
}
|
||||
|
||||
export function createUid(name?: string) {
|
||||
if (name) {
|
||||
return name + '-' + uuid.v4();
|
||||
} else {
|
||||
return uuid.v4();
|
||||
}
|
||||
}
|
||||
|
||||
export const isMac = () => {
|
||||
const isMac = /macintosh|mac os x/i.test(navigator.userAgent);
|
||||
if (isMac) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
export const changeItem = (
|
||||
store: Store,
|
||||
id: string,
|
||||
property: keyof IBlockType,
|
||||
value: IBlockType[keyof IBlockType]
|
||||
) => {
|
||||
const clonedata: IStoreData = deepCopy(store.getData());
|
||||
let canchange = true;
|
||||
clonedata.block.forEach((v) => {
|
||||
if (v.id === id) {
|
||||
if (specialCoList.includes(v.name)) {
|
||||
message.error('该组件不可调整');
|
||||
canchange = false;
|
||||
}
|
||||
v[property] = value as never;
|
||||
}
|
||||
});
|
||||
if (canchange) {
|
||||
store.setData(clonedata);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* 清除所有聚焦,选中某个元素
|
||||
* @param {Store} store
|
||||
* @param {string} id
|
||||
*/
|
||||
export const focusEle = (store: Store, id: string) => {
|
||||
const clonedata: IStoreData = deepCopy(store.getData());
|
||||
clonedata.block.forEach((v) => {
|
||||
if (v.id === id) {
|
||||
v.focus = true;
|
||||
} else {
|
||||
v.focus = false;
|
||||
}
|
||||
});
|
||||
store.setData(clonedata);
|
||||
};
|
||||
|
||||
export const changeLayer = (store: Store, id: string, action: 'up' | 'down' | 'delete') => {
|
||||
const clonedata: IStoreData = deepCopy(store.getData());
|
||||
let index = -1;
|
||||
switch (action) {
|
||||
case 'up':
|
||||
clonedata.block.forEach((v, i) => {
|
||||
if (v.id === id) {
|
||||
if (specialCoList.includes(v.name)) {
|
||||
message.error('该组件不可调整');
|
||||
return;
|
||||
} else {
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (index > 0) {
|
||||
// 查看上一个元素
|
||||
const item = clonedata.block[index - 1];
|
||||
if (specialCoList.includes(item.name)) {
|
||||
return;
|
||||
}
|
||||
swap(index, index - 1, clonedata.block);
|
||||
store.setData(clonedata);
|
||||
}
|
||||
return;
|
||||
case 'down':
|
||||
clonedata.block.forEach((v, i) => {
|
||||
if (v.id === id) {
|
||||
if (specialCoList.includes(v.name)) {
|
||||
message.error('该组件不可调整');
|
||||
return;
|
||||
} else {
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (index > -1 && index + 1 < clonedata.block.length) {
|
||||
const item = clonedata.block[index + 1];
|
||||
if (specialCoList.includes(item.name)) {
|
||||
return;
|
||||
}
|
||||
swap(index, index + 1, clonedata.block);
|
||||
store.setData(clonedata);
|
||||
}
|
||||
return;
|
||||
case 'delete':
|
||||
let candelete = true;
|
||||
clonedata.block = clonedata.block.filter((v) => {
|
||||
if (v.id === id) {
|
||||
if (specialCoList.includes(v.name)) {
|
||||
candelete = false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (candelete) {
|
||||
store.setData(clonedata);
|
||||
} else {
|
||||
message.error('该组件无法删除');
|
||||
}
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} array
|
||||
* @param {*} from
|
||||
* @param {*} to
|
||||
*/
|
||||
export const arrayMove = (array: any, from: number, to: number) => {
|
||||
array = [...array];
|
||||
arrayMoveMutate(array, from, to);
|
||||
return array;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} length
|
||||
* @param {*} index
|
||||
*/
|
||||
const indexSub = (arrLength: number, toIndex: number) => {
|
||||
return toIndex < 0 ? arrLength + toIndex : toIndex;
|
||||
// return resIndex;
|
||||
};
|
||||
|
||||
/**
|
||||
* 数组换位
|
||||
* @param {Array} array The array with the item to move. / [1,2,3]
|
||||
* @param {Number} from Index of item to move. If negative, it will begin that many elements from the end / 0 / -1 / 2
|
||||
* @param {Number} to Index of where to move the item. If negative, it will begin that many elements from the end / 0 / -1 / 2
|
||||
* returns A new array with the item moved to the new position [1,2,3] -> [1,3,2]
|
||||
*/
|
||||
const arrayMoveMutate = (array: [], from: number, to: number) => {
|
||||
const arrLength = array.length;
|
||||
const startIndex = indexSub(arrLength, from);
|
||||
if (startIndex >= 0 && startIndex < arrLength) {
|
||||
const endIndex = indexSub(arrLength, to);
|
||||
const [item] = array.splice(from, 1);
|
||||
array.splice(endIndex, 0, item);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* 这个函数将返回值全部统一成数组// modal的不走此方法
|
||||
* @param {keyof FunctionDataMap} v
|
||||
* @param {Record<string, any>} args
|
||||
* @param {string} name
|
||||
* @param {UserConfig} config
|
||||
* @param {Record<string, any>} ctx
|
||||
* @return {Array<string, any>}
|
||||
*/
|
||||
export const changeUserValue = (
|
||||
v: keyof FunctionDataMap,
|
||||
args: Record<string, any>,
|
||||
name: string,
|
||||
config: UserConfig,
|
||||
ctx: Record<string, any>
|
||||
) => {
|
||||
const userChoose = args[name];
|
||||
switch (v) {
|
||||
case 'ctx':
|
||||
if (Array.isArray(userChoose)) {
|
||||
return userChoose.reduce((pr: Array<string>, ne: string) => {
|
||||
const val = ctx[ne];
|
||||
pr.push(val);
|
||||
return pr;
|
||||
}, []);
|
||||
}
|
||||
return [];
|
||||
case 'dataSource':
|
||||
const dataCenter = config.getDataCenter().getDataMap();
|
||||
if (Array.isArray(userChoose)) {
|
||||
return userChoose.reduce((pr: Array<string>, ne: string) => {
|
||||
const val = dataCenter[ne];
|
||||
pr.push(val);
|
||||
return pr;
|
||||
}, []);
|
||||
}
|
||||
return [];
|
||||
default:
|
||||
if (Array.isArray(userChoose)) {
|
||||
return userChoose;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
};
|
||||
/**
|
||||
*
|
||||
* 这个函数将返回值全部统一成对象 modal的不走此方法
|
||||
* @param {keyof FunctionDataMap} v
|
||||
* @param {Record<string, any>} args
|
||||
* @param {string} name
|
||||
* @param {UserConfig} config
|
||||
* @param {Record<string, any>} ctx
|
||||
* @return {Record<string, any>}
|
||||
*/
|
||||
export const changeUserValueRecord = (
|
||||
v: keyof FunctionDataMap,
|
||||
args: Record<string, any>,
|
||||
name: string,
|
||||
config: UserConfig,
|
||||
ctx: Record<string, any>
|
||||
) => {
|
||||
const userChoose = args[name];
|
||||
switch (v) {
|
||||
case 'ctx':
|
||||
if (Array.isArray(userChoose)) {
|
||||
return userChoose.reduce((pr: Record<string, any>, ne: string) => {
|
||||
const val = ctx[ne];
|
||||
return Object.assign(pr, { [ne]: val });
|
||||
}, {});
|
||||
}
|
||||
return {};
|
||||
case 'dataSource':
|
||||
const dataCenter = config.getDataCenter().getDataMap();
|
||||
if (Array.isArray(userChoose)) {
|
||||
return userChoose.reduce((pr: Record<string, any>, ne: string) => {
|
||||
const val = dataCenter[ne];
|
||||
return Object.assign(pr, { [ne]: val });
|
||||
}, {});
|
||||
}
|
||||
return {};
|
||||
default:
|
||||
if (Array.isArray(userChoose)) {
|
||||
return userChoose.reduce((pr: Record<string, any>, ne: string) => {
|
||||
return Object.assign(pr, { [ne]: ne });
|
||||
}, {});
|
||||
}
|
||||
return {};
|
||||
}
|
||||
};
|
||||
1
packages/dooringx-lib/src/core/utils/special.ts
Normal file
1
packages/dooringx-lib/src/core/utils/special.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const specialCoList = ['modalMask'];
|
||||
98
packages/dooringx-lib/src/hooks/index.ts
Normal file
98
packages/dooringx-lib/src/hooks/index.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 05:35:15
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-04 17:52:02
|
||||
* @FilePath: \DooringV2\packages\dooring-v2-lib\src\hooks\index.ts
|
||||
*/
|
||||
import { store } from '../runtime/store';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import UserConfig from '../config';
|
||||
import { ComponentRenderConfigProps } from '../core/components/componentItem';
|
||||
import { registCommandFn, unRegistCommandFn } from '../core/command/runtime';
|
||||
|
||||
export function useStoreState(
|
||||
config: UserConfig,
|
||||
extraFn: Function = () => {},
|
||||
everyFn: Function = () => {}
|
||||
) {
|
||||
const [state, setState] = useState(store.getData());
|
||||
const forceUpdate = useState(0)[1];
|
||||
useEffect(() => {
|
||||
const unRegister = store.subscribe(() => {
|
||||
setState(store.getData());
|
||||
config.getEventCenter().syncEventMap(store.getData(), config.getStoreChanger());
|
||||
extraFn();
|
||||
});
|
||||
store.setForceUpdate(() => forceUpdate((v) => v + 1));
|
||||
const commandModules = config.getConfig().initCommandModule;
|
||||
const commander = config.getCommanderRegister();
|
||||
registCommandFn(commandModules, commander);
|
||||
return () => {
|
||||
unRegister();
|
||||
unRegistCommandFn(commandModules, commander);
|
||||
};
|
||||
}, [config, extraFn]);
|
||||
useEffect(() => {
|
||||
everyFn();
|
||||
}, [everyFn]);
|
||||
|
||||
// 去除默认滚动
|
||||
useEffect(() => {
|
||||
const fn1 = function (event: Event) {
|
||||
if ((event as MouseEvent).ctrlKey === true || (event as MouseEvent).metaKey) {
|
||||
event.preventDefault();
|
||||
}
|
||||
};
|
||||
const fn2 = function (event: Event) {
|
||||
if ((event as MouseEvent).ctrlKey === true || (event as MouseEvent).metaKey) {
|
||||
event.preventDefault();
|
||||
}
|
||||
};
|
||||
window.addEventListener('mousewheel', fn1, { passive: false });
|
||||
//firefox
|
||||
window.addEventListener('DOMMouseScroll', fn2, { passive: false });
|
||||
return () => {
|
||||
window.removeEventListener('mousewheel', fn1);
|
||||
window.removeEventListener('mousewheel', fn2);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return [state];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 组件动态注册eventMap与eventCenter
|
||||
* @export
|
||||
* @param {ComponentRenderConfigProps} props render参数传来的
|
||||
* @param {string} eventName 同一个组件名称不能重复
|
||||
* @returns
|
||||
*/
|
||||
export function useDynamicAddEventCenter(
|
||||
props: ComponentRenderConfigProps,
|
||||
eventName: string,
|
||||
displayName: string
|
||||
) {
|
||||
const eventCenter = useMemo(() => {
|
||||
return props.config.getEventCenter();
|
||||
}, [props.config]);
|
||||
|
||||
useEffect(() => {
|
||||
const data = props.store.getData();
|
||||
const map = props.data.eventMap;
|
||||
const storeItem = data.block.find((v) => v.id === props.data.id);
|
||||
if (storeItem) {
|
||||
if (!map[eventName]) {
|
||||
//动态store加属性需要通过hook
|
||||
storeItem.eventMap[eventName] = {
|
||||
arr: [],
|
||||
displayName,
|
||||
userSelect: [],
|
||||
};
|
||||
eventCenter.manualUpdateMap(eventName, displayName);
|
||||
}
|
||||
}
|
||||
}, [eventCenter, props.data.eventMap, props.data.id, props.store]);
|
||||
return;
|
||||
}
|
||||
173
packages/dooringx-lib/src/index.less
Normal file
173
packages/dooringx-lib/src/index.less
Normal file
@@ -0,0 +1,173 @@
|
||||
.yhLeftrender {
|
||||
overflow: auto;
|
||||
|
||||
.leftco {
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.coitem {
|
||||
margin: 5px;
|
||||
|
||||
.redbox {
|
||||
height: 68px;
|
||||
width: 68px;
|
||||
background: var(--redbox-color);
|
||||
// line-height: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
#icon-checkbox,
|
||||
#icon-tabs,
|
||||
#icon-jiantou path {
|
||||
fill: currentColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.yh_container {
|
||||
// ::selection {
|
||||
// color: inherit;
|
||||
// background-color: inherit;
|
||||
// }
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
|
||||
:global(.am-list-item) {
|
||||
background-color: rgba(255, 255, 255, 0);
|
||||
}
|
||||
|
||||
:global(.am-list-body) {
|
||||
background-color: rgba(255, 255, 255, 0);
|
||||
}
|
||||
|
||||
:global(.ant-input) {
|
||||
background-color: rgba(255, 255, 255, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.yh_container_preview {
|
||||
:global(.am-list-item) {
|
||||
background-color: rgba(255, 255, 255, 0);
|
||||
}
|
||||
|
||||
:global(.am-list-body) {
|
||||
background-color: rgba(255, 255, 255, 0);
|
||||
}
|
||||
|
||||
:global(.ant-input) {
|
||||
background-color: rgba(255, 255, 255, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.yh_block_focus {
|
||||
&::before {
|
||||
position: absolute;
|
||||
top: -3px;
|
||||
left: -3px;
|
||||
right: -3px;
|
||||
bottom: -3px;
|
||||
content: '';
|
||||
border: 2px dashed #2196f3;
|
||||
}
|
||||
}
|
||||
|
||||
.yhTempDiv {
|
||||
background-color: #7165fa2b;
|
||||
}
|
||||
|
||||
.resizepoint {
|
||||
position: absolute;
|
||||
height: 6px;
|
||||
width: 6px;
|
||||
background-color: #2196f3;
|
||||
cursor: pointer;
|
||||
border-radius: 50%;
|
||||
|
||||
&.left {
|
||||
left: -6px;
|
||||
top: calc(50% - 3px);
|
||||
|
||||
&:hover {
|
||||
cursor: e-resize;
|
||||
}
|
||||
}
|
||||
|
||||
&.right {
|
||||
right: -6px;
|
||||
top: calc(50% - 3px);
|
||||
|
||||
&:hover {
|
||||
cursor: e-resize;
|
||||
}
|
||||
}
|
||||
|
||||
&.top {
|
||||
top: -6px;
|
||||
left: calc(50% - 3px);
|
||||
|
||||
&:hover {
|
||||
cursor: s-resize;
|
||||
}
|
||||
}
|
||||
|
||||
&.bottom {
|
||||
bottom: -6px;
|
||||
left: calc(50% - 3px);
|
||||
|
||||
&:hover {
|
||||
cursor: s-resize;
|
||||
}
|
||||
}
|
||||
|
||||
&.topleft {
|
||||
top: -6px;
|
||||
left: -6px;
|
||||
|
||||
&:hover {
|
||||
cursor: nw-resize;
|
||||
}
|
||||
}
|
||||
|
||||
&.bottomleft {
|
||||
bottom: -6px;
|
||||
left: -6px;
|
||||
|
||||
&:hover {
|
||||
cursor: ne-resize;
|
||||
}
|
||||
}
|
||||
|
||||
&.bottomright {
|
||||
bottom: -6px;
|
||||
right: -6px;
|
||||
|
||||
&:hover {
|
||||
cursor: nw-resize;
|
||||
}
|
||||
}
|
||||
|
||||
&.topright {
|
||||
top: -6px;
|
||||
right: -6px;
|
||||
|
||||
&:hover {
|
||||
cursor: ne-resize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.menu_footer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
bottom: 0;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user