feat: 静态类中的函数继承范性
model onClose and ondestory函数 createDOMElemnet 从jsx
This commit is contained in:
parent
e2c0f80f04
commit
1fbf1b64d9
@ -3,6 +3,7 @@
|
|||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "dist/editor.js",
|
"main": "dist/editor.js",
|
||||||
|
"privite": false,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "rimraf -rf dist && rollup -c"
|
"build": "rimraf -rf dist && rollup -c"
|
||||||
},
|
},
|
||||||
@ -25,5 +26,9 @@
|
|||||||
"rollup": "^4.22.2",
|
"rollup": "^4.22.2",
|
||||||
"tslib": "^2.7.0",
|
"tslib": "^2.7.0",
|
||||||
"typescript": "^5.6.2"
|
"typescript": "^5.6.2"
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"registry": "https://registry.npmjs.org/",
|
||||||
|
"access": "public"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -14,7 +14,7 @@ export const App = () => {
|
|||||||
<Router>
|
<Router>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path='/' element={<Navigate to='/model/' />} />
|
<Route path='/' element={<Navigate to='/model/' />} />
|
||||||
<Route path='/model/*' element={<FlowApps />} />
|
<Route path='/modal/*' element={<FlowApps />} />
|
||||||
<Route path='/codemirror/*' element={<CodeMirrorApp />} />
|
<Route path='/codemirror/*' element={<CodeMirrorApp />} />
|
||||||
<Route path='/404' element={<div>404</div>} />
|
<Route path='/404' element={<div>404</div>} />
|
||||||
<Route path='*' element={<div>404</div>} />
|
<Route path='*' element={<div>404</div>} />
|
||||||
|
79
packages/theme-preview/src/pages/model/create-dom.ts
Normal file
79
packages/theme-preview/src/pages/model/create-dom.ts
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
export function createDOMElement(jsxElement: JSX.Element) {
|
||||||
|
// 如果 jsxElement 是 null, undefined 或者是布尔值,则直接跳过处理
|
||||||
|
if (jsxElement == null || typeof jsxElement === 'boolean') {
|
||||||
|
console.warn('Invalid JSX element:', jsxElement);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { type, props } = jsxElement;
|
||||||
|
|
||||||
|
// React Fragment 的处理
|
||||||
|
if (type === Symbol.for('react.fragment')) {
|
||||||
|
const fragment = document.createDocumentFragment();
|
||||||
|
if (props.children) {
|
||||||
|
if (Array.isArray(props.children)) {
|
||||||
|
props.children.forEach((child) => {
|
||||||
|
const childElement = createDOMElement(child);
|
||||||
|
if (childElement) {
|
||||||
|
fragment.appendChild(childElement);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const childElement = createDOMElement(props.children);
|
||||||
|
if (childElement) {
|
||||||
|
fragment.appendChild(childElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
const domElement = document.createElement(type);
|
||||||
|
|
||||||
|
// 处理 props
|
||||||
|
Object.keys(props).forEach((prop) => {
|
||||||
|
if (prop === 'children') {
|
||||||
|
// 递归处理 children
|
||||||
|
if (Array.isArray(props.children)) {
|
||||||
|
props.children.forEach((child) => {
|
||||||
|
const childElement = createDOMElement(child);
|
||||||
|
if (childElement) {
|
||||||
|
domElement.appendChild(childElement);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (typeof props.children === 'string') {
|
||||||
|
domElement.appendChild(document.createTextNode(props.children));
|
||||||
|
} else if (typeof props.children === 'object' && props.children !== null) {
|
||||||
|
const childElement = createDOMElement(props.children);
|
||||||
|
if (childElement) {
|
||||||
|
domElement.appendChild(childElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (prop.startsWith('on')) {
|
||||||
|
// 处理事件监听器
|
||||||
|
const eventType = prop.slice(2).toLowerCase(); // 提取事件类型(如 onClick -> click)
|
||||||
|
domElement.addEventListener(eventType, props[prop]);
|
||||||
|
} else if (prop === 'style' && typeof props[prop] === 'object') {
|
||||||
|
// 处理 style 属性
|
||||||
|
Object.assign(domElement.style, props[prop]);
|
||||||
|
} else if (prop === 'dangerouslySetInnerHTML') {
|
||||||
|
// 处理 dangerouslySetInnerHTML
|
||||||
|
if (props[prop] && typeof props[prop].__html === 'string') {
|
||||||
|
domElement.innerHTML = props[prop].__html;
|
||||||
|
} else {
|
||||||
|
console.warn('Invalid dangerouslySetInnerHTML content:', props[prop]);
|
||||||
|
}
|
||||||
|
} else if (prop === 'ref') {
|
||||||
|
// React 的 ref 在手动创建 DOM 时没有用处
|
||||||
|
console.warn('Ref prop is not supported in manual DOM creation');
|
||||||
|
} else if (prop === 'key') {
|
||||||
|
// React 的 key 属性是用于虚拟 DOM 的,不影响实际 DOM
|
||||||
|
console.warn('Key prop is not applicable in manual DOM creation');
|
||||||
|
} else {
|
||||||
|
// 处理其他普通属性
|
||||||
|
domElement.setAttribute(prop, props[prop]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return domElement;
|
||||||
|
}
|
@ -1,7 +1,9 @@
|
|||||||
import { modalStore, Modal, DialogModal } from '@kevisual/ui';
|
|
||||||
import { calc } from 'antd/es/theme/internal';
|
import { calc } from 'antd/es/theme/internal';
|
||||||
|
import { DialogModal, Modal, modalStore } from '@kevisual/ui';
|
||||||
import { useEffect, useRef } from 'react';
|
import { useEffect, useRef } from 'react';
|
||||||
import '@kevisual/ui/src/index.css';
|
import '@kevisual/ui/src/index.css';
|
||||||
|
import { createDOMElement } from './create-dom';
|
||||||
|
// import '@kevisual/ui/src/components/modal/index.css';
|
||||||
export const App = () => {
|
export const App = () => {
|
||||||
const showModel = () => {
|
const showModel = () => {
|
||||||
//
|
//
|
||||||
@ -17,6 +19,8 @@ export const App = () => {
|
|||||||
}}>
|
}}>
|
||||||
<ModelOne />
|
<ModelOne />
|
||||||
<ModelTwo />
|
<ModelTwo />
|
||||||
|
<ModelTwo2 />
|
||||||
|
<ModelThree />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -29,7 +33,7 @@ const ModelOne = () => {
|
|||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className='w-100 h-100 p-4 rounded-md shadow-md bg-slate-200'>
|
<div className='w-96 p-4 rounded-md shadow-md bg-slate-200'>
|
||||||
model one
|
model one
|
||||||
<div className='cursor-pointer p-2 border' onClick={showModel}>
|
<div className='cursor-pointer p-2 border' onClick={showModel}>
|
||||||
show
|
show
|
||||||
@ -50,12 +54,12 @@ const ModelTwo = () => {
|
|||||||
contentStyle: {
|
contentStyle: {
|
||||||
// maxHeight: '100px',
|
// maxHeight: '100px',
|
||||||
// overflow: 'auto',
|
// overflow: 'auto',
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className='w-100 h-100 p-4 rounded-md shadow-md bg-slate-200'>
|
<div className='w-96 p-4 rounded-md shadow-md bg-slate-200'>
|
||||||
model two
|
model two
|
||||||
<div className='cursor-pointer p-2 border' onClick={showModel}>
|
<div className='cursor-pointer p-2 border' onClick={showModel}>
|
||||||
show
|
show
|
||||||
@ -71,3 +75,130 @@ const ModelTwo = () => {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
const ModelTwo2 = () => {
|
||||||
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
|
const showModel = () => {
|
||||||
|
const div = createDOMElement(refHide);
|
||||||
|
const model = DialogModal.render(ref.current! || div, {
|
||||||
|
dialogTitle: 'Dialog Modal',
|
||||||
|
width: '400px',
|
||||||
|
dialogTitleCloseIcon: true,
|
||||||
|
contentStyle: {
|
||||||
|
// maxHeight: '100px',
|
||||||
|
// overflow: 'auto',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const refHide = (
|
||||||
|
<div ref={ref}>
|
||||||
|
当前元素是Modal Dialog 的渲染内容
|
||||||
|
<div>内容</div>
|
||||||
|
<div>内容2</div>
|
||||||
|
<div>内容3</div>
|
||||||
|
<div>内容4</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className='w-96 p-4 rounded-md shadow-md bg-slate-200'>
|
||||||
|
model two -假入ref的模块没有真实渲染到节点,如何处理。createDOMElement
|
||||||
|
<div className='cursor-pointer p-2 border' onClick={showModel}>
|
||||||
|
show
|
||||||
|
</div>
|
||||||
|
<div className='hidden'>{refHide}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ModelThree = () => {
|
||||||
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
|
let dialog = useRef<DialogModal | null>(null);
|
||||||
|
const showModel = () => {
|
||||||
|
const model = DialogModal.render(ref.current!, {
|
||||||
|
dialogTitle: 'Dialog Modal',
|
||||||
|
width: '400px',
|
||||||
|
dialogTitleCloseIcon: true,
|
||||||
|
open: false,
|
||||||
|
contentStyle: {
|
||||||
|
// maxHeight: '100px',
|
||||||
|
// overflow: 'auto',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const modals = modalStore.getState().modals;
|
||||||
|
console.log('modals', modals.length, model);
|
||||||
|
dialog.current = model;
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className='w-96 p-4 rounded-md shadow-md bg-slate-200'>
|
||||||
|
model 3
|
||||||
|
<div className='cursor-pointer p-2 border' onClick={showModel}>
|
||||||
|
show
|
||||||
|
</div>
|
||||||
|
<div ref={ref}>
|
||||||
|
当前元素是Modal Dialog 的渲染内容
|
||||||
|
<div>内容</div>
|
||||||
|
<div>内容2</div>
|
||||||
|
<div>内容3</div>
|
||||||
|
<div>内容4</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
onClick={() => {
|
||||||
|
if (dialog.current) {
|
||||||
|
dialog.current.setOpen(true);
|
||||||
|
}
|
||||||
|
console.log('open', dialog.current);
|
||||||
|
}}>
|
||||||
|
真打开
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Model4 = () => {
|
||||||
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
|
let dialog = useRef<DialogModal | null>(null);
|
||||||
|
const showModel = () => {
|
||||||
|
const model = DialogModal.create({
|
||||||
|
dialogTitle: 'Dialog Modal',
|
||||||
|
width: '400px',
|
||||||
|
dialogTitleCloseIcon: true,
|
||||||
|
open: false,
|
||||||
|
contentStyle: {
|
||||||
|
// maxHeight: '100px',
|
||||||
|
// overflow: 'auto',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const modals = modalStore.getState().modals;
|
||||||
|
console.log('modals', modals.length, model);
|
||||||
|
dialog.current = model;
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className='w-96 p-4 rounded-md shadow-md bg-slate-200'>
|
||||||
|
model 4
|
||||||
|
<div className='cursor-pointer p-2 border' onClick={showModel}>
|
||||||
|
show
|
||||||
|
</div>
|
||||||
|
<div ref={ref}>
|
||||||
|
当前元素是Modal Dialog 的渲染内容
|
||||||
|
<div>内容</div>
|
||||||
|
<div>内容2</div>
|
||||||
|
<div>内容3</div>
|
||||||
|
<div>内容4</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
onClick={() => {
|
||||||
|
if (dialog.current) {
|
||||||
|
dialog.current.setOpen(true);
|
||||||
|
}
|
||||||
|
console.log('open', dialog.current);
|
||||||
|
}}>
|
||||||
|
真打开
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
20
packages/theme-preview/src/pages/model/type.ts
Normal file
20
packages/theme-preview/src/pages/model/type.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
class A {
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
// 使用泛型和类构造函数签名来推断返回的类型
|
||||||
|
static render<T extends new (...args: any[]) => any>(this: T, el: string | HTMLDivElement): InstanceType<T> {
|
||||||
|
return new this();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class B extends A {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const a = A.render('div');
|
||||||
|
const b = B.render('div');
|
||||||
|
|
||||||
|
// type a == A
|
||||||
|
// type b == B
|
@ -1,10 +1,14 @@
|
|||||||
{
|
{
|
||||||
"name": "@kevisual/ui",
|
"name": "@kevisual/ui",
|
||||||
"version": "0.0.1",
|
"version": "0.0.2",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
|
"privite": false,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc"
|
"tsc": "tsc",
|
||||||
|
"dev": "rollup -c -w",
|
||||||
|
"build": "npm run clean && rollup -c",
|
||||||
|
"clean": "rimraf dist"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist",
|
"dist",
|
||||||
@ -15,10 +19,18 @@
|
|||||||
"author": "abearxiong",
|
"author": "abearxiong",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@emotion/serialize": "^1.3.1",
|
"@emotion/serialize": "^1.3.1",
|
||||||
|
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||||
|
"@rollup/plugin-typescript": "^11.1.6",
|
||||||
|
"@types/postcss-import": "^14.0.3",
|
||||||
"@types/react": "^18.3.8",
|
"@types/react": "^18.3.8",
|
||||||
|
"autoprefixer": "^10.4.20",
|
||||||
|
"cssnano": "^7.0.6",
|
||||||
"immer": "^10.1.1",
|
"immer": "^10.1.1",
|
||||||
"nanoid": "^5.0.7",
|
"nanoid": "^5.0.7",
|
||||||
|
"postcss-import": "^16.1.0",
|
||||||
"rollup": "^4.22.2",
|
"rollup": "^4.22.2",
|
||||||
|
"rollup-plugin-postcss": "^4.0.2",
|
||||||
|
"ts-lib": "^0.0.5",
|
||||||
"typescript": "^5.6.2",
|
"typescript": "^5.6.2",
|
||||||
"zustand": "5.0.0-rc.2"
|
"zustand": "5.0.0-rc.2"
|
||||||
},
|
},
|
||||||
|
47
packages/ui/rollup.config.mjs
Normal file
47
packages/ui/rollup.config.mjs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import resolve from '@rollup/plugin-node-resolve';
|
||||||
|
import typescript from '@rollup/plugin-typescript';
|
||||||
|
|
||||||
|
import postcss from 'rollup-plugin-postcss';
|
||||||
|
import autoprefixer from 'autoprefixer';
|
||||||
|
import cssnano from 'cssnano';
|
||||||
|
import postcssImport from 'postcss-import';
|
||||||
|
|
||||||
|
const entrys = ['index'];
|
||||||
|
const configs = entrys.map((entry) => ({
|
||||||
|
input: `./src/${entry}.ts`, // 修改输入文件为 TypeScript 文件
|
||||||
|
output: {
|
||||||
|
file: `./dist/${entry}.js`,
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
// resolve(),
|
||||||
|
typescript({
|
||||||
|
tsconfig: './tsconfig.json',
|
||||||
|
compilerOptions: {
|
||||||
|
declaration: true, // 生成声明文件
|
||||||
|
declarationDir: './dist', // 声明文件输出目录
|
||||||
|
},
|
||||||
|
}), // 添加 TypeScript 插件
|
||||||
|
],
|
||||||
|
}));
|
||||||
|
|
||||||
|
const entryCss = ['index'];
|
||||||
|
const configsCss = entryCss.map((entry) => ({
|
||||||
|
input: `./src/${entry}.css`, // 修改输入文件为 TypeScript 文件
|
||||||
|
output: {
|
||||||
|
file: `./dist/${entry}.css`,
|
||||||
|
},
|
||||||
|
include: ['src/**/*.css'],
|
||||||
|
plugins: [
|
||||||
|
// resolve(),
|
||||||
|
postcss({
|
||||||
|
// extract: true,
|
||||||
|
extract: true,
|
||||||
|
plugins: [
|
||||||
|
postcssImport(), // 处理 @import 语句
|
||||||
|
autoprefixer(),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}));
|
||||||
|
|
||||||
|
export default [...configs, ...configsCss];
|
@ -98,12 +98,12 @@ export class DialogModal extends Modal<DialogModalOpts, DialogDefaultStyle> {
|
|||||||
...opts?.defaultStyle?.defaultDialogFooterStyle,
|
...opts?.defaultStyle?.defaultDialogFooterStyle,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
static render(el: string | HTMLDivElement, id: string, opts?: DialogModalOpts): any;
|
// static render(el: string | HTMLDivElement, id: string, opts?: DialogModalOpts): DialogModal;
|
||||||
static render(el: string | HTMLDivElement, opts?: DialogModalOpts): any;
|
// static render(el: string | HTMLDivElement, opts?: DialogModalOpts): DialogModal;
|
||||||
static render(...args: any[]) {
|
// static render(...args: any[]) {
|
||||||
const [el, id, opts] = args;
|
// const [el, id, opts] = args;
|
||||||
super.render(el, id, opts);
|
// return super.render(el, id, opts);
|
||||||
}
|
// }
|
||||||
appendRoot(documentFragment: DocumentFragment): void {
|
appendRoot(documentFragment: DocumentFragment): void {
|
||||||
const cacheFragment = document.createDocumentFragment();
|
const cacheFragment = document.createDocumentFragment();
|
||||||
// 拿出来
|
// 拿出来
|
||||||
|
@ -8,10 +8,9 @@
|
|||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
z-index: 200;
|
z-index: 200;
|
||||||
display: none;
|
|
||||||
}
|
}
|
||||||
.ui-modal-open {
|
.ui-modal-close {
|
||||||
display: block;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-modal-mask {
|
.ui-modal-mask {
|
||||||
|
@ -23,8 +23,9 @@ export type ModalOpts<
|
|||||||
|
|
||||||
contentClassName?: string;
|
contentClassName?: string;
|
||||||
contentStyle?: ElStyle;
|
contentStyle?: ElStyle;
|
||||||
|
destroyOnClose?: boolean; // 关闭,把Element移动到cacheFragment中
|
||||||
destroyOnClose?: boolean;
|
hideOnClose?: boolean; // 关闭后是否销毁,设置display:none
|
||||||
|
open?: boolean;
|
||||||
|
|
||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
defaultStyle?: DefaultStyle<U>;
|
defaultStyle?: DefaultStyle<U>;
|
||||||
@ -49,6 +50,7 @@ export class Modal<T = any, U = KV> {
|
|||||||
contentStyle?: ElStyle;
|
contentStyle?: ElStyle;
|
||||||
|
|
||||||
destroyOnClose?: boolean;
|
destroyOnClose?: boolean;
|
||||||
|
hideOnClose?: boolean;
|
||||||
|
|
||||||
open?: boolean;
|
open?: boolean;
|
||||||
isUse = true;
|
isUse = true;
|
||||||
@ -67,12 +69,18 @@ export class Modal<T = any, U = KV> {
|
|||||||
this.maskClose = opts.maskClose ?? true;
|
this.maskClose = opts.maskClose ?? true;
|
||||||
this.contentClassName = opts.contentClassName;
|
this.contentClassName = opts.contentClassName;
|
||||||
this.contentStyle = opts.contentStyle;
|
this.contentStyle = opts.contentStyle;
|
||||||
this.destroyOnClose = opts.destroyOnClose ?? false;
|
this.destroyOnClose = opts.destroyOnClose ?? true;
|
||||||
|
this.hideOnClose = opts.hideOnClose ?? true;
|
||||||
|
if (!this.destroyOnClose && !this.hideOnClose) {
|
||||||
|
this.destroyOnClose = true; // 必须要有一个为true
|
||||||
|
console.warn('destroyOnClose Or hideOnClose must one is true');
|
||||||
|
}
|
||||||
this.cacheFragment = new DocumentFragment();
|
this.cacheFragment = new DocumentFragment();
|
||||||
this.defaultStyle = opts.defaultStyle || ({} as DefaultStyle<U>);
|
this.defaultStyle = opts.defaultStyle || ({} as DefaultStyle<U>);
|
||||||
|
this.open = opts.open ?? true;
|
||||||
this.onClose = opts.onClose;
|
this.onClose = opts.onClose;
|
||||||
}
|
}
|
||||||
initRoot(root: ModalOpts['root']) {
|
protected initRoot(root: ModalOpts['root']) {
|
||||||
let _root = querySelector(root);
|
let _root = querySelector(root);
|
||||||
if (!_root) {
|
if (!_root) {
|
||||||
// 查询ui-modal元素,不存在则创建一个ui-modal元素并添加到body上
|
// 查询ui-modal元素,不存在则创建一个ui-modal元素并添加到body上
|
||||||
@ -96,8 +104,8 @@ export class Modal<T = any, U = KV> {
|
|||||||
|
|
||||||
return _root;
|
return _root;
|
||||||
}
|
}
|
||||||
static render(el: string | HTMLDivElement, id: string, opts?: ModalOpts): any;
|
static render<T extends new (...args: any[]) => any>(this: T,el: string | HTMLDivElement, id: string, opts?: ModalOpts): InstanceType<T>;
|
||||||
static render(el: string | HTMLDivElement, opts?: ModalOpts): any;
|
static render<T extends new (...args: any[]) => any>(this: T,el: string | HTMLDivElement, opts?: ModalOpts): InstanceType<T>;
|
||||||
static render(...args: any[]) {
|
static render(...args: any[]) {
|
||||||
let [el, id, opts] = args;
|
let [el, id, opts] = args;
|
||||||
const _el = querySelector(el);
|
const _el = querySelector(el);
|
||||||
@ -131,6 +139,26 @@ export class Modal<T = any, U = KV> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
_modal.renderEl(_el);
|
_modal.renderEl(_el);
|
||||||
|
return _modal;
|
||||||
|
}
|
||||||
|
static create<T extends new (...args: any[]) => any>(this:T, opts: ModalOpts):InstanceType<T> {
|
||||||
|
let _id = opts.id;
|
||||||
|
let _modal: Modal | undefined;
|
||||||
|
const modalState = modalStore.getState();
|
||||||
|
if (_id) {
|
||||||
|
// 如果存在id,则判断是否已经存在该id的modal
|
||||||
|
_modal = modalStore.getState().getModal(_id);
|
||||||
|
}
|
||||||
|
if (!_modal) {
|
||||||
|
// 不存在modal,则创建一个modal
|
||||||
|
// console.log('create modal', id, opts);
|
||||||
|
const newModal = new this({ ...opts, id: _id });
|
||||||
|
_modal = newModal;
|
||||||
|
modalStore.setState({
|
||||||
|
modals: [...modalState.modals, newModal],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return _modal as InstanceType<T>;
|
||||||
}
|
}
|
||||||
createMask() {
|
createMask() {
|
||||||
const mask = document.createElement('div');
|
const mask = document.createElement('div');
|
||||||
@ -178,20 +206,26 @@ export class Modal<T = any, U = KV> {
|
|||||||
}
|
}
|
||||||
appendRoot(document: DocumentFragment) {
|
appendRoot(document: DocumentFragment) {
|
||||||
this.root.appendChild(document);
|
this.root.appendChild(document);
|
||||||
this.setOpen(true);
|
// 第一次渲染,open为true,显示弹窗
|
||||||
|
this.setOpen(this.open);
|
||||||
}
|
}
|
||||||
setOpen(open: boolean) {
|
setOpen(open: boolean) {
|
||||||
this.open = open;
|
this.open = open;
|
||||||
if (this.destroyOnClose && open === false) {
|
if (this.destroyOnClose) {
|
||||||
this.unMount();
|
|
||||||
}
|
|
||||||
if (open) {
|
if (open) {
|
||||||
this.modalElement.classList.add('ui-modal-open');
|
|
||||||
this.root.appendChild(this.modalElement);
|
this.root.appendChild(this.modalElement);
|
||||||
} else {
|
} else {
|
||||||
// this.modalElement.classList.remove('ui-modal-open');
|
|
||||||
this.cacheFragment.appendChild(this.modalElement);
|
this.cacheFragment.appendChild(this.modalElement);
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.hideOnClose) {
|
||||||
|
if (open) {
|
||||||
|
this.modalElement.classList.remove('ui-modal-close');
|
||||||
|
} else {
|
||||||
|
this.modalElement.classList.add('ui-modal-close');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
unMount() {
|
unMount() {
|
||||||
// 返回渲染的的Element, 然后删除modalElement
|
// 返回渲染的的Element, 然后删除modalElement
|
||||||
@ -204,10 +238,12 @@ export class Modal<T = any, U = KV> {
|
|||||||
modals: modalState.modals.filter((modal) => modal.id !== this.id),
|
modals: modalState.modals.filter((modal) => modal.id !== this.id),
|
||||||
});
|
});
|
||||||
this.isUse = false;
|
this.isUse = false;
|
||||||
|
this.cacheFragment = new DocumentFragment();
|
||||||
return fragment;
|
return fragment;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* 保留,暂时不用
|
* 保留,暂时不用
|
||||||
|
* // TODO: 研究
|
||||||
* @param force
|
* @param force
|
||||||
* @param opts
|
* @param opts
|
||||||
* @returns
|
* @returns
|
||||||
|
@ -1 +1 @@
|
|||||||
@import 'components/model/index.css';
|
@import './components/modal/index.css';
|
@ -1,3 +1,7 @@
|
|||||||
import { Modal, modalStore, BlankModal, DialogModal } from './components/modal';
|
import { Modal, modalStore, BlankModal, DialogModal } from './components/modal';
|
||||||
|
|
||||||
export { Modal, modalStore, BlankModal, DialogModal };
|
export { Modal, modalStore, BlankModal, DialogModal };
|
||||||
|
|
||||||
|
import { createDOMElement } from './utils/dom/create-dom-element';
|
||||||
|
|
||||||
|
export { createDOMElement };
|
||||||
|
82
packages/ui/src/utils/dom/create-dom-element.ts
Normal file
82
packages/ui/src/utils/dom/create-dom-element.ts
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
type JSXElement = {
|
||||||
|
type: string | symbol;
|
||||||
|
props: Record<string, any>;
|
||||||
|
key?: string | number;
|
||||||
|
};
|
||||||
|
export function createDOMElement(jsxElement: JSXElement) {
|
||||||
|
// 如果 jsxElement 是 null, undefined 或者是布尔值,则直接跳过处理
|
||||||
|
if (jsxElement == null || typeof jsxElement === 'boolean') {
|
||||||
|
console.warn('Invalid JSX element:', jsxElement);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const { type, props } = jsxElement;
|
||||||
|
// React Fragment 的处理
|
||||||
|
if (type === Symbol.for('react.fragment')) {
|
||||||
|
const fragment = document.createDocumentFragment();
|
||||||
|
if (props.children) {
|
||||||
|
if (Array.isArray(props.children)) {
|
||||||
|
props.children.forEach((child) => {
|
||||||
|
const childElement = createDOMElement(child);
|
||||||
|
if (childElement) {
|
||||||
|
fragment.appendChild(childElement);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const childElement = createDOMElement(props.children);
|
||||||
|
if (childElement) {
|
||||||
|
fragment.appendChild(childElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
const domElement = document.createElement(type as string);
|
||||||
|
|
||||||
|
// 处理 props
|
||||||
|
Object.keys(props).forEach((prop) => {
|
||||||
|
if (prop === 'children') {
|
||||||
|
// 递归处理 children
|
||||||
|
if (Array.isArray(props.children)) {
|
||||||
|
props.children.forEach((child) => {
|
||||||
|
const childElement = createDOMElement(child);
|
||||||
|
if (childElement) {
|
||||||
|
domElement.appendChild(childElement);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (typeof props.children === 'string') {
|
||||||
|
domElement.appendChild(document.createTextNode(props.children));
|
||||||
|
} else if (typeof props.children === 'object' && props.children !== null) {
|
||||||
|
const childElement = createDOMElement(props.children);
|
||||||
|
if (childElement) {
|
||||||
|
domElement.appendChild(childElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (prop.startsWith('on')) {
|
||||||
|
// 处理事件监听器
|
||||||
|
const eventType = prop.slice(2).toLowerCase(); // 提取事件类型(如 onClick -> click)
|
||||||
|
domElement.addEventListener(eventType, props[prop]);
|
||||||
|
} else if (prop === 'style' && typeof props[prop] === 'object') {
|
||||||
|
// 处理 style 属性
|
||||||
|
Object.assign(domElement.style, props[prop]);
|
||||||
|
} else if (prop === 'dangerouslySetInnerHTML') {
|
||||||
|
// 处理 dangerouslySetInnerHTML
|
||||||
|
if (props[prop] && typeof props[prop].__html === 'string') {
|
||||||
|
domElement.innerHTML = props[prop].__html;
|
||||||
|
} else {
|
||||||
|
console.warn('Invalid dangerouslySetInnerHTML content:', props[prop]);
|
||||||
|
}
|
||||||
|
} else if (prop === 'ref') {
|
||||||
|
// React 的 ref 在手动创建 DOM 时没有用处
|
||||||
|
console.warn('Ref prop is not supported in manual DOM creation');
|
||||||
|
} else if (prop === 'key') {
|
||||||
|
// React 的 key 属性是用于虚拟 DOM 的,不影响实际 DOM
|
||||||
|
console.warn('Key prop is not applicable in manual DOM creation');
|
||||||
|
} else {
|
||||||
|
// 处理其他普通属性
|
||||||
|
domElement.setAttribute(prop, props[prop]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return domElement;
|
||||||
|
}
|
1343
pnpm-lock.yaml
generated
1343
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user