feat: 静态类中的函数继承范性
model onClose and ondestory函数 createDOMElemnet 从jsx
This commit is contained in:
parent
e2c0f80f04
commit
1fbf1b64d9
@ -3,6 +3,7 @@
|
||||
"version": "0.0.1",
|
||||
"description": "",
|
||||
"main": "dist/editor.js",
|
||||
"privite": false,
|
||||
"scripts": {
|
||||
"build": "rimraf -rf dist && rollup -c"
|
||||
},
|
||||
@ -25,5 +26,9 @@
|
||||
"rollup": "^4.22.2",
|
||||
"tslib": "^2.7.0",
|
||||
"typescript": "^5.6.2"
|
||||
},
|
||||
"publishConfig": {
|
||||
"registry": "https://registry.npmjs.org/",
|
||||
"access": "public"
|
||||
}
|
||||
}
|
@ -14,7 +14,7 @@ export const App = () => {
|
||||
<Router>
|
||||
<Routes>
|
||||
<Route path='/' element={<Navigate to='/model/' />} />
|
||||
<Route path='/model/*' element={<FlowApps />} />
|
||||
<Route path='/modal/*' element={<FlowApps />} />
|
||||
<Route path='/codemirror/*' element={<CodeMirrorApp />} />
|
||||
<Route path='/404' 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 { DialogModal, Modal, modalStore } from '@kevisual/ui';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import '@kevisual/ui/src/index.css';
|
||||
import { createDOMElement } from './create-dom';
|
||||
// import '@kevisual/ui/src/components/modal/index.css';
|
||||
export const App = () => {
|
||||
const showModel = () => {
|
||||
//
|
||||
@ -17,6 +19,8 @@ export const App = () => {
|
||||
}}>
|
||||
<ModelOne />
|
||||
<ModelTwo />
|
||||
<ModelTwo2 />
|
||||
<ModelThree />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@ -29,7 +33,7 @@ const ModelOne = () => {
|
||||
};
|
||||
return (
|
||||
<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
|
||||
<div className='cursor-pointer p-2 border' onClick={showModel}>
|
||||
show
|
||||
@ -50,12 +54,12 @@ const ModelTwo = () => {
|
||||
contentStyle: {
|
||||
// maxHeight: '100px',
|
||||
// overflow: 'auto',
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
return (
|
||||
<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
|
||||
<div className='cursor-pointer p-2 border' onClick={showModel}>
|
||||
show
|
||||
@ -71,3 +75,130 @@ const ModelTwo = () => {
|
||||
</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",
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.2",
|
||||
"description": "",
|
||||
"main": "dist/index.js",
|
||||
"privite": false,
|
||||
"scripts": {
|
||||
"build": "tsc"
|
||||
"tsc": "tsc",
|
||||
"dev": "rollup -c -w",
|
||||
"build": "npm run clean && rollup -c",
|
||||
"clean": "rimraf dist"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
@ -15,10 +19,18 @@
|
||||
"author": "abearxiong",
|
||||
"devDependencies": {
|
||||
"@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",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"cssnano": "^7.0.6",
|
||||
"immer": "^10.1.1",
|
||||
"nanoid": "^5.0.7",
|
||||
"postcss-import": "^16.1.0",
|
||||
"rollup": "^4.22.2",
|
||||
"rollup-plugin-postcss": "^4.0.2",
|
||||
"ts-lib": "^0.0.5",
|
||||
"typescript": "^5.6.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,
|
||||
});
|
||||
}
|
||||
static render(el: string | HTMLDivElement, id: string, opts?: DialogModalOpts): any;
|
||||
static render(el: string | HTMLDivElement, opts?: DialogModalOpts): any;
|
||||
static render(...args: any[]) {
|
||||
const [el, id, opts] = args;
|
||||
super.render(el, id, opts);
|
||||
}
|
||||
// static render(el: string | HTMLDivElement, id: string, opts?: DialogModalOpts): DialogModal;
|
||||
// static render(el: string | HTMLDivElement, opts?: DialogModalOpts): DialogModal;
|
||||
// static render(...args: any[]) {
|
||||
// const [el, id, opts] = args;
|
||||
// return super.render(el, id, opts);
|
||||
// }
|
||||
appendRoot(documentFragment: DocumentFragment): void {
|
||||
const cacheFragment = document.createDocumentFragment();
|
||||
// 拿出来
|
||||
|
@ -8,10 +8,9 @@
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 200;
|
||||
display: none;
|
||||
}
|
||||
.ui-modal-open {
|
||||
display: block;
|
||||
.ui-modal-close {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ui-modal-mask {
|
||||
|
@ -23,8 +23,9 @@ export type ModalOpts<
|
||||
|
||||
contentClassName?: string;
|
||||
contentStyle?: ElStyle;
|
||||
|
||||
destroyOnClose?: boolean;
|
||||
destroyOnClose?: boolean; // 关闭,把Element移动到cacheFragment中
|
||||
hideOnClose?: boolean; // 关闭后是否销毁,设置display:none
|
||||
open?: boolean;
|
||||
|
||||
onClose?: () => void;
|
||||
defaultStyle?: DefaultStyle<U>;
|
||||
@ -49,6 +50,7 @@ export class Modal<T = any, U = KV> {
|
||||
contentStyle?: ElStyle;
|
||||
|
||||
destroyOnClose?: boolean;
|
||||
hideOnClose?: boolean;
|
||||
|
||||
open?: boolean;
|
||||
isUse = true;
|
||||
@ -67,12 +69,18 @@ export class Modal<T = any, U = KV> {
|
||||
this.maskClose = opts.maskClose ?? true;
|
||||
this.contentClassName = opts.contentClassName;
|
||||
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.defaultStyle = opts.defaultStyle || ({} as DefaultStyle<U>);
|
||||
this.open = opts.open ?? true;
|
||||
this.onClose = opts.onClose;
|
||||
}
|
||||
initRoot(root: ModalOpts['root']) {
|
||||
protected initRoot(root: ModalOpts['root']) {
|
||||
let _root = querySelector(root);
|
||||
if (!_root) {
|
||||
// 查询ui-modal元素,不存在则创建一个ui-modal元素并添加到body上
|
||||
@ -96,8 +104,8 @@ export class Modal<T = any, U = KV> {
|
||||
|
||||
return _root;
|
||||
}
|
||||
static render(el: string | HTMLDivElement, id: string, opts?: ModalOpts): any;
|
||||
static render(el: string | HTMLDivElement, opts?: ModalOpts): any;
|
||||
static render<T extends new (...args: any[]) => any>(this: T,el: string | HTMLDivElement, id: string, opts?: ModalOpts): InstanceType<T>;
|
||||
static render<T extends new (...args: any[]) => any>(this: T,el: string | HTMLDivElement, opts?: ModalOpts): InstanceType<T>;
|
||||
static render(...args: any[]) {
|
||||
let [el, id, opts] = args;
|
||||
const _el = querySelector(el);
|
||||
@ -131,6 +139,26 @@ export class Modal<T = any, U = KV> {
|
||||
});
|
||||
}
|
||||
_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() {
|
||||
const mask = document.createElement('div');
|
||||
@ -178,20 +206,26 @@ export class Modal<T = any, U = KV> {
|
||||
}
|
||||
appendRoot(document: DocumentFragment) {
|
||||
this.root.appendChild(document);
|
||||
this.setOpen(true);
|
||||
// 第一次渲染,open为true,显示弹窗
|
||||
this.setOpen(this.open);
|
||||
}
|
||||
setOpen(open: boolean) {
|
||||
this.open = open;
|
||||
if (this.destroyOnClose && open === false) {
|
||||
this.unMount();
|
||||
}
|
||||
if (this.destroyOnClose) {
|
||||
if (open) {
|
||||
this.modalElement.classList.add('ui-modal-open');
|
||||
this.root.appendChild(this.modalElement);
|
||||
} else {
|
||||
// this.modalElement.classList.remove('ui-modal-open');
|
||||
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() {
|
||||
// 返回渲染的的Element, 然后删除modalElement
|
||||
@ -204,10 +238,12 @@ export class Modal<T = any, U = KV> {
|
||||
modals: modalState.modals.filter((modal) => modal.id !== this.id),
|
||||
});
|
||||
this.isUse = false;
|
||||
this.cacheFragment = new DocumentFragment();
|
||||
return fragment;
|
||||
}
|
||||
/**
|
||||
* 保留,暂时不用
|
||||
* // TODO: 研究
|
||||
* @param force
|
||||
* @param opts
|
||||
* @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';
|
||||
|
||||
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